diff options
-rw-r--r-- | cmdline.c | 150 | ||||
-rw-r--r-- | cmdline.h | 4 | ||||
-rw-r--r-- | main.c | 14 | ||||
-rw-r--r-- | main_client.c | 11 | ||||
-rw-r--r-- | plugins.c | 94 | ||||
-rw-r--r-- | plugins.h | 15 |
6 files changed, 238 insertions, 50 deletions
@@ -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)); @@ -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 */ @@ -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(); @@ -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; } @@ -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); |