]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/basic/signal-util.c
man/systemd.mount: tmpfs automatically gains After=swap.target dep
[thirdparty/systemd.git] / src / basic / signal-util.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <errno.h>
4 #include <stdarg.h>
5
6 #include "errno-util.h"
7 #include "macro.h"
8 #include "missing_syscall.h"
9 #include "missing_threads.h"
10 #include "parse-util.h"
11 #include "signal-util.h"
12 #include "stdio-util.h"
13 #include "string-table.h"
14 #include "string-util.h"
15
16 int reset_all_signal_handlers(void) {
17 static const struct sigaction sa = {
18 .sa_handler = SIG_DFL,
19 .sa_flags = SA_RESTART,
20 };
21 int r = 0;
22
23 for (int sig = 1; sig < _NSIG; sig++) {
24
25 /* These two cannot be caught... */
26 if (IN_SET(sig, SIGKILL, SIGSTOP))
27 continue;
28
29 /* On Linux the first two RT signals are reserved by
30 * glibc, and sigaction() will return EINVAL for them. */
31 if (sigaction(sig, &sa, NULL) < 0)
32 if (errno != EINVAL && r >= 0)
33 r = -errno;
34 }
35
36 return r;
37 }
38
39 int reset_signal_mask(void) {
40 sigset_t ss;
41
42 if (sigemptyset(&ss) < 0)
43 return -errno;
44
45 return RET_NERRNO(sigprocmask(SIG_SETMASK, &ss, NULL));
46 }
47
48 int sigaction_many_internal(const struct sigaction *sa, ...) {
49 int sig, r = 0;
50 va_list ap;
51
52 va_start(ap, sa);
53
54 /* negative signal ends the list. 0 signal is skipped. */
55 while ((sig = va_arg(ap, int)) >= 0) {
56
57 if (sig == 0)
58 continue;
59
60 if (sigaction(sig, sa, NULL) < 0) {
61 if (r >= 0)
62 r = -errno;
63 }
64 }
65
66 va_end(ap);
67
68 return r;
69 }
70
71 static int sigset_add_many_ap(sigset_t *ss, va_list ap) {
72 int sig, r = 0;
73
74 assert(ss);
75
76 while ((sig = va_arg(ap, int)) >= 0) {
77
78 if (sig == 0)
79 continue;
80
81 if (sigaddset(ss, sig) < 0) {
82 if (r >= 0)
83 r = -errno;
84 }
85 }
86
87 return r;
88 }
89
90 int sigset_add_many(sigset_t *ss, ...) {
91 va_list ap;
92 int r;
93
94 va_start(ap, ss);
95 r = sigset_add_many_ap(ss, ap);
96 va_end(ap);
97
98 return r;
99 }
100
101 int sigprocmask_many(int how, sigset_t *old, ...) {
102 va_list ap;
103 sigset_t ss;
104 int r;
105
106 if (sigemptyset(&ss) < 0)
107 return -errno;
108
109 va_start(ap, old);
110 r = sigset_add_many_ap(&ss, ap);
111 va_end(ap);
112
113 if (r < 0)
114 return r;
115
116 if (sigprocmask(how, &ss, old) < 0)
117 return -errno;
118
119 return 0;
120 }
121
122 static const char *const static_signal_table[] = {
123 [SIGHUP] = "HUP",
124 [SIGINT] = "INT",
125 [SIGQUIT] = "QUIT",
126 [SIGILL] = "ILL",
127 [SIGTRAP] = "TRAP",
128 [SIGABRT] = "ABRT",
129 [SIGBUS] = "BUS",
130 [SIGFPE] = "FPE",
131 [SIGKILL] = "KILL",
132 [SIGUSR1] = "USR1",
133 [SIGSEGV] = "SEGV",
134 [SIGUSR2] = "USR2",
135 [SIGPIPE] = "PIPE",
136 [SIGALRM] = "ALRM",
137 [SIGTERM] = "TERM",
138 #ifdef SIGSTKFLT
139 [SIGSTKFLT] = "STKFLT", /* Linux on SPARC doesn't know SIGSTKFLT */
140 #endif
141 [SIGCHLD] = "CHLD",
142 [SIGCONT] = "CONT",
143 [SIGSTOP] = "STOP",
144 [SIGTSTP] = "TSTP",
145 [SIGTTIN] = "TTIN",
146 [SIGTTOU] = "TTOU",
147 [SIGURG] = "URG",
148 [SIGXCPU] = "XCPU",
149 [SIGXFSZ] = "XFSZ",
150 [SIGVTALRM] = "VTALRM",
151 [SIGPROF] = "PROF",
152 [SIGWINCH] = "WINCH",
153 [SIGIO] = "IO",
154 [SIGPWR] = "PWR",
155 [SIGSYS] = "SYS"
156 };
157
158 DEFINE_PRIVATE_STRING_TABLE_LOOKUP(static_signal, int);
159
160 const char *signal_to_string(int signo) {
161 static thread_local char buf[STRLEN("RTMIN+") + DECIMAL_STR_MAX(int)];
162 const char *name;
163
164 name = static_signal_to_string(signo);
165 if (name)
166 return name;
167
168 if (signo >= SIGRTMIN && signo <= SIGRTMAX)
169 xsprintf(buf, "RTMIN+%d", signo - SIGRTMIN);
170 else
171 xsprintf(buf, "%d", signo);
172
173 return buf;
174 }
175
176 int signal_from_string(const char *s) {
177 const char *p;
178 int signo, r;
179
180 /* Check that the input is a signal number. */
181 if (safe_atoi(s, &signo) >= 0) {
182 if (SIGNAL_VALID(signo))
183 return signo;
184 else
185 return -ERANGE;
186 }
187
188 /* Drop "SIG" prefix. */
189 if (startswith(s, "SIG"))
190 s += 3;
191
192 /* Check that the input is a signal name. */
193 signo = static_signal_from_string(s);
194 if (signo > 0)
195 return signo;
196
197 /* Check that the input is RTMIN or
198 * RTMIN+n (0 <= n <= SIGRTMAX-SIGRTMIN). */
199 p = startswith(s, "RTMIN");
200 if (p) {
201 if (*p == '\0')
202 return SIGRTMIN;
203 if (*p != '+')
204 return -EINVAL;
205
206 r = safe_atoi(p, &signo);
207 if (r < 0)
208 return r;
209
210 if (signo < 0 || signo > SIGRTMAX - SIGRTMIN)
211 return -ERANGE;
212
213 return signo + SIGRTMIN;
214 }
215
216 /* Check that the input is RTMAX or
217 * RTMAX-n (0 <= n <= SIGRTMAX-SIGRTMIN). */
218 p = startswith(s, "RTMAX");
219 if (p) {
220 if (*p == '\0')
221 return SIGRTMAX;
222 if (*p != '-')
223 return -EINVAL;
224
225 r = safe_atoi(p, &signo);
226 if (r < 0)
227 return r;
228
229 if (signo > 0 || signo < SIGRTMIN - SIGRTMAX)
230 return -ERANGE;
231
232 return signo + SIGRTMAX;
233 }
234
235 return -EINVAL;
236 }
237
238 void nop_signal_handler(int sig) {
239 /* nothing here */
240 }
241
242 int signal_is_blocked(int sig) {
243 sigset_t ss;
244 int r;
245
246 r = pthread_sigmask(SIG_SETMASK, NULL, &ss);
247 if (r != 0)
248 return -r;
249
250 return RET_NERRNO(sigismember(&ss, sig));
251 }
252
253 int pop_pending_signal_internal(int sig, ...) {
254 sigset_t ss;
255 va_list ap;
256 int r;
257
258 if (sig < 0) /* Empty list? */
259 return -EINVAL;
260
261 if (sigemptyset(&ss) < 0)
262 return -errno;
263
264 /* Add first signal (if the signal is zero, we'll silently skip it, to make it easier to build
265 * parameter lists where some element are sometimes off, similar to how sigset_add_many_ap() handles
266 * this.) */
267 if (sig > 0 && sigaddset(&ss, sig) < 0)
268 return -errno;
269
270 /* Add all other signals */
271 va_start(ap, sig);
272 r = sigset_add_many_ap(&ss, ap);
273 va_end(ap);
274 if (r < 0)
275 return r;
276
277 r = sigtimedwait(&ss, NULL, &(struct timespec) { 0, 0 });
278 if (r < 0) {
279 if (errno == EAGAIN)
280 return 0;
281
282 return -errno;
283 }
284
285 return r; /* Returns the signal popped */
286 }
287
288 void propagate_signal(int sig, siginfo_t *siginfo) {
289 pid_t p;
290
291 /* To be called from a signal handler. Will raise the same signal again, in our process + in our threads.
292 *
293 * Note that we use raw_getpid() instead of getpid_cached(). We might have forked with raw_clone()
294 * earlier (see PID 1), and hence let's go to the raw syscall here. In particular as this is not
295 * performance sensitive code.
296 *
297 * Note that we use kill() rather than raise() as fallback, for similar reasons. */
298
299 p = raw_getpid();
300
301 if (rt_tgsigqueueinfo(p, gettid(), sig, siginfo) < 0)
302 assert_se(kill(p, sig) >= 0);
303 }