1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ Implementation of sigs.h.
3 *
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 - 2020 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
6 * SPDX-License-Identifier: BSD-3-Clause TODO ISC (better: drop)
7 */
8 /*
9 * Copyright (c) 1980, 1993
10 * The Regents of the University of California. All rights reserved.
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 * 1. Redistributions of source code must retain the above copyright
16 * notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in the
19 * documentation and/or other materials provided with the distribution.
20 * 3. Neither the name of the University nor the names of its contributors
21 * may be used to endorse or promote products derived from this software
22 * without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 */
36 #undef su_FILE
37 #define su_FILE sigs
38 #define mx_SOURCE
39
40 #ifndef mx_HAVE_AMALGAMATION
41 # include "mx/nail.h"
42 #endif
43
44 #include <stdarg.h>
45
46 #include <su/cs.h>
47 #include <su/icodec.h>
48 #include <su/mem.h>
49
50 #include "mx/cmd.h"
51
52 /* TODO fake */
53 #include "mx/sigs.h"
54 #include "su/code-in.h"
55
56 /*
57 * TODO At the beginning of November 2015 -- for v14.9 -- i've tried for one
58 * TODO and a half week to convert this codebase to SysV style signal handling,
59 * TODO meaning no SA_RESTART and EINTR in a lot of places and error reporting
60 * TODO up the chain. I failed miserably, not only because S/MIME / SSL but
61 * TODO also because of general frustration. Directly after v14.10 i'll strip
62 * TODO ANYTHING off the codebase (socket stuff etc.) and keep only the very
63 * TODO core, doing namespace and type cleanup and convert this core to a clean
64 * TODO approach, from which i plan to start this thing anew.
65 * TODO For now i introduced the n_sigman, yet another hack, to be used in a
66 * TODO few places.
67 * TODO The real solution:
68 * TODO - No SA_RESTART. Just like for my C++ library: encapsulate EINTR in
69 * TODO userspace at the systemcall (I/O rewrite: drop stdio), normal
70 * TODO interface auto-restarts, special _intr() series return EINTR.
71 * TODO Do n_sigman_poll()/peek()/whatever whenever desired for the former,
72 * TODO report errors up the call chain, have places where operations can be
73 * TODO "properly" aborted.
74 * TODO - We save the initial signal settings upon program startup.
75 * TODO - We register our sigman handlers once, at program startup.
76 * TODO Maximally, and most likely only due to lack of atomic CAS, ignore
77 * TODO or block some signals temporarily. Best if not.
78 * TODO The signal handlers only set a flag. Block all signals for handler
79 * TODO execution, like this we are safe to "set the first signal was x".
80 * TODO - In interactive context, ignore SIGTERM.
81 * TODO I.e., see the POSIX standard for what a shell does.
82 * TODO - In non-interactive context, don't know anything about job control!?!
83 * TODO - Place child processes in their own process group. Restore the signal
84 * TODO mask back to the saved original one for them, before exec.
85 * TODO - Except for job control related (<-> interactive) ignore any signals
86 * TODO while we are "behind" a child that occupies the terminal. For those,
87 * TODO perform proper terminal attribute handling. For children that do not
88 * TODO occupy the terminal we "are the shell" and should therefore manage
89 * TODO them accordingly, including termination request as necessary.
90 * TODO And if we have a worker in a pipeline, we need to manage it and deal
91 * TODO with it properly, WITHOUT temporary signal overwrites.
92 * TODO - No more jumps.
93 * TODO - (When sockets will be reintroduced, non-blocking.)
94 * TODO - (When SSL is reintroduced, memory BIO. It MAY be necessary to
95 * TODO temporarily block signals during a few SSL functions? Read SSL docu!
96 * TODO But i prefer blocking since it's a single syscall, not temporary
97 * TODO SA_RESTART setting, since that has to be done for every signal.)
98 */
99
100 /* signal_all_ */
101 static uz volatile a_sigs_all_depth;
102 static sigset_t a_sigs_all_nset, a_sigs_all_oset;
103
104 /* {hold,rele}_sigs() */
105 static uz _hold_sigdepth;
106 static sigset_t _hold_nset, _hold_oset;
107
108 /* */
109 static void a_sigs_dummyhdl(int sig);
110
111 static void
a_sigs_dummyhdl(int sig)112 a_sigs_dummyhdl(int sig){
113 UNUSED(sig);
114 }
115
116 int
c_sleep(void * v)117 c_sleep(void *v){ /* XXX installs sighdl+ due to outer jumps and SA_RESTART! */
118 sigset_t nset, oset;
119 struct sigaction nact, oact;
120 boole ignint;
121 uz sec, msec;
122 char **argv;
123 NYD_IN;
124
125 argv = v;
126
127 if((su_idec_uz_cp(&sec, argv[0], 0, NULL) &
128 (su_IDEC_STATE_EMASK | su_IDEC_STATE_CONSUMED)
129 ) != su_IDEC_STATE_CONSUMED)
130 goto jesyn;
131
132 if(argv[1] == NULL){
133 msec = 0;
134 ignint = FAL0;
135 }else if((su_idec_uz_cp(&msec, argv[1], 0, NULL) &
136 (su_IDEC_STATE_EMASK | su_IDEC_STATE_CONSUMED)
137 ) != su_IDEC_STATE_CONSUMED)
138 goto jesyn;
139 else
140 ignint = (argv[2] != NULL);
141
142 if(UZ_MAX / n_DATE_MILLISSEC < sec)
143 goto jeover;
144 sec *= n_DATE_MILLISSEC;
145
146 if(UZ_MAX - sec < msec)
147 goto jeover;
148 msec += sec;
149
150 /* XXX This requires a terrible mess of signal handling:
151 * - we usually have our SA_RESTART handler, that must be replaced
152 * - we most often have a sigsetjmp() to overcome SA_RESTART
153 * - TODO for now signal_all_hold() most often on in robot mode,
154 * TODO therefore we also need sigprocmask(), to block anything
155 * TODO except SIGINT, and to unblock SIGINT, thus! */
156 su_mem_set(&nact, 0, sizeof nact);
157 nact.sa_handler = &a_sigs_dummyhdl;
158 sigemptyset(&nact.sa_mask);
159 sigaddset(&nact.sa_mask, SIGINT);
160 sigaction(SIGINT, &nact, &oact);
161
162 sigfillset(&nset);
163 sigdelset(&nset, SIGINT);
164 sigprocmask(SIG_BLOCK, &nset, &oset);
165 sigemptyset(&nset);
166 sigaddset(&nset, SIGINT);
167 sigprocmask(SIG_UNBLOCK, &nset, NULL);
168
169 n_pstate_err_no = (n_msleep(msec, ignint) > 0) ? su_ERR_INTR : su_ERR_NONE;
170
171 sigprocmask(SIG_SETMASK, &oset, NULL);
172 sigaction(SIGINT, &oact, NULL);
173 jleave:
174 NYD_OU;
175 return (argv == NULL);
176 jeover:
177 n_err(_("sleep: argument(s) overflow(s) datatype\n"));
178 n_pstate_err_no = su_ERR_OVERFLOW;
179 argv = NULL;
180 goto jleave;
181 jesyn:
182 mx_cmd_print_synopsis(mx_cmd_firstfit("sleep"), NIL);
183 n_pstate_err_no = su_ERR_INVAL;
184 argv = NULL;
185 goto jleave;
186 }
187
188 void
n_raise(int signo)189 n_raise(int signo)
190 {
191 NYD2_IN;
192 if(n_pid == 0)
193 n_pid = getpid();
194 kill(n_pid, signo);
195 NYD2_OU;
196 }
197
198 n_sighdl_t
safe_signal(int signum,n_sighdl_t handler)199 safe_signal(int signum, n_sighdl_t handler)
200 {
201 struct sigaction nact, oact;
202 n_sighdl_t rv;
203 NYD2_IN;
204
205 nact.sa_handler = handler;
206 sigfillset(&nact.sa_mask);
207 nact.sa_flags = SA_RESTART;
208 rv = (sigaction(signum, &nact, &oact) != 0) ? SIG_ERR : oact.sa_handler;
209 NYD2_OU;
210 return rv;
211 }
212
213 n_sighdl_t
n_signal(int signo,n_sighdl_t hdl)214 n_signal(int signo, n_sighdl_t hdl){
215 struct sigaction nact, oact;
216 NYD2_IN;
217
218 nact.sa_handler = hdl;
219 sigfillset(&nact.sa_mask);
220 nact.sa_flags = 0;
221 hdl = (sigaction(signo, &nact, &oact) != 0) ? SIG_ERR : oact.sa_handler;
222 NYD2_OU;
223 return hdl;
224 }
225
226 void
mx_sigs_all_hold(s32 sigadjust,...)227 mx_sigs_all_hold(s32 sigadjust, ...){
228 sigset_t unbl, *ossp;
229 boole nounbl, anyunbl;
230
231 sigemptyset(&unbl);
232
233 if((nounbl = (a_sigs_all_depth++ == 0))){
234 sigfillset(&a_sigs_all_nset);
235 sigdelset(&a_sigs_all_nset, SIGABRT);
236 #ifdef SIGBUS
237 sigdelset(&a_sigs_all_nset, SIGBUS);
238 #endif
239 sigdelset(&a_sigs_all_nset, SIGFPE);
240 sigdelset(&a_sigs_all_nset, SIGILL);
241 sigdelset(&a_sigs_all_nset, SIGSEGV);
242
243 sigdelset(&a_sigs_all_nset, SIGCHLD);
244
245 sigdelset(&a_sigs_all_nset, SIGKILL);
246 sigdelset(&a_sigs_all_nset, SIGSTOP);
247
248 ossp = &a_sigs_all_oset;
249 }else
250 ossp = NIL;
251
252 anyunbl = FAL0;
253 if(sigadjust != 0){
254 va_list val;
255
256 va_start(val, sigadjust);
257
258 do if(sigadjust > 0)
259 sigaddset(&a_sigs_all_nset, sigadjust);
260 else{
261 sigadjust = -sigadjust;
262 sigdelset(&a_sigs_all_nset, sigadjust);
263 sigaddset(&unbl, sigadjust);
264 anyunbl = TRU1;
265 }while((sigadjust = va_arg(val, s32)) != 0);
266
267 va_end(val);
268 }
269
270 sigprocmask(SIG_BLOCK, &a_sigs_all_nset, ossp);
271 if(!nounbl && anyunbl)
272 sigprocmask(SIG_UNBLOCK, &unbl, NIL);
273 }
274
275 void
mx_sigs_all_rele(void)276 mx_sigs_all_rele(void){
277 if(--a_sigs_all_depth == 0)
278 sigprocmask(SIG_SETMASK, &a_sigs_all_oset, NIL);
279 }
280
281 void
hold_sigs(void)282 hold_sigs(void)
283 {
284 NYD2_IN;
285 if (_hold_sigdepth++ == 0) {
286 sigemptyset(&_hold_nset);
287 sigaddset(&_hold_nset, SIGHUP);
288 sigaddset(&_hold_nset, SIGINT);
289 sigaddset(&_hold_nset, SIGQUIT);
290 sigprocmask(SIG_BLOCK, &_hold_nset, &_hold_oset);
291 }
292 NYD2_OU;
293 }
294
295 void
rele_sigs(void)296 rele_sigs(void)
297 {
298 NYD2_IN;
299 if (--_hold_sigdepth == 0)
300 sigprocmask(SIG_SETMASK, &_hold_oset, NULL);
301 NYD2_OU;
302 }
303
304 /* TODO This is temporary gracyness */
305 static struct n_sigman *n__sigman;
306 static void n__sigman_hdl(int signo);
307 static void
n__sigman_hdl(int signo)308 n__sigman_hdl(int signo){
309 NYD; /* Signal handler */
310 n__sigman->sm_signo = signo;
311 siglongjmp(n__sigman->sm_jump, 1);
312 }
313
314 int
n__sigman_enter(struct n_sigman * self,int flags)315 n__sigman_enter(struct n_sigman *self, int flags){
316 /* TODO no error checking when installing sighdls */
317 int rv;
318 NYD2_IN;
319
320 if((int)flags >= 0){
321 self->sm_flags = (enum n_sigman_flags)flags;
322 self->sm_signo = 0;
323 self->sm_outer = n__sigman;
324 if(flags & n_SIGMAN_HUP)
325 self->sm_ohup = safe_signal(SIGHUP, &n__sigman_hdl);
326 if(flags & n_SIGMAN_INT)
327 self->sm_oint = safe_signal(SIGINT, &n__sigman_hdl);
328 if(flags & n_SIGMAN_QUIT)
329 self->sm_oquit = safe_signal(SIGQUIT, &n__sigman_hdl);
330 if(flags & n_SIGMAN_PIPE)
331 self->sm_opipe = safe_signal(SIGPIPE, &n__sigman_hdl);
332 n__sigman = self;
333 rv = 0;
334 }else{
335 flags = self->sm_flags;
336
337 /* Just in case of a race (signal while holding and ignoring? really?) */
338 if(!(flags & n__SIGMAN_PING)){
339 if(flags & n_SIGMAN_HUP)
340 safe_signal(SIGHUP, SIG_IGN);
341 if(flags & n_SIGMAN_INT)
342 safe_signal(SIGINT, SIG_IGN);
343 if(flags & n_SIGMAN_QUIT)
344 safe_signal(SIGQUIT, SIG_IGN);
345 if(flags & n_SIGMAN_PIPE)
346 safe_signal(SIGPIPE, SIG_IGN);
347 }
348 rv = self->sm_signo;
349 /* The signal mask has been restored, but of course rele_sigs() has
350 * already been called: account for restoration due to jump */
351 ++_hold_sigdepth;
352 }
353 rele_sigs();
354 NYD2_OU;
355 return rv;
356 }
357
358 void
n_sigman_cleanup_ping(struct n_sigman * self)359 n_sigman_cleanup_ping(struct n_sigman *self){
360 u32 f;
361 NYD2_IN;
362
363 hold_sigs();
364
365 f = self->sm_flags;
366 f |= n__SIGMAN_PING;
367 self->sm_flags = f;
368
369 if(f & n_SIGMAN_HUP)
370 safe_signal(SIGHUP, SIG_IGN);
371 if(f & n_SIGMAN_INT)
372 safe_signal(SIGINT, SIG_IGN);
373 if(f & n_SIGMAN_QUIT)
374 safe_signal(SIGQUIT, SIG_IGN);
375 if(f & n_SIGMAN_PIPE)
376 safe_signal(SIGPIPE, SIG_IGN);
377
378 rele_sigs();
379 NYD2_OU;
380 }
381
382 void
n_sigman_leave(struct n_sigman * self,enum n_sigman_flags reraise_flags)383 n_sigman_leave(struct n_sigman *self,
384 enum n_sigman_flags reraise_flags){
385 u32 f;
386 int sig;
387 NYD2_IN;
388
389 hold_sigs();
390 n__sigman = self->sm_outer;
391
392 f = self->sm_flags;
393 if(f & n_SIGMAN_HUP)
394 safe_signal(SIGHUP, self->sm_ohup);
395 if(f & n_SIGMAN_INT)
396 safe_signal(SIGINT, self->sm_oint);
397 if(f & n_SIGMAN_QUIT)
398 safe_signal(SIGQUIT, self->sm_oquit);
399 if(f & n_SIGMAN_PIPE)
400 safe_signal(SIGPIPE, self->sm_opipe);
401
402 rele_sigs();
403
404 sig = 0;
405 switch(self->sm_signo){
406 case SIGPIPE:
407 if((reraise_flags & n_SIGMAN_PIPE) ||
408 ((reraise_flags & n_SIGMAN_NTTYOUT_PIPE) &&
409 !(n_psonce & n_PSO_TTYOUT)))
410 sig = SIGPIPE;
411 break;
412 case SIGHUP:
413 if(reraise_flags & n_SIGMAN_HUP)
414 sig = SIGHUP;
415 break;
416 case SIGINT:
417 if(reraise_flags & n_SIGMAN_INT)
418 sig = SIGINT;
419 break;
420 case SIGQUIT:
421 if(reraise_flags & n_SIGMAN_QUIT)
422 sig = SIGQUIT;
423 break;
424 default:
425 break;
426 }
427
428 NYD2_OU;
429 if(sig != 0){
430 sigset_t cset;
431
432 sigemptyset(&cset);
433 sigaddset(&cset, sig);
434 sigprocmask(SIG_UNBLOCK, &cset, NULL);
435 n_raise(sig);
436 }
437 }
438
439 int
n_sigman_peek(void)440 n_sigman_peek(void){
441 int rv;
442 NYD2_IN;
443 rv = 0;
444 NYD2_OU;
445 return rv;
446 }
447
448 void
n_sigman_consume(void)449 n_sigman_consume(void){
450 NYD2_IN;
451 NYD2_OU;
452 }
453
454 #if su_DVLOR(1, 0)
455 static void a_sigs_nyd__dump(su_up cookie, char const *buf, su_uz blen);
456 static void
a_sigs_nyd__dump(su_up cookie,char const * buf,su_uz blen)457 a_sigs_nyd__dump(su_up cookie, char const *buf, su_uz blen){
458 write((int)cookie, buf, blen);
459 }
460
461 void
mx__nyd_oncrash(int signo)462 mx__nyd_oncrash(int signo){
463 char pathbuf[PATH_MAX], s2ibuf[32], *cp;
464 int fd;
465 uz i, fnl;
466 char const *tmpdir;
467 u32 poption_save;
468
469 su_nyd_set_disabled(TRU1);
470
471 LCTA(sizeof("./") -1 + sizeof(VAL_UAGENT) -1 + sizeof(".dat") < PATH_MAX,
472 "System limits too low for fixed-size buffer operation");
473
474 poption_save = n_poption; /* XXX sigh */
475 n_poption &= ~n_PO_D_V;
476
477 i = su_cs_len(tmpdir = ok_vlook(TMPDIR));
478
479 n_poption = poption_save;
480
481 fnl = sizeof(VAL_UAGENT) -1;
482
483 if(i + 1 + fnl + 1 + sizeof(".dat") > sizeof(pathbuf)){
484 (cp = pathbuf)[0] = '.';
485 i = 1;
486 }else
487 su_mem_copy(cp = pathbuf, tmpdir, i);
488 cp[i++] = '/'; /* xxx pathsep */
489 su_mem_copy(cp += i, VAL_UAGENT, fnl);
490 i += fnl;
491 su_mem_copy(cp += fnl, ".dat", sizeof(".dat"));
492 fnl = i + sizeof(".dat") -1;
493
494 if((fd = open(pathbuf, O_WRONLY | O_CREAT | O_EXCL, 0666)) == -1)
495 fd = STDERR_FILENO;
496
497 # undef _X
498 # define _X(X) X, sizeof(X) -1
499 if(signo == SIGUSR2)
500 write(fd, _X("\n\nNYD: program chirp list"));
501 else
502 write(fd, _X("\n\nNYD: program dying due to signal "));
503
504 if(signo != SIGUSR2){
505 cp = &s2ibuf[sizeof(s2ibuf) -1];
506 *cp = '\0';
507 i = signo;
508 do{
509 *--cp = "0123456789"[i % 10];
510 i /= 10;
511 }while(i != 0);
512 write(fd, cp, P2UZ(&s2ibuf[sizeof(s2ibuf) -1] - cp));
513 }
514
515 write(fd, _X(":\n"));
516
517 su_nyd_dump(&a_sigs_nyd__dump, S(uz,S(u32,fd)));
518
519 write(fd, _X("-----\nCome up to the lab and see what's on the slab\n"));
520
521 if(fd != STDERR_FILENO){
522 write(STDERR_FILENO, _X("Crash NYD listing written to "));
523 write(STDERR_FILENO, pathbuf, fnl);
524 write(STDERR_FILENO, _X("\n"));
525 # undef _X
526
527 close(fd);
528 }
529
530 if(signo != SIGUSR2){
531 struct sigaction xact;
532 sigset_t xset;
533
534 xact.sa_handler = SIG_DFL;
535 sigemptyset(&xact.sa_mask);
536 xact.sa_flags = 0;
537 sigaction(signo, &xact, NIL);
538
539 sigemptyset(&xset);
540 sigaddset(&xset, signo);
541 sigprocmask(SIG_UNBLOCK, &xset, NIL);
542
543 n_raise(signo);
544
545 for(;;)
546 _exit(n_EXIT_ERR);
547 }
548
549 su_nyd_set_disabled(FAL0);
550 }
551 #endif /* DVLOR(1,0) */
552
553 #include "su/code-ou.h"
554 /* s-it-mode */
555