1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Copyright (C) 2018 VMware Inc, Slavomir Kaslev <kaslevs@vmware.com>
4 *
5 * based on prior implementation by Yoshihiro Yunomae
6 * Copyright (C) 2013 Hitachi, Ltd.
7 * Yoshihiro YUNOMAE <yoshihiro.yunomae.ez@hitachi.com>
8 */
9
10 #include <errno.h>
11 #include <fcntl.h>
12 #include <getopt.h>
13 #include <signal.h>
14 #include <stdbool.h>
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <sys/socket.h>
18 #include <sys/wait.h>
19 #include <unistd.h>
20 #include <pthread.h>
21
22 #include "trace-local.h"
23 #include "trace-msg.h"
24
25 #define GUEST_NAME "::GUEST::"
26
27 #define dprint(fmt, ...) tracecmd_debug(fmt, ##__VA_ARGS__)
28
make_vsocks(int nr,int * fds,unsigned int * ports)29 static void make_vsocks(int nr, int *fds, unsigned int *ports)
30 {
31 unsigned int port;
32 int i, fd, ret;
33
34 for (i = 0; i < nr; i++) {
35 fd = trace_vsock_make_any();
36 if (fd < 0)
37 die("Failed to open vsocket");
38
39 ret = trace_vsock_get_port(fd, &port);
40 if (ret < 0)
41 die("Failed to get vsocket address");
42
43 fds[i] = fd;
44 ports[i] = port;
45 }
46 }
47
make_net(int nr,int * fds,unsigned int * ports)48 static void make_net(int nr, int *fds, unsigned int *ports)
49 {
50 int port;
51 int i, fd;
52 int start_port = START_PORT_SEARCH;
53
54 for (i = 0; i < nr; i++) {
55 port = trace_net_search(start_port, &fd, USE_TCP);
56 if (port < 0)
57 die("Failed to open socket");
58 if (listen(fd, 5) < 0)
59 die("Failed to listen on port %d\n", port);
60 fds[i] = fd;
61 ports[i] = port;
62 dprint("CPU[%d]: fd:%d port:%d\n", i, fd, port);
63 start_port = port + 1;
64 }
65 }
66
make_sockets(int nr,int * fds,unsigned int * ports,const char * network)67 static void make_sockets(int nr, int *fds, unsigned int *ports,
68 const char * network)
69 {
70 if (network)
71 return make_net(nr, fds, ports);
72 else
73 return make_vsocks(nr, fds, ports);
74 }
75
open_agent_fifos(int nr_cpus,int * fds)76 static int open_agent_fifos(int nr_cpus, int *fds)
77 {
78 char path[PATH_MAX];
79 int i, fd, ret;
80
81 for (i = 0; i < nr_cpus; i++) {
82 snprintf(path, sizeof(path), VIRTIO_FIFO_FMT, i);
83 fd = open(path, O_WRONLY);
84 if (fd < 0) {
85 ret = -errno;
86 goto cleanup;
87 }
88
89 fds[i] = fd;
90 }
91
92 return 0;
93
94 cleanup:
95 while (--i >= 0)
96 close(fds[i]);
97
98 return ret;
99 }
100
get_clock(int argc,char ** argv)101 static char *get_clock(int argc, char **argv)
102 {
103 int i;
104
105 if (!argc || !argv)
106 return NULL;
107
108 for (i = 0; i < argc - 1; i++) {
109 if (!strcmp("-C", argv[i]))
110 return argv[i+1];
111 }
112 return NULL;
113 }
114
trace_print_connection(int fd,const char * network)115 static void trace_print_connection(int fd, const char *network)
116 {
117 int ret;
118
119 if (network)
120 ret = trace_net_print_connection(fd);
121 else
122 ret = trace_vsock_print_connection(fd);
123 if (ret < 0)
124 tracecmd_debug("Could not print connection fd:%d\n", fd);
125 }
126
wait_for_connection(int fd)127 static int wait_for_connection(int fd)
128 {
129 int sd;
130
131 if (fd < 0)
132 return -1;
133
134 while (true) {
135 tracecmd_debug("Listening on fd:%d\n", fd);
136 sd = accept(fd, NULL, NULL);
137 tracecmd_debug("Accepted fd:%d\n", sd);
138 if (sd < 0) {
139 if (errno == EINTR)
140 continue;
141 return -1;
142 }
143 break;
144 }
145 close(fd);
146 return sd;
147 }
148
agent_handle(int sd,int nr_cpus,int page_size,int cid,int rcid,const char * network)149 static void agent_handle(int sd, int nr_cpus, int page_size,
150 int cid, int rcid, const char *network)
151 {
152 struct tracecmd_tsync_protos *tsync_protos = NULL;
153 struct tracecmd_time_sync *tsync = NULL;
154 struct tracecmd_msg_handle *msg_handle;
155 const char *tsync_proto = NULL;
156 struct trace_guest *guest;
157 unsigned long long peer_trace_id;
158 unsigned long long trace_id;
159 unsigned long flags = rcid >= 0 ? TRACECMD_MSG_FL_PROXY : 0;
160 enum tracecmd_time_sync_role tsync_role = TRACECMD_TIME_SYNC_ROLE_GUEST;
161 unsigned int remote_id;
162 unsigned int local_id;
163 unsigned int tsync_port = 0;
164 unsigned int *ports;
165 unsigned int client_cpus = 0;
166 unsigned int guests = 0;
167 char **argv = NULL;
168 int argc = 0;
169 bool use_fifos;
170 int *fds;
171 int ret;
172 int fd;
173
174 fds = calloc(nr_cpus, sizeof(*fds));
175 ports = calloc(nr_cpus, sizeof(*ports));
176 if (!fds || !ports)
177 die("Failed to allocate memory");
178
179 msg_handle = tracecmd_msg_handle_alloc(sd, flags);
180 if (!msg_handle)
181 die("Failed to allocate message handle");
182
183 if (rcid >= 0) {
184 tsync_role = TRACECMD_TIME_SYNC_ROLE_HOST;
185 ret = tracecmd_msg_recv_trace_proxy(msg_handle, &argc, &argv,
186 &use_fifos, &peer_trace_id,
187 &tsync_protos,
188 &client_cpus,
189 &guests);
190 /* Update the guests peer_trace_id */
191 guest = trace_get_guest(rcid, NULL);
192 if (guest)
193 guest->trace_id = peer_trace_id;
194 } else {
195 ret = tracecmd_msg_recv_trace_req(msg_handle, &argc, &argv,
196 &use_fifos, &peer_trace_id,
197 &tsync_protos);
198 }
199 if (ret < 0)
200 die("Failed to receive trace request");
201
202 tsync_proto = tracecmd_tsync_get_proto(tsync_protos, get_clock(argc, argv),
203 tsync_role);
204
205 if (use_fifos && open_agent_fifos(nr_cpus, fds))
206 use_fifos = false;
207
208 if (!use_fifos)
209 make_sockets(nr_cpus, fds, ports, network);
210 if (tsync_proto) {
211 if (network) {
212 /* For now just use something */
213 remote_id = 2;
214 local_id = 1;
215 tsync_port = trace_net_search(START_PORT_SEARCH, &fd, USE_TCP);
216 if (listen(fd, 5) < 0)
217 die("Failed to listen on %d\n", tsync_port);
218 } else {
219 if (get_vsocket_params(msg_handle->fd, &local_id,
220 &remote_id)) {
221 warning("Failed to get local and remote ids");
222 /* Just make something up */
223 remote_id = -1;
224 local_id = -2;
225 }
226 fd = trace_vsock_make_any();
227 if (fd >= 0 &&
228 trace_vsock_get_port(fd, &tsync_port) < 0) {
229 close(fd);
230 fd = -1;
231 }
232 }
233 }
234 trace_id = tracecmd_generate_traceid();
235 ret = tracecmd_msg_send_trace_resp(msg_handle, nr_cpus, page_size,
236 ports, use_fifos, trace_id,
237 tsync_proto, tsync_port);
238 if (ret < 0)
239 die("Failed to send trace response");
240
241 if (tsync_proto) {
242 fd = wait_for_connection(fd);
243
244 if (rcid >= 0) {
245 tsync = trace_tsync_as_host(fd, trace_id, 0, rcid,
246 client_cpus, tsync_proto,
247 get_clock(argc, argv));
248 } else {
249 tsync = trace_tsync_as_guest(fd, tsync_proto,
250 get_clock(argc, argv),
251 remote_id, local_id);
252 }
253 if (!tsync)
254 close(fd);
255 }
256
257 trace_record_agent(msg_handle, nr_cpus, fds, argc, argv,
258 use_fifos, tsync, trace_id, rcid, network);
259
260 if (tsync) {
261 if (rcid < 0)
262 tracecmd_tsync_with_host_stop(tsync);
263 tracecmd_tsync_free(tsync);
264 }
265
266 if (tsync_protos) {
267 free(tsync_protos->names);
268 free(tsync_protos);
269 }
270 free(argv[0]);
271 free(argv);
272 free(ports);
273 free(fds);
274 tracecmd_msg_handle_close(msg_handle);
275 exit(0);
276 }
277
278 static volatile pid_t handler_pid;
279
handle_sigchld(int sig)280 static void handle_sigchld(int sig)
281 {
282 int wstatus;
283 pid_t pid;
284
285 for (;;) {
286 pid = waitpid(-1, &wstatus, WNOHANG);
287 if (pid <= 0)
288 break;
289
290 if (pid == handler_pid)
291 handler_pid = 0;
292 }
293 }
294
do_fork()295 static pid_t do_fork()
296 {
297 /* in debug mode, we do not fork off children */
298 if (tracecmd_get_debug())
299 return 0;
300
301 return fork();
302 }
303
agent_serve(unsigned int port,bool do_daemon,int proxy_id,const char * network)304 static void agent_serve(unsigned int port, bool do_daemon, int proxy_id,
305 const char *network)
306 {
307 struct sockaddr_storage net_addr;
308 struct sockaddr *addr = NULL;
309 socklen_t *addr_len_p = NULL;
310 socklen_t addr_len = sizeof(net_addr);
311 int sd, cd, nr_cpus;
312 unsigned int cid = -1, rcid = -1;
313 pid_t pid;
314
315 signal(SIGCHLD, handle_sigchld);
316
317 if (network) {
318 addr = (struct sockaddr *)&net_addr;
319 addr_len_p = &addr_len;
320 }
321
322 nr_cpus = tracecmd_count_cpus();
323 page_size = getpagesize();
324
325 if (network) {
326 sd = trace_net_make(port, USE_TCP);
327 if (listen(sd, 5) < 0)
328 die("Failed to listen on %d\n", port);
329 } else
330 sd = trace_vsock_make(port);
331 if (sd < 0)
332 die("Failed to open socket");
333 tracecmd_tsync_init();
334
335 if (!network) {
336 cid = trace_vsock_local_cid();
337 if (cid >= 0)
338 printf("listening on @%u:%u\n", cid, port);
339 }
340
341 if (do_daemon && daemon(1, 0))
342 die("daemon");
343
344 for (;;) {
345 cd = accept(sd, addr, addr_len_p);
346 if (cd < 0) {
347 if (errno == EINTR)
348 continue;
349 die("accept");
350 }
351 if (proxy_id >= 0) {
352 /* Only works with vsockets */
353 if (get_vsocket_params(cd, NULL, &rcid) < 0) {
354 dprint("Failed to find connected cid");
355 close(cd);
356 continue;
357 }
358 if (rcid != proxy_id) {
359 dprint("Cid %d does not match expected cid %d\n",
360 rcid, proxy_id);
361 close(cd);
362 continue;
363 }
364 }
365
366 if (tracecmd_get_debug())
367 trace_print_connection(cd, network);
368
369 if (network && !trace_net_cmp_connection(&net_addr, network)) {
370 dprint("Client does not match '%s'\n", network);
371 close(cd);
372 continue;
373 }
374
375 if (handler_pid)
376 goto busy;
377
378 pid = do_fork();
379 if (pid == 0) {
380 close(sd);
381 signal(SIGCHLD, SIG_DFL);
382 agent_handle(cd, nr_cpus, page_size, cid, rcid, network);
383 }
384 if (pid > 0)
385 handler_pid = pid;
386
387 busy:
388 close(cd);
389 }
390 }
391
392 enum {
393 OPT_verbose = 254,
394 OPT_debug = 255,
395 OPT_notimeout = 256,
396 };
397
trace_agent(int argc,char ** argv)398 void trace_agent(int argc, char **argv)
399 {
400 struct trace_guest *guest;
401 bool do_daemon = false;
402 unsigned int port = TRACE_AGENT_DEFAULT_PORT;
403 const char *network = NULL;
404 int proxy_id = -1;
405
406 if (argc < 2)
407 usage(argv);
408
409 if (strcmp(argv[1], "agent") != 0)
410 usage(argv);
411
412 for (;;) {
413 int c, option_index = 0;
414 static struct option long_options[] = {
415 {"port", required_argument, NULL, 'p'},
416 {"help", no_argument, NULL, '?'},
417 {"debug", no_argument, NULL, OPT_debug},
418 {"notimeout", no_argument, NULL, OPT_notimeout},
419 {"verbose", optional_argument, NULL, OPT_verbose},
420 {NULL, 0, NULL, 0}
421 };
422
423 c = getopt_long(argc-1, argv+1, "+hp:DN:P:",
424 long_options, &option_index);
425 if (c == -1)
426 break;
427 switch (c) {
428 case 'h':
429 usage(argv);
430 break;
431 case 'N':
432 network = optarg;
433 break;
434 case 'p':
435 port = atoi(optarg);
436 if (proxy_id >= 0)
437 die("-N cannot be used with -P");
438 break;
439 case 'D':
440 do_daemon = true;
441 break;
442 case 'P':
443 proxy_id = atoi(optarg);
444
445 guest = trace_get_guest(proxy_id, GUEST_NAME);
446 if (!guest)
447 die("Failed to allocate guest instance");
448
449 break;
450 case OPT_debug:
451 tracecmd_set_debug(true);
452 break;
453 case OPT_notimeout:
454 tracecmd_set_notimeout(true);
455 break;
456 case OPT_verbose:
457 if (trace_set_verbose(optarg) < 0)
458 die("invalid verbose level %s", optarg);
459 break;
460 default:
461 usage(argv);
462 }
463 }
464
465 if (optind < argc-1)
466 usage(argv);
467
468 agent_serve(port, do_daemon, proxy_id, network);
469 }
470