]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/analyze/analyze-syscall-filter.c
Merge pull request #22608 from keszybz/doc-cleanups
[thirdparty/systemd.git] / src / analyze / analyze-syscall-filter.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include "analyze-syscall-filter.h"
4 #include "analyze.h"
5 #include "fd-util.h"
6 #include "fileio.h"
7 #include "nulstr-util.h"
8 #include "seccomp-util.h"
9 #include "set.h"
10 #include "strv.h"
11 #include "terminal-util.h"
12
13 #if HAVE_SECCOMP
14
15 static int load_kernel_syscalls(Set **ret) {
16 _cleanup_set_free_ Set *syscalls = NULL;
17 _cleanup_fclose_ FILE *f = NULL;
18 int r;
19
20 /* Let's read the available system calls from the list of available tracing events. Slightly dirty,
21 * but good enough for analysis purposes. */
22
23 f = fopen("/sys/kernel/tracing/available_events", "re");
24 if (!f) {
25 /* We tried the non-debugfs mount point and that didn't work. If it wasn't mounted, maybe the
26 * old debugfs mount point works? */
27 f = fopen("/sys/kernel/debug/tracing/available_events", "re");
28 if (!f)
29 return log_full_errno(IN_SET(errno, EPERM, EACCES, ENOENT) ? LOG_DEBUG : LOG_WARNING, errno,
30 "Can't read open tracefs' available_events file: %m");
31 }
32
33 for (;;) {
34 _cleanup_free_ char *line = NULL;
35 const char *e;
36
37 r = read_line(f, LONG_LINE_MAX, &line);
38 if (r < 0)
39 return log_error_errno(r, "Failed to read system call list: %m");
40 if (r == 0)
41 break;
42
43 e = startswith(line, "syscalls:sys_enter_");
44 if (!e)
45 continue;
46
47 /* These are named differently inside the kernel than their external name for historical
48 * reasons. Let's hide them here. */
49 if (STR_IN_SET(e, "newuname", "newfstat", "newstat", "newlstat", "sysctl"))
50 continue;
51
52 r = set_put_strdup(&syscalls, e);
53 if (r < 0)
54 return log_error_errno(r, "Failed to add system call to list: %m");
55 }
56
57 *ret = TAKE_PTR(syscalls);
58 return 0;
59 }
60
61 static void syscall_set_remove(Set *s, const SyscallFilterSet *set) {
62 const char *syscall;
63
64 if (!set)
65 return;
66
67 NULSTR_FOREACH(syscall, set->value) {
68 if (syscall[0] == '@')
69 continue;
70
71 free(set_remove(s, syscall));
72 }
73 }
74
75 static void dump_syscall_filter(const SyscallFilterSet *set) {
76 const char *syscall;
77
78 printf("%s%s%s\n"
79 " # %s\n",
80 ansi_highlight(),
81 set->name,
82 ansi_normal(),
83 set->help);
84
85 NULSTR_FOREACH(syscall, set->value)
86 printf(" %s%s%s\n", syscall[0] == '@' ? ansi_underline() : "", syscall, ansi_normal());
87 }
88
89 int verb_syscall_filters(int argc, char *argv[], void *userdata) {
90 bool first = true;
91
92 pager_open(arg_pager_flags);
93
94 if (strv_isempty(strv_skip(argv, 1))) {
95 _cleanup_set_free_ Set *kernel = NULL, *known = NULL;
96 const char *sys;
97 int k = 0; /* explicit initialization to appease gcc */
98
99 NULSTR_FOREACH(sys, syscall_filter_sets[SYSCALL_FILTER_SET_KNOWN].value)
100 if (set_put_strdup(&known, sys) < 0)
101 return log_oom();
102
103 if (!arg_quiet)
104 k = load_kernel_syscalls(&kernel);
105
106 for (int i = 0; i < _SYSCALL_FILTER_SET_MAX; i++) {
107 const SyscallFilterSet *set = syscall_filter_sets + i;
108 if (!first)
109 puts("");
110
111 dump_syscall_filter(set);
112 syscall_set_remove(kernel, set);
113 if (i != SYSCALL_FILTER_SET_KNOWN)
114 syscall_set_remove(known, set);
115 first = false;
116 }
117
118 if (arg_quiet) /* Let's not show the extra stuff in quiet mode */
119 return 0;
120
121 if (!set_isempty(known)) {
122 _cleanup_free_ char **l = NULL;
123 char **syscall;
124
125 printf("\n"
126 "# %sUngrouped System Calls%s (known but not included in any of the groups except @known):\n",
127 ansi_highlight(), ansi_normal());
128
129 l = set_get_strv(known);
130 if (!l)
131 return log_oom();
132
133 strv_sort(l);
134
135 STRV_FOREACH(syscall, l)
136 printf("# %s\n", *syscall);
137 }
138
139 if (k < 0) {
140 fputc('\n', stdout);
141 fflush(stdout);
142 if (!arg_quiet)
143 log_notice_errno(k, "# Not showing unlisted system calls, couldn't retrieve kernel system call list: %m");
144 } else if (!set_isempty(kernel)) {
145 _cleanup_free_ char **l = NULL;
146 char **syscall;
147
148 printf("\n"
149 "# %sUnlisted System Calls%s (supported by the local kernel, but not included in any of the groups listed above):\n",
150 ansi_highlight(), ansi_normal());
151
152 l = set_get_strv(kernel);
153 if (!l)
154 return log_oom();
155
156 strv_sort(l);
157
158 STRV_FOREACH(syscall, l)
159 printf("# %s\n", *syscall);
160 }
161 } else {
162 char **name;
163
164 STRV_FOREACH(name, strv_skip(argv, 1)) {
165 const SyscallFilterSet *set;
166
167 if (!first)
168 puts("");
169
170 set = syscall_filter_set_find(*name);
171 if (!set) {
172 /* make sure the error appears below normal output */
173 fflush(stdout);
174
175 return log_error_errno(SYNTHETIC_ERRNO(ENOENT),
176 "Filter set \"%s\" not found.", *name);
177 }
178
179 dump_syscall_filter(set);
180 first = false;
181 }
182 }
183
184 return 0;
185 }
186
187 #else
188 int verb_syscall_filters(int argc, char *argv[], void *userdata) {
189 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "Not compiled with syscall filters, sorry.");
190 }
191 #endif