Merge branch 't/gui_improvements'
[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 }