]> granicus.if.org Git - strace/blob - basic_filters.c
Add support for long options
[strace] / basic_filters.c
1 /*
2  * Copyright (c) 2016 Dmitry V. Levin <ldv@altlinux.org>
3  * Copyright (c) 2016-2018 The strace developers.
4  * All rights reserved.
5  *
6  * SPDX-License-Identifier: LGPL-2.1-or-later
7  */
8
9 #include "defs.h"
10
11 #include <regex.h>
12
13 #include "filter.h"
14 #include "number_set.h"
15 #include "xstring.h"
16
17
18 /**
19  * Checks whether a @-separated personality specification suffix is present.
20  * Personality suffix is a one of strings stored in personality_designators
21  * array.
22  *
23  * @param[in]  s Specification string to check.
24  * @param[out] p Where to store personality number if it is found.
25  * @return       If personality is found, the provided string is copied without
26  *               suffix and returned as a result (callee should de-alllocate it
27  *               with free() after use), and personality number is written to p.
28  *               Otherwise, NULL is returned and p is untouched.
29  */
30 static char *
31 qualify_syscall_separate_personality(const char *s, unsigned int *p)
32 {
33         char *pos = strchr(s, '@');
34
35         if (!pos)
36                 return NULL;
37
38         for (unsigned int i = 0; i < SUPPORTED_PERSONALITIES; i++) {
39                 if (!strcmp(pos + 1, personality_designators[i])) {
40                         *p = i;
41                         return xstrndup(s, pos - s);
42                 }
43         }
44
45         error_msg_and_help("incorrect personality designator '%s'"
46                            " in qualification '%s'", pos + 1, s);
47 }
48
49 static bool
50 qualify_syscall_number_personality(int n, unsigned int p,
51                                    struct number_set *set)
52 {
53         if ((unsigned int) n >= nsyscall_vec[p])
54                 return false;
55
56         add_number_to_set_array(n, set, p);
57
58         return true;
59 }
60
61 static bool
62 qualify_syscall_number(const char *s, struct number_set *set)
63 {
64         unsigned int p;
65         char *num_str = qualify_syscall_separate_personality(s, &p);
66         int n;
67
68         if (num_str) {
69                 n = string_to_uint(num_str);
70                 free(num_str);
71
72                 if (n < 0)
73                         return false;
74
75                 return qualify_syscall_number_personality(n, p, set);
76         }
77
78         n = string_to_uint(s);
79         if (n < 0)
80                 return false;
81
82         bool done = false;
83
84         for (p = 0; p < SUPPORTED_PERSONALITIES; ++p)
85                 done |= qualify_syscall_number_personality(n, p, set);
86
87         return done;
88 }
89
90 static void
91 regerror_msg_and_die(int errcode, const regex_t *preg,
92                      const char *str, const char *pattern)
93 {
94         char buf[512];
95
96         regerror(errcode, preg, buf, sizeof(buf));
97         error_msg_and_die("%s: %s: %s", str, pattern, buf);
98 }
99
100 static bool
101 qualify_syscall_regex(const char *s, struct number_set *set)
102 {
103         regex_t preg;
104         int rc;
105
106         if ((rc = regcomp(&preg, s, REG_EXTENDED | REG_NOSUB)) != 0)
107                 regerror_msg_and_die(rc, &preg, "regcomp", s);
108
109         bool found = false;
110
111         for (unsigned int p = 0; p < SUPPORTED_PERSONALITIES; ++p) {
112                 for (unsigned int i = 0; i < nsyscall_vec[p]; ++i) {
113                         if (!sysent_vec[p][i].sys_name)
114                                 continue;
115
116                         rc = regexec(&preg, sysent_vec[p][i].sys_name,
117                                      0, NULL, 0);
118
119                         if (rc == REG_NOMATCH) {
120                                 char name_buf[128];
121                                 char *pos = stpcpy(name_buf,
122                                                    sysent_vec[p][i].sys_name);
123
124                                 (void) xappendstr(name_buf, pos, "@%s",
125                                                   personality_designators[p]);
126
127                                 rc = regexec(&preg, name_buf, 0, NULL, 0);
128                         }
129
130                         if (rc == REG_NOMATCH)
131                                 continue;
132                         else if (rc)
133                                 regerror_msg_and_die(rc, &preg, "regexec", s);
134
135                         add_number_to_set_array(i, set, p);
136                         found = true;
137                 }
138         }
139
140         regfree(&preg);
141         return found;
142 }
143
144 static unsigned int
145 lookup_class(const char *s)
146 {
147         static const struct {
148                 const char *name;
149                 unsigned int value;
150         } syscall_class[] = {
151                 { "%desc",      TRACE_DESC      },
152                 { "%file",      TRACE_FILE      },
153                 { "%memory",    TRACE_MEMORY    },
154                 { "%process",   TRACE_PROCESS   },
155                 { "%signal",    TRACE_SIGNAL    },
156                 { "%ipc",       TRACE_IPC       },
157                 { "%net",       TRACE_NETWORK   },
158                 { "%network",   TRACE_NETWORK   },
159                 { "%stat",      TRACE_STAT      },
160                 { "%lstat",     TRACE_LSTAT     },
161                 { "%fstat",     TRACE_FSTAT     },
162                 { "%%stat",     TRACE_STAT_LIKE },
163                 { "%statfs",    TRACE_STATFS    },
164                 { "%fstatfs",   TRACE_FSTATFS   },
165                 { "%%statfs",   TRACE_STATFS_LIKE       },
166                 { "%pure",      TRACE_PURE      },
167                 /* legacy class names */
168                 { "desc",       TRACE_DESC      },
169                 { "file",       TRACE_FILE      },
170                 { "memory",     TRACE_MEMORY    },
171                 { "process",    TRACE_PROCESS   },
172                 { "signal",     TRACE_SIGNAL    },
173                 { "ipc",        TRACE_IPC       },
174                 { "network",    TRACE_NETWORK   },
175         };
176
177         for (unsigned int i = 0; i < ARRAY_SIZE(syscall_class); ++i) {
178                 if (strcmp(s, syscall_class[i].name) == 0)
179                         return syscall_class[i].value;
180         }
181
182         return 0;
183 }
184
185 static bool
186 qualify_syscall_class(const char *s, struct number_set *set)
187 {
188         const unsigned int n = lookup_class(s);
189         if (!n)
190                 return false;
191
192         for (unsigned int p = 0; p < SUPPORTED_PERSONALITIES; ++p) {
193                 for (unsigned int i = 0; i < nsyscall_vec[p]; ++i) {
194                         if (sysent_vec[p][i].sys_name &&
195                             (sysent_vec[p][i].sys_flags & n) == n)
196                                 add_number_to_set_array(i, set, p);
197                 }
198         }
199
200         return true;
201 }
202
203 kernel_long_t
204 scno_by_name(const char *s, unsigned int p, kernel_long_t start)
205 {
206         if (p >= SUPPORTED_PERSONALITIES)
207                 return -1;
208
209         for (kernel_ulong_t i = start; i < nsyscall_vec[p]; ++i) {
210                 if (sysent_vec[p][i].sys_name &&
211                     strcmp(s, sysent_vec[p][i].sys_name) == 0)
212                         return i;
213         }
214
215         return -1;
216 }
217
218 static bool
219 qualify_syscall_name_personality(const char *s, unsigned int p,
220                                  struct number_set *set)
221 {
222         bool found = false;
223
224         for (kernel_long_t scno = 0; (scno = scno_by_name(s, p, scno)) >= 0;
225              ++scno) {
226                 add_number_to_set_array(scno, set, p);
227                 found = true;
228         }
229
230         return found;
231 }
232
233 static bool
234 qualify_syscall_name(const char *s, struct number_set *set)
235 {
236         unsigned int p;
237         char *name_str = qualify_syscall_separate_personality(s, &p);
238         bool found = false;
239
240         if (name_str) {
241                 found = qualify_syscall_name_personality(name_str, p, set);
242                 free(name_str);
243
244                 return found;
245         }
246
247         for (p = 0; p < SUPPORTED_PERSONALITIES; ++p)
248                 found |= qualify_syscall_name_personality(s, p, set);
249
250         return found;
251 }
252
253 static bool
254 qualify_syscall(const char *token, struct number_set *set)
255 {
256         bool ignore_fail = false;
257
258         while (*token == '?') {
259                 token++;
260                 ignore_fail = true;
261         }
262         if (*token >= '0' && *token <= '9')
263                 return qualify_syscall_number(token, set) || ignore_fail;
264         if (*token == '/')
265                 return qualify_syscall_regex(token + 1, set) || ignore_fail;
266         return qualify_syscall_class(token, set)
267                || qualify_syscall_name(token, set)
268                || ignore_fail;
269 }
270
271 /*
272  * Add syscall numbers to SETs for each supported personality
273  * according to STR specification.
274  */
275 void
276 qualify_syscall_tokens(const char *const str, struct number_set *const set)
277 {
278         /* Clear all sets. */
279         clear_number_set_array(set, SUPPORTED_PERSONALITIES);
280
281         /*
282          * Each leading ! character means inversion
283          * of the remaining specification.
284          */
285         const char *s = str;
286         while (*s == '!') {
287                 invert_number_set_array(set, SUPPORTED_PERSONALITIES);
288                 ++s;
289         }
290
291         if (strcmp(s, "none") == 0) {
292                 /*
293                  * No syscall numbers are added to sets.
294                  * Subsequent is_number_in_set* invocations
295                  * will return set[p]->not.
296                  */
297                 return;
298         } else if (strcmp(s, "all") == 0) {
299                 /* "all" == "!none" */
300                 invert_number_set_array(set, SUPPORTED_PERSONALITIES);
301                 return;
302         }
303
304         /*
305          * Split the string into comma separated tokens.
306          * For each token, call qualify_syscall that will take care
307          * if adding appropriate syscall numbers to sets.
308          * The absence of tokens or a negative return code
309          * from qualify_syscall is a fatal error.
310          */
311         char *copy = xstrdup(s);
312         char *saveptr = NULL;
313         bool done = false;
314
315         for (const char *token = strtok_r(copy, ",", &saveptr);
316              token; token = strtok_r(NULL, ",", &saveptr)) {
317                 done = qualify_syscall(token, set);
318                 if (!done)
319                         error_msg_and_die("invalid system call '%s'", token);
320         }
321
322         free(copy);
323
324         if (!done)
325                 error_msg_and_die("invalid system call '%s'", str);
326 }
327
328 /*
329  * Add numbers to SET according to STR specification.
330  */
331 void
332 qualify_tokens(const char *const str, struct number_set *const set,
333                string_to_uint_func func, const char *const name)
334 {
335         /* Clear the set. */
336         clear_number_set_array(set, 1);
337
338         /*
339          * Each leading ! character means inversion
340          * of the remaining specification.
341          */
342         const char *s = str;
343         while (*s == '!') {
344                 invert_number_set_array(set, 1);
345                 ++s;
346         }
347
348         if (strcmp(s, "none") == 0) {
349                 /*
350                  * No numbers are added to the set.
351                  * Subsequent is_number_in_set* invocations
352                  * will return set->not.
353                  */
354                 return;
355         } else if (strcmp(s, "all") == 0) {
356                 /* "all" == "!none" */
357                 invert_number_set_array(set, 1);
358                 return;
359         }
360
361         /*
362          * Split the string into comma separated tokens.
363          * For each token, find out the corresponding number
364          * by calling FUNC, and add that number to the set.
365          * The absence of tokens or a negative answer
366          * from FUNC is a fatal error.
367          */
368         char *copy = xstrdup(s);
369         char *saveptr = NULL;
370         int number = -1;
371
372         for (const char *token = strtok_r(copy, ",", &saveptr);
373              token; token = strtok_r(NULL, ",", &saveptr)) {
374                 number = func(token);
375                 if (number < 0)
376                         error_msg_and_die("invalid %s '%s'", name, token);
377
378                 add_number_to_set(number, set);
379         }
380
381         free(copy);
382
383         if (number < 0)
384                 error_msg_and_die("invalid %s '%s'", name, str);
385 }