1 /* 2 * Copyright 6WIND S.A., 2014 3 * 4 * This work is licensed under the terms of the GNU GPL, version 2 or 5 * (at your option) any later version. See the COPYING file in the 6 * top-level directory. 7 */ 8 9 #include "qemu/osdep.h" 10 #include "qemu-common.h" 11 12 #include "ivshmem-client.h" 13 14 #define IVSHMEM_CLIENT_DEFAULT_VERBOSE 0 15 #define IVSHMEM_CLIENT_DEFAULT_UNIX_SOCK_PATH "/tmp/ivshmem_socket" 16 17 typedef struct IvshmemClientArgs { 18 bool verbose; 19 const char *unix_sock_path; 20 } IvshmemClientArgs; 21 22 /* show ivshmem_client_usage and exit with given error code */ 23 static void 24 ivshmem_client_usage(const char *name, int code) 25 { 26 fprintf(stderr, "%s [opts]\n", name); 27 fprintf(stderr, " -h: show this help\n"); 28 fprintf(stderr, " -v: verbose mode\n"); 29 fprintf(stderr, " -S <unix_sock_path>: path to the unix socket\n" 30 " to connect to.\n" 31 " default=%s\n", IVSHMEM_CLIENT_DEFAULT_UNIX_SOCK_PATH); 32 exit(code); 33 } 34 35 /* parse the program arguments, exit on error */ 36 static void 37 ivshmem_client_parse_args(IvshmemClientArgs *args, int argc, char *argv[]) 38 { 39 int c; 40 41 while ((c = getopt(argc, argv, 42 "h" /* help */ 43 "v" /* verbose */ 44 "S:" /* unix_sock_path */ 45 )) != -1) { 46 47 switch (c) { 48 case 'h': /* help */ 49 ivshmem_client_usage(argv[0], 0); 50 break; 51 52 case 'v': /* verbose */ 53 args->verbose = 1; 54 break; 55 56 case 'S': /* unix_sock_path */ 57 args->unix_sock_path = optarg; 58 break; 59 60 default: 61 ivshmem_client_usage(argv[0], 1); 62 break; 63 } 64 } 65 } 66 67 /* show command line help */ 68 static void 69 ivshmem_client_cmdline_help(void) 70 { 71 printf("dump: dump peers (including us)\n" 72 "int <peer> <vector>: notify one vector on a peer\n" 73 "int <peer> all: notify all vectors of a peer\n" 74 "int all: notify all vectors of all peers (excepting us)\n"); 75 } 76 77 /* read stdin and handle commands */ 78 static int 79 ivshmem_client_handle_stdin_command(IvshmemClient *client) 80 { 81 IvshmemClientPeer *peer; 82 char buf[128]; 83 char *s, *token; 84 int ret; 85 int peer_id, vector; 86 87 memset(buf, 0, sizeof(buf)); 88 ret = read(0, buf, sizeof(buf) - 1); 89 if (ret < 0) { 90 return -1; 91 } 92 93 s = buf; 94 while ((token = strsep(&s, "\n\r;")) != NULL) { 95 if (!strcmp(token, "")) { 96 continue; 97 } 98 if (!strcmp(token, "?")) { 99 ivshmem_client_cmdline_help(); 100 } 101 if (!strcmp(token, "help")) { 102 ivshmem_client_cmdline_help(); 103 } else if (!strcmp(token, "dump")) { 104 ivshmem_client_dump(client); 105 } else if (!strcmp(token, "int all")) { 106 ivshmem_client_notify_broadcast(client); 107 } else if (sscanf(token, "int %d %d", &peer_id, &vector) == 2) { 108 peer = ivshmem_client_search_peer(client, peer_id); 109 if (peer == NULL) { 110 printf("cannot find peer_id = %d\n", peer_id); 111 continue; 112 } 113 ivshmem_client_notify(client, peer, vector); 114 } else if (sscanf(token, "int %d all", &peer_id) == 1) { 115 peer = ivshmem_client_search_peer(client, peer_id); 116 if (peer == NULL) { 117 printf("cannot find peer_id = %d\n", peer_id); 118 continue; 119 } 120 ivshmem_client_notify_all_vects(client, peer); 121 } else { 122 printf("invalid command, type help\n"); 123 } 124 } 125 126 printf("cmd> "); 127 fflush(stdout); 128 return 0; 129 } 130 131 /* listen on stdin (command line), on unix socket (notifications of new 132 * and dead peers), and on eventfd (IRQ request) */ 133 static int 134 ivshmem_client_poll_events(IvshmemClient *client) 135 { 136 fd_set fds; 137 int ret, maxfd; 138 139 while (1) { 140 141 FD_ZERO(&fds); 142 FD_SET(0, &fds); /* add stdin in fd_set */ 143 maxfd = 1; 144 145 ivshmem_client_get_fds(client, &fds, &maxfd); 146 147 ret = select(maxfd, &fds, NULL, NULL, NULL); 148 if (ret < 0) { 149 if (errno == EINTR) { 150 continue; 151 } 152 153 fprintf(stderr, "select error: %s\n", strerror(errno)); 154 break; 155 } 156 if (ret == 0) { 157 continue; 158 } 159 160 if (FD_ISSET(0, &fds) && 161 ivshmem_client_handle_stdin_command(client) < 0 && errno != EINTR) { 162 fprintf(stderr, "ivshmem_client_handle_stdin_command() failed\n"); 163 break; 164 } 165 166 if (ivshmem_client_handle_fds(client, &fds, maxfd) < 0) { 167 fprintf(stderr, "ivshmem_client_handle_fds() failed\n"); 168 break; 169 } 170 } 171 172 return ret; 173 } 174 175 /* callback when we receive a notification (just display it) */ 176 static void 177 ivshmem_client_notification_cb(const IvshmemClient *client, 178 const IvshmemClientPeer *peer, 179 unsigned vect, void *arg) 180 { 181 (void)client; 182 (void)arg; 183 printf("receive notification from peer_id=%" PRId64 " vector=%u\n", 184 peer->id, vect); 185 } 186 187 int 188 main(int argc, char *argv[]) 189 { 190 struct sigaction sa; 191 IvshmemClient client; 192 IvshmemClientArgs args = { 193 .verbose = IVSHMEM_CLIENT_DEFAULT_VERBOSE, 194 .unix_sock_path = IVSHMEM_CLIENT_DEFAULT_UNIX_SOCK_PATH, 195 }; 196 197 /* parse arguments, will exit on error */ 198 ivshmem_client_parse_args(&args, argc, argv); 199 200 /* Ignore SIGPIPE, see this link for more info: 201 * http://www.mail-archive.com/libevent-users@monkey.org/msg01606.html */ 202 sa.sa_handler = SIG_IGN; 203 sa.sa_flags = 0; 204 if (sigemptyset(&sa.sa_mask) == -1 || 205 sigaction(SIGPIPE, &sa, 0) == -1) { 206 perror("failed to ignore SIGPIPE; sigaction"); 207 return 1; 208 } 209 210 ivshmem_client_cmdline_help(); 211 printf("cmd> "); 212 fflush(stdout); 213 214 if (ivshmem_client_init(&client, args.unix_sock_path, 215 ivshmem_client_notification_cb, NULL, 216 args.verbose) < 0) { 217 fprintf(stderr, "cannot init client\n"); 218 return 1; 219 } 220 221 while (1) { 222 if (ivshmem_client_connect(&client) < 0) { 223 fprintf(stderr, "cannot connect to server, retry in 1 second\n"); 224 sleep(1); 225 continue; 226 } 227 228 fprintf(stdout, "listen on server socket %d\n", client.sock_fd); 229 230 if (ivshmem_client_poll_events(&client) == 0) { 231 continue; 232 } 233 234 /* disconnected from server, reset all peers */ 235 fprintf(stdout, "disconnected from server\n"); 236 237 ivshmem_client_close(&client); 238 } 239 240 return 0; 241 } 242