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