• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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