dpkg (1.18.25) stretch; urgency=medium
[dpkg] / lib / dpkg / ehandle.c
CommitLineData
1479465f
GJ
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. */
41volatile int onerr_abort = 0;
42
43#define NCALLS 2
44
45struct 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
56struct 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
79static 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 */
87static 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 */
110void
111catch_fatal_error(void)
112{
113 pop_error_context(ehflag_bombout);
114 exit(2);
115}
116
117void
118print_fatal_error(const char *emsg, const void *data)
119{
120 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}
124
125static void
126print_abort_error(const char *etype, const char *emsg)
127{
128 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}
132
133static struct error_context *
134error_context_new(void)
135{
136 struct error_context *necp;
137
138 necp = malloc(sizeof(struct error_context));
139 if (!necp)
140 ohshite(_("out of memory for new error context"));
141 necp->next= econtext;
142 necp->cleanups= NULL;
143 necp->errmsg = NULL;
144 econtext= necp;
145
146 return necp;
147}
148
149static void
150set_error_printer(struct error_context *ec, error_printer_func *func,
151 const void *data)
152{
153 ec->printer.func = func;
154 ec->printer.data = data;
155}
156
157static void
158set_func_handler(struct error_context *ec, error_handler_func *func)
159{
160 ec->handler_type = HANDLER_TYPE_FUNC;
161 ec->handler.func = func;
162}
163
164static void
165set_jump_handler(struct error_context *ec, jmp_buf *jump)
166{
167 ec->handler_type = HANDLER_TYPE_JUMP;
168 ec->handler.jump = jump;
169}
170
171static void
172error_context_errmsg_free(struct error_context *ec)
173{
174 if (ec->errmsg != emergency.errmsg)
175 free(ec->errmsg);
176}
177
178static void
179error_context_errmsg_set(struct error_context *ec, char *errmsg)
180{
181 error_context_errmsg_free(ec);
182 ec->errmsg = errmsg;
183}
184
185static int DPKG_ATTR_VPRINTF(1)
186error_context_errmsg_format(const char *fmt, va_list args)
187{
188 va_list args_copy;
189 char *errmsg = NULL;
190 int rc;
191
192 va_copy(args_copy, args);
193 rc = vasprintf(&errmsg, fmt, args);
194 va_end(args_copy);
195
196 /* If the message was constructed successfully, at least we have some
197 * error message, which is better than nothing. */
198 if (rc >= 0)
199 error_context_errmsg_set(econtext, errmsg);
200
201 if (rc < 0) {
202 /* If there was any error, just use the emergency error message buffer,
203 * even if it ends up being truncated, at least we will have a big part
204 * of the problem. */
205 rc = vsnprintf(emergency.errmsg, sizeof(emergency.errmsg), fmt, args);
206
207 /* Return failure only if we get truncated. */
208 if (rc >= (int)sizeof(emergency.errmsg))
209 rc = -1;
210
211 error_context_errmsg_set(econtext, emergency.errmsg);
212 }
213
214 return rc;
215}
216
217void
218push_error_context_func(error_handler_func *handler,
219 error_printer_func *printer,
220 const void *printer_data)
221{
222 struct error_context *ec;
223
224 ec = error_context_new();
225 set_error_printer(ec, printer, printer_data);
226 set_func_handler(ec, handler);
227 onerr_abort = 0;
228}
229
230void
231push_error_context_jump(jmp_buf *jumper,
232 error_printer_func *printer,
233 const void *printer_data)
234{
235 struct error_context *ec;
236
237 ec = error_context_new();
238 set_error_printer(ec, printer, printer_data);
239 set_jump_handler(ec, jumper);
240 onerr_abort = 0;
241}
242
243void
244push_error_context(void)
245{
246 push_error_context_func(catch_fatal_error, print_fatal_error, NULL);
247}
248
249static void
250print_cleanup_error(const char *emsg, const void *data)
251{
252 print_abort_error(_("error while cleaning up"), emsg);
253}
254
255static void
256run_cleanups(struct error_context *econ, int flagsetin)
257{
258 static volatile int preventrecurse= 0;
259 struct cleanup_entry *volatile cep;
260 struct cleanup_entry *ncep;
261 struct error_context recurserr, *oldecontext;
262 jmp_buf recurse_jump;
263 volatile int i, flagset;
264
265 if (econ->printer.func)
266 econ->printer.func(econ->errmsg, econ->printer.data);
267
268 if (++preventrecurse > 3) {
269 onerr_abort++;
270 print_cleanup_error(_("too many nested errors during error recovery"), NULL);
271 flagset= 0;
272 } else {
273 flagset= flagsetin;
274 }
275 cep= econ->cleanups;
276 oldecontext= econtext;
277 while (cep) {
278 for (i=0; i<NCALLS; i++) {
279 if (cep->calls[i].call && cep->calls[i].mask & flagset) {
280 if (setjmp(recurse_jump)) {
281 run_cleanups(&recurserr, ehflag_bombout | ehflag_recursiveerror);
282 } else {
283 memset(&recurserr, 0, sizeof(recurserr));
284 set_error_printer(&recurserr, print_cleanup_error, NULL);
285 set_jump_handler(&recurserr, &recurse_jump);
286 econtext= &recurserr;
287 cep->calls[i].call(cep->argc,cep->argv);
288 }
289 econtext= oldecontext;
290 }
291 }
292 flagset &= cep->cpmask;
293 flagset |= cep->cpvalue;
294 ncep= cep->next;
295 if (cep != &emergency.ce) free(cep);
296 cep= ncep;
297 }
298 preventrecurse--;
299}
300
301/**
302 * Push an error cleanup checkpoint.
303 *
304 * This will arrange that when pop_error_context() is called, all previous
305 * cleanups will be executed with
306 * flagset = (original_flagset & mask) | value
307 * where original_flagset is the argument to pop_error_context() (as
308 * modified by any checkpoint which was pushed later).
309 */
310void push_checkpoint(int mask, int value) {
311 struct cleanup_entry *cep;
312 int i;
313
314 cep = malloc(sizeof(struct cleanup_entry) + sizeof(char *));
315 if (cep == NULL) {
316 onerr_abort++;
317 ohshite(_("out of memory for new cleanup entry"));
318 }
319
320 for (i=0; i<NCALLS; i++) { cep->calls[i].call=NULL; cep->calls[i].mask=0; }
321 cep->cpmask= mask; cep->cpvalue= value;
322 cep->argc= 0; cep->argv[0]= NULL;
323 cep->next= econtext->cleanups;
324 econtext->cleanups= cep;
325}
326
327void push_cleanup(void (*call1)(int argc, void **argv), int mask1,
328 void (*call2)(int argc, void **argv), int mask2,
329 unsigned int nargs, ...) {
330 struct cleanup_entry *cep;
331 void **argv;
332 int e = 0;
333 va_list args;
334
335 onerr_abort++;
336
337 cep = malloc(sizeof(struct cleanup_entry) + sizeof(char *) * (nargs + 1));
338 if (!cep) {
339 if (nargs > array_count(emergency.args))
340 ohshite(_("out of memory for new cleanup entry with many arguments"));
341 e= errno; cep= &emergency.ce;
342 }
343 cep->calls[0].call= call1; cep->calls[0].mask= mask1;
344 cep->calls[1].call= call2; cep->calls[1].mask= mask2;
345 cep->cpmask=~0; cep->cpvalue=0; cep->argc= nargs;
346 va_start(args, nargs);
347 argv = cep->argv;
348 while (nargs-- > 0)
349 *argv++ = va_arg(args, void *);
350 *argv++ = NULL;
351 va_end(args);
352 cep->next= econtext->cleanups;
353 econtext->cleanups= cep;
354 if (cep == &emergency.ce) {
355 errno = e;
356 ohshite(_("out of memory for new cleanup entry"));
357 }
358
359 onerr_abort--;
360}
361
362void pop_cleanup(int flagset) {
363 struct cleanup_entry *cep;
364 int i;
365
366 cep= econtext->cleanups;
367 econtext->cleanups= cep->next;
368 for (i=0; i<NCALLS; i++) {
369 if (cep->calls[i].call && cep->calls[i].mask & flagset)
370 cep->calls[i].call(cep->argc,cep->argv);
371 }
372 if (cep != &emergency.ce) free(cep);
373}
374
375/**
376 * Unwind the current error context by running its registered cleanups.
377 */
378void
379pop_error_context(int flagset)
380{
381 struct error_context *tecp;
382
383 tecp = econtext;
384 econtext = tecp->next;
385
386 /* If we are cleaning up normally, do not print anything. */
387 if (flagset & ehflag_normaltidy)
388 set_error_printer(tecp, NULL, NULL);
389 run_cleanups(tecp, flagset);
390
391 error_context_errmsg_free(tecp);
392 free(tecp);
393}
394
395static void DPKG_ATTR_NORET
396run_error_handler(void)
397{
398 if (onerr_abort) {
399 /* We arrived here due to a fatal error from which we cannot recover,
400 * and trying to do so would most probably get us here again. That's
401 * why we will not try to do any error unwinding either. We'll just
402 * abort. Hopefully the user can fix the situation (out of disk, out
403 * of memory, etc). */
404 print_abort_error(_("unrecoverable fatal error, aborting"), econtext->errmsg);
405 exit(2);
406 }
407
408 if (econtext == NULL) {
409 print_abort_error(_("outside error context, aborting"), econtext->errmsg);
410 exit(2);
411 } else if (econtext->handler_type == HANDLER_TYPE_FUNC) {
412 econtext->handler.func();
413 internerr("error handler returned unexpectedly!");
414 } else if (econtext->handler_type == HANDLER_TYPE_JUMP) {
415 longjmp(*econtext->handler.jump, 1);
416 } else {
417 internerr("unknown error handler type %d!", econtext->handler_type);
418 }
419}
420
421void ohshit(const char *fmt, ...) {
422 va_list args;
423
424 va_start(args, fmt);
425 error_context_errmsg_format(fmt, args);
426 va_end(args);
427
428 run_error_handler();
429}
430
431void
432ohshitv(const char *fmt, va_list args)
433{
434 error_context_errmsg_format(fmt, args);
435
436 run_error_handler();
437}
438
439void ohshite(const char *fmt, ...) {
440 int e, rc;
441 va_list args;
442
443 e=errno;
444
445 /* First we construct the formatted message. */
446 va_start(args, fmt);
447 rc = error_context_errmsg_format(fmt, args);
448 va_end(args);
449
450 /* Then if there was no error we append the string for errno. Otherwise
451 * we just use the emergency error message buffer, and ignore the errno
452 * value, as we will probably have no space left anyway. */
453 if (rc > 0) {
454 char *errmsg = NULL;
455
456 rc = asprintf(&errmsg, "%s: %s", econtext->errmsg, strerror(e));
457 if (rc > 0)
458 error_context_errmsg_set(econtext, errmsg);
459 }
460
461 run_error_handler();
462}
463
464void
465do_internerr(const char *file, int line, const char *func, const char *fmt, ...)
466{
467 va_list args;
468
469 va_start(args, fmt);
470 error_context_errmsg_format(fmt, args);
471 va_end(args);
472
473 fprintf(stderr, "%s%s:%s:%d:%s:%s %s%s:%s %s\n", color_get(COLOR_PROG),
474 dpkg_get_progname(), file, line, func, color_reset(),
475 color_get(COLOR_ERROR), _("internal error"), color_reset(),
476 econtext->errmsg);
477
478 abort();
479}