Replace license boilerplate with single line SPDX comments.
[dss.git] / sig.c
1 /* SPDX-License-Identifier: GPL-2.0 */
2
3 #include <string.h>
4 #include <errno.h>
5 #include <signal.h>
6 #include <sys/types.h>
7 #include <sys/wait.h>
8 #include <dirent.h>
9 #include <assert.h>
10 #include <stdio.h>
11 #include <unistd.h>
12 #include <signal.h>
13 #include <stdlib.h>
14 #include <sys/select.h>
15
16
17 #include "gcc-compat.h"
18 #include "err.h"
19 #include "log.h"
20 #include "str.h"
21 #include "file.h"
22 #include "sig.h"
23
24 static int signal_pipe[2];
25
26 /**
27 * Initialize the signal subsystem.
28 *
29 * This function creates a pipe, the signal pipe, to deliver pending signals to
30 * the application (Bernstein's trick). It should be called during the
31 * application's startup part, followed by subsequent calls to
32 * install_sighandler() for each signal that should be caught.
33 *
34 * signal_init() installs a generic signal handler which is used for all
35 * signals simultaneously. When a signal arrives, this generic signal handler
36 * writes the corresponding signal number to the signal pipe so that the
37 * application can test for pending signals simply by checking the signal pipe
38 * for reading, e.g. by using the select(2) system call.
39 *
40 * \return This function either succeeds or calls exit(2) to terminate
41 * the current process. On success, the file descriptor of the signal pipe is
42 * returned.
43 */
44 int signal_init(void)
45 {
46 int ret;
47 if (pipe(signal_pipe) < 0) {
48 ret = -ERRNO_TO_DSS_ERROR(errno);
49 goto err_out;
50 }
51 ret = mark_fd_nonblocking(signal_pipe[0]);
52 if (ret < 0)
53 goto err_out;
54 ret = mark_fd_nonblocking(signal_pipe[1]);
55 if (ret < 0)
56 goto err_out;
57 return signal_pipe[0];
58 err_out:
59 DSS_EMERG_LOG(("%s\n", dss_strerror(-ret)));
60 exit(EXIT_FAILURE);
61 }
62
63 /* Write the signal number to the signal pipe, abort on errors. */
64 static void generic_signal_handler(int s)
65 {
66 /*
67 * Signal handlers that make system calls must save a copy of errno on
68 * entry to the handler and restore it on exit, to prevent the
69 * possibility of overwriting a errno value that had previously been
70 * set in the main program.
71 */
72 int save_errno = errno;
73 ssize_t ret = write(signal_pipe[1], &s, sizeof(int));
74
75 if (ret == sizeof(int)) {
76 errno = save_errno;
77 return;
78 }
79 if (ret < 0)
80 DSS_EMERG_LOG(("%s\n", strerror(errno)));
81 else
82 DSS_EMERG_LOG(("short write to signal pipe\n"));
83 exit(EXIT_FAILURE);
84 }
85
86 /**
87 * Reap one child.
88 *
89 * \param pid In case a child died, its pid is returned here.
90 *
91 * Call waitpid() and print a log message containing the pid and the cause of
92 * the child's death.
93 *
94 * \return A (negative) error code on errors, zero, if no child died, one
95 * otherwise. If and only if the function returns one, the content of \a pid is
96 * meaningful.
97 *
98 * \sa waitpid(2)
99 */
100 int reap_child(pid_t *pid, int *status)
101 {
102 *pid = waitpid(-1, status, WNOHANG);
103
104 if (!*pid)
105 return 0;
106 if (*pid < 0)
107 return -ERRNO_TO_DSS_ERROR(errno);
108 if (WIFEXITED(*status))
109 DSS_DEBUG_LOG(("child %i exited. Exit status: %i\n", (int)*pid,
110 WEXITSTATUS(*status)));
111 else if (WIFSIGNALED(*status))
112 DSS_DEBUG_LOG(("child %i was killed by signal %i\n", (int)*pid,
113 WTERMSIG(*status)));
114 else
115 DSS_WARNING_LOG(("child %i terminated abormally\n", (int)*pid));
116 return 1;
117 }
118
119 /**
120 * Wrapper around signal(2)
121 *
122 * \param sig The number of the signal to catch.
123 *
124 * This installs the generic signal handler for the given signal.
125 *
126 * \return This function returns 1 on success and \p -E_SIGNAL_SIG_ERR on errors.
127 * \sa signal(2)
128 */
129 int install_sighandler(int sig)
130 {
131 DSS_DEBUG_LOG(("catching signal %d\n", sig));
132 if (signal(sig, &generic_signal_handler) != SIG_ERR)
133 return 1;
134 return -E_SIGNAL_SIG_ERR;
135 }
136
137 /**
138 * Return number of next pending signal.
139 *
140 * This should be called if the fd for the signal pipe is ready for reading.
141 *
142 * \return On success, the number of the received signal is returned.
143 * If the read was interrupted by another signal the function returns 0.
144 * Otherwise a negative error code is returned.
145 */
146 int next_signal(void)
147 {
148 int s, err;
149 ssize_t r;
150
151 r = read(signal_pipe[0], &s, sizeof(s));
152 if (r == sizeof(s)) {
153 DSS_DEBUG_LOG(("next signal: %d\n", s));
154 return s;
155 }
156 err = errno;
157 assert(r < 0);
158 if (err == EAGAIN)
159 return 0;
160 DSS_ERROR_LOG(("failed to read from signal pipe\n"));
161 return -ERRNO_TO_DSS_ERROR(err);
162 }
163
164 /**
165 * Close the signal pipe.
166 */
167 void signal_shutdown(void)
168 {
169 close(signal_pipe[1]);
170 }