Add missing newline to --version output.
[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 }