/*
* Copyright (c) 2016 Dmitry V. Levin <ldv@altlinux.org>
+ * Copyright (c) 2016-2017 The strace developers.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
*/
#include "defs.h"
+#include "nsig.h"
+#include <regex.h>
typedef unsigned int number_slot_t;
#define BITS_PER_SLOT (sizeof(number_slot_t) * 8)
struct number_set signal_set;
static struct number_set abbrev_set[SUPPORTED_PERSONALITIES];
-static struct number_set fault_set[SUPPORTED_PERSONALITIES];
+static struct number_set inject_set[SUPPORTED_PERSONALITIES];
static struct number_set raw_set[SUPPORTED_PERSONALITIES];
static struct number_set trace_set[SUPPORTED_PERSONALITIES];
static struct number_set verbose_set[SUPPORTED_PERSONALITIES];
return done;
}
+static void
+regerror_msg_and_die(int errcode, const regex_t *preg,
+ const char *str, const char *pattern)
+{
+ char buf[512];
+
+ regerror(errcode, preg, buf, sizeof(buf));
+ error_msg_and_die("%s: %s: %s", str, pattern, buf);
+}
+
+static bool
+qualify_syscall_regex(const char *s, struct number_set *set)
+{
+ regex_t preg;
+ int rc;
+
+ if ((rc = regcomp(&preg, s, REG_EXTENDED | REG_NOSUB)) != 0)
+ regerror_msg_and_die(rc, &preg, "regcomp", s);
+
+ unsigned int p;
+ bool found = false;
+ for (p = 0; p < SUPPORTED_PERSONALITIES; ++p) {
+ unsigned int i;
+
+ for (i = 0; i < nsyscall_vec[p]; ++i) {
+ if (!sysent_vec[p][i].sys_name)
+ continue;
+ rc = regexec(&preg, sysent_vec[p][i].sys_name,
+ 0, NULL, 0);
+ if (rc == REG_NOMATCH)
+ continue;
+ else if (rc)
+ regerror_msg_and_die(rc, &preg, "regexec", s);
+ add_number_to_set(i, &set[p]);
+ found = true;
+ }
+ }
+
+ regfree(&preg);
+ return found;
+}
+
static unsigned int
lookup_class(const char *s)
{
{ "signal", TRACE_SIGNAL },
{ "ipc", TRACE_IPC },
{ "network", TRACE_NETWORK },
+ { "%desc", TRACE_DESC },
+ { "%file", TRACE_FILE },
+ { "%memory", TRACE_MEMORY },
+ { "%process", TRACE_PROCESS },
+ { "%signal", TRACE_SIGNAL },
+ { "%ipc", TRACE_IPC },
+ { "%network", TRACE_NETWORK },
+ { "%stat", TRACE_STAT },
+ { "%lstat", TRACE_LSTAT },
+ { "%fstat", TRACE_FSTAT },
+ { "%%stat", TRACE_STAT_LIKE },
+ { "%statfs", TRACE_STATFS },
+ { "%fstatfs", TRACE_FSTATFS },
+ { "%%statfs", TRACE_STATFS_LIKE },
};
unsigned int i;
static bool
qualify_syscall(const char *token, struct number_set *set)
{
+ bool ignore_fail = false;
+
+ while (*token == '?') {
+ token++;
+ ignore_fail = true;
+ }
if (*token >= '0' && *token <= '9')
- return qualify_syscall_number(token, set);
+ return qualify_syscall_number(token, set) || ignore_fail;
+ if (*token == '/')
+ return qualify_syscall_regex(token + 1, set) || ignore_fail;
return qualify_syscall_class(token, set)
- || qualify_syscall_name(token, set);
+ || qualify_syscall_name(token, set)
+ || ignore_fail;
}
/*
}
}
-/*
- * Returns NULL if STR does not start with PREFIX,
- * or a pointer to the first char in STR after PREFIX.
- */
-static const char *
-strip_prefix(const char *prefix, const char *str)
-{
- size_t len = strlen(prefix);
-
- return strncmp(prefix, str, len) ? NULL : str + len;
-}
-
static int
find_errno_by_name(const char *name)
{
unsigned int i;
for (i = 1; i < nerrnos; ++i) {
- if (errnoent[i] && (strcmp(name, errnoent[i]) == 0))
+ if (errnoent[i] && (strcasecmp(name, errnoent[i]) == 0))
return i;
}
}
static bool
-parse_fault_token(const char *const token, struct fault_opts *const fopts)
+parse_inject_token(const char *const token, struct inject_opts *const fopts,
+ const bool fault_tokens_only)
{
const char *val;
int intval;
- if ((val = strip_prefix("when=", token))) {
+ if ((val = STR_STRIP_PREFIX(token, "when=")) != token) {
/*
- * == 1+1
+ * == 1+1
* F == F+0
* F+ == F+1
* F+S
/* F == F+0 */
fopts->step = 0;
}
- } else if ((val = strip_prefix("error=", token))) {
- intval = string_to_uint_upto(val, 4095);
+ } else if ((val = STR_STRIP_PREFIX(token, "error=")) != token) {
+ if (fopts->rval != INJECT_OPTS_RVAL_DEFAULT)
+ return false;
+ intval = string_to_uint_upto(val, MAX_ERRNO_VALUE);
if (intval < 0)
intval = find_errno_by_name(val);
if (intval < 1)
return false;
- fopts->err = intval;
+ fopts->rval = -intval;
+ } else if (!fault_tokens_only
+ && (val = STR_STRIP_PREFIX(token, "retval=")) != token) {
+ if (fopts->rval != INJECT_OPTS_RVAL_DEFAULT)
+ return false;
+ intval = string_to_uint(val);
+ if (intval < 0)
+ return false;
+ fopts->rval = intval;
+ } else if (!fault_tokens_only
+ && (val = STR_STRIP_PREFIX(token, "signal=")) != token) {
+ intval = sigstr_to_uint(val);
+ if (intval < 1 || intval > NSIG_BYTES * 8)
+ return false;
+ fopts->signo = intval;
} else {
return false;
}
}
static char *
-parse_fault_expression(const char *const s, char **buf,
- struct fault_opts *const fopts)
+parse_inject_expression(const char *const s, char **buf,
+ struct inject_opts *const fopts,
+ const bool fault_tokens_only)
{
char *saveptr = NULL;
char *name = NULL;
token = strtok_r(NULL, ":", &saveptr)) {
if (!name)
name = token;
- else if (!parse_fault_token(token, fopts))
+ else if (!parse_inject_token(token, fopts, fault_tokens_only))
goto parse_error;
}
}
static void
-qualify_fault(const char *const str)
+qualify_inject_common(const char *const str,
+ const bool fault_tokens_only,
+ const char *const description)
{
- struct fault_opts opts = {
+ struct inject_opts opts = {
.first = 1,
.step = 1,
- .err = 0
+ .rval = INJECT_OPTS_RVAL_DEFAULT,
+ .signo = 0
};
char *buf = NULL;
- char *name = parse_fault_expression(str, &buf, &opts);
+ char *name = parse_inject_expression(str, &buf, &opts, fault_tokens_only);
if (!name) {
- error_msg_and_die("invalid %s '%s'", "fault argument", str);
+ error_msg_and_die("invalid %s '%s'", description, str);
}
+ /* If neither of retval, error, or signal is specified, then ... */
+ if (opts.rval == INJECT_OPTS_RVAL_DEFAULT && !opts.signo) {
+ if (fault_tokens_only) {
+ /* in fault= syntax the default error code is ENOSYS. */
+ opts.rval = -ENOSYS;
+ } else {
+ /* in inject= syntax this is not allowed. */
+ error_msg_and_die("invalid %s '%s'", description, str);
+ }
+ }
struct number_set tmp_set[SUPPORTED_PERSONALITIES];
memset(tmp_set, 0, sizeof(tmp_set));
- qualify_syscall_tokens(name, tmp_set, "fault argument");
+ qualify_syscall_tokens(name, tmp_set, description);
free(buf);
/*
- * Initialize fault_vec accourding to tmp_set.
- * Merge tmp_set into fault_set.
+ * Initialize inject_vec accourding to tmp_set.
+ * Merge tmp_set into inject_set.
*/
unsigned int p;
for (p = 0; p < SUPPORTED_PERSONALITIES; ++p) {
continue;
}
- if (!fault_vec[p]) {
- fault_vec[p] = xcalloc(nsyscall_vec[p],
- sizeof(*fault_vec[p]));
+ if (!inject_vec[p]) {
+ inject_vec[p] = xcalloc(nsyscall_vec[p],
+ sizeof(*inject_vec[p]));
}
unsigned int i;
for (i = 0; i < nsyscall_vec[p]; ++i) {
if (is_number_in_set(i, &tmp_set[p])) {
- add_number_to_set(i, &fault_set[p]);
- fault_vec[p][i] = opts;
+ add_number_to_set(i, &inject_set[p]);
+ inject_vec[p][i] = opts;
}
}
}
}
+static void
+qualify_fault(const char *const str)
+{
+ qualify_inject_common(str, true, "fault argument");
+}
+
+static void
+qualify_inject(const char *const str)
+{
+ qualify_inject_common(str, false, "inject argument");
+}
+
static const struct qual_options {
const char *name;
void (*qualify)(const char *);
{ "writes", qualify_write },
{ "w", qualify_write },
{ "fault", qualify_fault },
+ { "inject", qualify_inject },
};
void
unsigned int i;
for (i = 0; i < ARRAY_SIZE(qual_options); ++i) {
- const char *p = qual_options[i].name;
- unsigned int len = strlen(p);
+ const char *name = qual_options[i].name;
+ const size_t len = strlen(name);
+ const char *val = str_strip_prefix_len(str, name, len);
- if (strncmp(str, p, len) || str[len] != '=')
+ if (val == str || *val != '=')
continue;
-
+ str = val + 1;
opt = &qual_options[i];
- str += len + 1;
break;
}
? QUAL_VERBOSE : 0)
| (is_number_in_set(scno, &raw_set[current_personality])
? QUAL_RAW : 0)
- | (is_number_in_set(scno, &fault_set[current_personality])
- ? QUAL_FAULT : 0);
+ | (is_number_in_set(scno, &inject_set[current_personality])
+ ? QUAL_INJECT : 0);
}