xref: /openbmc/qemu/contrib/ivshmem-client/main.c (revision 6016b7b4)
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 
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