c7c88f6315a0a8987912dc003979d0f8454247bd
[lttng-tools.git] / lttng / lttng.c
1 /*
2 * Copyright (c) 2011 David Goulet <david.goulet@polymtl.ca>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 */
18
19 #define _GNU_SOURCE
20 #include <errno.h>
21 #include <fcntl.h>
22 #include <getopt.h>
23 #include <grp.h>
24 #include <limits.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <sys/stat.h>
29 #include <sys/types.h>
30 #include <sys/wait.h>
31 #include <unistd.h>
32
33 #include <lttng/lttng.h>
34
35 #include "lttng.h"
36 #include "lttngerr.h"
37
38 /* Variables */
39 static char *progname;
40 static char *session_name;
41 static uuid_t current_uuid;
42 static int auto_session;
43 static int auto_trace;
44
45 /* Prototypes */
46 static int process_client_opt(void);
47 static int process_opt_list_apps(void);
48 static int process_opt_list_sessions(void);
49 static int process_opt_list_traces(void);
50 static int process_opt_create_session(void);
51 static int process_kernel_create_trace(void);
52 static int process_opt_kernel_event(void);
53 static int set_session_uuid(void);
54 static void sighandler(int sig);
55 static int set_signal_handler(void);
56 static int validate_options(void);
57 static char *get_cmdline_by_pid(pid_t pid);
58 static void set_opt_session_info(void);
59
60 /*
61 * start_client
62 *
63 * Process client request from the command line
64 * options. Every tracing action is done by the
65 * liblttngctl API.
66 */
67 static int process_client_opt(void)
68 {
69 int ret;
70
71 set_opt_session_info();
72
73 if (opt_list_apps) {
74 ret = process_opt_list_apps();
75 if (ret < 0) {
76 goto end;
77 }
78 goto error;
79 }
80
81 if (opt_list_session) {
82 ret = process_opt_list_sessions();
83 if (ret < 0) {
84 goto end;
85 }
86 goto error;
87 }
88
89 /* Session creation or auto session set on */
90 if (auto_session || opt_create_session) {
91 DBG("Creating a new session");
92 ret = process_opt_create_session();
93 if (ret < 0) {
94 goto end;
95 }
96 }
97
98 ret = set_session_uuid();
99 if (ret < 0) {
100 ERR("Session %s not found", opt_session_name);
101 goto error;
102 }
103
104 if (opt_destroy_session) {
105 ret = lttng_destroy_session(&current_uuid);
106 if (ret < 0) {
107 goto end;
108 }
109 MSG("Session %s destroyed.", opt_session_name);
110 }
111
112 if (opt_list_traces) {
113 ret = process_opt_list_traces();
114 if (ret < 0) {
115 goto end;
116 }
117 }
118
119 /*
120 * Action on traces (kernel or/and userspace).
121 */
122
123 if (opt_trace_kernel) {
124 if (auto_trace || opt_create_trace) {
125 DBG("Creating a kernel trace");
126 ret = process_kernel_create_trace();
127 if (ret < 0) {
128 goto end;
129 }
130 }
131
132 if (opt_event_list != NULL) {
133 ret = process_opt_kernel_event();
134 if (ret < 0) {
135 goto end;
136 }
137 } else {
138 // Enable all events
139 }
140 }
141
142 if (opt_trace_pid != 0) {
143 if (auto_trace || opt_create_trace) {
144 DBG("Create a userspace trace for pid %d", opt_trace_pid);
145 ret = lttng_ust_create_trace(opt_trace_pid);
146 if (ret < 0) {
147 goto end;
148 }
149 MSG("Trace created successfully!");
150 }
151
152 if (auto_trace || opt_start_trace) {
153 DBG("Start trace for pid %d", opt_trace_pid);
154 ret = lttng_ust_start_trace(opt_trace_pid);
155 if (ret < 0) {
156 goto end;
157 }
158 MSG("Trace started successfully!");
159 } else if (opt_stop_trace) {
160 DBG("Stop trace for pid %d", opt_trace_pid);
161 ret = lttng_ust_stop_trace(opt_trace_pid);
162 if (ret < 0) {
163 goto end;
164 }
165 MSG("Trace stopped successfully!");
166 }
167
168 }
169
170 return 0;
171
172 end:
173 ERR("%s", lttng_get_readable_code(ret));
174 error: /* fall through */
175 return ret;
176 }
177
178 /*
179 * process_kernel_create_trace
180 *
181 * Create a kernel trace.
182 */
183 static int process_kernel_create_trace(void)
184 {
185 int ret;
186
187 /* Setup kernel session */
188 ret = lttng_kernel_create_session();
189 if (ret < 0) {
190 goto error;
191 }
192
193 /* Create an empty channel (with no event) */
194 ret = lttng_kernel_create_channel();
195 if (ret < 0) {
196 goto error;
197 }
198
199 return 0;
200
201 error:
202 return ret;
203 }
204
205 /*
206 * process_kernel_event
207 *
208 * Enable kernel event from the command line list given.
209 */
210 static int process_opt_kernel_event(void)
211 {
212 int ret;
213 char *event_name;
214
215 event_name = strtok(opt_event_list, ",");
216 while (event_name != NULL) {
217 DBG("Enabling kernel event %s", event_name);
218 ret = lttng_kernel_enable_event(event_name);
219 if (ret < 0) {
220 ERR("%s %s", lttng_get_readable_code(ret), event_name);
221 } else {
222 MSG("Kernel event %s enabled.", event_name);
223 }
224 /* Next event */
225 event_name = strtok(NULL, ",");
226 }
227
228 return 0;
229 }
230
231 /*
232 * set_opt_session_info
233 *
234 * Setup session_name, current_uuid, short_str_uuid and
235 * long_str_uuid using the command line options.
236 */
237 static void set_opt_session_info(void)
238 {
239 if (opt_session_name != NULL) {
240 session_name = strndup(opt_session_name, NAME_MAX);
241 DBG("Session name set to %s", session_name);
242 }
243 }
244
245 /*
246 * set_session_uuid
247 *
248 * Set current session uuid to the current flow of command(s) using the
249 * session_name.
250 */
251 static int set_session_uuid(void)
252 {
253 int ret, count, i, found = 0;
254 struct lttng_session *sessions;
255
256 if (!uuid_is_null(current_uuid)) {
257 lttng_set_current_session_uuid(&current_uuid);
258 goto end;
259 }
260
261 count = lttng_list_sessions(&sessions);
262 if (count < 0) {
263 ret = count;
264 goto error;
265 }
266
267 for (i = 0; i < count; i++) {
268 if (strncmp(sessions[i].name, session_name, NAME_MAX) == 0) {
269 lttng_set_current_session_uuid(&sessions[i].uuid);
270 uuid_copy(current_uuid, sessions[i].uuid);
271 found = 1;
272 break;
273 }
274 }
275
276 free(sessions);
277
278 if (!found) {
279 return -1;
280 }
281
282 end:
283 DBG("Session UUID set");
284 return 0;
285
286 error:
287 return ret;
288 }
289
290 /*
291 * process_opt_list_traces
292 *
293 * Get list of all traces for a specific session uuid.
294 */
295 static int process_opt_list_traces(void)
296 {
297 int ret, i;
298 struct lttng_trace *traces;
299
300 ret = lttng_list_traces(&current_uuid, &traces);
301 DBG("Number of traces to list %d", ret);
302 if (ret < 0) {
303 goto error;
304 }
305
306 /* No traces */
307 if (ret == 0) {
308 MSG("No traces found.");
309 goto error;
310 }
311
312 MSG("Userspace traces:");
313 for (i = 0; i < ret; i++) {
314 if (traces[i].type == USERSPACE) {
315 MSG("\t%d) %s (pid: %d): %s",
316 i, traces[i].name, traces[i].pid,
317 get_cmdline_by_pid(traces[i].pid));
318 } else {
319 break;
320 }
321 }
322
323 MSG("Kernel traces:");
324 for (;i < ret; i++) {
325 if (traces[i].type == KERNEL) {
326 MSG("\t%d) %s", i, traces[i].name);
327 }
328 }
329
330 free(traces);
331
332 error:
333 return ret;
334 }
335
336 /*
337 * process_opt_create_session
338 *
339 * Create a new session using the name pass
340 * to the command line.
341 */
342 static int process_opt_create_session(void)
343 {
344 int ret;
345 char name[NAME_MAX];
346 time_t rawtime;
347 struct tm *timeinfo;
348
349 /* Auto session name creation */
350 if (opt_session_name == NULL) {
351 time(&rawtime);
352 timeinfo = localtime(&rawtime);
353 strftime(name, sizeof(name), "auto-%Y%m%d-%H%M%S", timeinfo);
354 session_name = strndup(name, sizeof(name));
355 DBG("Auto session name set to %s", session_name);
356 }
357
358 ret = lttng_create_session(session_name);
359 if (ret < 0) {
360 goto error;
361 }
362
363 MSG("Session created: %s", session_name);
364
365 error:
366 return ret;
367 }
368
369 /*
370 * process_opt_list_sessions
371 *
372 * Get the list of available sessions from
373 * the session daemon and print it to user.
374 */
375 static int process_opt_list_sessions(void)
376 {
377 int ret, count, i;
378 struct lttng_session *sessions;
379
380 count = lttng_list_sessions(&sessions);
381 DBG("Session count %d", count);
382 if (count < 0) {
383 ret = count;
384 goto error;
385 }
386
387 MSG("Available sessions (UUIDs):");
388 for (i = 0; i < count; i++) {
389 MSG(" %d) %s", i+1, sessions[i].name);
390 }
391
392 free(sessions);
393 MSG("\nTo select a session, use -s, --session UUID.");
394
395 return 0;
396
397 error:
398 return ret;
399 }
400
401 /*
402 * process_opt_list_apps
403 *
404 * Get the UST traceable pid list and print
405 * them to the user.
406 */
407 static int process_opt_list_apps(void)
408 {
409 int i, ret, count;
410 pid_t *pids;
411 char *cmdline;
412
413 count = lttng_ust_list_apps(&pids);
414 if (count < 0) {
415 ret = count;
416 goto error;
417 }
418
419 MSG("LTTng UST traceable application [name (pid)]:");
420 for (i=0; i < count; i++) {
421 cmdline = get_cmdline_by_pid(pids[i]);
422 if (cmdline == NULL) {
423 MSG("\t(not running) (%d)", pids[i]);
424 continue;
425 }
426 MSG("\t%s (%d)", cmdline, pids[i]);
427 free(cmdline);
428 }
429
430 /* Allocated by lttng_ust_list_apps() */
431 free(pids);
432
433 return 0;
434
435 error:
436 return ret;
437 }
438
439 /*
440 * get_cmdline_by_pid
441 *
442 * Get command line from /proc for a specific pid.
443 *
444 * On success, return an allocated string pointer pointing to
445 * the proc cmdline.
446 * On error, return NULL.
447 */
448 static char *get_cmdline_by_pid(pid_t pid)
449 {
450 int ret;
451 FILE *fp;
452 char *cmdline = NULL;
453 char path[24]; /* Can't go bigger than /proc/65535/cmdline */
454
455 snprintf(path, sizeof(path), "/proc/%d/cmdline", pid);
456 fp = fopen(path, "r");
457 if (fp == NULL) {
458 goto end;
459 }
460
461 /* Caller must free() *cmdline */
462 cmdline = malloc(PATH_MAX);
463 ret = fread(cmdline, 1, PATH_MAX, fp);
464 fclose(fp);
465
466 end:
467 return cmdline;
468 }
469
470 /*
471 * validate_options
472 *
473 * Make sure that all options passed to the command line are compatible with
474 * each others.
475 *
476 * On error, return -1
477 * On success, return 0
478 */
479 static int validate_options(void)
480 {
481 /* If listing options, jump validation */
482 if (opt_list_apps || opt_list_session) {
483 goto end;
484 }
485 /* Conflicting command */
486 if (opt_start_trace && opt_stop_trace) {
487 ERR("Can't use --start and --stop together.");
488 goto error;
489 /* If no PID specified and trace_kernel is off */
490 } else if ((opt_trace_pid == 0 && !opt_trace_kernel) &&
491 (opt_create_trace || opt_start_trace || opt_stop_trace || opt_destroy_trace)) {
492 ERR("Please specify for which tracer (-k or -p PID).");
493 goto error;
494 /* List traces, we need a session name */
495 } else if (opt_list_traces && opt_session_name == NULL) {
496 ERR("Can't use -t without -s, --session option.");
497 goto error;
498 /* Can't set event for both kernel and userspace at the same time */
499 } else if (opt_event_list != NULL && (opt_trace_kernel && opt_trace_pid)) {
500 ERR("Please don't use --event for both kernel and userspace.\nOne at a time to enable events.");
501 goto error;
502 /* Don't need a trace name for kernel tracig */
503 } else if (opt_trace_name != NULL && opt_trace_kernel) {
504 ERR("For action on a kernel trace, please don't specify a trace name.");
505 goto error;
506 } else if (opt_destroy_trace && opt_session_name == NULL) {
507 ERR("Please specify a session in order to destroy a trace");
508 goto error;
509 } else if (opt_create_trace || opt_destroy_trace) {
510 /* Both kernel and user-space are denied for these options */
511 if (opt_trace_pid != 0 && opt_trace_kernel) {
512 ERR("Kernel and user-space trace creation and destruction can't be used together.");
513 goto error;
514 /* Need a trace name for user-space tracing */
515 } else if (opt_trace_name == NULL && opt_trace_pid != 0) {
516 ERR("Please specify a trace name for user-space tracing");
517 goto error;
518 }
519 } else if (opt_stop_trace && opt_trace_pid != 0 && opt_trace_name == NULL) {
520 ERR("Please specify a trace name for user-space tracing");
521 goto error;
522 }
523
524 /* If start trace, auto start tracing */
525 if (opt_start_trace || opt_event_list != NULL) {
526 DBG("Requesting auto tracing");
527 auto_trace = 1;
528 }
529
530 /* If no session, auto create one */
531 if (opt_session_name == NULL) {
532 DBG("Requesting an auto session creation");
533 auto_session = 1;
534 }
535
536 end:
537 return 0;
538
539 error:
540 return -1;
541 }
542
543 /*
544 * spawn_sessiond
545 *
546 * Spawn a session daemon by forking and execv.
547 */
548 static int spawn_sessiond(char *pathname)
549 {
550 int ret = 0;
551 pid_t pid;
552
553 MSG("Spawning session daemon");
554 pid = fork();
555 if (pid == 0) {
556 /*
557 * Spawn session daemon and tell
558 * it to signal us when ready.
559 */
560 execlp(pathname, "ltt-sessiond", "--sig-parent", "--quiet", NULL);
561 /* execlp only returns if error happened */
562 if (errno == ENOENT) {
563 ERR("No session daemon found. Use --sessiond-path.");
564 } else {
565 perror("execlp");
566 }
567 kill(getppid(), SIGTERM); /* unpause parent */
568 exit(EXIT_FAILURE);
569 } else if (pid > 0) {
570 /* Wait for ltt-sessiond to start */
571 pause();
572 goto end;
573 } else {
574 perror("fork");
575 ret = -1;
576 goto end;
577 }
578
579 end:
580 return ret;
581 }
582
583 /*
584 * check_ltt_sessiond
585 *
586 * Check if the session daemon is available using
587 * the liblttngctl API for the check. If not, try to
588 * spawn a daemon.
589 */
590 static int check_ltt_sessiond(void)
591 {
592 int ret;
593 char *pathname = NULL, *alloc_pathname = NULL;
594
595 ret = lttng_check_session_daemon();
596 if (ret < 0) {
597 /* Try command line option path */
598 if (opt_sessiond_path != NULL) {
599 ret = access(opt_sessiond_path, F_OK | X_OK);
600 if (ret < 0) {
601 ERR("No such file: %s", opt_sessiond_path);
602 goto end;
603 }
604 pathname = opt_sessiond_path;
605 } else {
606 /* Try LTTNG_SESSIOND_PATH env variable */
607 pathname = getenv(LTTNG_SESSIOND_PATH_ENV);
608 }
609
610 /* Let's rock and roll */
611 if (pathname == NULL) {
612 ret = asprintf(&alloc_pathname, "ltt-sessiond");
613 if (ret < 0) {
614 goto end;
615 }
616 pathname = alloc_pathname;
617 }
618
619 ret = spawn_sessiond(pathname);
620 free(alloc_pathname);
621 if (ret < 0) {
622 ERR("Problem occurs when starting %s", pathname);
623 goto end;
624 }
625 }
626
627 end:
628 return ret;
629 }
630
631 /*
632 * set_signal_handler
633 *
634 * Setup signal handler for SIGCHLD and SIGTERM.
635 */
636 static int set_signal_handler(void)
637 {
638 int ret = 0;
639 struct sigaction sa;
640 sigset_t sigset;
641
642 if ((ret = sigemptyset(&sigset)) < 0) {
643 perror("sigemptyset");
644 goto end;
645 }
646
647 sa.sa_handler = sighandler;
648 sa.sa_mask = sigset;
649 sa.sa_flags = 0;
650 if ((ret = sigaction(SIGCHLD, &sa, NULL)) < 0) {
651 perror("sigaction");
652 goto end;
653 }
654
655 if ((ret = sigaction(SIGTERM, &sa, NULL)) < 0) {
656 perror("sigaction");
657 goto end;
658 }
659
660 end:
661 return ret;
662 }
663
664 /*
665 * sighandler
666 *
667 * Signal handler for the daemon
668 */
669 static void sighandler(int sig)
670 {
671 switch (sig) {
672 case SIGTERM:
673 DBG("SIGTERM catched");
674 clean_exit(EXIT_FAILURE);
675 break;
676 case SIGCHLD:
677 /* Notify is done */
678 DBG("SIGCHLD catched");
679 break;
680 default:
681 DBG("Unknown signal %d catched", sig);
682 break;
683 }
684
685 return;
686 }
687
688 /*
689 * clean_exit
690 */
691 void clean_exit(int code)
692 {
693 DBG("Clean exit");
694 if (session_name) {
695 free(session_name);
696 }
697
698 exit(code);
699 }
700
701 /*
702 * main
703 */
704 int main(int argc, char *argv[])
705 {
706 int ret;
707
708 progname = argv[0] ? argv[0] : "lttng";
709
710 /* For Mathieu Desnoyers aka Dr Tracing */
711 if (strncmp(progname, "drtrace", 7) == 0) {
712 MSG("%c[%d;%dmWelcome back Dr Tracing!%c[%dm\n\n", 27,1,33,27,0);
713 }
714
715 ret = parse_args(argc, (const char **) argv);
716 if (ret < 0) {
717 clean_exit(EXIT_FAILURE);
718 }
719
720 ret = validate_options();
721 if (ret < 0) {
722 return EXIT_FAILURE;
723 }
724
725 ret = set_signal_handler();
726 if (ret < 0) {
727 clean_exit(ret);
728 }
729
730 if (opt_tracing_group != NULL) {
731 DBG("Set tracing group to '%s'", opt_tracing_group);
732 lttng_set_tracing_group(opt_tracing_group);
733 }
734
735 /* If ask for kernel tracing, need root perms */
736 if (opt_trace_kernel) {
737 DBG("Kernel tracing activated");
738 if (getuid() != 0) {
739 ERR("%s must be setuid root", progname);
740 clean_exit(-EPERM);
741 }
742 }
743
744 /* Check if the lttng session daemon is running.
745 * If no, a daemon will be spawned.
746 */
747 if (opt_no_sessiond == 0 && (check_ltt_sessiond() < 0)) {
748 clean_exit(EXIT_FAILURE);
749 }
750
751 ret = process_client_opt();
752 if (ret < 0) {
753 clean_exit(ret);
754 }
755
756 clean_exit(0);
757
758 return 0;
759 }
This page took 0.068527 seconds and 3 git commands to generate.