PageRenderTime 50ms CodeModel.GetById 21ms RepoModel.GetById 1ms app.codeStats 0ms

/branches/ignore-mergeinfo/subversion/libsvn_subr/error.c

#
C | 662 lines | 460 code | 107 blank | 95 comment | 58 complexity | 14f626cc850d9b8e9cb9b1a26a85d95d MD5 | raw file
Possible License(s): Apache-2.0, CC-BY-SA-3.0, BSD-3-Clause, LGPL-2.0
  1. /* error.c: common exception handling for Subversion
  2. *
  3. * ====================================================================
  4. * Licensed to the Apache Software Foundation (ASF) under one
  5. * or more contributor license agreements. See the NOTICE file
  6. * distributed with this work for additional information
  7. * regarding copyright ownership. The ASF licenses this file
  8. * to you under the Apache License, Version 2.0 (the
  9. * "License"); you may not use this file except in compliance
  10. * with the License. You may obtain a copy of the License at
  11. *
  12. * http://www.apache.org/licenses/LICENSE-2.0
  13. *
  14. * Unless required by applicable law or agreed to in writing,
  15. * software distributed under the License is distributed on an
  16. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  17. * KIND, either express or implied. See the License for the
  18. * specific language governing permissions and limitations
  19. * under the License.
  20. * ====================================================================
  21. */
  22. #include <stdarg.h>
  23. #include <apr_general.h>
  24. #include <apr_pools.h>
  25. #include <apr_strings.h>
  26. #include "svn_cmdline.h"
  27. #include "svn_error.h"
  28. #include "svn_pools.h"
  29. #include "svn_utf.h"
  30. #ifdef SVN_DEBUG
  31. /* XXX FIXME: These should be protected by a thread mutex.
  32. svn_error__locate and make_error_internal should cooperate
  33. in locking and unlocking it. */
  34. /* XXX TODO: Define mutex here #if APR_HAS_THREADS */
  35. static const char * volatile error_file = NULL;
  36. static long volatile error_line = -1;
  37. /* file_line for the non-debug case. */
  38. static const char SVN_FILE_LINE_UNDEFINED[] = "svn:<undefined>";
  39. #endif /* SVN_DEBUG */
  40. #include "svn_private_config.h"
  41. #include "private/svn_error_private.h"
  42. /*
  43. * Undefine the helpers for creating errors.
  44. *
  45. * *NOTE*: Any use of these functions in any other function may need
  46. * to call svn_error__locate() because the macro that would otherwise
  47. * do this is being undefined and the filename and line number will
  48. * not be properly set in the static error_file and error_line
  49. * variables.
  50. */
  51. #undef svn_error_create
  52. #undef svn_error_createf
  53. #undef svn_error_quick_wrap
  54. #undef svn_error_wrap_apr
  55. /* Note: This function was historically in the public API, so we need
  56. * to define it even when !SVN_DEBUG. */
  57. void
  58. svn_error__locate(const char *file, long line)
  59. {
  60. #if defined(SVN_DEBUG)
  61. /* XXX TODO: Lock mutex here */
  62. error_file = file;
  63. error_line = line;
  64. #endif
  65. }
  66. /* Cleanup function for errors. svn_error_clear () removes this so
  67. errors that are properly handled *don't* hit this code. */
  68. #if defined(SVN_DEBUG)
  69. static apr_status_t err_abort(void *data)
  70. {
  71. svn_error_t *err = data; /* For easy viewing in a debugger */
  72. err = err; /* Fake a use for the variable to avoid compiler warnings */
  73. if (!getenv("SVN_DBG_NO_ABORT_ON_ERROR_LEAK"))
  74. abort();
  75. return APR_SUCCESS;
  76. }
  77. #endif
  78. static svn_error_t *
  79. make_error_internal(apr_status_t apr_err,
  80. svn_error_t *child)
  81. {
  82. apr_pool_t *pool;
  83. svn_error_t *new_error;
  84. /* Reuse the child's pool, or create our own. */
  85. if (child)
  86. pool = child->pool;
  87. else
  88. {
  89. if (apr_pool_create(&pool, NULL))
  90. abort();
  91. }
  92. /* Create the new error structure */
  93. new_error = apr_pcalloc(pool, sizeof(*new_error));
  94. /* Fill 'er up. */
  95. new_error->apr_err = apr_err;
  96. new_error->child = child;
  97. new_error->pool = pool;
  98. #if defined(SVN_DEBUG)
  99. new_error->file = error_file;
  100. new_error->line = error_line;
  101. /* XXX TODO: Unlock mutex here */
  102. if (! child)
  103. apr_pool_cleanup_register(pool, new_error,
  104. err_abort,
  105. apr_pool_cleanup_null);
  106. #endif
  107. return new_error;
  108. }
  109. /*** Creating and destroying errors. ***/
  110. svn_error_t *
  111. svn_error_create(apr_status_t apr_err,
  112. svn_error_t *child,
  113. const char *message)
  114. {
  115. svn_error_t *err;
  116. err = make_error_internal(apr_err, child);
  117. if (message)
  118. err->message = apr_pstrdup(err->pool, message);
  119. return err;
  120. }
  121. svn_error_t *
  122. svn_error_createf(apr_status_t apr_err,
  123. svn_error_t *child,
  124. const char *fmt,
  125. ...)
  126. {
  127. svn_error_t *err;
  128. va_list ap;
  129. err = make_error_internal(apr_err, child);
  130. va_start(ap, fmt);
  131. err->message = apr_pvsprintf(err->pool, fmt, ap);
  132. va_end(ap);
  133. return err;
  134. }
  135. svn_error_t *
  136. svn_error_wrap_apr(apr_status_t status,
  137. const char *fmt,
  138. ...)
  139. {
  140. svn_error_t *err, *utf8_err;
  141. va_list ap;
  142. char errbuf[255];
  143. const char *msg_apr, *msg;
  144. err = make_error_internal(status, NULL);
  145. if (fmt)
  146. {
  147. /* Grab the APR error message. */
  148. apr_strerror(status, errbuf, sizeof(errbuf));
  149. utf8_err = svn_utf_cstring_to_utf8(&msg_apr, errbuf, err->pool);
  150. if (utf8_err)
  151. msg_apr = NULL;
  152. svn_error_clear(utf8_err);
  153. /* Append it to the formatted message. */
  154. va_start(ap, fmt);
  155. msg = apr_pvsprintf(err->pool, fmt, ap);
  156. va_end(ap);
  157. err->message = apr_psprintf(err->pool, "%s%s%s", msg,
  158. (msg_apr) ? ": " : "",
  159. (msg_apr) ? msg_apr : "");
  160. }
  161. return err;
  162. }
  163. svn_error_t *
  164. svn_error_quick_wrap(svn_error_t *child, const char *new_msg)
  165. {
  166. if (child == SVN_NO_ERROR)
  167. return SVN_NO_ERROR;
  168. return svn_error_create(child->apr_err,
  169. child,
  170. new_msg);
  171. }
  172. svn_error_t *
  173. svn_error_compose_create(svn_error_t *err1,
  174. svn_error_t *err2)
  175. {
  176. if (err1 && err2)
  177. {
  178. svn_error_compose(err1, err2);
  179. return err1;
  180. }
  181. return err1 ? err1 : err2;
  182. }
  183. void
  184. svn_error_compose(svn_error_t *chain, svn_error_t *new_err)
  185. {
  186. apr_pool_t *pool = chain->pool;
  187. apr_pool_t *oldpool = new_err->pool;
  188. while (chain->child)
  189. chain = chain->child;
  190. #if defined(SVN_DEBUG)
  191. /* Kill existing handler since the end of the chain is going to change */
  192. apr_pool_cleanup_kill(pool, chain, err_abort);
  193. #endif
  194. /* Copy the new error chain into the old chain's pool. */
  195. while (new_err)
  196. {
  197. chain->child = apr_palloc(pool, sizeof(*chain->child));
  198. chain = chain->child;
  199. *chain = *new_err;
  200. if (chain->message)
  201. chain->message = apr_pstrdup(pool, new_err->message);
  202. chain->pool = pool;
  203. #if defined(SVN_DEBUG)
  204. if (! new_err->child)
  205. apr_pool_cleanup_kill(oldpool, new_err, err_abort);
  206. #endif
  207. new_err = new_err->child;
  208. }
  209. #if defined(SVN_DEBUG)
  210. apr_pool_cleanup_register(pool, chain,
  211. err_abort,
  212. apr_pool_cleanup_null);
  213. #endif
  214. /* Destroy the new error chain. */
  215. svn_pool_destroy(oldpool);
  216. }
  217. svn_error_t *
  218. svn_error_root_cause(svn_error_t *err)
  219. {
  220. while (err)
  221. {
  222. if (err->child)
  223. err = err->child;
  224. else
  225. break;
  226. }
  227. return err;
  228. }
  229. svn_error_t *
  230. svn_error_find_cause(svn_error_t *err, apr_status_t apr_err)
  231. {
  232. svn_error_t *child;
  233. for (child = err; child; child = child->child)
  234. if (child->apr_err == apr_err)
  235. return child;
  236. return SVN_NO_ERROR;
  237. }
  238. svn_error_t *
  239. svn_error_dup(svn_error_t *err)
  240. {
  241. apr_pool_t *pool;
  242. svn_error_t *new_err = NULL, *tmp_err = NULL;
  243. if (apr_pool_create(&pool, NULL))
  244. abort();
  245. for (; err; err = err->child)
  246. {
  247. if (! new_err)
  248. {
  249. new_err = apr_palloc(pool, sizeof(*new_err));
  250. tmp_err = new_err;
  251. }
  252. else
  253. {
  254. tmp_err->child = apr_palloc(pool, sizeof(*tmp_err->child));
  255. tmp_err = tmp_err->child;
  256. }
  257. *tmp_err = *err;
  258. tmp_err->pool = pool;
  259. if (tmp_err->message)
  260. tmp_err->message = apr_pstrdup(pool, tmp_err->message);
  261. }
  262. #if defined(SVN_DEBUG)
  263. apr_pool_cleanup_register(pool, tmp_err,
  264. err_abort,
  265. apr_pool_cleanup_null);
  266. #endif
  267. return new_err;
  268. }
  269. void
  270. svn_error_clear(svn_error_t *err)
  271. {
  272. if (err)
  273. {
  274. #if defined(SVN_DEBUG)
  275. while (err->child)
  276. err = err->child;
  277. apr_pool_cleanup_kill(err->pool, err, err_abort);
  278. #endif
  279. svn_pool_destroy(err->pool);
  280. }
  281. }
  282. svn_boolean_t
  283. svn_error__is_tracing_link(svn_error_t *err)
  284. {
  285. #ifdef SVN_ERR__TRACING
  286. /* ### A strcmp()? Really? I think it's the best we can do unless
  287. ### we add a boolean field to svn_error_t that's set only for
  288. ### these "placeholder error chain" items. Not such a bad idea,
  289. ### really... */
  290. return (err && err->message && !strcmp(err->message, SVN_ERR__TRACED));
  291. #else
  292. return FALSE;
  293. #endif
  294. }
  295. svn_error_t *
  296. svn_error_purge_tracing(svn_error_t *err)
  297. {
  298. #ifdef SVN_ERR__TRACING
  299. svn_error_t *new_err = NULL, *new_err_leaf = NULL;
  300. if (! err)
  301. return SVN_NO_ERROR;
  302. do
  303. {
  304. svn_error_t *tmp_err;
  305. /* Skip over any trace-only links. */
  306. while (err && svn_error__is_tracing_link(err))
  307. err = err->child;
  308. /* The link must be a real link in the error chain, otherwise an
  309. error chain with trace only links would map into SVN_NO_ERROR. */
  310. SVN_ERR_ASSERT(err);
  311. /* Copy the current error except for its child error pointer
  312. into the new error. Share any message and source filename
  313. strings from the error. */
  314. tmp_err = apr_palloc(err->pool, sizeof(*tmp_err));
  315. *tmp_err = *err;
  316. tmp_err->child = NULL;
  317. /* Add a new link to the new chain (creating the chain if necessary). */
  318. if (! new_err)
  319. {
  320. new_err = tmp_err;
  321. new_err_leaf = tmp_err;
  322. }
  323. else
  324. {
  325. new_err_leaf->child = tmp_err;
  326. new_err_leaf = tmp_err;
  327. }
  328. /* Advance to the next link in the original chain. */
  329. err = err->child;
  330. } while (err);
  331. return new_err;
  332. #else /* SVN_ERR__TRACING */
  333. return err;
  334. #endif /* SVN_ERR__TRACING */
  335. }
  336. static void
  337. print_error(svn_error_t *err, FILE *stream, const char *prefix)
  338. {
  339. char errbuf[256];
  340. const char *err_string;
  341. svn_error_t *temp_err = NULL; /* ensure initialized even if
  342. err->file == NULL */
  343. /* Pretty-print the error */
  344. /* Note: we can also log errors here someday. */
  345. #ifdef SVN_DEBUG
  346. /* Note: err->file is _not_ in UTF-8, because it's expanded from
  347. the __FILE__ preprocessor macro. */
  348. const char *file_utf8;
  349. if (err->file
  350. && !(temp_err = svn_utf_cstring_to_utf8(&file_utf8, err->file,
  351. err->pool)))
  352. svn_error_clear(svn_cmdline_fprintf(stream, err->pool,
  353. "%s:%ld", err->file, err->line));
  354. else
  355. {
  356. svn_error_clear(svn_cmdline_fputs(SVN_FILE_LINE_UNDEFINED,
  357. stream, err->pool));
  358. svn_error_clear(temp_err);
  359. }
  360. svn_error_clear(svn_cmdline_fprintf(stream, err->pool,
  361. ": (apr_err=%d)\n", err->apr_err));
  362. #endif /* SVN_DEBUG */
  363. /* "traced call" */
  364. if (svn_error__is_tracing_link(err))
  365. {
  366. /* Skip it. We already printed the file-line coordinates. */
  367. }
  368. /* Only print the same APR error string once. */
  369. else if (err->message)
  370. {
  371. svn_error_clear(svn_cmdline_fprintf(stream, err->pool, "%s%s\n",
  372. prefix, err->message));
  373. }
  374. else
  375. {
  376. /* Is this a Subversion-specific error code? */
  377. if ((err->apr_err > APR_OS_START_USEERR)
  378. && (err->apr_err <= APR_OS_START_CANONERR))
  379. err_string = svn_strerror(err->apr_err, errbuf, sizeof(errbuf));
  380. /* Otherwise, this must be an APR error code. */
  381. else if ((temp_err = svn_utf_cstring_to_utf8
  382. (&err_string, apr_strerror(err->apr_err, errbuf,
  383. sizeof(errbuf)), err->pool)))
  384. {
  385. svn_error_clear(temp_err);
  386. err_string = _("Can't recode error string from APR");
  387. }
  388. svn_error_clear(svn_cmdline_fprintf(stream, err->pool,
  389. "%s%s\n", prefix, err_string));
  390. }
  391. }
  392. void
  393. svn_handle_error(svn_error_t *err, FILE *stream, svn_boolean_t fatal)
  394. {
  395. svn_handle_error2(err, stream, fatal, "svn: ");
  396. }
  397. void
  398. svn_handle_error2(svn_error_t *err,
  399. FILE *stream,
  400. svn_boolean_t fatal,
  401. const char *prefix)
  402. {
  403. /* In a long error chain, there may be multiple errors with the same
  404. error code and no custom message. We only want to print the
  405. default message for that code once; printing it multiple times
  406. would add no useful information. The 'empties' array below
  407. remembers the codes of empty errors already seen in the chain.
  408. We could allocate it in err->pool, but there's no telling how
  409. long err will live or how many times it will get handled. So we
  410. use a subpool. */
  411. apr_pool_t *subpool;
  412. apr_array_header_t *empties;
  413. svn_error_t *tmp_err;
  414. /* ### The rest of this file carefully avoids using svn_pool_*(),
  415. preferring apr_pool_*() instead. I can't remember why -- it may
  416. be an artifact of r3719, or it may be for some deeper reason --
  417. but I'm playing it safe and using apr_pool_*() here too. */
  418. apr_pool_create(&subpool, err->pool);
  419. empties = apr_array_make(subpool, 0, sizeof(apr_status_t));
  420. tmp_err = err;
  421. while (tmp_err)
  422. {
  423. svn_boolean_t printed_already = FALSE;
  424. if (! tmp_err->message)
  425. {
  426. int i;
  427. for (i = 0; i < empties->nelts; i++)
  428. {
  429. if (tmp_err->apr_err == APR_ARRAY_IDX(empties, i, apr_status_t) )
  430. {
  431. printed_already = TRUE;
  432. break;
  433. }
  434. }
  435. }
  436. if (! printed_already)
  437. {
  438. print_error(tmp_err, stream, prefix);
  439. if (! tmp_err->message)
  440. {
  441. APR_ARRAY_PUSH(empties, apr_status_t) = tmp_err->apr_err;
  442. }
  443. }
  444. tmp_err = tmp_err->child;
  445. }
  446. svn_pool_destroy(subpool);
  447. fflush(stream);
  448. if (fatal)
  449. {
  450. /* Avoid abort()s in maintainer mode. */
  451. svn_error_clear(err);
  452. /* We exit(1) here instead of abort()ing so that atexit handlers
  453. get called. */
  454. exit(EXIT_FAILURE);
  455. }
  456. }
  457. void
  458. svn_handle_warning(FILE *stream, svn_error_t *err)
  459. {
  460. svn_handle_warning2(stream, err, "svn: ");
  461. }
  462. void
  463. svn_handle_warning2(FILE *stream, svn_error_t *err, const char *prefix)
  464. {
  465. char buf[256];
  466. svn_error_clear(svn_cmdline_fprintf
  467. (stream, err->pool,
  468. _("%swarning: %s\n"),
  469. prefix, svn_err_best_message(err, buf, sizeof(buf))));
  470. fflush(stream);
  471. }
  472. const char *
  473. svn_err_best_message(svn_error_t *err, char *buf, apr_size_t bufsize)
  474. {
  475. /* Skip over any trace records. */
  476. while (svn_error__is_tracing_link(err))
  477. err = err->child;
  478. if (err->message)
  479. return err->message;
  480. else
  481. return svn_strerror(err->apr_err, buf, bufsize);
  482. }
  483. /* svn_strerror() and helpers */
  484. typedef struct err_defn {
  485. svn_errno_t errcode;
  486. const char *errdesc;
  487. } err_defn;
  488. /* To understand what is going on here, read svn_error_codes.h. */
  489. #define SVN_ERROR_BUILD_ARRAY
  490. #include "svn_error_codes.h"
  491. char *
  492. svn_strerror(apr_status_t statcode, char *buf, apr_size_t bufsize)
  493. {
  494. const err_defn *defn;
  495. for (defn = error_table; defn->errdesc != NULL; ++defn)
  496. if (defn->errcode == (svn_errno_t)statcode)
  497. {
  498. apr_cpystrn(buf, _(defn->errdesc), bufsize);
  499. return buf;
  500. }
  501. return apr_strerror(statcode, buf, bufsize);
  502. }
  503. svn_error_t *
  504. svn_error_raise_on_malfunction(svn_boolean_t can_return,
  505. const char *file, int line,
  506. const char *expr)
  507. {
  508. if (!can_return)
  509. abort(); /* Nothing else we can do as a library */
  510. /* The filename and line number of the error source needs to be set
  511. here because svn_error_createf() is not the macro defined in
  512. svn_error.h but the real function. */
  513. svn_error__locate(file, line);
  514. if (expr)
  515. return svn_error_createf(SVN_ERR_ASSERTION_FAIL, NULL,
  516. _("In file '%s' line %d: assertion failed (%s)"),
  517. file, line, expr);
  518. else
  519. return svn_error_createf(SVN_ERR_ASSERTION_FAIL, NULL,
  520. _("In file '%s' line %d: internal malfunction"),
  521. file, line);
  522. }
  523. svn_error_t *
  524. svn_error_abort_on_malfunction(svn_boolean_t can_return,
  525. const char *file, int line,
  526. const char *expr)
  527. {
  528. svn_error_t *err = svn_error_raise_on_malfunction(TRUE, file, line, expr);
  529. svn_handle_error2(err, stderr, FALSE, "svn: ");
  530. abort();
  531. return err; /* Not reached. */
  532. }
  533. /* The current handler for reporting malfunctions, and its default setting. */
  534. static svn_error_malfunction_handler_t malfunction_handler
  535. = svn_error_abort_on_malfunction;
  536. svn_error_malfunction_handler_t
  537. svn_error_set_malfunction_handler(svn_error_malfunction_handler_t func)
  538. {
  539. svn_error_malfunction_handler_t old_malfunction_handler
  540. = malfunction_handler;
  541. malfunction_handler = func;
  542. return old_malfunction_handler;
  543. }
  544. svn_error_t *
  545. svn_error__malfunction(svn_boolean_t can_return,
  546. const char *file, int line,
  547. const char *expr)
  548. {
  549. return malfunction_handler(can_return, file, line, expr);
  550. }