1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ Signal related stuff as well as NotYetDead functions.
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 * Copyright (c) 2012 - 2018 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
6 * SPDX-License-Identifier: BSD-3-Clause TODO ISC (better: drop)
9 * Copyright (c) 1980, 1993
10 * The Regents of the University of California. All rights reserved.
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
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.
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
39 #ifndef HAVE_AMALGAMATION
44 * TODO At the beginning of November 2015 -- for v14.9 -- i've tried for one
45 * TODO and a half week to convert this codebase to SysV style signal handling,
46 * TODO meaning no SA_RESTART and EINTR in a lot of places and error reporting
47 * TODO up the chain. I failed miserably, not only because S/MIME / SSL but
48 * TODO also because of general frustration. Directly after v14.9 i will strip
49 * TODO ANYTHING off the codebase (socket stuff etc.) and keep only the very
50 * TODO core, doing namespace and type cleanup and convert this core to a clean
51 * TODO approach, from which i plan to start this thing anew.
52 * TODO For now i introduced the n_sigman, yet another hack, to be used in a
54 * TODO The real solution:
55 * TODO - No SA_RESTART. Just like for my C++ library: encapsulate EINTR in
56 * TODO userspace at the systemcall (I/O rewrite: drop stdio), normal
57 * TODO interface auto-restarts, special _intr() series return EINTR.
58 * TODO Do n_sigman_poll()/peek()/whatever whenever desired for the former,
59 * TODO report errors up the call chain, have places where operations can be
60 * TODO "properly" aborted.
61 * TODO - We save the initial signal settings upon program startup.
62 * TODO - We register our sigman handlers once, at program startup.
63 * TODO Maximally, and most likely only due to lack of atomic CAS, ignore
64 * TODO or block some signals temporarily. Best if not.
65 * TODO The signal handlers only set a flag. Block all signals for handler
66 * TODO execution, like this we are safe to "set the first signal was x".
67 * TODO - In interactive context, ignore SIGTERM.
68 * TODO I.e., see the POSIX standard for what a shell does.
69 * TODO - In non-interactive context, don't know anything about job control!?!
70 * TODO - Place child processes in their own process group. Restore the signal
71 * TODO mask back to the saved original one for them, before exec.
72 * TODO - Except for job control related (<-> interactive) ignore any signals
73 * TODO while we are "behind" a child that occupies the terminal. For those,
74 * TODO perform proper terminal attribute handling. For childs that don't
75 * TODO occupy the terminal we "are the shell" and should therefore manage
76 * TODO them accordingly, including termination request as necessary.
77 * TODO And if we have a worker in a pipeline, we need to manage it and deal
78 * TODO with it properly, WITHOUT temporary signal overwrites.
79 * TODO - No more jumps.
80 * TODO - (When sockets will be reintroduced, non-blocking.)
81 * TODO - (When SSL is reintroduced, memory BIO. It MAY be necessary to
82 * TODO temporarily block signals during a few SSL functions? Read SSL docu!
83 * TODO But i prefer blocking since it's a single syscall, not temporary
84 * TODO SA_RESTART setting, since that has to be done for every signal.)
96 /* {hold,rele}_all_sigs() */
97 static size_t _alls_depth
;
98 static sigset_t _alls_nset
, _alls_oset
;
100 /* {hold,rele}_sigs() */
101 static size_t _hold_sigdepth
;
102 static sigset_t _hold_nset
, _hold_oset
;
104 /* NYD, memory pool debug */
106 static ui32_t _nyd_curr
, _nyd_level
;
107 static struct nyd_info _nyd_infos
[NYD_CALLS_MAX
];
111 static void a_signal_dummyhdl(int sig
);
115 static void _nyd_print(int fd
, struct nyd_info
*nip
);
119 a_signal_dummyhdl(int sig
){
125 _nyd_print(int fd
, struct nyd_info
*nip
)
128 union {int i
; size_t z
;} u
;
130 u
.i
= snprintf(buf
, sizeof buf
,
131 "%c [%2" PRIu32
"] %.25s (%.40s:%" PRIu32
")\n",
132 "=><"[(nip
->ni_chirp_line
>> 29) & 0x3], nip
->ni_level
, nip
->ni_fun
,
133 nip
->ni_file
, (nip
->ni_chirp_line
& 0x1FFFFFFFu
));
136 if (u
.z
> sizeof buf
)
137 u
.z
= sizeof buf
- 1; /* (Skip \0) */
144 c_sleep(void *v
){ /* XXX installs sighdl+ due to outer jumps and SA_RESTART! */
146 struct sigaction nact
, oact
;
155 if((n_idec_uiz_cp(&sec
, argv
[0], 0, NULL
) &
156 (n_IDEC_STATE_EMASK
| n_IDEC_STATE_CONSUMED
)
157 ) != n_IDEC_STATE_CONSUMED
)
163 }else if((n_idec_uiz_cp(&msec
, argv
[1], 0, NULL
) &
164 (n_IDEC_STATE_EMASK
| n_IDEC_STATE_CONSUMED
)
165 ) != n_IDEC_STATE_CONSUMED
)
168 ignint
= (argv
[2] != NULL
);
170 if(UIZ_MAX
/ n_DATE_MILLISSEC
< sec
)
172 sec
*= n_DATE_MILLISSEC
;
174 if(UIZ_MAX
- sec
< msec
)
178 /* XXX This requires a terrible mess of signal handling:
179 * - we usually have our SA_RESTART handler, that must be replaced
180 * - we most often have a sigsetjmp() to overcome SA_RESTART
181 * - TODO for now hold_all_sigs() most often on in robot mode,
182 * TODO therefore we also need sigprocmask(), to block anything
183 * TODO except SIGINT, and to unblock SIGINT, thus! */
184 memset(&nact
, 0, sizeof nact
);
185 nact
.sa_handler
= &a_signal_dummyhdl
;
186 sigemptyset(&nact
.sa_mask
);
187 sigaddset(&nact
.sa_mask
, SIGINT
);
188 sigaction(SIGINT
, &nact
, &oact
);
191 sigdelset(&nset
, SIGINT
);
192 sigprocmask(SIG_BLOCK
, &nset
, &oset
);
194 sigaddset(&nset
, SIGINT
);
195 sigprocmask(SIG_UNBLOCK
, &nset
, NULL
);
197 n_pstate_err_no
= (n_msleep(msec
, ignint
) > 0) ? n_ERR_INTR
: n_ERR_NONE
;
199 sigprocmask(SIG_SETMASK
, &oset
, NULL
);
200 sigaction(SIGINT
, &oact
, NULL
);
203 return (argv
== NULL
);
205 n_err(_("`sleep': argument(s) overflow(s) datatype\n"));
206 n_pstate_err_no
= n_ERR_OVERFLOW
;
210 n_err(_("Synopsis: sleep: <seconds> [<milliseconds>] [uninterruptible]\n"));
211 n_pstate_err_no
= n_ERR_INVAL
;
219 c_sigstate(void *vp
){ /* TODO remove again */
223 } const *hdlp
, hdla
[] = {
224 {SIGINT
, "SIGINT"}, {SIGHUP
, "SIGHUP"}, {SIGQUIT
, "SIGQUIT"},
225 {SIGTSTP
, "SIGTSTP"}, {SIGTTIN
, "SIGTTIN"}, {SIGTTOU
, "SIGTTOU"},
226 {SIGCHLD
, "SIGCHLD"}, {SIGPIPE
, "SIGPIPE"}
231 if((cp
= vp
) != NULL
&& cp
[0] != '\0'){
232 if(!asccasecmp(&cp
[1], "all")){
237 }else if(!asccasecmp(&cp
[1], "hold")){
245 fprintf(n_stdout
, "alls_depth %zu, hold_sigdepth %zu\nHandlers:\n",
246 _alls_depth
, _hold_sigdepth
);
247 for(hdlp
= hdla
; hdlp
< &hdla
[n_NELEM(hdla
)]; ++hdlp
){
250 shp
= safe_signal(hdlp
->val
, SIG_IGN
);
251 safe_signal(hdlp
->val
, shp
);
252 fprintf(n_stdout
, " %s: %p (%s)\n", hdlp
->name
, shp
,
253 (shp
== SIG_ERR
? "ERR" : (shp
== SIG_DFL
? "DFL"
254 : (shp
== SIG_IGN
? "IGN" : "ptf?"))));
259 #endif /* HAVE_DEVEL */
272 safe_signal(int signum
, sighandler_type handler
)
274 struct sigaction nact
, oact
;
278 nact
.sa_handler
= handler
;
279 sigfillset(&nact
.sa_mask
);
280 nact
.sa_flags
= SA_RESTART
;
281 rv
= (sigaction(signum
, &nact
, &oact
) != 0) ? SIG_ERR
: oact
.sa_handler
;
287 n_signal(int signo
, n_sighdl_t hdl
){
288 struct sigaction nact
, oact
;
291 nact
.sa_handler
= hdl
;
292 sigfillset(&nact
.sa_mask
);
294 hdl
= (sigaction(signo
, &nact
, &oact
) != 0) ? SIG_ERR
: oact
.sa_handler
;
303 if (_alls_depth
++ == 0) {
304 sigfillset(&_alls_nset
);
305 sigdelset(&_alls_nset
, SIGABRT
);
307 sigdelset(&_alls_nset
, SIGBUS
);
309 sigdelset(&_alls_nset
, SIGFPE
);
310 sigdelset(&_alls_nset
, SIGILL
);
311 sigdelset(&_alls_nset
, SIGKILL
);
312 sigdelset(&_alls_nset
, SIGSEGV
);
313 sigdelset(&_alls_nset
, SIGSTOP
);
315 sigdelset(&_alls_nset
, SIGCHLD
);
316 sigprocmask(SIG_BLOCK
, &_alls_nset
, &_alls_oset
);
325 if (--_alls_depth
== 0)
326 sigprocmask(SIG_SETMASK
, &_alls_oset
, (sigset_t
*)NULL
);
334 if (_hold_sigdepth
++ == 0) {
335 sigemptyset(&_hold_nset
);
336 sigaddset(&_hold_nset
, SIGHUP
);
337 sigaddset(&_hold_nset
, SIGINT
);
338 sigaddset(&_hold_nset
, SIGQUIT
);
339 sigprocmask(SIG_BLOCK
, &_hold_nset
, &_hold_oset
);
348 if (--_hold_sigdepth
== 0)
349 sigprocmask(SIG_SETMASK
, &_hold_oset
, NULL
);
353 /* TODO This is temporary gracyness */
354 static struct n_sigman
*n__sigman
;
355 static void n__sigman_hdl(int signo
);
357 n__sigman_hdl(int signo
){
358 NYD_X
; /* Signal handler */
359 n__sigman
->sm_signo
= signo
;
360 siglongjmp(n__sigman
->sm_jump
, 1);
364 n__sigman_enter(struct n_sigman
*self
, int flags
){
365 /* TODO no error checking when installing sighdls */
370 self
->sm_flags
= (enum n_sigman_flags
)flags
;
372 self
->sm_outer
= n__sigman
;
373 if(flags
& n_SIGMAN_HUP
)
374 self
->sm_ohup
= safe_signal(SIGHUP
, &n__sigman_hdl
);
375 if(flags
& n_SIGMAN_INT
)
376 self
->sm_oint
= safe_signal(SIGINT
, &n__sigman_hdl
);
377 if(flags
& n_SIGMAN_QUIT
)
378 self
->sm_oquit
= safe_signal(SIGQUIT
, &n__sigman_hdl
);
379 if(flags
& n_SIGMAN_PIPE
)
380 self
->sm_opipe
= safe_signal(SIGPIPE
, &n__sigman_hdl
);
384 flags
= self
->sm_flags
;
386 /* Just in case of a race (signal while holding and ignoring? really?) */
387 if(!(flags
& n__SIGMAN_PING
)){
388 if(flags
& n_SIGMAN_HUP
)
389 safe_signal(SIGHUP
, SIG_IGN
);
390 if(flags
& n_SIGMAN_INT
)
391 safe_signal(SIGINT
, SIG_IGN
);
392 if(flags
& n_SIGMAN_QUIT
)
393 safe_signal(SIGQUIT
, SIG_IGN
);
394 if(flags
& n_SIGMAN_PIPE
)
395 safe_signal(SIGPIPE
, SIG_IGN
);
398 /* The signal mask has been restored, but of course rele_sigs() has
399 * already been called: account for restoration due to jump */
408 n_sigman_cleanup_ping(struct n_sigman
*self
){
419 safe_signal(SIGHUP
, SIG_IGN
);
421 safe_signal(SIGINT
, SIG_IGN
);
422 if(f
& n_SIGMAN_QUIT
)
423 safe_signal(SIGQUIT
, SIG_IGN
);
424 if(f
& n_SIGMAN_PIPE
)
425 safe_signal(SIGPIPE
, SIG_IGN
);
432 n_sigman_leave(struct n_sigman
*self
,
433 enum n_sigman_flags reraise_flags
){
439 n__sigman
= self
->sm_outer
;
443 safe_signal(SIGHUP
, self
->sm_ohup
);
445 safe_signal(SIGINT
, self
->sm_oint
);
446 if(f
& n_SIGMAN_QUIT
)
447 safe_signal(SIGQUIT
, self
->sm_oquit
);
448 if(f
& n_SIGMAN_PIPE
)
449 safe_signal(SIGPIPE
, self
->sm_opipe
);
454 switch(self
->sm_signo
){
456 if((reraise_flags
& n_SIGMAN_PIPE
) ||
457 ((reraise_flags
& n_SIGMAN_NTTYOUT_PIPE
) &&
458 !(n_psonce
& n_PSO_TTYOUT
)))
462 if(reraise_flags
& n_SIGMAN_HUP
)
466 if(reraise_flags
& n_SIGMAN_INT
)
470 if(reraise_flags
& n_SIGMAN_QUIT
)
482 sigaddset(&cset
, sig
);
483 sigprocmask(SIG_UNBLOCK
, &cset
, NULL
);
498 n_sigman_consume(void){
505 _nyd_chirp(ui8_t act
, char const *file
, ui32_t line
, char const *fun
)
507 struct nyd_info
*nip
= _nyd_infos
;
509 if (_nyd_curr
!= n_NELEM(_nyd_infos
))
515 nip
->ni_chirp_line
= ((ui32_t
)(act
& 0x3) << 29) | (line
& 0x1FFFFFFFu
);
516 nip
->ni_level
= ((act
== 0) ? _nyd_level
517 : (act
== 1) ? ++_nyd_level
: _nyd_level
--);
521 _nyd_oncrash(int signo
)
523 char pathbuf
[PATH_MAX
], s2ibuf
[32], *cp
;
524 struct sigaction xact
;
526 struct nyd_info
*nip
;
531 n_LCTA(sizeof("./") -1 + sizeof(VAL_UAGENT
) -1 + sizeof(".dat") < PATH_MAX
,
532 "System limits too low for fixed-size buffer operation");
534 xact
.sa_handler
= SIG_DFL
;
535 sigemptyset(&xact
.sa_mask
);
537 sigaction(signo
, &xact
, NULL
);
539 i
= strlen(tmpdir
= ok_vlook(TMPDIR
));
540 fnl
= sizeof(VAL_UAGENT
) -1;
542 if (i
+ 1 + fnl
+ 1 + sizeof(".dat") > sizeof(pathbuf
)) {
543 (cp
= pathbuf
)[0] = '.';
546 memcpy(cp
= pathbuf
, tmpdir
, i
);
547 cp
[i
++] = '/'; /* xxx pathsep */
548 memcpy(cp
+= i
, VAL_UAGENT
, fnl
);
550 memcpy(cp
+= fnl
, ".dat", sizeof(".dat"));
551 fnl
= i
+ sizeof(".dat") -1;
553 if ((fd
= open(pathbuf
, O_WRONLY
| O_CREAT
| O_EXCL
, 0666)) == -1)
557 # define _X(X) (X), sizeof(X) -1
558 write(fd
, _X("\n\nNYD: program dying due to signal "));
560 cp
= s2ibuf
+ sizeof(s2ibuf
) -1;
564 *--cp
= "0123456789"[i
% 10];
567 write(fd
, cp
, PTR2SIZE((s2ibuf
+ sizeof(s2ibuf
) -1) - cp
));
569 write(fd
, _X(":\n"));
571 if (_nyd_infos
[n_NELEM(_nyd_infos
) - 1].ni_file
!= NULL
)
572 for (i
= _nyd_curr
, nip
= _nyd_infos
+ i
; i
< n_NELEM(_nyd_infos
); ++i
)
573 _nyd_print(fd
, nip
++);
574 for (i
= 0, nip
= _nyd_infos
; i
< _nyd_curr
; ++i
)
575 _nyd_print(fd
, nip
++);
577 write(fd
, _X("----------\nCome up to the lab and see what's on the slab\n"));
579 if (fd
!= STDERR_FILENO
) {
580 write(STDERR_FILENO
, _X("Crash NYD listing written to "));
581 write(STDERR_FILENO
, pathbuf
, fnl
);
582 write(STDERR_FILENO
, _X("\n"));
589 sigaddset(&xset
, signo
);
590 sigprocmask(SIG_UNBLOCK
, &xset
, NULL
);
595 #endif /* HAVE_NYD */