xref: /illumos-gate/usr/src/cmd/mailx/sigretro.c (revision 2a8bcb4e)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
23 /*	  All Rights Reserved  	*/
24 
25 
26 /*
27  * University Copyright- Copyright (c) 1982, 1986, 1988
28  * The Regents of the University of California
29  * All Rights Reserved
30  *
31  * University Acknowledgment- Portions of this document are derived from
32  * software developed by the University of California, Berkeley, and its
33  * contributors.
34  */
35 
36 /*
37  * mailx -- a modified version of a University of California at Berkeley
38  *	mail program
39  */
40 
41 /*
42  * This code is only compiled in if SIG_HOLD is not defined!
43  *
44  * Retrofit new signal interface to old signal primitives.
45  * Supported routines:
46  *	sigsys(sig, func)
47  *	sigset(sig, func)
48  *	sighold(sig)
49  *	sigrelse(sig)
50  *	sigignore(sig)
51  *	sigpause(sig)
52  * Also,
53  *	sigchild()
54  *		to set all held signals to ignored signals in the
55  *		child process after fork(2)
56  */
57 #include <signal.h>
58 
59 #ifndef SIG_HOLD
60 # include <errno.h>
61 # include <setjmp.h>
62 # include <stdio.h>
63 
64 extern int errno;
65 
66 typedef void	(*sigtype)();
67 
68 #define SIG_HOLD	((sigtype) 2)
69 #ifndef SIG_ERR
70 # define SIG_ERR	((sigtype) -1)
71 #endif
72 
73 sigtype	sigdisp(), sighold(), sigignore();
74 void	_Sigtramp();
75 
76 /*
77  * The following helps us keep the extended signal semantics together.
78  * We remember for each signal the address of the function we're
79  * supposed to call.  s_func is SIG_DFL / SIG_IGN if appropriate.
80  */
81 static struct sigtable {
82 	sigtype	s_func;			/* What to call */
83 	int	s_flag;			/* Signal flags; see below */
84 } sigtable[NSIG + 1];
85 
86 /*
87  * Signal flag values.
88  */
89 #define	SHELD		1		/* Signal is being held */
90 #define	SDEFER		2		/* Signal occured while held */
91 #define	SSET		4		/* s_func is believable */
92 #define	SPAUSE		8		/* are pausing, waiting for sig */
93 
94 jmp_buf	_pause;				/* For doing sigpause() */
95 
96 /*
97  * Approximate sigsys() system call
98  * This is almost useless since one only calls sigsys()
99  * in the child of a vfork().  If you have vfork(), you have new signals
100  * anyway.  The real sigsys() does all the stuff needed to support
101  * the real sigset() library.  We don't bother here, assuming that
102  * you are either ignoring or defaulting a signal in the child.
103  */
104 sigtype
sigsys(int sig,sigtype func)105 sigsys(int sig, sigtype func)
106 {
107 	sigtype old;
108 
109 	old = sigdisp(sig);
110 	signal(sig, func);
111 	return(old);
112 }
113 
114 
115 /*
116  * Set the (permanent) disposition of a signal.
117  * If the signal is subsequently (or even now) held,
118  * the action you set here can be enabled using sigrelse().
119  */
120 sigtype
sigset(int sig,sigtype func)121 sigset(int sig, sigtype func)
122 {
123 	sigtype old;
124 
125 	if (sig < 1 || sig > NSIG) {
126 		errno = EINVAL;
127 		return(SIG_ERR);
128 	}
129 	old = sigdisp(sig);
130 	/*
131 	 * Does anyone actually call sigset with SIG_HOLD!?
132 	 */
133 	if (func == SIG_HOLD) {
134 		sighold(sig);
135 		return(old);
136 	}
137 	sigtable[sig].s_flag |= SSET;
138 	sigtable[sig].s_func = func;
139 	if (func == SIG_DFL) {
140 		/*
141 		 * If signal has been held, must retain
142 		 * the catch so that we can note occurrance
143 		 * of signal.
144 		 */
145 		if ((sigtable[sig].s_flag & SHELD) == 0)
146 			signal(sig, SIG_DFL);
147 		else
148 			signal(sig, _Sigtramp);
149 		return(old);
150 	}
151 	if (func == SIG_IGN) {
152 		/*
153 		 * Clear pending signal
154 		 */
155 		signal(sig, SIG_IGN);
156 		sigtable[sig].s_flag &= ~SDEFER;
157 		return(old);
158 	}
159 	signal(sig, _Sigtramp);
160 	return(old);
161 }
162 
163 /*
164  * Hold a signal.
165  * This CAN be tricky if the signal's disposition is SIG_DFL.
166  * In that case, we still catch the signal so we can note it
167  */
168 sigtype
sighold(int sig)169 sighold(int sig)
170 {
171 	sigtype old;
172 
173 	if (sig < 1 || sig > NSIG) {
174 		errno = EINVAL;
175 		return(SIG_ERR);
176 	}
177 	old = sigdisp(sig);
178 	if (sigtable[sig].s_flag & SHELD)
179 		return(old);
180 	/*
181 	 * When the default action is required, we have to
182 	 * set up to catch the signal to note signal's occurrance.
183 	 */
184 	if (old == SIG_DFL) {
185 		sigtable[sig].s_flag |= SSET;
186 		signal(sig, _Sigtramp);
187 	}
188 	sigtable[sig].s_flag |= SHELD;
189 	return(old);
190 }
191 
192 /*
193  * Release a signal
194  * If the signal occurred while we had it held, cause the signal.
195  */
196 sigtype
sigrelse(int sig)197 sigrelse(int sig)
198 {
199 	sigtype old;
200 
201 	if (sig < 1 || sig > NSIG) {
202 		errno = EINVAL;
203 		return(SIG_ERR);
204 	}
205 	old = sigdisp(sig);
206 	if ((sigtable[sig].s_flag & SHELD) == 0)
207 		return(old);
208 	sigtable[sig].s_flag &= ~SHELD;
209 	if (sigtable[sig].s_flag & SDEFER)
210 		_Sigtramp(sig);
211 	/*
212 	 * If disposition was the default, then we can unset the
213 	 * catch to _Sigtramp() and let the system do the work.
214 	 */
215 	if (sigtable[sig].s_func == SIG_DFL)
216 		signal(sig, SIG_DFL);
217 	return(old);
218 }
219 
220 /*
221  * Ignore a signal.
222  */
223 sigtype
sigignore(int sig)224 sigignore(int sig)
225 {
226 	return(sigset(sig, SIG_IGN));
227 }
228 
229 /*
230  * Pause, waiting for sig to occur.
231  * We assume LUSER called us with the signal held.
232  * When we got the signal, mark the signal as having
233  * occurred.  It will actually cause something when
234  * the signal is released.
235  */
236 int
sigpause(int sig)237 sigpause(int sig)
238 {
239 	if (sig < 1 || sig > NSIG) {
240 		errno = EINVAL;
241 		return;
242 	}
243 	sigtable[sig].s_flag |= SHELD|SPAUSE;
244 	if (setjmp(_pause) == 0)
245 		pause();
246 	sigtable[sig].s_flag &= ~SPAUSE;
247 	sigtable[sig].s_flag |= SDEFER;
248 }
249 
250 /*
251  * In the child process after fork(2), set the disposition of all held
252  * signals to SIG_IGN.  This is a new procedure not in the real sigset()
253  * package, provided for retrofitting purposes.
254  */
255 int
sigchild(void)256 sigchild(void)
257 {
258 	register int i;
259 
260 	for (i = 1; i <= NSIG; i++)
261 		if (sigtable[i].s_flag & SHELD)
262 			signal(i, SIG_IGN);
263 }
264 
265 
266 /*
267  * Return the current disposition of a signal
268  * If we have not set this signal before, we have to
269  * ask the system
270  */
271 sigtype
sigdisp(int sig)272 sigdisp(int sig)
273 {
274 	sigtype old;
275 
276 	if (sig < 1 || sig > NSIG) {
277 		errno = EINVAL;
278 		return(SIG_ERR);
279 	}
280 	/*
281 	 * If we have no knowledge of this signal,
282 	 * ask the system, then save the result for later.
283 	 */
284 	if ((sigtable[sig].s_flag & SSET) == 0) {
285 		old = signal(sig, SIG_IGN);
286 		sigtable[sig].s_func = old;
287 		sigtable[sig].s_flag |= SSET;
288 		signal(sig, old);
289 		return(old);
290 	}
291 	/*
292 	 * If we have set this signal before, then sigset()
293 	 * will have been careful to leave something meaningful
294 	 * in s_func.
295 	 */
296 	return(sigtable[sig].s_func);
297 }
298 
299 /*
300  * The following routine gets called for any signal
301  * that is to be trapped to a user function.
302  */
303 void
_Sigtramp(int sig)304 _Sigtramp(int sig)
305 {
306 	sigtype old;
307 
308 	if (sig < 1 || sig > NSIG) {
309 		errno = EINVAL;
310 		return;
311 	}
312 
313 top:
314 	old = signal(sig, SIG_IGN);
315 	/*
316 	 * If signal being paused on, wakeup sigpause()
317 	 */
318 	if (sigtable[sig].s_flag & SPAUSE)
319 		longjmp(_pause, 1);
320 	/*
321 	 * If signal is being held, mark its table entry
322 	 * so we can trigger it when signal is released.
323 	 * Then just return.
324 	 */
325 	if (sigtable[sig].s_flag & SHELD) {
326 		sigtable[sig].s_flag |= SDEFER;
327 		signal(sig, _Sigtramp);
328 		return;
329 	}
330 	/*
331 	 * If the signal is being ignored, just return.
332 	 * This would make SIGCONT more normal, but of course
333 	 * any system with SIGCONT also has the new signal pkg, so...
334 	 */
335 	if (sigtable[sig].s_func == SIG_IGN)
336 		return;
337 	/*
338 	 * If the signal is SIG_DFL, then we probably got here
339 	 * by holding the signal, having it happen, then releasing
340 	 * the signal.
341 	 */
342 	if (sigtable[sig].s_func == SIG_DFL) {
343 		signal(sig, SIG_DFL);
344 		kill(getpid(), sig);
345 		/* Will we get back here? */
346 		return;
347 	}
348 	/*
349 	 * Looks like we should just cause the signal...
350 	 * We hold the signal for the duration of the user's
351 	 * code with the signal re-enabled.  If the signal
352 	 * happens again while in user code, we will recursively
353 	 * trap here and mark that we had another occurance
354 	 * and return to the user's trap code.  When we return
355 	 * from there, we can cause the signal again.
356 	 */
357 	sigtable[sig].s_flag &= ~SDEFER;
358 	sigtable[sig].s_flag |= SHELD;
359 	signal(sig, _Sigtramp);
360 	(*sigtable[sig].s_func)(sig);
361 	/*
362 	 * If the signal re-occurred while in the user's routine,
363 	 * just go try it again...
364 	 */
365 	sigtable[sig].s_flag &= ~SHELD;
366 	if (sigtable[sig].s_flag & SDEFER)
367 		goto top;
368 }
369 #endif
370