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