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>
31 #if defined(SVN_DEBUG) && APR_HAS_THREADS
32 #include <apr_thread_proc.h>
37 #ifndef SVN_ERR__TRACING
38 #define SVN_ERR__TRACING
40 #include "svn_cmdline.h"
41 #include "svn_error.h"
42 #include "svn_pools.h"
45 #include "private/svn_error_private.h"
46 #include "svn_private_config.h"
48 #if defined(SVN_DEBUG) && APR_HAS_THREADS
49 #include "private/svn_atomic.h"
56 static apr_threadkey_t *error_file_key = NULL;
57 static apr_threadkey_t *error_line_key = NULL;
59 /* No-op destructor for apr_threadkey_private_create(). */
60 static void null_threadkey_dtor(void *stuff) {}
62 /* Implements svn_atomic__str_init_func_t.
63 Callback used by svn_error__locate to initialize the thread-local
64 error location storage. This function will never return an
67 locate_init_once(void *ignored_baton)
69 /* Strictly speaking, this is a memory leak, since we're creating an
70 unmanaged, top-level pool and never destroying it. We do this
71 because this pool controls the lifetime of the thread-local
72 storage for error locations, and that storage must always be
74 apr_pool_t *threadkey_pool = svn_pool__create_unmanaged(TRUE);
77 status = apr_threadkey_private_create(&error_file_key,
80 if (status == APR_SUCCESS)
81 status = apr_threadkey_private_create(&error_line_key,
85 /* If anything went wrong with the creation of the thread-local
86 storage, we'll revert to the old, thread-agnostic behaviour */
87 if (status != APR_SUCCESS)
88 error_file_key = error_line_key = NULL;
92 # endif /* APR_HAS_THREADS */
94 /* These location variables will be used in no-threads mode or if
95 thread-local storage is not available. */
96 static const char * volatile error_file = NULL;
97 static long volatile error_line = -1;
99 /* file_line for the non-debug case. */
100 static const char SVN_FILE_LINE_UNDEFINED[] = "svn:<undefined>";
101 #endif /* SVN_DEBUG */
105 * Undefine the helpers for creating errors.
107 * *NOTE*: Any use of these functions in any other function may need
108 * to call svn_error__locate() because the macro that would otherwise
109 * do this is being undefined and the filename and line number will
110 * not be properly set in the static error_file and error_line
113 #undef svn_error_create
114 #undef svn_error_createf
115 #undef svn_error_quick_wrap
116 #undef svn_error_quick_wrapf
117 #undef svn_error_wrap_apr
119 /* Note: Although this is a "__" function, it was historically in the
120 * public ABI, so we can never change it or remove its signature, even
121 * though it is now only used in SVN_DEBUG mode. */
123 svn_error__locate(const char *file, long line)
127 static volatile svn_atomic_t init_status = 0;
128 svn_atomic__init_once_no_error(&init_status, locate_init_once, NULL);
130 if (error_file_key && error_line_key)
133 status = apr_threadkey_private_set((char*)file, error_file_key);
134 if (status == APR_SUCCESS)
135 status = apr_threadkey_private_set((void*)line, error_line_key);
136 if (status == APR_SUCCESS)
139 # endif /* APR_HAS_THREADS */
143 #endif /* SVN_DEBUG */
147 /* Cleanup function for errors. svn_error_clear () removes this so
148 errors that are properly handled *don't* hit this code. */
149 static apr_status_t err_abort(void *data)
151 svn_error_t *err = data; /* For easy viewing in a debugger */
154 if (!getenv("SVN_DBG_NO_ABORT_ON_ERROR_LEAK"))
161 make_error_internal(apr_status_t apr_err,
165 svn_error_t *new_error;
167 apr_status_t status = APR_ENOTIMPL;
170 /* Reuse the child's pool, or create our own. */
175 pool = svn_pool_create(NULL);
180 /* Create the new error structure */
181 new_error = apr_pcalloc(pool, sizeof(*new_error));
184 new_error->apr_err = apr_err;
185 new_error->child = child;
186 new_error->pool = pool;
190 if (error_file_key && error_line_key)
193 status = apr_threadkey_private_get(&item, error_file_key);
194 if (status == APR_SUCCESS)
196 new_error->file = item;
197 status = apr_threadkey_private_get(&item, error_line_key);
198 if (status == APR_SUCCESS)
199 new_error->line = (long)item;
202 # endif /* APR_HAS_THREADS */
204 if (status != APR_SUCCESS)
206 new_error->file = error_file;
207 new_error->line = error_line;
211 apr_pool_cleanup_register(pool, new_error,
213 apr_pool_cleanup_null);
214 #endif /* SVN_DEBUG */
221 /*** Creating and destroying errors. ***/
224 svn_error_create(apr_status_t apr_err,
230 err = make_error_internal(apr_err, child);
233 err->message = apr_pstrdup(err->pool, message);
240 svn_error_createf(apr_status_t apr_err,
248 err = make_error_internal(apr_err, child);
251 err->message = apr_pvsprintf(err->pool, fmt, ap);
259 svn_error_wrap_apr(apr_status_t status,
263 svn_error_t *err, *utf8_err;
266 const char *msg_apr, *msg;
268 err = make_error_internal(status, NULL);
272 /* Grab the APR error message. */
273 apr_strerror(status, errbuf, sizeof(errbuf));
274 utf8_err = svn_utf_cstring_to_utf8(&msg_apr, errbuf, err->pool);
277 svn_error_clear(utf8_err);
279 /* Append it to the formatted message. */
281 msg = apr_pvsprintf(err->pool, fmt, ap);
285 err->message = apr_pstrcat(err->pool, msg, ": ", msg_apr,
299 svn_error_quick_wrap(svn_error_t *child, const char *new_msg)
301 if (child == SVN_NO_ERROR)
304 return svn_error_create(child->apr_err,
310 svn_error_quick_wrapf(svn_error_t *child,
317 if (child == SVN_NO_ERROR)
320 err = make_error_internal(child->apr_err, child);
323 err->message = apr_pvsprintf(err->pool, fmt, ap);
329 /* Messages in tracing errors all point to this static string. */
330 static const char error_tracing_link[] = "traced call";
333 svn_error__trace(const char *file, long line, svn_error_t *err)
337 /* We shouldn't even be here, but whatever. Just return the error as-is. */
342 /* Only do the work when an error occurs. */
346 svn_error__locate(file, line);
347 trace = make_error_internal(err->apr_err, err);
348 trace->message = error_tracing_link;
358 svn_error_compose_create(svn_error_t *err1,
363 svn_error_compose(err1,
364 svn_error_create(SVN_ERR_COMPOSED_ERROR, err2, NULL));
367 return err1 ? err1 : err2;
372 svn_error_compose(svn_error_t *chain, svn_error_t *new_err)
374 apr_pool_t *pool = chain->pool;
375 apr_pool_t *oldpool = new_err->pool;
378 chain = chain->child;
380 #if defined(SVN_DEBUG)
381 /* Kill existing handler since the end of the chain is going to change */
382 apr_pool_cleanup_kill(pool, chain, err_abort);
385 /* Copy the new error chain into the old chain's pool. */
388 chain->child = apr_palloc(pool, sizeof(*chain->child));
389 chain = chain->child;
392 chain->message = apr_pstrdup(pool, new_err->message);
394 chain->file = apr_pstrdup(pool, new_err->file);
396 #if defined(SVN_DEBUG)
397 if (! new_err->child)
398 apr_pool_cleanup_kill(oldpool, new_err, err_abort);
400 new_err = new_err->child;
403 #if defined(SVN_DEBUG)
404 apr_pool_cleanup_register(pool, chain,
406 apr_pool_cleanup_null);
409 /* Destroy the new error chain. */
410 svn_pool_destroy(oldpool);
414 svn_error_root_cause(svn_error_t *err)
418 /* I don't think we can change the behavior here, but the additional
419 error chain doesn't define the root cause. Perhaps we should rev
421 if (err->child /*&& err->child->apr_err != SVN_ERR_COMPOSED_ERROR*/)
431 svn_error_find_cause(svn_error_t *err, apr_status_t apr_err)
435 for (child = err; child; child = child->child)
436 if (child->apr_err == apr_err)
443 svn_error_dup(const svn_error_t *err)
446 svn_error_t *new_err = NULL, *tmp_err = NULL;
451 pool = svn_pool_create(NULL);
455 for (; err; err = err->child)
459 new_err = apr_palloc(pool, sizeof(*new_err));
464 tmp_err->child = apr_palloc(pool, sizeof(*tmp_err->child));
465 tmp_err = tmp_err->child;
468 tmp_err->pool = pool;
469 if (tmp_err->message)
470 tmp_err->message = apr_pstrdup(pool, tmp_err->message);
472 tmp_err->file = apr_pstrdup(pool, tmp_err->file);
475 #if defined(SVN_DEBUG)
476 apr_pool_cleanup_register(pool, tmp_err,
478 apr_pool_cleanup_null);
485 svn_error_clear(svn_error_t *err)
489 #if defined(SVN_DEBUG)
492 apr_pool_cleanup_kill(err->pool, err, err_abort);
494 svn_pool_destroy(err->pool);
499 svn_error__is_tracing_link(const svn_error_t *err)
501 #ifdef SVN_ERR__TRACING
502 /* ### A strcmp()? Really? I think it's the best we can do unless
503 ### we add a boolean field to svn_error_t that's set only for
504 ### these "placeholder error chain" items. Not such a bad idea,
506 return (err && err->message && !strcmp(err->message, error_tracing_link));
513 svn_error_purge_tracing(svn_error_t *err)
515 #ifdef SVN_ERR__TRACING
516 svn_error_t *new_err = NULL, *new_err_leaf = NULL;
523 svn_error_t *tmp_err;
525 /* Skip over any trace-only links. */
526 while (err && svn_error__is_tracing_link(err))
529 /* The link must be a real link in the error chain, otherwise an
530 error chain with trace only links would map into SVN_NO_ERROR. */
532 return svn_error_create(
533 SVN_ERR_ASSERTION_ONLY_TRACING_LINKS,
534 svn_error__malfunction(TRUE, __FILE__, __LINE__,
535 NULL /* ### say something? */),
538 /* Copy the current error except for its child error pointer
539 into the new error. Share any message and source filename
540 strings from the error. */
541 tmp_err = apr_palloc(err->pool, sizeof(*tmp_err));
543 tmp_err->child = NULL;
545 /* Add a new link to the new chain (creating the chain if necessary). */
549 new_err_leaf = tmp_err;
553 new_err_leaf->child = tmp_err;
554 new_err_leaf = tmp_err;
557 /* Advance to the next link in the original chain. */
562 #else /* SVN_ERR__TRACING */
564 #endif /* SVN_ERR__TRACING */
567 /* ### The logic around omitting (sic) apr_err= in maintainer mode is tightly
568 ### coupled to the current sole caller.*/
570 print_error(svn_error_t *err, FILE *stream, const char *prefix)
573 const char *err_string;
574 svn_error_t *temp_err = NULL; /* ensure initialized even if
576 /* Pretty-print the error */
577 /* Note: we can also log errors here someday. */
580 /* Note: err->file is _not_ in UTF-8, because it's expanded from
581 the __FILE__ preprocessor macro. */
582 const char *file_utf8;
585 && !(temp_err = svn_utf_cstring_to_utf8(&file_utf8, err->file,
587 svn_error_clear(svn_cmdline_fprintf(stream, err->pool,
588 "%s:%ld", err->file, err->line));
591 svn_error_clear(svn_cmdline_fputs(SVN_FILE_LINE_UNDEFINED,
593 svn_error_clear(temp_err);
597 const char *symbolic_name;
598 if (svn_error__is_tracing_link(err))
599 /* Skip it; the error code will be printed by the real link. */
600 svn_error_clear(svn_cmdline_fprintf(stream, err->pool, ",\n"));
601 else if ((symbolic_name = svn_error_symbolic_name(err->apr_err)))
602 svn_error_clear(svn_cmdline_fprintf(stream, err->pool,
603 ": (apr_err=%s)\n", symbolic_name));
605 svn_error_clear(svn_cmdline_fprintf(stream, err->pool,
606 ": (apr_err=%d)\n", err->apr_err));
608 #endif /* SVN_DEBUG */
611 if (svn_error__is_tracing_link(err))
613 /* Skip it. We already printed the file-line coordinates. */
615 /* Only print the same APR error string once. */
616 else if (err->message)
618 svn_error_clear(svn_cmdline_fprintf(stream, err->pool,
620 prefix, err->apr_err, err->message));
624 /* Is this a Subversion-specific error code? */
625 if ((err->apr_err > APR_OS_START_USEERR)
626 && (err->apr_err <= APR_OS_START_CANONERR))
627 err_string = svn_strerror(err->apr_err, errbuf, sizeof(errbuf));
628 /* Otherwise, this must be an APR error code. */
629 else if ((temp_err = svn_utf_cstring_to_utf8
630 (&err_string, apr_strerror(err->apr_err, errbuf,
631 sizeof(errbuf)), err->pool)))
633 svn_error_clear(temp_err);
634 err_string = _("Can't recode error string from APR");
637 svn_error_clear(svn_cmdline_fprintf(stream, err->pool,
639 prefix, err->apr_err, err_string));
644 svn_handle_error2(svn_error_t *err,
649 /* In a long error chain, there may be multiple errors with the same
650 error code and no custom message. We only want to print the
651 default message for that code once; printing it multiple times
652 would add no useful information. The 'empties' array below
653 remembers the codes of empty errors already seen in the chain.
655 We could allocate it in err->pool, but there's no telling how
656 long err will live or how many times it will get handled. So we
659 apr_array_header_t *empties;
660 svn_error_t *tmp_err;
662 subpool = svn_pool_create(err->pool);
663 empties = apr_array_make(subpool, 0, sizeof(apr_status_t));
668 svn_boolean_t printed_already = FALSE;
670 if (! tmp_err->message)
674 for (i = 0; i < empties->nelts; i++)
676 if (tmp_err->apr_err == APR_ARRAY_IDX(empties, i, apr_status_t) )
678 printed_already = TRUE;
684 if (! printed_already)
686 print_error(tmp_err, stream, prefix);
687 if (! tmp_err->message)
689 APR_ARRAY_PUSH(empties, apr_status_t) = tmp_err->apr_err;
693 tmp_err = tmp_err->child;
696 svn_pool_destroy(subpool);
701 /* Avoid abort()s in maintainer mode. */
702 svn_error_clear(err);
704 /* We exit(1) here instead of abort()ing so that atexit handlers
711 svn_handle_warning2(FILE *stream, const svn_error_t *err, const char *prefix)
715 const char *symbolic_name = svn_error_symbolic_name(err->apr_err);
721 svn_cmdline_fprintf(stream, err->pool, "%swarning: apr_err=%s\n",
722 prefix, symbolic_name));
725 svn_error_clear(svn_cmdline_fprintf
727 _("%swarning: W%06d: %s\n"),
728 prefix, err->apr_err,
729 svn_err_best_message(err, buf, sizeof(buf))));
734 svn_err_best_message(const svn_error_t *err, char *buf, apr_size_t bufsize)
736 /* Skip over any trace records. */
737 while (svn_error__is_tracing_link(err))
742 return svn_strerror(err->apr_err, buf, bufsize);
746 /* svn_strerror() and helpers */
748 /* Duplicate of the same typedef in tests/libsvn_subr/error-code-test.c */
749 typedef struct err_defn {
750 svn_errno_t errcode; /* 160004 */
751 const char *errname; /* SVN_ERR_FS_CORRUPT */
752 const char *errdesc; /* default message */
755 /* To understand what is going on here, read svn_error_codes.h. */
756 #define SVN_ERROR_BUILD_ARRAY
757 #include "svn_error_codes.h"
760 svn_strerror(apr_status_t statcode, char *buf, apr_size_t bufsize)
762 const err_defn *defn;
764 for (defn = error_table; defn->errdesc != NULL; ++defn)
765 if (defn->errcode == (svn_errno_t)statcode)
767 apr_cpystrn(buf, _(defn->errdesc), bufsize);
771 return apr_strerror(statcode, buf, bufsize);
775 /* Defines svn__errno and svn__apr_errno */
776 #include "errorcode.inc"
780 svn_error_symbolic_name(apr_status_t statcode)
782 const err_defn *defn;
785 #endif /* SVN_DEBUG */
787 for (defn = error_table; defn->errdesc != NULL; ++defn)
788 if (defn->errcode == (svn_errno_t)statcode)
789 return defn->errname;
791 /* "No error" is not in error_table. */
792 if (statcode == APR_SUCCESS)
793 return "SVN_NO_ERROR";
796 /* Try errno.h symbols. */
797 /* Linear search through a sorted array */
798 for (i = 0; i < sizeof(svn__errno) / sizeof(svn__errno[0]); i++)
799 if (svn__errno[i].errcode == (int)statcode)
800 return svn__errno[i].errname;
802 /* Try APR errors. */
803 /* Linear search through a sorted array */
804 for (i = 0; i < sizeof(svn__apr_errno) / sizeof(svn__apr_errno[0]); i++)
805 if (svn__apr_errno[i].errcode == (int)statcode)
806 return svn__apr_errno[i].errname;
807 #endif /* SVN_DEBUG */
809 /* ### TODO: do we need APR_* error macros? What about APR_TO_OS_ERROR()? */
819 svn_error_raise_on_malfunction(svn_boolean_t can_return,
820 const char *file, int line,
824 abort(); /* Nothing else we can do as a library */
826 /* The filename and line number of the error source needs to be set
827 here because svn_error_createf() is not the macro defined in
828 svn_error.h but the real function. */
829 svn_error__locate(file, line);
832 return svn_error_createf(SVN_ERR_ASSERTION_FAIL, NULL,
833 _("In file '%s' line %d: assertion failed (%s)"),
836 return svn_error_createf(SVN_ERR_ASSERTION_FAIL, NULL,
837 _("In file '%s' line %d: internal malfunction"),
842 svn_error_abort_on_malfunction(svn_boolean_t can_return,
843 const char *file, int line,
846 svn_error_t *err = svn_error_raise_on_malfunction(TRUE, file, line, expr);
848 svn_handle_error2(err, stderr, FALSE, "svn: ");
850 return err; /* Not reached. */
853 /* The current handler for reporting malfunctions, and its default setting. */
854 static svn_error_malfunction_handler_t malfunction_handler
855 = svn_error_abort_on_malfunction;
857 svn_error_malfunction_handler_t
858 svn_error_set_malfunction_handler(svn_error_malfunction_handler_t func)
860 svn_error_malfunction_handler_t old_malfunction_handler
861 = malfunction_handler;
863 malfunction_handler = func;
864 return old_malfunction_handler;
867 svn_error_malfunction_handler_t
868 svn_error_get_malfunction_handler(void)
870 return malfunction_handler;
873 /* Note: Although this is a "__" function, it is in the public ABI, so
874 * we can never remove it or change its signature. */
876 svn_error__malfunction(svn_boolean_t can_return,
877 const char *file, int line,
880 return malfunction_handler(can_return, file, line, expr);
887 svn_error__wrap_zlib(int zerr, const char *function, const char *message)
898 status = SVN_ERR_STREAM_MALFORMED_DATA;
899 zmsg = _("stream error");
904 zmsg = _("out of memory");
909 zmsg = _("buffer error");
912 case Z_VERSION_ERROR:
913 status = SVN_ERR_STREAM_UNRECOGNIZED_DATA;
914 zmsg = _("version error");
918 status = SVN_ERR_STREAM_MALFORMED_DATA;
919 zmsg = _("corrupt data");
923 status = SVN_ERR_STREAM_UNRECOGNIZED_DATA;
924 zmsg = _("unknown error");
929 return svn_error_createf(status, NULL, "zlib (%s): %s: %s", function,
932 return svn_error_createf(status, NULL, "zlib (%s): %s", function, zmsg);