*/
#define _GNU_SOURCE
-#include <errno.h>
-#include <fcntl.h>
#include <getopt.h>
-#include <grp.h>
-#include <limits.h>
+#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <sys/wait.h>
#include <unistd.h>
-#include <lttng/liblttngctl.h>
+#include <lttng/lttng.h>
-#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);
-static int process_opt_list_sessions(void);
-static int process_opt_list_traces(void);
-static int process_opt_create_session(void);
-static void sighandler(int sig);
-static int set_signal_handler(void);
-static int validate_options(void);
-static char *get_cmdline_by_pid(pid_t pid);
+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},
+ { "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] <command>\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-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;
- uuid_t uuid;
-
- /* Connect to the session daemon */
- ret = lttng_connect_sessiond();
- if (ret < 0) {
- goto end;
- }
-
- if (opt_list_apps) {
- ret = process_opt_list_apps();
- if (ret < 0) {
- goto end;
- }
- }
-
- if (opt_list_session) {
- ret = process_opt_list_sessions();
- if (ret < 0) {
- goto end;
- }
- }
-
- if (opt_list_traces) {
- ret = process_opt_list_traces();
- if (ret < 0) {
- goto end;
- }
- }
-
- if (opt_create_session != NULL) {
- ret = process_opt_create_session();
- if (ret < 0) {
- goto end;
- }
- }
-
- if (opt_destroy_session != NULL) {
- uuid_parse(opt_destroy_session, uuid);
- ret = lttng_destroy_session(&uuid);
- if (ret < 0) {
- goto end;
- }
- }
+ int i = 0;
+ struct option *option = NULL;
- if (opt_session_uuid != NULL) {
- DBG("Set session uuid to %s", opt_session_uuid);
- lttng_set_current_session_uuid(opt_session_uuid);
- }
+ option = &long_options[i];
+ while (option->name != NULL) {
+ fprintf(ofp, "--%s\n", option->name);
- if (opt_create_trace) {
- DBG("Create trace for pid %d", opt_create_trace);
- ret = lttng_ust_create_trace(opt_create_trace);
- if (ret < 0) {
- goto end;
+ if (isprint(option->val)) {
+ fprintf(ofp, "-%c\n", option->val);
}
- MSG("Trace created successfully!\nUse --start PID to start tracing.");
- }
- if (opt_start_trace) {
- DBG("Start trace for pid %d", opt_start_trace);
- ret = lttng_ust_start_trace(opt_start_trace);
- if (ret < 0) {
- goto end;
- }
- MSG("Trace started successfully!");
+ i++;
+ option = &long_options[i];
}
-
- if (opt_stop_trace) {
- DBG("Stop trace for pid %d", opt_stop_trace);
- ret = lttng_ust_stop_trace(opt_stop_trace);
- if (ret < 0) {
- goto end;
- }
- MSG("Trace stopped successfully!");
- }
-
- return 0;
-
-end:
- ERR("%s", lttng_get_readable_code(ret));
- return ret;
}
/*
- * process_opt_list_traces
+ * list_commands
*
- * Get list of all traces for a specific session uuid.
+ * List commands line by line. This is mostly for bash auto completion and to
+ * avoid difficult parsing.
*/
-static int process_opt_list_traces(void)
+static void list_commands(FILE *ofp)
{
- int ret, i;
- uuid_t uuid;
- struct lttng_trace *traces;
+ int i = 0;
+ struct cmd_struct *cmd = NULL;
- uuid_parse(opt_session_uuid, uuid);
- ret = lttng_list_traces(&uuid, &traces);
- if (ret < 0) {
- goto error;
+ cmd = &commands[i];
+ while (cmd->name != NULL) {
+ fprintf(ofp, "%s\n", cmd->name);
+ i++;
+ cmd = &commands[i];
}
-
- MSG("Userspace traces:");
- for (i = 0; i < ret; i++) {
- if (traces[i].type == USERSPACE) {
- MSG("\t%d) %s (pid: %d): %s",
- i, traces[i].name, traces[i].pid,
- get_cmdline_by_pid(traces[i].pid));
- } else {
- break;
- }
- }
-
- MSG("Kernel traces:");
- for (;i < ret; i++) {
- if (traces[i].type == KERNEL) {
- MSG("\t%d) %s", i, traces[i].name);
- }
- }
-
- free(traces);
-
-error:
- return ret;
}
/*
- * process_opt_create_session
- *
- * Create a new session using the name pass
- * to the command line.
+ * clean_exit
*/
-static int process_opt_create_session(void)
+static void clean_exit(int code)
{
- int ret;
- uuid_t session_id;
- char str_uuid[37];
-
- ret = lttng_create_session(opt_create_session, &session_id);
- if (ret < 0) {
- goto error;
- }
-
- uuid_unparse(session_id, str_uuid);
-
- MSG("Session created:");
- MSG(" %s (%s)", opt_create_session, str_uuid);
-
-error:
- return ret;
+ DBG("Clean exit");
+ exit(code);
}
/*
- * process_opt_list_sessions
+ * sighandler
*
- * Get the list of available sessions from
- * the session daemon and print it to user.
+ * Signal handler for the daemon
*/
-static int process_opt_list_sessions(void)
+static void sighandler(int sig)
{
- int ret, count, i;
- struct lttng_session *sess;
-
- count = lttng_list_sessions(&sess);
- if (count < 0) {
- ret = count;
- goto error;
- }
-
- MSG("Available sessions [Name (uuid)]:");
- for (i = 0; i < count; i++) {
- MSG("\tName: %s (uuid: %s)", sess[i].name, sess[i].uuid);
+ 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;
}
- free(sess);
- MSG("\nTo select a session, use --session UUID.");
-
- return 0;
-
-error:
- return ret;
+ return;
}
/*
- * process_opt_list_apps
+ * set_signal_handler
*
- * Get the UST traceable pid list and print
- * them to the user.
+ * Setup signal handler for SIGCHLD and SIGTERM.
*/
-static int process_opt_list_apps(void)
+static int set_signal_handler(void)
{
- int i, ret, count;
- pid_t *pids;
- char *cmdline;
+ int ret = 0;
+ struct sigaction sa;
+ sigset_t sigset;
- count = lttng_ust_list_apps(&pids);
- if (count < 0) {
- ret = count;
- goto error;
+ if ((ret = sigemptyset(&sigset)) < 0) {
+ perror("sigemptyset");
+ goto end;
}
- MSG("LTTng UST traceable application [name (pid)]:");
- for (i=0; i < count; i++) {
- cmdline = get_cmdline_by_pid(pids[i]);
- if (cmdline == NULL) {
- MSG("\t(not running) (%d)", pids[i]);
- continue;
- }
- MSG("\t%s (%d)", cmdline, pids[i]);
- free(cmdline);
+ sa.sa_handler = sighandler;
+ sa.sa_mask = sigset;
+ sa.sa_flags = 0;
+ if ((ret = sigaction(SIGCHLD, &sa, NULL)) < 0) {
+ perror("sigaction");
+ goto end;
}
- /* Allocated by lttng_ust_list_apps() */
- free(pids);
-
- return 0;
+ if ((ret = sigaction(SIGTERM, &sa, NULL)) < 0) {
+ perror("sigaction");
+ goto end;
+ }
-error:
+end:
return ret;
}
/*
- * get_cmdline_by_pid
+ * handle_command
*
- * Get command line from /proc for a specific pid.
+ * 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.
*
- * On success, return an allocated string pointer pointing to
- * the proc cmdline.
- * On error, return NULL.
+ * If command not found, return -1
+ * else, return function command error code.
*/
-static char *get_cmdline_by_pid(pid_t pid)
+static int handle_command(int argc, char **argv)
{
- int ret;
- FILE *fp;
- char *cmdline = NULL;
- char path[24]; /* Can't go bigger than /proc/65535/cmdline */
+ int i = 0, ret;
+ struct cmd_struct *cmd;
- snprintf(path, sizeof(path), "/proc/%d/cmdline", pid);
- fp = fopen(path, "r");
- if (fp == NULL) {
+ if (*argv == NULL) {
+ ret = CMD_SUCCESS;
goto end;
}
- /* Caller must free() *cmdline */
- cmdline = malloc(PATH_MAX);
- ret = fread(cmdline, 1, PATH_MAX, fp);
- fclose(fp);
-
-end:
- return cmdline;
-}
-
-/*
- * validate_options
- *
- * Make sure that all options passed to the command line
- * are compatible with each others.
- *
- * On error, return -1
- * On success, return 0
- */
-static int validate_options(void)
-{
- if ((opt_session_uuid == NULL) &&
- (opt_create_trace || opt_start_trace || opt_list_traces)) {
- ERR("You need to specify a session UUID.\nPlease use --session UUID to do so.");
- goto error;
+ 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;
+ }
+ i++;
+ cmd = &commands[i];
}
- return 0;
+ /* Command not found */
+ ret = -1;
-error:
- return -1;
+end:
+ return ret;
}
/*
int ret = 0;
pid_t pid;
- MSG("Spawning session daemon");
+ MSG("Spawning a session daemon");
pid = fork();
if (pid == 0) {
- /* Spawn session daemon and tell
+ /*
+ * Spawn session daemon and tell
* it to signal us when ready.
*/
- ret = execlp(pathname, "ltt-sessiond", "--sig-parent", "--quiet", NULL);
- if (ret < 0) {
- if (errno == ENOENT) {
- ERR("No session daemon found. Use --sessiond-path.");
- } else {
- perror("execlp");
- }
- kill(getppid(), SIGTERM);
- exit(EXIT_FAILURE);
+ 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");
}
- exit(EXIT_SUCCESS);
+ kill(getppid(), SIGTERM); /* unpause parent */
+ exit(EXIT_FAILURE);
} else if (pid > 0) {
/* Wait for ltt-sessiond to start */
pause();
}
/*
- * check_ltt_sessiond
+ * check_sessiond
*
* Check if the session daemon is available using
* 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;
+ char *pathname = NULL, *alloc_pathname = NULL;
- ret = lttng_check_session_daemon();
- if (ret < 0) {
+ 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);
} else {
/* Try LTTNG_SESSIOND_PATH env variable */
pathname = getenv(LTTNG_SESSIOND_PATH_ENV);
- if (pathname != NULL) {
- /* strdup here in order to make the free()
- * not fail later on.
- */
- pathname = strdup(pathname);
- }
}
/* Let's rock and roll */
if (pathname == NULL) {
- ret = asprintf(&pathname, "ltt-sessiond");
+ ret = asprintf(&alloc_pathname, "ltt-sessiond");
if (ret < 0) {
goto end;
}
+ pathname = alloc_pathname;
}
ret = spawn_sessiond(pathname);
- free(pathname);
+ free(alloc_pathname);
if (ret < 0) {
ERR("Problem occurs when starting %s", pathname);
goto end;
}
/*
- * set_signal_handler
+ * parse_args
*
- * Setup signal handler for SIGCHLD and SIGTERM.
+ * Parse command line arguments.
+ * Return 0 if OK, else -1
*/
-static int set_signal_handler(void)
+static int parse_args(int argc, char **argv)
{
- int ret = 0;
- struct sigaction sa;
- sigset_t sigset;
+ int opt, ret;
- if ((ret = sigemptyset(&sigset)) < 0) {
- perror("sigemptyset");
- goto end;
+ if (argc < 2) {
+ usage(stderr);
+ clean_exit(EXIT_FAILURE);
}
- sa.sa_handler = sighandler;
- sa.sa_mask = sigset;
- sa.sa_flags = 0;
- if ((ret = sigaction(SIGCHLD, &sa, NULL)) < 0) {
- perror("sigaction");
- goto end;
+ 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 ((ret = sigaction(SIGTERM, &sa, NULL)) < 0) {
- perror("sigaction");
- goto end;
+ /* If both options are specified, quiet wins */
+ if (opt_verbose && opt_quiet) {
+ opt_verbose = 0;
}
-end:
- return ret;
-}
+ /* Spawn session daemon if needed */
+ if (opt_no_sessiond == 0 && (check_sessiond() < 0)) {
+ goto error;
+ }
-/*
- * 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;
+ /* No leftovers, print usage and quit */
+ if ((argc - optind) == 0) {
+ usage(stderr);
+ goto error;
}
- return;
-}
-/*
- * clean_exit
- */
-void clean_exit(int code)
-{
- DBG("Clean exit");
- if (lttng_disconnect_sessiond() < 0) {
- ERR("Session daemon disconnect failed.");
+ /*
+ * 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;
}
- exit(code);
+
+ return 0;
+
+error:
+ return -1;
}
+
/*
* main
*/
MSG("%c[%d;%dmWelcome back Dr Tracing!%c[%dm\n\n", 27,1,33,27,0);
}
- ret = parse_args(argc, (const char **) argv);
- if (ret < 0) {
- clean_exit(EXIT_FAILURE);
- }
-
- ret = validate_options();
- if (ret < 0) {
- return EXIT_FAILURE;
- }
-
ret = set_signal_handler();
if (ret < 0) {
clean_exit(ret);
}
- 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);
- clean_exit(-EPERM);
- }
- }
-
- /* Check if the lttng session daemon is running.
- * If no, a daemon will be spawned.
- */
- if (opt_no_sessiond == 0 && (check_ltt_sessiond() < 0)) {
- clean_exit(EXIT_FAILURE);
- }
-
- ret = process_client_opt();
+ ret = parse_args(argc, argv);
if (ret < 0) {
- clean_exit(ret);
+ clean_exit(EXIT_FAILURE);
}
- clean_exit(0);
-
return 0;
}