ehandle.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421
  1. /*
  2. * libdpkg - Debian packaging suite library routines
  3. * ehandle.c - error handling
  4. *
  5. * Copyright © 1994,1995 Ian Jackson <ian@chiark.greenend.org.uk>
  6. *
  7. * This is free software; you can redistribute it and/or modify
  8. * it under the terms of the GNU General Public License as published by
  9. * the Free Software Foundation; either version 2 of the License, or
  10. * (at your option) any later version.
  11. *
  12. * This is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License
  18. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  19. */
  20. #include <config.h>
  21. #include <compat.h>
  22. #include <assert.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/ehandle.h>
  33. /* 6x255 for inserted strings (%.255s &c in fmt; and %s with limited length arg)
  34. * 1x255 for constant text
  35. * 1x255 for strerror()
  36. * same again just in case. */
  37. static char errmsg[4096];
  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. #define NCALLS 2
  43. struct cleanup_entry {
  44. struct cleanup_entry *next;
  45. struct {
  46. int mask;
  47. void (*call)(int argc, void **argv);
  48. } calls[NCALLS];
  49. int cpmask, cpvalue;
  50. int argc;
  51. void *argv[1];
  52. };
  53. struct error_context {
  54. struct error_context *next;
  55. enum {
  56. handler_type_func,
  57. handler_type_jump,
  58. } handler_type;
  59. union {
  60. error_handler *func;
  61. jmp_buf *jump;
  62. } handler;
  63. struct cleanup_entry *cleanups;
  64. void (*printerror)(const char *emsg, const char *contextstring);
  65. const char *contextstring;
  66. };
  67. static struct error_context *volatile econtext = NULL;
  68. static struct {
  69. struct cleanup_entry ce;
  70. void *args[20];
  71. } emergency;
  72. static void DPKG_ATTR_NORET
  73. run_error_handler(void)
  74. {
  75. if (onerr_abort) {
  76. /* We arrived here due to a fatal error from which we cannot recover,
  77. * and trying to do so would most probably get us here again. That's
  78. * why we will not try to do any error unwinding either. We'll just
  79. * abort. Hopefully the user can fix the situation (out of disk, out
  80. * of memory, etc). */
  81. fprintf(stderr, _("%s: unrecoverable fatal error, aborting:\n %s\n"),
  82. dpkg_get_progname(), errmsg);
  83. exit(2);
  84. }
  85. if (econtext == NULL) {
  86. fprintf(stderr, _("%s: outside error context, aborting:\n %s\n"),
  87. dpkg_get_progname(), errmsg);
  88. exit(2);
  89. } else if (econtext->handler_type == handler_type_func) {
  90. econtext->handler.func();
  91. internerr("error handler returned unexpectedly!");
  92. } else if (econtext->handler_type == handler_type_jump) {
  93. longjmp(*econtext->handler.jump, 1);
  94. } else {
  95. internerr("unknown error handler type %d!", econtext->handler_type);
  96. }
  97. }
  98. static struct error_context *
  99. error_context_new(void)
  100. {
  101. struct error_context *necp;
  102. necp = malloc(sizeof(struct error_context));
  103. if (!necp)
  104. ohshite(_("out of memory for new error context"));
  105. necp->next= econtext;
  106. necp->cleanups= NULL;
  107. econtext= necp;
  108. return necp;
  109. }
  110. static void
  111. set_error_printer(struct error_context *ec, error_printer *printerror,
  112. const char *contextstring)
  113. {
  114. ec->printerror = printerror;
  115. ec->contextstring = contextstring;
  116. }
  117. static void
  118. set_func_handler(struct error_context *ec, error_handler *func)
  119. {
  120. ec->handler_type = handler_type_func;
  121. ec->handler.func = func;
  122. }
  123. static void
  124. set_jump_handler(struct error_context *ec, jmp_buf *jump)
  125. {
  126. ec->handler_type = handler_type_jump;
  127. ec->handler.jump = jump;
  128. }
  129. void
  130. push_error_context_func(error_handler *func, error_printer *printerror,
  131. const char *contextstring)
  132. {
  133. struct error_context *ec;
  134. ec = error_context_new();
  135. set_error_printer(ec, printerror, contextstring);
  136. set_func_handler(ec, func);
  137. onerr_abort = 0;
  138. }
  139. void
  140. push_error_context_jump(jmp_buf *jump, error_printer *printerror,
  141. const char *contextstring)
  142. {
  143. struct error_context *ec;
  144. ec = error_context_new();
  145. set_error_printer(ec, printerror, contextstring);
  146. set_jump_handler(ec, jump);
  147. onerr_abort = 0;
  148. }
  149. void
  150. push_error_context(void)
  151. {
  152. push_error_context_func(catch_fatal_error, print_fatal_error, NULL);
  153. }
  154. static void
  155. print_cleanup_error(const char *emsg, const char *contextstring)
  156. {
  157. fprintf(stderr, _("%s: error while cleaning up:\n %s\n"),
  158. dpkg_get_progname(), emsg);
  159. }
  160. static void
  161. run_cleanups(struct error_context *econ, int flagsetin)
  162. {
  163. static volatile int preventrecurse= 0;
  164. struct cleanup_entry *volatile cep;
  165. struct cleanup_entry *ncep;
  166. struct error_context recurserr, *oldecontext;
  167. jmp_buf recurse_jump;
  168. volatile int i, flagset;
  169. if (econ->printerror) econ->printerror(errmsg,econ->contextstring);
  170. if (++preventrecurse > 3) {
  171. onerr_abort++;
  172. fprintf(stderr, _("%s: too many nested errors during error recovery!!\n"),
  173. dpkg_get_progname());
  174. flagset= 0;
  175. } else {
  176. flagset= flagsetin;
  177. }
  178. cep= econ->cleanups;
  179. oldecontext= econtext;
  180. while (cep) {
  181. for (i=0; i<NCALLS; i++) {
  182. if (cep->calls[i].call && cep->calls[i].mask & flagset) {
  183. if (setjmp(recurse_jump)) {
  184. run_cleanups(&recurserr, ehflag_bombout | ehflag_recursiveerror);
  185. } else {
  186. recurserr.cleanups= NULL;
  187. recurserr.next= NULL;
  188. set_error_printer(&recurserr, print_cleanup_error, NULL);
  189. set_jump_handler(&recurserr, &recurse_jump);
  190. econtext= &recurserr;
  191. cep->calls[i].call(cep->argc,cep->argv);
  192. }
  193. econtext= oldecontext;
  194. }
  195. }
  196. flagset &= cep->cpmask;
  197. flagset |= cep->cpvalue;
  198. ncep= cep->next;
  199. if (cep != &emergency.ce) free(cep);
  200. cep= ncep;
  201. }
  202. preventrecurse--;
  203. }
  204. /**
  205. * Unwind the current error context by running its registered cleanups.
  206. */
  207. void
  208. pop_error_context(int flagset)
  209. {
  210. struct error_context *tecp;
  211. tecp= econtext;
  212. econtext= tecp->next;
  213. /* If we are cleaning up normally, do not print anything. */
  214. if (flagset & ehflag_normaltidy)
  215. set_error_printer(tecp, NULL, NULL);
  216. run_cleanups(tecp,flagset);
  217. free(tecp);
  218. }
  219. /**
  220. * Push an error cleanup checkpoint.
  221. *
  222. * This will arrange that when pop_error_context() is called, all previous
  223. * cleanups will be executed with
  224. * flagset = (original_flagset & mask) | value
  225. * where original_flagset is the argument to pop_error_context() (as
  226. * modified by any checkpoint which was pushed later).
  227. */
  228. void push_checkpoint(int mask, int value) {
  229. struct cleanup_entry *cep;
  230. int i;
  231. cep = malloc(sizeof(struct cleanup_entry) + sizeof(char *));
  232. if (cep == NULL) {
  233. onerr_abort++;
  234. ohshite(_("out of memory for new cleanup entry"));
  235. }
  236. for (i=0; i<NCALLS; i++) { cep->calls[i].call=NULL; cep->calls[i].mask=0; }
  237. cep->cpmask= mask; cep->cpvalue= value;
  238. cep->argc= 0; cep->argv[0]= NULL;
  239. cep->next= econtext->cleanups;
  240. econtext->cleanups= cep;
  241. }
  242. void push_cleanup(void (*call1)(int argc, void **argv), int mask1,
  243. void (*call2)(int argc, void **argv), int mask2,
  244. unsigned int nargs, ...) {
  245. struct cleanup_entry *cep;
  246. void **argv;
  247. int e = 0;
  248. va_list args;
  249. onerr_abort++;
  250. cep = malloc(sizeof(struct cleanup_entry) + sizeof(char *) * (nargs + 1));
  251. if (!cep) {
  252. if (nargs > array_count(emergency.args))
  253. ohshite(_("out of memory for new cleanup entry with many arguments"));
  254. e= errno; cep= &emergency.ce;
  255. }
  256. cep->calls[0].call= call1; cep->calls[0].mask= mask1;
  257. cep->calls[1].call= call2; cep->calls[1].mask= mask2;
  258. cep->cpmask=~0; cep->cpvalue=0; cep->argc= nargs;
  259. va_start(args, nargs);
  260. argv = cep->argv;
  261. while (nargs-- > 0)
  262. *argv++ = va_arg(args, void *);
  263. *argv++ = NULL;
  264. va_end(args);
  265. cep->next= econtext->cleanups;
  266. econtext->cleanups= cep;
  267. if (cep == &emergency.ce) {
  268. errno = e;
  269. ohshite(_("out of memory for new cleanup entry"));
  270. }
  271. onerr_abort--;
  272. }
  273. void pop_cleanup(int flagset) {
  274. struct cleanup_entry *cep;
  275. int i;
  276. cep= econtext->cleanups;
  277. econtext->cleanups= cep->next;
  278. for (i=0; i<NCALLS; i++) {
  279. if (cep->calls[i].call && cep->calls[i].mask & flagset)
  280. cep->calls[i].call(cep->argc,cep->argv);
  281. }
  282. if (cep != &emergency.ce) free(cep);
  283. }
  284. void ohshit(const char *fmt, ...) {
  285. va_list args;
  286. va_start(args, fmt);
  287. vsnprintf(errmsg, sizeof(errmsg), fmt, args);
  288. va_end(args);
  289. run_error_handler();
  290. }
  291. /**
  292. * Default fatal error handler.
  293. *
  294. * This handler performs all error unwinding for the current context, and
  295. * terminates the program with an error exit code.
  296. */
  297. void
  298. catch_fatal_error(void)
  299. {
  300. pop_error_context(ehflag_bombout);
  301. exit(2);
  302. }
  303. void
  304. print_fatal_error(const char *emsg, const char *contextstring)
  305. {
  306. fprintf(stderr, _("%s: error: %s\n"), dpkg_get_progname(), emsg);
  307. }
  308. void
  309. ohshitv(const char *fmt, va_list args)
  310. {
  311. vsnprintf(errmsg, sizeof(errmsg), fmt, args);
  312. run_error_handler();
  313. }
  314. void ohshite(const char *fmt, ...) {
  315. int e;
  316. va_list args;
  317. char buf[1024];
  318. e=errno;
  319. va_start(args, fmt);
  320. vsnprintf(buf, sizeof(buf), fmt, args);
  321. va_end(args);
  322. snprintf(errmsg, sizeof(errmsg), "%s: %s", buf, strerror(e));
  323. run_error_handler();
  324. }
  325. static int warn_count = 0;
  326. int
  327. warning_get_count(void)
  328. {
  329. return warn_count;
  330. }
  331. void
  332. warningv(const char *fmt, va_list args)
  333. {
  334. char buf[1024];
  335. warn_count++;
  336. vsnprintf(buf, sizeof(buf), fmt, args);
  337. fprintf(stderr, _("%s: warning: %s\n"), dpkg_get_progname(), buf);
  338. }
  339. void
  340. warning(const char *fmt, ...)
  341. {
  342. va_list args;
  343. va_start(args, fmt);
  344. warningv(fmt, args);
  345. va_end(args);
  346. }
  347. void
  348. do_internerr(const char *file, int line, const char *func, const char *fmt, ...)
  349. {
  350. va_list args;
  351. char buf[1024];
  352. va_start(args, fmt);
  353. vsnprintf(buf, sizeof(buf), fmt, args);
  354. va_end(args);
  355. fprintf(stderr, _("%s:%s:%d:%s: internal error: %s\n"),
  356. dpkg_get_progname(), file, line, func, buf);
  357. abort();
  358. }