Merge branch 'refs/heads/t/wmadec'
[paraslash.git] / signal.c
1 /*
2  * Copyright (C) 2004 Andre Noll <maan@tuebingen.mpg.de>
3  *
4  * Licensed under the GPL v2. For licencing details see COPYING.
5  */
6 /** \file signal.c Signal handling functions. */
7
8 #include <signal.h>
9 #include <sys/types.h>
10 #include <regex.h>
11
12 #include "para.h"
13 #include "error.h"
14 #include "fd.h"
15 #include "list.h"
16 #include "string.h"
17 #include "sched.h"
18 #include "signal.h"
19
20 static int signal_pipe[2];
21
22 /**
23  * Initialize the paraslash signal subsystem.
24  *
25  * This function creates a pipe, the signal pipe, to deliver pending
26  * signals to the application (Bernstein's trick). It should be called
27  * during the application's startup part, followed by subsequent calls
28  * to para_install_sighandler() for each signal that should be caught.
29  *
30  * A generic signal handler is used for all signals simultaneously. When a
31  * signal arrives, the signal handler writes the number of the signal received
32  * to one end of the signal pipe. The application can test for pending signals
33  * by checking if the file descriptor of the other end of the signal pipe is
34  * ready for reading, see select(2).
35  *
36  * \return This function either succeeds or calls exit(2) to terminate the
37  * current process. On success, a signal task structure is returned.
38  */
39 struct signal_task *signal_init_or_die(void)
40 {
41         struct signal_task *st;
42         int ret;
43
44         PARA_NOTICE_LOG("setting up signal handling\n");
45         if (pipe(signal_pipe) < 0) {
46                 ret = -ERRNO_TO_PARA_ERROR(errno);
47                 goto err_out;
48         }
49         ret = mark_fd_nonblocking(signal_pipe[0]);
50         if (ret < 0)
51                 goto err_out;
52         ret = mark_fd_nonblocking(signal_pipe[1]);
53         if (ret < 0)
54                 goto err_out;
55         st = para_calloc(sizeof(*st));
56         st->fd = signal_pipe[0];
57         return st;
58 err_out:
59         PARA_EMERG_LOG("%s\n", para_strerror(-ret));
60         exit(EXIT_FAILURE);
61 }
62
63 /* Write the signal number to signal pipe. */
64 static void generic_signal_handler(int s)
65 {
66         /*
67          * Signal handlers that make system calls must save a copy of errno on
68          * entry to the handler and restore it on exit, to prevent the
69          * possibility of overwriting a errno value that had previously been
70          * set in the main program.
71          */
72         int save_errno = errno;
73         ssize_t ret = write(signal_pipe[1], &s, sizeof(int));
74
75         if (ret == sizeof(int)) {
76                 errno = save_errno;
77                 return;
78         }
79         if (ret < 0)
80                 PARA_EMERG_LOG("%s\n", strerror(errno));
81         else
82                 PARA_EMERG_LOG("short write to signal pipe\n");
83         exit(EXIT_FAILURE);
84 }
85
86 /**
87  * Reap one child.
88  *
89  * \param pid In case a child died, its pid is returned here.
90  *
91  * Call waitpid() and print a log message containing the pid and the cause of
92  * the child's death.
93  *
94  * \return A (negative) paraslash error code on errors, zero, if no child died,
95  * one otherwise. If and only if the function returns one, the content of \a
96  * pid is meaningful.
97  *
98  * \sa waitpid(2).
99  */
100 int para_reap_child(pid_t *pid)
101 {
102         int status;
103         *pid = waitpid(-1, &status, WNOHANG);
104
105         if (!*pid)
106                 return 0;
107         if (*pid < 0)
108                 return -ERRNO_TO_PARA_ERROR(errno);
109         if (WIFEXITED(status))
110                 PARA_DEBUG_LOG("child %i exited. Exit status: %i\n", (int)*pid,
111                         WEXITSTATUS(status));
112         else if (WIFSIGNALED(status))
113                 PARA_DEBUG_LOG("child %i was killed by signal %i\n", (int)*pid,
114                         WTERMSIG(status));
115         else
116                 PARA_WARNING_LOG("child %i terminated abormally\n", (int)*pid);
117         return 1;
118 }
119
120 /**
121  * Install the given handler for the given signal.
122  *
123  * \param sig The number of the signal to catch.
124  * \param handler to be installed, \p SIG_IGN, or \p SIG_DFL.
125  *
126  * This either succeeds or calls exit(EXIT_FAILURE).
127  *
128  * \sa sigaction(2).
129  */
130 void para_sigaction(int sig, void (*handler)(int))
131 {
132         struct sigaction act;
133
134         PARA_DEBUG_LOG("catching signal %d\n", sig);
135         act.sa_handler = handler;
136         sigemptyset(&act.sa_mask);
137         act.sa_flags = 0;
138         if (sig == SIGALRM) {
139                 #ifdef SA_INTERRUPT /* SunOS */
140                         act.sa_flags |= SA_INTERRUPT;
141                 #endif
142         } else {
143                 #ifdef SA_RESTART /* BSD */
144                         act.sa_flags |= SA_RESTART;
145                 #endif
146         }
147         if (sigaction(sig, &act, NULL) >= 0)
148                 return;
149         PARA_EMERG_LOG("failed to install signal handler for signal %d\n",
150                 sig);
151         exit(EXIT_FAILURE);
152 }
153
154 /**
155  * Install the generic signal handler for the given signal number.
156  *
157  * \param sig The number of the signal to catch.
158  *
159  * \sa signal(2), sigaction(2).
160  */
161 void para_install_sighandler(int sig)
162 {
163         para_sigaction(sig, &generic_signal_handler);
164 }
165
166 /**
167  * Block a signal for the caller.
168  *
169  * \param sig The signal to block.
170  *
171  * This sets the given signal in the current signal mask of the calling process
172  * to prevent this signal from delivery.
173  *
174  * \sa \ref para_unblock_signal(), sigprocmask(2), sigaddset(3).
175  */
176 void para_block_signal(int sig)
177 {
178         sigset_t set;
179
180         PARA_DEBUG_LOG("blocking signal %d\n", sig);
181         sigemptyset(&set);
182         sigaddset(&set, sig);
183         sigprocmask(SIG_BLOCK, &set, NULL);
184 }
185
186 /**
187  * Unblock a signal.
188  *
189  * \param sig The signal to unblock.
190  *
191  * This function removes the given signal from the current set of blocked
192  * signals.
193  *
194  * \sa \ref para_block_signal(), sigprocmask(2), sigaddset(3).
195  */
196 void para_unblock_signal(int sig)
197 {
198         sigset_t set;
199
200         PARA_DEBUG_LOG("unblocking signal %d\n", sig);
201         sigemptyset(&set);
202         sigaddset(&set, sig);
203         sigprocmask(SIG_UNBLOCK, &set, NULL);
204 }
205
206 /**
207  * Return the number of the next pending signal.
208  *
209  * \param rfds The fd_set containing the signal pipe.
210  *
211  * \return On success, the number of the received signal is returned. If there
212  * is no signal currently pending, the function returns zero. On read errors
213  * from the signal pipe, the process is terminated.
214  */
215 int para_next_signal(fd_set *rfds)
216 {
217         size_t n;
218         int s, ret = read_nonblock(signal_pipe[0], &s, sizeof(s), rfds, &n);
219
220         if (ret < 0) {
221                 PARA_EMERG_LOG("%s\n", para_strerror(-ret));
222                 exit(EXIT_FAILURE);
223         }
224         if (n == 0)
225                 return 0;
226         assert(n == sizeof(s));
227         PARA_DEBUG_LOG("next signal: %d\n", s);
228         return s;
229 }
230
231 /**
232  * Close the write end of the signal pipe, deallocate resources.
233  *
234  * \param st The pointer obtained earlier from signal_init_or_die().
235  */
236 void signal_shutdown(struct signal_task *st)
237 {
238         close(signal_pipe[1]);
239         free(st);
240 }