1 /* signal.c -- signal handling ($Revision: 1.1.1.1 $) */
2 
3 #include "es.h"
4 #include "sigmsgs.h"
5 
6 typedef Sigresult (*Sighandler)(int);
7 
8 Boolean sigint_newline = TRUE;
9 
10 jmp_buf slowlabel;
11 Atomic slow = FALSE;
12 Atomic interrupted = FALSE;
13 static Atomic sigcount;
14 static Atomic caught[NSIG];
15 static Sigeffect sigeffect[NSIG];
16 
17 #if HAVE_SIGACTION
18 #ifndef	SA_NOCLDSTOP
19 #define	SA_NOCLDSTOP	0
20 #endif
21 #ifndef	SA_NOCLDWAIT
22 #define	SA_NOCLDWAIT	0
23 #endif
24 #ifndef	SA_INTERRUPT		/* for sunos */
25 #define	SA_INTERRUPT	0
26 #endif
27 #endif
28 
29 
30 /*
31  * name<->signal mappings
32  */
33 
signumber(const char * name)34 extern int signumber(const char *name) {
35 	int i;
36 	char *suffix;
37 	if (!hasprefix(name, "sig"))
38 		return -1;
39 	for (i = 0; i < nsignals; i++)
40 		if (streq(signals[i].name, name))
41 			return signals[i].sig;
42 	i = strtol(name + 3, &suffix, 10);
43 	if (0 < i && i < NSIG && (suffix == NULL || *suffix == '\0'))
44 		return i;
45 	return -1;
46 }
47 
signame(int sig)48 extern char *signame(int sig) {
49 	int i;
50 	for (i = 0; i < nsignals; i++)
51 		if (signals[i].sig == sig)
52 			return (char *) signals[i].name;
53 	return str("sig%d", sig);
54 }
55 
sigmessage(int sig)56 extern char *sigmessage(int sig) {
57 	int i;
58 	for (i = 0; i < nsignals; i++)
59 		if (signals[i].sig == sig)
60 			return (char *) signals[i].msg;
61 	return str("unknown signal %d", sig);
62 }
63 
64 
65 /*
66  * the signal handler
67  */
68 
69 /* catcher -- catch (and defer) a signal from the kernel */
catcher(int sig)70 static void catcher(int sig) {
71 #if SYSV_SIGNALS /* only do this for unreliable signals */
72 	signal(sig, catcher);
73 #endif
74 	if (hasforked)
75 		/* exit unconditionally on a signal in a child process */
76 		exit(1);
77 	if (caught[sig] == 0) {
78 		caught[sig] = TRUE;
79 		++sigcount;
80 	}
81 	interrupted = TRUE;
82 	if (slow)
83 		longjmp(slowlabel, 1);
84 }
85 
86 
87 /*
88  * setting and getting signal effects
89  */
90 
setsignal(int sig,Sighandler handler)91 static Sighandler setsignal(int sig, Sighandler handler) {
92 #if HAVE_SIGACTION
93 	struct sigaction nsa, osa;
94 	sigemptyset(&nsa.sa_mask);
95 	nsa.sa_handler = handler;
96 	nsa.sa_flags = SA_INTERRUPT;
97 	if (sigaction(sig, &nsa, &osa) == -1)
98 		return SIG_ERR;
99 	return osa.sa_handler;
100 #else /* !HAVE_SIGACTION */
101 #ifdef SIGCLD
102 	if (sig == SIGCLD && handler != SIG_DFL)
103 		return SIG_ERR;
104 #endif
105 	return signal(sig, handler);
106 #endif /* !HAVE_SIGACTION */
107 }
108 
esignal(int sig,Sigeffect effect)109 extern Sigeffect esignal(int sig, Sigeffect effect) {
110 	Sigeffect old;
111 	assert(0 < sig && sig <= NSIG);
112 	old = sigeffect[sig];
113 	if (effect != sig_nochange && effect != old) {
114 		switch (effect) {
115 		case sig_ignore:
116 			if (setsignal(sig, SIG_IGN) == SIG_ERR) {
117 				eprint("$&setsignals: cannot ignore %s\n", signame(sig));
118 				return old;
119 			}
120 			break;
121 		case sig_special:
122 			if (sig != SIGINT) {
123 				eprint("$&setsignals: special handler not defined for %s\n", signame(sig));
124 				return old;
125 			}
126 		case sig_catch:
127 		case sig_noop:
128 			if (setsignal(sig, catcher) == SIG_ERR) {
129 				eprint("$&setsignals: cannot catch %s\n", signame(sig));
130 				return old;
131 			}
132 			break;
133 		case sig_default:
134 			setsignal(sig, SIG_DFL);
135 			break;
136 		default:
137 			NOTREACHED;
138 		}
139 		sigeffect[sig] = effect;
140 	}
141 	return old;
142 }
143 
setsigeffects(const Sigeffect effects[])144 extern void setsigeffects(const Sigeffect effects[]) {
145 	int sig;
146 	for (sig = 1; sig < NSIG; sig++)
147 		esignal(sig, effects[sig]);
148 }
149 
getsigeffects(Sigeffect effects[])150 extern void getsigeffects(Sigeffect effects[]) {
151 	memcpy(effects, sigeffect, sizeof sigeffect);
152 }
153 
154 
155 /*
156  * initialization
157  */
158 
initsignals(Boolean interactive,Boolean allowdumps)159 extern void initsignals(Boolean interactive, Boolean allowdumps) {
160 	int sig;
161 	Push settor;
162 
163 	for (sig = 0; sig < nsignals; sig++)
164 		if (signals[sig].sig < 1 || NSIG <= signals[sig].sig)
165 			panic(
166 				"initsignals: bad signal in sigmsgs.c: %s (see mksignal)",
167 				signals[sig].name
168 			);
169 
170 	for (sig = 1; sig < NSIG; sig++) {
171 		Sighandler h;
172 #if HAVE_SIGACTION
173 		struct sigaction sa;
174 		sigaction(sig, NULL, &sa);
175 		h = sa.sa_handler;
176 		if (h == SIG_IGN)
177 			sigeffect[sig] = sig_ignore;
178 #else /* !HAVE_SIGACTION */
179 		h = signal(sig, SIG_DFL);
180 		if (h == SIG_IGN) {
181 			setsignal(sig, SIG_IGN);
182 			sigeffect[sig] = sig_ignore;
183 		}
184 #endif /* !HAVE_SIGACTION */
185 		else if (h == SIG_DFL || h == SIG_ERR)
186 			sigeffect[sig] = sig_default;
187 		else
188 			panic(
189 				"initsignals: bad incoming signal value for %s: %x",
190 				signame(sig), h
191 			);
192 	}
193 
194 	if (interactive || sigeffect[SIGINT] == sig_default)
195 		esignal(SIGINT, sig_special);
196 	if (!allowdumps) {
197 		if (interactive)
198 			esignal(SIGTERM, sig_noop);
199 		if (interactive || sigeffect[SIGQUIT] == sig_default)
200 			esignal(SIGQUIT, sig_noop);
201 	}
202 
203 	/* here's the end-run around set-signals */
204 	varpush(&settor, "set-signals", NULL);
205 	vardef("signals", NULL, mksiglist());
206 	varpop(&settor);
207 }
208 
setsigdefaults(void)209 extern void setsigdefaults(void) {
210 	int sig;
211 	for (sig = 1; sig < NSIG; sig++) {
212 		Sigeffect e = sigeffect[sig];
213 		if (e == sig_catch || e == sig_noop || e == sig_special)
214 			esignal(sig, sig_default);
215 	}
216 }
217 
218 
219 /*
220  * utility functions
221  */
222 
issilentsignal(List * e)223 extern Boolean issilentsignal(List *e) {
224 	return (termeq(e->term, "signal"))
225 		&& e->next != NULL
226 		&& termeq(e->next->term, "sigint");
227 }
228 
mksiglist(void)229 extern List *mksiglist(void) {
230 	int sig = NSIG;
231 	Sigeffect effects[NSIG];
232 	getsigeffects(effects);
233 	Ref(List *, lp, NULL);
234 	while (--sig > 0) {
235 		int prefix;
236 		switch (effects[sig]) {
237 		default: panic("mksiglist: bad sigeffects for %s: %d", signame(sig), effects[sig]);
238 		case sig_default:	continue;
239 		case sig_catch:		prefix = '\0';	break;
240 		case sig_ignore:	prefix = '-';	break;
241 		case sig_noop:		prefix = '/';	break;
242 		case sig_special:	prefix = '.';	break;
243 		}
244 		Ref(char *, name, signame(sig));
245 		if (prefix != '\0')
246 			name = str("%c%s", prefix, name);
247 		Ref(Term *, term, mkstr(name));
248 		lp = mklist(term, lp);
249 		RefEnd2(term, name);
250 	}
251 	RefReturn(lp);
252 }
253 
254 
255 /*
256  * signal delivery
257  */
258 
259 static int blocked = 0;
260 
261 /* blocksignals -- turn off delivery of signals as exceptions */
blocksignals(void)262 extern void blocksignals(void) {
263 	assert(blocked >= 0);
264 	++blocked;
265 }
266 
267 /* unblocksignals -- turn on delivery of signals as exceptions */
unblocksignals(void)268 extern void unblocksignals(void) {
269 	assert(blocked > 0);
270 	--blocked;
271 }
272 
273 /* sigchk -- throw the signal as an exception */
sigchk(void)274 extern void sigchk(void) {
275 	int sig;
276 
277 	if (sigcount == 0 || blocked)
278 		return;
279 	if (hasforked)
280 		/* exit unconditionally on a signal in a child process */
281 		exit(1);
282 
283 	for (sig = 0;; sig++) {
284 		if (caught[sig] != 0) {
285 			--sigcount;
286 			caught[sig] = 0;
287 			break;
288 		}
289 		if (sig >= NSIG) {
290 			sigcount = 0;
291 			return;
292 		}
293 	}
294 	resetparser();
295 	Ref(List *, e,
296 	    mklist(mkstr("signal"), mklist(mkstr(signame(sig)), NULL)));
297 
298 	switch (sigeffect[sig]) {
299 	case sig_catch:
300 		while (gcisblocked())
301 			gcenable();
302 		throw(e);
303 		NOTREACHED;
304 	case sig_special:
305 		assert(sig == SIGINT);
306 		/* this is the newline you see when you hit ^C while typing a command */
307 		if (sigint_newline)
308 			eprint("\n");
309 		sigint_newline = TRUE;
310 		while (gcisblocked())
311 			gcenable();
312 		throw(e);
313 		NOTREACHED;
314 		break;
315 	case sig_noop:
316 		break;
317 	default:
318 		/* panic("sigchk: caught %L with sigeffect %d", e, " ", sigeffect[sig]); */
319 		break;
320 	}
321 	RefEnd(e);
322 }
323