Branch data Line data Source code
1 : : /*
2 : : * libdpkg - Debian packaging suite library routines
3 : : * ehandle.c - error handling
4 : : *
5 : : * Copyright © 1994,1995 Ian Jackson <ijackson@chiark.greenend.org.uk>
6 : : * Copyright © 2008-2014 Guillem Jover <guillem@debian.org>
7 : : *
8 : : * This is free software; you can redistribute it and/or modify
9 : : * it under the terms of the GNU General Public License as published by
10 : : * the Free Software Foundation; either version 2 of the License, or
11 : : * (at your option) any later version.
12 : : *
13 : : * This is distributed in the hope that it will be useful,
14 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 : : * GNU General Public License for more details.
17 : : *
18 : : * You should have received a copy of the GNU General Public License
19 : : * along with this program. If not, see <https://www.gnu.org/licenses/>.
20 : : */
21 : :
22 : : #include <config.h>
23 : : #include <compat.h>
24 : :
25 : : #include <errno.h>
26 : : #include <string.h>
27 : : #include <unistd.h>
28 : : #include <stdarg.h>
29 : : #include <stdlib.h>
30 : : #include <stdio.h>
31 : :
32 : : #include <dpkg/macros.h>
33 : : #include <dpkg/i18n.h>
34 : : #include <dpkg/progname.h>
35 : : #include <dpkg/color.h>
36 : : #include <dpkg/ehandle.h>
37 : :
38 : : /* Incremented when we do some kind of generally necessary operation,
39 : : * so that loops &c know to quit if we take an error exit. Decremented
40 : : * again afterwards. */
41 : : volatile int onerr_abort = 0;
42 : :
43 : : #define NCALLS 2
44 : :
45 : : struct cleanup_entry {
46 : : struct cleanup_entry *next;
47 : : struct {
48 : : int mask;
49 : : void (*call)(int argc, void **argv);
50 : : } calls[NCALLS];
51 : : int cpmask, cpvalue;
52 : : int argc;
53 : : void *argv[1];
54 : : };
55 : :
56 : : struct error_context {
57 : : struct error_context *next;
58 : :
59 : : enum {
60 : : HANDLER_TYPE_FUNC,
61 : : HANDLER_TYPE_JUMP,
62 : : } handler_type;
63 : :
64 : : union {
65 : : error_handler_func *func;
66 : : jmp_buf *jump;
67 : : } handler;
68 : :
69 : : struct {
70 : : error_printer_func *func;
71 : : const void *data;
72 : : } printer;
73 : :
74 : : struct cleanup_entry *cleanups;
75 : :
76 : : char *errmsg;
77 : : };
78 : :
79 : : static struct error_context *volatile econtext = NULL;
80 : :
81 : : /**
82 : : * Emergency variables.
83 : : *
84 : : * These are used when the system is out of resources, and we want to be able
85 : : * to proceed anyway at least to the point of a controlled shutdown.
86 : : */
87 : : static struct {
88 : : struct cleanup_entry ce;
89 : : void *args[20];
90 : :
91 : : /**
92 : : * Emergency error message buffer.
93 : : *
94 : : * The size is estimated using the following heuristic:
95 : : * - 6x255 For inserted strings (%.255s &c in fmt; and %s with limited
96 : : * length arg).
97 : : * - 1x255 For constant text.
98 : : * - 1x255 For strerror().
99 : : * - And the total doubled just in case.
100 : : */
101 : : char errmsg[4096];
102 : : } emergency;
103 : :
104 : : /**
105 : : * Default fatal error handler.
106 : : *
107 : : * This handler performs all error unwinding for the current context, and
108 : : * terminates the program with an error exit code.
109 : : */
110 : : void
111 : 68 : catch_fatal_error(void)
112 : : {
113 : 68 : pop_error_context(ehflag_bombout);
114 : 68 : exit(2);
115 : : }
116 : :
117 : : void
118 : 68 : print_fatal_error(const char *emsg, const void *data)
119 : : {
120 : 68 : fprintf(stderr, "%s%s:%s %s%s:%s %s\n",
121 : : color_get(COLOR_PROG), dpkg_get_progname(), color_reset(),
122 : : color_get(COLOR_ERROR), _("error"), color_reset(), emsg);
123 : 68 : }
124 : :
125 : : static void
126 : 4 : print_abort_error(const char *etype, const char *emsg)
127 : : {
128 : 4 : fprintf(stderr, _("%s%s%s: %s%s:%s\n %s\n"),
129 : : color_get(COLOR_PROG), dpkg_get_progname(), color_reset(),
130 : : color_get(COLOR_ERROR), etype, color_reset(), emsg);
131 : 4 : }
132 : :
133 : : static struct error_context *
134 : 665 : error_context_new(void)
135 : : {
136 : : struct error_context *necp;
137 : :
138 : 665 : necp = malloc(sizeof(*necp));
139 [ - + ]: 665 : if (!necp)
140 : 0 : ohshite(_("out of memory for new error context"));
141 : 665 : necp->next= econtext;
142 : 665 : necp->cleanups= NULL;
143 : 665 : necp->errmsg = NULL;
144 : 665 : econtext= necp;
145 : :
146 : 665 : return necp;
147 : : }
148 : :
149 : : static void
150 : 1086 : set_error_printer(struct error_context *ec, error_printer_func *func,
151 : : const void *data)
152 : : {
153 : 1086 : ec->printer.func = func;
154 : 1086 : ec->printer.data = data;
155 : 1086 : }
156 : :
157 : : static void
158 : 661 : set_func_handler(struct error_context *ec, error_handler_func *func)
159 : : {
160 : 661 : ec->handler_type = HANDLER_TYPE_FUNC;
161 : 661 : ec->handler.func = func;
162 : 661 : }
163 : :
164 : : static void
165 : 33 : set_jump_handler(struct error_context *ec, jmp_buf *jump)
166 : : {
167 : 33 : ec->handler_type = HANDLER_TYPE_JUMP;
168 : 33 : ec->handler.jump = jump;
169 : 33 : }
170 : :
171 : : static void
172 : 549 : error_context_errmsg_free(struct error_context *ec)
173 : : {
174 [ + - ]: 549 : if (ec->errmsg != emergency.errmsg)
175 : 549 : free(ec->errmsg);
176 : 549 : ec->errmsg = NULL;
177 : 549 : }
178 : :
179 : : static void
180 : 84 : error_context_errmsg_set(struct error_context *ec, char *errmsg)
181 : : {
182 : 84 : error_context_errmsg_free(ec);
183 : 84 : ec->errmsg = errmsg;
184 : 84 : }
185 : :
186 : : static int DPKG_ATTR_VPRINTF(1)
187 : 76 : error_context_errmsg_format(const char *fmt, va_list args)
188 : : {
189 : : va_list args_copy;
190 : 76 : char *errmsg = NULL;
191 : : int rc;
192 : :
193 : 76 : va_copy(args_copy, args);
194 : 76 : rc = vasprintf(&errmsg, fmt, args_copy);
195 : 76 : va_end(args_copy);
196 : :
197 : : /* If the message was constructed successfully, at least we have some
198 : : * error message, which is better than nothing. */
199 [ + - ]: 76 : if (rc >= 0)
200 : 76 : error_context_errmsg_set(econtext, errmsg);
201 : :
202 [ - + ]: 76 : if (rc < 0) {
203 : : /* If there was any error, just use the emergency error message buffer,
204 : : * even if it ends up being truncated, at least we will have a big part
205 : : * of the problem. */
206 : 0 : rc = vsnprintf(emergency.errmsg, sizeof(emergency.errmsg), fmt, args);
207 : :
208 : : /* Return failure only if we get truncated. */
209 [ # # ]: 0 : if (rc >= (int)sizeof(emergency.errmsg))
210 : 0 : rc = -1;
211 : :
212 : 0 : error_context_errmsg_set(econtext, emergency.errmsg);
213 : : }
214 : :
215 : 76 : return rc;
216 : : }
217 : :
218 : : void
219 : 661 : push_error_context_func(error_handler_func *handler,
220 : : error_printer_func *printer,
221 : : const void *printer_data)
222 : : {
223 : : struct error_context *ec;
224 : :
225 : 661 : ec = error_context_new();
226 : 661 : set_error_printer(ec, printer, printer_data);
227 : 661 : set_func_handler(ec, handler);
228 : 661 : onerr_abort = 0;
229 : 661 : }
230 : :
231 : : void
232 : 4 : push_error_context_jump(jmp_buf *jumper,
233 : : error_printer_func *printer,
234 : : const void *printer_data)
235 : : {
236 : : struct error_context *ec;
237 : :
238 : 4 : ec = error_context_new();
239 : 4 : set_error_printer(ec, printer, printer_data);
240 : 4 : set_jump_handler(ec, jumper);
241 : 4 : onerr_abort = 0;
242 : 4 : }
243 : :
244 : : void
245 : 463 : push_error_context(void)
246 : : {
247 : 463 : push_error_context_func(catch_fatal_error, print_fatal_error, NULL);
248 : 463 : }
249 : :
250 : : static void
251 : 1 : print_cleanup_error(const char *emsg, const void *data)
252 : : {
253 : 1 : print_abort_error(_("error while cleaning up"), emsg);
254 : 1 : }
255 : :
256 : : static void
257 : 462 : run_cleanups(struct error_context *econ, int flagsetin)
258 : : {
259 : : static volatile int preventrecurse= 0;
260 : : struct cleanup_entry *volatile cep;
261 : : struct cleanup_entry *ncep;
262 : : struct error_context recurserr, *oldecontext;
263 : : jmp_buf recurse_jump;
264 : : volatile int i, flagset;
265 : :
266 [ + + ]: 462 : if (econ->printer.func)
267 : 70 : econ->printer.func(econ->errmsg, econ->printer.data);
268 : :
269 [ - + ]: 462 : if (++preventrecurse > 3) {
270 : 0 : onerr_abort++;
271 : 0 : print_cleanup_error(_("too many nested errors during error recovery"), NULL);
272 : 0 : flagset= 0;
273 : : } else {
274 : 462 : flagset= flagsetin;
275 : : }
276 : 462 : cep= econ->cleanups;
277 : 462 : oldecontext= econtext;
278 [ + + ]: 491 : while (cep) {
279 [ + + ]: 87 : for (i=0; i<NCALLS; i++) {
280 [ + + + - ]: 58 : if (cep->calls[i].call && cep->calls[i].mask & flagset) {
281 [ + + ]: 29 : if (setjmp(recurse_jump)) {
282 : 1 : run_cleanups(&recurserr, ehflag_bombout | ehflag_recursiveerror);
283 : 1 : error_context_errmsg_free(&recurserr);
284 : : } else {
285 : 29 : memset(&recurserr, 0, sizeof(recurserr));
286 : 29 : set_error_printer(&recurserr, print_cleanup_error, NULL);
287 : 29 : set_jump_handler(&recurserr, &recurse_jump);
288 : 29 : econtext= &recurserr;
289 : 29 : cep->calls[i].call(cep->argc,cep->argv);
290 : : }
291 : 29 : econtext= oldecontext;
292 : : }
293 : : }
294 : 29 : flagset &= cep->cpmask;
295 : 29 : flagset |= cep->cpvalue;
296 : 29 : ncep= cep->next;
297 [ + - ]: 29 : if (cep != &emergency.ce) free(cep);
298 : 29 : cep= ncep;
299 : : }
300 : 462 : preventrecurse--;
301 : 462 : }
302 : :
303 : : /**
304 : : * Push an error cleanup checkpoint.
305 : : *
306 : : * This will arrange that when pop_error_context() is called, all previous
307 : : * cleanups will be executed with
308 : : * flagset = (original_flagset & mask) | value
309 : : * where original_flagset is the argument to pop_error_context() (as
310 : : * modified by any checkpoint which was pushed later).
311 : : */
312 : 0 : void push_checkpoint(int mask, int value) {
313 : : struct cleanup_entry *cep;
314 : : int i;
315 : :
316 : 0 : cep = malloc(sizeof(*cep) + sizeof(void *));
317 [ # # ]: 0 : if (cep == NULL) {
318 : 0 : onerr_abort++;
319 : 0 : ohshite(_("out of memory for new cleanup entry"));
320 : : }
321 : :
322 [ # # ]: 0 : for (i=0; i<NCALLS; i++) { cep->calls[i].call=NULL; cep->calls[i].mask=0; }
323 : 0 : cep->cpmask= mask; cep->cpvalue= value;
324 : 0 : cep->argc= 0; cep->argv[0]= NULL;
325 : 0 : cep->next= econtext->cleanups;
326 : 0 : econtext->cleanups= cep;
327 : 0 : }
328 : :
329 : : static void
330 : 161 : cleanup_entry_new(void (*call1)(int argc, void **argv), int mask1,
331 : : void (*call2)(int argc, void **argv), int mask2,
332 : : unsigned int nargs, va_list vargs)
333 : : {
334 : : struct cleanup_entry *cep;
335 : : void **argv;
336 : 161 : int e = 0;
337 : : va_list args;
338 : :
339 : 161 : onerr_abort++;
340 : :
341 : 161 : cep = malloc(sizeof(*cep) + sizeof(void *) * (nargs + 1));
342 [ - + ]: 161 : if (!cep) {
343 [ # # ]: 0 : if (nargs > array_count(emergency.args))
344 : 0 : ohshite(_("out of memory for new cleanup entry with many arguments"));
345 : 0 : e= errno; cep= &emergency.ce;
346 : : }
347 : 161 : cep->calls[0].call= call1; cep->calls[0].mask= mask1;
348 : 161 : cep->calls[1].call= call2; cep->calls[1].mask= mask2;
349 : 161 : cep->cpmask=~0; cep->cpvalue=0; cep->argc= nargs;
350 : :
351 : 161 : va_copy(args, vargs);
352 : 161 : argv = cep->argv;
353 [ + + ]: 335 : while (nargs-- > 0)
354 : 174 : *argv++ = va_arg(args, void *);
355 : 161 : *argv++ = NULL;
356 : 161 : va_end(args);
357 : 161 : cep->next= econtext->cleanups;
358 : 161 : econtext->cleanups= cep;
359 [ - + ]: 161 : if (cep == &emergency.ce) {
360 : 0 : errno = e;
361 : 0 : ohshite(_("out of memory for new cleanup entry"));
362 : : }
363 : :
364 : 161 : onerr_abort--;
365 : 161 : }
366 : :
367 : : void
368 : 161 : push_cleanup(void (*call)(int argc, void **argv), int mask,
369 : : unsigned int nargs, ...)
370 : : {
371 : : va_list args;
372 : :
373 : 161 : va_start(args, nargs);
374 : 161 : cleanup_entry_new(call, mask, NULL, 0, nargs, args);
375 : 161 : va_end(args);
376 : 161 : }
377 : :
378 : : void
379 : 0 : push_cleanup_fallback(void (*call1)(int argc, void **argv), int mask1,
380 : : void (*call2)(int argc, void **argv), int mask2,
381 : : unsigned int nargs, ...)
382 : : {
383 : : va_list args;
384 : :
385 : 0 : va_start(args, nargs);
386 : 0 : cleanup_entry_new(call1, mask1, call2, mask2, nargs, args);
387 : 0 : va_end(args);
388 : 0 : }
389 : :
390 : 132 : void pop_cleanup(int flagset) {
391 : : struct cleanup_entry *cep;
392 : : int i;
393 : :
394 : 132 : cep= econtext->cleanups;
395 : 132 : econtext->cleanups= cep->next;
396 [ + + ]: 396 : for (i=0; i<NCALLS; i++) {
397 [ + + + + ]: 264 : if (cep->calls[i].call && cep->calls[i].mask & flagset)
398 : 1 : cep->calls[i].call(cep->argc,cep->argv);
399 : : }
400 [ + - ]: 132 : if (cep != &emergency.ce) free(cep);
401 : 132 : }
402 : :
403 : : /**
404 : : * Unwind the current error context by running its registered cleanups.
405 : : */
406 : : void
407 : 461 : pop_error_context(int flagset)
408 : : {
409 : : struct error_context *tecp;
410 : :
411 : 461 : tecp = econtext;
412 : 461 : econtext = tecp->next;
413 : :
414 : : /* If we are cleaning up normally, do not print anything. */
415 [ + + ]: 461 : if (flagset & ehflag_normaltidy)
416 : 392 : set_error_printer(tecp, NULL, NULL);
417 : 461 : run_cleanups(tecp, flagset);
418 : :
419 : 461 : error_context_errmsg_free(tecp);
420 : 461 : free(tecp);
421 : 461 : }
422 : :
423 : : static void DPKG_ATTR_NORET
424 : 76 : run_error_handler(void)
425 : : {
426 [ + + ]: 76 : if (onerr_abort) {
427 : : /* We arrived here due to a fatal error from which we cannot recover,
428 : : * and trying to do so would most probably get us here again. That's
429 : : * why we will not try to do any error unwinding either. We'll just
430 : : * abort. Hopefully the user can fix the situation (out of disk, out
431 : : * of memory, etc). */
432 : 3 : print_abort_error(_("unrecoverable fatal error, aborting"), econtext->errmsg);
433 : 3 : error_context_errmsg_free(econtext);
434 : 3 : exit(2);
435 : : }
436 : :
437 [ - + ]: 73 : if (econtext == NULL) {
438 : 0 : print_abort_error(_("outside error context, aborting"),
439 : 0 : _("an error occurred with no error handling in place"));
440 : 0 : exit(2);
441 [ + + ]: 73 : } else if (econtext->handler_type == HANDLER_TYPE_FUNC) {
442 : 69 : econtext->handler.func();
443 : 0 : internerr("error handler returned unexpectedly!");
444 [ + - ]: 4 : } else if (econtext->handler_type == HANDLER_TYPE_JUMP) {
445 : 4 : longjmp(*econtext->handler.jump, 1);
446 : : } else {
447 : 0 : internerr("unknown error handler type %d!", econtext->handler_type);
448 : : }
449 : : }
450 : :
451 : 68 : void ohshit(const char *fmt, ...) {
452 : : va_list args;
453 : :
454 : 68 : va_start(args, fmt);
455 : 68 : error_context_errmsg_format(fmt, args);
456 : 68 : va_end(args);
457 : :
458 : 68 : run_error_handler();
459 : : }
460 : :
461 : : void
462 : 0 : ohshitv(const char *fmt, va_list args)
463 : : {
464 : 0 : error_context_errmsg_format(fmt, args);
465 : :
466 : 0 : run_error_handler();
467 : : }
468 : :
469 : 8 : void ohshite(const char *fmt, ...) {
470 : : int e, rc;
471 : : va_list args;
472 : :
473 : 8 : e=errno;
474 : :
475 : : /* First we construct the formatted message. */
476 : 8 : va_start(args, fmt);
477 : 8 : rc = error_context_errmsg_format(fmt, args);
478 : 8 : va_end(args);
479 : :
480 : : /* Then if there was no error we append the string for errno. Otherwise
481 : : * we just use the emergency error message buffer, and ignore the errno
482 : : * value, as we will probably have no space left anyway. */
483 [ + - ]: 8 : if (rc > 0) {
484 : 8 : char *errmsg = NULL;
485 : :
486 : 8 : rc = asprintf(&errmsg, "%s: %s", econtext->errmsg, strerror(e));
487 [ + - ]: 8 : if (rc > 0)
488 : 8 : error_context_errmsg_set(econtext, errmsg);
489 : : }
490 : :
491 : 8 : run_error_handler();
492 : : }
493 : :
494 : : void
495 : 0 : do_internerr(const char *file, int line, const char *func, const char *fmt, ...)
496 : : {
497 : : va_list args;
498 : :
499 : 0 : va_start(args, fmt);
500 : 0 : error_context_errmsg_format(fmt, args);
501 : 0 : va_end(args);
502 : :
503 : 0 : fprintf(stderr, "%s%s:%s:%d:%s:%s %s%s:%s %s\n", color_get(COLOR_PROG),
504 : : dpkg_get_progname(), file, line, func, color_reset(),
505 : : color_get(COLOR_ERROR), _("internal error"), color_reset(),
506 : 0 : econtext->errmsg);
507 : :
508 : 0 : error_context_errmsg_free(econtext);
509 : :
510 : 0 : abort();
511 : : }
|