xref: /netbsd/usr.bin/mail/sig.c (revision d51a8cc9)
1 /*	$NetBSD: sig.c,v 1.4 2021/11/27 22:16:41 rillig Exp $	*/
2 
3 /*
4  * Copyright (c) 1980, 1993
5  *	The Regents of the University of California.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. Neither the name of the University nor the names of its contributors
16  *    may be used to endorse or promote products derived from this software
17  *    without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31 
32 #include <sys/cdefs.h>
33 #ifndef lint
34 __RCSID("$NetBSD: sig.c,v 1.4 2021/11/27 22:16:41 rillig Exp $");
35 #endif /* not lint */
36 
37 #include <assert.h>
38 #include <util.h>
39 #include <sys/queue.h>
40 
41 #include "rcv.h"
42 #include "extern.h"
43 #include "sig.h"
44 
45 /*
46  * Mail -- a mail program
47  *
48  * Signal routines.
49  */
50 
51 static sig_t sigarray[NSIG];
52 
53 typedef struct q_entry_s {
54 	int qe_signo;
55 	sig_t qe_handler;
56 	struct q_entry_s *qe_next;
57 } q_entry_t;
58 
59 static struct {
60 	q_entry_t *qe_first;
61 	q_entry_t **qe_last;
62 } sigq = { NULL, &sigq.qe_first };
63 #define SIGQUEUE_INIT(p)  do {\
64 	(p)->qe_first = NULL;\
65 	(p)->qe_last = &((p)->qe_first);\
66   } while (0)
67 
68 /*
69  * The routines alloc_entry() and free_entry() manage the queue
70  * elements.
71  *
72  * Currently, they just assign one element per signo from a fix array
73  * as we don't support POSIX signal queues.  We leave them as this may
74  * change in the future and the modifications will be isolated.
75  */
76 static q_entry_t *
alloc_entry(int signo)77 alloc_entry(int signo)
78 {
79 	static q_entry_t entries[NSIG];
80 	q_entry_t *e;
81 
82 	/*
83 	 * We currently only post one signal per signal number, so
84 	 * there is no need to make this complicated.
85 	 */
86 	e = &entries[signo];
87 	if (e->qe_signo != 0)
88 		return NULL;
89 
90 	e->qe_signo = signo;
91 	e->qe_handler = sigarray[signo];
92 	e->qe_next = NULL;
93 
94 	return e;
95 }
96 
97 static void
free_entry(q_entry_t * e)98 free_entry(q_entry_t *e)
99 {
100 
101 	e->qe_signo = 0;
102 	e->qe_handler = NULL;
103 	e->qe_next = NULL;
104 }
105 
106 /*
107  * Attempt to post a signal to the sigq.
108  */
109 static void
sig_post(int signo)110 sig_post(int signo)
111 {
112 	q_entry_t *e;
113 
114 	if (sigarray[signo] == SIG_DFL || sigarray[signo] == SIG_IGN)
115 		return;
116 
117 	e = alloc_entry(signo);
118 	if (e != NULL) {
119 		*sigq.qe_last = e;
120 		sigq.qe_last = &e->qe_next;
121 	}
122 }
123 
124 /*
125  * Check the sigq for any pending signals.  If any are found,
126  * preform the required actions and remove them from the queue.
127  */
128 PUBLIC void
sig_check(void)129 sig_check(void)
130 {
131 	q_entry_t *e;
132 	sigset_t nset;
133 	sigset_t oset;
134 	void (*handler)(int);
135 	int signo;
136 
137 	(void)sigfillset(&nset);
138 	(void)sigprocmask(SIG_SETMASK, &nset, &oset);
139 
140 	while ((e = sigq.qe_first) != NULL) {
141 		signo = e->qe_signo;
142 		handler = e->qe_handler;
143 
144 		/*
145 		 * Remove the entry from the queue and free it.
146 		 */
147 		sigq.qe_first = e->qe_next;
148 		if (sigq.qe_first == NULL)
149 			sigq.qe_last = &sigq.qe_first;
150 		free_entry(e);
151 
152 		if (handler == SIG_DFL || handler == SIG_IGN) {
153 			assert(/*CONSTCOND*/ 0);	/* These should not get posted. */
154 		}
155 		else {
156 			(void)sigprocmask(SIG_SETMASK, &oset, NULL);
157 			handler(signo);
158 			(void)sigprocmask(SIG_SETMASK, &nset, NULL);
159 		}
160 	}
161 	(void)sigprocmask(SIG_SETMASK, &oset, NULL);
162 }
163 
164 PUBLIC sig_t
sig_current(int signo)165 sig_current(int signo)
166 {
167 	assert(signo > 0 && signo < NSIG);
168 	return sigarray[signo];
169 }
170 
171 PUBLIC sig_t
sig_signal(int signo,sig_t handler)172 sig_signal(int signo, sig_t handler)
173 {
174 	sig_t old_handler;
175 	sigset_t nset;
176 	sigset_t oset;
177 
178 	assert(signo > 0 && signo < NSIG);
179 
180 	(void)sigemptyset(&nset);
181 	(void)sigaddset(&nset, signo);
182 	(void)sigprocmask(SIG_BLOCK, &nset, &oset);
183 
184 	old_handler = sigarray[signo];
185 	sigarray[signo] = handler;
186 
187 	(void)sigprocmask(SIG_SETMASK, &oset, NULL);
188 
189 	return old_handler;
190 }
191 
192 static void
do_default_handler(int signo,int flags)193 do_default_handler(int signo, int flags)
194 {
195 	struct sigaction nsa;
196 	struct sigaction osa;
197 	sigset_t nset;
198 	sigset_t oset;
199 	int save_errno;
200 
201 	save_errno = errno;
202 	(void)sigemptyset(&nsa.sa_mask);
203 	nsa.sa_flags = flags;
204 	nsa.sa_handler = SIG_DFL;
205 	(void)sigaction(signo, &nsa, &osa);
206 
207 	(void)sigemptyset(&nset);
208 	(void)sigaddset(&nset, signo);
209 	(void)sigprocmask(SIG_UNBLOCK, &nset, &oset);
210 
211 	(void)kill(0, signo);
212 
213 	(void)sigprocmask(SIG_SETMASK, &oset, NULL);
214 	(void)sigaction(signo, &osa, NULL);
215 	errno = save_errno;
216 }
217 
218 /*
219  * Our generic signal handler.
220  */
221 static void
sig_handler(int signo)222 sig_handler(int signo)
223 {
224 	sigset_t nset;
225 	sigset_t oset;
226 
227 	(void)sigfillset(&nset);
228 	(void)sigprocmask(SIG_SETMASK, &nset, &oset);
229 
230 	assert (signo > 0 && signo < NSIG);	/* Should be guaranteed. */
231 
232 	sig_post(signo);
233 
234 	switch (signo) {
235 	case SIGCONT:
236 		assert(/*CONSTCOND*/ 0);	/* We should not be seeing these. */
237 		do_default_handler(signo, 0);
238 		break;
239 
240 	case SIGTSTP:
241 	case SIGTTIN:
242 	case SIGTTOU:
243 		do_default_handler(signo, 0);
244 		break;
245 
246 	case SIGINT:
247 	case SIGHUP:
248 	case SIGQUIT:
249 	case SIGPIPE:
250 	default:
251 		if (sigarray[signo] == SIG_DFL)
252 			do_default_handler(signo, SA_RESTART);
253 		break;
254 	}
255 	(void)sigprocmask(SIG_SETMASK, &oset, NULL);
256 }
257 
258 /*
259  * Setup the signal handlers.
260  */
261 PUBLIC void
sig_setup(void)262 sig_setup(void)
263 {
264 	sigset_t nset;
265 	sigset_t oset;
266 	struct sigaction sa;
267 	struct sigaction osa;
268 
269 	/* Block all signals while setting things. */
270 	(void)sigfillset(&nset);
271 	(void)sigprocmask(SIG_BLOCK, &nset, &oset);
272 
273 	/*
274 	 * Flow Control - SIGTSTP, SIGTTIN, SIGTTOU, SIGCONT:
275 	 *
276 	 * We grab SIGTSTP, SIGTTIN, and SIGTTOU so that we post the
277 	 * signals before suspending so that they are available when
278 	 * we resume.  If we were to use SIGCONT instead, they will
279 	 * not get posted until SIGCONT is unblocked, even though the
280 	 * process has resumed.
281 	 *
282 	 * NOTE: We default these to SA_RESTART here, but we need to
283 	 * change this in certain cases, e.g., when reading from a
284 	 * tty.
285 	 */
286 	(void)sigemptyset(&sa.sa_mask);
287 	sa.sa_flags = SA_RESTART;
288 	sa.sa_handler = sig_handler;
289 	(void)sigaction(SIGTSTP, &sa, NULL);
290 	(void)sigaction(SIGTTIN, &sa, NULL);
291 	(void)sigaction(SIGTTOU, &sa, NULL);
292 
293 	/*
294 	 * SIGHUP, SIGINT, and SIGQUIT:
295 	 *
296 	 * SIGHUP and SIGINT are trapped unless they are being
297 	 * ignored.
298 	 *
299 	 * Currently, we let the default handler deal with SIGQUIT.
300 	 */
301 	(void)sigemptyset(&sa.sa_mask);
302 	sa.sa_flags = 0;
303 	sa.sa_handler = sig_handler;
304 
305 	if (sigaction(SIGHUP, &sa, &osa) != -1 && osa.sa_handler == SIG_IGN)
306 		(void)signal(SIGHUP, SIG_IGN);
307 
308 	if (sigaction(SIGINT, &sa, &osa) != -1 && osa.sa_handler == SIG_IGN)
309 		(void)signal(SIGINT, SIG_IGN);
310 #if 0
311 	if (signal(SIGQUIT, SIG_DFL) == SIG_IGN)
312 		(void)signal(SIGQUIT, SIG_IGN);
313 #endif
314 	/*
315 	 * SIGCHLD and SIGPIPE:
316 	 *
317 	 * SIGCHLD is setup early in main.  The handler lives in
318 	 * popen.c as it uses internals of that module.
319 	 *
320 	 * SIGPIPE is grabbed here.  It is only used in
321 	 * lex.c:setup_piping(), cmd1.c:type1(), and cmd1.c:pipecmd().
322 	 */
323 	(void)sigemptyset(&sa.sa_mask);
324 	sa.sa_flags = 0;
325 	sa.sa_handler = sig_handler;
326 	(void)sigaction(SIGPIPE, &sa, NULL);
327 
328 	/*
329 	 * Make sure our structures are initialized.
330 	 * XXX: This should be unnecessary.
331 	 */
332 	(void)memset(sigarray, 0, sizeof(sigarray));
333 	SIGQUEUE_INIT(&sigq);
334 
335 	/* Restore the signal mask. */
336 	(void)sigprocmask(SIG_SETMASK, &oset, NULL);
337 }
338 
339 static struct {		/* data shared by sig_hold() and sig_release() */
340 	int depth;	/* depth of sig_hold() */
341 	sigset_t oset;	/* old signal mask saved by sig_hold() */
342 } hold;
343 
344 /*
345  * Hold signals SIGHUP, SIGINT, and SIGQUIT.
346  */
347 PUBLIC void
sig_hold(void)348 sig_hold(void)
349 {
350 	sigset_t nset;
351 
352 	if (hold.depth++ == 0) {
353 		(void)sigemptyset(&nset);
354 		(void)sigaddset(&nset, SIGHUP);
355 		(void)sigaddset(&nset, SIGINT);
356 		(void)sigaddset(&nset, SIGQUIT);
357 		(void)sigprocmask(SIG_BLOCK, &nset, &hold.oset);
358 	}
359 }
360 
361 /*
362  * Release signals SIGHUP, SIGINT, and SIGQUIT.
363  */
364 PUBLIC void
sig_release(void)365 sig_release(void)
366 {
367 
368 	if (--hold.depth == 0)
369 		(void)sigprocmask(SIG_SETMASK, &hold.oset, NULL);
370 }
371 
372 /*
373  * Unblock and ignore a signal.
374  */
375 PUBLIC int
sig_ignore(int sig,struct sigaction * osa,sigset_t * oset)376 sig_ignore(int sig, struct sigaction *osa, sigset_t *oset)
377 {
378 	struct sigaction act;
379 	sigset_t nset;
380 	int error;
381 
382 	(void)sigemptyset(&act.sa_mask);
383 	act.sa_flags = SA_RESTART;
384 	act.sa_handler = SIG_IGN;
385 	error = sigaction(sig, &act, osa);
386 
387 	if (error != -1) {
388 		(void)sigemptyset(&nset);
389 		(void)sigaddset(&nset, sig);
390 		(void)sigprocmask(SIG_UNBLOCK, &nset, oset);
391 	} else if (oset != NULL)
392 		(void)sigprocmask(SIG_UNBLOCK, NULL, oset);
393 
394 	return error;
395 }
396 
397 /*
398  * Restore a signal and the current signal mask.
399  */
400 PUBLIC int
sig_restore(int sig,struct sigaction * osa,sigset_t * oset)401 sig_restore(int sig, struct sigaction *osa, sigset_t *oset)
402 {
403 	int error;
404 
405 	error = 0;
406 	if (oset)
407 		error = sigprocmask(SIG_SETMASK, oset, NULL);
408 	if (osa)
409 		error = sigaction(sig, osa, NULL);
410 
411 	return error;
412 }
413 
414 /*
415  * Change the current flags and (optionally) return the old sigaction
416  * structure so we can restore things later.  This is used to turn
417  * SA_RESTART on or off.
418  */
419 PUBLIC int
sig_setflags(int signo,int flags,struct sigaction * osa)420 sig_setflags(int signo, int flags, struct sigaction *osa)
421 {
422 	struct sigaction sa;
423 
424 	if (sigaction(signo, NULL, &sa) == -1)
425 		return -1;
426 	if (osa)
427 		*osa = sa;
428 	sa.sa_flags = flags;
429 	return sigaction(signo, &sa, NULL);
430 }
431 
432