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