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 
reset_all_signal_handlers(void)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 
reset_signal_mask(void)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 
sigaction_many_internal(const struct sigaction * sa,...)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 
sigset_add_many_ap(sigset_t * ss,va_list ap)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 
sigset_add_many(sigset_t * ss,...)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 
sigprocmask_many(int how,sigset_t * old,...)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 
signal_to_string(int signo)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 
signal_from_string(const char * s)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 
nop_signal_handler(int sig)236 void nop_signal_handler(int sig) {
237         /* nothing here */
238 }
239 
signal_is_blocked(int sig)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 
pop_pending_signal_internal(int sig,...)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 }
285