]>
Commit | Line | Data |
---|---|---|
064af421 BP |
1 | /* |
2 | * Copyright (c) 2008, 2009 Nicira Networks. | |
3 | * | |
a14bc59f BP |
4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
5 | * you may not use this file except in compliance with the License. | |
6 | * You may obtain a copy of the License at: | |
064af421 | 7 | * |
a14bc59f BP |
8 | * http://www.apache.org/licenses/LICENSE-2.0 |
9 | * | |
10 | * Unless required by applicable law or agreed to in writing, software | |
11 | * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | * See the License for the specific language governing permissions and | |
14 | * limitations under the License. | |
064af421 BP |
15 | */ |
16 | #include <config.h> | |
17 | #include "fatal-signal.h" | |
18 | #include <assert.h> | |
19 | #include <errno.h> | |
20 | #include <signal.h> | |
21 | #include <stdbool.h> | |
22 | #include <stdio.h> | |
23 | #include <stdlib.h> | |
24 | #include <string.h> | |
25 | #include <unistd.h> | |
26 | #include "util.h" | |
27 | ||
28 | /* Signals to catch. */ | |
29 | static const int fatal_signals[] = { SIGTERM, SIGINT, SIGHUP, SIGALRM }; | |
30 | ||
31 | /* Signals to catch as a sigset_t. */ | |
32 | static sigset_t fatal_signal_set; | |
33 | ||
34 | /* Hooks to call upon catching a signal */ | |
35 | struct hook { | |
36 | void (*func)(void *aux); | |
37 | void *aux; | |
38 | bool run_at_exit; | |
39 | }; | |
40 | #define MAX_HOOKS 32 | |
41 | static struct hook hooks[MAX_HOOKS]; | |
42 | static size_t n_hooks; | |
43 | ||
44 | /* Number of nesting signal blockers. */ | |
45 | static int block_level = 0; | |
46 | ||
47 | /* Signal mask saved by outermost signal blocker. */ | |
48 | static sigset_t saved_signal_mask; | |
49 | ||
50 | /* Disabled by fatal_signal_fork()? */ | |
51 | static bool disabled; | |
52 | ||
53 | static void call_sigprocmask(int how, sigset_t* new_set, sigset_t* old_set); | |
54 | static void atexit_handler(void); | |
55 | static void call_hooks(int sig_nr); | |
56 | ||
57 | /* Registers 'hook' to be called when a process termination signal is raised. | |
58 | * If 'run_at_exit' is true, 'hook' is also called during normal process | |
59 | * termination, e.g. when exit() is called or when main() returns. */ | |
60 | void | |
61 | fatal_signal_add_hook(void (*func)(void *aux), void *aux, bool run_at_exit) | |
62 | { | |
63 | fatal_signal_block(); | |
64 | assert(n_hooks < MAX_HOOKS); | |
65 | hooks[n_hooks].func = func; | |
66 | hooks[n_hooks].aux = aux; | |
67 | hooks[n_hooks].run_at_exit = run_at_exit; | |
68 | n_hooks++; | |
69 | fatal_signal_unblock(); | |
70 | } | |
71 | ||
72 | /* Blocks program termination signals until fatal_signal_unblock() is called. | |
73 | * May be called multiple times with nesting; if so, fatal_signal_unblock() | |
74 | * must be called the same number of times to unblock signals. | |
75 | * | |
76 | * This is needed while adjusting a data structure that will be accessed by a | |
77 | * fatal signal hook, so that the hook is not invoked while the data structure | |
78 | * is in an inconsistent state. */ | |
79 | void | |
80 | fatal_signal_block(void) | |
81 | { | |
82 | static bool inited = false; | |
83 | if (!inited) { | |
84 | size_t i; | |
85 | ||
86 | inited = true; | |
87 | sigemptyset(&fatal_signal_set); | |
88 | for (i = 0; i < ARRAY_SIZE(fatal_signals); i++) { | |
89 | int sig_nr = fatal_signals[i]; | |
90 | struct sigaction old_sa; | |
91 | ||
92 | sigaddset(&fatal_signal_set, sig_nr); | |
93 | if (sigaction(sig_nr, NULL, &old_sa)) { | |
94 | ovs_fatal(errno, "sigaction"); | |
95 | } | |
96 | if (old_sa.sa_handler == SIG_DFL | |
97 | && signal(sig_nr, fatal_signal_handler) == SIG_ERR) { | |
98 | ovs_fatal(errno, "signal"); | |
99 | } | |
100 | } | |
101 | atexit(atexit_handler); | |
102 | } | |
103 | ||
104 | if (++block_level == 1) { | |
105 | call_sigprocmask(SIG_BLOCK, &fatal_signal_set, &saved_signal_mask); | |
106 | } | |
107 | } | |
108 | ||
109 | /* Unblocks program termination signals blocked by fatal_signal_block() is | |
110 | * called. If multiple calls to fatal_signal_block() are nested, | |
111 | * fatal_signal_unblock() must be called the same number of times to unblock | |
112 | * signals. */ | |
113 | void | |
114 | fatal_signal_unblock(void) | |
115 | { | |
116 | assert(block_level > 0); | |
117 | if (--block_level == 0) { | |
118 | call_sigprocmask(SIG_SETMASK, &saved_signal_mask, NULL); | |
119 | } | |
120 | } | |
121 | ||
122 | /* Handles fatal signal number 'sig_nr'. | |
123 | * | |
124 | * Ordinarily this is the actual signal handler. When other code needs to | |
125 | * handle one of our signals, however, it can register for that signal and, if | |
126 | * and when necessary, call this function to do fatal signal processing for it | |
127 | * and terminate the process. Currently only timeval.c does this, for SIGALRM. | |
128 | * (It is not important whether the other code sets up its signal handler | |
129 | * before or after this file, because this file will only set up a signal | |
130 | * handler in the case where the signal has its default handling.) */ | |
131 | void | |
132 | fatal_signal_handler(int sig_nr) | |
133 | { | |
134 | call_hooks(sig_nr); | |
135 | ||
136 | /* Re-raise the signal with the default handling so that the program | |
137 | * termination status reflects that we were killed by this signal */ | |
138 | signal(sig_nr, SIG_DFL); | |
139 | raise(sig_nr); | |
140 | } | |
141 | ||
142 | static void | |
143 | atexit_handler(void) | |
144 | { | |
145 | if (!disabled) { | |
146 | call_hooks(0); | |
147 | } | |
148 | } | |
149 | ||
150 | static void | |
151 | call_hooks(int sig_nr) | |
152 | { | |
153 | static volatile sig_atomic_t recurse = 0; | |
154 | if (!recurse) { | |
155 | size_t i; | |
156 | ||
157 | recurse = 1; | |
158 | ||
159 | for (i = 0; i < n_hooks; i++) { | |
160 | struct hook *h = &hooks[i]; | |
161 | if (sig_nr || h->run_at_exit) { | |
162 | h->func(h->aux); | |
163 | } | |
164 | } | |
165 | } | |
166 | } | |
167 | \f | |
168 | static char **files; | |
169 | static size_t n_files, max_files; | |
170 | ||
171 | static void unlink_files(void *aux); | |
172 | static void do_unlink_files(void); | |
173 | ||
174 | /* Registers 'file' to be unlinked when the program terminates via exit() or a | |
175 | * fatal signal. */ | |
176 | void | |
177 | fatal_signal_add_file_to_unlink(const char *file) | |
178 | { | |
179 | static bool added_hook = false; | |
180 | if (!added_hook) { | |
181 | added_hook = true; | |
182 | fatal_signal_add_hook(unlink_files, NULL, true); | |
183 | } | |
184 | ||
185 | fatal_signal_block(); | |
186 | if (n_files >= max_files) { | |
187 | files = x2nrealloc(files, &max_files, sizeof *files); | |
188 | } | |
189 | files[n_files++] = xstrdup(file); | |
190 | fatal_signal_unblock(); | |
191 | } | |
192 | ||
193 | /* Unregisters 'file' from being unlinked when the program terminates via | |
194 | * exit() or a fatal signal. */ | |
195 | void | |
196 | fatal_signal_remove_file_to_unlink(const char *file) | |
197 | { | |
198 | size_t i; | |
199 | ||
200 | fatal_signal_block(); | |
201 | for (i = 0; i < n_files; i++) { | |
202 | if (!strcmp(files[i], file)) { | |
203 | free(files[i]); | |
204 | files[i] = files[--n_files]; | |
205 | break; | |
206 | } | |
207 | } | |
208 | fatal_signal_unblock(); | |
209 | } | |
210 | ||
211 | static void | |
212 | unlink_files(void *aux UNUSED) | |
213 | { | |
214 | do_unlink_files(); | |
215 | } | |
216 | ||
217 | static void | |
218 | do_unlink_files(void) | |
219 | { | |
220 | size_t i; | |
221 | ||
222 | for (i = 0; i < n_files; i++) { | |
223 | unlink(files[i]); | |
224 | } | |
225 | } | |
226 | \f | |
227 | /* Disables the fatal signal hook mechanism. Following a fork, one of the | |
228 | * resulting processes can call this function to allow it to terminate without | |
229 | * triggering fatal signal processing or removing files. Fatal signal | |
230 | * processing is still enabled in the other process. */ | |
231 | void | |
232 | fatal_signal_fork(void) | |
233 | { | |
234 | size_t i; | |
235 | ||
236 | disabled = true; | |
237 | ||
238 | for (i = 0; i < ARRAY_SIZE(fatal_signals); i++) { | |
239 | int sig_nr = fatal_signals[i]; | |
240 | if (signal(sig_nr, SIG_DFL) == SIG_IGN) { | |
241 | signal(sig_nr, SIG_IGN); | |
242 | } | |
243 | } | |
244 | } | |
245 | \f | |
246 | static void | |
247 | call_sigprocmask(int how, sigset_t* new_set, sigset_t* old_set) | |
248 | { | |
249 | int error = sigprocmask(how, new_set, old_set); | |
250 | if (error) { | |
251 | fprintf(stderr, "sigprocmask: %s\n", strerror(errno)); | |
252 | } | |
253 | } |