Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--cmdline.c150
-rw-r--r--cmdline.h4
-rw-r--r--main.c14
-rw-r--r--main_client.c11
-rw-r--r--plugins.c94
-rw-r--r--plugins.h15
6 files changed, 238 insertions, 50 deletions
diff --git a/cmdline.c b/cmdline.c
index a78403b1..1a171624 100644
--- a/cmdline.c
+++ b/cmdline.c
@@ -10,6 +10,7 @@
*
* Contributors:
* Wind River Systems - initial API and implementation
+ * Michael Sills-Lavoie - client enhancement system
*******************************************************************************/
/*
@@ -33,8 +34,16 @@
#include "protocol.h"
#include "trace.h"
#include "channel.h"
+#include "plugins.h"
+
+struct cmd {
+ char * cmd;
+ char * help;
+ int (*hnd)(char *);
+};
static Channel * chan;
+static Protocol * proto;
static FILE * infile;
static int interactive_flag;
static int cmdline_suspended;
@@ -43,6 +52,23 @@ static char * cmdline_string;
static pthread_mutex_t cmdline_mutex;
static pthread_cond_t cmdline_signal;
static pthread_t interactive_thread;
+static struct cmd * cmds = NULL;
+static size_t cmd_count = 0;
+static void (**connect_hnds)(Channel *) = NULL;
+static size_t connect_hnd_count = 0;
+static void (**disconnect_hnds)(Channel *) = NULL;
+static size_t disconnect_hnd_count = 0;
+
+static void destroy_cmdline_handler() {
+ size_t i;
+ for (i = 0; i < cmd_count; ++i) {
+ loc_free(cmds[i].cmd);
+ loc_free(cmds[i].help);
+ }
+ loc_free(cmds);
+ loc_free(connect_hnds);
+ loc_free(disconnect_hnds);
+}
static void channel_connecting(Channel * c) {
send_hello_message(c->client_data, c);
@@ -57,10 +83,15 @@ static void channel_receive(Channel * c) {
}
static void channel_disconnected(Channel * c) {
+ size_t i = 0;
if (chan == c) chan = NULL;
+ protocol_release(proto);
+ for (; i < disconnect_hnd_count; ++i)
+ disconnect_hnds[i](c);
}
static int cmd_exit(char * s) {
+ destroy_cmdline_handler();
exit(0);
}
@@ -81,6 +112,11 @@ static void display_tcf_reply(Channel * c, void * client_data, int error) {
putchar(i);
}
putchar('\n');
+
+ /* We flush the stream to be able to connect to the client with pipes
+ * and receive the message when it's displayed */
+ fflush(0);
+
cmd_done();
}
@@ -136,7 +172,8 @@ static int print_peer_flags(PeerServer * ps) {
printf(", ");
}
cnt++;
- printf(flagnames[i].name);
+ /* We add the "s" format string to get rid of a gcc warning */
+ printf("%s", flagnames[i].name);
flags &= ~flagnames[i].flag;
}
}
@@ -188,13 +225,17 @@ static void connect_done(void * args, int error, Channel * c) {
fprintf(stderr, "Error: Cannot connect: %s\n", errno_to_str(error));
}
else {
+ size_t i = 0;
c->connecting = channel_connecting;
c->connected = channel_connected;
c->receive = channel_receive;
c->disconnected = channel_disconnected;
- c->client_data = protocol_alloc();
+ protocol_reference(proto);
+ c->client_data = proto;
channel_start(c);
chan = c;
+ for (; i < connect_hnd_count; ++i)
+ connect_hnds[i](c);
}
peer_server_free(ps);
cmd_done();
@@ -217,18 +258,7 @@ static void event_cmd_line(void * arg) {
char * s = (char *)arg;
int len;
int delayed = 0;
- struct {
- char * cmd;
- char * help;
- int (*hnd)(char *);
- } cmds[] = {
- { "exit", "quit the program", cmd_exit },
- { "tcf", "send TCF command", cmd_tcf },
- { "peers", "show list of known peers", cmd_peers },
- { "peerinfo", "show info about a peer", cmd_peerinfo },
- { "connect", "connect a peer", cmd_connect },
- { 0 }
- }, *cp;
+ size_t cp;
if (cmdline_suspended) {
cmdline_string = s;
@@ -237,20 +267,20 @@ static void event_cmd_line(void * arg) {
while (*s && isspace(*s)) s++;
if (*s) {
- for (cp = cmds; cp->cmd != 0; cp++) {
- len = strlen(cp->cmd);
- if (strncmp(s, cp->cmd, len) == 0 && (s[len] == 0 || isspace(s[len]))) {
+ for (cp = 0; cp < cmd_count; ++cp) {
+ len = strlen(cmds[cp].cmd);
+ if (strncmp(s, cmds[cp].cmd, len) == 0 && (s[len] == 0 || isspace(s[len]))) {
s += len;
while (*s && isspace(*s)) s++;
- delayed = cp->hnd(s);
+ delayed = cmds[cp].hnd(s);
break;
}
}
- if (cp->cmd == 0) {
+ if (cp == cmd_count) {
fprintf(stderr, "Unknown command: %s\n", s);
fprintf(stderr, "Available commands:\n");
- for (cp = cmds; cp->cmd != 0; cp++) {
- fprintf(stderr, " %-10s - %s\n", cp->cmd, cp->help);
+ for (cp = 0; cp < cmd_count; ++cp) {
+ fprintf(stderr, " %-10s - %s\n", cmds[cp].cmd, cmds[cp].help);
}
}
}
@@ -322,7 +352,83 @@ void open_script_file(char * script_name) {
}
}
-void ini_cmdline_handler(int interactive) {
+static int add_cmdline_cmd(const char * cmd_name, const char * cmd_desc,
+ int (*hnd)(char *)) {
+ size_t i;
+ assert(is_dispatch_thread());
+ if (!cmd_name || !cmd_desc || !hnd) return -EINVAL;
+
+ /* Check if the cmd name already exists */
+ for (i = 0; i < cmd_count; ++i)
+ if (!strcmp(cmd_name, cmds[i].cmd))
+ return -EEXIST;
+
+ cmds = (struct cmd *) loc_realloc(cmds, ++cmd_count * sizeof(struct cmd));
+
+ cmds[cmd_count-1].cmd = loc_strdup(cmd_name);
+ cmds[cmd_count-1].help = loc_strdup(cmd_desc);
+ cmds[cmd_count-1].hnd = hnd;
+
+ return 0;
+}
+
+#if ENABLE_Plugins
+static int add_connect_callback(void (*hnd)(Channel *)) {
+ size_t i;
+ assert(is_dispatch_thread());
+ if (!hnd) return -EINVAL;
+
+ /* Check if the handle already exists */
+ for (i = 0; i < connect_hnd_count; ++i)
+ if (hnd == connect_hnds[i])
+ return -EEXIST;
+
+ connect_hnds = loc_realloc(connect_hnds, ++connect_hnd_count * sizeof(void (*)(Channel *)));
+
+ connect_hnds[connect_hnd_count-1] = hnd;
+
+ return 0;
+}
+
+static int add_disconnect_callback(void (*hnd)(Channel *)) {
+ size_t i;
+ assert(is_dispatch_thread());
+ if (!hnd) return -EINVAL;
+
+ /* Check if the handle already exists */
+ for (i = 0; i < disconnect_hnd_count; ++i)
+ if (hnd == disconnect_hnds[i])
+ return -EEXIST;
+
+ disconnect_hnds = loc_realloc(disconnect_hnds, ++disconnect_hnd_count * sizeof(void (*)(Channel *)));
+
+ disconnect_hnds[disconnect_hnd_count-1] = hnd;
+
+ return 0;
+}
+#endif /* ENABLE_Plugins */
+
+void ini_cmdline_handler(int interactive, Protocol * protocol) {
+ proto = protocol;
+
+#if ENABLE_Plugins
+ if (plugin_add_function("Cmdline_add_cmd", (void *)add_cmdline_cmd)) {
+ fprintf(stderr, "Error: Cannot add add_cmd shared function\n");
+ }
+ if (plugin_add_function("Cmdline_add_connect_callback", (void *)add_connect_callback)) {
+ fprintf(stderr, "Error: Cannot add add_connect_callback shared function\n");
+ }
+ if (plugin_add_function("Cmdline_add_disconnect_callback", (void *)add_disconnect_callback)) {
+ fprintf(stderr, "Error: Cannot add add_disconnect_callback shared function\n");
+ }
+#endif
+
+ add_cmdline_cmd("exit", "quit the program", cmd_exit);
+ add_cmdline_cmd("tcf", "send TCF command", cmd_tcf);
+ add_cmdline_cmd("peers", "show list of known peers", cmd_peers);
+ add_cmdline_cmd("peerinfo", "show info about a peer", cmd_peerinfo);
+ add_cmdline_cmd("connect", "connect a peer", cmd_connect);
+
interactive_flag = interactive;
if (infile == NULL) infile = stdin;
check_error(pthread_mutex_init(&cmdline_mutex, NULL));
diff --git a/cmdline.h b/cmdline.h
index 7f42fb54..d65decab 100644
--- a/cmdline.h
+++ b/cmdline.h
@@ -21,9 +21,11 @@
#if ENABLE_Cmdline
+#include "protocol.h"
+
extern void cmdline_suspend(void);
extern void cmdline_resume(void);
-extern void ini_cmdline_handler(int interactive);
+extern void ini_cmdline_handler(int interactive, Protocol * proto);
extern void open_script_file(char * script_name);
#else /* ENABLE_Cmdline */
diff --git a/main.c b/main.c
index 844e0f33..051772f6 100644
--- a/main.c
+++ b/main.c
@@ -182,6 +182,14 @@ int main(int argc, char ** argv) {
bcg = broadcast_group_alloc();
spg = suspend_group_alloc();
proto = protocol_alloc();
+
+ /* The static services must be initialised before the plugins */
+#if ENABLE_Cmdline
+ if (interactive) ini_cmdline_handler(interactive, proto);
+#else
+ if (interactive) fprintf(stderr, "Warning: This version does not support interactive mode.\n");
+#endif
+
ini_services(proto, bcg, spg);
ps = channel_peer_from_url(url);
@@ -198,12 +206,6 @@ int main(int argc, char ** argv) {
discovery_start();
-#if ENABLE_Cmdline
- if (interactive) ini_cmdline_handler(interactive);
-#else
- if (interactive) fprintf(stderr, "Warning: This version does not support interactive mode.\n");
-#endif
-
/* Process events - must run on the initial thread since ptrace()
* returns ECHILD otherwise, thinking we are not the owner. */
run_event_loop();
diff --git a/main_client.c b/main_client.c
index df7ebe03..bf59b2c4 100644
--- a/main_client.c
+++ b/main_client.c
@@ -31,8 +31,10 @@
#include "proxy.h"
#include "discovery.h"
#include "cmdline.h"
+#include "plugins.h"
static char * progname;
+static Protocol * proto;
/*
* main entry point for TCF client
@@ -125,13 +127,20 @@ int main(int argc, char ** argv) {
#endif
discovery_start();
+
+ proto = protocol_alloc();
+
#if ENABLE_Cmdline
if (script_name != NULL) open_script_file(script_name);
- ini_cmdline_handler(interactive);
+ ini_cmdline_handler(interactive, proto);
#else
if (script_name != NULL) fprintf(stderr, "Warning: This version does not support script file as input.\n");
#endif
+#if ENABLE_Plugins
+ plugins_load(proto, NULL, NULL);
+#endif
+
/* Process events - must run on the initial thread since ptrace()
* returns ECHILD otherwise, thinking we are not the owner. */
run_event_loop();
diff --git a/plugins.c b/plugins.c
index ac3f20f2..e1f00d9d 100644
--- a/plugins.c
+++ b/plugins.c
@@ -12,6 +12,8 @@
*
* Contributors:
* Philippe Proulx - initial plugins system
+ * Michael Sills-Lavoie - deterministic plugins loading order
+ * Michael Sills-Lavoie - plugin's shared functions
*******************************************************************************/
/*
@@ -28,48 +30,69 @@
#include <sys/types.h>
#include <dirent.h>
#include <dlfcn.h>
+#include <errno.h>
#include "trace.h"
+#include "myalloc.h"
#include "plugins.h"
static void ** plugins_handles = NULL;
static size_t plugins_count = 0;
+static struct function_entry {
+ char * name;
+ void * function;
+} * function_entries = NULL;
+static size_t function_entry_count = 0;
static inline int plugins_ext_is(const char * ext, const char * filename) {
const char * real_ext = strrchr(filename, '.');
return real_ext != NULL && !strcmp(real_ext + 1, ext);
}
+static int plugins_filter(const struct dirent * dirent) {
+ if (!strcmp(dirent->d_name, ".") || !strcmp(dirent->d_name, ".."))
+ return 0;
+ if (!plugins_ext_is(PLUGINS_DEF_EXT, dirent->d_name) || dirent->d_type == DT_DIR)
+ return 0;
+ return 1;
+}
+
+static int plugins_ralphasort(const void *a, const void *b) {
+ return -alphasort(a,b);
+}
+
int plugins_load(Protocol * proto, TCFBroadcastGroup * bcg, TCFSuspendGroup * spg) {
+ struct dirent ** files;
+ int file_count;
int ret = 0;
- struct dirent * dirent;
- DIR * dir;
- dir = opendir(QUOTE(PATH_Plugins));
- if (!dir) {
+ file_count = scandir(QUOTE(PATH_Plugins), &files, plugins_filter, plugins_ralphasort);
+ if (file_count < 0) {
trace(LOG_ALWAYS, "plugins error: failed opening plugins directory \"" QUOTE(PATH_Plugins) "\"");
return -1;
}
- while (dirent = readdir(dir)) {
+
+ while (file_count--) {
char * cur_plugin_path = NULL;
- if (!strcmp(dirent->d_name, ".") || !strcmp(dirent->d_name, "..")) {
- continue;
- }
- if (!plugins_ext_is(PLUGINS_DEF_EXT, dirent->d_name) || dirent->d_type == DT_DIR) {
- continue;
- }
- if (asprintf(&cur_plugin_path, QUOTE(PATH_Plugins) "/%s", dirent->d_name) == -1) {
- trace(LOG_ALWAYS, "plugins error: `asprintf' failed for plugin \"%s\"", dirent->d_name);
- return -1;
+
+ if (asprintf(&cur_plugin_path, QUOTE(PATH_Plugins) "/%s", files[file_count]->d_name) == -1) {
+ trace(LOG_ALWAYS, "plugins error: `asprintf' failed for plugin \"%s\"", files[file_count]->d_name);
+ ret = -1;
+ goto delete_cur_entry;
}
if (plugin_init(cur_plugin_path, proto, bcg, spg)) {
trace(LOG_ALWAYS, "plugins error: unable to start plugin \"%s\"", cur_plugin_path);
ret = -1;
/* Continue to load the rest of plugins */
}
+
+ /* cur_plugin_path and files were allocated by asprintf() and scandir(),
+ * and they should be released by free(), don't call loc_free() here. */
free(cur_plugin_path);
+delete_cur_entry:
+ free(files[file_count]);
}
- closedir(dir);
+ free(files);
return ret;
}
@@ -97,23 +120,56 @@ int plugin_init(const char * name, Protocol * proto, TCFBroadcastGroup * bcg, TC
init(proto, bcg, spg);
/* Handles table update: */
- plugins_handles = (void **) realloc(plugins_handles, ++plugins_count * sizeof(void *));
+ plugins_handles = (void **) loc_realloc(plugins_handles,
+ ++plugins_count * sizeof(void *));
plugins_handles[plugins_count - 1] = handle;
return 0;
}
-int plugins_destroy(void) {
+int plugin_add_function(const char * name, void * function) {
+ size_t i;
+
+ if (!name || !function) return -EINVAL;
+
+ /* Check if the function name already exists */
+ for (i = 0; i < function_entry_count; ++i)
+ if (!strcmp(name, function_entries[i].name))
+ return -EEXIST;
+
+ function_entries = (struct function_entry *) loc_realloc(function_entries,
+ ++function_entry_count * sizeof(struct function_entry));
+
+ function_entries[function_entry_count-1].function = function;
+ function_entries[function_entry_count-1].name = loc_strdup(name);
+ return 0;
+}
+
+void * plugin_get_function(const char * name) {
size_t i;
- if (plugins_handles == NULL) return 0;
+ if (!name) return NULL;
+
+ for (i = 0; i < function_entry_count; ++i)
+ if (!strcmp(name, function_entries[i].name))
+ return function_entries[i].function;
+
+ return NULL;
+}
+
+int plugins_destroy(void) {
+ size_t i;
for (i = 0; i < plugins_count; ++i) {
if (dlclose(plugins_handles[i])) {
trace(LOG_ALWAYS, "plugins error: \"%s\"", dlerror());
}
}
- free(plugins_handles);
+ loc_free(plugins_handles);
+
+ for (i = 0; i < function_entry_count; ++i)
+ loc_free(function_entries[i].name);
+ loc_free(function_entries);
return 0;
}
diff --git a/plugins.h b/plugins.h
index 54394e9d..0d7aa345 100644
--- a/plugins.h
+++ b/plugins.h
@@ -12,6 +12,8 @@
*
* Contributors:
* Philippe Proulx - initial plugins system
+ * Michael Sills-Lavoie - deterministic plugins loading order
+ * Michael Sills-Lavoie - plugin's shared functions
*******************************************************************************/
/*
@@ -29,7 +31,8 @@
#define PLUGINS_DEF_EXT "so" /* Default plugins' extension */
/*
- * Loads ALL plugins from the directory PATH_Plugins (from `config.h').
+ * Loads ALL plugins from the directory PATH_Plugins (from `config.h') in
+ * alphabetical order.
*/
int plugins_load(Protocol *, TCFBroadcastGroup *, TCFSuspendGroup *);
@@ -39,6 +42,16 @@ int plugins_load(Protocol *, TCFBroadcastGroup *, TCFSuspendGroup *);
int plugin_init(const char *, Protocol *, TCFBroadcastGroup *, TCFSuspendGroup *);
/*
+ * Add a new public plugin function for the other plugins to see.
+ */
+int plugin_add_function(const char *, void *);
+
+/*
+ * Get a public function from its name.
+ */
+void * plugin_get_function(const char *);
+
+/*
* Destroys loaded plugins.
*/
int plugins_destroy(void);

Back to the top