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