cc-test.sh:t_compose_hooks(): even more simple digmsg usage
[s-mailx.git] / signal.c
blob8ab80a869cfa328a50e55276962e8cc3e5e4c160
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)
7 */
8 /*
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
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.
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.
36 #undef n_FILE
37 #define n_FILE signal
39 #ifndef HAVE_AMALGAMATION
40 # include "nail.h"
41 #endif
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
53 * TODO few places.
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.)
87 #ifdef HAVE_NYD
88 struct nyd_info {
89 char const *ni_file;
90 char const *ni_fun;
91 ui32_t ni_chirp_line;
92 ui32_t ni_level;
94 #endif
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 */
105 #ifdef HAVE_NYD
106 static ui32_t _nyd_curr, _nyd_level;
107 static struct nyd_info _nyd_infos[NYD_CALLS_MAX];
108 #endif
110 /* */
111 static void a_signal_dummyhdl(int sig);
113 /* */
114 #ifdef HAVE_NYD
115 static void _nyd_print(int fd, struct nyd_info *nip);
116 #endif
118 static void
119 a_signal_dummyhdl(int sig){
120 n_UNUSED(sig);
123 #ifdef HAVE_NYD
124 static void
125 _nyd_print(int fd, struct nyd_info *nip)
127 char buf[80];
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));
134 if (u.i > 0) {
135 u.z = u.i;
136 if (u.z > sizeof buf)
137 u.z = sizeof buf - 1; /* (Skip \0) */
138 write(fd, buf, u.z);
141 #endif
143 FL int
144 c_sleep(void *v){ /* XXX installs sighdl+ due to outer jumps and SA_RESTART! */
145 sigset_t nset, oset;
146 struct sigaction nact, oact;
147 n_sighdl_t oint;
148 bool_t ignint;
149 uiz_t sec, msec;
150 char **argv;
151 NYD_ENTER;
153 argv = v;
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)
158 goto jesyn;
160 if(argv[1] == NULL){
161 msec = 0;
162 ignint = FAL0;
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)
166 goto jesyn;
167 else
168 ignint = (argv[2] != NULL);
170 if(UIZ_MAX / n_DATE_MILLISSEC < sec)
171 goto jeover;
172 sec *= n_DATE_MILLISSEC;
174 if(UIZ_MAX - sec < msec)
175 goto jeover;
176 msec += sec;
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);
190 sigfillset(&nset);
191 sigdelset(&nset, SIGINT);
192 sigprocmask(SIG_BLOCK, &nset, &oset);
193 sigemptyset(&nset);
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);
201 jleave:
202 NYD_LEAVE;
203 return (argv == NULL);
204 jeover:
205 n_err(_("`sleep': argument(s) overflow(s) datatype\n"));
206 n_pstate_err_no = n_ERR_OVERFLOW;
207 argv = NULL;
208 goto jleave;
209 jesyn:
210 n_err(_("Synopsis: sleep: <seconds> [<milliseconds>] [uninterruptible]\n"));
211 n_pstate_err_no = n_ERR_INVAL;
212 argv = NULL;
213 goto jleave;
217 #ifdef HAVE_DEVEL
218 FL int
219 c_sigstate(void *vp){ /* TODO remove again */
220 struct{
221 int val;
222 char const name[12];
223 } const *hdlp, hdla[] = {
224 {SIGINT, "SIGINT"}, {SIGHUP, "SIGHUP"}, {SIGQUIT, "SIGQUIT"},
225 {SIGTSTP, "SIGTSTP"}, {SIGTTIN, "SIGTTIN"}, {SIGTTOU, "SIGTTOU"},
226 {SIGCHLD, "SIGCHLD"}, {SIGPIPE, "SIGPIPE"}
228 char const *cp;
229 NYD2_ENTER;
231 if((cp = vp) != NULL && cp[0] != '\0'){
232 if(!asccasecmp(&cp[1], "all")){
233 if(cp[0] == '+')
234 hold_all_sigs();
235 else
236 rele_all_sigs();
237 }else if(!asccasecmp(&cp[1], "hold")){
238 if(cp[0] == '+')
239 hold_sigs();
240 else
241 rele_sigs();
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){
248 sighandler_type shp;
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?"))));
256 NYD2_LEAVE;
257 return OKAY;
259 #endif /* HAVE_DEVEL */
261 FL void
262 n_raise(int signo)
264 NYD2_ENTER;
265 if(n_pid == 0)
266 n_pid = getpid();
267 kill(n_pid, signo);
268 NYD2_LEAVE;
271 FL sighandler_type
272 safe_signal(int signum, sighandler_type handler)
274 struct sigaction nact, oact;
275 sighandler_type rv;
276 NYD2_ENTER;
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;
282 NYD2_LEAVE;
283 return rv;
286 FL n_sighdl_t
287 n_signal(int signo, n_sighdl_t hdl){
288 struct sigaction nact, oact;
289 NYD2_ENTER;
291 nact.sa_handler = hdl;
292 sigfillset(&nact.sa_mask);
293 nact.sa_flags = 0;
294 hdl = (sigaction(signo, &nact, &oact) != 0) ? SIG_ERR : oact.sa_handler;
295 NYD2_LEAVE;
296 return hdl;
299 FL void
300 hold_all_sigs(void)
302 NYD2_ENTER;
303 if (_alls_depth++ == 0) {
304 sigfillset(&_alls_nset);
305 sigdelset(&_alls_nset, SIGABRT);
306 #ifdef SIGBUS
307 sigdelset(&_alls_nset, SIGBUS);
308 #endif
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);
318 NYD2_LEAVE;
321 FL void
322 rele_all_sigs(void)
324 NYD2_ENTER;
325 if (--_alls_depth == 0)
326 sigprocmask(SIG_SETMASK, &_alls_oset, (sigset_t*)NULL);
327 NYD2_LEAVE;
330 FL void
331 hold_sigs(void)
333 NYD2_ENTER;
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);
341 NYD2_LEAVE;
344 FL void
345 rele_sigs(void)
347 NYD2_ENTER;
348 if (--_hold_sigdepth == 0)
349 sigprocmask(SIG_SETMASK, &_hold_oset, NULL);
350 NYD2_LEAVE;
353 /* TODO This is temporary gracyness */
354 static struct n_sigman *n__sigman;
355 static void n__sigman_hdl(int signo);
356 static void
357 n__sigman_hdl(int signo){
358 NYD_X; /* Signal handler */
359 n__sigman->sm_signo = signo;
360 siglongjmp(n__sigman->sm_jump, 1);
363 FL int
364 n__sigman_enter(struct n_sigman *self, int flags){
365 /* TODO no error checking when installing sighdls */
366 int rv;
367 NYD2_ENTER;
369 if((int)flags >= 0){
370 self->sm_flags = (enum n_sigman_flags)flags;
371 self->sm_signo = 0;
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);
381 n__sigman = self;
382 rv = 0;
383 }else{
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);
397 rv = self->sm_signo;
398 /* The signal mask has been restored, but of course rele_sigs() has
399 * already been called: account for restoration due to jump */
400 ++_hold_sigdepth;
402 rele_sigs();
403 NYD2_LEAVE;
404 return rv;
407 FL void
408 n_sigman_cleanup_ping(struct n_sigman *self){
409 ui32_t f;
410 NYD2_ENTER;
412 hold_sigs();
414 f = self->sm_flags;
415 f |= n__SIGMAN_PING;
416 self->sm_flags = f;
418 if(f & n_SIGMAN_HUP)
419 safe_signal(SIGHUP, SIG_IGN);
420 if(f & n_SIGMAN_INT)
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);
427 rele_sigs();
428 NYD2_LEAVE;
431 FL void
432 n_sigman_leave(struct n_sigman *self,
433 enum n_sigman_flags reraise_flags){
434 ui32_t f;
435 int sig;
436 NYD2_ENTER;
438 hold_sigs();
439 n__sigman = self->sm_outer;
441 f = self->sm_flags;
442 if(f & n_SIGMAN_HUP)
443 safe_signal(SIGHUP, self->sm_ohup);
444 if(f & n_SIGMAN_INT)
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);
451 rele_sigs();
453 sig = 0;
454 switch(self->sm_signo){
455 case SIGPIPE:
456 if((reraise_flags & n_SIGMAN_PIPE) ||
457 ((reraise_flags & n_SIGMAN_NTTYOUT_PIPE) &&
458 !(n_psonce & n_PSO_TTYOUT)))
459 sig = SIGPIPE;
460 break;
461 case SIGHUP:
462 if(reraise_flags & n_SIGMAN_HUP)
463 sig = SIGHUP;
464 break;
465 case SIGINT:
466 if(reraise_flags & n_SIGMAN_INT)
467 sig = SIGINT;
468 break;
469 case SIGQUIT:
470 if(reraise_flags & n_SIGMAN_QUIT)
471 sig = SIGQUIT;
472 break;
473 default:
474 break;
477 NYD2_LEAVE;
478 if(sig != 0){
479 sigset_t cset;
481 sigemptyset(&cset);
482 sigaddset(&cset, sig);
483 sigprocmask(SIG_UNBLOCK, &cset, NULL);
484 n_raise(sig);
488 FL int
489 n_sigman_peek(void){
490 int rv;
491 NYD2_ENTER;
492 rv = 0;
493 NYD2_LEAVE;
494 return rv;
497 FL void
498 n_sigman_consume(void){
499 NYD2_ENTER;
500 NYD2_LEAVE;
503 #ifdef HAVE_NYD
504 FL 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))
510 nip += _nyd_curr++;
511 else
512 _nyd_curr = 1;
513 nip->ni_file = file;
514 nip->ni_fun = fun;
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--);
520 FL void
521 _nyd_oncrash(int signo)
523 char pathbuf[PATH_MAX], s2ibuf[32], *cp;
524 struct sigaction xact;
525 sigset_t xset;
526 struct nyd_info *nip;
527 int fd;
528 size_t i, fnl;
529 char const *tmpdir;
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);
536 xact.sa_flags = 0;
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] = '.';
544 i = 1;
545 } else
546 memcpy(cp = pathbuf, tmpdir, i);
547 cp[i++] = '/'; /* xxx pathsep */
548 memcpy(cp += i, VAL_UAGENT, fnl);
549 i += 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)
554 fd = STDERR_FILENO;
556 # undef _X
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;
561 *cp = '\0';
562 i = signo;
563 do {
564 *--cp = "0123456789"[i % 10];
565 i /= 10;
566 } while (i != 0);
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"));
583 # undef _X
585 close(fd);
588 sigemptyset(&xset);
589 sigaddset(&xset, signo);
590 sigprocmask(SIG_UNBLOCK, &xset, NULL);
591 n_raise(signo);
592 for (;;)
593 _exit(n_EXIT_ERR);
595 #endif /* HAVE_NYD */
597 /* s-it-mode */