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