X-Git-Url: https://git.lttng.org/?p=lttng-tools.git;a=blobdiff_plain;f=lttng%2Flttng.c;h=379cbb1fdbb6183377bb4e50664d0c84bdb2a1e8;hp=6cc540922a77b78ebd29c505255bfa54ad65996b;hb=8490ae7b70872d03eda98f1fb2c76488a47e3084;hpb=e065084a507dd07b658b3d26c58b1ddd519e25a0 diff --git a/lttng/lttng.c b/lttng/lttng.c index 6cc540922..379cbb1fd 100644 --- a/lttng/lttng.c +++ b/lttng/lttng.c @@ -17,133 +17,434 @@ */ #define _GNU_SOURCE -#include -#include #include -#include -#include +#include #include #include #include -#include -#include -#include #include -#include +#include -#include "lttng.h" +#include "cmd.h" +#include "conf.h" #include "lttngerr.h" /* Variables */ static char *progname; -/* Prototypes */ -static int process_client_opt(void); -static int process_opt_list_apps(void); +int opt_quiet; +int opt_verbose; +static int opt_no_sessiond; +static char *opt_sessiond_path; + +enum { + OPT_SESSION_PATH, + OPT_DUMP_OPTIONS, + OPT_DUMP_COMMANDS, +}; + +/* Getopt options. No first level command. */ +static struct option long_options[] = { + {"help", 0, NULL, 'h'}, + {"group", 1, NULL, 'g'}, + {"verbose", 0, NULL, 'v'}, + {"quiet", 0, NULL, 'q'}, + {"no-sessiond", 0, NULL, 'n'}, + {"sessiond-path", 1, NULL, OPT_SESSION_PATH}, + {"list-options", 0, NULL, OPT_DUMP_OPTIONS}, + {"list-commands", 0, NULL, OPT_DUMP_COMMANDS}, + {NULL, 0, NULL, 0} +}; + +/* First level command */ +static struct cmd_struct commands[] = { + { "list", cmd_list}, + { "create", cmd_create}, + { "destroy", cmd_destroy}, + { "add-channel", cmd_add_channel}, + { "start", cmd_start}, + { "stop", cmd_stop}, + { "enable-event", cmd_enable_events}, + { "disable-event", cmd_disable_events}, + { "enable-channel", cmd_enable_channels}, + { "disable-channel", cmd_disable_channels}, + { "add-context", cmd_add_context}, + { "set-session", cmd_set_session}, + { "version", cmd_version}, + { NULL, NULL} /* Array closure */ +}; + +static void usage(FILE *ofp) +{ + fprintf(ofp, "LTTng Trace Control " VERSION"\n\n"); + fprintf(ofp, "usage: lttng [options] \n"); + fprintf(ofp, "\n"); + fprintf(ofp, "Options:\n"); + fprintf(ofp, " -h, --help Show this help\n"); + fprintf(ofp, " -g, --group NAME Unix tracing group name. (default: tracing)\n"); + fprintf(ofp, " -v, --verbose Verbose mode\n"); + fprintf(ofp, " -q, --quiet Quiet mode\n"); + fprintf(ofp, " -n, --no-sessiond Don't spawn a session daemon\n"); + fprintf(ofp, " --sessiond-path Session daemon full path\n"); + fprintf(ofp, " --list-options Simple listing of lttng options\n"); + fprintf(ofp, " --list-commands Simple listing of lttng commands\n"); + fprintf(ofp, "\n"); + fprintf(ofp, "Commands:\n"); + fprintf(ofp, " add-channel Add channel to tracer\n"); + fprintf(ofp, " add-context Add context to event or/and channel\n"); + fprintf(ofp, " create Create tracing session\n"); + fprintf(ofp, " destroy Teardown tracing session\n"); + fprintf(ofp, " enable-channel Enable tracing channel\n"); + fprintf(ofp, " enable-event Enable tracing event\n"); + fprintf(ofp, " disable-channel Disable tracing channel\n"); + fprintf(ofp, " disable-event Disable tracing event\n"); + fprintf(ofp, " list List possible tracing options\n"); + fprintf(ofp, " set-session Set current session name\n"); + fprintf(ofp, " start Start tracing\n"); + fprintf(ofp, " stop Stop tracing\n"); + fprintf(ofp, " version Show version information\n"); + fprintf(ofp, "\n"); + fprintf(ofp, "Please see the lttng(1) man page for full documentation.\n"); + fprintf(ofp, "See http://lttng.org for updates, bug reports and news.\n"); +} /* - * start_client + * list_options * - * Process client request from the command line - * options. Every tracing action is done by the - * liblttngctl API. + * List options line by line. This is mostly for bash auto completion and to + * avoid difficult parsing. */ -static int process_client_opt(void) +static void list_options(FILE *ofp) { - int ret; + int i = 0; + struct option *option = NULL; - /* Connect to the session daemon */ - ret = lttng_connect_sessiond(); - if (ret < 0) { - ERR("%s", lttng_get_readable_code(ret)); + option = &long_options[i]; + while (option->name != NULL) { + fprintf(ofp, "--%s\n", option->name); + + if (isprint(option->val)) { + fprintf(ofp, "-%c\n", option->val); + } + + i++; + option = &long_options[i]; + } +} + +/* + * list_commands + * + * List commands line by line. This is mostly for bash auto completion and to + * avoid difficult parsing. + */ +static void list_commands(FILE *ofp) +{ + int i = 0; + struct cmd_struct *cmd = NULL; + + cmd = &commands[i]; + while (cmd->name != NULL) { + fprintf(ofp, "%s\n", cmd->name); + i++; + cmd = &commands[i]; + } +} + +/* + * clean_exit + */ +static void clean_exit(int code) +{ + DBG("Clean exit"); + exit(code); +} + +/* + * sighandler + * + * Signal handler for the daemon + */ +static void sighandler(int sig) +{ + switch (sig) { + case SIGTERM: + DBG("SIGTERM catched"); + clean_exit(EXIT_FAILURE); + break; + case SIGCHLD: + /* Notify is done */ + DBG("SIGCHLD catched"); + break; + default: + DBG("Unknown signal %d catched", sig); + break; + } + + return; +} + +/* + * set_signal_handler + * + * Setup signal handler for SIGCHLD and SIGTERM. + */ +static int set_signal_handler(void) +{ + int ret = 0; + struct sigaction sa; + sigset_t sigset; + + if ((ret = sigemptyset(&sigset)) < 0) { + perror("sigemptyset"); goto end; } - if (opt_list_apps) { - ret = process_opt_list_apps(); - if (ret < 0) { - ERR("%s", lttng_get_readable_code(ret)); - goto end; - } + sa.sa_handler = sighandler; + sa.sa_mask = sigset; + sa.sa_flags = 0; + if ((ret = sigaction(SIGCHLD, &sa, NULL)) < 0) { + perror("sigaction"); + goto end; } - return 0; + if ((ret = sigaction(SIGTERM, &sa, NULL)) < 0) { + perror("sigaction"); + goto end; + } end: return ret; } /* - * process_opt_list_apps + * handle_command * - * Get the UST traceable pid list and print - * them to the user. + * Handle the full argv list of a first level command. Will find the command + * in the global commands array and call the function callback associated. + * + * If command not found, return -1 + * else, return function command error code. */ -static int process_opt_list_apps(void) +static int handle_command(int argc, char **argv) { - int i, ret; - pid_t *pids; - FILE *fp; - char path[24]; /* Can't go bigger than /proc/65535/cmdline */ - char cmdline[PATH_MAX]; + int i = 0, ret; + struct cmd_struct *cmd; - ret = lttng_ust_list_apps(&pids); - if (ret < 0) { - goto error; + if (*argv == NULL) { + ret = CMD_SUCCESS; + goto end; } - MSG("LTTng UST traceable application [name (pid)]:"); - for (i=0; i < ret; i++) { - snprintf(path, sizeof(path), "/proc/%d/cmdline", pids[i]); - fp = fopen(path, "r"); - if (fp == NULL) { - continue; + cmd = &commands[i]; + while (cmd->func != NULL) { + /* Find command */ + if (strcmp(argv[0], cmd->name) == 0) { + ret = cmd->func(argc, (const char**) argv); + switch (ret) { + case CMD_ERROR: + ERR("Command error"); + break; + case CMD_NOT_IMPLEMENTED: + ERR("Options not implemented"); + break; + case CMD_UNDEFINED: + ERR("Undefined command"); + break; + case CMD_FATAL: + ERR("Fatal error"); + break; + } + goto end; } - ret = fread(cmdline, 1, sizeof(cmdline), fp); - MSG("\t%s (%d)", cmdline, pids[i]); - fclose(fp); + i++; + cmd = &commands[i]; } - /* Allocated by lttng_ust_list_apps() */ - free(pids); + /* Command not found */ + ret = -1; - return 0; +end: + return ret; +} -error: +/* + * spawn_sessiond + * + * Spawn a session daemon by forking and execv. + */ +static int spawn_sessiond(char *pathname) +{ + int ret = 0; + pid_t pid; + + MSG("Spawning a session daemon"); + pid = fork(); + if (pid == 0) { + /* + * Spawn session daemon and tell + * it to signal us when ready. + */ + execlp(pathname, "ltt-sessiond", "--sig-parent", "--quiet", NULL); + /* execlp only returns if error happened */ + if (errno == ENOENT) { + ERR("No session daemon found. Use --sessiond-path."); + } else { + perror("execlp"); + } + kill(getppid(), SIGTERM); /* unpause parent */ + exit(EXIT_FAILURE); + } else if (pid > 0) { + /* Wait for ltt-sessiond to start */ + pause(); + goto end; + } else { + perror("fork"); + ret = -1; + goto end; + } + +end: return ret; } /* - * check_ltt_sessiond + * check_sessiond * * Check if the session daemon is available using - * the liblttngctl API for the check. + * the liblttngctl API for the check. If not, try to + * spawn a daemon. */ -static int check_ltt_sessiond(void) +static int check_sessiond(void) { int ret; + char *pathname = NULL, *alloc_pathname = NULL; - ret = lttng_check_session_daemon(); - if (ret < 0) { - ERR("No session daemon found. Aborting."); + ret = lttng_session_daemon_alive(); + if (ret == 0) { /* not alive */ + /* Try command line option path */ + if (opt_sessiond_path != NULL) { + ret = access(opt_sessiond_path, F_OK | X_OK); + if (ret < 0) { + ERR("No such file: %s", opt_sessiond_path); + goto end; + } + pathname = opt_sessiond_path; + } else { + /* Try LTTNG_SESSIOND_PATH env variable */ + pathname = getenv(LTTNG_SESSIOND_PATH_ENV); + } + + /* Let's rock and roll */ + if (pathname == NULL) { + ret = asprintf(&alloc_pathname, "ltt-sessiond"); + if (ret < 0) { + goto end; + } + pathname = alloc_pathname; + } + + ret = spawn_sessiond(pathname); + free(alloc_pathname); + if (ret < 0) { + ERR("Problem occurs when starting %s", pathname); + goto end; + } } +end: return ret; } - /* - * clean_exit + * parse_args + * + * Parse command line arguments. + * Return 0 if OK, else -1 */ -void clean_exit(int code) +static int parse_args(int argc, char **argv) { - DBG("Clean exit"); - exit(code); + int opt, ret; + + if (argc < 2) { + usage(stderr); + clean_exit(EXIT_FAILURE); + } + + while ((opt = getopt_long(argc, argv, "+hnvqg:", long_options, NULL)) != -1) { + switch (opt) { + case 'h': + usage(stderr); + goto error; + case 'v': + opt_verbose = 1; + break; + case 'q': + opt_quiet = 1; + break; + case 'g': + lttng_set_tracing_group(optarg); + break; + case 'n': + opt_no_sessiond = 1; + break; + case OPT_SESSION_PATH: + opt_sessiond_path = strdup(optarg); + break; + case OPT_DUMP_OPTIONS: + list_options(stdout); + ret = 0; + goto error; + case OPT_DUMP_COMMANDS: + list_commands(stdout); + ret = 0; + goto error; + default: + usage(stderr); + goto error; + } + } + + /* If both options are specified, quiet wins */ + if (opt_verbose && opt_quiet) { + opt_verbose = 0; + } + + /* Spawn session daemon if needed */ + if (opt_no_sessiond == 0 && (check_sessiond() < 0)) { + goto error; + } + + /* No leftovers, print usage and quit */ + if ((argc - optind) == 0) { + usage(stderr); + goto error; + } + + /* + * Handle leftovers which is a first level command with the trailing + * options. + */ + ret = handle_command(argc - optind, argv + optind); + if (ret < 0) { + if (ret == -1) { + usage(stderr); + } else { + ERR("%s", lttng_get_readable_code(ret)); + } + goto error; + } + + return 0; + +error: + return -1; } + /* - * main + * main */ int main(int argc, char *argv[]) { @@ -156,35 +457,14 @@ int main(int argc, char *argv[]) MSG("%c[%d;%dmWelcome back Dr Tracing!%c[%dm\n\n", 27,1,33,27,0); } - ret = parse_args(argc, (const char **) argv); + ret = set_signal_handler(); if (ret < 0) { - return EXIT_FAILURE; - } - - if (opt_tracing_group != NULL) { - DBG("Set tracing group to '%s'", opt_tracing_group); - lttng_set_tracing_group(opt_tracing_group); - } - - /* If ask for kernel tracing, need root perms */ - if (opt_trace_kernel) { - DBG("Kernel tracing activated"); - if (getuid() != 0) { - ERR("%s must be setuid root", progname); - return -EPERM; - } - } - - /* Check if the lttng session daemon is running. - * If no, a daemon will be spawned. - */ - if (check_ltt_sessiond() < 0) { - return EXIT_FAILURE; + clean_exit(ret); } - ret = process_client_opt(); + ret = parse_args(argc, argv); if (ret < 0) { - return ret; + clean_exit(EXIT_FAILURE); } return 0;