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_quick_wrapf
71 #undef svn_error_wrap_apr
73 /* Note: Although this is a "__" function, it was historically in the
74 * public ABI, so we can never change it or remove its signature, even
75 * though it is now only used in SVN_DEBUG mode. */
77 svn_error__locate(const char *file, long line)
79 #if defined(SVN_DEBUG)
80 /* XXX TODO: Lock mutex here */
87 /* Cleanup function for errors. svn_error_clear () removes this so
88 errors that are properly handled *don't* hit this code. */
89 static apr_status_t err_abort(void *data)
91 svn_error_t *err = data; /* For easy viewing in a debugger */
94 if (!getenv("SVN_DBG_NO_ABORT_ON_ERROR_LEAK"))
101 make_error_internal(apr_status_t apr_err,
105 svn_error_t *new_error;
107 /* Reuse the child's pool, or create our own. */
112 pool = svn_pool_create(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,
218 svn_error_quick_wrap(svn_error_t *child, const char *new_msg)
220 if (child == SVN_NO_ERROR)
223 return svn_error_create(child->apr_err,
229 svn_error_quick_wrapf(svn_error_t *child,
236 if (child == SVN_NO_ERROR)
239 err = make_error_internal(child->apr_err, child);
242 err->message = apr_pvsprintf(err->pool, fmt, ap);
248 /* Messages in tracing errors all point to this static string. */
249 static const char error_tracing_link[] = "traced call";
252 svn_error__trace(const char *file, long line, svn_error_t *err)
256 /* We shouldn't even be here, but whatever. Just return the error as-is. */
261 /* Only do the work when an error occurs. */
265 svn_error__locate(file, line);
266 trace = make_error_internal(err->apr_err, err);
267 trace->message = error_tracing_link;
277 svn_error_compose_create(svn_error_t *err1,
282 svn_error_compose(err1,
283 svn_error_create(SVN_ERR_COMPOSED_ERROR, err2, NULL));
286 return err1 ? err1 : err2;
291 svn_error_compose(svn_error_t *chain, svn_error_t *new_err)
293 apr_pool_t *pool = chain->pool;
294 apr_pool_t *oldpool = new_err->pool;
297 chain = chain->child;
299 #if defined(SVN_DEBUG)
300 /* Kill existing handler since the end of the chain is going to change */
301 apr_pool_cleanup_kill(pool, chain, err_abort);
304 /* Copy the new error chain into the old chain's pool. */
307 chain->child = apr_palloc(pool, sizeof(*chain->child));
308 chain = chain->child;
311 chain->message = apr_pstrdup(pool, new_err->message);
313 chain->file = apr_pstrdup(pool, new_err->file);
315 #if defined(SVN_DEBUG)
316 if (! new_err->child)
317 apr_pool_cleanup_kill(oldpool, new_err, err_abort);
319 new_err = new_err->child;
322 #if defined(SVN_DEBUG)
323 apr_pool_cleanup_register(pool, chain,
325 apr_pool_cleanup_null);
328 /* Destroy the new error chain. */
329 svn_pool_destroy(oldpool);
333 svn_error_root_cause(svn_error_t *err)
337 /* I don't think we can change the behavior here, but the additional
338 error chain doesn't define the root cause. Perhaps we should rev
340 if (err->child /*&& err->child->apr_err != SVN_ERR_COMPOSED_ERROR*/)
350 svn_error_find_cause(svn_error_t *err, apr_status_t apr_err)
354 for (child = err; child; child = child->child)
355 if (child->apr_err == apr_err)
362 svn_error_dup(const svn_error_t *err)
365 svn_error_t *new_err = NULL, *tmp_err = NULL;
370 pool = svn_pool_create(NULL);
374 for (; err; err = err->child)
378 new_err = apr_palloc(pool, sizeof(*new_err));
383 tmp_err->child = apr_palloc(pool, sizeof(*tmp_err->child));
384 tmp_err = tmp_err->child;
387 tmp_err->pool = pool;
388 if (tmp_err->message)
389 tmp_err->message = apr_pstrdup(pool, tmp_err->message);
391 tmp_err->file = apr_pstrdup(pool, tmp_err->file);
394 #if defined(SVN_DEBUG)
395 apr_pool_cleanup_register(pool, tmp_err,
397 apr_pool_cleanup_null);
404 svn_error_clear(svn_error_t *err)
408 #if defined(SVN_DEBUG)
411 apr_pool_cleanup_kill(err->pool, err, err_abort);
413 svn_pool_destroy(err->pool);
418 svn_error__is_tracing_link(const svn_error_t *err)
420 #ifdef SVN_ERR__TRACING
421 /* ### A strcmp()? Really? I think it's the best we can do unless
422 ### we add a boolean field to svn_error_t that's set only for
423 ### these "placeholder error chain" items. Not such a bad idea,
425 return (err && err->message && !strcmp(err->message, error_tracing_link));
432 svn_error_purge_tracing(svn_error_t *err)
434 #ifdef SVN_ERR__TRACING
435 svn_error_t *new_err = NULL, *new_err_leaf = NULL;
442 svn_error_t *tmp_err;
444 /* Skip over any trace-only links. */
445 while (err && svn_error__is_tracing_link(err))
448 /* The link must be a real link in the error chain, otherwise an
449 error chain with trace only links would map into SVN_NO_ERROR. */
451 return svn_error_create(
452 SVN_ERR_ASSERTION_ONLY_TRACING_LINKS,
453 svn_error__malfunction(TRUE, __FILE__, __LINE__,
454 NULL /* ### say something? */),
457 /* Copy the current error except for its child error pointer
458 into the new error. Share any message and source filename
459 strings from the error. */
460 tmp_err = apr_palloc(err->pool, sizeof(*tmp_err));
462 tmp_err->child = NULL;
464 /* Add a new link to the new chain (creating the chain if necessary). */
468 new_err_leaf = tmp_err;
472 new_err_leaf->child = tmp_err;
473 new_err_leaf = tmp_err;
476 /* Advance to the next link in the original chain. */
481 #else /* SVN_ERR__TRACING */
483 #endif /* SVN_ERR__TRACING */
486 /* ### The logic around omitting (sic) apr_err= in maintainer mode is tightly
487 ### coupled to the current sole caller.*/
489 print_error(svn_error_t *err, FILE *stream, const char *prefix)
492 const char *err_string;
493 svn_error_t *temp_err = NULL; /* ensure initialized even if
495 /* Pretty-print the error */
496 /* Note: we can also log errors here someday. */
499 /* Note: err->file is _not_ in UTF-8, because it's expanded from
500 the __FILE__ preprocessor macro. */
501 const char *file_utf8;
504 && !(temp_err = svn_utf_cstring_to_utf8(&file_utf8, err->file,
506 svn_error_clear(svn_cmdline_fprintf(stream, err->pool,
507 "%s:%ld", err->file, err->line));
510 svn_error_clear(svn_cmdline_fputs(SVN_FILE_LINE_UNDEFINED,
512 svn_error_clear(temp_err);
516 const char *symbolic_name;
517 if (svn_error__is_tracing_link(err))
518 /* Skip it; the error code will be printed by the real link. */
519 svn_error_clear(svn_cmdline_fprintf(stream, err->pool, ",\n"));
520 else if ((symbolic_name = svn_error_symbolic_name(err->apr_err)))
521 svn_error_clear(svn_cmdline_fprintf(stream, err->pool,
522 ": (apr_err=%s)\n", symbolic_name));
524 svn_error_clear(svn_cmdline_fprintf(stream, err->pool,
525 ": (apr_err=%d)\n", err->apr_err));
527 #endif /* SVN_DEBUG */
530 if (svn_error__is_tracing_link(err))
532 /* Skip it. We already printed the file-line coordinates. */
534 /* Only print the same APR error string once. */
535 else if (err->message)
537 svn_error_clear(svn_cmdline_fprintf(stream, err->pool,
539 prefix, err->apr_err, err->message));
543 /* Is this a Subversion-specific error code? */
544 if ((err->apr_err > APR_OS_START_USEERR)
545 && (err->apr_err <= APR_OS_START_CANONERR))
546 err_string = svn_strerror(err->apr_err, errbuf, sizeof(errbuf));
547 /* Otherwise, this must be an APR error code. */
548 else if ((temp_err = svn_utf_cstring_to_utf8
549 (&err_string, apr_strerror(err->apr_err, errbuf,
550 sizeof(errbuf)), err->pool)))
552 svn_error_clear(temp_err);
553 err_string = _("Can't recode error string from APR");
556 svn_error_clear(svn_cmdline_fprintf(stream, err->pool,
558 prefix, err->apr_err, err_string));
563 svn_handle_error2(svn_error_t *err,
568 /* In a long error chain, there may be multiple errors with the same
569 error code and no custom message. We only want to print the
570 default message for that code once; printing it multiple times
571 would add no useful information. The 'empties' array below
572 remembers the codes of empty errors already seen in the chain.
574 We could allocate it in err->pool, but there's no telling how
575 long err will live or how many times it will get handled. So we
578 apr_array_header_t *empties;
579 svn_error_t *tmp_err;
581 subpool = svn_pool_create(err->pool);
582 empties = apr_array_make(subpool, 0, sizeof(apr_status_t));
587 svn_boolean_t printed_already = FALSE;
589 if (! tmp_err->message)
593 for (i = 0; i < empties->nelts; i++)
595 if (tmp_err->apr_err == APR_ARRAY_IDX(empties, i, apr_status_t) )
597 printed_already = TRUE;
603 if (! printed_already)
605 print_error(tmp_err, stream, prefix);
606 if (! tmp_err->message)
608 APR_ARRAY_PUSH(empties, apr_status_t) = tmp_err->apr_err;
612 tmp_err = tmp_err->child;
615 svn_pool_destroy(subpool);
620 /* Avoid abort()s in maintainer mode. */
621 svn_error_clear(err);
623 /* We exit(1) here instead of abort()ing so that atexit handlers
630 svn_handle_warning2(FILE *stream, const svn_error_t *err, const char *prefix)
634 const char *symbolic_name = svn_error_symbolic_name(err->apr_err);
640 svn_cmdline_fprintf(stream, err->pool, "%swarning: apr_err=%s\n",
641 prefix, symbolic_name));
644 svn_error_clear(svn_cmdline_fprintf
646 _("%swarning: W%06d: %s\n"),
647 prefix, err->apr_err,
648 svn_err_best_message(err, buf, sizeof(buf))));
653 svn_err_best_message(const svn_error_t *err, char *buf, apr_size_t bufsize)
655 /* Skip over any trace records. */
656 while (svn_error__is_tracing_link(err))
661 return svn_strerror(err->apr_err, buf, bufsize);
665 /* svn_strerror() and helpers */
667 /* Duplicate of the same typedef in tests/libsvn_subr/error-code-test.c */
668 typedef struct err_defn {
669 svn_errno_t errcode; /* 160004 */
670 const char *errname; /* SVN_ERR_FS_CORRUPT */
671 const char *errdesc; /* default message */
674 /* To understand what is going on here, read svn_error_codes.h. */
675 #define SVN_ERROR_BUILD_ARRAY
676 #include "svn_error_codes.h"
679 svn_strerror(apr_status_t statcode, char *buf, apr_size_t bufsize)
681 const err_defn *defn;
683 for (defn = error_table; defn->errdesc != NULL; ++defn)
684 if (defn->errcode == (svn_errno_t)statcode)
686 apr_cpystrn(buf, _(defn->errdesc), bufsize);
690 return apr_strerror(statcode, buf, bufsize);
694 /* Defines svn__errno and svn__apr_errno */
695 #include "errorcode.inc"
699 svn_error_symbolic_name(apr_status_t statcode)
701 const err_defn *defn;
704 #endif /* SVN_DEBUG */
706 for (defn = error_table; defn->errdesc != NULL; ++defn)
707 if (defn->errcode == (svn_errno_t)statcode)
708 return defn->errname;
710 /* "No error" is not in error_table. */
711 if (statcode == APR_SUCCESS)
712 return "SVN_NO_ERROR";
715 /* Try errno.h symbols. */
716 /* Linear search through a sorted array */
717 for (i = 0; i < sizeof(svn__errno) / sizeof(svn__errno[0]); i++)
718 if (svn__errno[i].errcode == (int)statcode)
719 return svn__errno[i].errname;
721 /* Try APR errors. */
722 /* Linear search through a sorted array */
723 for (i = 0; i < sizeof(svn__apr_errno) / sizeof(svn__apr_errno[0]); i++)
724 if (svn__apr_errno[i].errcode == (int)statcode)
725 return svn__apr_errno[i].errname;
726 #endif /* SVN_DEBUG */
728 /* ### TODO: do we need APR_* error macros? What about APR_TO_OS_ERROR()? */
738 svn_error_raise_on_malfunction(svn_boolean_t can_return,
739 const char *file, int line,
743 abort(); /* Nothing else we can do as a library */
745 /* The filename and line number of the error source needs to be set
746 here because svn_error_createf() is not the macro defined in
747 svn_error.h but the real function. */
748 svn_error__locate(file, line);
751 return svn_error_createf(SVN_ERR_ASSERTION_FAIL, NULL,
752 _("In file '%s' line %d: assertion failed (%s)"),
755 return svn_error_createf(SVN_ERR_ASSERTION_FAIL, NULL,
756 _("In file '%s' line %d: internal malfunction"),
761 svn_error_abort_on_malfunction(svn_boolean_t can_return,
762 const char *file, int line,
765 svn_error_t *err = svn_error_raise_on_malfunction(TRUE, file, line, expr);
767 svn_handle_error2(err, stderr, FALSE, "svn: ");
769 return err; /* Not reached. */
772 /* The current handler for reporting malfunctions, and its default setting. */
773 static svn_error_malfunction_handler_t malfunction_handler
774 = svn_error_abort_on_malfunction;
776 svn_error_malfunction_handler_t
777 svn_error_set_malfunction_handler(svn_error_malfunction_handler_t func)
779 svn_error_malfunction_handler_t old_malfunction_handler
780 = malfunction_handler;
782 malfunction_handler = func;
783 return old_malfunction_handler;
786 svn_error_malfunction_handler_t
787 svn_error_get_malfunction_handler(void)
789 return malfunction_handler;
792 /* Note: Although this is a "__" function, it is in the public ABI, so
793 * we can never remove it or change its signature. */
795 svn_error__malfunction(svn_boolean_t can_return,
796 const char *file, int line,
799 return malfunction_handler(can_return, file, line, expr);
806 svn_error__wrap_zlib(int zerr, const char *function, const char *message)
817 status = SVN_ERR_STREAM_MALFORMED_DATA;
818 zmsg = _("stream error");
823 zmsg = _("out of memory");
828 zmsg = _("buffer error");
831 case Z_VERSION_ERROR:
832 status = SVN_ERR_STREAM_UNRECOGNIZED_DATA;
833 zmsg = _("version error");
837 status = SVN_ERR_STREAM_MALFORMED_DATA;
838 zmsg = _("corrupt data");
842 status = SVN_ERR_STREAM_UNRECOGNIZED_DATA;
843 zmsg = _("unknown error");
848 return svn_error_createf(status, NULL, "zlib (%s): %s: %s", function,
851 return svn_error_createf(status, NULL, "zlib (%s): %s", function, zmsg);