ehandle.c 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312
  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/dpkg.h>
  32. static const char *errmsg; /* points to errmsgbuf or malloc'd */
  33. static char errmsgbuf[4096];
  34. /* 6x255 for inserted strings (%.255s &c in fmt; also %s with limited length arg)
  35. * 1x255 for constant text
  36. * 1x255 for strerror()
  37. * same again just in case.
  38. */
  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. */
  43. volatile int onerr_abort = 0;
  44. #define NCALLS 2
  45. struct cleanupentry {
  46. struct cleanupentry *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. struct errorcontext {
  56. struct errorcontext *next;
  57. jmp_buf *jbufp;
  58. struct cleanupentry *cleanups;
  59. void (*printerror)(const char *emsg, const char *contextstring);
  60. const char *contextstring;
  61. };
  62. static struct errorcontext *volatile econtext= NULL;
  63. static struct { struct cleanupentry ce; void *args[20]; } emergency;
  64. void set_error_display(error_printer *printerror,
  65. const char *contextstring) {
  66. assert(econtext);
  67. econtext->printerror= printerror;
  68. econtext->contextstring= contextstring;
  69. }
  70. static void DPKG_ATTR_NORET
  71. run_error_handler(void)
  72. {
  73. if (onerr_abort) {
  74. /* We arrived here due to a fatal error from which we cannot recover,
  75. * and trying to do so would most probably get us here again. That's
  76. * why we will not try to do any error unwinding either. We'll just
  77. * abort. Hopefully the user can fix the situation (out of disk, out
  78. * of memory, etc).
  79. */
  80. fprintf(stderr, _("%s: unrecoverable fatal error, aborting:\n %s\n"),
  81. thisname, errmsg);
  82. exit(2);
  83. } else {
  84. longjmp(*econtext->jbufp, 1);
  85. }
  86. }
  87. void push_error_handler(jmp_buf *jbufp,
  88. error_printer *printerror,
  89. const char *contextstring) {
  90. struct errorcontext *necp;
  91. necp= malloc(sizeof(struct errorcontext));
  92. if (!necp) {
  93. int e= errno;
  94. snprintf(errmsgbuf, sizeof(errmsgbuf), "%s%s",
  95. _("out of memory pushing error handler: "), strerror(e));
  96. errmsg= errmsgbuf;
  97. if (econtext)
  98. run_error_handler();
  99. fprintf(stderr, "%s: %s\n", thisname, errmsgbuf); exit(2);
  100. }
  101. necp->next= econtext;
  102. necp->jbufp= jbufp;
  103. necp->cleanups= NULL;
  104. necp->printerror= printerror;
  105. necp->contextstring= contextstring;
  106. econtext= necp;
  107. onerr_abort= 0;
  108. }
  109. static void print_error_cleanup(const char *emsg, const char *contextstring) {
  110. fprintf(stderr, _("%s: error while cleaning up:\n %s\n"),thisname,emsg);
  111. }
  112. static void run_cleanups(struct errorcontext *econ, int flagsetin) {
  113. static volatile int preventrecurse= 0;
  114. struct cleanupentry *volatile cep;
  115. struct cleanupentry *ncep;
  116. struct errorcontext recurserr, *oldecontext;
  117. jmp_buf recurejbuf;
  118. volatile int i, flagset;
  119. if (econ->printerror) econ->printerror(errmsg,econ->contextstring);
  120. if (++preventrecurse > 3) {
  121. onerr_abort++;
  122. fprintf(stderr, _("dpkg: too many nested errors during error recovery !!\n"));
  123. flagset= 0;
  124. } else {
  125. flagset= flagsetin;
  126. }
  127. cep= econ->cleanups;
  128. oldecontext= econtext;
  129. while (cep) {
  130. for (i=0; i<NCALLS; i++) {
  131. if (cep->calls[i].call && cep->calls[i].mask & flagset) {
  132. if (setjmp(recurejbuf)) {
  133. run_cleanups(&recurserr, ehflag_bombout | ehflag_recursiveerror);
  134. } else {
  135. recurserr.jbufp= &recurejbuf;
  136. recurserr.cleanups= NULL;
  137. recurserr.next= NULL;
  138. recurserr.printerror= print_error_cleanup;
  139. recurserr.contextstring= NULL;
  140. econtext= &recurserr;
  141. cep->calls[i].call(cep->argc,cep->argv);
  142. }
  143. econtext= oldecontext;
  144. }
  145. }
  146. flagset &= cep->cpmask;
  147. flagset |= cep->cpvalue;
  148. ncep= cep->next;
  149. if (cep != &emergency.ce) free(cep);
  150. cep= ncep;
  151. }
  152. preventrecurse--;
  153. }
  154. void error_unwind(int flagset) {
  155. struct errorcontext *tecp;
  156. tecp= econtext;
  157. econtext= tecp->next;
  158. run_cleanups(tecp,flagset);
  159. free(tecp);
  160. }
  161. void push_checkpoint(int mask, int value) {
  162. /* This will arrange that when error_unwind() is called,
  163. * all previous cleanups will be executed with
  164. * flagset= (original_flagset & mask) | value
  165. * where original_flagset is the argument to error_unwind
  166. * (as modified by any checkpoint which was pushed later).
  167. */
  168. struct cleanupentry *cep;
  169. int i;
  170. cep= m_malloc(sizeof(struct cleanupentry) + sizeof(char*));
  171. for (i=0; i<NCALLS; i++) { cep->calls[i].call=NULL; cep->calls[i].mask=0; }
  172. cep->cpmask= mask; cep->cpvalue= value;
  173. cep->argc= 0; cep->argv[0]= NULL;
  174. cep->next= econtext->cleanups;
  175. econtext->cleanups= cep;
  176. }
  177. void push_cleanup(void (*call1)(int argc, void **argv), int mask1,
  178. void (*call2)(int argc, void **argv), int mask2,
  179. unsigned int nargs, ...) {
  180. struct cleanupentry *cep;
  181. void **args;
  182. int e;
  183. va_list al;
  184. onerr_abort++;
  185. cep= malloc(sizeof(struct cleanupentry) + sizeof(char*)*(nargs+1));
  186. if (!cep) {
  187. if (nargs > array_count(emergency.args))
  188. ohshite(_("out of memory for new cleanup entry with many arguments"));
  189. e= errno; cep= &emergency.ce;
  190. }
  191. cep->calls[0].call= call1; cep->calls[0].mask= mask1;
  192. cep->calls[1].call= call2; cep->calls[1].mask= mask2;
  193. cep->cpmask=~0; cep->cpvalue=0; cep->argc= nargs;
  194. va_start(al,nargs);
  195. args= cep->argv; while (nargs-- >0) *args++= va_arg(al,void*);
  196. *args++= NULL;
  197. va_end(al);
  198. cep->next= econtext->cleanups;
  199. econtext->cleanups= cep;
  200. if (cep == &emergency.ce) { e= errno; ohshite(_("out of memory for new cleanup entry")); }
  201. onerr_abort--;
  202. }
  203. void pop_cleanup(int flagset) {
  204. struct cleanupentry *cep;
  205. int i;
  206. cep= econtext->cleanups;
  207. econtext->cleanups= cep->next;
  208. for (i=0; i<NCALLS; i++) {
  209. if (cep->calls[i].call && cep->calls[i].mask & flagset)
  210. cep->calls[i].call(cep->argc,cep->argv);
  211. }
  212. if (cep != &emergency.ce) free(cep);
  213. }
  214. void ohshit(const char *fmt, ...) {
  215. va_list al;
  216. va_start(al,fmt);
  217. vsnprintf(errmsgbuf,sizeof(errmsgbuf),fmt,al);
  218. va_end(al);
  219. errmsg= errmsgbuf;
  220. run_error_handler();
  221. }
  222. void print_error_fatal(const char *emsg, const char *contextstring) {
  223. fprintf(stderr, "%s: %s\n",thisname,emsg);
  224. }
  225. void ohshitv(const char *fmt, va_list al) {
  226. vsnprintf(errmsgbuf,sizeof(errmsgbuf),fmt,al);
  227. errmsg= errmsgbuf;
  228. run_error_handler();
  229. }
  230. void ohshite(const char *fmt, ...) {
  231. int e;
  232. va_list al;
  233. char buf[1024];
  234. e=errno;
  235. va_start(al,fmt);
  236. vsnprintf(buf,sizeof(buf),fmt,al);
  237. va_end(al);
  238. snprintf(errmsgbuf,sizeof(errmsgbuf),"%s: %s",buf,strerror(e));
  239. errmsg= errmsgbuf;
  240. run_error_handler();
  241. }
  242. void
  243. warning(const char *fmt, ...)
  244. {
  245. va_list al;
  246. char buf[1024];
  247. va_start(al,fmt);
  248. vsnprintf(buf,sizeof(buf),fmt,al);
  249. va_end(al);
  250. fprintf(stderr, _("%s: warning: %s\n"), thisname, buf);
  251. }
  252. void werr(const char *fn) {
  253. ohshite(_("error writing `%s'"),fn);
  254. }
  255. void
  256. do_internerr(const char *file, int line, const char *fmt, ...)
  257. {
  258. va_list al;
  259. char buf[1024];
  260. va_start(al, fmt);
  261. vsnprintf(buf, sizeof(buf), fmt, al);
  262. va_end(al);
  263. fprintf(stderr, _("%s:%s:%d: internal error: %s\n"),
  264. thisname, file, line, buf);
  265. abort();
  266. }