ehandle.c 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403
  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. #include <config.h>
  22. #include <compat.h>
  23. #include <errno.h>
  24. #include <string.h>
  25. #include <unistd.h>
  26. #include <stdarg.h>
  27. #include <stdlib.h>
  28. #include <stdio.h>
  29. #include <dpkg/macros.h>
  30. #include <dpkg/i18n.h>
  31. #include <dpkg/progname.h>
  32. #include <dpkg/color.h>
  33. #include <dpkg/ehandle.h>
  34. /* 6x255 for inserted strings (%.255s &c in fmt; and %s with limited length arg)
  35. * 1x255 for constant text
  36. * 1x255 for strerror()
  37. * same again just in case. */
  38. static char errmsg[4096];
  39. /* Incremented when we do some kind of generally necessary operation,
  40. * so that loops &c know to quit if we take an error exit. Decremented
  41. * again afterwards. */
  42. volatile int onerr_abort = 0;
  43. #define NCALLS 2
  44. struct cleanup_entry {
  45. struct cleanup_entry *next;
  46. struct {
  47. int mask;
  48. void (*call)(int argc, void **argv);
  49. } calls[NCALLS];
  50. int cpmask, cpvalue;
  51. int argc;
  52. void *argv[1];
  53. };
  54. struct error_context {
  55. struct error_context *next;
  56. enum {
  57. HANDLER_TYPE_FUNC,
  58. HANDLER_TYPE_JUMP,
  59. } handler_type;
  60. union {
  61. error_handler_func *func;
  62. jmp_buf *jump;
  63. } handler;
  64. struct {
  65. error_printer_func *func;
  66. const void *data;
  67. } printer;
  68. struct cleanup_entry *cleanups;
  69. };
  70. static struct error_context *volatile econtext = NULL;
  71. static struct {
  72. struct cleanup_entry ce;
  73. void *args[20];
  74. } emergency;
  75. static void DPKG_ATTR_NORET
  76. run_error_handler(void)
  77. {
  78. if (onerr_abort) {
  79. /* We arrived here due to a fatal error from which we cannot recover,
  80. * and trying to do so would most probably get us here again. That's
  81. * why we will not try to do any error unwinding either. We'll just
  82. * abort. Hopefully the user can fix the situation (out of disk, out
  83. * of memory, etc). */
  84. fprintf(stderr, _("%s: unrecoverable fatal error, aborting:\n %s\n"),
  85. dpkg_get_progname(), errmsg);
  86. exit(2);
  87. }
  88. if (econtext == NULL) {
  89. fprintf(stderr, _("%s: outside error context, aborting:\n %s\n"),
  90. dpkg_get_progname(), errmsg);
  91. exit(2);
  92. } else if (econtext->handler_type == HANDLER_TYPE_FUNC) {
  93. econtext->handler.func();
  94. internerr("error handler returned unexpectedly!");
  95. } else if (econtext->handler_type == HANDLER_TYPE_JUMP) {
  96. longjmp(*econtext->handler.jump, 1);
  97. } else {
  98. internerr("unknown error handler type %d!", econtext->handler_type);
  99. }
  100. }
  101. static struct error_context *
  102. error_context_new(void)
  103. {
  104. struct error_context *necp;
  105. necp = malloc(sizeof(struct error_context));
  106. if (!necp)
  107. ohshite(_("out of memory for new error context"));
  108. necp->next= econtext;
  109. necp->cleanups= NULL;
  110. econtext= necp;
  111. return necp;
  112. }
  113. static void
  114. set_error_printer(struct error_context *ec, error_printer_func *func,
  115. const void *data)
  116. {
  117. ec->printer.func = func;
  118. ec->printer.data = data;
  119. }
  120. static void
  121. set_func_handler(struct error_context *ec, error_handler_func *func)
  122. {
  123. ec->handler_type = HANDLER_TYPE_FUNC;
  124. ec->handler.func = func;
  125. }
  126. static void
  127. set_jump_handler(struct error_context *ec, jmp_buf *jump)
  128. {
  129. ec->handler_type = HANDLER_TYPE_JUMP;
  130. ec->handler.jump = jump;
  131. }
  132. void
  133. push_error_context_func(error_handler_func *handler,
  134. error_printer_func *printer,
  135. const void *printer_data)
  136. {
  137. struct error_context *ec;
  138. ec = error_context_new();
  139. set_error_printer(ec, printer, printer_data);
  140. set_func_handler(ec, handler);
  141. onerr_abort = 0;
  142. }
  143. void
  144. push_error_context_jump(jmp_buf *jumper,
  145. error_printer_func *printer,
  146. const void *printer_data)
  147. {
  148. struct error_context *ec;
  149. ec = error_context_new();
  150. set_error_printer(ec, printer, printer_data);
  151. set_jump_handler(ec, jumper);
  152. onerr_abort = 0;
  153. }
  154. void
  155. push_error_context(void)
  156. {
  157. push_error_context_func(catch_fatal_error, print_fatal_error, NULL);
  158. }
  159. static void
  160. print_cleanup_error(const char *emsg, const void *data)
  161. {
  162. fprintf(stderr, _("%s: error while cleaning up:\n %s\n"),
  163. dpkg_get_progname(), emsg);
  164. }
  165. static void
  166. run_cleanups(struct error_context *econ, int flagsetin)
  167. {
  168. static volatile int preventrecurse= 0;
  169. struct cleanup_entry *volatile cep;
  170. struct cleanup_entry *ncep;
  171. struct error_context recurserr, *oldecontext;
  172. jmp_buf recurse_jump;
  173. volatile int i, flagset;
  174. if (econ->printer.func)
  175. econ->printer.func(errmsg, econ->printer.data);
  176. if (++preventrecurse > 3) {
  177. onerr_abort++;
  178. fprintf(stderr, _("%s: too many nested errors during error recovery!!\n"),
  179. dpkg_get_progname());
  180. flagset= 0;
  181. } else {
  182. flagset= flagsetin;
  183. }
  184. cep= econ->cleanups;
  185. oldecontext= econtext;
  186. while (cep) {
  187. for (i=0; i<NCALLS; i++) {
  188. if (cep->calls[i].call && cep->calls[i].mask & flagset) {
  189. if (setjmp(recurse_jump)) {
  190. run_cleanups(&recurserr, ehflag_bombout | ehflag_recursiveerror);
  191. } else {
  192. recurserr.cleanups= NULL;
  193. recurserr.next= NULL;
  194. set_error_printer(&recurserr, print_cleanup_error, NULL);
  195. set_jump_handler(&recurserr, &recurse_jump);
  196. econtext= &recurserr;
  197. cep->calls[i].call(cep->argc,cep->argv);
  198. }
  199. econtext= oldecontext;
  200. }
  201. }
  202. flagset &= cep->cpmask;
  203. flagset |= cep->cpvalue;
  204. ncep= cep->next;
  205. if (cep != &emergency.ce) free(cep);
  206. cep= ncep;
  207. }
  208. preventrecurse--;
  209. }
  210. /**
  211. * Unwind the current error context by running its registered cleanups.
  212. */
  213. void
  214. pop_error_context(int flagset)
  215. {
  216. struct error_context *tecp;
  217. tecp= econtext;
  218. econtext= tecp->next;
  219. /* If we are cleaning up normally, do not print anything. */
  220. if (flagset & ehflag_normaltidy)
  221. set_error_printer(tecp, NULL, NULL);
  222. run_cleanups(tecp,flagset);
  223. free(tecp);
  224. }
  225. /**
  226. * Push an error cleanup checkpoint.
  227. *
  228. * This will arrange that when pop_error_context() is called, all previous
  229. * cleanups will be executed with
  230. * flagset = (original_flagset & mask) | value
  231. * where original_flagset is the argument to pop_error_context() (as
  232. * modified by any checkpoint which was pushed later).
  233. */
  234. void push_checkpoint(int mask, int value) {
  235. struct cleanup_entry *cep;
  236. int i;
  237. cep = malloc(sizeof(struct cleanup_entry) + sizeof(char *));
  238. if (cep == NULL) {
  239. onerr_abort++;
  240. ohshite(_("out of memory for new cleanup entry"));
  241. }
  242. for (i=0; i<NCALLS; i++) { cep->calls[i].call=NULL; cep->calls[i].mask=0; }
  243. cep->cpmask= mask; cep->cpvalue= value;
  244. cep->argc= 0; cep->argv[0]= NULL;
  245. cep->next= econtext->cleanups;
  246. econtext->cleanups= cep;
  247. }
  248. void push_cleanup(void (*call1)(int argc, void **argv), int mask1,
  249. void (*call2)(int argc, void **argv), int mask2,
  250. unsigned int nargs, ...) {
  251. struct cleanup_entry *cep;
  252. void **argv;
  253. int e = 0;
  254. va_list args;
  255. onerr_abort++;
  256. cep = malloc(sizeof(struct cleanup_entry) + sizeof(char *) * (nargs + 1));
  257. if (!cep) {
  258. if (nargs > array_count(emergency.args))
  259. ohshite(_("out of memory for new cleanup entry with many arguments"));
  260. e= errno; cep= &emergency.ce;
  261. }
  262. cep->calls[0].call= call1; cep->calls[0].mask= mask1;
  263. cep->calls[1].call= call2; cep->calls[1].mask= mask2;
  264. cep->cpmask=~0; cep->cpvalue=0; cep->argc= nargs;
  265. va_start(args, nargs);
  266. argv = cep->argv;
  267. while (nargs-- > 0)
  268. *argv++ = va_arg(args, void *);
  269. *argv++ = NULL;
  270. va_end(args);
  271. cep->next= econtext->cleanups;
  272. econtext->cleanups= cep;
  273. if (cep == &emergency.ce) {
  274. errno = e;
  275. ohshite(_("out of memory for new cleanup entry"));
  276. }
  277. onerr_abort--;
  278. }
  279. void pop_cleanup(int flagset) {
  280. struct cleanup_entry *cep;
  281. int i;
  282. cep= econtext->cleanups;
  283. econtext->cleanups= cep->next;
  284. for (i=0; i<NCALLS; i++) {
  285. if (cep->calls[i].call && cep->calls[i].mask & flagset)
  286. cep->calls[i].call(cep->argc,cep->argv);
  287. }
  288. if (cep != &emergency.ce) free(cep);
  289. }
  290. void ohshit(const char *fmt, ...) {
  291. va_list args;
  292. va_start(args, fmt);
  293. vsnprintf(errmsg, sizeof(errmsg), fmt, args);
  294. va_end(args);
  295. run_error_handler();
  296. }
  297. /**
  298. * Default fatal error handler.
  299. *
  300. * This handler performs all error unwinding for the current context, and
  301. * terminates the program with an error exit code.
  302. */
  303. void
  304. catch_fatal_error(void)
  305. {
  306. pop_error_context(ehflag_bombout);
  307. exit(2);
  308. }
  309. void
  310. print_fatal_error(const char *emsg, const void *data)
  311. {
  312. fprintf(stderr, "%s%s:%s %s%s:%s %s\n",
  313. color_get(COLOR_PROG), dpkg_get_progname(), color_reset(),
  314. color_get(COLOR_ERROR), _("error"), color_reset(), emsg);
  315. }
  316. void
  317. ohshitv(const char *fmt, va_list args)
  318. {
  319. vsnprintf(errmsg, sizeof(errmsg), fmt, args);
  320. run_error_handler();
  321. }
  322. void ohshite(const char *fmt, ...) {
  323. int e;
  324. va_list args;
  325. char buf[1024];
  326. e=errno;
  327. va_start(args, fmt);
  328. vsnprintf(buf, sizeof(buf), fmt, args);
  329. va_end(args);
  330. snprintf(errmsg, sizeof(errmsg), "%s: %s", buf, strerror(e));
  331. run_error_handler();
  332. }
  333. void
  334. do_internerr(const char *file, int line, const char *func, const char *fmt, ...)
  335. {
  336. va_list args;
  337. char buf[1024];
  338. va_start(args, fmt);
  339. vsnprintf(buf, sizeof(buf), fmt, args);
  340. va_end(args);
  341. fprintf(stderr, "%s%s:%s:%d:%s:%s %s%s:%s %s\n", color_get(COLOR_PROG),
  342. dpkg_get_progname(), file, line, func, color_reset(),
  343. color_get(COLOR_ERROR), _("internal error"), color_reset(), buf);
  344. abort();
  345. }