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
catch_fatal_error(void)111 catch_fatal_error(void)
112 {
113   pop_error_context(ehflag_bombout);
114   exit(2);
115 }
116 
117 void
print_fatal_error(const char * emsg,const void * data)118 print_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 
125 static void
print_abort_error(const char * etype,const char * emsg)126 print_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 
133 static struct error_context *
error_context_new(void)134 error_context_new(void)
135 {
136   struct error_context *necp;
137 
138   necp = malloc(sizeof(*necp));
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 
149 static void
set_error_printer(struct error_context * ec,error_printer_func * func,const void * data)150 set_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 
157 static void
set_func_handler(struct error_context * ec,error_handler_func * func)158 set_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 
164 static void
set_jump_handler(struct error_context * ec,jmp_buf * jump)165 set_jump_handler(struct error_context *ec, jmp_buf *jump)
166 {
167   ec->handler_type = HANDLER_TYPE_JUMP;
168   ec->handler.jump = jump;
169 }
170 
171 static void
error_context_errmsg_free(struct error_context * ec)172 error_context_errmsg_free(struct error_context *ec)
173 {
174   if (ec->errmsg != emergency.errmsg)
175     free(ec->errmsg);
176 }
177 
178 static void
error_context_errmsg_set(struct error_context * ec,char * errmsg)179 error_context_errmsg_set(struct error_context *ec, char *errmsg)
180 {
181   error_context_errmsg_free(ec);
182   ec->errmsg = errmsg;
183 }
184 
185 static int DPKG_ATTR_VPRINTF(1)
error_context_errmsg_format(const char * fmt,va_list args)186 error_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_copy);
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 
217 void
push_error_context_func(error_handler_func * handler,error_printer_func * printer,const void * printer_data)218 push_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 
230 void
push_error_context_jump(jmp_buf * jumper,error_printer_func * printer,const void * printer_data)231 push_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 
243 void
push_error_context(void)244 push_error_context(void)
245 {
246   push_error_context_func(catch_fatal_error, print_fatal_error, NULL);
247 }
248 
249 static void
print_cleanup_error(const char * emsg,const void * data)250 print_cleanup_error(const char *emsg, const void *data)
251 {
252   print_abort_error(_("error while cleaning up"), emsg);
253 }
254 
255 static void
run_cleanups(struct error_context * econ,int flagsetin)256 run_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  */
push_checkpoint(int mask,int value)310 void push_checkpoint(int mask, int value) {
311   struct cleanup_entry *cep;
312   int i;
313 
314   cep = malloc(sizeof(*cep) + sizeof(void *));
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 
327 static void
cleanup_entry_new(void (* call1)(int argc,void ** argv),int mask1,void (* call2)(int argc,void ** argv),int mask2,unsigned int nargs,va_list vargs)328 cleanup_entry_new(void (*call1)(int argc, void **argv), int mask1,
329                   void (*call2)(int argc, void **argv), int mask2,
330                   unsigned int nargs, va_list vargs)
331 {
332   struct cleanup_entry *cep;
333   void **argv;
334   int e = 0;
335   va_list args;
336 
337   onerr_abort++;
338 
339   cep = malloc(sizeof(*cep) + sizeof(void *) * (nargs + 1));
340   if (!cep) {
341     if (nargs > array_count(emergency.args))
342       ohshite(_("out of memory for new cleanup entry with many arguments"));
343     e= errno; cep= &emergency.ce;
344   }
345   cep->calls[0].call= call1; cep->calls[0].mask= mask1;
346   cep->calls[1].call= call2; cep->calls[1].mask= mask2;
347   cep->cpmask=~0; cep->cpvalue=0; cep->argc= nargs;
348 
349   va_copy(args, vargs);
350   argv = cep->argv;
351   while (nargs-- > 0)
352     *argv++ = va_arg(args, void *);
353   *argv++ = NULL;
354   va_end(args);
355   cep->next= econtext->cleanups;
356   econtext->cleanups= cep;
357   if (cep == &emergency.ce) {
358     errno = e;
359     ohshite(_("out of memory for new cleanup entry"));
360   }
361 
362   onerr_abort--;
363 }
364 
365 void
push_cleanup(void (* call)(int argc,void ** argv),int mask,unsigned int nargs,...)366 push_cleanup(void (*call)(int argc, void **argv), int mask,
367              unsigned int nargs, ...)
368 {
369   va_list args;
370 
371   va_start(args, nargs);
372   cleanup_entry_new(call, mask, NULL, 0, nargs, args);
373   va_end(args);
374 }
375 
376 void
push_cleanup_fallback(void (* call1)(int argc,void ** argv),int mask1,void (* call2)(int argc,void ** argv),int mask2,unsigned int nargs,...)377 push_cleanup_fallback(void (*call1)(int argc, void **argv), int mask1,
378                       void (*call2)(int argc, void **argv), int mask2,
379                       unsigned int nargs, ...)
380 {
381   va_list args;
382 
383   va_start(args, nargs);
384   cleanup_entry_new(call1, mask1, call2, mask2, nargs, args);
385   va_end(args);
386 }
387 
pop_cleanup(int flagset)388 void pop_cleanup(int flagset) {
389   struct cleanup_entry *cep;
390   int i;
391 
392   cep= econtext->cleanups;
393   econtext->cleanups= cep->next;
394   for (i=0; i<NCALLS; i++) {
395     if (cep->calls[i].call && cep->calls[i].mask & flagset)
396       cep->calls[i].call(cep->argc,cep->argv);
397   }
398   if (cep != &emergency.ce) free(cep);
399 }
400 
401 /**
402  * Unwind the current error context by running its registered cleanups.
403  */
404 void
pop_error_context(int flagset)405 pop_error_context(int flagset)
406 {
407   struct error_context *tecp;
408 
409   tecp = econtext;
410   econtext = tecp->next;
411 
412   /* If we are cleaning up normally, do not print anything. */
413   if (flagset & ehflag_normaltidy)
414     set_error_printer(tecp, NULL, NULL);
415   run_cleanups(tecp, flagset);
416 
417   error_context_errmsg_free(tecp);
418   free(tecp);
419 }
420 
421 static void DPKG_ATTR_NORET
run_error_handler(void)422 run_error_handler(void)
423 {
424   if (onerr_abort) {
425     /* We arrived here due to a fatal error from which we cannot recover,
426      * and trying to do so would most probably get us here again. That's
427      * why we will not try to do any error unwinding either. We'll just
428      * abort. Hopefully the user can fix the situation (out of disk, out
429      * of memory, etc). */
430     print_abort_error(_("unrecoverable fatal error, aborting"), econtext->errmsg);
431     exit(2);
432   }
433 
434   if (econtext == NULL) {
435     print_abort_error(_("outside error context, aborting"), econtext->errmsg);
436     exit(2);
437   } else if (econtext->handler_type == HANDLER_TYPE_FUNC) {
438     econtext->handler.func();
439     internerr("error handler returned unexpectedly!");
440   } else if (econtext->handler_type == HANDLER_TYPE_JUMP) {
441     longjmp(*econtext->handler.jump, 1);
442   } else {
443     internerr("unknown error handler type %d!", econtext->handler_type);
444   }
445 }
446 
ohshit(const char * fmt,...)447 void ohshit(const char *fmt, ...) {
448   va_list args;
449 
450   va_start(args, fmt);
451   error_context_errmsg_format(fmt, args);
452   va_end(args);
453 
454   run_error_handler();
455 }
456 
457 void
ohshitv(const char * fmt,va_list args)458 ohshitv(const char *fmt, va_list args)
459 {
460   error_context_errmsg_format(fmt, args);
461 
462   run_error_handler();
463 }
464 
ohshite(const char * fmt,...)465 void ohshite(const char *fmt, ...) {
466   int e, rc;
467   va_list args;
468 
469   e=errno;
470 
471   /* First we construct the formatted message. */
472   va_start(args, fmt);
473   rc = error_context_errmsg_format(fmt, args);
474   va_end(args);
475 
476   /* Then if there was no error we append the string for errno. Otherwise
477    * we just use the emergency error message buffer, and ignore the errno
478    * value, as we will probably have no space left anyway. */
479   if (rc > 0) {
480     char *errmsg = NULL;
481 
482     rc = asprintf(&errmsg, "%s: %s", econtext->errmsg, strerror(e));
483     if (rc > 0)
484       error_context_errmsg_set(econtext, errmsg);
485   }
486 
487   run_error_handler();
488 }
489 
490 void
do_internerr(const char * file,int line,const char * func,const char * fmt,...)491 do_internerr(const char *file, int line, const char *func, const char *fmt, ...)
492 {
493   va_list args;
494 
495   va_start(args, fmt);
496   error_context_errmsg_format(fmt, args);
497   va_end(args);
498 
499   fprintf(stderr, "%s%s:%s:%d:%s:%s %s%s:%s %s\n", color_get(COLOR_PROG),
500           dpkg_get_progname(), file, line, func, color_reset(),
501           color_get(COLOR_ERROR), _("internal error"), color_reset(),
502           econtext->errmsg);
503 
504   abort();
505 }
506