diff options
Diffstat (limited to 'runtime/staprun/common.c')
-rw-r--r-- | runtime/staprun/common.c | 307 |
1 files changed, 307 insertions, 0 deletions
diff --git a/runtime/staprun/common.c b/runtime/staprun/common.c new file mode 100644 index 00000000..cbe88be0 --- /dev/null +++ b/runtime/staprun/common.c @@ -0,0 +1,307 @@ +/* -*- linux-c -*- + * + * common.c - staprun suid/user common code + * + * This file is part of systemtap, and is free software. You can + * redistribute it and/or modify it under the terms of the GNU General + * Public License (GPL); either version 2, or (at your option) any + * later version. + * + * Copyright (C) 2007 Red Hat Inc. + */ + +#include "staprun.h" +#include <sys/types.h> +#include <unistd.h> +#include <sys/utsname.h> + +/* variables needed by parse_args() */ +int verbose; +int target_pid; +unsigned int buffer_size; +char *target_cmd; +char *outfile_name; +int attach_mod; +int load_only; + +/* module variables */ +char *modname = NULL; +char *modpath = ""; +char *modoptions[MAXMODOPTIONS]; + +int initialized = 0; +int control_channel = 0; + +void parse_args(int argc, char **argv) +{ + int c; + + /* Initialize option variables. */ + verbose = 0; + target_pid = 0; + buffer_size = 0; + target_cmd = NULL; + outfile_name = NULL; + attach_mod = 0; + load_only = 0; + + while ((c = getopt(argc, argv, "ALvb:t:d:c:o:x:")) != EOF) { + switch (c) { + case 'v': + verbose++; + break; + case 'b': + buffer_size = (unsigned)atoi(optarg); + if (buffer_size < 1 || buffer_size > 64) { + err("Invalid buffer size '%d' (should be 1-64).\n", buffer_size); + usage(argv[0]); + } + break; + case 't': + case 'x': + target_pid = atoi(optarg); + break; + case 'd': + /* obsolete internal option used by stap */ + break; + case 'c': + target_cmd = optarg; + break; + case 'o': + outfile_name = optarg; + break; + case 'A': + attach_mod = 1; + break; + case 'L': + load_only = 1; + break; + default: + usage(argv[0]); + } + } + + if (attach_mod && load_only) { + err("You can't specify the '-A' and '-L' options together.\n"); + usage(argv[0]); + } + + 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]); + } + + 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]); + } + + 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]); + } +} + +void usage(char *prog) +{ + err("\n%s [-v] [-c cmd ] [-x pid] [-u user]\n" + "\t[-A|-L] [-b bufsize] [-o FILE] MODULE [module-options]\n", prog); + err("-v Increase verbosity.\n"); + err("-c cmd Command \'cmd\' will be run and staprun will\n"); + err(" exit when it does. The '_stp_target' variable\n"); + err(" will contain the pid for the command.\n"); + err("-x pid Sets the '_stp_target' variable to pid.\n"); + err("-o FILE Send output to FILE.\n"); + err("-b buffer size The systemtap module specifies a buffer size.\n"); + err(" Setting one here will override that value. The\n"); + err(" value should be an integer between 1 and 64\n"); + err(" which be assumed to be the buffer size in MB.\n"); + err(" That value will be per-cpu in bulk mode.\n"); + err("-L Load module and start probes, then detach.\n"); + err("-A Attach to loaded systemtap module.\n"); + err("MODULE can be either a module name or a module path. If a\n"); + err("module name is used, it is looked for in the following\n"); + err("directory: /lib/modules/`uname -r`/systemtap\n"); + exit(1); +} + +/* + * parse_modpath. Here's how this code interprets the global modpath: + * + * (1) If modpath contains a '/', it is assumed to be an absolute or + * relative file path to a module (such as "../foo.ko" or + * "/tmp/stapXYZ/stap_foo.ko"). + * + * (2) If modpath doesn't contain a '/' and ends in '.ko', it is a file + * path to a module in the current directory (such as "foo.ko"). + * + * (3) If modpath doesn't contain a '/' and doesn't end in '.ko', then + * it is a module name and the full pathname of the module is + * '/lib/modules/`uname -r`/systemtap/PATH.ko'. For instance, if + * modpath was "foo", the full module pathname would be + * '/lib/modules/`uname -r`/systemtap/foo.ko'. + */ +void parse_modpath(const char *inpath) +{ + const char *mptr = rindex(inpath, '/'); + char *ptr; + + dbug(3, "inpath=%s\n", inpath); + + /* If we couldn't find a '/', ... */ + if (mptr == NULL) { + size_t plen = strlen(inpath); + + /* If the path ends with the '.ko' file extension, + * then we've got a module in the current + * directory. */ + 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 { + /* If we didn't find the '.ko' file extension, then + * we've just got a module name, not a module path. + * Look for the module in /lib/modules/`uname + * -r`/systemtap. */ + + struct utsname utsbuf; + int len; + #define MODULE_PATH "/lib/modules/%s/systemtap/%s.ko" + + /* First, we need to figure out what the + * kernel version. */ + if (uname(&utsbuf) != 0) { + perr("Unable to determine kernel version, uname failed"); + exit(-1); + } + + /* Build the module path, which will look like + * '/lib/modules/KVER/systemtap/{path}.ko'. */ + 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 = rindex(modpath, '/'); + mptr++; + } + } else { + /* We found a '/', so the module name starts with the next + * character. */ + 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 = rindex(modname, '.'); + if (ptr) + *ptr = '\0'; + + /* We've finally got a real modname. Make sure it isn't too + * long. If it is too long, init_module() will appear to + * work, but the module can't be removed (because you end up + * with control characters in the module name). */ + if (strlen(modname) > MODULE_NAME_LEN) { + err("ERROR: 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); + if (initialized) + _exit(3); + else + _exit(1); +} + +void setup_signals(void) +{ + sigset_t s; + struct sigaction a; + + /* blocking all signals while we set things up */ + sigfillset(&s); +#ifdef SINGLE_THREADED + sigprocmask(SIG_SETMASK, &s, NULL); +#else + pthread_sigmask(SIG_SETMASK, &s, NULL); +#endif + /* set some of them to be ignored */ + memset(&a, 0, sizeof(a)); + sigfillset(&a.sa_mask); + a.sa_handler = SIG_IGN; + sigaction(SIGPIPE, &a, NULL); + sigaction(SIGUSR2, &a, NULL); + + /* for serious errors, handle them in fatal_handler */ + 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); + + /* unblock all signals */ + sigemptyset(&s); + +#ifdef SINGLE_THREADED + sigprocmask(SIG_SETMASK, &s, NULL); +#else + pthread_sigmask(SIG_SETMASK, &s, NULL); +#endif +} + +/** + * send_request - send request to kernel over control channel + * @type: the relay-app command id + * @data: pointer to the data to be sent + * @len: length of the data to be sent + * + * Returns 0 on success, negative otherwise. + */ +int send_request(int type, void *data, int len) +{ + char buf[1024]; + + /* Before doing memcpy, make sure 'buf' is big enough. */ + if ((len + 4) > (int)sizeof(buf)) { + _err("exceeded maximum send_request size.\n"); + return -1; + } + memcpy(buf, &type, 4); + memcpy(&buf[4], data, len); + return write(control_channel, buf, len+4); +} |