1 /*
2 * cleanup.c -- simple dynamic cleanup function management
3 * Copyright (C) 1995 Markus Armbruster.
4 * Copyright (C) 2007 Colin Watson.
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Library General Public License for more details.
15 *
16 * You should have received a copy of the GNU Library General Public
17 * License along with this library; see the file docs/COPYING.LIB. If not,
18 * write to the Free Software Foundation, Inc., 51 Franklin St, Fifth
19 * Floor, Boston, MA 02110-1301 USA.
20 */
21
22 #ifdef HAVE_CONFIG_H
23 # include "config.h"
24 #endif /* HAVE_CONFIG_H */
25
26 #include <stdbool.h>
27 #include <stdlib.h>
28 #include <stdio.h> /* SunOS's losing assert.h needs it */
29 #include <assert.h>
30 #include <signal.h>
31 #include <unistd.h>
32 #include <string.h>
33
34 #include "xalloc.h"
35
36 #include "manconfig.h" /* for FATAL */
37 #include "cleanup.h"
38
39
40
41 /* Dealing with signals */
42
43
44 /* saved signal actions */
45 #ifdef SIGHUP
46 static struct sigaction saved_hup_action;
47 #endif /* SIGHUP */
48 static struct sigaction saved_int_action;
49 static struct sigaction saved_term_action;
50
51
52 /* Run cleanups, then reraise signal with default handler. */
53 static _Noreturn void
54 sighandler (int signo)
55 {
56 struct sigaction act;
57 sigset_t set;
58
59 do_cleanups_sigsafe (true);
60
61 /* set default signal action */
62 memset (&act, 0, sizeof act);
63 act.sa_handler = SIG_DFL;
64 sigemptyset (&act.sa_mask);
65 act.sa_flags = 0;
66 if (sigaction (signo, &act, NULL)) {
67 /* should not happen */
68 _exit (FATAL); /* exit() is taboo from signal handlers! */
69 }
70
71 /* unmask signo */
72 if ( sigemptyset (&set)
73 || sigaddset (&set, signo)
74 || sigprocmask (SIG_UNBLOCK, &set, NULL)) {
75 /* shouldn't happen */
76 _exit (FATAL); /* exit() is taboo from signal handlers! */
77 }
78
79 /* signal has now default action and is unmasked,
80 reraise it to terminate program abnormally */
81 raise (signo);
82 abort();
83 }
84
85
86 /* Save signo's current action to oldact, if its handler is SIG_DFL
87 install sighandler, return 0 on success, -1 on failure. */
88 static int
89 trap_signal (int signo, struct sigaction *oldact)
90 {
91 if (sigaction (signo, NULL, oldact)) {
92 return -1;
93 }
94
95 if (oldact->sa_handler == SIG_DFL) {
96 struct sigaction act;
97
98 memset (&act, 0, sizeof act);
99 act.sa_handler = sighandler;
100 sigemptyset (&act.sa_mask);
101 act.sa_flags = 0;
102 return sigaction (signo, &act, oldact);
103 }
104
105 return 0;
106 }
107
108
109 /* Trap some abnormal exits to call do_cleanups(). */
110 static int
111 trap_abnormal_exits (void)
112 {
113 #ifdef SIGHUP
114 if (trap_signal (SIGHUP, &saved_hup_action))
115 return -1;
116 #endif /* SIGHUP */
117 if (trap_signal (SIGINT, &saved_int_action))
118 return -1;
119 if (trap_signal (SIGTERM, &saved_term_action))
120 return -1;
121 return 0;
122 }
123
124
125 /* Restore signo's action from oldact if its current handler is
126 sighandler, return 0 on success, -1 on failure. */
127 static int
128 untrap_signal (int signo, struct sigaction *oldact)
129 {
130 struct sigaction act;
131 if (sigaction (signo, NULL, &act)) {
132 return -1;
133 }
134
135 if (act.sa_handler == sighandler) {
136 return sigaction (signo, oldact, NULL);
137 }
138
139 return 0;
140 }
141
142
143 /* Undo a previous trap_abnormal_exits(). */
144 static int
145 untrap_abnormal_exits (void)
146 {
147 #ifdef SIGHUP
148 if (untrap_signal (SIGHUP, &saved_hup_action))
149 return -1;
150 #endif /* SIGHUP */
151 if (untrap_signal (SIGINT, &saved_int_action))
152 return -1;
153 if (untrap_signal (SIGTERM, &saved_term_action))
154 return -1;
155 return 0;
156 }
157
158
159
160 typedef struct {
161 cleanup_fun fun;
162 void *arg;
163 int sigsafe;
164 } slot;
165
166 static slot *stack = NULL; /* stack of cleanup functions */
167 static unsigned nslots = 0; /* #slots in stack */
168 static unsigned tos = 0; /* top of stack, 0 <= tos <= nslots */
169
170 /* Call cleanup functions in stack from from top to bottom,
171 * Automatically called on program termination via exit(3) or default
172 * action for SIGHUP, SIGINT or SIGTERM.
173 * Since this may be called from a signal handler, do not use free().
174 * If in_sighandler is true, cleanup functions with sigsafe=0 will not be
175 * called.
176 */
177 void
178 do_cleanups_sigsafe (bool in_sighandler)
179 {
180 unsigned i;
181
182 assert (tos <= nslots);
183 for (i = tos; i > 0; --i)
184 if (!in_sighandler || stack[i-1].sigsafe)
185 stack[i-1].fun (stack[i-1].arg);
186 }
187
188 /* Call cleanup functions in stack from from top to bottom,
189 * Automatically called on program termination via exit(3).
190 */
191 void
192 do_cleanups (void)
193 {
194 do_cleanups_sigsafe (false);
195 tos = 0;
196 nslots = 0;
197 free (stack);
198 stack = NULL;
199 }
200
201
202 /* Push a cleanup function on the cleanup stack,
203 * return 0 on success, -1 on failure.
204 * Caution: the cleanup function may be called from signal handlers if
205 * sigsafe=1. If you just want a convenient atexit() wrapper, pass
206 * sigsafe=0.
207 */
208 int
209 push_cleanup (cleanup_fun fun, void *arg, int sigsafe)
210 {
211 static bool handler_installed = false;
212
213 assert (tos <= nslots);
214
215 if (!handler_installed) {
216 if (atexit (do_cleanups))
217 return -1;
218 handler_installed = true;
219 }
220
221 if (tos == nslots) {
222 /* stack is full, allocate another slot */
223 /* stack is not expected to grow much, otherwise we would double it */
224 slot *new_stack;
225
226 if (stack) {
227 new_stack = xnrealloc (stack, nslots+1, sizeof (slot));
228 } else {
229 new_stack = xnmalloc (nslots+1, sizeof (slot));
230 }
231
232 if (!new_stack) return -1;
233 stack = new_stack;
234 ++nslots;
235 }
236
237 assert (tos < nslots);
238 stack[tos].fun = fun;
239 stack[tos].arg = arg;
240 stack[tos].sigsafe = sigsafe;
241 ++tos;
242
243
244 trap_abnormal_exits();
245
246 return 0;
247 }
248
249
250 /* Remove topmost cleanup function from the cleanup stack that matches the
251 * given values.
252 */
253 void
254 pop_cleanup (cleanup_fun fun, void *arg)
255 {
256 unsigned i, j;
257
258 assert (tos > 0);
259
260 for (i = tos; i > 0; --i) {
261 if (stack[i-1].fun == fun && stack[i-1].arg == arg) {
262 for (j = i; j < tos; ++j)
263 stack[j-1] = stack[j];
264 --tos;
265 break;
266 }
267 }
268
269 if (tos == 0) untrap_abnormal_exits();
270 }
271
272
273 /* Pop all cleanup functions from the cleanup stack. */
274 void
275 pop_all_cleanups (void)
276 {
277 tos = 0;
278 untrap_abnormal_exits();
279 }