collect(): ~[FfMmUu]: should default to the "dot" (Andrew Gee)
[s-mailx.git] / signal.c
blob3773e4afca1647ed10b2b05fc6c08af14031f0f4
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 */
7 /*
8 * Copyright (c) 1980, 1993
9 * The Regents of the University of California. All rights reserved.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. Neither the name of the University nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
35 #undef n_FILE
36 #define n_FILE signal
38 #ifndef HAVE_AMALGAMATION
39 # include "nail.h"
40 #endif
43 * TODO At the beginning of November 2015 -- for v14.9 -- i've tried for one
44 * TODO and a half week to convert this codebase to SysV style signal handling,
45 * TODO meaning no SA_RESTART and EINTR in a lot of places and error reporting
46 * TODO up the chain. I failed miserably, not only because S/MIME / SSL but
47 * TODO also because of general frustration. Directly after v14.9 i will strip
48 * TODO ANYTHING off the codebase (socket stuff etc.) and keep only the very
49 * TODO core, doing namespace and type cleanup and convert this core to a clean
50 * TODO approach, from which i plan to start this thing anew.
51 * TODO For now i introduced the n_sigman, yet another hack, to be used in a
52 * TODO few places.
53 * TODO The real solution:
54 * TODO - No SA_RESTART. Just like for my C++ library: encapsulate EINTR in
55 * TODO userspace at the systemcall (I/O rewrite: drop stdio), normal
56 * TODO interface auto-restarts, special _intr() series return EINTR.
57 * TODO Do n_sigman_poll()/peek()/whatever whenever desired for the former,
58 * TODO report errors up the call chain, have places where operations can be
59 * TODO "properly" aborted.
60 * TODO - We save the initial signal settings upon program startup.
61 * TODO - We register our sigman handlers once, at program startup.
62 * TODO Maximally, and most likely only due to lack of atomic CAS, ignore
63 * TODO or block some signals temporarily. Best if not.
64 * TODO The signal handlers only set a flag. Block all signals for handler
65 * TODO execution, like this we are safe to "set the first signal was x".
66 * TODO - In interactive context, ignore SIGTERM.
67 * TODO I.e., see the POSIX standard for what a shell does.
68 * TODO - In non-interactive context, don't know anything about job control!?!
69 * TODO - Place child processes in their own process group. Restore the signal
70 * TODO mask back to the saved original one for them, before exec.
71 * TODO - Except for job control related (<-> interactive) ignore any signals
72 * TODO while we are "behind" a child that occupies the terminal. For those,
73 * TODO perform proper terminal attribute handling. For childs that don't
74 * TODO occupy the terminal we "are the shell" and should therefore manage
75 * TODO them accordingly, including termination request as necessary.
76 * TODO And if we have a worker in a pipeline, we need to manage it and deal
77 * TODO with it properly, WITHOUT temporary signal overwrites.
78 * TODO - No more jumps.
79 * TODO - (When sockets will be reintroduced, non-blocking.)
80 * TODO - (When SSL is reintroduced, memory BIO. It MAY be necessary to
81 * TODO temporarily block signals during a few SSL functions? Read SSL docu!
82 * TODO But i prefer blocking since it's a single syscall, not temporary
83 * TODO SA_RESTART setting, since that has to be done for every signal.)
86 #ifdef HAVE_NYD
87 struct nyd_info {
88 char const *ni_file;
89 char const *ni_fun;
90 ui32_t ni_chirp_line;
91 ui32_t ni_level;
93 #endif
95 /* {hold,rele}_all_sigs() */
96 static size_t _alls_depth;
97 static sigset_t _alls_nset, _alls_oset;
99 /* {hold,rele}_sigs() */
100 static size_t _hold_sigdepth;
101 static sigset_t _hold_nset, _hold_oset;
103 /* NYD, memory pool debug */
104 #ifdef HAVE_NYD
105 static ui32_t _nyd_curr, _nyd_level;
106 static struct nyd_info _nyd_infos[NYD_CALLS_MAX];
107 #endif
109 /* */
110 #ifdef HAVE_NYD
111 static void _nyd_print(int fd, struct nyd_info *nip);
112 #endif
114 #ifdef HAVE_NYD
115 static void
116 _nyd_print(int fd, struct nyd_info *nip)
118 char buf[80];
119 union {int i; size_t z;} u;
121 u.i = snprintf(buf, sizeof buf,
122 "%c [%2" PRIu32 "] %.25s (%.16s:%" PRIu32 ")\n",
123 "=><"[(nip->ni_chirp_line >> 29) & 0x3], nip->ni_level, nip->ni_fun,
124 nip->ni_file, (nip->ni_chirp_line & 0x1FFFFFFFu));
125 if (u.i > 0) {
126 u.z = u.i;
127 if (u.z > sizeof buf)
128 u.z = sizeof buf - 1; /* (Skip \0) */
129 write(fd, buf, u.z);
132 #endif
134 #ifdef HAVE_DEVEL
135 FL int
136 c_sigstate(void *vp){ /* TODO remove again */
137 struct{
138 int val;
139 char const name[12];
140 } const *hdlp, hdla[] = {
141 {SIGINT, "SIGINT"}, {SIGHUP, "SIGHUP"}, {SIGQUIT, "SIGQUIT"},
142 {SIGTSTP, "SIGTSTP"}, {SIGTTIN, "SIGTTIN"}, {SIGTTOU, "SIGTTOU"},
143 {SIGCHLD, "SIGCHLD"}, {SIGPIPE, "SIGPIPE"}
145 char const *cp;
146 NYD2_ENTER;
148 if((cp = vp) != NULL && cp[0] != '\0'){
149 if(!asccasecmp(&cp[1], "all")){
150 if(cp[0] == '+')
151 hold_all_sigs();
152 else
153 rele_all_sigs();
154 }else if(!asccasecmp(&cp[1], "hold")){
155 if(cp[0] == '+')
156 hold_sigs();
157 else
158 rele_sigs();
162 fprintf(n_stdout, "alls_depth %zu, hold_sigdepth %zu\nHandlers:\n",
163 _alls_depth, _hold_sigdepth);
164 for(hdlp = hdla; hdlp < &hdla[n_NELEM(hdla)]; ++hdlp){
165 sighandler_type shp;
167 shp = safe_signal(hdlp->val, SIG_IGN);
168 safe_signal(hdlp->val, shp);
169 fprintf(n_stdout, " %s: %p (%s)\n", hdlp->name, shp,
170 (shp == SIG_ERR ? "ERR" : (shp == SIG_DFL ? "DFL"
171 : (shp == SIG_IGN ? "IGN" : "ptf?"))));
173 NYD2_LEAVE;
174 return OKAY;
176 #endif /* HAVE_DEVEL */
178 FL void
179 n_raise(int signo)
181 NYD2_ENTER;
182 if(n_pid == 0)
183 n_pid = getpid();
184 kill(n_pid, signo);
185 NYD2_LEAVE;
188 FL sighandler_type
189 safe_signal(int signum, sighandler_type handler)
191 struct sigaction nact, oact;
192 sighandler_type rv;
193 NYD2_ENTER;
195 nact.sa_handler = handler;
196 sigfillset(&nact.sa_mask);
197 nact.sa_flags = SA_RESTART;
198 rv = (sigaction(signum, &nact, &oact) != 0) ? SIG_ERR : oact.sa_handler;
199 NYD2_LEAVE;
200 return rv;
203 FL n_sighdl_t
204 n_signal(int signo, n_sighdl_t hdl){
205 struct sigaction nact, oact;
206 NYD2_ENTER;
208 nact.sa_handler = hdl;
209 sigfillset(&nact.sa_mask);
210 nact.sa_flags = 0;
211 hdl = (sigaction(signo, &nact, &oact) != 0) ? SIG_ERR : oact.sa_handler;
212 NYD2_LEAVE;
213 return hdl;
216 FL void
217 hold_all_sigs(void)
219 NYD2_ENTER;
220 if (_alls_depth++ == 0) {
221 sigfillset(&_alls_nset);
222 sigdelset(&_alls_nset, SIGABRT);
223 #ifdef SIGBUS
224 sigdelset(&_alls_nset, SIGBUS);
225 #endif
226 sigdelset(&_alls_nset, SIGFPE);
227 sigdelset(&_alls_nset, SIGILL);
228 sigdelset(&_alls_nset, SIGKILL);
229 sigdelset(&_alls_nset, SIGSEGV);
230 sigdelset(&_alls_nset, SIGSTOP);
232 sigdelset(&_alls_nset, SIGCHLD);
233 sigprocmask(SIG_BLOCK, &_alls_nset, &_alls_oset);
235 NYD2_LEAVE;
238 FL void
239 rele_all_sigs(void)
241 NYD2_ENTER;
242 if (--_alls_depth == 0)
243 sigprocmask(SIG_SETMASK, &_alls_oset, (sigset_t*)NULL);
244 NYD2_LEAVE;
247 FL void
248 hold_sigs(void)
250 NYD2_ENTER;
251 if (_hold_sigdepth++ == 0) {
252 sigemptyset(&_hold_nset);
253 sigaddset(&_hold_nset, SIGHUP);
254 sigaddset(&_hold_nset, SIGINT);
255 sigaddset(&_hold_nset, SIGQUIT);
256 sigprocmask(SIG_BLOCK, &_hold_nset, &_hold_oset);
258 NYD2_LEAVE;
261 FL void
262 rele_sigs(void)
264 NYD2_ENTER;
265 if (--_hold_sigdepth == 0)
266 sigprocmask(SIG_SETMASK, &_hold_oset, NULL);
267 NYD2_LEAVE;
270 /* TODO This is temporary gracyness */
271 static struct n_sigman *n__sigman;
272 static void n__sigman_hdl(int signo);
273 static void
274 n__sigman_hdl(int signo){
275 NYD_X; /* Signal handler */
276 n__sigman->sm_signo = signo;
277 siglongjmp(n__sigman->sm_jump, 1);
280 FL int
281 n__sigman_enter(struct n_sigman *self, int flags){
282 /* TODO no error checking when installing sighdls */
283 int rv;
284 NYD2_ENTER;
286 if((int)flags >= 0){
287 self->sm_flags = (enum n_sigman_flags)flags;
288 self->sm_signo = 0;
289 self->sm_outer = n__sigman;
290 if(flags & n_SIGMAN_HUP)
291 self->sm_ohup = safe_signal(SIGHUP, &n__sigman_hdl);
292 if(flags & n_SIGMAN_INT)
293 self->sm_oint = safe_signal(SIGINT, &n__sigman_hdl);
294 if(flags & n_SIGMAN_QUIT)
295 self->sm_oquit = safe_signal(SIGQUIT, &n__sigman_hdl);
296 if(flags & n_SIGMAN_PIPE)
297 self->sm_opipe = safe_signal(SIGPIPE, &n__sigman_hdl);
298 n__sigman = self;
299 rv = 0;
300 }else{
301 flags = self->sm_flags;
303 /* Just in case of a race (signal while holding and ignoring? really?) */
304 if(!(flags & n__SIGMAN_PING)){
305 if(flags & n_SIGMAN_HUP)
306 safe_signal(SIGHUP, SIG_IGN);
307 if(flags & n_SIGMAN_INT)
308 safe_signal(SIGINT, SIG_IGN);
309 if(flags & n_SIGMAN_QUIT)
310 safe_signal(SIGQUIT, SIG_IGN);
311 if(flags & n_SIGMAN_PIPE)
312 safe_signal(SIGPIPE, SIG_IGN);
314 rv = self->sm_signo;
315 /* The signal mask has been restored, but of course rele_sigs() has
316 * already been called: account for restoration due to jump */
317 ++_hold_sigdepth;
319 rele_sigs();
320 NYD2_LEAVE;
321 return rv;
324 FL void
325 n_sigman_cleanup_ping(struct n_sigman *self){
326 ui32_t f;
327 NYD2_ENTER;
329 hold_sigs();
331 f = self->sm_flags;
332 f |= n__SIGMAN_PING;
333 self->sm_flags = f;
335 if(f & n_SIGMAN_HUP)
336 safe_signal(SIGHUP, SIG_IGN);
337 if(f & n_SIGMAN_INT)
338 safe_signal(SIGINT, SIG_IGN);
339 if(f & n_SIGMAN_QUIT)
340 safe_signal(SIGQUIT, SIG_IGN);
341 if(f & n_SIGMAN_PIPE)
342 safe_signal(SIGPIPE, SIG_IGN);
344 rele_sigs();
345 NYD2_LEAVE;
348 FL void
349 n_sigman_leave(struct n_sigman *self,
350 enum n_sigman_flags reraise_flags){
351 ui32_t f;
352 int sig;
353 NYD2_ENTER;
355 hold_sigs();
356 n__sigman = self->sm_outer;
358 f = self->sm_flags;
359 if(f & n_SIGMAN_HUP)
360 safe_signal(SIGHUP, self->sm_ohup);
361 if(f & n_SIGMAN_INT)
362 safe_signal(SIGINT, self->sm_oint);
363 if(f & n_SIGMAN_QUIT)
364 safe_signal(SIGQUIT, self->sm_oquit);
365 if(f & n_SIGMAN_PIPE)
366 safe_signal(SIGPIPE, self->sm_opipe);
368 rele_sigs();
370 sig = 0;
371 switch(self->sm_signo){
372 case SIGPIPE:
373 if((reraise_flags & n_SIGMAN_PIPE) ||
374 ((reraise_flags & n_SIGMAN_NTTYOUT_PIPE) &&
375 !(n_psonce & n_PSO_TTYOUT)))
376 sig = SIGPIPE;
377 break;
378 case SIGHUP:
379 if(reraise_flags & n_SIGMAN_HUP)
380 sig = SIGHUP;
381 break;
382 case SIGINT:
383 if(reraise_flags & n_SIGMAN_INT)
384 sig = SIGINT;
385 break;
386 case SIGQUIT:
387 if(reraise_flags & n_SIGMAN_QUIT)
388 sig = SIGQUIT;
389 break;
390 default:
391 break;
394 NYD2_LEAVE;
395 if(sig != 0){
396 sigset_t cset;
398 sigemptyset(&cset);
399 sigaddset(&cset, sig);
400 sigprocmask(SIG_UNBLOCK, &cset, NULL);
401 n_raise(sig);
405 FL int
406 n_sigman_peek(void){
407 int rv;
408 NYD2_ENTER;
409 rv = 0;
410 NYD2_LEAVE;
411 return rv;
414 FL void
415 n_sigman_consume(void){
416 NYD2_ENTER;
417 NYD2_LEAVE;
420 #ifdef HAVE_NYD
421 FL void
422 _nyd_chirp(ui8_t act, char const *file, ui32_t line, char const *fun)
424 struct nyd_info *nip = _nyd_infos;
426 if (_nyd_curr != n_NELEM(_nyd_infos))
427 nip += _nyd_curr++;
428 else
429 _nyd_curr = 1;
430 nip->ni_file = file;
431 nip->ni_fun = fun;
432 nip->ni_chirp_line = ((ui32_t)(act & 0x3) << 29) | (line & 0x1FFFFFFFu);
433 nip->ni_level = ((act == 0) ? _nyd_level
434 : (act == 1) ? ++_nyd_level : _nyd_level--);
437 FL void
438 _nyd_oncrash(int signo)
440 char pathbuf[PATH_MAX], s2ibuf[32], *cp;
441 struct sigaction xact;
442 sigset_t xset;
443 struct nyd_info *nip;
444 int fd;
445 size_t i, fnl;
446 char const *tmpdir;
448 n_LCTA(sizeof("./") -1 + sizeof(VAL_UAGENT) -1 + sizeof(".dat") < PATH_MAX,
449 "System limits too low for fixed-size buffer operation");
451 xact.sa_handler = SIG_DFL;
452 sigemptyset(&xact.sa_mask);
453 xact.sa_flags = 0;
454 sigaction(signo, &xact, NULL);
456 i = strlen(tmpdir = ok_vlook(TMPDIR));
457 fnl = sizeof(VAL_UAGENT) -1;
459 if (i + 1 + fnl + 1 + sizeof(".dat") > sizeof(pathbuf)) {
460 (cp = pathbuf)[0] = '.';
461 i = 1;
462 } else
463 memcpy(cp = pathbuf, tmpdir, i);
464 cp[i++] = '/'; /* xxx pathsep */
465 memcpy(cp += i, VAL_UAGENT, fnl);
466 i += fnl;
467 memcpy(cp += fnl, ".dat", sizeof(".dat"));
468 fnl = i + sizeof(".dat") -1;
470 if ((fd = open(pathbuf, O_WRONLY | O_CREAT | O_EXCL, 0666)) == -1)
471 fd = STDERR_FILENO;
473 # undef _X
474 # define _X(X) (X), sizeof(X) -1
475 write(fd, _X("\n\nNYD: program dying due to signal "));
477 cp = s2ibuf + sizeof(s2ibuf) -1;
478 *cp = '\0';
479 i = signo;
480 do {
481 *--cp = "0123456789"[i % 10];
482 i /= 10;
483 } while (i != 0);
484 write(fd, cp, PTR2SIZE((s2ibuf + sizeof(s2ibuf) -1) - cp));
486 write(fd, _X(":\n"));
488 if (_nyd_infos[n_NELEM(_nyd_infos) - 1].ni_file != NULL)
489 for (i = _nyd_curr, nip = _nyd_infos + i; i < n_NELEM(_nyd_infos); ++i)
490 _nyd_print(fd, nip++);
491 for (i = 0, nip = _nyd_infos; i < _nyd_curr; ++i)
492 _nyd_print(fd, nip++);
494 write(fd, _X("----------\nCome up to the lab and see what's on the slab\n"));
496 if (fd != STDERR_FILENO) {
497 write(STDERR_FILENO, _X("Crash NYD listing written to "));
498 write(STDERR_FILENO, pathbuf, fnl);
499 write(STDERR_FILENO, _X("\n"));
500 # undef _X
502 close(fd);
505 sigemptyset(&xset);
506 sigaddset(&xset, signo);
507 sigprocmask(SIG_UNBLOCK, &xset, NULL);
508 n_raise(signo);
509 for (;;)
510 _exit(n_EXIT_ERR);
512 #endif /* HAVE_NYD */
514 /* s-it-mode */