]> granicus.if.org Git - strace/blob - basic_filters.c
Move number_set interface to separate files
[strace] / basic_filters.c
1 /*
2  * Copyright (c) 2016 Dmitry V. Levin <ldv@altlinux.org>
3  * Copyright (c) 2016-2017 The strace developers.
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  * 3. The name of the author may not be used to endorse or promote products
15  *    derived from this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28
29 #include "defs.h"
30 #include "number_set.h"
31 #include "filter.h"
32 #include <regex.h>
33
34 static bool
35 qualify_syscall_number(const char *s, struct number_set *set)
36 {
37         int n = string_to_uint(s);
38         if (n < 0)
39                 return false;
40
41         unsigned int p;
42         bool done = false;
43
44         for (p = 0; p < SUPPORTED_PERSONALITIES; ++p) {
45                 if ((unsigned) n >= nsyscall_vec[p]) {
46                         continue;
47                 }
48                 add_number_to_set(n, &set[p]);
49                 done = true;
50         }
51
52         return done;
53 }
54
55 static void
56 regerror_msg_and_die(int errcode, const regex_t *preg,
57                      const char *str, const char *pattern)
58 {
59         char buf[512];
60
61         regerror(errcode, preg, buf, sizeof(buf));
62         error_msg_and_die("%s: %s: %s", str, pattern, buf);
63 }
64
65 static bool
66 qualify_syscall_regex(const char *s, struct number_set *set)
67 {
68         regex_t preg;
69         int rc;
70
71         if ((rc = regcomp(&preg, s, REG_EXTENDED | REG_NOSUB)) != 0)
72                 regerror_msg_and_die(rc, &preg, "regcomp", s);
73
74         unsigned int p;
75         bool found = false;
76         for (p = 0; p < SUPPORTED_PERSONALITIES; ++p) {
77                 unsigned int i;
78
79                 for (i = 0; i < nsyscall_vec[p]; ++i) {
80                         if (!sysent_vec[p][i].sys_name)
81                                 continue;
82                         rc = regexec(&preg, sysent_vec[p][i].sys_name,
83                                      0, NULL, 0);
84                         if (rc == REG_NOMATCH)
85                                 continue;
86                         else if (rc)
87                                 regerror_msg_and_die(rc, &preg, "regexec", s);
88                         add_number_to_set(i, &set[p]);
89                         found = true;
90                 }
91         }
92
93         regfree(&preg);
94         return found;
95 }
96
97 static unsigned int
98 lookup_class(const char *s)
99 {
100         static const struct {
101                 const char *name;
102                 unsigned int value;
103         } syscall_class[] = {
104                 { "desc",       TRACE_DESC      },
105                 { "file",       TRACE_FILE      },
106                 { "memory",     TRACE_MEMORY    },
107                 { "process",    TRACE_PROCESS   },
108                 { "signal",     TRACE_SIGNAL    },
109                 { "ipc",        TRACE_IPC       },
110                 { "network",    TRACE_NETWORK   },
111                 { "%desc",      TRACE_DESC      },
112                 { "%file",      TRACE_FILE      },
113                 { "%memory",    TRACE_MEMORY    },
114                 { "%process",   TRACE_PROCESS   },
115                 { "%signal",    TRACE_SIGNAL    },
116                 { "%ipc",       TRACE_IPC       },
117                 { "%network",   TRACE_NETWORK   },
118                 { "%stat",      TRACE_STAT      },
119                 { "%lstat",     TRACE_LSTAT     },
120                 { "%fstat",     TRACE_FSTAT     },
121                 { "%%stat",     TRACE_STAT_LIKE },
122                 { "%statfs",    TRACE_STATFS    },
123                 { "%fstatfs",   TRACE_FSTATFS   },
124                 { "%%statfs",   TRACE_STATFS_LIKE       },
125         };
126
127         unsigned int i;
128         for (i = 0; i < ARRAY_SIZE(syscall_class); ++i) {
129                 if (strcmp(s, syscall_class[i].name) == 0) {
130                         return syscall_class[i].value;
131                 }
132         }
133
134         return 0;
135 }
136
137 static bool
138 qualify_syscall_class(const char *s, struct number_set *set)
139 {
140         const unsigned int n = lookup_class(s);
141         if (!n)
142                 return false;
143
144         unsigned int p;
145         for (p = 0; p < SUPPORTED_PERSONALITIES; ++p) {
146                 unsigned int i;
147
148                 for (i = 0; i < nsyscall_vec[p]; ++i) {
149                         if (!sysent_vec[p][i].sys_name
150                             || (sysent_vec[p][i].sys_flags & n) != n) {
151                                 continue;
152                         }
153                         add_number_to_set(i, &set[p]);
154                 }
155         }
156
157         return true;
158 }
159
160 static bool
161 qualify_syscall_name(const char *s, struct number_set *set)
162 {
163         unsigned int p;
164         bool found = false;
165
166         for (p = 0; p < SUPPORTED_PERSONALITIES; ++p) {
167                 unsigned int i;
168
169                 for (i = 0; i < nsyscall_vec[p]; ++i) {
170                         if (!sysent_vec[p][i].sys_name
171                             || strcmp(s, sysent_vec[p][i].sys_name)) {
172                                 continue;
173                         }
174                         add_number_to_set(i, &set[p]);
175                         found = true;
176                 }
177         }
178
179         return found;
180 }
181
182 static bool
183 qualify_syscall(const char *token, struct number_set *set)
184 {
185         bool ignore_fail = false;
186
187         while (*token == '?') {
188                 token++;
189                 ignore_fail = true;
190         }
191         if (*token >= '0' && *token <= '9')
192                 return qualify_syscall_number(token, set) || ignore_fail;
193         if (*token == '/')
194                 return qualify_syscall_regex(token + 1, set) || ignore_fail;
195         return qualify_syscall_class(token, set)
196                || qualify_syscall_name(token, set)
197                || ignore_fail;
198 }
199
200 /*
201  * Add syscall numbers to SETs for each supported personality
202  * according to STR specification.
203  */
204 void
205 qualify_syscall_tokens(const char *const str, struct number_set *const set,
206                        const char *const name)
207 {
208         /* Clear all sets. */
209         unsigned int p;
210         for (p = 0; p < SUPPORTED_PERSONALITIES; ++p) {
211                 if (set[p].nslots)
212                         memset(set[p].vec, 0,
213                                sizeof(*set[p].vec) * set[p].nslots);
214                 set[p].not = false;
215         }
216
217         /*
218          * Each leading ! character means inversion
219          * of the remaining specification.
220          */
221         const char *s = str;
222 handle_inversion:
223         while (*s == '!') {
224                 for (p = 0; p < SUPPORTED_PERSONALITIES; ++p) {
225                         set[p].not = !set[p].not;
226                 }
227                 ++s;
228         }
229
230         if (strcmp(s, "none") == 0) {
231                 /*
232                  * No syscall numbers are added to sets.
233                  * Subsequent is_number_in_set invocations
234                  * will return set[p]->not.
235                  */
236                 return;
237         } else if (strcmp(s, "all") == 0) {
238                 s = "!none";
239                 goto handle_inversion;
240         }
241
242         /*
243          * Split the string into comma separated tokens.
244          * For each token, call qualify_syscall that will take care
245          * if adding appropriate syscall numbers to sets.
246          * The absence of tokens or a negative return code
247          * from qualify_syscall is a fatal error.
248          */
249         char *copy = xstrdup(s);
250         char *saveptr = NULL;
251         const char *token;
252         bool done = false;
253
254         for (token = strtok_r(copy, ",", &saveptr); token;
255              token = strtok_r(NULL, ",", &saveptr)) {
256                 done = qualify_syscall(token, set);
257                 if (!done) {
258                         error_msg_and_die("invalid %s '%s'", name, token);
259                 }
260         }
261
262         free(copy);
263
264         if (!done) {
265                 error_msg_and_die("invalid %s '%s'", name, str);
266         }
267 }
268
269 /*
270  * Add numbers to SET according to STR specification.
271  */
272 void
273 qualify_tokens(const char *const str, struct number_set *const set,
274                string_to_uint_func func, const char *const name)
275 {
276         /* Clear the set. */
277         if (set->nslots)
278                 memset(set->vec, 0, sizeof(*set->vec) * set->nslots);
279         set->not = false;
280
281         /*
282          * Each leading ! character means inversion
283          * of the remaining specification.
284          */
285         const char *s = str;
286 handle_inversion:
287         while (*s == '!') {
288                 set->not = !set->not;
289                 ++s;
290         }
291
292         if (strcmp(s, "none") == 0) {
293                 /*
294                  * No numbers are added to the set.
295                  * Subsequent is_number_in_set invocations will return set->not.
296                  */
297                 return;
298         } else if (strcmp(s, "all") == 0) {
299                 s = "!none";
300                 goto handle_inversion;
301         }
302
303         /*
304          * Split the string into comma separated tokens.
305          * For each token, find out the corresponding number
306          * by calling FUNC, and add that number to the set.
307          * The absence of tokens or a negative answer
308          * from FUNC is a fatal error.
309          */
310         char *copy = xstrdup(s);
311         char *saveptr = NULL;
312         const char *token;
313         int number = -1;
314
315         for (token = strtok_r(copy, ",", &saveptr); token;
316              token = strtok_r(NULL, ",", &saveptr)) {
317                 number = func(token);
318                 if (number < 0) {
319                         error_msg_and_die("invalid %s '%s'", name, token);
320                 }
321
322                 add_number_to_set(number, set);
323         }
324
325         free(copy);
326
327         if (number < 0) {
328                 error_msg_and_die("invalid %s '%s'", name, str);
329         }
330 }