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