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 #if defined(SVN_DEBUG)
294 if (! new_err
->child
)
295 apr_pool_cleanup_kill(oldpool
, new_err
, err_abort
);
297 new_err
= new_err
->child
;
300 #if defined(SVN_DEBUG)
301 apr_pool_cleanup_register(pool
, chain
,
303 apr_pool_cleanup_null
);
306 /* Destroy the new error chain. */
307 svn_pool_destroy(oldpool
);
311 svn_error_root_cause(svn_error_t
*err
)
325 svn_error_find_cause(svn_error_t
*err
, apr_status_t apr_err
)
329 for (child
= err
; child
; child
= child
->child
)
330 if (child
->apr_err
== apr_err
)
337 svn_error_dup(svn_error_t
*err
)
340 svn_error_t
*new_err
= NULL
, *tmp_err
= NULL
;
342 if (apr_pool_create(&pool
, NULL
))
345 for (; err
; err
= err
->child
)
349 new_err
= apr_palloc(pool
, sizeof(*new_err
));
354 tmp_err
->child
= apr_palloc(pool
, sizeof(*tmp_err
->child
));
355 tmp_err
= tmp_err
->child
;
358 tmp_err
->pool
= pool
;
359 if (tmp_err
->message
)
360 tmp_err
->message
= apr_pstrdup(pool
, tmp_err
->message
);
363 #if defined(SVN_DEBUG)
364 apr_pool_cleanup_register(pool
, tmp_err
,
366 apr_pool_cleanup_null
);
373 svn_error_clear(svn_error_t
*err
)
377 #if defined(SVN_DEBUG)
380 apr_pool_cleanup_kill(err
->pool
, err
, err_abort
);
382 svn_pool_destroy(err
->pool
);
387 svn_error__is_tracing_link(svn_error_t
*err
)
389 #ifdef SVN_ERR__TRACING
390 /* ### A strcmp()? Really? I think it's the best we can do unless
391 ### we add a boolean field to svn_error_t that's set only for
392 ### these "placeholder error chain" items. Not such a bad idea,
394 return (err
&& err
->message
&& !strcmp(err
->message
, error_tracing_link
));
401 svn_error_purge_tracing(svn_error_t
*err
)
403 #ifdef SVN_ERR__TRACING
404 svn_error_t
*new_err
= NULL
, *new_err_leaf
= NULL
;
411 svn_error_t
*tmp_err
;
413 /* Skip over any trace-only links. */
414 while (err
&& svn_error__is_tracing_link(err
))
417 /* The link must be a real link in the error chain, otherwise an
418 error chain with trace only links would map into SVN_NO_ERROR. */
420 return svn_error_create(
421 SVN_ERR_ASSERTION_ONLY_TRACING_LINKS
,
422 svn_error_compose_create(
423 svn_error__malfunction(TRUE
, __FILE__
, __LINE__
,
424 NULL
/* ### say something? */),
428 /* Copy the current error except for its child error pointer
429 into the new error. Share any message and source filename
430 strings from the error. */
431 tmp_err
= apr_palloc(err
->pool
, sizeof(*tmp_err
));
433 tmp_err
->child
= NULL
;
435 /* Add a new link to the new chain (creating the chain if necessary). */
439 new_err_leaf
= tmp_err
;
443 new_err_leaf
->child
= tmp_err
;
444 new_err_leaf
= tmp_err
;
447 /* Advance to the next link in the original chain. */
452 #else /* SVN_ERR__TRACING */
454 #endif /* SVN_ERR__TRACING */
457 /* ### The logic around omitting (sic) apr_err= in maintainer mode is tightly
458 ### coupled to the current sole caller.*/
460 print_error(svn_error_t
*err
, FILE *stream
, const char *prefix
)
463 const char *err_string
;
464 svn_error_t
*temp_err
= NULL
; /* ensure initialized even if
466 /* Pretty-print the error */
467 /* Note: we can also log errors here someday. */
470 /* Note: err->file is _not_ in UTF-8, because it's expanded from
471 the __FILE__ preprocessor macro. */
472 const char *file_utf8
;
475 && !(temp_err
= svn_utf_cstring_to_utf8(&file_utf8
, err
->file
,
477 svn_error_clear(svn_cmdline_fprintf(stream
, err
->pool
,
478 "%s:%ld", err
->file
, err
->line
));
481 svn_error_clear(svn_cmdline_fputs(SVN_FILE_LINE_UNDEFINED
,
483 svn_error_clear(temp_err
);
487 const char *symbolic_name
;
488 if (svn_error__is_tracing_link(err
))
489 /* Skip it; the error code will be printed by the real link. */
490 svn_error_clear(svn_cmdline_fprintf(stream
, err
->pool
, ",\n"));
491 else if ((symbolic_name
= svn_error_symbolic_name(err
->apr_err
)))
492 svn_error_clear(svn_cmdline_fprintf(stream
, err
->pool
,
493 ": (apr_err=%s)\n", symbolic_name
));
495 svn_error_clear(svn_cmdline_fprintf(stream
, err
->pool
,
496 ": (apr_err=%d)\n", err
->apr_err
));
498 #endif /* SVN_DEBUG */
501 if (svn_error__is_tracing_link(err
))
503 /* Skip it. We already printed the file-line coordinates. */
505 /* Only print the same APR error string once. */
506 else if (err
->message
)
508 svn_error_clear(svn_cmdline_fprintf(stream
, err
->pool
,
510 prefix
, err
->apr_err
, err
->message
));
514 /* Is this a Subversion-specific error code? */
515 if ((err
->apr_err
> APR_OS_START_USEERR
)
516 && (err
->apr_err
<= APR_OS_START_CANONERR
))
517 err_string
= svn_strerror(err
->apr_err
, errbuf
, sizeof(errbuf
));
518 /* Otherwise, this must be an APR error code. */
519 else if ((temp_err
= svn_utf_cstring_to_utf8
520 (&err_string
, apr_strerror(err
->apr_err
, errbuf
,
521 sizeof(errbuf
)), err
->pool
)))
523 svn_error_clear(temp_err
);
524 err_string
= _("Can't recode error string from APR");
527 svn_error_clear(svn_cmdline_fprintf(stream
, err
->pool
,
529 prefix
, err
->apr_err
, err_string
));
534 svn_handle_error(svn_error_t
*err
, FILE *stream
, svn_boolean_t fatal
)
536 svn_handle_error2(err
, stream
, fatal
, "svn: ");
540 svn_handle_error2(svn_error_t
*err
,
545 /* In a long error chain, there may be multiple errors with the same
546 error code and no custom message. We only want to print the
547 default message for that code once; printing it multiple times
548 would add no useful information. The 'empties' array below
549 remembers the codes of empty errors already seen in the chain.
551 We could allocate it in err->pool, but there's no telling how
552 long err will live or how many times it will get handled. So we
555 apr_array_header_t
*empties
;
556 svn_error_t
*tmp_err
;
558 /* ### The rest of this file carefully avoids using svn_pool_*(),
559 preferring apr_pool_*() instead. I can't remember why -- it may
560 be an artifact of r843793, or it may be for some deeper reason --
561 but I'm playing it safe and using apr_pool_*() here too. */
562 apr_pool_create(&subpool
, err
->pool
);
563 empties
= apr_array_make(subpool
, 0, sizeof(apr_status_t
));
568 svn_boolean_t printed_already
= FALSE
;
570 if (! tmp_err
->message
)
574 for (i
= 0; i
< empties
->nelts
; i
++)
576 if (tmp_err
->apr_err
== APR_ARRAY_IDX(empties
, i
, apr_status_t
) )
578 printed_already
= TRUE
;
584 if (! printed_already
)
586 print_error(tmp_err
, stream
, prefix
);
587 if (! tmp_err
->message
)
589 APR_ARRAY_PUSH(empties
, apr_status_t
) = tmp_err
->apr_err
;
593 tmp_err
= tmp_err
->child
;
596 svn_pool_destroy(subpool
);
601 /* Avoid abort()s in maintainer mode. */
602 svn_error_clear(err
);
604 /* We exit(1) here instead of abort()ing so that atexit handlers
612 svn_handle_warning(FILE *stream
, svn_error_t
*err
)
614 svn_handle_warning2(stream
, err
, "svn: ");
618 svn_handle_warning2(FILE *stream
, svn_error_t
*err
, const char *prefix
)
622 svn_error_clear(svn_cmdline_fprintf
624 _("%swarning: W%06d: %s\n"),
625 prefix
, err
->apr_err
,
626 svn_err_best_message(err
, buf
, sizeof(buf
))));
631 svn_err_best_message(svn_error_t
*err
, char *buf
, apr_size_t bufsize
)
633 /* Skip over any trace records. */
634 while (svn_error__is_tracing_link(err
))
639 return svn_strerror(err
->apr_err
, buf
, bufsize
);
643 /* svn_strerror() and helpers */
645 /* Duplicate of the same typedef in tests/libsvn_subr/error-code-test.c */
646 typedef struct err_defn
{
647 svn_errno_t errcode
; /* 160004 */
648 const char *errname
; /* SVN_ERR_FS_CORRUPT */
649 const char *errdesc
; /* default message */
652 /* To understand what is going on here, read svn_error_codes.h. */
653 #define SVN_ERROR_BUILD_ARRAY
654 #include "svn_error_codes.h"
657 svn_strerror(apr_status_t statcode
, char *buf
, apr_size_t bufsize
)
659 const err_defn
*defn
;
661 for (defn
= error_table
; defn
->errdesc
!= NULL
; ++defn
)
662 if (defn
->errcode
== (svn_errno_t
)statcode
)
664 apr_cpystrn(buf
, _(defn
->errdesc
), bufsize
);
668 return apr_strerror(statcode
, buf
, bufsize
);
672 svn_error_symbolic_name(apr_status_t statcode
)
674 const err_defn
*defn
;
676 for (defn
= error_table
; defn
->errdesc
!= NULL
; ++defn
)
677 if (defn
->errcode
== (svn_errno_t
)statcode
)
678 return defn
->errname
;
680 /* "No error" is not in error_table. */
681 if (statcode
== SVN_NO_ERROR
)
682 return "SVN_NO_ERROR";
692 svn_error_raise_on_malfunction(svn_boolean_t can_return
,
693 const char *file
, int line
,
697 abort(); /* Nothing else we can do as a library */
699 /* The filename and line number of the error source needs to be set
700 here because svn_error_createf() is not the macro defined in
701 svn_error.h but the real function. */
702 svn_error__locate(file
, line
);
705 return svn_error_createf(SVN_ERR_ASSERTION_FAIL
, NULL
,
706 _("In file '%s' line %d: assertion failed (%s)"),
709 return svn_error_createf(SVN_ERR_ASSERTION_FAIL
, NULL
,
710 _("In file '%s' line %d: internal malfunction"),
715 svn_error_abort_on_malfunction(svn_boolean_t can_return
,
716 const char *file
, int line
,
719 svn_error_t
*err
= svn_error_raise_on_malfunction(TRUE
, file
, line
, expr
);
721 svn_handle_error2(err
, stderr
, FALSE
, "svn: ");
723 return err
; /* Not reached. */
726 /* The current handler for reporting malfunctions, and its default setting. */
727 static svn_error_malfunction_handler_t malfunction_handler
728 = svn_error_abort_on_malfunction
;
730 svn_error_malfunction_handler_t
731 svn_error_set_malfunction_handler(svn_error_malfunction_handler_t func
)
733 svn_error_malfunction_handler_t old_malfunction_handler
734 = malfunction_handler
;
736 malfunction_handler
= func
;
737 return old_malfunction_handler
;
740 /* Note: Although this is a "__" function, it is in the public ABI, so
741 * we can never remove it or change its signature. */
743 svn_error__malfunction(svn_boolean_t can_return
,
744 const char *file
, int line
,
747 return malfunction_handler(can_return
, file
, line
, expr
);
754 svn_error__wrap_zlib(int zerr
, const char *function
, const char *message
)
765 status
= SVN_ERR_STREAM_MALFORMED_DATA
;
766 zmsg
= _("stream error");
771 zmsg
= _("out of memory");
776 zmsg
= _("buffer error");
779 case Z_VERSION_ERROR
:
780 status
= SVN_ERR_STREAM_UNRECOGNIZED_DATA
;
781 zmsg
= _("version error");
785 status
= SVN_ERR_STREAM_MALFORMED_DATA
;
786 zmsg
= _("corrupt data");
790 status
= SVN_ERR_STREAM_UNRECOGNIZED_DATA
;
791 zmsg
= _("unknown error");
796 return svn_error_createf(status
, NULL
, "zlib (%s): %s: %s", function
,
799 return svn_error_createf(status
, NULL
, "zlib (%s): %s", function
, zmsg
);