]> granicus.if.org Git - strace/blob - qualify.c
Include "kernel_types.h" in defs.h and tests/tests.h
[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
30 typedef unsigned int number_slot_t;
31 #define BITS_PER_SLOT (sizeof(number_slot_t) * 8)
32
33 struct number_set {
34         number_slot_t *vec;
35         unsigned int nslots;
36         bool not;
37 };
38
39 struct number_set read_set;
40 struct number_set write_set;
41 struct number_set signal_set;
42
43 static struct number_set abbrev_set[SUPPORTED_PERSONALITIES];
44 static struct number_set fault_set[SUPPORTED_PERSONALITIES];
45 static struct number_set raw_set[SUPPORTED_PERSONALITIES];
46 static struct number_set trace_set[SUPPORTED_PERSONALITIES];
47 static struct number_set verbose_set[SUPPORTED_PERSONALITIES];
48
49 static void
50 number_setbit(const unsigned int i, number_slot_t *const vec)
51 {
52         vec[i / BITS_PER_SLOT] |= (number_slot_t) 1 << (i % BITS_PER_SLOT);
53 }
54
55 static bool
56 number_isset(const unsigned int i, const number_slot_t *const vec)
57 {
58         return vec[i / BITS_PER_SLOT] & ((number_slot_t) 1 << (i % BITS_PER_SLOT));
59 }
60
61 static void
62 reallocate_number_set(struct number_set *const set, const unsigned int new_nslots)
63 {
64         if (new_nslots <= set->nslots)
65                 return;
66         set->vec = xreallocarray(set->vec, new_nslots, sizeof(*set->vec));
67         memset(set->vec + set->nslots, 0,
68                sizeof(*set->vec) * (new_nslots - set->nslots));
69         set->nslots = new_nslots;
70 }
71
72 static void
73 add_number_to_set(const unsigned int number, struct number_set *const set)
74 {
75         reallocate_number_set(set, number / BITS_PER_SLOT + 1);
76         number_setbit(number, set->vec);
77 }
78
79 bool
80 is_number_in_set(const unsigned int number, const struct number_set *const set)
81 {
82         return ((number / BITS_PER_SLOT < set->nslots)
83                 && number_isset(number, set->vec)) ^ set->not;
84 }
85
86 typedef int (*string_to_uint_func)(const char *);
87
88 /*
89  * Add numbers to SET according to STR specification.
90  */
91 static void
92 qualify_tokens(const char *const str, struct number_set *const set,
93                string_to_uint_func func, const char *const name)
94 {
95         /* Clear the set. */
96         if (set->nslots)
97                 memset(set->vec, 0, sizeof(*set->vec) * set->nslots);
98         set->not = false;
99
100         /*
101          * Each leading ! character means inversion
102          * of the remaining specification.
103          */
104         const char *s = str;
105 handle_inversion:
106         while (*s == '!') {
107                 set->not = !set->not;
108                 ++s;
109         }
110
111         if (strcmp(s, "none") == 0) {
112                 /*
113                  * No numbers are added to the set.
114                  * Subsequent is_number_in_set invocations will return set->not.
115                  */
116                 return;
117         } else if (strcmp(s, "all") == 0) {
118                 s = "!none";
119                 goto handle_inversion;
120         }
121
122         /*
123          * Split the string into comma separated tokens.
124          * For each token, find out the corresponding number
125          * by calling FUNC, and add that number to the set.
126          * The absence of tokens or a negative answer
127          * from FUNC is a fatal error.
128          */
129         char *copy = xstrdup(s);
130         char *saveptr = NULL;
131         const char *token;
132         int number = -1;
133
134         for (token = strtok_r(copy, ",", &saveptr); token;
135              token = strtok_r(NULL, ",", &saveptr)) {
136                 number = func(token);
137                 if (number < 0) {
138                         error_msg_and_die("invalid %s '%s'", name, token);
139                 }
140
141                 add_number_to_set(number, set);
142         }
143
144         free(copy);
145
146         if (number < 0) {
147                 error_msg_and_die("invalid %s '%s'", name, str);
148         }
149 }
150
151 static int
152 sigstr_to_uint(const char *s)
153 {
154         int i;
155
156         if (*s >= '0' && *s <= '9')
157                 return string_to_uint_upto(s, 255);
158
159         if (strncasecmp(s, "SIG", 3) == 0)
160                 s += 3;
161
162         for (i = 0; i <= 255; ++i) {
163                 const char *name = signame(i);
164
165                 if (strncasecmp(name, "SIG", 3) != 0)
166                         continue;
167
168                 name += 3;
169
170                 if (strcasecmp(name, s) != 0)
171                         continue;
172
173                 return i;
174         }
175
176         return -1;
177 }
178
179 static bool
180 qualify_syscall_number(const char *s, struct number_set *set)
181 {
182         int n = string_to_uint(s);
183         if (n < 0)
184                 return false;
185
186         unsigned int p;
187         bool done = false;
188
189         for (p = 0; p < SUPPORTED_PERSONALITIES; ++p) {
190                 if ((unsigned) n >= nsyscall_vec[p]) {
191                         continue;
192                 }
193                 add_number_to_set(n, &set[p]);
194                 done = true;
195         }
196
197         return done;
198 }
199
200 static unsigned int
201 lookup_class(const char *s)
202 {
203         static const struct {
204                 const char *name;
205                 unsigned int value;
206         } syscall_class[] = {
207                 { "desc",       TRACE_DESC      },
208                 { "file",       TRACE_FILE      },
209                 { "memory",     TRACE_MEMORY    },
210                 { "process",    TRACE_PROCESS   },
211                 { "signal",     TRACE_SIGNAL    },
212                 { "ipc",        TRACE_IPC       },
213                 { "network",    TRACE_NETWORK   },
214         };
215
216         unsigned int i;
217         for (i = 0; i < ARRAY_SIZE(syscall_class); ++i) {
218                 if (strcmp(s, syscall_class[i].name) == 0) {
219                         return syscall_class[i].value;
220                 }
221         }
222
223         return 0;
224 }
225
226 static bool
227 qualify_syscall_class(const char *s, struct number_set *set)
228 {
229         const unsigned int n = lookup_class(s);
230         if (!n)
231                 return false;
232
233         unsigned int p;
234         for (p = 0; p < SUPPORTED_PERSONALITIES; ++p) {
235                 unsigned int i;
236
237                 for (i = 0; i < nsyscall_vec[p]; ++i) {
238                         if (!sysent_vec[p][i].sys_name
239                             || (sysent_vec[p][i].sys_flags & n) != n) {
240                                 continue;
241                         }
242                         add_number_to_set(i, &set[p]);
243                 }
244         }
245
246         return true;
247 }
248
249 static bool
250 qualify_syscall_name(const char *s, struct number_set *set)
251 {
252         unsigned int p;
253         bool found = false;
254
255         for (p = 0; p < SUPPORTED_PERSONALITIES; ++p) {
256                 unsigned int i;
257
258                 for (i = 0; i < nsyscall_vec[p]; ++i) {
259                         if (!sysent_vec[p][i].sys_name
260                             || strcmp(s, sysent_vec[p][i].sys_name)) {
261                                 continue;
262                         }
263                         add_number_to_set(i, &set[p]);
264                         found = true;
265                 }
266         }
267
268         return found;
269 }
270
271 static bool
272 qualify_syscall(const char *token, struct number_set *set)
273 {
274         if (*token >= '0' && *token <= '9')
275                 return qualify_syscall_number(token, set);
276         return qualify_syscall_class(token, set)
277                || qualify_syscall_name(token, set);
278 }
279
280 /*
281  * Add syscall numbers to SETs for each supported personality
282  * according to STR specification.
283  */
284 static void
285 qualify_syscall_tokens(const char *const str, struct number_set *const set,
286                        const char *const name)
287 {
288         /* Clear all sets. */
289         unsigned int p;
290         for (p = 0; p < SUPPORTED_PERSONALITIES; ++p) {
291                 if (set[p].nslots)
292                         memset(set[p].vec, 0,
293                                sizeof(*set[p].vec) * set[p].nslots);
294                 set[p].not = false;
295         }
296
297         /*
298          * Each leading ! character means inversion
299          * of the remaining specification.
300          */
301         const char *s = str;
302 handle_inversion:
303         while (*s == '!') {
304                 for (p = 0; p < SUPPORTED_PERSONALITIES; ++p) {
305                         set[p].not = !set[p].not;
306                 }
307                 ++s;
308         }
309
310         if (strcmp(s, "none") == 0) {
311                 /*
312                  * No syscall numbers are added to sets.
313                  * Subsequent is_number_in_set invocations
314                  * will return set[p]->not.
315                  */
316                 return;
317         } else if (strcmp(s, "all") == 0) {
318                 s = "!none";
319                 goto handle_inversion;
320         }
321
322         /*
323          * Split the string into comma separated tokens.
324          * For each token, call qualify_syscall that will take care
325          * if adding appropriate syscall numbers to sets.
326          * The absence of tokens or a negative return code
327          * from qualify_syscall is a fatal error.
328          */
329         char *copy = xstrdup(s);
330         char *saveptr = NULL;
331         const char *token;
332         bool done = false;
333
334         for (token = strtok_r(copy, ",", &saveptr); token;
335              token = strtok_r(NULL, ",", &saveptr)) {
336                 done = qualify_syscall(token, set);
337                 if (!done) {
338                         error_msg_and_die("invalid %s '%s'", name, token);
339                 }
340         }
341
342         free(copy);
343
344         if (!done) {
345                 error_msg_and_die("invalid %s '%s'", name, str);
346         }
347 }
348
349 /*
350  * Returns NULL if STR does not start with PREFIX,
351  * or a pointer to the first char in STR after PREFIX.
352  */
353 static const char *
354 strip_prefix(const char *prefix, const char *str)
355 {
356         size_t len = strlen(prefix);
357
358         return strncmp(prefix, str, len) ? NULL : str + len;
359 }
360
361 static int
362 find_errno_by_name(const char *name)
363 {
364         unsigned int i;
365
366         for (i = 1; i < nerrnos; ++i) {
367                 if (errnoent[i] && (strcmp(name, errnoent[i]) == 0))
368                         return i;
369         }
370
371         return -1;
372 }
373
374 static bool
375 parse_fault_token(const char *const token, struct fault_opts *const fopts)
376 {
377         const char *val;
378         int intval;
379
380         if ((val = strip_prefix("when=", token))) {
381                 /*
382                  *      == 1+1
383                  * F    == F+0
384                  * F+   == F+1
385                  * F+S
386                  */
387                 char *end;
388                 intval = string_to_uint_ex(val, &end, 0xffff, "+");
389                 if (intval < 1)
390                         return false;
391
392                 fopts->first = intval;
393
394                 if (*end) {
395                         val = end + 1;
396                         if (*val) {
397                                 /* F+S */
398                                 intval = string_to_uint_upto(val, 0xffff);
399                                 if (intval < 1)
400                                         return false;
401                                 fopts->step = intval;
402                         } else {
403                                 /* F+ == F+1 */
404                                 fopts->step = 1;
405                         }
406                 } else {
407                         /* F == F+0 */
408                         fopts->step = 0;
409                 }
410         } else if ((val = strip_prefix("error=", token))) {
411                 intval = string_to_uint_upto(val, 4095);
412                 if (intval < 0)
413                         intval = find_errno_by_name(val);
414                 if (intval < 1)
415                         return false;
416                 fopts->err = intval;
417         } else {
418                 return false;
419         }
420
421         return true;
422 }
423
424 static char *
425 parse_fault_expression(const char *const s, char **buf,
426                        struct fault_opts *const fopts)
427 {
428         char *saveptr = NULL;
429         char *name = NULL;
430         char *token;
431
432         *buf = xstrdup(s);
433         for (token = strtok_r(*buf, ":", &saveptr); token;
434              token = strtok_r(NULL, ":", &saveptr)) {
435                 if (!name)
436                         name = token;
437                 else if (!parse_fault_token(token, fopts))
438                         goto parse_error;
439         }
440
441         if (name)
442                 return name;
443
444 parse_error:
445         free(*buf);
446         return *buf = NULL;
447 }
448
449 static void
450 qualify_read(const char *const str)
451 {
452         qualify_tokens(str, &read_set, string_to_uint, "descriptor");
453 }
454
455 static void
456 qualify_write(const char *const str)
457 {
458         qualify_tokens(str, &write_set, string_to_uint, "descriptor");
459 }
460
461 static void
462 qualify_signals(const char *const str)
463 {
464         qualify_tokens(str, &signal_set, sigstr_to_uint, "signal");
465 }
466
467 static void
468 qualify_trace(const char *const str)
469 {
470         qualify_syscall_tokens(str, trace_set, "system call");
471 }
472
473 static void
474 qualify_abbrev(const char *const str)
475 {
476         qualify_syscall_tokens(str, abbrev_set, "system call");
477 }
478
479 static void
480 qualify_verbose(const char *const str)
481 {
482         qualify_syscall_tokens(str, verbose_set, "system call");
483 }
484
485 static void
486 qualify_raw(const char *const str)
487 {
488         qualify_syscall_tokens(str, raw_set, "system call");
489 }
490
491 static void
492 qualify_fault(const char *const str)
493 {
494         struct fault_opts opts = {
495                 .first = 1,
496                 .step = 1,
497                 .err = 0
498         };
499         char *buf = NULL;
500         char *name = parse_fault_expression(str, &buf, &opts);
501         if (!name) {
502                 error_msg_and_die("invalid %s '%s'", "fault argument", str);
503         }
504
505
506         struct number_set tmp_set[SUPPORTED_PERSONALITIES];
507         memset(tmp_set, 0, sizeof(tmp_set));
508         qualify_syscall_tokens(name, tmp_set, "fault argument");
509
510         free(buf);
511
512         /*
513          * Initialize fault_vec accourding to tmp_set.
514          * Merge tmp_set into fault_set.
515          */
516         unsigned int p;
517         for (p = 0; p < SUPPORTED_PERSONALITIES; ++p) {
518                 if (!tmp_set[p].nslots && !tmp_set[p].not) {
519                         continue;
520                 }
521
522                 if (!fault_vec[p]) {
523                         fault_vec[p] = xcalloc(nsyscall_vec[p],
524                                                sizeof(*fault_vec[p]));
525                 }
526
527                 unsigned int i;
528                 for (i = 0; i < nsyscall_vec[p]; ++i) {
529                         if (is_number_in_set(i, &tmp_set[p])) {
530                                 add_number_to_set(i, &fault_set[p]);
531                                 fault_vec[p][i] = opts;
532                         }
533                 }
534
535                 free(tmp_set[p].vec);
536         }
537 }
538
539 static const struct qual_options {
540         const char *name;
541         void (*qualify)(const char *);
542 } qual_options[] = {
543         { "trace",      qualify_trace   },
544         { "t",          qualify_trace   },
545         { "abbrev",     qualify_abbrev  },
546         { "a",          qualify_abbrev  },
547         { "verbose",    qualify_verbose },
548         { "v",          qualify_verbose },
549         { "raw",        qualify_raw     },
550         { "x",          qualify_raw     },
551         { "signal",     qualify_signals },
552         { "signals",    qualify_signals },
553         { "s",          qualify_signals },
554         { "read",       qualify_read    },
555         { "reads",      qualify_read    },
556         { "r",          qualify_read    },
557         { "write",      qualify_write   },
558         { "writes",     qualify_write   },
559         { "w",          qualify_write   },
560         { "fault",      qualify_fault   },
561 };
562
563 void
564 qualify(const char *str)
565 {
566         const struct qual_options *opt = qual_options;
567         unsigned int i;
568
569         for (i = 0; i < ARRAY_SIZE(qual_options); ++i) {
570                 const char *p = qual_options[i].name;
571                 unsigned int len = strlen(p);
572
573                 if (strncmp(str, p, len) || str[len] != '=')
574                         continue;
575
576                 opt = &qual_options[i];
577                 str += len + 1;
578                 break;
579         }
580
581         opt->qualify(str);
582 }
583
584 unsigned int
585 qual_flags(const unsigned int scno)
586 {
587         return  (is_number_in_set(scno, &trace_set[current_personality])
588                    ? QUAL_TRACE : 0)
589                 | (is_number_in_set(scno, &abbrev_set[current_personality])
590                    ? QUAL_ABBREV : 0)
591                 | (is_number_in_set(scno, &verbose_set[current_personality])
592                    ? QUAL_VERBOSE : 0)
593                 | (is_number_in_set(scno, &raw_set[current_personality])
594                    ? QUAL_RAW : 0)
595                 | (is_number_in_set(scno, &fault_set[current_personality])
596                    ? QUAL_FAULT : 0);
597 }