]> granicus.if.org Git - strace/blobdiff - qualify.c
netlink_sock_diag: print inet_diag_sockid.idiag_if as an interface index
[strace] / qualify.c
index 5116c4875d92c3aa1c72140e55a9d771887f44c7..3df4805a46ae317f639f9096f5975e2dfce4597c 100644 (file)
--- a/qualify.c
+++ b/qualify.c
@@ -1,5 +1,6 @@
 /*
  * 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
@@ -27,6 +28,7 @@
 
 #include "defs.h"
 #include "nsig.h"
+#include <regex.h>
 
 typedef unsigned int number_slot_t;
 #define BITS_PER_SLOT (sizeof(number_slot_t) * 8)
@@ -198,6 +200,48 @@ qualify_syscall_number(const char *s, struct number_set *set)
        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)
 {
@@ -212,6 +256,20 @@ 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;
@@ -272,10 +330,19 @@ qualify_syscall_name(const char *s, struct number_set *set)
 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;
 }
 
 /*
@@ -347,25 +414,13 @@ handle_inversion:
        }
 }
 
-/*
- * 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;
        }
 
@@ -373,14 +428,15 @@ find_errno_by_name(const char *name)
 }
 
 static bool
-parse_inject_token(const char *const token, struct inject_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
@@ -408,7 +464,7 @@ parse_inject_token(const char *const token, struct inject_opts *const fopts)
                        /* F == F+0 */
                        fopts->step = 0;
                }
-       } else if ((val = strip_prefix("error=", token))) {
+       } 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);
@@ -417,14 +473,16 @@ parse_inject_token(const char *const token, struct inject_opts *const fopts)
                if (intval < 1)
                        return false;
                fopts->rval = -intval;
-       } else if ((val = strip_prefix("retval=", token))) {
+       } 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 ((val = strip_prefix("signal=", token))) {
+       } 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;
@@ -438,7 +496,8 @@ parse_inject_token(const char *const token, struct inject_opts *const fopts)
 
 static char *
 parse_inject_expression(const char *const s, char **buf,
-                       struct inject_opts *const fopts)
+                       struct inject_opts *const fopts,
+                       const bool fault_tokens_only)
 {
        char *saveptr = NULL;
        char *name = NULL;
@@ -449,7 +508,7 @@ parse_inject_expression(const char *const s, char **buf,
             token = strtok_r(NULL, ":", &saveptr)) {
                if (!name)
                        name = token;
-               else if (!parse_inject_token(token, fopts))
+               else if (!parse_inject_token(token, fopts, fault_tokens_only))
                        goto parse_error;
        }
 
@@ -504,7 +563,9 @@ qualify_raw(const char *const str)
 }
 
 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 inject_opts opts = {
                .first = 1,
@@ -513,22 +574,25 @@ qualify_fault(const char *const str)
                .signo = 0
        };
        char *buf = NULL;
-       char *name = parse_inject_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 signal is specified but neither retval nor error are specified,
-        * disable syscall fault injection.
-        */
-       if (opts.signo && opts.rval == INJECT_OPTS_RVAL_DEFAULT) {
-               opts.rval = INJECT_OPTS_RVAL_DISABLE;
+       /* 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);
 
@@ -559,6 +623,18 @@ qualify_fault(const char *const str)
        }
 }
 
+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 *);
@@ -581,6 +657,7 @@ static const struct qual_options {
        { "writes",     qualify_write   },
        { "w",          qualify_write   },
        { "fault",      qualify_fault   },
+       { "inject",     qualify_inject  },
 };
 
 void
@@ -590,14 +667,14 @@ qualify(const char *str)
        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;
        }