diff options
Diffstat (limited to 'trace-listen.c')
-rw-r--r-- | trace-listen.c | 450 |
1 files changed, 450 insertions, 0 deletions
diff --git a/trace-listen.c b/trace-listen.c new file mode 100644 index 0000000..d1314a5 --- /dev/null +++ b/trace-listen.c | |||
@@ -0,0 +1,450 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2009, 2010 Red Hat Inc, Steven Rostedt <srostedt@redhat.com> | ||
3 | * | ||
4 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; version 2 of the License (not later!) | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program; if not, write to the Free Software | ||
17 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
18 | * | ||
19 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
20 | */ | ||
21 | #define _LARGEFILE64_SOURCE | ||
22 | #define _GNU_SOURCE | ||
23 | #include <dirent.h> | ||
24 | #include <stdio.h> | ||
25 | #include <stdlib.h> | ||
26 | #include <string.h> | ||
27 | #include <getopt.h> | ||
28 | #include <sys/types.h> | ||
29 | #include <sys/socket.h> | ||
30 | #include <netdb.h> | ||
31 | #include <unistd.h> | ||
32 | #include <fcntl.h> | ||
33 | #include <signal.h> | ||
34 | #include <errno.h> | ||
35 | |||
36 | #include "trace-local.h" | ||
37 | |||
38 | static char *default_output_dir = "."; | ||
39 | static char *output_dir; | ||
40 | static char *default_output_file = "trace"; | ||
41 | static char *output_file; | ||
42 | |||
43 | static int backlog = 5; | ||
44 | |||
45 | #define TEMP_FILE_STR "%s.%s:%s.cpu%d", output_file, host, port, cpu | ||
46 | static char *get_temp_file(const char *host, const char *port, int cpu) | ||
47 | { | ||
48 | char *file = NULL; | ||
49 | int size; | ||
50 | |||
51 | size = snprintf(file, 0, TEMP_FILE_STR); | ||
52 | file = malloc_or_die(size + 1); | ||
53 | sprintf(file, TEMP_FILE_STR); | ||
54 | |||
55 | return file; | ||
56 | } | ||
57 | |||
58 | static void put_temp_file(char *file) | ||
59 | { | ||
60 | free(file); | ||
61 | } | ||
62 | |||
63 | #define MAX_PATH 1024 | ||
64 | |||
65 | static void delete_temp_file(const char *host, const char *port, int cpu) | ||
66 | { | ||
67 | char file[MAX_PATH]; | ||
68 | |||
69 | snprintf(file, MAX_PATH, TEMP_FILE_STR); | ||
70 | unlink(file); | ||
71 | } | ||
72 | |||
73 | static int read_string(int fd, char *buf, size_t size) | ||
74 | { | ||
75 | size_t i; | ||
76 | int n; | ||
77 | |||
78 | for (i = 0; i < size; i++) { | ||
79 | n = read(fd, buf+i, 1); | ||
80 | if (!buf[i] || n <= 0) | ||
81 | break; | ||
82 | } | ||
83 | |||
84 | return i; | ||
85 | } | ||
86 | |||
87 | static int done; | ||
88 | static void finish(int sig) | ||
89 | { | ||
90 | done = 1; | ||
91 | } | ||
92 | |||
93 | static void process_udp_child(int sfd, const char *host, const char *port, | ||
94 | int cpu, int page_size) | ||
95 | { | ||
96 | struct sockaddr_storage peer_addr; | ||
97 | socklen_t peer_addr_len; | ||
98 | char buf[page_size]; | ||
99 | char *tempfile; | ||
100 | int fd; | ||
101 | int n; | ||
102 | int once = 0; | ||
103 | |||
104 | signal(SIGUSR1, finish); | ||
105 | |||
106 | tempfile = get_temp_file(host, port, cpu); | ||
107 | fd = open(tempfile, O_WRONLY | O_TRUNC | O_CREAT, 0644); | ||
108 | if (fd < 0) | ||
109 | die("creating %s", tempfile); | ||
110 | |||
111 | do { | ||
112 | peer_addr_len = sizeof(peer_addr); | ||
113 | /* TODO, make this copyless! */ | ||
114 | n = read(sfd, buf, page_size); | ||
115 | #if 0 | ||
116 | n = recvfrom(sfd, buf, page_size, 0, | ||
117 | (struct sockaddr *)&peer_addr, &peer_addr_len); | ||
118 | #endif | ||
119 | if (!n) | ||
120 | break; | ||
121 | if (n < page_size && !once) { | ||
122 | once = 1; | ||
123 | warning("read %d bytes, expected %d", n, page_size); | ||
124 | } | ||
125 | write(fd, buf, n); | ||
126 | } while (!done); | ||
127 | |||
128 | put_temp_file(tempfile); | ||
129 | exit(0); | ||
130 | } | ||
131 | |||
132 | #define START_PORT_SEARCH 1500 | ||
133 | #define MAX_PORT_SEARCH 6000 | ||
134 | |||
135 | static int open_udp(const char *node, const char *port, int *pid, | ||
136 | int cpu, int pagesize) | ||
137 | { | ||
138 | struct addrinfo hints; | ||
139 | struct addrinfo *result, *rp; | ||
140 | int sfd, s; | ||
141 | char buf[BUFSIZ]; | ||
142 | int num_port = START_PORT_SEARCH; | ||
143 | |||
144 | again: | ||
145 | snprintf(buf, BUFSIZ, "%d", num_port); | ||
146 | |||
147 | memset(&hints, 0, sizeof(hints)); | ||
148 | hints.ai_family = AF_UNSPEC; | ||
149 | hints.ai_socktype = SOCK_DGRAM; | ||
150 | hints.ai_flags = AI_PASSIVE; | ||
151 | |||
152 | s = getaddrinfo(NULL, buf, &hints, &result); | ||
153 | if (s != 0) | ||
154 | die("getaddrinfo: error opening udp socket"); | ||
155 | |||
156 | for (rp = result; rp != NULL; rp = rp->ai_next) { | ||
157 | sfd = socket(rp->ai_family, rp->ai_socktype, | ||
158 | rp->ai_protocol); | ||
159 | if (sfd < 0) | ||
160 | continue; | ||
161 | |||
162 | if (bind(sfd, rp->ai_addr, rp->ai_addrlen) == 0) | ||
163 | break; | ||
164 | |||
165 | close(sfd); | ||
166 | } | ||
167 | |||
168 | if (rp == NULL) { | ||
169 | freeaddrinfo(result); | ||
170 | if (++num_port > MAX_PORT_SEARCH) | ||
171 | die("No available ports to bind"); | ||
172 | goto again; | ||
173 | } | ||
174 | |||
175 | freeaddrinfo(result); | ||
176 | |||
177 | *pid = fork(); | ||
178 | |||
179 | if (*pid < 0) | ||
180 | die("creating udp reader"); | ||
181 | |||
182 | if (!*pid) | ||
183 | process_udp_child(sfd, node, port, cpu, pagesize); | ||
184 | |||
185 | close(sfd); | ||
186 | |||
187 | return num_port; | ||
188 | } | ||
189 | |||
190 | static void process_client(const char *node, const char *port, int fd) | ||
191 | { | ||
192 | char **temp_files; | ||
193 | char buf[BUFSIZ]; | ||
194 | int *port_array; | ||
195 | int *pid_array; | ||
196 | int pagesize; | ||
197 | int udp_port; | ||
198 | int cpus; | ||
199 | int cpu; | ||
200 | int pid; | ||
201 | int ofd; | ||
202 | int n, s, t; | ||
203 | |||
204 | /* Let the client know what we are */ | ||
205 | write(fd, "tracecmd", 8); | ||
206 | |||
207 | /* read back the CPU count */ | ||
208 | n = read_string(fd, buf, BUFSIZ); | ||
209 | if (n == BUFSIZ) | ||
210 | /** ERROR **/ | ||
211 | return; | ||
212 | |||
213 | cpus = atoi(buf); | ||
214 | |||
215 | printf("cpus=%d\n", cpus); | ||
216 | if (cpus < 0) | ||
217 | return; | ||
218 | |||
219 | /* next read the page size */ | ||
220 | n = read_string(fd, buf, BUFSIZ); | ||
221 | if (n == BUFSIZ) | ||
222 | /** ERROR **/ | ||
223 | return; | ||
224 | |||
225 | pagesize = atoi(buf); | ||
226 | |||
227 | printf("pagesize=%d\n", pagesize); | ||
228 | if (pagesize <= 0) | ||
229 | return; | ||
230 | |||
231 | /* Create the client file */ | ||
232 | snprintf(buf, BUFSIZ, "%s.%s:%s.dat", output_file, node, port); | ||
233 | |||
234 | ofd = open(buf, O_RDWR | O_CREAT | O_TRUNC, 0644); | ||
235 | if (ofd < 0) | ||
236 | die("Can not create file %s", buf); | ||
237 | |||
238 | port_array = malloc_or_die(sizeof(int) * cpus); | ||
239 | pid_array = malloc_or_die(sizeof(int) * cpus); | ||
240 | memset(pid_array, 0, sizeof(int) * cpus); | ||
241 | |||
242 | /* Now create a UDP port for each CPU */ | ||
243 | for (cpu = 0; cpu < cpus; cpu++) { | ||
244 | udp_port = open_udp(node, port, &pid, cpu, pagesize); | ||
245 | if (udp_port < 0) | ||
246 | goto out_free; | ||
247 | port_array[cpu] = udp_port; | ||
248 | pid_array[cpu] = pid; | ||
249 | } | ||
250 | |||
251 | /* send the client a comma deliminated set of port numbers */ | ||
252 | for (cpu = 0; cpu < cpus; cpu++) { | ||
253 | snprintf(buf, BUFSIZ, "%s%d", | ||
254 | cpu ? "," : "", port_array[cpu]); | ||
255 | write(fd, buf, strlen(buf)); | ||
256 | } | ||
257 | /* end with null terminator */ | ||
258 | write(fd, "\0", 1); | ||
259 | |||
260 | /* Now we are ready to start reading data from the client */ | ||
261 | do { | ||
262 | n = read(fd, buf, BUFSIZ); | ||
263 | t = n; | ||
264 | s = 0; | ||
265 | do { | ||
266 | s = write(ofd, buf+s, t); | ||
267 | if (s < 0) | ||
268 | die("writing to file"); | ||
269 | t -= s; | ||
270 | s = n - t; | ||
271 | } while (t); | ||
272 | } while (n > 0); | ||
273 | |||
274 | /* wait a little to let our readers finish reading */ | ||
275 | sleep(1); | ||
276 | |||
277 | /* stop our readers */ | ||
278 | for (cpu = 0; cpu < cpus; cpu++) { | ||
279 | if (pid_array[cpu] > 0) | ||
280 | kill(pid_array[cpu], SIGUSR1); | ||
281 | } | ||
282 | |||
283 | /* wait a little to have the readers clean up */ | ||
284 | sleep(1); | ||
285 | |||
286 | /* Now put together the file */ | ||
287 | temp_files = malloc_or_die(sizeof(*temp_files) * cpus); | ||
288 | |||
289 | for (cpu = 0; cpu < cpus; cpu++) | ||
290 | temp_files[cpu] = get_temp_file(node, port, cpu); | ||
291 | |||
292 | tracecmd_attach_cpu_data_fd(ofd, cpus, temp_files); | ||
293 | |||
294 | out_free: | ||
295 | for (cpu = 0; cpu < cpus; cpu++) { | ||
296 | if (pid_array[cpu] > 0) { | ||
297 | kill(pid_array[cpu], SIGKILL); | ||
298 | delete_temp_file(node, port, cpu); | ||
299 | pid_array[cpu] = 0; | ||
300 | } | ||
301 | } | ||
302 | } | ||
303 | |||
304 | static void do_listen(char *port) | ||
305 | { | ||
306 | struct addrinfo hints; | ||
307 | struct addrinfo *result, *rp; | ||
308 | int sfd, s, cfd; | ||
309 | struct sockaddr_storage peer_addr; | ||
310 | socklen_t peer_addr_len; | ||
311 | ssize_t nread; | ||
312 | char buf[BUFSIZ]; | ||
313 | char host[NI_MAXHOST], service[NI_MAXSERV]; | ||
314 | |||
315 | memset(&hints, 0, sizeof(hints)); | ||
316 | hints.ai_family = AF_UNSPEC; | ||
317 | hints.ai_socktype = SOCK_STREAM; | ||
318 | hints.ai_flags = AI_PASSIVE; | ||
319 | |||
320 | s = getaddrinfo(NULL, port, &hints, &result); | ||
321 | if (s != 0) | ||
322 | die("getaddrinfo: error opening %s", port); | ||
323 | |||
324 | for (rp = result; rp != NULL; rp = rp->ai_next) { | ||
325 | sfd = socket(rp->ai_family, rp->ai_socktype, | ||
326 | rp->ai_protocol); | ||
327 | if (sfd < 0) | ||
328 | continue; | ||
329 | |||
330 | if (bind(sfd, rp->ai_addr, rp->ai_addrlen) == 0) | ||
331 | break; | ||
332 | |||
333 | close(sfd); | ||
334 | } | ||
335 | |||
336 | if (rp == NULL) | ||
337 | die("Could not bind"); | ||
338 | |||
339 | freeaddrinfo(result); | ||
340 | |||
341 | if (listen(sfd, backlog) < 0) | ||
342 | die("listen"); | ||
343 | |||
344 | peer_addr_len = sizeof(peer_addr); | ||
345 | |||
346 | do { | ||
347 | cfd = accept(sfd, (struct sockaddr *)&peer_addr, &peer_addr_len); | ||
348 | if (cfd < 0) | ||
349 | die("connecting"); | ||
350 | s = getnameinfo((struct sockaddr *)&peer_addr, peer_addr_len, | ||
351 | host, NI_MAXHOST, | ||
352 | service, NI_MAXSERV, NI_NUMERICSERV); | ||
353 | |||
354 | if (s == 0) | ||
355 | printf("Connected with %s:%s\n", | ||
356 | host, service); | ||
357 | else { | ||
358 | printf("Error with getnameinfo: %s\n", | ||
359 | gai_strerror(s)); | ||
360 | close(cfd); | ||
361 | close(sfd); | ||
362 | return; | ||
363 | } | ||
364 | |||
365 | process_client(host, service, cfd); | ||
366 | |||
367 | do { | ||
368 | if (nread > 0) | ||
369 | nread = read(cfd, buf, BUFSIZ); | ||
370 | if (cfd < 0) | ||
371 | die("client"); | ||
372 | if (nread > 0) | ||
373 | write(1, buf, nread); | ||
374 | } while (nread); | ||
375 | |||
376 | close(cfd); | ||
377 | } while (0); | ||
378 | } | ||
379 | |||
380 | static void start_daemon(void) | ||
381 | { | ||
382 | } | ||
383 | |||
384 | void trace_listen(int argc, char **argv) | ||
385 | { | ||
386 | char *port = NULL; | ||
387 | char *iface; | ||
388 | int daemon = 0; | ||
389 | int c; | ||
390 | |||
391 | if (argc < 2) | ||
392 | usage(argv); | ||
393 | |||
394 | if (strcmp(argv[1], "listen") != 0) | ||
395 | usage(argv); | ||
396 | |||
397 | for (;;) { | ||
398 | int option_index = 0; | ||
399 | static struct option long_options[] = { | ||
400 | {"port", required_argument, NULL, 'p'}, | ||
401 | {"help", no_argument, NULL, '?'}, | ||
402 | {NULL, 0, NULL, 0} | ||
403 | }; | ||
404 | |||
405 | c = getopt_long (argc-1, argv+1, "+hp:o:d:i:D", | ||
406 | long_options, &option_index); | ||
407 | if (c == -1) | ||
408 | break; | ||
409 | switch (c) { | ||
410 | case 'h': | ||
411 | usage(argv); | ||
412 | break; | ||
413 | case 'p': | ||
414 | port = optarg; | ||
415 | break; | ||
416 | case 'i': | ||
417 | iface = optarg; | ||
418 | break; | ||
419 | case 'd': | ||
420 | output_dir = optarg; | ||
421 | break; | ||
422 | case 'o': | ||
423 | output_file = optarg; | ||
424 | break; | ||
425 | case 'D': | ||
426 | daemon = 1; | ||
427 | default: | ||
428 | usage(argv); | ||
429 | } | ||
430 | } | ||
431 | |||
432 | if (!port) | ||
433 | usage(argv); | ||
434 | |||
435 | if ((argc - optind) >= 2) | ||
436 | usage(argv); | ||
437 | |||
438 | if (!output_file) | ||
439 | output_file = default_output_file; | ||
440 | |||
441 | if (!output_dir) | ||
442 | output_dir = default_output_dir; | ||
443 | |||
444 | if (daemon) | ||
445 | start_daemon(); | ||
446 | |||
447 | do_listen(port); | ||
448 | |||
449 | return; | ||
450 | } | ||