move ltt-control out of trunk/ltt-control
[ltt-control.git] / liblttctl / liblttctl.c
diff --git a/liblttctl/liblttctl.c b/liblttctl/liblttctl.c
new file mode 100644 (file)
index 0000000..6ee37a4
--- /dev/null
@@ -0,0 +1,722 @@
+/* libltt
+ *
+ * Linux Trace Toolkit Netlink Control Library
+ *
+ * Controls the ltt-control kernel module through debugfs.
+ *
+ * Copyright 2005 -
+ *     Mathieu Desnoyers <mathieu.desnoyers@polymtl.ca>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.        See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <liblttctl/lttctl.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <dirent.h>
+#include <limits.h>
+#include <fcntl.h>
+#include <stdlib.h>
+
+#define MAX_CHANNEL    (256)
+
+static char debugfsmntdir[PATH_MAX];
+
+static int initdebugfsmntdir(void)
+{
+       return getdebugfsmntdir(debugfsmntdir);
+}
+
+/*
+ * This function must called posterior to initdebugfsmntdir(),
+ * because it need to use debugfsmntdir[] which is inited in initdebugfsmntdir()
+ */
+static int initmodule(void)
+{
+       char controldirname[PATH_MAX];
+       DIR *dir;
+       int tryload_done = 0;
+
+       sprintf(controldirname, "%s/ltt/control/", debugfsmntdir);
+
+check_again:
+       /*
+        * Check ltt control's debugfs dir
+        *
+        * We don't check is ltt-trace-control module exist, because it maybe
+        * compiled into kernel.
+        */
+       dir = opendir(controldirname);
+       if (dir) {
+               closedir(dir);
+               return 0;
+       }
+
+       if (!tryload_done) {
+               system("modprobe ltt-trace-control");
+               tryload_done = 1;
+               goto check_again;
+       }
+
+       return -ENOENT;
+}
+
+int lttctl_init(void)
+{
+       int ret;
+
+
+       ret = initdebugfsmntdir();
+       if (ret) {
+               fprintf(stderr, "Get debugfs mount point failed\n");
+               return ret;
+       }
+
+       ret = initmodule();
+       if (ret) {
+               fprintf(stderr, "Control module seems not work\n");
+               return ret;
+       }
+
+       return 0;
+}
+
+int lttctl_destroy(void)
+{
+       return 0;
+}
+
+static int lttctl_sendop(const char *fname, const char *op)
+{
+       int fd;
+
+       if (!fname) {
+               fprintf(stderr, "%s: args invalid\n", __func__);
+               return 1;
+       }
+
+       fd = open(fname, O_WRONLY);
+       if (fd == -1) {
+               fprintf(stderr, "%s: open %s failed: %s\n", __func__, fname,
+                       strerror(errno));
+               return errno;
+       }
+
+       if (write(fd, op, strlen(op)) == -1) {
+               int ret = errno;
+               fprintf(stderr, "%s: write %s to %s failed: %s\n", __func__, op,
+                       fname, strerror(errno));
+               close(fd);
+               return ret;
+       }
+
+       close(fd);
+
+       return 0;
+}
+
+/*
+ * check is trace exist(check debugfsmntdir too)
+ * expect:
+ *   0: expect that trace not exist
+ *   !0: expect that trace exist
+ *
+ * ret:
+ *   0: check pass
+ *   -(EEXIST | ENOENT): check failed
+ *   -ERRNO: error happened (no check)
+ */
+static int lttctl_check_trace(const char *name, int expect)
+{
+       char tracedirname[PATH_MAX];
+       DIR *dir;
+       int exist;
+
+       if (!name) {
+               fprintf(stderr, "%s: args invalid\n", __func__);
+               return -EINVAL;
+       }
+
+       if (!debugfsmntdir[0]) {
+               fprintf(stderr, "%s: debugfsmntdir not valid\n", __func__);
+               return -EINVAL;
+       }
+
+       sprintf(tracedirname, "%s/ltt/control/%s", debugfsmntdir, name);
+
+       dir = opendir(tracedirname);
+       if (dir) {
+               exist = 1;
+       } else {
+               if (errno != ENOENT) {
+                       fprintf(stderr, "%s: %s\n", __func__, strerror(errno));
+                       return -EINVAL;
+               }
+               exist = 0;
+       }
+
+       closedir(dir);
+
+       if (!expect != !exist) {
+               if (exist)
+               {
+                       fprintf(stderr, "Trace %s already exist\n", name);
+                       return -EEXIST;
+               }
+               else
+               {
+                       fprintf(stderr, "Trace %s not exist\n", name);
+                       return -ENOENT;
+               }
+               
+       }
+
+       return 0;
+}
+
+/*
+ * get channel list of a trace
+ * don't include metadata channel when metadata is 0
+ *
+ * return number of channel on success
+ * return negative number on fail
+ * Caller must free channellist.
+ */
+static int lttctl_get_channellist(const char *tracename,
+               char ***channellist, int metadata)
+{
+       char tracedirname[PATH_MAX];
+       struct dirent *dirent;
+       DIR *dir;
+       char **list = NULL, **old_list;
+       int nr_chan = 0;
+       
+       sprintf(tracedirname, "%s/ltt/control/%s/channel", debugfsmntdir,
+               tracename);
+
+       dir = opendir(tracedirname);
+       if (!dir) {
+               nr_chan = -ENOENT;
+               goto error;
+       }
+
+       for (;;) {
+               dirent = readdir(dir);
+               if (!dirent)
+                       break;
+               if (!strcmp(dirent->d_name, ".")
+                               || !strcmp(dirent->d_name, ".."))
+                       continue;
+               if (!metadata && !strcmp(dirent->d_name, "metadata"))
+                       continue;
+               old_list = list;
+               list = malloc(sizeof(char *) * ++nr_chan);
+               memcpy(list, old_list, sizeof(*list) * (nr_chan - 1));
+               free(old_list);
+               list[nr_chan - 1] = strdup(dirent->d_name);
+       }       
+
+       closedir(dir);
+
+       *channellist = list;
+       return nr_chan;
+error:
+       free(list);
+       *channellist = NULL;
+       return nr_chan;
+}
+
+static void lttctl_free_channellist(char **channellist, int n_channel)
+{
+       int i = 0;
+       for(; i < n_channel; ++i)
+               free(channellist[i]);
+       free(channellist);
+}
+
+int lttctl_setup_trace(const char *name)
+{
+       int ret;
+       char ctlfname[PATH_MAX];
+
+       if (!name) {
+               fprintf(stderr, "%s: args invalid\n", __func__);
+               ret = -EINVAL;
+               goto arg_error;
+       }
+
+       ret = lttctl_check_trace(name, 0);
+       if (ret)
+               goto arg_error;
+
+       sprintf(ctlfname, "%s/ltt/setup_trace", debugfsmntdir);
+
+       ret = lttctl_sendop(ctlfname, name);
+       if (ret) {
+               fprintf(stderr, "Setup trace failed\n");
+               goto op_err;
+       }
+
+       return 0;
+
+op_err:
+arg_error:
+       return ret;
+}
+
+int lttctl_destroy_trace(const char *name)
+{
+       int ret;
+       char ctlfname[PATH_MAX];
+
+       if (!name) {
+               fprintf(stderr, "%s: args invalid\n", __func__);
+               ret = -EINVAL;
+               goto arg_error;
+       }
+
+       ret = lttctl_check_trace(name, 1);
+       if (ret)
+               goto arg_error;
+
+       sprintf(ctlfname, "%s/ltt/destroy_trace", debugfsmntdir);
+
+       ret = lttctl_sendop(ctlfname, name);
+       if (ret) {
+               fprintf(stderr, "Destroy trace failed\n");
+               goto op_err;
+       }
+
+       return 0;
+
+op_err:
+arg_error:
+       return ret;
+}
+
+int lttctl_alloc_trace(const char *name)
+{
+       int ret;
+       char ctlfname[PATH_MAX];
+
+       if (!name) {
+               fprintf(stderr, "%s: args invalid\n", __func__);
+               ret = -EINVAL;
+               goto arg_error;
+       }
+
+       ret = lttctl_check_trace(name, 1);
+       if (ret)
+               goto arg_error;
+
+       sprintf(ctlfname, "%s/ltt/control/%s/alloc", debugfsmntdir, name);
+
+       ret = lttctl_sendop(ctlfname, "1");
+       if (ret) {
+               fprintf(stderr, "Allocate trace failed\n");
+               goto op_err;
+       }
+
+       return 0;
+
+op_err:
+arg_error:
+       return ret;
+}
+
+int lttctl_start(const char *name)
+{
+       int ret;
+       char ctlfname[PATH_MAX];
+
+       if (!name) {
+               fprintf(stderr, "%s: args invalid\n", __func__);
+               ret = -EINVAL;
+               goto arg_error;
+       }
+
+       ret = lttctl_check_trace(name, 1);
+       if (ret)
+               goto arg_error;
+
+       sprintf(ctlfname, "%s/ltt/control/%s/enabled", debugfsmntdir, name);
+
+       ret = lttctl_sendop(ctlfname, "1");
+       if (ret) {
+               fprintf(stderr, "Start trace failed\n");
+               goto op_err;
+       }
+
+       return 0;
+
+op_err:
+arg_error:
+       return ret;
+}
+
+int lttctl_pause(const char *name)
+{
+       int ret;
+       char ctlfname[PATH_MAX];
+
+       if (!name) {
+               fprintf(stderr, "%s: args invalid\n", __func__);
+               ret = -EINVAL;
+               goto arg_error;
+       }
+
+       ret = lttctl_check_trace(name, 1);
+       if (ret)
+               goto arg_error;
+
+       sprintf(ctlfname, "%s/ltt/control/%s/enabled", debugfsmntdir, name);
+
+       ret = lttctl_sendop(ctlfname, "0");
+       if (ret) {
+               fprintf(stderr, "Pause trace failed\n");
+               goto op_err;
+       }
+
+       return 0;
+
+op_err:
+arg_error:
+       return ret;
+}
+
+int lttctl_set_trans(const char *name, const char *trans)
+{
+       int ret;
+       char ctlfname[PATH_MAX];
+
+       if (!name) {
+               fprintf(stderr, "%s: args invalid\n", __func__);
+               ret = -EINVAL;
+               goto arg_error;
+       }
+
+       ret = lttctl_check_trace(name, 1);
+       if (ret)
+               goto arg_error;
+
+       sprintf(ctlfname, "%s/ltt/control/%s/trans", debugfsmntdir, name);
+
+       ret = lttctl_sendop(ctlfname, trans);
+       if (ret) {
+               fprintf(stderr, "Set transport failed\n");
+               goto op_err;
+       }
+
+       return 0;
+
+op_err:
+arg_error:
+       return ret;
+}
+
+static int __lttctl_set_channel_enable(const char *name, const char *channel,
+               int enable)
+{
+       int ret;
+       char ctlfname[PATH_MAX];
+
+       sprintf(ctlfname, "%s/ltt/control/%s/channel/%s/enable", debugfsmntdir,
+               name, channel);
+
+       ret = lttctl_sendop(ctlfname, enable ? "1" : "0");
+       if (ret)
+               fprintf(stderr, "Set channel's enable mode failed\n");
+
+       return ret;
+}
+int lttctl_set_channel_enable(const char *name, const char *channel,
+               int enable)
+{
+       int ret;
+       char **channellist;
+       int n_channel;
+
+       if (!name || !channel) {
+               fprintf(stderr, "%s: args invalid\n", __func__);
+               ret = -EINVAL;
+               goto arg_error;
+       }
+
+       ret = lttctl_check_trace(name, 1);
+       if (ret)
+               goto arg_error;
+
+       if (strcmp(channel, "all")) {
+               ret = __lttctl_set_channel_enable(name, channel, enable);
+               if (ret)
+                       goto op_err;
+       } else {
+               /* Don't allow set enable state for metadata channel */
+               n_channel = lttctl_get_channellist(name, &channellist, 0);
+               if (n_channel < 0) {
+                       fprintf(stderr, "%s: lttctl_get_channellist failed\n",
+                               __func__);
+                       ret = -ENOENT;
+                       goto op_err;
+               }
+
+               for (; n_channel > 0; n_channel--) {
+                       ret = __lttctl_set_channel_enable(name,
+                               channellist[n_channel - 1], enable);
+                       if (ret)
+                               goto op_err_clean;
+               }
+               lttctl_free_channellist(channellist, n_channel);
+       }
+
+       return 0;
+
+op_err_clean:
+       lttctl_free_channellist(channellist, n_channel);
+op_err:
+arg_error:
+       return ret;
+}
+
+static int __lttctl_set_channel_overwrite(const char *name, const char *channel,
+               int overwrite)
+{
+       int ret;
+       char ctlfname[PATH_MAX];
+
+       sprintf(ctlfname, "%s/ltt/control/%s/channel/%s/overwrite",
+               debugfsmntdir, name, channel);
+
+       ret = lttctl_sendop(ctlfname, overwrite ? "1" : "0");
+       if (ret)
+               fprintf(stderr, "Set channel's overwrite mode failed\n");
+
+       return ret;
+}
+int lttctl_set_channel_overwrite(const char *name, const char *channel,
+               int overwrite)
+{
+       int ret;
+       char **channellist;
+       int n_channel;
+
+       if (!name || !channel) {
+               fprintf(stderr, "%s: args invalid\n", __func__);
+               ret = -EINVAL;
+               goto arg_error;
+       }
+
+       ret = lttctl_check_trace(name, 1);
+       if (ret)
+               goto arg_error;
+
+       if (strcmp(channel, "all")) {
+               ret = __lttctl_set_channel_overwrite(name, channel, overwrite);
+               if (ret)
+                       goto op_err;
+       } else {
+               /* Don't allow set overwrite for metadata channel */
+               n_channel = lttctl_get_channellist(name, &channellist, 0);
+               if (n_channel < 0) {
+                       fprintf(stderr, "%s: lttctl_get_channellist failed\n",
+                               __func__);
+                       ret = -ENOENT;
+                       goto op_err;
+               }
+
+               for (; n_channel > 0; n_channel--) {
+                       ret = __lttctl_set_channel_overwrite(name,
+                               channellist[n_channel - 1], overwrite);
+                       if (ret)
+                               goto op_err_clean;
+               }
+               lttctl_free_channellist(channellist, n_channel);
+       }
+
+       return 0;
+
+op_err_clean:
+       lttctl_free_channellist(channellist, n_channel);
+op_err:
+arg_error:
+       return ret;
+}
+
+static int __lttctl_set_channel_subbuf_num(const char *name,
+               const char *channel, unsigned subbuf_num)
+{
+       int ret;
+       char ctlfname[PATH_MAX];
+       char opstr[32];
+
+       sprintf(ctlfname, "%s/ltt/control/%s/channel/%s/subbuf_num",
+               debugfsmntdir, name, channel);
+
+       sprintf(opstr, "%u", subbuf_num);
+
+       ret = lttctl_sendop(ctlfname, opstr);
+       if (ret)
+               fprintf(stderr, "Set channel's subbuf number failed\n");
+
+       return ret;
+}
+int lttctl_set_channel_subbuf_num(const char *name, const char *channel,
+               unsigned subbuf_num)
+{
+       int ret;
+       char **channellist;
+       int n_channel;
+
+       if (!name || !channel) {
+               fprintf(stderr, "%s: args invalid\n", __func__);
+               ret = -EINVAL;
+               goto arg_error;
+       }
+
+       ret = lttctl_check_trace(name, 1);
+       if (ret)
+               goto arg_error;
+
+       if (strcmp(channel, "all")) {
+               ret = __lttctl_set_channel_subbuf_num(name, channel,
+                       subbuf_num);
+               if (ret)
+                       goto op_err;
+       } else {
+               /* allow set subbuf_num for metadata channel */
+               n_channel = lttctl_get_channellist(name, &channellist, 1);
+               if (n_channel < 0) {
+                       fprintf(stderr, "%s: lttctl_get_channellist failed\n",
+                               __func__);
+                       ret = -ENOENT;
+                       goto op_err;
+               }
+
+               for (; n_channel > 0; n_channel--) {
+                       ret = __lttctl_set_channel_subbuf_num(name,
+                               channellist[n_channel - 1], subbuf_num);
+                       if (ret)
+                               goto op_err_clean;
+               }
+               lttctl_free_channellist(channellist, n_channel);
+       }
+
+       return 0;
+
+op_err_clean:
+       lttctl_free_channellist(channellist, n_channel);
+op_err:
+arg_error:
+       return ret;
+}
+
+static int __lttctl_set_channel_subbuf_size(const char *name,
+               const char *channel, unsigned subbuf_size)
+{
+       int ret;
+       char ctlfname[PATH_MAX];
+       char opstr[32];
+
+       sprintf(ctlfname, "%s/ltt/control/%s/channel/%s/subbuf_size",
+               debugfsmntdir, name, channel);
+
+       sprintf(opstr, "%u", subbuf_size);
+
+       ret = lttctl_sendop(ctlfname, opstr);
+       if (ret)
+               fprintf(stderr, "Set channel's subbuf size failed\n");
+}
+int lttctl_set_channel_subbuf_size(const char *name, const char *channel,
+               unsigned subbuf_size)
+{
+       int ret;
+       char **channellist;
+       int n_channel;
+
+       if (!name || !channel) {
+               fprintf(stderr, "%s: args invalid\n", __func__);
+               ret = -EINVAL;
+               goto arg_error;
+       }
+
+       ret = lttctl_check_trace(name, 1);
+       if (ret)
+               goto arg_error;
+
+       if (strcmp(channel, "all")) {
+               ret = __lttctl_set_channel_subbuf_size(name, channel,
+                       subbuf_size);
+               if (ret)
+                       goto op_err;
+       } else {
+               /* allow set subbuf_size for metadata channel */
+               n_channel = lttctl_get_channellist(name, &channellist, 1);
+               if (n_channel < 0) {
+                       fprintf(stderr, "%s: lttctl_get_channellist failed\n",
+                               __func__);
+                       ret = -ENOENT;
+                       goto op_err;
+               }
+
+               for (; n_channel > 0; n_channel--) {
+                       ret = __lttctl_set_channel_subbuf_size(name,
+                               channellist[n_channel - 1], subbuf_size);
+                       if (ret)
+                               goto op_err_clean;
+               }
+               lttctl_free_channellist(channellist, n_channel);
+       }
+
+       return 0;
+
+op_err_clean:
+       lttctl_free_channellist(channellist, n_channel);
+op_err:
+arg_error:
+       return ret;
+}
+
+int getdebugfsmntdir(char *mntdir)
+{
+       char mnt_dir[PATH_MAX];
+       char mnt_type[PATH_MAX];
+       int trymount_done = 0;
+
+       FILE *fp = fopen("/proc/mounts", "r");
+       if (!fp)
+               return -EINVAL;
+
+find_again:
+       while (1) {
+               if (fscanf(fp, "%*s %s %s %*s %*s %*s", mnt_dir, mnt_type) <= 0)
+                       break;
+
+               if (!strcmp(mnt_type, "debugfs")) {
+                       strcpy(mntdir, mnt_dir);
+                       return 0;
+               }
+       }
+
+       if (!trymount_done) {
+               mount("debugfs", "/sys/kernel/debug/", "debugfs", 0, NULL);
+               trymount_done = 1;
+               goto find_again;
+       }
+
+       return -ENOENT;
+}
This page took 0.031127 seconds and 4 git commands to generate.