]> granicus.if.org Git - strace/blob - qualify.c
Implement -e trace=%fstatfs option
[strace] / qualify.c
1 /*
2  * Copyright (c) 2016 Dmitry V. Levin <ldv@altlinux.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. The name of the author may not be used to endorse or promote products
14  *    derived from this software without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27
28 #include "defs.h"
29 #include "nsig.h"
30 #include <regex.h>
31
32 typedef unsigned int number_slot_t;
33 #define BITS_PER_SLOT (sizeof(number_slot_t) * 8)
34
35 struct number_set {
36         number_slot_t *vec;
37         unsigned int nslots;
38         bool not;
39 };
40
41 struct number_set read_set;
42 struct number_set write_set;
43 struct number_set signal_set;
44
45 static struct number_set abbrev_set[SUPPORTED_PERSONALITIES];
46 static struct number_set inject_set[SUPPORTED_PERSONALITIES];
47 static struct number_set raw_set[SUPPORTED_PERSONALITIES];
48 static struct number_set trace_set[SUPPORTED_PERSONALITIES];
49 static struct number_set verbose_set[SUPPORTED_PERSONALITIES];
50
51 static void
52 number_setbit(const unsigned int i, number_slot_t *const vec)
53 {
54         vec[i / BITS_PER_SLOT] |= (number_slot_t) 1 << (i % BITS_PER_SLOT);
55 }
56
57 static bool
58 number_isset(const unsigned int i, const number_slot_t *const vec)
59 {
60         return vec[i / BITS_PER_SLOT] & ((number_slot_t) 1 << (i % BITS_PER_SLOT));
61 }
62
63 static void
64 reallocate_number_set(struct number_set *const set, const unsigned int new_nslots)
65 {
66         if (new_nslots <= set->nslots)
67                 return;
68         set->vec = xreallocarray(set->vec, new_nslots, sizeof(*set->vec));
69         memset(set->vec + set->nslots, 0,
70                sizeof(*set->vec) * (new_nslots - set->nslots));
71         set->nslots = new_nslots;
72 }
73
74 static void
75 add_number_to_set(const unsigned int number, struct number_set *const set)
76 {
77         reallocate_number_set(set, number / BITS_PER_SLOT + 1);
78         number_setbit(number, set->vec);
79 }
80
81 bool
82 is_number_in_set(const unsigned int number, const struct number_set *const set)
83 {
84         return ((number / BITS_PER_SLOT < set->nslots)
85                 && number_isset(number, set->vec)) ^ set->not;
86 }
87
88 typedef int (*string_to_uint_func)(const char *);
89
90 /*
91  * Add numbers to SET according to STR specification.
92  */
93 static void
94 qualify_tokens(const char *const str, struct number_set *const set,
95                string_to_uint_func func, const char *const name)
96 {
97         /* Clear the set. */
98         if (set->nslots)
99                 memset(set->vec, 0, sizeof(*set->vec) * set->nslots);
100         set->not = false;
101
102         /*
103          * Each leading ! character means inversion
104          * of the remaining specification.
105          */
106         const char *s = str;
107 handle_inversion:
108         while (*s == '!') {
109                 set->not = !set->not;
110                 ++s;
111         }
112
113         if (strcmp(s, "none") == 0) {
114                 /*
115                  * No numbers are added to the set.
116                  * Subsequent is_number_in_set invocations will return set->not.
117                  */
118                 return;
119         } else if (strcmp(s, "all") == 0) {
120                 s = "!none";
121                 goto handle_inversion;
122         }
123
124         /*
125          * Split the string into comma separated tokens.
126          * For each token, find out the corresponding number
127          * by calling FUNC, and add that number to the set.
128          * The absence of tokens or a negative answer
129          * from FUNC is a fatal error.
130          */
131         char *copy = xstrdup(s);
132         char *saveptr = NULL;
133         const char *token;
134         int number = -1;
135
136         for (token = strtok_r(copy, ",", &saveptr); token;
137              token = strtok_r(NULL, ",", &saveptr)) {
138                 number = func(token);
139                 if (number < 0) {
140                         error_msg_and_die("invalid %s '%s'", name, token);
141                 }
142
143                 add_number_to_set(number, set);
144         }
145
146         free(copy);
147
148         if (number < 0) {
149                 error_msg_and_die("invalid %s '%s'", name, str);
150         }
151 }
152
153 static int
154 sigstr_to_uint(const char *s)
155 {
156         int i;
157
158         if (*s >= '0' && *s <= '9')
159                 return string_to_uint_upto(s, 255);
160
161         if (strncasecmp(s, "SIG", 3) == 0)
162                 s += 3;
163
164         for (i = 0; i <= 255; ++i) {
165                 const char *name = signame(i);
166
167                 if (strncasecmp(name, "SIG", 3) != 0)
168                         continue;
169
170                 name += 3;
171
172                 if (strcasecmp(name, s) != 0)
173                         continue;
174
175                 return i;
176         }
177
178         return -1;
179 }
180
181 static bool
182 qualify_syscall_number(const char *s, struct number_set *set)
183 {
184         int n = string_to_uint(s);
185         if (n < 0)
186                 return false;
187
188         unsigned int p;
189         bool done = false;
190
191         for (p = 0; p < SUPPORTED_PERSONALITIES; ++p) {
192                 if ((unsigned) n >= nsyscall_vec[p]) {
193                         continue;
194                 }
195                 add_number_to_set(n, &set[p]);
196                 done = true;
197         }
198
199         return done;
200 }
201
202 static void
203 regerror_msg_and_die(int errcode, const regex_t *preg,
204                      const char *str, const char *pattern)
205 {
206         char buf[512];
207
208         regerror(errcode, preg, buf, sizeof(buf));
209         error_msg_and_die("%s: %s: %s", str, pattern, buf);
210 }
211
212 static bool
213 qualify_syscall_regex(const char *s, struct number_set *set)
214 {
215         regex_t preg;
216         int rc;
217
218         if ((rc = regcomp(&preg, s, REG_EXTENDED | REG_NOSUB)) != 0)
219                 regerror_msg_and_die(rc, &preg, "regcomp", s);
220
221         unsigned int p;
222         bool found = false;
223         for (p = 0; p < SUPPORTED_PERSONALITIES; ++p) {
224                 unsigned int i;
225
226                 for (i = 0; i < nsyscall_vec[p]; ++i) {
227                         if (!sysent_vec[p][i].sys_name)
228                                 continue;
229                         rc = regexec(&preg, sysent_vec[p][i].sys_name,
230                                      0, NULL, 0);
231                         if (rc == REG_NOMATCH)
232                                 continue;
233                         else if (rc)
234                                 regerror_msg_and_die(rc, &preg, "regexec", s);
235                         add_number_to_set(i, &set[p]);
236                         found = true;
237                 }
238         }
239
240         regfree(&preg);
241         return found;
242 }
243
244 static unsigned int
245 lookup_class(const char *s)
246 {
247         static const struct {
248                 const char *name;
249                 unsigned int value;
250         } syscall_class[] = {
251                 { "desc",       TRACE_DESC      },
252                 { "file",       TRACE_FILE      },
253                 { "memory",     TRACE_MEMORY    },
254                 { "process",    TRACE_PROCESS   },
255                 { "signal",     TRACE_SIGNAL    },
256                 { "ipc",        TRACE_IPC       },
257                 { "network",    TRACE_NETWORK   },
258                 { "%desc",      TRACE_DESC      },
259                 { "%file",      TRACE_FILE      },
260                 { "%memory",    TRACE_MEMORY    },
261                 { "%process",   TRACE_PROCESS   },
262                 { "%signal",    TRACE_SIGNAL    },
263                 { "%ipc",       TRACE_IPC       },
264                 { "%network",   TRACE_NETWORK   },
265                 { "%statfs",    TRACE_STATFS    },
266                 { "%fstatfs",   TRACE_FSTATFS   },
267         };
268
269         unsigned int i;
270         for (i = 0; i < ARRAY_SIZE(syscall_class); ++i) {
271                 if (strcmp(s, syscall_class[i].name) == 0) {
272                         return syscall_class[i].value;
273                 }
274         }
275
276         return 0;
277 }
278
279 static bool
280 qualify_syscall_class(const char *s, struct number_set *set)
281 {
282         const unsigned int n = lookup_class(s);
283         if (!n)
284                 return false;
285
286         unsigned int p;
287         for (p = 0; p < SUPPORTED_PERSONALITIES; ++p) {
288                 unsigned int i;
289
290                 for (i = 0; i < nsyscall_vec[p]; ++i) {
291                         if (!sysent_vec[p][i].sys_name
292                             || (sysent_vec[p][i].sys_flags & n) != n) {
293                                 continue;
294                         }
295                         add_number_to_set(i, &set[p]);
296                 }
297         }
298
299         return true;
300 }
301
302 static bool
303 qualify_syscall_name(const char *s, struct number_set *set)
304 {
305         unsigned int p;
306         bool found = false;
307
308         for (p = 0; p < SUPPORTED_PERSONALITIES; ++p) {
309                 unsigned int i;
310
311                 for (i = 0; i < nsyscall_vec[p]; ++i) {
312                         if (!sysent_vec[p][i].sys_name
313                             || strcmp(s, sysent_vec[p][i].sys_name)) {
314                                 continue;
315                         }
316                         add_number_to_set(i, &set[p]);
317                         found = true;
318                 }
319         }
320
321         return found;
322 }
323
324 static bool
325 qualify_syscall(const char *token, struct number_set *set)
326 {
327         if (*token >= '0' && *token <= '9')
328                 return qualify_syscall_number(token, set);
329         if (*token == '/')
330                 return qualify_syscall_regex(token + 1, set);
331         return qualify_syscall_class(token, set)
332                || qualify_syscall_name(token, set);
333 }
334
335 /*
336  * Add syscall numbers to SETs for each supported personality
337  * according to STR specification.
338  */
339 static void
340 qualify_syscall_tokens(const char *const str, struct number_set *const set,
341                        const char *const name)
342 {
343         /* Clear all sets. */
344         unsigned int p;
345         for (p = 0; p < SUPPORTED_PERSONALITIES; ++p) {
346                 if (set[p].nslots)
347                         memset(set[p].vec, 0,
348                                sizeof(*set[p].vec) * set[p].nslots);
349                 set[p].not = false;
350         }
351
352         /*
353          * Each leading ! character means inversion
354          * of the remaining specification.
355          */
356         const char *s = str;
357 handle_inversion:
358         while (*s == '!') {
359                 for (p = 0; p < SUPPORTED_PERSONALITIES; ++p) {
360                         set[p].not = !set[p].not;
361                 }
362                 ++s;
363         }
364
365         if (strcmp(s, "none") == 0) {
366                 /*
367                  * No syscall numbers are added to sets.
368                  * Subsequent is_number_in_set invocations
369                  * will return set[p]->not.
370                  */
371                 return;
372         } else if (strcmp(s, "all") == 0) {
373                 s = "!none";
374                 goto handle_inversion;
375         }
376
377         /*
378          * Split the string into comma separated tokens.
379          * For each token, call qualify_syscall that will take care
380          * if adding appropriate syscall numbers to sets.
381          * The absence of tokens or a negative return code
382          * from qualify_syscall is a fatal error.
383          */
384         char *copy = xstrdup(s);
385         char *saveptr = NULL;
386         const char *token;
387         bool done = false;
388
389         for (token = strtok_r(copy, ",", &saveptr); token;
390              token = strtok_r(NULL, ",", &saveptr)) {
391                 done = qualify_syscall(token, set);
392                 if (!done) {
393                         error_msg_and_die("invalid %s '%s'", name, token);
394                 }
395         }
396
397         free(copy);
398
399         if (!done) {
400                 error_msg_and_die("invalid %s '%s'", name, str);
401         }
402 }
403
404 /*
405  * Returns NULL if STR does not start with PREFIX,
406  * or a pointer to the first char in STR after PREFIX.
407  */
408 static const char *
409 strip_prefix(const char *prefix, const char *str)
410 {
411         size_t len = strlen(prefix);
412
413         return strncmp(prefix, str, len) ? NULL : str + len;
414 }
415
416 static int
417 find_errno_by_name(const char *name)
418 {
419         unsigned int i;
420
421         for (i = 1; i < nerrnos; ++i) {
422                 if (errnoent[i] && (strcasecmp(name, errnoent[i]) == 0))
423                         return i;
424         }
425
426         return -1;
427 }
428
429 static bool
430 parse_inject_token(const char *const token, struct inject_opts *const fopts,
431                    const bool fault_tokens_only)
432 {
433         const char *val;
434         int intval;
435
436         if ((val = strip_prefix("when=", token))) {
437                 /*
438                  *      == 1+1
439                  * F    == F+0
440                  * F+   == F+1
441                  * F+S
442                  */
443                 char *end;
444                 intval = string_to_uint_ex(val, &end, 0xffff, "+");
445                 if (intval < 1)
446                         return false;
447
448                 fopts->first = intval;
449
450                 if (*end) {
451                         val = end + 1;
452                         if (*val) {
453                                 /* F+S */
454                                 intval = string_to_uint_upto(val, 0xffff);
455                                 if (intval < 1)
456                                         return false;
457                                 fopts->step = intval;
458                         } else {
459                                 /* F+ == F+1 */
460                                 fopts->step = 1;
461                         }
462                 } else {
463                         /* F == F+0 */
464                         fopts->step = 0;
465                 }
466         } else if ((val = strip_prefix("error=", token))) {
467                 if (fopts->rval != INJECT_OPTS_RVAL_DEFAULT)
468                         return false;
469                 intval = string_to_uint_upto(val, MAX_ERRNO_VALUE);
470                 if (intval < 0)
471                         intval = find_errno_by_name(val);
472                 if (intval < 1)
473                         return false;
474                 fopts->rval = -intval;
475         } else if (!fault_tokens_only && (val = strip_prefix("retval=", token))) {
476                 if (fopts->rval != INJECT_OPTS_RVAL_DEFAULT)
477                         return false;
478                 intval = string_to_uint(val);
479                 if (intval < 0)
480                         return false;
481                 fopts->rval = intval;
482         } else if (!fault_tokens_only && (val = strip_prefix("signal=", token))) {
483                 intval = sigstr_to_uint(val);
484                 if (intval < 1 || intval > NSIG_BYTES * 8)
485                         return false;
486                 fopts->signo = intval;
487         } else {
488                 return false;
489         }
490
491         return true;
492 }
493
494 static char *
495 parse_inject_expression(const char *const s, char **buf,
496                         struct inject_opts *const fopts,
497                         const bool fault_tokens_only)
498 {
499         char *saveptr = NULL;
500         char *name = NULL;
501         char *token;
502
503         *buf = xstrdup(s);
504         for (token = strtok_r(*buf, ":", &saveptr); token;
505              token = strtok_r(NULL, ":", &saveptr)) {
506                 if (!name)
507                         name = token;
508                 else if (!parse_inject_token(token, fopts, fault_tokens_only))
509                         goto parse_error;
510         }
511
512         if (name)
513                 return name;
514
515 parse_error:
516         free(*buf);
517         return *buf = NULL;
518 }
519
520 static void
521 qualify_read(const char *const str)
522 {
523         qualify_tokens(str, &read_set, string_to_uint, "descriptor");
524 }
525
526 static void
527 qualify_write(const char *const str)
528 {
529         qualify_tokens(str, &write_set, string_to_uint, "descriptor");
530 }
531
532 static void
533 qualify_signals(const char *const str)
534 {
535         qualify_tokens(str, &signal_set, sigstr_to_uint, "signal");
536 }
537
538 static void
539 qualify_trace(const char *const str)
540 {
541         qualify_syscall_tokens(str, trace_set, "system call");
542 }
543
544 static void
545 qualify_abbrev(const char *const str)
546 {
547         qualify_syscall_tokens(str, abbrev_set, "system call");
548 }
549
550 static void
551 qualify_verbose(const char *const str)
552 {
553         qualify_syscall_tokens(str, verbose_set, "system call");
554 }
555
556 static void
557 qualify_raw(const char *const str)
558 {
559         qualify_syscall_tokens(str, raw_set, "system call");
560 }
561
562 static void
563 qualify_inject_common(const char *const str,
564                       const bool fault_tokens_only,
565                       const char *const description)
566 {
567         struct inject_opts opts = {
568                 .first = 1,
569                 .step = 1,
570                 .rval = INJECT_OPTS_RVAL_DEFAULT,
571                 .signo = 0
572         };
573         char *buf = NULL;
574         char *name = parse_inject_expression(str, &buf, &opts, fault_tokens_only);
575         if (!name) {
576                 error_msg_and_die("invalid %s '%s'", description, str);
577         }
578
579         /* If neither of retval, error, or signal is specified, then ... */
580         if (opts.rval == INJECT_OPTS_RVAL_DEFAULT && !opts.signo) {
581                 if (fault_tokens_only) {
582                         /* in fault= syntax the default error code is ENOSYS. */
583                         opts.rval = -ENOSYS;
584                 } else {
585                         /* in inject= syntax this is not allowed. */
586                         error_msg_and_die("invalid %s '%s'", description, str);
587                 }
588         }
589
590         struct number_set tmp_set[SUPPORTED_PERSONALITIES];
591         memset(tmp_set, 0, sizeof(tmp_set));
592         qualify_syscall_tokens(name, tmp_set, description);
593
594         free(buf);
595
596         /*
597          * Initialize inject_vec accourding to tmp_set.
598          * Merge tmp_set into inject_set.
599          */
600         unsigned int p;
601         for (p = 0; p < SUPPORTED_PERSONALITIES; ++p) {
602                 if (!tmp_set[p].nslots && !tmp_set[p].not) {
603                         continue;
604                 }
605
606                 if (!inject_vec[p]) {
607                         inject_vec[p] = xcalloc(nsyscall_vec[p],
608                                                sizeof(*inject_vec[p]));
609                 }
610
611                 unsigned int i;
612                 for (i = 0; i < nsyscall_vec[p]; ++i) {
613                         if (is_number_in_set(i, &tmp_set[p])) {
614                                 add_number_to_set(i, &inject_set[p]);
615                                 inject_vec[p][i] = opts;
616                         }
617                 }
618
619                 free(tmp_set[p].vec);
620         }
621 }
622
623 static void
624 qualify_fault(const char *const str)
625 {
626         qualify_inject_common(str, true, "fault argument");
627 }
628
629 static void
630 qualify_inject(const char *const str)
631 {
632         qualify_inject_common(str, false, "inject argument");
633 }
634
635 static const struct qual_options {
636         const char *name;
637         void (*qualify)(const char *);
638 } qual_options[] = {
639         { "trace",      qualify_trace   },
640         { "t",          qualify_trace   },
641         { "abbrev",     qualify_abbrev  },
642         { "a",          qualify_abbrev  },
643         { "verbose",    qualify_verbose },
644         { "v",          qualify_verbose },
645         { "raw",        qualify_raw     },
646         { "x",          qualify_raw     },
647         { "signal",     qualify_signals },
648         { "signals",    qualify_signals },
649         { "s",          qualify_signals },
650         { "read",       qualify_read    },
651         { "reads",      qualify_read    },
652         { "r",          qualify_read    },
653         { "write",      qualify_write   },
654         { "writes",     qualify_write   },
655         { "w",          qualify_write   },
656         { "fault",      qualify_fault   },
657         { "inject",     qualify_inject  },
658 };
659
660 void
661 qualify(const char *str)
662 {
663         const struct qual_options *opt = qual_options;
664         unsigned int i;
665
666         for (i = 0; i < ARRAY_SIZE(qual_options); ++i) {
667                 const char *p = qual_options[i].name;
668                 unsigned int len = strlen(p);
669
670                 if (strncmp(str, p, len) || str[len] != '=')
671                         continue;
672
673                 opt = &qual_options[i];
674                 str += len + 1;
675                 break;
676         }
677
678         opt->qualify(str);
679 }
680
681 unsigned int
682 qual_flags(const unsigned int scno)
683 {
684         return  (is_number_in_set(scno, &trace_set[current_personality])
685                    ? QUAL_TRACE : 0)
686                 | (is_number_in_set(scno, &abbrev_set[current_personality])
687                    ? QUAL_ABBREV : 0)
688                 | (is_number_in_set(scno, &verbose_set[current_personality])
689                    ? QUAL_VERBOSE : 0)
690                 | (is_number_in_set(scno, &raw_set[current_personality])
691                    ? QUAL_RAW : 0)
692                 | (is_number_in_set(scno, &inject_set[current_personality])
693                    ? QUAL_INJECT : 0);
694 }