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; }