1 /* error.c: common exception handling for Subversion
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
12 * http://www.apache.org/licenses/LICENSE-2.0
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
20 * ====================================================================
27 #include <apr_general.h>
28 #include <apr_pools.h>
29 #include <apr_strings.h>
33 #ifndef SVN_ERR__TRACING
34 #define SVN_ERR__TRACING
36 #include "svn_cmdline.h"
37 #include "svn_error.h"
38 #include "svn_pools.h"
42 /* XXX FIXME: These should be protected by a thread mutex.
43 svn_error__locate and make_error_internal should cooperate
44 in locking and unlocking it. */
46 /* XXX TODO: Define mutex here #if APR_HAS_THREADS */
47 static const char * volatile error_file = NULL;
48 static long volatile error_line = -1;
50 /* file_line for the non-debug case. */
51 static const char SVN_FILE_LINE_UNDEFINED[] = "svn:<undefined>";
52 #endif /* SVN_DEBUG */
54 #include "svn_private_config.h"
55 #include "private/svn_error_private.h"
59 * Undefine the helpers for creating errors.
61 * *NOTE*: Any use of these functions in any other function may need
62 * to call svn_error__locate() because the macro that would otherwise
63 * do this is being undefined and the filename and line number will
64 * not be properly set in the static error_file and error_line
67 #undef svn_error_create
68 #undef svn_error_createf
69 #undef svn_error_quick_wrap
70 #undef svn_error_wrap_apr
72 /* Note: Although this is a "__" function, it was historically in the
73 * public ABI, so we can never change it or remove its signature, even
74 * though it is now only used in SVN_DEBUG mode. */
76 svn_error__locate(const char *file, long line)
78 #if defined(SVN_DEBUG)
79 /* XXX TODO: Lock mutex here */
86 /* Cleanup function for errors. svn_error_clear () removes this so
87 errors that are properly handled *don't* hit this code. */
88 #if defined(SVN_DEBUG)
89 static apr_status_t err_abort(void *data)
91 svn_error_t *err = data; /* For easy viewing in a debugger */
92 err = err; /* Fake a use for the variable to avoid compiler warnings */
94 if (!getenv("SVN_DBG_NO_ABORT_ON_ERROR_LEAK"))
102 make_error_internal(apr_status_t apr_err,
106 svn_error_t *new_error;
108 /* Reuse the child's pool, or create our own. */
113 if (apr_pool_create(&pool, NULL))
117 /* Create the new error structure */
118 new_error = apr_pcalloc(pool, sizeof(*new_error));
121 new_error->apr_err = apr_err;
122 new_error->child = child;
123 new_error->pool = pool;
124 #if defined(SVN_DEBUG)
125 new_error->file = error_file;
126 new_error->line = error_line;
127 /* XXX TODO: Unlock mutex here */
130 apr_pool_cleanup_register(pool, new_error,
132 apr_pool_cleanup_null);
140 /*** Creating and destroying errors. ***/
143 svn_error_create(apr_status_t apr_err,
149 err = make_error_internal(apr_err, child);
152 err->message = apr_pstrdup(err->pool, message);
159 svn_error_createf(apr_status_t apr_err,
167 err = make_error_internal(apr_err, child);
170 err->message = apr_pvsprintf(err->pool, fmt, ap);
178 svn_error_wrap_apr(apr_status_t status,
182 svn_error_t *err, *utf8_err;
185 const char *msg_apr, *msg;
187 err = make_error_internal(status, NULL);
191 /* Grab the APR error message. */
192 apr_strerror(status, errbuf, sizeof(errbuf));
193 utf8_err = svn_utf_cstring_to_utf8(&msg_apr, errbuf, err->pool);
196 svn_error_clear(utf8_err);
198 /* Append it to the formatted message. */
200 msg = apr_pvsprintf(err->pool, fmt, ap);
204 err->message = apr_pstrcat(err->pool, msg, ": ", msg_apr, NULL);
217 svn_error_quick_wrap(svn_error_t *child, const char *new_msg)
219 if (child == SVN_NO_ERROR)
222 return svn_error_create(child->apr_err,
227 /* Messages in tracing errors all point to this static string. */
228 static const char error_tracing_link[] = "traced call";
231 svn_error__trace(const char *file, long line, svn_error_t *err)
235 /* We shouldn't even be here, but whatever. Just return the error as-is. */
240 /* Only do the work when an error occurs. */
244 svn_error__locate(file, line);
245 trace = make_error_internal(err->apr_err, err);
246 trace->message = error_tracing_link;
256 svn_error_compose_create(svn_error_t *err1,
261 svn_error_compose(err1,
262 svn_error_quick_wrap(err2,
263 _("Additional errors:")));
266 return err1 ? err1 : err2;
271 svn_error_compose(svn_error_t *chain, svn_error_t *new_err)
273 apr_pool_t *pool = chain->pool;
274 apr_pool_t *oldpool = new_err->pool;
277 chain = chain->child;
279 #if defined(SVN_DEBUG)
280 /* Kill existing handler since the end of the chain is going to change */
281 apr_pool_cleanup_kill(pool, chain, err_abort);
284 /* Copy the new error chain into the old chain's pool. */
287 chain->child = apr_palloc(pool, sizeof(*chain->child));
288 chain = chain->child;
291 chain->message = apr_pstrdup(pool, new_err->message);
293 chain->file = apr_pstrdup(pool, new_err->file);
295 #if defined(SVN_DEBUG)
296 if (! new_err->child)
297 apr_pool_cleanup_kill(oldpool, new_err, err_abort);
299 new_err = new_err->child;
302 #if defined(SVN_DEBUG)
303 apr_pool_cleanup_register(pool, chain,
305 apr_pool_cleanup_null);
308 /* Destroy the new error chain. */
309 svn_pool_destroy(oldpool);
313 svn_error_root_cause(svn_error_t *err)
327 svn_error_find_cause(svn_error_t *err, apr_status_t apr_err)
331 for (child = err; child; child = child->child)
332 if (child->apr_err == apr_err)
339 svn_error_dup(svn_error_t *err)
342 svn_error_t *new_err = NULL, *tmp_err = NULL;
344 if (apr_pool_create(&pool, NULL))
347 for (; err; err = err->child)
351 new_err = apr_palloc(pool, sizeof(*new_err));
356 tmp_err->child = apr_palloc(pool, sizeof(*tmp_err->child));
357 tmp_err = tmp_err->child;
360 tmp_err->pool = pool;
361 if (tmp_err->message)
362 tmp_err->message = apr_pstrdup(pool, tmp_err->message);
364 tmp_err->file = apr_pstrdup(pool, tmp_err->file);
367 #if defined(SVN_DEBUG)
368 apr_pool_cleanup_register(pool, tmp_err,
370 apr_pool_cleanup_null);
377 svn_error_clear(svn_error_t *err)
381 #if defined(SVN_DEBUG)
384 apr_pool_cleanup_kill(err->pool, err, err_abort);
386 svn_pool_destroy(err->pool);
391 svn_error__is_tracing_link(svn_error_t *err)
393 #ifdef SVN_ERR__TRACING
394 /* ### A strcmp()? Really? I think it's the best we can do unless
395 ### we add a boolean field to svn_error_t that's set only for
396 ### these "placeholder error chain" items. Not such a bad idea,
398 return (err && err->message && !strcmp(err->message, error_tracing_link));
405 svn_error_purge_tracing(svn_error_t *err)
407 #ifdef SVN_ERR__TRACING
408 svn_error_t *new_err = NULL, *new_err_leaf = NULL;
415 svn_error_t *tmp_err;
417 /* Skip over any trace-only links. */
418 while (err && svn_error__is_tracing_link(err))
421 /* The link must be a real link in the error chain, otherwise an
422 error chain with trace only links would map into SVN_NO_ERROR. */
424 return svn_error_create(
425 SVN_ERR_ASSERTION_ONLY_TRACING_LINKS,
426 svn_error_compose_create(
427 svn_error__malfunction(TRUE, __FILE__, __LINE__,
428 NULL /* ### say something? */),
432 /* Copy the current error except for its child error pointer
433 into the new error. Share any message and source filename
434 strings from the error. */
435 tmp_err = apr_palloc(err->pool, sizeof(*tmp_err));
437 tmp_err->child = NULL;
439 /* Add a new link to the new chain (creating the chain if necessary). */
443 new_err_leaf = tmp_err;
447 new_err_leaf->child = tmp_err;
448 new_err_leaf = tmp_err;
451 /* Advance to the next link in the original chain. */
456 #else /* SVN_ERR__TRACING */
458 #endif /* SVN_ERR__TRACING */
461 /* ### The logic around omitting (sic) apr_err= in maintainer mode is tightly
462 ### coupled to the current sole caller.*/
464 print_error(svn_error_t *err, FILE *stream, const char *prefix)
467 const char *err_string;
468 svn_error_t *temp_err = NULL; /* ensure initialized even if
470 /* Pretty-print the error */
471 /* Note: we can also log errors here someday. */
474 /* Note: err->file is _not_ in UTF-8, because it's expanded from
475 the __FILE__ preprocessor macro. */
476 const char *file_utf8;
479 && !(temp_err = svn_utf_cstring_to_utf8(&file_utf8, err->file,
481 svn_error_clear(svn_cmdline_fprintf(stream, err->pool,
482 "%s:%ld", err->file, err->line));
485 svn_error_clear(svn_cmdline_fputs(SVN_FILE_LINE_UNDEFINED,
487 svn_error_clear(temp_err);
491 const char *symbolic_name;
492 if (svn_error__is_tracing_link(err))
493 /* Skip it; the error code will be printed by the real link. */
494 svn_error_clear(svn_cmdline_fprintf(stream, err->pool, ",\n"));
495 else if ((symbolic_name = svn_error_symbolic_name(err->apr_err)))
496 svn_error_clear(svn_cmdline_fprintf(stream, err->pool,
497 ": (apr_err=%s)\n", symbolic_name));
499 svn_error_clear(svn_cmdline_fprintf(stream, err->pool,
500 ": (apr_err=%d)\n", err->apr_err));
502 #endif /* SVN_DEBUG */
505 if (svn_error__is_tracing_link(err))
507 /* Skip it. We already printed the file-line coordinates. */
509 /* Only print the same APR error string once. */
510 else if (err->message)
512 svn_error_clear(svn_cmdline_fprintf(stream, err->pool,
514 prefix, err->apr_err, err->message));
518 /* Is this a Subversion-specific error code? */
519 if ((err->apr_err > APR_OS_START_USEERR)
520 && (err->apr_err <= APR_OS_START_CANONERR))
521 err_string = svn_strerror(err->apr_err, errbuf, sizeof(errbuf));
522 /* Otherwise, this must be an APR error code. */
523 else if ((temp_err = svn_utf_cstring_to_utf8
524 (&err_string, apr_strerror(err->apr_err, errbuf,
525 sizeof(errbuf)), err->pool)))
527 svn_error_clear(temp_err);
528 err_string = _("Can't recode error string from APR");
531 svn_error_clear(svn_cmdline_fprintf(stream, err->pool,
533 prefix, err->apr_err, err_string));
538 svn_handle_error(svn_error_t *err, FILE *stream, svn_boolean_t fatal)
540 svn_handle_error2(err, stream, fatal, "svn: ");
544 svn_handle_error2(svn_error_t *err,
549 /* In a long error chain, there may be multiple errors with the same
550 error code and no custom message. We only want to print the
551 default message for that code once; printing it multiple times
552 would add no useful information. The 'empties' array below
553 remembers the codes of empty errors already seen in the chain.
555 We could allocate it in err->pool, but there's no telling how
556 long err will live or how many times it will get handled. So we
559 apr_array_header_t *empties;
560 svn_error_t *tmp_err;
562 /* ### The rest of this file carefully avoids using svn_pool_*(),
563 preferring apr_pool_*() instead. I can't remember why -- it may
564 be an artifact of r843793, or it may be for some deeper reason --
565 but I'm playing it safe and using apr_pool_*() here too. */
566 apr_pool_create(&subpool, err->pool);
567 empties = apr_array_make(subpool, 0, sizeof(apr_status_t));
572 svn_boolean_t printed_already = FALSE;
574 if (! tmp_err->message)
578 for (i = 0; i < empties->nelts; i++)
580 if (tmp_err->apr_err == APR_ARRAY_IDX(empties, i, apr_status_t) )
582 printed_already = TRUE;
588 if (! printed_already)
590 print_error(tmp_err, stream, prefix);
591 if (! tmp_err->message)
593 APR_ARRAY_PUSH(empties, apr_status_t) = tmp_err->apr_err;
597 tmp_err = tmp_err->child;
600 svn_pool_destroy(subpool);
605 /* Avoid abort()s in maintainer mode. */
606 svn_error_clear(err);
608 /* We exit(1) here instead of abort()ing so that atexit handlers
616 svn_handle_warning(FILE *stream, svn_error_t *err)
618 svn_handle_warning2(stream, err, "svn: ");
622 svn_handle_warning2(FILE *stream, svn_error_t *err, const char *prefix)
626 svn_error_clear(svn_cmdline_fprintf
628 _("%swarning: W%06d: %s\n"),
629 prefix, err->apr_err,
630 svn_err_best_message(err, buf, sizeof(buf))));
635 svn_err_best_message(svn_error_t *err, char *buf, apr_size_t bufsize)
637 /* Skip over any trace records. */
638 while (svn_error__is_tracing_link(err))
643 return svn_strerror(err->apr_err, buf, bufsize);
647 /* svn_strerror() and helpers */
649 /* Duplicate of the same typedef in tests/libsvn_subr/error-code-test.c */
650 typedef struct err_defn {
651 svn_errno_t errcode; /* 160004 */
652 const char *errname; /* SVN_ERR_FS_CORRUPT */
653 const char *errdesc; /* default message */
656 /* To understand what is going on here, read svn_error_codes.h. */
657 #define SVN_ERROR_BUILD_ARRAY
658 #include "svn_error_codes.h"
661 svn_strerror(apr_status_t statcode, char *buf, apr_size_t bufsize)
663 const err_defn *defn;
665 for (defn = error_table; defn->errdesc != NULL; ++defn)
666 if (defn->errcode == (svn_errno_t)statcode)
668 apr_cpystrn(buf, _(defn->errdesc), bufsize);
672 return apr_strerror(statcode, buf, bufsize);
676 svn_error_symbolic_name(apr_status_t statcode)
678 const err_defn *defn;
680 for (defn = error_table; defn->errdesc != NULL; ++defn)
681 if (defn->errcode == (svn_errno_t)statcode)
682 return defn->errname;
684 /* "No error" is not in error_table. */
685 if (statcode == SVN_NO_ERROR)
686 return "SVN_NO_ERROR";
696 svn_error_raise_on_malfunction(svn_boolean_t can_return,
697 const char *file, int line,
701 abort(); /* Nothing else we can do as a library */
703 /* The filename and line number of the error source needs to be set
704 here because svn_error_createf() is not the macro defined in
705 svn_error.h but the real function. */
706 svn_error__locate(file, line);
709 return svn_error_createf(SVN_ERR_ASSERTION_FAIL, NULL,
710 _("In file '%s' line %d: assertion failed (%s)"),
713 return svn_error_createf(SVN_ERR_ASSERTION_FAIL, NULL,
714 _("In file '%s' line %d: internal malfunction"),
719 svn_error_abort_on_malfunction(svn_boolean_t can_return,
720 const char *file, int line,
723 svn_error_t *err = svn_error_raise_on_malfunction(TRUE, file, line, expr);
725 svn_handle_error2(err, stderr, FALSE, "svn: ");
727 return err; /* Not reached. */
730 /* The current handler for reporting malfunctions, and its default setting. */
731 static svn_error_malfunction_handler_t malfunction_handler
732 = svn_error_abort_on_malfunction;
734 svn_error_malfunction_handler_t
735 svn_error_set_malfunction_handler(svn_error_malfunction_handler_t func)
737 svn_error_malfunction_handler_t old_malfunction_handler
738 = malfunction_handler;
740 malfunction_handler = func;
741 return old_malfunction_handler;
744 /* Note: Although this is a "__" function, it is in the public ABI, so
745 * we can never remove it or change its signature. */
747 svn_error__malfunction(svn_boolean_t can_return,
748 const char *file, int line,
751 return malfunction_handler(can_return, file, line, expr);
758 svn_error__wrap_zlib(int zerr, const char *function, const char *message)
769 status = SVN_ERR_STREAM_MALFORMED_DATA;
770 zmsg = _("stream error");
775 zmsg = _("out of memory");
780 zmsg = _("buffer error");
783 case Z_VERSION_ERROR:
784 status = SVN_ERR_STREAM_UNRECOGNIZED_DATA;
785 zmsg = _("version error");
789 status = SVN_ERR_STREAM_MALFORMED_DATA;
790 zmsg = _("corrupt data");
794 status = SVN_ERR_STREAM_UNRECOGNIZED_DATA;
795 zmsg = _("unknown error");
800 return svn_error_createf(status, NULL, "zlib (%s): %s: %s", function,
803 return svn_error_createf(status, NULL, "zlib (%s): %s", function, zmsg);