aft: Do not invalidate status items when closing the audio file table.
[paraslash.git] / signal.c
1 /*
2  * Copyright (C) 2004-2011 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  * Return the number of the next pending signal.
152  *
153  * \param rfds Th fd_set containing the signal pipe.
154  *
155  * \return On success, the number of the received signal is returned. If there
156  * is no signal currently pending, the function returns zero. On read errors
157  * from the signal pipe, the process is terminated.
158  */
159 int para_next_signal(fd_set *rfds)
160 {
161         size_t n;
162         int s, ret = read_nonblock(signal_pipe[0], &s, sizeof(s), rfds, &n);
163
164         if (ret < 0) {
165                 PARA_EMERG_LOG("%s\n", para_strerror(-ret));
166                 exit(EXIT_FAILURE);
167         }
168         if (n == 0)
169                 return 0;
170         assert(n == sizeof(s));
171         PARA_DEBUG_LOG("next signal: %d\n", s);
172         return s;
173 }
174
175 /**
176  * Close the write end of the signal pipe.
177  */
178 void para_signal_shutdown(void)
179 {
180         close(signal_pipe[1]);
181 }