Import kernel control interface
[lttng-tools.git] / lttng / lttng.c
... / ...
CommitLineData
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 */
39static char *progname;
40static char *session_name;
41static char short_str_uuid[UUID_SHORT_STR_LEN];
42static char long_str_uuid[UUID_STR_LEN];
43static uuid_t current_uuid;
44
45/* Prototypes */
46static int process_client_opt(void);
47static int process_opt_list_apps(void);
48static int process_opt_list_sessions(void);
49static int process_opt_list_traces(void);
50static int process_opt_create_session(void);
51static int set_session_uuid(void);
52static void sighandler(int sig);
53static int set_signal_handler(void);
54static int validate_options(void);
55static char *get_cmdline_by_pid(pid_t pid);
56static void set_opt_session_info(void);
57static void shorten_uuid(char *long_u, char *short_u);
58
59/*
60 * start_client
61 *
62 * Process client request from the command line
63 * options. Every tracing action is done by the
64 * liblttngctl API.
65 */
66static int process_client_opt(void)
67{
68 int ret;
69
70 set_opt_session_info();
71
72 if (opt_list_apps) {
73 ret = process_opt_list_apps();
74 if (ret < 0) {
75 goto end;
76 }
77 }
78
79 if (opt_list_session) {
80 ret = process_opt_list_sessions();
81 if (ret < 0) {
82 goto end;
83 }
84 }
85
86 if (opt_destroy_session) {
87 ret = lttng_destroy_session(&current_uuid);
88 if (ret < 0) {
89 goto end;
90 }
91 MSG("Session %s destroyed.", opt_session_uuid);
92 }
93
94 if (!opt_list_session && !opt_list_apps) {
95 if (uuid_is_null(current_uuid)) {
96 /* If no session uuid, create session */
97 DBG("No session specified. Creating session.");
98 ret = process_opt_create_session();
99 if (ret < 0) {
100 goto end;
101 }
102 }
103
104 DBG("Set session uuid to %s", long_str_uuid);
105 ret = set_session_uuid();
106 if (ret < 0) {
107 ERR("Session UUID %s not found", opt_session_uuid);
108 goto error;
109 }
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 if (opt_trace_kernel) {
123 ERR("Not implemented yet");
124 ret = -ENOSYS;
125 goto end;
126 }
127
128 if (opt_trace_pid != 0) {
129 if (opt_create_trace) {
130 DBG("Create a userspace trace for pid %d", opt_trace_pid);
131 ret = lttng_ust_create_trace(opt_trace_pid);
132 if (ret < 0) {
133 goto end;
134 }
135 MSG("Trace created successfully!\nUse --start to start tracing.");
136 }
137
138 if (opt_start_trace) {
139 DBG("Start trace for pid %d", opt_trace_pid);
140 ret = lttng_ust_start_trace(opt_trace_pid);
141 if (ret < 0) {
142 goto end;
143 }
144 MSG("Trace started successfully!");
145 } else if (opt_stop_trace) {
146 DBG("Stop trace for pid %d", opt_trace_pid);
147 ret = lttng_ust_stop_trace(opt_trace_pid);
148 if (ret < 0) {
149 goto end;
150 }
151 MSG("Trace stopped successfully!");
152 }
153
154 }
155
156 return 0;
157
158end:
159 ERR("%s", lttng_get_readable_code(ret));
160error: /* fall through */
161 return ret;
162}
163
164/*
165 * set_opt_session_info
166 *
167 * Setup session_name, current_uuid, short_str_uuid and
168 * long_str_uuid using the command line options.
169 */
170static void set_opt_session_info(void)
171{
172 int count, i, short_len;
173 char *tok;
174 struct lttng_session *sessions;
175
176 if (opt_session_uuid != NULL) {
177 short_len = sizeof(short_str_uuid) - 1;
178 /* Shorten uuid */
179 tok = strchr(opt_session_uuid, '.');
180 if (strlen(tok + 1) == short_len) {
181 memcpy(short_str_uuid, tok + 1, short_len);
182 short_str_uuid[short_len] = '\0';
183 }
184
185 /* Get long real uuid_t from session daemon */
186 count = lttng_list_sessions(&sessions);
187 for (i = 0; i < count; i++) {
188 uuid_unparse(sessions[i].uuid, long_str_uuid);
189 if (strncmp(long_str_uuid, short_str_uuid, 8) == 0) {
190 uuid_copy(current_uuid, sessions[i].uuid);
191 break;
192 }
193 }
194 }
195
196 if (opt_session_name != NULL) {
197 session_name = strndup(opt_session_name, NAME_MAX);
198 }
199}
200
201/*
202 * shorten_uuid
203 *
204 * Small function to shorten the 37 bytes long uuid_t
205 * string representation to 8 characters.
206 */
207static void shorten_uuid(char *long_u, char *short_u)
208{
209 memcpy(short_u, long_u, 8);
210 short_u[UUID_SHORT_STR_LEN - 1] = '\0';
211}
212
213/*
214 * set_session_uuid
215 *
216 * Set current session uuid to the current flow of
217 * command(s) using the already shorten uuid or
218 * current full uuid.
219 */
220static int set_session_uuid(void)
221{
222 int ret, count, i;
223 char str_uuid[37];
224 struct lttng_session *sessions;
225
226 if (!uuid_is_null(current_uuid)) {
227 lttng_set_current_session_uuid(&current_uuid);
228 goto end;
229 }
230
231 count = lttng_list_sessions(&sessions);
232 if (count < 0) {
233 ret = count;
234 goto error;
235 }
236
237 for (i = 0; i < count; i++) {
238 uuid_unparse(sessions[i].uuid, str_uuid);
239 if (strncmp(str_uuid, short_str_uuid, 8) == 0) {
240 lttng_set_current_session_uuid(&sessions[i].uuid);
241 break;
242 }
243 }
244
245 free(sessions);
246
247end:
248 return 0;
249
250error:
251 return ret;
252}
253
254/*
255 * process_opt_list_traces
256 *
257 * Get list of all traces for a specific session uuid.
258 */
259static int process_opt_list_traces(void)
260{
261 int ret, i;
262 struct lttng_trace *traces;
263
264 ret = lttng_list_traces(&current_uuid, &traces);
265 if (ret < 0) {
266 goto error;
267 }
268
269 MSG("Userspace traces:");
270 for (i = 0; i < ret; i++) {
271 if (traces[i].type == USERSPACE) {
272 MSG("\t%d) %s (pid: %d): %s",
273 i, traces[i].name, traces[i].pid,
274 get_cmdline_by_pid(traces[i].pid));
275 } else {
276 break;
277 }
278 }
279
280 MSG("Kernel traces:");
281 for (;i < ret; i++) {
282 if (traces[i].type == KERNEL) {
283 MSG("\t%d) %s", i, traces[i].name);
284 }
285 }
286
287 free(traces);
288
289error:
290 return ret;
291}
292
293/*
294 * process_opt_create_session
295 *
296 * Create a new session using the name pass
297 * to the command line.
298 */
299static int process_opt_create_session(void)
300{
301 int ret;
302 uuid_t session_id;
303 char str_uuid[37];
304 char name[NAME_MAX];
305 time_t rawtime;
306 struct tm *timeinfo;
307
308 /* Auto session creation */
309 if (opt_create_session == 0) {
310 time(&rawtime);
311 timeinfo = localtime(&rawtime);
312 strftime(name, sizeof(name), "%Y%m%d-%H%M%S", timeinfo);
313 session_name = strndup(name, sizeof(name));
314 }
315
316 ret = lttng_create_session(session_name, &session_id);
317 if (ret < 0) {
318 goto error;
319 }
320
321 uuid_unparse(session_id, str_uuid);
322 uuid_copy(current_uuid, session_id);
323 shorten_uuid(str_uuid, short_str_uuid);
324
325 MSG("Session UUID created: %s.%s", session_name, short_str_uuid);
326
327error:
328 return ret;
329}
330
331/*
332 * process_opt_list_sessions
333 *
334 * Get the list of available sessions from
335 * the session daemon and print it to user.
336 */
337static int process_opt_list_sessions(void)
338{
339 int ret, count, i;
340 char tmp_short_uuid[9];
341 char str_uuid[37];
342 struct lttng_session *sess;
343
344 count = lttng_list_sessions(&sess);
345 DBG("Session count %d", count);
346 if (count < 0) {
347 ret = count;
348 goto error;
349 }
350
351 MSG("Available sessions (UUIDs):");
352 for (i = 0; i < count; i++) {
353 uuid_unparse(sess[i].uuid, str_uuid);
354 shorten_uuid(str_uuid, tmp_short_uuid);
355 MSG(" %d) %s.%s", i+1, sess[i].name, tmp_short_uuid);
356 }
357
358 free(sess);
359 MSG("\nTo select a session, use -s, --session UUID.");
360
361 return 0;
362
363error:
364 return ret;
365}
366
367/*
368 * process_opt_list_apps
369 *
370 * Get the UST traceable pid list and print
371 * them to the user.
372 */
373static int process_opt_list_apps(void)
374{
375 int i, ret, count;
376 pid_t *pids;
377 char *cmdline;
378
379 count = lttng_ust_list_apps(&pids);
380 if (count < 0) {
381 ret = count;
382 goto error;
383 }
384
385 MSG("LTTng UST traceable application [name (pid)]:");
386 for (i=0; i < count; i++) {
387 cmdline = get_cmdline_by_pid(pids[i]);
388 if (cmdline == NULL) {
389 MSG("\t(not running) (%d)", pids[i]);
390 continue;
391 }
392 MSG("\t%s (%d)", cmdline, pids[i]);
393 free(cmdline);
394 }
395
396 /* Allocated by lttng_ust_list_apps() */
397 free(pids);
398
399 return 0;
400
401error:
402 return ret;
403}
404
405/*
406 * get_cmdline_by_pid
407 *
408 * Get command line from /proc for a specific pid.
409 *
410 * On success, return an allocated string pointer pointing to
411 * the proc cmdline.
412 * On error, return NULL.
413 */
414static char *get_cmdline_by_pid(pid_t pid)
415{
416 int ret;
417 FILE *fp;
418 char *cmdline = NULL;
419 char path[24]; /* Can't go bigger than /proc/65535/cmdline */
420
421 snprintf(path, sizeof(path), "/proc/%d/cmdline", pid);
422 fp = fopen(path, "r");
423 if (fp == NULL) {
424 goto end;
425 }
426
427 /* Caller must free() *cmdline */
428 cmdline = malloc(PATH_MAX);
429 ret = fread(cmdline, 1, PATH_MAX, fp);
430 fclose(fp);
431
432end:
433 return cmdline;
434}
435
436/*
437 * validate_options
438 *
439 * Make sure that all options passed to the command line
440 * are compatible with each others.
441 *
442 * On error, return -1
443 * On success, return 0
444 */
445static int validate_options(void)
446{
447 /* Conflicting command */
448 if (opt_start_trace && opt_stop_trace) {
449 ERR("Can't use --start and --stop together.");
450 goto error;
451 /* If no PID specified and trace_kernel is off */
452 } else if ((opt_trace_pid == 0 && opt_trace_kernel == 0) &&
453 (opt_create_trace || opt_start_trace || opt_stop_trace)) {
454 ERR("Please specify a PID using -p, --pid PID.");
455 goto error;
456 } else if (opt_session_uuid && opt_create_session) {
457 ERR("Please don't use -s and -c together. Useless action.");
458 goto error;
459 } else if (opt_list_traces && opt_session_uuid == NULL) {
460 ERR("Can't use -t without -s, --session option.");
461 goto error;
462 }
463
464 return 0;
465
466error:
467 return -1;
468}
469
470/*
471 * spawn_sessiond
472 *
473 * Spawn a session daemon by forking and execv.
474 */
475static int spawn_sessiond(char *pathname)
476{
477 int ret = 0;
478 pid_t pid;
479
480 MSG("Spawning session daemon");
481 pid = fork();
482 if (pid == 0) {
483 /*
484 * Spawn session daemon and tell
485 * it to signal us when ready.
486 */
487 execlp(pathname, "ltt-sessiond", "--sig-parent", "--quiet", NULL);
488 /* execlp only returns if error happened */
489 if (errno == ENOENT) {
490 ERR("No session daemon found. Use --sessiond-path.");
491 } else {
492 perror("execlp");
493 }
494 kill(getppid(), SIGTERM); /* unpause parent */
495 exit(EXIT_FAILURE);
496 } else if (pid > 0) {
497 /* Wait for ltt-sessiond to start */
498 pause();
499 goto end;
500 } else {
501 perror("fork");
502 ret = -1;
503 goto end;
504 }
505
506end:
507 return ret;
508}
509
510/*
511 * check_ltt_sessiond
512 *
513 * Check if the session daemon is available using
514 * the liblttngctl API for the check. If not, try to
515 * spawn a daemon.
516 */
517static int check_ltt_sessiond(void)
518{
519 int ret;
520 char *pathname = NULL, *alloc_pathname = NULL;
521
522 ret = lttng_check_session_daemon();
523 if (ret < 0) {
524 /* Try command line option path */
525 if (opt_sessiond_path != NULL) {
526 ret = access(opt_sessiond_path, F_OK | X_OK);
527 if (ret < 0) {
528 ERR("No such file: %s", opt_sessiond_path);
529 goto end;
530 }
531 pathname = opt_sessiond_path;
532 } else {
533 /* Try LTTNG_SESSIOND_PATH env variable */
534 pathname = getenv(LTTNG_SESSIOND_PATH_ENV);
535 }
536
537 /* Let's rock and roll */
538 if (pathname == NULL) {
539 ret = asprintf(&alloc_pathname, "ltt-sessiond");
540 if (ret < 0) {
541 goto end;
542 }
543 pathname = alloc_pathname;
544 }
545
546 ret = spawn_sessiond(pathname);
547 free(alloc_pathname);
548 if (ret < 0) {
549 ERR("Problem occurs when starting %s", pathname);
550 goto end;
551 }
552 }
553
554end:
555 return ret;
556}
557
558/*
559 * set_signal_handler
560 *
561 * Setup signal handler for SIGCHLD and SIGTERM.
562 */
563static int set_signal_handler(void)
564{
565 int ret = 0;
566 struct sigaction sa;
567 sigset_t sigset;
568
569 if ((ret = sigemptyset(&sigset)) < 0) {
570 perror("sigemptyset");
571 goto end;
572 }
573
574 sa.sa_handler = sighandler;
575 sa.sa_mask = sigset;
576 sa.sa_flags = 0;
577 if ((ret = sigaction(SIGCHLD, &sa, NULL)) < 0) {
578 perror("sigaction");
579 goto end;
580 }
581
582 if ((ret = sigaction(SIGTERM, &sa, NULL)) < 0) {
583 perror("sigaction");
584 goto end;
585 }
586
587end:
588 return ret;
589}
590
591/*
592 * sighandler
593 *
594 * Signal handler for the daemon
595 */
596static void sighandler(int sig)
597{
598 switch (sig) {
599 case SIGTERM:
600 DBG("SIGTERM catched");
601 clean_exit(EXIT_FAILURE);
602 break;
603 case SIGCHLD:
604 /* Notify is done */
605 DBG("SIGCHLD catched");
606 break;
607 default:
608 DBG("Unknown signal %d catched", sig);
609 break;
610 }
611
612 return;
613}
614
615/*
616 * clean_exit
617 */
618void clean_exit(int code)
619{
620 DBG("Clean exit");
621 if (session_name) {
622 free(session_name);
623 }
624
625 exit(code);
626}
627
628/*
629 * main
630 */
631int main(int argc, char *argv[])
632{
633 int ret;
634
635 progname = argv[0] ? argv[0] : "lttng";
636
637 /* For Mathieu Desnoyers aka Dr Tracing */
638 if (strncmp(progname, "drtrace", 7) == 0) {
639 MSG("%c[%d;%dmWelcome back Dr Tracing!%c[%dm\n\n", 27,1,33,27,0);
640 }
641
642 ret = parse_args(argc, (const char **) argv);
643 if (ret < 0) {
644 clean_exit(EXIT_FAILURE);
645 }
646
647 ret = validate_options();
648 if (ret < 0) {
649 return EXIT_FAILURE;
650 }
651
652 ret = set_signal_handler();
653 if (ret < 0) {
654 clean_exit(ret);
655 }
656
657 if (opt_tracing_group != NULL) {
658 DBG("Set tracing group to '%s'", opt_tracing_group);
659 lttng_set_tracing_group(opt_tracing_group);
660 }
661
662 /* If ask for kernel tracing, need root perms */
663 if (opt_trace_kernel) {
664 DBG("Kernel tracing activated");
665 if (getuid() != 0) {
666 ERR("%s must be setuid root", progname);
667 clean_exit(-EPERM);
668 }
669 }
670
671 /* Check if the lttng session daemon is running.
672 * If no, a daemon will be spawned.
673 */
674 if (opt_no_sessiond == 0 && (check_ltt_sessiond() < 0)) {
675 clean_exit(EXIT_FAILURE);
676 }
677
678 ret = process_client_opt();
679 if (ret < 0) {
680 clean_exit(ret);
681 }
682
683 clean_exit(0);
684
685 return 0;
686}
This page took 0.025814 seconds and 5 git commands to generate.