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