aboutsummaryrefslogtreecommitdiffstats
path: root/trace-listen.c
diff options
context:
space:
mode:
Diffstat (limited to 'trace-listen.c')
-rw-r--r--trace-listen.c450
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
38static char *default_output_dir = ".";
39static char *output_dir;
40static char *default_output_file = "trace";
41static char *output_file;
42
43static int backlog = 5;
44
45#define TEMP_FILE_STR "%s.%s:%s.cpu%d", output_file, host, port, cpu
46static 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
58static void put_temp_file(char *file)
59{
60 free(file);
61}
62
63#define MAX_PATH 1024
64
65static 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
73static 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
87static int done;
88static void finish(int sig)
89{
90 done = 1;
91}
92
93static 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
135static 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
190static 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
304static 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
380static void start_daemon(void)
381{
382}
383
384void 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}