audiod: Avoid delay when closing slot.
[paraslash.git] / signal.c
1 /*
2  * Copyright (C) 2004-2013 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  * A generic signal handler is used for all signals simultaneously. When a
26  * signal arrives, the signal handler writes the number of the signal received
27  * to one end of the signal pipe. The application can test for pending signals
28  * by checking if the file descriptor of the other end of the signal pipe is
29  * ready for reading, see select(2).
30  *
31  * \return This function either succeeds or calls exit(2) to terminate the
32  * current process. On success, the file descriptor of the read end of the
33  * signal pipe is 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 /* Write the signal number to signal pipe. */
55 static void generic_signal_handler(int s)
56 {
57         /*
58          * Signal handlers that make system calls must save a copy of errno on
59          * entry to the handler and restore it on exit, to prevent the
60          * possibility of overwriting a errno value that had previously been
61          * set in the main program.
62          */
63         int save_errno = errno;
64         ssize_t ret = write(signal_pipe[1], &s, sizeof(int));
65
66         if (ret == sizeof(int)) {
67                 errno = save_errno;
68                 return;
69         }
70         if (ret < 0)
71                 PARA_EMERG_LOG("%s\n", strerror(errno));
72         else
73                 PARA_EMERG_LOG("short write to signal pipe\n");
74         exit(EXIT_FAILURE);
75 }
76
77 /**
78  * Reap one child.
79  *
80  * \param pid In case a child died, its pid is returned here.
81  *
82  * Call waitpid() and print a log message containing the pid and the cause of
83  * the child's death.
84  *
85  * \return A (negative) paraslash error code on errors, zero, if no child died,
86  * one otherwise. If and only if the function returns one, the content of \a
87  * pid is meaningful.
88  *
89  * \sa waitpid(2).
90  */
91 int para_reap_child(pid_t *pid)
92 {
93         int status;
94         *pid = waitpid(-1, &status, WNOHANG);
95
96         if (!*pid)
97                 return 0;
98         if (*pid < 0)
99                 return -ERRNO_TO_PARA_ERROR(errno);
100         if (WIFEXITED(status))
101                 PARA_DEBUG_LOG("child %i exited. Exit status: %i\n", (int)*pid,
102                         WEXITSTATUS(status));
103         else if (WIFSIGNALED(status))
104                 PARA_DEBUG_LOG("child %i was killed by signal %i\n", (int)*pid,
105                         WTERMSIG(status));
106         else
107                 PARA_WARNING_LOG("child %i terminated abormally\n", (int)*pid);
108         return 1;
109 }
110
111 /**
112  * Install the given handler for the given signal.
113  *
114  * \param sig The number of the signal to catch.
115  * \param handler to be installed, \p SIG_IGN, or \p SIG_DFL.
116  *
117  * This either succeeds or calls exit(EXIT_FAILURE).
118  *
119  * \sa sigaction(2).
120  */
121 void para_sigaction(int sig, void (*handler)(int))
122 {
123         struct sigaction act;
124
125         PARA_DEBUG_LOG("catching signal %d\n", sig);
126         act.sa_handler = handler;
127         sigemptyset(&act.sa_mask);
128         act.sa_flags = 0;
129         if (sig == SIGALRM) {
130                 #ifdef SA_INTERRUPT /* SunOS */
131                         act.sa_flags |= SA_INTERRUPT;
132                 #endif
133         } else {
134                 #ifdef SA_RESTART /* BSD */
135                         act.sa_flags |= SA_RESTART;
136                 #endif
137         }
138         if (sigaction(sig, &act, NULL) >= 0)
139                 return;
140         PARA_EMERG_LOG("failed to install signal handler for signal %d\n",
141                 sig);
142         exit(EXIT_FAILURE);
143 }
144
145 /**
146  * Install the generic signal handler for the given signal number.
147  *
148  * \param sig The number of the signal to catch.
149  *
150  * \sa signal(2), sigaction(2).
151  */
152 void para_install_sighandler(int sig)
153 {
154         para_sigaction(sig, &generic_signal_handler);
155 }
156
157 /**
158  * Block a signal for the caller.
159  *
160  * \param sig The signal to block.
161  *
162  * This sets the given signal in the current signal mask of the calling process
163  * to prevent this signal from delivery.
164  *
165  * \sa \ref para_unblock_signal(), sigprocmask(2), sigaddset(3).
166  */
167 void para_block_signal(int sig)
168 {
169         sigset_t set;
170
171         PARA_DEBUG_LOG("blocking signal %d\n", sig);
172         sigemptyset(&set);
173         sigaddset(&set, sig);
174         sigprocmask(SIG_BLOCK, &set, NULL);
175 }
176
177 /**
178  * Unblock a signal.
179  *
180  * \param sig The signal to unblock.
181  *
182  * This function removes the given signal from the current set of blocked
183  * signals.
184  *
185  * \sa \ref para_block_signal(), sigprocmask(2), sigaddset(3).
186  */
187 void para_unblock_signal(int sig)
188 {
189         sigset_t set;
190
191         PARA_DEBUG_LOG("unblocking signal %d\n", sig);
192         sigemptyset(&set);
193         sigaddset(&set, sig);
194         sigprocmask(SIG_UNBLOCK, &set, NULL);
195 }
196
197 /**
198  * Return the number of the next pending signal.
199  *
200  * \param rfds The fd_set containing the signal pipe.
201  *
202  * \return On success, the number of the received signal is returned. If there
203  * is no signal currently pending, the function returns zero. On read errors
204  * from the signal pipe, the process is terminated.
205  */
206 int para_next_signal(fd_set *rfds)
207 {
208         size_t n;
209         int s, ret = read_nonblock(signal_pipe[0], &s, sizeof(s), rfds, &n);
210
211         if (ret < 0) {
212                 PARA_EMERG_LOG("%s\n", para_strerror(-ret));
213                 exit(EXIT_FAILURE);
214         }
215         if (n == 0)
216                 return 0;
217         assert(n == sizeof(s));
218         PARA_DEBUG_LOG("next signal: %d\n", s);
219         return s;
220 }
221
222 /**
223  * Close the write end of the signal pipe.
224  */
225 void para_signal_shutdown(void)
226 {
227         close(signal_pipe[1]);
228 }