Commit | Line | Data |
---|---|---|
cc410b67 KB |
1 | /*- |
2 | * Copyright (c) 1991 The Regents of the University of California. | |
3 | * All rights reserved. | |
4 | * | |
5 | * This code is derived from software contributed to Berkeley by | |
6 | * Kenneth Almquist. | |
7 | * | |
8 | * %sccs.include.redist.c% | |
9 | */ | |
10 | ||
11 | #ifndef lint | |
12 | static char sccsid[] = "@(#)trap.c 5.1 (Berkeley) %G%"; | |
13 | #endif /* not lint */ | |
14 | ||
15 | /* | |
16 | * Routines for dealing with signals. | |
17 | */ | |
18 | ||
19 | #include "shell.h" | |
20 | #include "main.h" | |
21 | #include "nodes.h" /* for other headers */ | |
22 | #include "eval.h" | |
23 | #include "jobs.h" | |
24 | #include "options.h" | |
25 | #include "syntax.h" | |
26 | #include "signames.h" | |
27 | #include "output.h" | |
28 | #include "memalloc.h" | |
29 | #include "error.h" | |
30 | #include "trap.h" | |
31 | #include "mystring.h" | |
32 | #include <signal.h> | |
33 | ||
34 | ||
35 | /* | |
36 | * Sigmode records the current value of the signal handlers for the various | |
37 | * modes. A value of zero means that the current handler is not known. | |
38 | * S_HARD_IGN indicates that the signal was ignored on entry to the shell, | |
39 | */ | |
40 | ||
41 | #define S_DFL 1 /* default signal handling (SIG_DFL) */ | |
42 | #define S_CATCH 2 /* signal is caught */ | |
43 | #define S_IGN 3 /* signal is ignored (SIG_IGN) */ | |
44 | #define S_HARD_IGN 4 /* signal is ignored permenantly */ | |
45 | ||
46 | ||
47 | extern char nullstr[1]; /* null string */ | |
48 | ||
49 | char *trap[MAXSIG+1]; /* trap handler commands */ | |
50 | MKINIT char sigmode[MAXSIG]; /* current value of signal */ | |
51 | char gotsig[MAXSIG]; /* indicates specified signal received */ | |
52 | int pendingsigs; /* indicates some signal received */ | |
53 | ||
54 | ||
55 | /* | |
56 | * The trap builtin. | |
57 | */ | |
58 | ||
59 | trapcmd(argc, argv) char **argv; { | |
60 | char *action; | |
61 | char **ap; | |
62 | int signo; | |
63 | ||
64 | if (argc <= 1) { | |
65 | for (signo = 0 ; signo <= MAXSIG ; signo++) { | |
66 | if (trap[signo] != NULL) | |
67 | out1fmt("%d: %s\n", signo, trap[signo]); | |
68 | } | |
69 | return 0; | |
70 | } | |
71 | ap = argv + 1; | |
72 | if (is_number(*ap)) | |
73 | action = NULL; | |
74 | else | |
75 | action = *ap++; | |
76 | while (*ap) { | |
77 | if ((signo = number(*ap)) < 0 || signo > MAXSIG) | |
78 | error("%s: bad trap", *ap); | |
79 | INTOFF; | |
80 | if (action) | |
81 | action = savestr(action); | |
82 | if (trap[signo]) | |
83 | ckfree(trap[signo]); | |
84 | trap[signo] = action; | |
85 | if (signo != 0) | |
86 | setsignal(signo); | |
87 | INTON; | |
88 | ap++; | |
89 | } | |
90 | return 0; | |
91 | } | |
92 | ||
93 | ||
94 | ||
95 | /* | |
96 | * Clear traps on a fork. | |
97 | */ | |
98 | ||
99 | void | |
100 | clear_traps() { | |
101 | char **tp; | |
102 | ||
103 | for (tp = trap ; tp <= &trap[MAXSIG] ; tp++) { | |
104 | if (*tp && **tp) { /* trap not NULL or SIG_IGN */ | |
105 | INTOFF; | |
106 | ckfree(*tp); | |
107 | *tp = NULL; | |
108 | if (tp != &trap[0]) | |
109 | setsignal(tp - trap); | |
110 | INTON; | |
111 | } | |
112 | } | |
113 | } | |
114 | ||
115 | ||
116 | ||
117 | /* | |
118 | * Set the signal handler for the specified signal. The routine figures | |
119 | * out what it should be set to. | |
120 | */ | |
121 | ||
122 | int | |
123 | setsignal(signo) { | |
124 | int action; | |
125 | sig_t sigact; | |
126 | char *t; | |
127 | extern void onsig(); | |
128 | ||
129 | if ((t = trap[signo]) == NULL) | |
130 | action = S_DFL; | |
131 | else if (*t != '\0') | |
132 | action = S_CATCH; | |
133 | else | |
134 | action = S_IGN; | |
135 | if (rootshell && action == S_DFL) { | |
136 | switch (signo) { | |
137 | case SIGINT: | |
138 | if (iflag) | |
139 | action = S_CATCH; | |
140 | break; | |
141 | #ifndef DEBUG | |
142 | case SIGQUIT: | |
143 | #endif | |
144 | case SIGTERM: | |
145 | if (iflag) | |
146 | action = S_IGN; | |
147 | break; | |
148 | #if JOBS | |
149 | case SIGTSTP: | |
150 | case SIGTTOU: | |
151 | if (jflag) | |
152 | action = S_IGN; | |
153 | break; | |
154 | #endif | |
155 | } | |
156 | } | |
157 | t = &sigmode[signo - 1]; | |
158 | if (*t == 0) { /* current setting unknown */ | |
159 | /* | |
160 | * There is a race condition here if action is not S_IGN. | |
161 | * A signal can be ignored that shouldn't be. | |
162 | */ | |
163 | if ((int)(sigact = signal(signo, SIG_IGN)) == -1) | |
164 | error("Signal system call failed"); | |
165 | if (sigact == SIG_IGN) { | |
166 | *t = S_HARD_IGN; | |
167 | } else { | |
168 | *t = S_IGN; | |
169 | } | |
170 | } | |
171 | if (*t == S_HARD_IGN || *t == action) | |
172 | return 0; | |
173 | switch (action) { | |
174 | case S_DFL: sigact = SIG_DFL; break; | |
175 | case S_CATCH: sigact = onsig; break; | |
176 | case S_IGN: sigact = SIG_IGN; break; | |
177 | } | |
178 | *t = action; | |
179 | return (int)signal(signo, sigact); | |
180 | } | |
181 | ||
182 | ||
183 | ||
184 | /* | |
185 | * Ignore a signal. | |
186 | */ | |
187 | ||
188 | void | |
189 | ignoresig(signo) { | |
190 | if (sigmode[signo - 1] != S_IGN && sigmode[signo - 1] != S_HARD_IGN) { | |
191 | signal(signo, SIG_IGN); | |
192 | } | |
193 | sigmode[signo - 1] = S_HARD_IGN; | |
194 | } | |
195 | ||
196 | ||
197 | #ifdef mkinit | |
198 | INCLUDE "signames.h" | |
199 | INCLUDE "trap.h" | |
200 | ||
201 | SHELLPROC { | |
202 | char *sm; | |
203 | ||
204 | clear_traps(); | |
205 | for (sm = sigmode ; sm < sigmode + MAXSIG ; sm++) { | |
206 | if (*sm == S_IGN) | |
207 | *sm = S_HARD_IGN; | |
208 | } | |
209 | } | |
210 | #endif | |
211 | ||
212 | ||
213 | ||
214 | /* | |
215 | * Signal handler. | |
216 | */ | |
217 | ||
218 | void | |
219 | onsig(signo) { | |
220 | signal(signo, onsig); | |
221 | if (signo == SIGINT && trap[SIGINT] == NULL) { | |
222 | onint(); | |
223 | return; | |
224 | } | |
225 | gotsig[signo - 1] = 1; | |
226 | pendingsigs++; | |
227 | } | |
228 | ||
229 | ||
230 | ||
231 | /* | |
232 | * Called to execute a trap. Perhaps we should avoid entering new trap | |
233 | * handlers while we are executing a trap handler. | |
234 | */ | |
235 | ||
236 | void | |
237 | dotrap() { | |
238 | int i; | |
239 | ||
240 | for (;;) { | |
241 | for (i = 1 ; ; i++) { | |
242 | if (gotsig[i - 1]) | |
243 | break; | |
244 | if (i >= MAXSIG) | |
245 | goto done; | |
246 | } | |
247 | gotsig[i - 1] = 0; | |
248 | evalstring(trap[i]); | |
249 | } | |
250 | done: | |
251 | pendingsigs = 0; | |
252 | } | |
253 | ||
254 | ||
255 | ||
256 | /* | |
257 | * Controls whether the shell is interactive or not. | |
258 | */ | |
259 | ||
260 | int is_interactive; | |
261 | ||
262 | void | |
263 | setinteractive(on) { | |
264 | if (on == is_interactive) | |
265 | return; | |
266 | setsignal(SIGINT); | |
267 | setsignal(SIGQUIT); | |
268 | setsignal(SIGTERM); | |
269 | is_interactive = on; | |
270 | } | |
271 | ||
272 | ||
273 | ||
274 | /* | |
275 | * Called to exit the shell. | |
276 | */ | |
277 | ||
278 | void | |
279 | exitshell(status) { | |
280 | struct jmploc loc1, loc2; | |
281 | char *p; | |
282 | ||
283 | TRACE(("exitshell(%d) pid=%d\n", status, getpid())); | |
284 | if (setjmp(loc1.loc)) goto l1; | |
285 | if (setjmp(loc2.loc)) goto l2; | |
286 | handler = &loc1; | |
287 | if ((p = trap[0]) != NULL && *p != '\0') { | |
288 | trap[0] = NULL; | |
289 | evalstring(p); | |
290 | } | |
291 | l1: handler = &loc2; /* probably unnecessary */ | |
292 | flushall(); | |
293 | #if JOBS | |
294 | setjobctl(0); | |
295 | #endif | |
296 | l2: _exit(status); | |
297 | } |