staprun/common.c - systemtap
 Global variables defined
 
 Functions defined
 
 Macros defined
 
 Source code
  
#include "staprun.h"
#include <sys/types.h>
#include <unistd.h>
#include <sys/utsname.h>
#include <assert.h>
#include <string.h>
#include "../git_version.h"
#include "../version.h"
int verbose;
int suppress_warnings;
int target_pid;
unsigned int buffer_size;
unsigned int reader_timeout_ms;
char *target_cmd;
char *outfile_name;
int rename_mod;
int attach_mod;
int delete_mod;
int load_only;
int need_uprobes;
const char *uprobes_path = NULL;
int daemon_mode;
off_t fsize_max;
int fnum_max;
int remote_id;
const char *remote_uri;
int relay_basedir_fd;
int color_errors;
color_modes color_mode;
char *modname = NULL;
char *modpath = "";
char *modoptions[MAXMODOPTIONS];
int control_channel = -1; 
static char path_buf[PATH_MAX];
static char *get_abspath(char *path)
{
    int len;
    if (path[0] == '/')
        return path;
    len = strlen(getcwd(path_buf, PATH_MAX));
    if (len + 2 + strlen(path) >= PATH_MAX)
        return NULL;
    path_buf[len] = '/';
        strcpy(&path_buf[len + 1], path);
    return path_buf;
}
int make_outfile_name(char *buf, int max, int fnum, int cpu, time_t t, int bulk)
{
    int len;
    if (PATH_MAX < max)
        max = PATH_MAX;
    len = stap_strfloctime(buf, max, outfile_name, t);
    if (len < 0) {
        err(_("Invalid FILE name format\n"));
        return -1;
    }
        if (strcmp(outfile_name, "/dev/null") == 0) {
                strcpy(buf, "/dev/null");
    } else {
        if (bulk) {
            if (snprintf_chk(&buf[len], max - len, "_cpu%d.%d",
                     cpu, fnum))
                return -1;
        } else {
                        if (snprintf_chk(&buf[len], max - len, ".%d", fnum))
                return -1;
        }
    }
    return 0;
}
void parse_args(int argc, char **argv)
{
    int c;
    char *s;
        verbose = 0;
    suppress_warnings = 0;
    target_pid = 0;
    buffer_size = 0;
        reader_timeout_ms = 0;
    target_cmd = NULL;
    outfile_name = NULL;
    rename_mod = 0;
    attach_mod = 0;
    delete_mod = 0;
    load_only = 0;
    need_uprobes = 0;
    daemon_mode = 0;
    fsize_max = 0;
    fnum_max = 0;
        remote_id = -1;
        remote_uri = NULL;
        relay_basedir_fd = -1;
        color_mode = color_auto;
        color_errors = isatty(STDERR_FILENO)
                && strcmp(getenv("TERM") ?: "notdumb", "dumb");
    while ((c = getopt(argc, argv, "ALu::vhb:t:dc:o:x:S:DwRr:VT:C:"
#ifdef HAVE_OPENAT
                           "F:"
#endif
                        )) != EOF) {
        switch (c) {
        case 'u':
            need_uprobes = 1;
            if (optarg)
              uprobes_path = strdup (optarg);
            break;
        case 'v':
            verbose++;
            break;
        case 'w':
            suppress_warnings=1;
            break;
        case 'b':
            buffer_size = (unsigned)atoi(optarg);
            if (buffer_size < 1 || buffer_size > 4095) {
                err(_("Invalid buffer size '%d' (should be 1-4095).\n"), buffer_size);
                usage(argv[0],1);
            }
            break;
        case 't':
        case 'x':
            target_pid = atoi(optarg);
            break;
        case 'd':
                        delete_mod = 1;
            break;
        case 'c':
            target_cmd = optarg;
            break;
        case 'o':
            outfile_name = optarg;
            break;
        case 'R':
            rename_mod = 1;
            break;
        case 'A':
            attach_mod = 1;
            break;
        case 'L':
            load_only = 1;
            break;
        case 'D':
            daemon_mode = 1;
            break;
        case 'F':
            relay_basedir_fd = atoi(optarg);
            if (relay_basedir_fd < 0) {
                err(_("Invalid file descriptor option '%s'.\n"), optarg);
                usage(argv[0],1);
            }
            break;
        case 'S':
            assert(optarg != 0);             fsize_max = strtoul(optarg, &s, 10);
            fsize_max <<= 20;
            if (s[0] == ',')
                fnum_max = (int)strtoul(&s[1], &s, 10);
            if (s[0] != '\0') {
                err(_("Invalid file size option '%s'.\n"), optarg);
                usage(argv[0],1);
            }
            break;
        case 'r':
            assert(optarg != 0);                         remote_id = strtoul(optarg, &s, 10);
            if (s[0] == ':')
                                remote_uri = strdup (& s[1]);
                        if (remote_id < 0 || remote_uri == 0 || remote_uri[0] == '\0') {
                                err(_("Cannot process remote id option '%s'.\n"), optarg);
                usage(argv[0],1);
                        }
            break;
        case 'V':
                        printf(_("Systemtap module loader/runner (version %s, %s)\n"
                              "Copyright (C) 2005-2014 Red Hat, Inc. and others\n"
                              "This is free software; see the source for copying conditions.\n"),
                            VERSION, STAP_EXTENDED_VERSION);
                        _exit(0);
                        break;
                case 'h':
                        usage(argv[0],0);
                        break;
                case 'T':
                        reader_timeout_ms = (unsigned)atoi(optarg);
                        if (reader_timeout_ms < 1) {
                                err(_("Invalid reader timeout value '%d' (should be >= 1).\n"), reader_timeout_ms);
                                usage(argv[0],1);
                        }
                        break;
        case 'C':
            assert(optarg != 0);             if (!strcmp(optarg, "never"))
                color_mode = color_never;
            else if (!strcmp(optarg, "auto"))
                color_mode = color_auto;
            else if (!strcmp(optarg, "always"))
                color_mode = color_always;
            else {
                err(_("Invalid option '%s' for -C."), optarg);
                usage(argv[0],1);
            }
            color_errors = color_mode == color_always
                || (color_mode == color_auto && isatty(STDERR_FILENO)
                        && strcmp(getenv("TERM") ?: "notdumb", "dumb"));
            break;
        default:
            usage(argv[0],1);
        }
    }
    if (outfile_name) {
        char tmp[PATH_MAX];
        int ret;
        outfile_name = get_abspath(outfile_name);
        if (outfile_name == NULL) {
            err(_("File name is too long.\n"));
            usage(argv[0],1);
        }
        ret = stap_strfloctime(tmp, PATH_MAX - 18,                        outfile_name, time(NULL));
        if (ret < 0) {
            err(_("Filename format is invalid or too long.\n"));
            usage(argv[0],1);
        }
    }
    if (attach_mod && load_only) {
        err(_("You can't specify the '-A' and '-L' options together.\n"));
        usage(argv[0],1);
    }
    if (attach_mod && buffer_size) {
        err(_("You can't specify the '-A' and '-b' options together.  The '-b'\n"
            "buffer size option only has an effect when the module is inserted.\n"));
        usage(argv[0],1);
    }
    if (attach_mod && target_cmd) {
        err(_("You can't specify the '-A' and '-c' options together.  The '-c cmd'\n"
            "option used to start a command only has an effect when the module\n"
            "is inserted.\n"));
        usage(argv[0],1);
    }
    if (attach_mod && target_pid) {
        err(_("You can't specify the '-A' and '-x' options together.  The '-x pid'\n"
            "option only has an effect when the module is inserted.\n"));
        usage(argv[0],1);
    }
    if (target_cmd && target_pid) {
        err(_("You can't specify the '-c' and '-x' options together.\n"));
        usage(argv[0],1);
    }
    if (daemon_mode && load_only) {
        err(_("You can't specify the '-D' and '-L' options together.\n"));
        usage(argv[0],1);
    }
    if (daemon_mode && delete_mod) {
        err(_("You can't specify the '-D' and '-d' options together.\n"));
        usage(argv[0],1);
    }
    if (daemon_mode && target_cmd) {
        err(_("You can't specify the '-D' and '-c' options together.\n"));
        usage(argv[0],1);
    }
    if (daemon_mode && outfile_name == NULL) {
        err(_("You have to specify output FILE with '-D' option.\n"));
        usage(argv[0],1);
    }
    if (outfile_name == NULL && fsize_max != 0) {
        err(_("You have to specify output FILE with '-S' option.\n"));
        usage(argv[0],1);
    }
}
void usage(char *prog, int rc)
{
    printf(_("\n%s [-v] [-w] [-V] [-h] [-u] [-c cmd ] [-x pid] [-u user] [-A|-L|-d] [-C WHEN]\n"
                "\t[-b bufsize] [-R] [-r N:URI] [-o FILE [-D] [-S size[,N]]] MODULE [module-options]\n"), prog);
    printf(_("-v              Increase verbosity.\n"
    "-V              Print version number and exit.\n"
    "-h              Print this help text and exit.\n"
    "-w              Suppress warnings.\n"
    "-u              Load uprobes.ko\n"
    "-c cmd          Command \'cmd\' will be run and staprun will\n"
    "                exit when it does.  The '_stp_target' variable\n"
    "                will contain the pid for the command.\n"
    "-x pid          Sets the '_stp_target' variable to pid.\n"
    "-o FILE         Send output to FILE. This supports strftime(3)\n"
    "                formats for FILE.\n"
    "-b buffer size  The systemtap module specifies a buffer size.\n"
    "                Setting one here will override that value.  The\n"
    "                value should be an integer between 1 and 4095 \n"
    "                which be assumed to be the buffer size in MB.\n"
    "                That value will be per-cpu in bulk mode.\n"
    "-L              Load module and start probes, then detach.\n"
    "-A              Attach to loaded systemtap module.\n"
    "-C WHEN         Enable colored errors. WHEN must be either 'auto',\n"
    "                'never', or 'always'. Set to 'auto' by default.\n"
    "-d              Delete a module.  Only detached or unused modules\n"
    "                the user has permission to access will be deleted. Use \"*\"\n"
    "                (quoted) to delete all unused modules.\n"
        "-R              Have staprun create a new name for the module before\n"
        "                inserting it. This allows the same module to be inserted\n"
        "                more than once.\n"
        "-r N:URI        Pass N:URI data to tapset functions remote_id()/remote_uri().\n"
    "-D              Run in background. This requires '-o' option.\n"
    "-S size[,N]     Switches output file to next file when the size\n"
    "                of file reaches the specified size. The value\n"
    "                should be an integer greater than 1 which is\n"
    "                assumed to be the maximum file size in MB.\n"
    "                When the number of output files reaches N, it\n"
    "                switches to the first output file. You can omit\n"
    "                the second argument.\n"
        "-T timeout      Specifies upper limit on amount of time reader thread\n"
        "                will wait for new full trace buffer. Value should be an\n"
        "                integer >= 1, which is timeout value in ms. Default 200ms.\n"
#ifdef HAVE_OPENAT
        "-F fd           Specifies file descriptor for module relay directory\n"
#endif
    "\n"
    "MODULE can be either a module name or a module path.  If a\n"
    "module name is used, it is searched in the following directory:\n"));
        {
                struct utsname utsbuf;
                int rc = uname (& utsbuf);
                if (! rc)
                        printf("/lib/modules/%s/systemtap\n", utsbuf.release);
                else
                        printf("/lib/modules/`uname -r`/systemtap\n");
        }
    exit(rc);
}
void parse_modpath(const char *inpath)
{
    const char *mptr = strrchr(inpath, '/');
    char *ptr;
    dbug(3, "inpath=%s\n", inpath);
        if (mptr == NULL) {
        size_t plen = strlen(inpath);
                if (plen > 3 && strcmp(&inpath[plen - 3], ".ko") == 0) {
            mptr = inpath;
            modpath = strdup(inpath);
            if (!modpath) {
                err(_("Memory allocation failed. Exiting.\n"));
                exit(1);
            }
        } else {
            
            struct utsname utsbuf;
            int len;
            #define MODULE_PATH "/lib/modules/%s/systemtap/%s.ko"
                        if (uname(&utsbuf) != 0) {
                perr(_("Unable to determine kernel version, uname failed"));
                exit(-1);
            }
                        len = sizeof(MODULE_PATH) + sizeof(utsbuf.release) + strlen(inpath);
            modpath = malloc(len);
            if (!modpath) {
                err(_("Memory allocation failed. Exiting.\n"));
                exit(1);
            }
            if (snprintf_chk(modpath, len, MODULE_PATH, utsbuf.release, inpath))
                exit(-1);
            dbug(2, "modpath=\"%s\"\n", modpath);
            mptr = strrchr(modpath, '/');
            mptr++;
        }
    } else {
                mptr++;
        modpath = strdup(inpath);
        if (!modpath) {
            err(_("Memory allocation failed. Exiting.\n"));
            exit(1);
        }
    }
    modname = strdup(mptr);
    if (!modname) {
        err(_("Memory allocation failed. Exiting.\n"));
        exit(1);
    }
    ptr = strrchr(modname, '.');
    if (ptr)
        *ptr = '\0';
        if (strlen(modname) > MODULE_NAME_LEN) {
        err(_("Module name ('%s') is too long.\n"), modname);
        exit(1);
    }
}
#define ERR_MSG "\nUNEXPECTED FATAL ERROR in staprun. Please file a bug report.\n"
static void fatal_handler (int signum)
{
        int rc;
        char *str = strsignal(signum);
        rc = write (STDERR_FILENO, ERR_MSG, sizeof(ERR_MSG));
        rc = write (STDERR_FILENO, str, strlen(str));
        rc = write (STDERR_FILENO, "\n", 1);
        (void) rc;     _exit(1);
}
void setup_signals(void)
{
    sigset_t s;
    struct sigaction a;
        sigfillset(&s);
#ifdef SINGLE_THREADED
    sigprocmask(SIG_SETMASK, &s, NULL);
#else
    pthread_sigmask(SIG_SETMASK, &s, NULL);
#endif
        memset(&a, 0, sizeof(a));
    sigfillset(&a.sa_mask);
    a.sa_handler = SIG_IGN;
    sigaction(SIGPIPE, &a, NULL);
    sigaction(SIGUSR2, &a, NULL);
        a.sa_handler = fatal_handler;
    sigaction(SIGBUS, &a, NULL);
    sigaction(SIGFPE, &a, NULL);
    sigaction(SIGILL, &a, NULL);
    sigaction(SIGSEGV, &a, NULL);
    sigaction(SIGXCPU, &a, NULL);
    sigaction(SIGXFSZ, &a, NULL);
        sigemptyset(&s);
#ifdef SINGLE_THREADED
    sigprocmask(SIG_SETMASK, &s, NULL);
#else
    pthread_sigmask(SIG_SETMASK, &s, NULL);
#endif
}
int set_clexec(int fd)
{
    int val;
    if ((val = fcntl(fd, F_GETFD, 0)) < 0)
        goto err;
    if ((val = fcntl(fd, F_SETFD, val | FD_CLOEXEC)) < 0)
        goto err;
    return 0;
err:
    perr("fcntl failed");
    close(fd);
    return -1;
}
int send_request(int type, void *data, int len)
{
    char buf[1024];
        int rc = 0;
    PROBE3(stapio, send__ctlmsg, type, data, len);
        if ((len + sizeof(type)) > (int)sizeof(buf)) {
        _err(_("exceeded maximum send_request size.\n"));
        return -1;
    }
    memcpy(buf, &type, sizeof (type));
    memcpy(&buf[sizeof (type)], data, len);
    errno = 0;
        assert (control_channel >= 0);
    rc = write (control_channel, buf, len + sizeof (type));
        if (rc < 0) return rc;
        return (rc != len && rc != len + (int)sizeof (type));
}
#include <stdarg.h>
static int use_syslog = 0;
void eprintf(const char *fmt, ...)
{
    va_list va;
    va_start(va, fmt);
    if (use_syslog)
        vsyslog(LOG_ERR, fmt, va);
    else
        vfprintf(stderr, fmt, va);
    va_end(va);
}
void switch_syslog(const char *name)
{
    openlog(name, LOG_PID, LOG_DAEMON);
    use_syslog = 1;
    color_errors = 0;
}
void print_color(const char *type)
{
    if (!color_errors)
        return;
    if (type == NULL)         eprintf("\033[m\033[K");
    else {
        char *seq = parse_stap_color(type);
        if (seq != NULL) {
            eprintf("\033[");
            eprintf(seq);
            eprintf("m\033[K");
            free(seq);
        }
    }
}
char *parse_stap_color(const char *type)
{
    const char *key, *col, *eq;
    int n = strlen(type);
    int done = 0;
    key = getenv("SYSTEMTAP_COLORS");
    if (key == NULL)
        key = "error=01;31:warning=00;33:source=00;34:caret=01:token=01";
    else if (*key == '\0')
        return NULL; 
    while (!done) {
        if (!(col = strchr(key, ':'))) {
            col = strchr(key, '\0');
            done = 1;
        }
        if (!((eq = strchr(key, '=')) && eq < col))
            return NULL;         if (!(key < eq && eq < col-1))
            return NULL;         if (strspn(eq+1, "0123456789;") < (size_t)(col-eq-1))
            return NULL;         if (eq-key == n && !strncmp(key, type, n))
            return strndup(eq+1, col-eq-1);
        if (!done) key = col+1;     }
    return NULL; }