]> git.tuebingen.mpg.de Git - paraslash.git/blob - signal.c
server: Avoid deadlock in daemon_log().
[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, see select(2).
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 = para_calloc(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  * \param rfds The fd_set containing the signal pipe.
209  *
210  * \return On success, the number of the received signal is returned. If there
211  * is no signal currently pending, the function returns zero. On read errors
212  * from the signal pipe, the process is terminated.
213  */
214 int para_next_signal(fd_set *rfds)
215 {
216         size_t n;
217         int s, ret = read_nonblock(signal_pipe[0], &s, sizeof(s), rfds, &n);
218
219         if (ret < 0) {
220                 PARA_EMERG_LOG("%s\n", para_strerror(-ret));
221                 exit(EXIT_FAILURE);
222         }
223         if (n == 0)
224                 return 0;
225         assert(n == sizeof(s));
226         PARA_DEBUG_LOG("next signal: %d\n", s);
227         return s;
228 }
229
230 /**
231  * Close the write end of the signal pipe, deallocate resources.
232  *
233  * \param st The pointer obtained earlier from signal_init_or_die().
234  */
235 void signal_shutdown(struct signal_task *st)
236 {
237         close(signal_pipe[1]);
238         free(st);
239 }