sig_tstp: Use _exit() rather than exit().
[elinks.git] / src / osdep / signals.c
blob72bc31cc65f776246a51f9e17d8d8098599c37b1
1 /* Signals handling. */
3 #ifdef HAVE_CONFIG_H
4 #include "config.h"
5 #endif
7 #include <errno.h>
8 #include <signal.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #ifdef HAVE_SYS_SIGNAL_H
12 #include <sys/signal.h>
13 #endif
14 #include <sys/types.h>
15 #ifdef HAVE_SYS_WAIT_H
16 #include <sys/wait.h>
17 #endif
18 #ifdef HAVE_UNISTD_H
19 #include <unistd.h>
20 #endif
22 #include "elinks.h"
24 #include "main/main.h"
25 #include "main/select.h"
26 #include "main/version.h"
27 #include "osdep/signals.h"
28 #include "terminal/kbd.h"
29 #include "terminal/terminal.h"
30 #include "util/error.h"
33 static void unhandle_basic_signals(struct terminal *term);
35 static void
36 sig_terminate(struct terminal *term)
38 unhandle_basic_signals(term);
39 program.terminate = 1;
40 program.retval = RET_SIGNAL;
43 #ifdef SIGHUP
44 static void
45 sig_intr(struct terminal *term)
47 unhandle_basic_signals(term);
49 if (!term)
50 program.terminate = 1;
51 else
52 register_bottom_half(destroy_terminal, term);
54 #endif
56 void
57 sig_ctrl_c(struct terminal *term)
59 if (!is_blocked()) kbd_ctrl_c();
62 #ifdef SIGTTOU
63 static void
64 sig_ign(void *x)
67 #endif
69 #if defined(SIGTSTP) || defined(SIGTTIN)
70 static void
71 sig_tstp(struct terminal *term)
73 #ifdef SIGSTOP
74 pid_t pid = getpid();
76 block_itrm(0);
77 #if defined (SIGCONT) && defined(SIGTTOU)
78 if (!fork()) {
79 sleep(1);
80 kill(pid, SIGCONT);
81 /* Use _exit() rather than exit(), so that atexit
82 * functions are not called, and stdio output buffers
83 * are not flushed. Any such things must have been
84 * inherited from the parent process, which will take
85 * care of them when appropriate. */
86 _exit(0);
88 #endif
89 raise(SIGSTOP);
90 #endif
92 #endif
94 #ifdef SIGCONT
95 static void
96 sig_cont(struct terminal *term)
98 if (!unblock_itrm(0)) resize_terminal();
100 #endif
102 #ifdef CONFIG_BACKTRACE
103 static void
104 sig_segv(struct terminal *term)
106 /* Get some attention. */
107 fputs("\a", stderr); fflush(stderr); sleep(1); fputs("\a\n", stderr);
109 /* Rant. */
110 fputs( "ELinks crashed. That shouldn't happen. Please report this incident to\n"
111 "the developers. If you would like to help to debug the problem you just\n"
112 "uncovered, please keep the core you just got and send the developers\n"
113 "the output of 'bt' command entered inside of gdb (which you run as:\n"
114 "gdb elinks core). Thanks a lot for your cooperation!\n\n", stderr);
116 /* version information */
117 fputs(full_static_version, stderr);
118 fputs("\n\n", stderr);
120 /* Backtrace. */
121 dump_backtrace(stderr, 1);
123 /* TODO: Perhaps offer launching of gdb? Or trying to continue w/
124 * program execution? --pasky */
126 /* The fastest way OUT! */
127 abort();
129 #endif
132 void
133 handle_basic_signals(struct terminal *term)
135 #ifdef SIGHUP
136 install_signal_handler(SIGHUP, (void (*)(void *)) sig_intr, term, 0);
137 #endif
138 install_signal_handler(SIGINT, (void (*)(void *)) sig_ctrl_c, term, 0);
139 install_signal_handler(SIGTERM, (void (*)(void *)) sig_terminate, term, 0);
140 #ifdef SIGTSTP
141 install_signal_handler(SIGTSTP, (void (*)(void *)) sig_tstp, term, 0);
142 #endif
143 #ifdef SIGTTIN
144 install_signal_handler(SIGTTIN, (void (*)(void *)) sig_tstp, term, 0);
145 #endif
146 #ifdef SIGTTOU
147 install_signal_handler(SIGTTOU, (void (*)(void *)) sig_ign, term, 0);
148 #endif
149 #ifdef SIGCONT
150 install_signal_handler(SIGCONT, (void (*)(void *)) sig_cont, term, 0);
151 #endif
152 #ifdef CONFIG_BACKTRACE
153 install_signal_handler(SIGSEGV, (void (*)(void *)) sig_segv, term, 1);
154 #endif
157 void
158 unhandle_terminal_signals(struct terminal *term)
160 #ifdef SIGHUP
161 install_signal_handler(SIGHUP, NULL, NULL, 0);
162 #endif
163 install_signal_handler(SIGINT, NULL, NULL, 0);
164 #ifdef SIGTSTP
165 install_signal_handler(SIGTSTP, NULL, NULL, 0);
166 #endif
167 #ifdef SIGTTIN
168 install_signal_handler(SIGTTIN, NULL, NULL, 0);
169 #endif
170 #ifdef SIGTTOU
171 install_signal_handler(SIGTTOU, NULL, NULL, 0);
172 #endif
173 #ifdef SIGCONT
174 install_signal_handler(SIGCONT, NULL, NULL, 0);
175 #endif
176 #ifdef CONFIG_BACKTRACE
177 install_signal_handler(SIGSEGV, NULL, NULL, 0);
178 #endif
181 static void
182 unhandle_basic_signals(struct terminal *term)
184 #ifdef SIGHUP
185 install_signal_handler(SIGHUP, NULL, NULL, 0);
186 #endif
187 install_signal_handler(SIGINT, NULL, NULL, 0);
188 install_signal_handler(SIGTERM, NULL, NULL, 0);
189 #ifdef SIGTSTP
190 install_signal_handler(SIGTSTP, NULL, NULL, 0);
191 #endif
192 #ifdef SIGTTIN
193 install_signal_handler(SIGTTIN, NULL, NULL, 0);
194 #endif
195 #ifdef SIGTTOU
196 install_signal_handler(SIGTTOU, NULL, NULL, 0);
197 #endif
198 #ifdef SIGCONT
199 install_signal_handler(SIGCONT, NULL, NULL, 0);
200 #endif
201 #ifdef CONFIG_BACKTRACE
202 install_signal_handler(SIGSEGV, NULL, NULL, 0);
203 #endif
206 struct signal_info {
207 void (*handler)(void *);
208 void *data;
209 int critical;
210 int mask;
213 static struct signal_info signal_info[NUM_SIGNALS];
214 volatile int critical_section = 0;
216 static void check_for_select_race(void);
218 /* TODO: In order to gain better portability, we should use signal() instead.
219 * Highest care should be given to careful watching of which signals are
220 * blocked and which aren't then, though. --pasky */
222 static void
223 got_signal(int sig)
225 struct signal_info *s;
226 int saved_errno = errno;
228 if (sig >= NUM_SIGNALS || sig < 0) {
229 /* Signal handler - we have no good way how to tell this the
230 * user. She won't care anyway, tho'. */
231 return;
234 s = &signal_info[sig];
236 if (!s->handler) return;
238 if (s->critical) {
239 s->handler(s->data);
240 errno = saved_errno;
241 return;
244 s->mask = 1;
245 check_for_select_race();
247 errno = saved_errno;
250 void
251 install_signal_handler(int sig, void (*fn)(void *), void *data, int critical)
253 #ifdef HAVE_SIGACTION
254 struct sigaction sa;
255 #else
256 void (*handler)(int) = fn ? got_signal : SIG_IGN;
257 #endif
259 /* Yes, assertm() in signal handler is totally unsafe and depends just
260 * on good luck. But hey, assert()ions are never triggered ;-). */
261 assertm(sig >= 0 && sig < NUM_SIGNALS, "bad signal number: %d", sig);
262 if_assert_failed return;
264 #ifdef HAVE_SIGACTION
265 /* AIX has problem with empty static initializers. */
266 memset(&sa, 0, sizeof(sa));
268 if (!fn)
269 sa.sa_handler = SIG_IGN;
270 else
271 sa.sa_handler = got_signal;
273 sigfillset(&sa.sa_mask);
274 if (!fn) sigaction(sig, &sa, NULL);
275 #endif
277 signal_info[sig].handler = fn;
278 signal_info[sig].data = data;
279 signal_info[sig].critical = critical;
281 #ifdef HAVE_SIGACTION
282 if (fn) sigaction(sig, &sa, NULL);
283 #else
284 signal(sig, handler);
285 #endif
288 static volatile int pending_alarm = 0;
290 #ifdef SIGALRM
291 static void
292 alarm_handler(void *x)
294 pending_alarm = 0;
295 check_for_select_race();
297 #endif
299 static void
300 check_for_select_race(void)
302 if (critical_section) {
303 #ifdef SIGALRM
304 install_signal_handler(SIGALRM, alarm_handler, NULL, 1);
305 #endif
306 pending_alarm = 1;
307 #ifdef HAVE_ALARM
308 alarm(1);
309 #endif
313 void
314 uninstall_alarm(void)
316 pending_alarm = 0;
317 #ifdef HAVE_ALARM
318 alarm(0);
319 #endif
323 #ifdef SIGCHLD
324 static void
325 sig_chld(void *p)
327 #ifdef WNOHANG
328 while ((int) waitpid(-1, NULL, WNOHANG) > 0);
329 #else
330 wait(NULL);
331 #endif
333 #endif
335 void
336 set_sigcld(void)
338 #ifdef SIGCHLD
339 install_signal_handler(SIGCHLD, sig_chld, NULL, 1);
340 #endif
343 void
344 clear_signal_mask_and_handlers(void)
346 memset(signal_info, 0, sizeof(signal_info));
350 check_signals(void)
352 int i, r = 0;
354 for (i = 0; i < NUM_SIGNALS; i++) {
355 struct signal_info *s = &signal_info[i];
357 if (!s->mask) continue;
359 s->mask = 0;
360 if (s->handler)
361 s->handler(s->data);
362 check_bottom_halves();
363 r = 1;
366 return r;