]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - contrib/subversion/subversion/libsvn_subr/error.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / contrib / subversion / subversion / libsvn_subr / error.c
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
23
24 \f
25 #include <stdarg.h>
26
27 #include <apr_general.h>
28 #include <apr_pools.h>
29 #include <apr_strings.h>
30
31 #include <zlib.h>
32
33 #ifndef SVN_ERR__TRACING
34 #define SVN_ERR__TRACING
35 #endif
36 #include "svn_cmdline.h"
37 #include "svn_error.h"
38 #include "svn_pools.h"
39 #include "svn_utf.h"
40
41 #ifdef SVN_DEBUG
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. */
45
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;
49
50 /* file_line for the non-debug case. */
51 static const char SVN_FILE_LINE_UNDEFINED[] = "svn:<undefined>";
52 #endif /* SVN_DEBUG */
53
54 #include "svn_private_config.h"
55 #include "private/svn_error_private.h"
56
57 \f
58 /*
59  * Undefine the helpers for creating errors.
60  *
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
65  * variables.
66  */
67 #undef svn_error_create
68 #undef svn_error_createf
69 #undef svn_error_quick_wrap
70 #undef svn_error_wrap_apr
71
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. */
75 void
76 svn_error__locate(const char *file, long line)
77 {
78 #if defined(SVN_DEBUG)
79   /* XXX TODO: Lock mutex here */
80   error_file = file;
81   error_line = line;
82 #endif
83 }
84
85
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)
90 {
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 */
93
94   if (!getenv("SVN_DBG_NO_ABORT_ON_ERROR_LEAK"))
95     abort();
96   return APR_SUCCESS;
97 }
98 #endif
99
100
101 static svn_error_t *
102 make_error_internal(apr_status_t apr_err,
103                     svn_error_t *child)
104 {
105   apr_pool_t *pool;
106   svn_error_t *new_error;
107
108   /* Reuse the child's pool, or create our own. */
109   if (child)
110     pool = child->pool;
111   else
112     {
113       if (apr_pool_create(&pool, NULL))
114         abort();
115     }
116
117   /* Create the new error structure */
118   new_error = apr_pcalloc(pool, sizeof(*new_error));
119
120   /* Fill 'er up. */
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 */
128
129   if (! child)
130       apr_pool_cleanup_register(pool, new_error,
131                                 err_abort,
132                                 apr_pool_cleanup_null);
133 #endif
134
135   return new_error;
136 }
137
138
139 \f
140 /*** Creating and destroying errors. ***/
141
142 svn_error_t *
143 svn_error_create(apr_status_t apr_err,
144                  svn_error_t *child,
145                  const char *message)
146 {
147   svn_error_t *err;
148
149   err = make_error_internal(apr_err, child);
150
151   if (message)
152     err->message = apr_pstrdup(err->pool, message);
153
154   return err;
155 }
156
157
158 svn_error_t *
159 svn_error_createf(apr_status_t apr_err,
160                   svn_error_t *child,
161                   const char *fmt,
162                   ...)
163 {
164   svn_error_t *err;
165   va_list ap;
166
167   err = make_error_internal(apr_err, child);
168
169   va_start(ap, fmt);
170   err->message = apr_pvsprintf(err->pool, fmt, ap);
171   va_end(ap);
172
173   return err;
174 }
175
176
177 svn_error_t *
178 svn_error_wrap_apr(apr_status_t status,
179                    const char *fmt,
180                    ...)
181 {
182   svn_error_t *err, *utf8_err;
183   va_list ap;
184   char errbuf[255];
185   const char *msg_apr, *msg;
186
187   err = make_error_internal(status, NULL);
188
189   if (fmt)
190     {
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);
194       if (utf8_err)
195         msg_apr = NULL;
196       svn_error_clear(utf8_err);
197
198       /* Append it to the formatted message. */
199       va_start(ap, fmt);
200       msg = apr_pvsprintf(err->pool, fmt, ap);
201       va_end(ap);
202       if (msg_apr)
203         {
204           err->message = apr_pstrcat(err->pool, msg, ": ", msg_apr, NULL);
205         }
206       else
207         {
208           err->message = msg;
209         }
210     }
211
212   return err;
213 }
214
215
216 svn_error_t *
217 svn_error_quick_wrap(svn_error_t *child, const char *new_msg)
218 {
219   if (child == SVN_NO_ERROR)
220     return SVN_NO_ERROR;
221
222   return svn_error_create(child->apr_err,
223                           child,
224                           new_msg);
225 }
226
227 /* Messages in tracing errors all point to this static string. */
228 static const char error_tracing_link[] = "traced call";
229
230 svn_error_t *
231 svn_error__trace(const char *file, long line, svn_error_t *err)
232 {
233 #ifndef SVN_DEBUG
234
235   /* We shouldn't even be here, but whatever. Just return the error as-is.  */
236   return err;
237
238 #else
239
240   /* Only do the work when an error occurs.  */
241   if (err)
242     {
243       svn_error_t *trace;
244       svn_error__locate(file, line);
245       trace = make_error_internal(err->apr_err, err);
246       trace->message = error_tracing_link;
247       return trace;
248     }
249   return SVN_NO_ERROR;
250
251 #endif
252 }
253
254
255 svn_error_t *
256 svn_error_compose_create(svn_error_t *err1,
257                          svn_error_t *err2)
258 {
259   if (err1 && err2)
260     {
261       svn_error_compose(err1,
262                         svn_error_quick_wrap(err2,
263                                              _("Additional errors:")));
264       return err1;
265     }
266   return err1 ? err1 : err2;
267 }
268
269
270 void
271 svn_error_compose(svn_error_t *chain, svn_error_t *new_err)
272 {
273   apr_pool_t *pool = chain->pool;
274   apr_pool_t *oldpool = new_err->pool;
275
276   while (chain->child)
277     chain = chain->child;
278
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);
282 #endif
283
284   /* Copy the new error chain into the old chain's pool. */
285   while (new_err)
286     {
287       chain->child = apr_palloc(pool, sizeof(*chain->child));
288       chain = chain->child;
289       *chain = *new_err;
290       if (chain->message)
291         chain->message = apr_pstrdup(pool, new_err->message);
292       chain->pool = pool;
293 #if defined(SVN_DEBUG)
294       if (! new_err->child)
295         apr_pool_cleanup_kill(oldpool, new_err, err_abort);
296 #endif
297       new_err = new_err->child;
298     }
299
300 #if defined(SVN_DEBUG)
301   apr_pool_cleanup_register(pool, chain,
302                             err_abort,
303                             apr_pool_cleanup_null);
304 #endif
305
306   /* Destroy the new error chain. */
307   svn_pool_destroy(oldpool);
308 }
309
310 svn_error_t *
311 svn_error_root_cause(svn_error_t *err)
312 {
313   while (err)
314     {
315       if (err->child)
316         err = err->child;
317       else
318         break;
319     }
320
321   return err;
322 }
323
324 svn_error_t *
325 svn_error_find_cause(svn_error_t *err, apr_status_t apr_err)
326 {
327   svn_error_t *child;
328
329   for (child = err; child; child = child->child)
330     if (child->apr_err == apr_err)
331       return child;
332
333   return SVN_NO_ERROR;
334 }
335
336 svn_error_t *
337 svn_error_dup(svn_error_t *err)
338 {
339   apr_pool_t *pool;
340   svn_error_t *new_err = NULL, *tmp_err = NULL;
341
342   if (apr_pool_create(&pool, NULL))
343     abort();
344
345   for (; err; err = err->child)
346     {
347       if (! new_err)
348         {
349           new_err = apr_palloc(pool, sizeof(*new_err));
350           tmp_err = new_err;
351         }
352       else
353         {
354           tmp_err->child = apr_palloc(pool, sizeof(*tmp_err->child));
355           tmp_err = tmp_err->child;
356         }
357       *tmp_err = *err;
358       tmp_err->pool = pool;
359       if (tmp_err->message)
360         tmp_err->message = apr_pstrdup(pool, tmp_err->message);
361     }
362
363 #if defined(SVN_DEBUG)
364   apr_pool_cleanup_register(pool, tmp_err,
365                             err_abort,
366                             apr_pool_cleanup_null);
367 #endif
368
369   return new_err;
370 }
371
372 void
373 svn_error_clear(svn_error_t *err)
374 {
375   if (err)
376     {
377 #if defined(SVN_DEBUG)
378       while (err->child)
379         err = err->child;
380       apr_pool_cleanup_kill(err->pool, err, err_abort);
381 #endif
382       svn_pool_destroy(err->pool);
383     }
384 }
385
386 svn_boolean_t
387 svn_error__is_tracing_link(svn_error_t *err)
388 {
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,
393      ### really...  */
394   return (err && err->message && !strcmp(err->message, error_tracing_link));
395 #else
396   return FALSE;
397 #endif
398 }
399
400 svn_error_t *
401 svn_error_purge_tracing(svn_error_t *err)
402 {
403 #ifdef SVN_ERR__TRACING
404   svn_error_t *new_err = NULL, *new_err_leaf = NULL;
405
406   if (! err)
407     return SVN_NO_ERROR;
408
409   do
410     {
411       svn_error_t *tmp_err;
412
413       /* Skip over any trace-only links. */
414       while (err && svn_error__is_tracing_link(err))
415         err = err->child;
416
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. */
419       if (! err)
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? */),
425                    err),
426                  NULL);
427
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));
432       *tmp_err = *err;
433       tmp_err->child = NULL;
434
435       /* Add a new link to the new chain (creating the chain if necessary). */
436       if (! new_err)
437         {
438           new_err = tmp_err;
439           new_err_leaf = tmp_err;
440         }
441       else
442         {
443           new_err_leaf->child = tmp_err;
444           new_err_leaf = tmp_err;
445         }
446
447       /* Advance to the next link in the original chain. */
448       err = err->child;
449     } while (err);
450
451   return new_err;
452 #else  /* SVN_ERR__TRACING */
453   return err;
454 #endif /* SVN_ERR__TRACING */
455 }
456
457 /* ### The logic around omitting (sic) apr_err= in maintainer mode is tightly
458    ### coupled to the current sole caller.*/
459 static void
460 print_error(svn_error_t *err, FILE *stream, const char *prefix)
461 {
462   char errbuf[256];
463   const char *err_string;
464   svn_error_t *temp_err = NULL;  /* ensure initialized even if
465                                     err->file == NULL */
466   /* Pretty-print the error */
467   /* Note: we can also log errors here someday. */
468
469 #ifdef SVN_DEBUG
470   /* Note: err->file is _not_ in UTF-8, because it's expanded from
471            the __FILE__ preprocessor macro. */
472   const char *file_utf8;
473
474   if (err->file
475       && !(temp_err = svn_utf_cstring_to_utf8(&file_utf8, err->file,
476                                               err->pool)))
477     svn_error_clear(svn_cmdline_fprintf(stream, err->pool,
478                                         "%s:%ld", err->file, err->line));
479   else
480     {
481       svn_error_clear(svn_cmdline_fputs(SVN_FILE_LINE_UNDEFINED,
482                                         stream, err->pool));
483       svn_error_clear(temp_err);
484     }
485
486   {
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));
494     else
495       svn_error_clear(svn_cmdline_fprintf(stream, err->pool,
496                                           ": (apr_err=%d)\n", err->apr_err));
497   }
498 #endif /* SVN_DEBUG */
499
500   /* "traced call" */
501   if (svn_error__is_tracing_link(err))
502     {
503       /* Skip it.  We already printed the file-line coordinates. */
504     }
505   /* Only print the same APR error string once. */
506   else if (err->message)
507     {
508       svn_error_clear(svn_cmdline_fprintf(stream, err->pool,
509                                           "%sE%06d: %s\n",
510                                           prefix, err->apr_err, err->message));
511     }
512   else
513     {
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)))
522         {
523           svn_error_clear(temp_err);
524           err_string = _("Can't recode error string from APR");
525         }
526
527       svn_error_clear(svn_cmdline_fprintf(stream, err->pool,
528                                           "%sE%06d: %s\n",
529                                           prefix, err->apr_err, err_string));
530     }
531 }
532
533 void
534 svn_handle_error(svn_error_t *err, FILE *stream, svn_boolean_t fatal)
535 {
536   svn_handle_error2(err, stream, fatal, "svn: ");
537 }
538
539 void
540 svn_handle_error2(svn_error_t *err,
541                   FILE *stream,
542                   svn_boolean_t fatal,
543                   const char *prefix)
544 {
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.
550
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
553      use a subpool. */
554   apr_pool_t *subpool;
555   apr_array_header_t *empties;
556   svn_error_t *tmp_err;
557
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));
564
565   tmp_err = err;
566   while (tmp_err)
567     {
568       svn_boolean_t printed_already = FALSE;
569
570       if (! tmp_err->message)
571         {
572           int i;
573
574           for (i = 0; i < empties->nelts; i++)
575             {
576               if (tmp_err->apr_err == APR_ARRAY_IDX(empties, i, apr_status_t) )
577                 {
578                   printed_already = TRUE;
579                   break;
580                 }
581             }
582         }
583
584       if (! printed_already)
585         {
586           print_error(tmp_err, stream, prefix);
587           if (! tmp_err->message)
588             {
589               APR_ARRAY_PUSH(empties, apr_status_t) = tmp_err->apr_err;
590             }
591         }
592
593       tmp_err = tmp_err->child;
594     }
595
596   svn_pool_destroy(subpool);
597
598   fflush(stream);
599   if (fatal)
600     {
601       /* Avoid abort()s in maintainer mode. */
602       svn_error_clear(err);
603
604       /* We exit(1) here instead of abort()ing so that atexit handlers
605          get called. */
606       exit(EXIT_FAILURE);
607     }
608 }
609
610
611 void
612 svn_handle_warning(FILE *stream, svn_error_t *err)
613 {
614   svn_handle_warning2(stream, err, "svn: ");
615 }
616
617 void
618 svn_handle_warning2(FILE *stream, svn_error_t *err, const char *prefix)
619 {
620   char buf[256];
621
622   svn_error_clear(svn_cmdline_fprintf
623                   (stream, err->pool,
624                    _("%swarning: W%06d: %s\n"),
625                    prefix, err->apr_err,
626                    svn_err_best_message(err, buf, sizeof(buf))));
627   fflush(stream);
628 }
629
630 const char *
631 svn_err_best_message(svn_error_t *err, char *buf, apr_size_t bufsize)
632 {
633   /* Skip over any trace records.  */
634   while (svn_error__is_tracing_link(err))
635     err = err->child;
636   if (err->message)
637     return err->message;
638   else
639     return svn_strerror(err->apr_err, buf, bufsize);
640 }
641
642 \f
643 /* svn_strerror() and helpers */
644
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 */
650 } err_defn;
651
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"
655
656 char *
657 svn_strerror(apr_status_t statcode, char *buf, apr_size_t bufsize)
658 {
659   const err_defn *defn;
660
661   for (defn = error_table; defn->errdesc != NULL; ++defn)
662     if (defn->errcode == (svn_errno_t)statcode)
663       {
664         apr_cpystrn(buf, _(defn->errdesc), bufsize);
665         return buf;
666       }
667
668   return apr_strerror(statcode, buf, bufsize);
669 }
670
671 const char *
672 svn_error_symbolic_name(apr_status_t statcode)
673 {
674   const err_defn *defn;
675
676   for (defn = error_table; defn->errdesc != NULL; ++defn)
677     if (defn->errcode == (svn_errno_t)statcode)
678       return defn->errname;
679
680   /* "No error" is not in error_table. */
681   if (statcode == SVN_NO_ERROR)
682     return "SVN_NO_ERROR";
683
684   return NULL;
685 }
686
687
688 \f
689 /* Malfunctions. */
690
691 svn_error_t *
692 svn_error_raise_on_malfunction(svn_boolean_t can_return,
693                                const char *file, int line,
694                                const char *expr)
695 {
696   if (!can_return)
697     abort(); /* Nothing else we can do as a library */
698
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);
703
704   if (expr)
705     return svn_error_createf(SVN_ERR_ASSERTION_FAIL, NULL,
706                              _("In file '%s' line %d: assertion failed (%s)"),
707                              file, line, expr);
708   else
709     return svn_error_createf(SVN_ERR_ASSERTION_FAIL, NULL,
710                              _("In file '%s' line %d: internal malfunction"),
711                              file, line);
712 }
713
714 svn_error_t *
715 svn_error_abort_on_malfunction(svn_boolean_t can_return,
716                                const char *file, int line,
717                                const char *expr)
718 {
719   svn_error_t *err = svn_error_raise_on_malfunction(TRUE, file, line, expr);
720
721   svn_handle_error2(err, stderr, FALSE, "svn: ");
722   abort();
723   return err;  /* Not reached. */
724 }
725
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;
729
730 svn_error_malfunction_handler_t
731 svn_error_set_malfunction_handler(svn_error_malfunction_handler_t func)
732 {
733   svn_error_malfunction_handler_t old_malfunction_handler
734     = malfunction_handler;
735
736   malfunction_handler = func;
737   return old_malfunction_handler;
738 }
739
740 /* Note: Although this is a "__" function, it is in the public ABI, so
741  * we can never remove it or change its signature. */
742 svn_error_t *
743 svn_error__malfunction(svn_boolean_t can_return,
744                        const char *file, int line,
745                        const char *expr)
746 {
747   return malfunction_handler(can_return, file, line, expr);
748 }
749
750 \f
751 /* Misc. */
752
753 svn_error_t *
754 svn_error__wrap_zlib(int zerr, const char *function, const char *message)
755 {
756   apr_status_t status;
757   const char *zmsg;
758
759   if (zerr == Z_OK)
760     return SVN_NO_ERROR;
761
762   switch (zerr)
763     {
764     case Z_STREAM_ERROR:
765       status = SVN_ERR_STREAM_MALFORMED_DATA;
766       zmsg = _("stream error");
767       break;
768
769     case Z_MEM_ERROR:
770       status = APR_ENOMEM;
771       zmsg = _("out of memory");
772       break;
773
774     case Z_BUF_ERROR:
775       status = APR_ENOMEM;
776       zmsg = _("buffer error");
777       break;
778
779     case Z_VERSION_ERROR:
780       status = SVN_ERR_STREAM_UNRECOGNIZED_DATA;
781       zmsg = _("version error");
782       break;
783
784     case Z_DATA_ERROR:
785       status = SVN_ERR_STREAM_MALFORMED_DATA;
786       zmsg = _("corrupt data");
787       break;
788
789     default:
790       status = SVN_ERR_STREAM_UNRECOGNIZED_DATA;
791       zmsg = _("unknown error");
792       break;
793     }
794
795   if (message != NULL)
796     return svn_error_createf(status, NULL, "zlib (%s): %s: %s", function,
797                              zmsg, message);
798   else
799     return svn_error_createf(status, NULL, "zlib (%s): %s", function, zmsg);
800 }