1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Copyright (C) 2018 VMware Inc, Slavomir Kaslev <[email protected]>
4 *
5 * based on prior implementation by Yoshihiro Yunomae
6 * Copyright (C) 2013 Hitachi, Ltd.
7 * Yoshihiro YUNOMAE <[email protected]>
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 dprint(fmt, ...) tracecmd_debug(fmt, ##__VA_ARGS__)
26
make_vsocks(int nr,int * fds,unsigned int * ports)27 static void make_vsocks(int nr, int *fds, unsigned int *ports)
28 {
29 unsigned int port;
30 int i, fd, ret;
31
32 for (i = 0; i < nr; i++) {
33 fd = trace_vsock_make_any();
34 if (fd < 0)
35 die("Failed to open vsocket");
36
37 ret = trace_vsock_get_port(fd, &port);
38 if (ret < 0)
39 die("Failed to get vsocket address");
40
41 fds[i] = fd;
42 ports[i] = port;
43 }
44 }
45
make_net(int nr,int * fds,unsigned int * ports)46 static void make_net(int nr, int *fds, unsigned int *ports)
47 {
48 int port;
49 int i, fd;
50 int start_port = START_PORT_SEARCH;
51
52 for (i = 0; i < nr; i++) {
53 port = trace_net_search(start_port, &fd, USE_TCP);
54 if (port < 0)
55 die("Failed to open socket");
56 if (listen(fd, 5) < 0)
57 die("Failed to listen on port %d\n", port);
58 fds[i] = fd;
59 ports[i] = port;
60 dprint("CPU[%d]: fd:%d port:%d\n", i, fd, port);
61 start_port = port + 1;
62 }
63 }
64
make_sockets(int nr,int * fds,unsigned int * ports,const char * network)65 static void make_sockets(int nr, int *fds, unsigned int *ports,
66 const char * network)
67 {
68 if (network)
69 return make_net(nr, fds, ports);
70 else
71 return make_vsocks(nr, fds, ports);
72 }
73
open_agent_fifos(int nr_cpus,int * fds)74 static int open_agent_fifos(int nr_cpus, int *fds)
75 {
76 char path[PATH_MAX];
77 int i, fd, ret;
78
79 for (i = 0; i < nr_cpus; i++) {
80 snprintf(path, sizeof(path), VIRTIO_FIFO_FMT, i);
81 fd = open(path, O_WRONLY);
82 if (fd < 0) {
83 ret = -errno;
84 goto cleanup;
85 }
86
87 fds[i] = fd;
88 }
89
90 return 0;
91
92 cleanup:
93 while (--i >= 0)
94 close(fds[i]);
95
96 return ret;
97 }
98
get_clock(int argc,char ** argv)99 static char *get_clock(int argc, char **argv)
100 {
101 int i;
102
103 if (!argc || !argv)
104 return NULL;
105
106 for (i = 0; i < argc - 1; i++) {
107 if (!strcmp("-C", argv[i]))
108 return argv[i+1];
109 }
110 return NULL;
111 }
112
trace_print_connection(int fd,const char * network)113 static void trace_print_connection(int fd, const char *network)
114 {
115 int ret;
116
117 if (network)
118 ret = trace_net_print_connection(fd);
119 else
120 ret = trace_vsock_print_connection(fd);
121 if (ret < 0)
122 tracecmd_debug("Could not print connection fd:%d\n", fd);
123 }
124
agent_handle(int sd,int nr_cpus,int page_size,const char * network)125 static void agent_handle(int sd, int nr_cpus, int page_size, const char *network)
126 {
127 struct tracecmd_tsync_protos *tsync_protos = NULL;
128 struct tracecmd_time_sync *tsync = NULL;
129 struct tracecmd_msg_handle *msg_handle;
130 char *tsync_proto = NULL;
131 unsigned long long trace_id;
132 unsigned int remote_id;
133 unsigned int local_id;
134 unsigned int tsync_port = 0;
135 unsigned int *ports;
136 char **argv = NULL;
137 int argc = 0;
138 bool use_fifos;
139 int *fds;
140 int ret;
141 int fd;
142
143 fds = calloc(nr_cpus, sizeof(*fds));
144 ports = calloc(nr_cpus, sizeof(*ports));
145 if (!fds || !ports)
146 die("Failed to allocate memory");
147
148 msg_handle = tracecmd_msg_handle_alloc(sd, 0);
149 if (!msg_handle)
150 die("Failed to allocate message handle");
151
152 ret = tracecmd_msg_recv_trace_req(msg_handle, &argc, &argv,
153 &use_fifos, &trace_id,
154 &tsync_protos);
155 if (ret < 0)
156 die("Failed to receive trace request");
157
158 if (use_fifos && open_agent_fifos(nr_cpus, fds))
159 use_fifos = false;
160
161 if (!use_fifos)
162 make_sockets(nr_cpus, fds, ports, network);
163 if (tsync_protos && tsync_protos->names) {
164 if (network) {
165 /* For now just use something */
166 remote_id = 2;
167 local_id = 1;
168 tsync_port = trace_net_search(START_PORT_SEARCH, &fd, USE_TCP);
169 if (listen(fd, 5) < 0)
170 die("Failed to listen on %d\n", tsync_port);
171 } else {
172 if (get_vsocket_params(msg_handle->fd, &local_id,
173 &remote_id)) {
174 warning("Failed to get local and remote ids");
175 /* Just make something up */
176 remote_id = -1;
177 local_id = -2;
178 }
179 fd = trace_vsock_make_any();
180 if (fd >= 0 &&
181 trace_vsock_get_port(fd, &tsync_port) < 0) {
182 close(fd);
183 fd = -1;
184 }
185 }
186 if (fd >= 0) {
187 tsync = tracecmd_tsync_with_host(fd, tsync_protos,
188 get_clock(argc, argv),
189 remote_id, local_id);
190 }
191 if (tsync) {
192 tracecmd_tsync_get_selected_proto(tsync, &tsync_proto);
193 } else {
194 warning("Failed to negotiate timestamps synchronization with the host");
195 if (fd >= 0)
196 close(fd);
197 }
198 }
199 trace_id = tracecmd_generate_traceid();
200 ret = tracecmd_msg_send_trace_resp(msg_handle, nr_cpus, page_size,
201 ports, use_fifos, trace_id,
202 tsync_proto, tsync_port);
203 if (ret < 0)
204 die("Failed to send trace response");
205
206 trace_record_agent(msg_handle, nr_cpus, fds, argc, argv,
207 use_fifos, trace_id, network);
208
209 if (tsync) {
210 tracecmd_tsync_with_host_stop(tsync);
211 tracecmd_tsync_free(tsync);
212 }
213
214 if (tsync_protos) {
215 free(tsync_protos->names);
216 free(tsync_protos);
217 }
218 free(argv[0]);
219 free(argv);
220 free(ports);
221 free(fds);
222 tracecmd_msg_handle_close(msg_handle);
223 exit(0);
224 }
225
226 static volatile pid_t handler_pid;
227
handle_sigchld(int sig)228 static void handle_sigchld(int sig)
229 {
230 int wstatus;
231 pid_t pid;
232
233 for (;;) {
234 pid = waitpid(-1, &wstatus, WNOHANG);
235 if (pid <= 0)
236 break;
237
238 if (pid == handler_pid)
239 handler_pid = 0;
240 }
241 }
242
do_fork()243 static pid_t do_fork()
244 {
245 /* in debug mode, we do not fork off children */
246 if (tracecmd_get_debug())
247 return 0;
248
249 return fork();
250 }
251
agent_serve(unsigned int port,bool do_daemon,const char * network)252 static void agent_serve(unsigned int port, bool do_daemon, const char *network)
253 {
254 struct sockaddr_storage net_addr;
255 struct sockaddr *addr = NULL;
256 socklen_t *addr_len_p = NULL;
257 socklen_t addr_len = sizeof(net_addr);
258 int sd, cd, nr_cpus;
259 unsigned int cid;
260 pid_t pid;
261
262 signal(SIGCHLD, handle_sigchld);
263
264 if (network) {
265 addr = (struct sockaddr *)&net_addr;
266 addr_len_p = &addr_len;
267 }
268
269 nr_cpus = tracecmd_count_cpus();
270 page_size = getpagesize();
271
272 if (network) {
273 sd = trace_net_make(port, USE_TCP);
274 if (listen(sd, 5) < 0)
275 die("Failed to listen on %d\n", port);
276 } else
277 sd = trace_vsock_make(port);
278 if (sd < 0)
279 die("Failed to open socket");
280 tracecmd_tsync_init();
281
282 if (!network) {
283 cid = trace_vsock_local_cid();
284 if (cid >= 0)
285 printf("listening on @%u:%u\n", cid, port);
286 }
287
288 if (do_daemon && daemon(1, 0))
289 die("daemon");
290
291 for (;;) {
292 cd = accept(sd, addr, addr_len_p);
293 if (cd < 0) {
294 if (errno == EINTR)
295 continue;
296 die("accept");
297 }
298 if (tracecmd_get_debug())
299 trace_print_connection(cd, network);
300
301 if (network && !trace_net_cmp_connection(&net_addr, network)) {
302 dprint("Client does not match '%s'\n", network);
303 close(cd);
304 continue;
305 }
306
307 if (handler_pid)
308 goto busy;
309
310 pid = do_fork();
311 if (pid == 0) {
312 close(sd);
313 signal(SIGCHLD, SIG_DFL);
314 agent_handle(cd, nr_cpus, page_size, network);
315 }
316 if (pid > 0)
317 handler_pid = pid;
318
319 busy:
320 close(cd);
321 }
322 }
323
324 enum {
325 OPT_verbose = 254,
326 DO_DEBUG = 255
327 };
328
trace_agent(int argc,char ** argv)329 void trace_agent(int argc, char **argv)
330 {
331 bool do_daemon = false;
332 unsigned int port = TRACE_AGENT_DEFAULT_PORT;
333 const char *network = NULL;
334
335 if (argc < 2)
336 usage(argv);
337
338 if (strcmp(argv[1], "agent") != 0)
339 usage(argv);
340
341 for (;;) {
342 int c, option_index = 0;
343 static struct option long_options[] = {
344 {"port", required_argument, NULL, 'p'},
345 {"help", no_argument, NULL, '?'},
346 {"debug", no_argument, NULL, DO_DEBUG},
347 {"verbose", optional_argument, NULL, OPT_verbose},
348 {NULL, 0, NULL, 0}
349 };
350
351 c = getopt_long(argc-1, argv+1, "+hp:DN:",
352 long_options, &option_index);
353 if (c == -1)
354 break;
355 switch (c) {
356 case 'h':
357 usage(argv);
358 break;
359 case 'N':
360 network = optarg;
361 break;
362 case 'p':
363 port = atoi(optarg);
364 break;
365 case 'D':
366 do_daemon = true;
367 break;
368 case DO_DEBUG:
369 tracecmd_set_debug(true);
370 break;
371 case OPT_verbose:
372 if (trace_set_verbose(optarg) < 0)
373 die("invalid verbose level %s", optarg);
374 break;
375 default:
376 usage(argv);
377 }
378 }
379
380 if (optind < argc-1)
381 usage(argv);
382
383 agent_serve(port, do_daemon, network);
384 }
385