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