| /******************************************************************************* |
| * Copyright (c) 2007, 2010 Wind River Systems, Inc. and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * and Eclipse Distribution License v1.0 which accompany this distribution. |
| * The Eclipse Public License is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * and the Eclipse Distribution License is available at |
| * http://www.eclipse.org/org/documents/edl-v10.php. |
| * |
| * Contributors: |
| * Wind River Systems - initial API and implementation |
| * Michael Sills-Lavoie - client enhancement system |
| *******************************************************************************/ |
| |
| /* |
| * Command line interpreter. |
| */ |
| |
| #include <config.h> |
| |
| #if ENABLE_Cmdline |
| |
| #include <assert.h> |
| #include <stdlib.h> |
| #include <stdio.h> |
| #include <string.h> |
| #include <ctype.h> |
| #include <framework/events.h> |
| #include <framework/errors.h> |
| #include <framework/myalloc.h> |
| #include <framework/peer.h> |
| #include <framework/protocol.h> |
| #include <framework/trace.h> |
| #include <framework/channel.h> |
| #include <framework/plugins.h> |
| #include <main/cmdline.h> |
| |
| struct CmdDesc { |
| char * cmd; |
| char * help; |
| int (*hnd)(char *); |
| }; |
| |
| static Channel * chan; |
| static Protocol * proto; |
| static FILE * infile; |
| static int mode_flag; |
| static int keep_alive_flag = 0; |
| static int cmdline_suspended; |
| static int cmdline_pending; |
| static int last_error = 0; |
| static char * host_name = NULL; |
| static char * single_command = NULL; |
| static char * cmdline_string; |
| static pthread_mutex_t cmdline_mutex; |
| static pthread_cond_t cmdline_signal; |
| static pthread_t command_thread; |
| static struct CmdDesc * cmds = NULL; |
| static size_t cmd_count = 0; |
| |
| typedef void (*PluginCallBack)(Channel *); |
| |
| static PluginCallBack * connect_hnds = NULL; |
| static size_t connect_hnd_count = 0; |
| static PluginCallBack * disconnect_hnds = NULL; |
| static size_t disconnect_hnd_count = 0; |
| |
| static void cmd_done(int error); |
| |
| 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); |
| if (host_name) loc_free(host_name); |
| if (single_command) loc_free(single_command); |
| } |
| |
| static void channel_connected(Channel * c) { |
| /* We are now connected, so there is no error (0)*/ |
| if (c == chan) cmd_done(0); |
| } |
| |
| static void channel_disconnected(Channel * c) { |
| size_t i = 0; |
| if (chan == c) chan = NULL; |
| protocol_release(c->protocol); |
| for (i = 0; i < disconnect_hnd_count; ++i) disconnect_hnds[i](c); |
| } |
| |
| static int cmd_exit(char * s) { |
| destroy_cmdline_handler(); |
| exit(0); |
| } |
| |
| static void display_tcf_reply(Channel * c, void * client_data, int error) { |
| int i; |
| |
| if (error) { |
| printf("Reply error %d: %s\n", error, errno_to_str(error)); |
| cmd_done(error); |
| return; |
| } |
| for (;;) { |
| i = read_stream(&c->inp); |
| if (i == MARKER_EOM) break; |
| if (i == 0) i = ' '; |
| 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(error); |
| } |
| |
| #define maxargs 20 |
| |
| static int cmd_tcf(char *s) { |
| int i; |
| int ind; |
| char * args[maxargs]; |
| Channel * c = chan; |
| |
| if (c == NULL) { |
| printf("Error: Channel not connected, use 'connect' command\n"); |
| return -1; |
| } |
| ind = 0; |
| args[ind] = strtok(s, " \t"); |
| while (args[ind] != NULL && ++ind < maxargs) { |
| args[ind] = strtok(NULL, " \t"); |
| } |
| if (args[0] == NULL || args[1] == NULL) { |
| printf("Error: Expected at least service and command name arguments\n"); |
| return -1; |
| } |
| protocol_send_command(c, args[0], args[1], display_tcf_reply, c); |
| for (i = 2; i < ind; i++) { |
| write_stringz(&c->out, args[i]); |
| } |
| write_stream(&c->out, MARKER_EOM); |
| return 1; |
| } |
| |
| static int print_peer_flags(PeerServer * ps) { |
| unsigned int flags = ps->flags; |
| int cnt; |
| int i; |
| struct { |
| unsigned int flag; |
| const char * name; |
| } flagnames[] = { |
| { PS_FLAG_LOCAL, "local" }, |
| { PS_FLAG_PRIVATE, "private" }, |
| { PS_FLAG_DISCOVERABLE, "discoverable" }, |
| { 0 } |
| }; |
| |
| printf(" "); |
| cnt = 0; |
| for (i = 0; flagnames[i].flag != 0; i++) { |
| if (flags & flagnames[i].flag) { |
| if (cnt != 0) { |
| printf(", "); |
| } |
| cnt++; |
| /* We add the "s" format string to get rid of a gcc warning */ |
| printf("%s", flagnames[i].name); |
| flags &= ~flagnames[i].flag; |
| } |
| } |
| if (flags || cnt == 0) printf("0x%x", flags); |
| return 0; |
| } |
| |
| static int print_peer_summary(PeerServer * ps, void * client_data) { |
| const char * s = peer_server_getprop(ps, "Name", NULL); |
| printf(" %s", ps->id); |
| if (s != NULL) printf(", %s", s); |
| printf("\n"); |
| return 0; |
| } |
| |
| static int cmd_peers(char * s) { |
| printf("Peers:\n"); |
| peer_server_iter(print_peer_summary, NULL); |
| return 0; |
| } |
| |
| static int cmd_peerinfo(char * s) { |
| PeerServer * ps; |
| int i; |
| |
| printf("Peer information: %s\n", s); |
| ps = peer_server_find(s); |
| if (ps == NULL) { |
| fprintf(stderr, "Error: Cannot find id: %s\n", s); |
| return -1; |
| } |
| printf(" ID: %s\n", ps->id); |
| for (i = 0; i < ps->ind; i++) { |
| printf(" %s: %s\n", ps->list[i].name, ps->list[i].value); |
| } |
| print_peer_flags(ps); |
| printf("\n"); |
| return 0; |
| } |
| |
| static void connect_callback(void * args, int error, Channel * c) { |
| PeerServer * ps = (PeerServer *)args; |
| |
| if (error) { |
| fprintf(stderr, "Error: Cannot connect: %s\n", errno_to_str(error)); |
| cmd_done(error); |
| } |
| else { |
| size_t i = 0; |
| c->connected = channel_connected; |
| c->disconnected = channel_disconnected; |
| c->protocol = proto; |
| protocol_reference(proto); |
| channel_start(c); |
| chan = c; |
| for (i = 0; i < connect_hnd_count; ++i) connect_hnds[i](c); |
| } |
| peer_server_free(ps); |
| } |
| |
| static int cmd_connect(char * s) { |
| PeerServer * ps = NULL; |
| |
| ps = channel_peer_from_url(s); |
| if (ps == NULL) { |
| fprintf(stderr, "Error: Cannot parse peer identifer: %s\n", s); |
| return -1; |
| } |
| |
| channel_connect(ps, connect_callback, ps); |
| return 1; |
| } |
| |
| static void event_cmd_line(void * arg) { |
| char * s = (char *)arg; |
| size_t len; |
| int delayed = 0; |
| int error = 0; |
| size_t cp; |
| |
| if (cmdline_suspended) { |
| cmdline_string = s; |
| return; |
| } |
| |
| while (*s && isspace((int)*s)) s++; |
| if (*s) { |
| for (cp = 0; cp < cmd_count; ++cp) { |
| len = strlen(cmds[cp].cmd); |
| if (strncmp(s, cmds[cp].cmd, len) == 0 && (s[len] == 0 || isspace((int)s[len]))) { |
| s += len; |
| while (*s && isspace((int)*s)) s++; |
| delayed = cmds[cp].hnd(s); |
| if (delayed != 1 || delayed != 0) error = delayed; |
| break; |
| } |
| } |
| if (cp == cmd_count) { |
| fprintf(stderr, "Unknown command: %s\n", s); |
| fprintf(stderr, "Available commands:\n"); |
| for (cp = 0; cp < cmd_count; ++cp) { |
| fprintf(stderr, " %-10s - %s\n", cmds[cp].cmd, cmds[cp].help); |
| } |
| error = 1; |
| } |
| } |
| loc_free(arg); |
| if (delayed != 1) cmd_done(error); |
| } |
| |
| void cmdline_suspend(void) { |
| assert(!cmdline_suspended); |
| cmdline_suspended = 1; |
| } |
| |
| void cmdline_resume(void) { |
| assert(cmdline_suspended); |
| cmdline_suspended = 0; |
| if (cmdline_string != NULL) { |
| post_event(event_cmd_line, cmdline_string); |
| cmdline_string = NULL; |
| } |
| } |
| |
| static void cmd_done_event(void * arg) { |
| check_error(pthread_mutex_lock(&cmdline_mutex)); |
| assert(cmdline_pending); |
| cmdline_pending = 0; |
| check_error(pthread_cond_signal(&cmdline_signal)); |
| check_error(pthread_mutex_unlock(&cmdline_mutex)); |
| } |
| |
| static void cmd_done(int error) { |
| last_error = error; |
| post_event(cmd_done_event, NULL); |
| } |
| |
| static void * interactive_handler(void * x) { |
| int done = 0; |
| size_t len; |
| char buf[1000]; |
| |
| check_error(pthread_mutex_lock(&cmdline_mutex)); |
| while (!done) { |
| if (cmdline_pending) { |
| check_error(pthread_cond_wait(&cmdline_signal, &cmdline_mutex)); |
| continue; |
| } |
| if (mode_flag == 1) { |
| printf("> "); |
| fflush(stdout); |
| } |
| if (fgets(buf, sizeof(buf), infile) == NULL) { |
| strcpy(buf, "exit"); |
| done = 1; |
| } |
| len = strlen(buf); |
| if (len > 0 && buf[len-1] == '\n') { |
| buf[--len] = '\0'; |
| } |
| post_event(event_cmd_line, loc_strdup(buf)); |
| cmdline_pending = 1; |
| } |
| check_error(pthread_mutex_unlock(&cmdline_mutex)); |
| return NULL; |
| } |
| |
| static void * single_command_handler(void * x) { |
| const char * connect_string = "connect "; |
| |
| check_error(pthread_mutex_lock(&cmdline_mutex)); |
| |
| post_event(event_cmd_line, loc_strdup2(connect_string, host_name)); |
| cmdline_pending = 1; |
| check_error(pthread_cond_wait(&cmdline_signal, &cmdline_mutex)); |
| if (last_error) { |
| destroy_cmdline_handler(); |
| exit(last_error); |
| } |
| |
| post_event(event_cmd_line, loc_strdup(single_command)); |
| cmdline_pending = 1; |
| check_error(pthread_cond_wait(&cmdline_signal, &cmdline_mutex)); |
| if (last_error) { |
| destroy_cmdline_handler(); |
| exit(last_error); |
| } |
| |
| check_error(pthread_mutex_unlock(&cmdline_mutex)); |
| |
| destroy_cmdline_handler(); |
| |
| if (!keep_alive_flag) { |
| exit(0); |
| } |
| |
| return NULL; |
| } |
| |
| void open_script_file(const char * script_name) { |
| if (script_name == NULL || (infile = fopen(script_name, "r")) == NULL) { |
| if (script_name == NULL) script_name = "<null>"; |
| fprintf(stderr, "Error: Cannot open script file %s\n", script_name); |
| exit(1); |
| } |
| } |
| |
| void set_single_command(int keep_alive, const char * host, const char * command) { |
| if (host == NULL || command == NULL) { |
| fprintf(stderr, "Error: Cannot send single command\n"); |
| exit(1); |
| } |
| |
| keep_alive_flag = keep_alive; |
| host_name = loc_strdup(host); |
| single_command = loc_strdup(command); |
| } |
| |
| 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 CmdDesc *)loc_realloc(cmds, ++cmd_count * sizeof(struct CmdDesc)); |
| |
| 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(PluginCallBack hnd){ |
| 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 = (PluginCallBack *)loc_realloc(connect_hnds, ++connect_hnd_count * sizeof(PluginCallBack)); |
| connect_hnds[connect_hnd_count - 1] = hnd; |
| |
| return 0; |
| } |
| |
| static int add_disconnect_callback(PluginCallBack hnd) { |
| 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 = (PluginCallBack *)loc_realloc(disconnect_hnds, ++disconnect_hnd_count * sizeof(PluginCallBack)); |
| disconnect_hnds[disconnect_hnd_count - 1] = hnd; |
| |
| return 0; |
| } |
| #endif /* ENABLE_Plugins */ |
| |
| void ini_cmdline_handler(int mode, Protocol * protocol) { |
| proto = protocol; |
| |
| #if ENABLE_Plugins |
| if (plugin_add_function("Cmdline_cmd_done", (void *)cmd_done)) { |
| fprintf(stderr, "Error: Cannot add cmd_done shared function\n"); |
| } |
| 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); |
| |
| mode_flag = mode; |
| if (infile == NULL) infile = stdin; |
| check_error(pthread_mutex_init(&cmdline_mutex, NULL)); |
| check_error(pthread_cond_init(&cmdline_signal, NULL)); |
| |
| /* Create thread to read cmd line in interactive and script mode*/ |
| if (mode == 0 || mode == 1) |
| check_error(pthread_create(&command_thread, &pthread_create_attr, interactive_handler, 0)); |
| else |
| check_error(pthread_create(&command_thread, &pthread_create_attr, single_command_handler, 0)); |
| } |
| |
| #endif /* ENABLE_Cmdline */ |