Initial commit of the HEAD branch of the ELinks CVS repository, as of
[elinks/images.git] / src / osdep / signals.c
blobabee9b250859fb5a5b858dca9ed7944602b33ac2
1 /* Signals handling. */
2 /* $Id: signals.c,v 1.35 2005/07/04 01:37:36 miciah Exp $ */
4 #ifdef HAVE_CONFIG_H
5 #include "config.h"
6 #endif
8 #include <errno.h>
9 #include <signal.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #ifdef HAVE_SYS_SIGNAL_H
13 #include <sys/signal.h>
14 #endif
15 #include <sys/types.h>
16 #ifdef HAVE_SYS_WAIT_H
17 #include <sys/wait.h>
18 #endif
19 #ifdef HAVE_UNISTD_H
20 #include <unistd.h>
21 #endif
23 #include "elinks.h"
25 #include "main/main.h"
26 #include "main/select.h"
27 #include "main/version.h"
28 #include "osdep/signals.h"
29 #include "terminal/kbd.h"
30 #include "terminal/terminal.h"
31 #include "util/error.h"
34 static void unhandle_basic_signals(struct terminal *term);
36 static void
37 sig_terminate(struct terminal *term)
39 unhandle_basic_signals(term);
40 program.terminate = 1;
41 program.retval = RET_SIGNAL;
44 #ifdef SIGHUP
45 static void
46 sig_intr(struct terminal *term)
48 unhandle_basic_signals(term);
50 if (!term)
51 program.terminate = 1;
52 else
53 register_bottom_half(destroy_terminal, term);
55 #endif
57 void
58 sig_ctrl_c(struct terminal *term)
60 if (!is_blocked()) kbd_ctrl_c();
63 #ifdef SIGTTOU
64 static void
65 sig_ign(void *x)
68 #endif
70 #if defined(SIGTSTP) || defined(SIGTTIN)
71 static void
72 sig_tstp(struct terminal *term)
74 #ifdef SIGSTOP
75 pid_t pid = getpid();
77 block_itrm(0);
78 #if defined (SIGCONT) && defined(SIGTTOU)
79 if (!fork()) {
80 sleep(1);
81 kill(pid, SIGCONT);
82 exit(0);
84 #endif
85 raise(SIGSTOP);
86 #endif
88 #endif
90 #ifdef SIGCONT
91 static void
92 sig_cont(struct terminal *term)
94 if (!unblock_itrm(0)) resize_terminal();
96 #endif
98 #ifdef CONFIG_BACKTRACE
99 static void
100 sig_segv(struct terminal *term)
102 /* Get some attention. */
103 fputs("\a", stderr); fflush(stderr); sleep(1); fputs("\a\n", stderr);
105 /* Rant. */
106 fputs( "ELinks crashed. That shouldn't happen. Please report this incident to\n"
107 "the developers. If you would like to help to debug the problem you just\n"
108 "uncovered, please keep the core you just got and send the developers\n"
109 "the output of 'bt' command entered inside of gdb (which you run as:\n"
110 "gdb elinks core). Thanks a lot for your cooperation!\n\n", stderr);
112 /* version information */
113 fputs(full_static_version, stderr);
114 fputs("\n\n", stderr);
116 /* Backtrace. */
117 dump_backtrace(stderr, 1);
119 /* TODO: Perhaps offer launching of gdb? Or trying to continue w/
120 * program execution? --pasky */
122 /* The fastest way OUT! */
123 abort();
125 #endif
128 void
129 handle_basic_signals(struct terminal *term)
131 #ifdef SIGHUP
132 install_signal_handler(SIGHUP, (void (*)(void *)) sig_intr, term, 0);
133 #endif
134 install_signal_handler(SIGINT, (void (*)(void *)) sig_ctrl_c, term, 0);
135 install_signal_handler(SIGTERM, (void (*)(void *)) sig_terminate, term, 0);
136 #ifdef SIGTSTP
137 install_signal_handler(SIGTSTP, (void (*)(void *)) sig_tstp, term, 0);
138 #endif
139 #ifdef SIGTTIN
140 install_signal_handler(SIGTTIN, (void (*)(void *)) sig_tstp, term, 0);
141 #endif
142 #ifdef SIGTTOU
143 install_signal_handler(SIGTTOU, (void (*)(void *)) sig_ign, term, 0);
144 #endif
145 #ifdef SIGCONT
146 install_signal_handler(SIGCONT, (void (*)(void *)) sig_cont, term, 0);
147 #endif
148 #ifdef CONFIG_BACKTRACE
149 install_signal_handler(SIGSEGV, (void (*)(void *)) sig_segv, term, 1);
150 #endif
153 void
154 unhandle_terminal_signals(struct terminal *term)
156 #ifdef SIGHUP
157 install_signal_handler(SIGHUP, NULL, NULL, 0);
158 #endif
159 install_signal_handler(SIGINT, NULL, NULL, 0);
160 #ifdef SIGTSTP
161 install_signal_handler(SIGTSTP, NULL, NULL, 0);
162 #endif
163 #ifdef SIGTTIN
164 install_signal_handler(SIGTTIN, NULL, NULL, 0);
165 #endif
166 #ifdef SIGTTOU
167 install_signal_handler(SIGTTOU, NULL, NULL, 0);
168 #endif
169 #ifdef SIGCONT
170 install_signal_handler(SIGCONT, NULL, NULL, 0);
171 #endif
172 #ifdef CONFIG_BACKTRACE
173 install_signal_handler(SIGSEGV, NULL, NULL, 0);
174 #endif
177 static void
178 unhandle_basic_signals(struct terminal *term)
180 #ifdef SIGHUP
181 install_signal_handler(SIGHUP, NULL, NULL, 0);
182 #endif
183 install_signal_handler(SIGINT, NULL, NULL, 0);
184 install_signal_handler(SIGTERM, NULL, NULL, 0);
185 #ifdef SIGTSTP
186 install_signal_handler(SIGTSTP, NULL, NULL, 0);
187 #endif
188 #ifdef SIGTTIN
189 install_signal_handler(SIGTTIN, NULL, NULL, 0);
190 #endif
191 #ifdef SIGTTOU
192 install_signal_handler(SIGTTOU, NULL, NULL, 0);
193 #endif
194 #ifdef SIGCONT
195 install_signal_handler(SIGCONT, NULL, NULL, 0);
196 #endif
197 #ifdef CONFIG_BACKTRACE
198 install_signal_handler(SIGSEGV, NULL, NULL, 0);
199 #endif
202 struct signal_info {
203 void (*handler)(void *);
204 void *data;
205 int critical;
206 int mask;
209 static struct signal_info signal_info[NUM_SIGNALS];
210 volatile int critical_section = 0;
212 static void check_for_select_race(void);
214 /* TODO: In order to gain better portability, we should use signal() instead.
215 * Highest care should be given to careful watching of which signals are
216 * blocked and which aren't then, though. --pasky */
218 static void
219 got_signal(int sig)
221 struct signal_info *s;
222 int saved_errno = errno;
224 if (sig >= NUM_SIGNALS || sig < 0) {
225 /* Signal handler - we have no good way how to tell this the
226 * user. She won't care anyway, tho'. */
227 return;
230 s = &signal_info[sig];
232 if (!s->handler) return;
234 if (s->critical) {
235 s->handler(s->data);
236 errno = saved_errno;
237 return;
240 s->mask = 1;
241 check_for_select_race();
243 errno = saved_errno;
246 void
247 install_signal_handler(int sig, void (*fn)(void *), void *data, int critical)
249 #ifdef HAVE_SIGACTION
250 struct sigaction sa;
251 #else
252 void (*handler)(int) = fn ? got_signal : SIG_IGN;
253 #endif
255 /* Yes, assertm() in signal handler is totally unsafe and depends just
256 * on good luck. But hey, assert()ions are never triggered ;-). */
257 assertm(sig >= 0 && sig < NUM_SIGNALS, "bad signal number: %d", sig);
258 if_assert_failed return;
260 #ifdef HAVE_SIGACTION
261 /* AIX has problem with empty static initializers. */
262 memset(&sa, 0, sizeof(sa));
264 if (!fn)
265 sa.sa_handler = SIG_IGN;
266 else
267 sa.sa_handler = got_signal;
269 sigfillset(&sa.sa_mask);
270 if (!fn) sigaction(sig, &sa, NULL);
271 #endif
273 signal_info[sig].handler = fn;
274 signal_info[sig].data = data;
275 signal_info[sig].critical = critical;
277 #ifdef HAVE_SIGACTION
278 if (fn) sigaction(sig, &sa, NULL);
279 #else
280 signal(sig, handler);
281 #endif
284 static volatile int pending_alarm = 0;
286 #ifdef SIGALRM
287 static void
288 alarm_handler(void *x)
290 pending_alarm = 0;
291 check_for_select_race();
293 #endif
295 static void
296 check_for_select_race(void)
298 if (critical_section) {
299 #ifdef SIGALRM
300 install_signal_handler(SIGALRM, alarm_handler, NULL, 1);
301 #endif
302 pending_alarm = 1;
303 #ifdef HAVE_ALARM
304 alarm(1);
305 #endif
309 void
310 uninstall_alarm(void)
312 pending_alarm = 0;
313 #ifdef HAVE_ALARM
314 alarm(0);
315 #endif
319 #ifdef SIGCHLD
320 static void
321 sig_chld(void *p)
323 #ifdef WNOHANG
324 while ((int) waitpid(-1, NULL, WNOHANG) > 0);
325 #else
326 wait(NULL);
327 #endif
329 #endif
331 void
332 set_sigcld(void)
334 #ifdef SIGCHLD
335 install_signal_handler(SIGCHLD, sig_chld, NULL, 1);
336 #endif
339 void
340 clear_signal_mask_and_handlers(void)
342 memset(signal_info, 0, sizeof(signal_info));
346 check_signals(void)
348 int i, r = 0;
350 for (i = 0; i < NUM_SIGNALS; i++) {
351 struct signal_info *s = &signal_info[i];
353 if (!s->mask) continue;
355 s->mask = 0;
356 if (s->handler)
357 s->handler(s->data);
358 check_bottom_halves();
359 r = 1;
362 return r;