source: trunk/third/sendmail/libsm/exc.c @ 19204

Revision 19204, 13.3 KB checked in by zacheiss, 22 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r19203, which included commits to RCS files with non-trunk default branches.
Line 
1/*
2 * Copyright (c) 2000-2002 Sendmail, Inc. and its suppliers.
3 *      All rights reserved.
4 *
5 * By using this file, you agree to the terms and conditions set
6 * forth in the LICENSE file which can be found at the top level of
7 * the sendmail distribution.
8 *
9 */
10
11#include <sm/gen.h>
12SM_RCSID("@(#)$Id: exc.c,v 1.1.1.1 2003-04-08 15:06:46 zacheiss Exp $")
13
14/*
15**  exception handling
16**  For documentation, see exc.html
17*/
18
19#include <ctype.h>
20#include <string.h>
21
22#include <sm/errstring.h>
23#include <sm/exc.h>
24#include <sm/heap.h>
25#include <sm/string.h>
26#include <sm/varargs.h>
27#include <sm/io.h>
28
29const char SmExcMagic[] = "sm_exc";
30const char SmExcTypeMagic[] = "sm_exc_type";
31
32/*
33**  SM_ETYPE_PRINTF -- printf for exception types.
34**
35**      Parameters:
36**              exc -- exception.
37**              stream -- file for output.
38**
39**      Returns:
40**              none.
41*/
42
43/*
44**  A simple formatted print function that can be used as the print function
45**  by most exception types.  It prints the printcontext string, interpreting
46**  occurrences of %0 through %9 as references to the argument vector.
47**  If exception argument 3 is an int or long, then %3 will print the
48**  argument in decimal, and %o3 or %x3 will print it in octal or hex.
49*/
50
51void
52sm_etype_printf(exc, stream)
53        SM_EXC_T *exc;
54        SM_FILE_T *stream;
55{
56        size_t n = strlen(exc->exc_type->etype_argformat);
57        const char *p, *s;
58        char format;
59
60        for (p = exc->exc_type->etype_printcontext; *p != '\0'; ++p)
61        {
62                if (*p != '%')
63                {
64                        (void) sm_io_putc(stream, SM_TIME_DEFAULT, *p);
65                        continue;
66                }
67                ++p;
68                if (*p == '\0')
69                {
70                        (void) sm_io_putc(stream, SM_TIME_DEFAULT, '%');
71                        break;
72                }
73                if (*p == '%')
74                {
75                        (void) sm_io_putc(stream, SM_TIME_DEFAULT, '%');
76                        continue;
77                }
78                format = '\0';
79                if (isalpha(*p))
80                {
81                        format = *p++;
82                        if (*p == '\0')
83                        {
84                                (void) sm_io_putc(stream, SM_TIME_DEFAULT, '%');
85                                (void) sm_io_putc(stream, SM_TIME_DEFAULT,
86                                                  format);
87                                break;
88                        }
89                }
90                if (isdigit(*p))
91                {
92                        size_t i = *p - '0';
93                        if (i < n)
94                        {
95                                switch (exc->exc_type->etype_argformat[i])
96                                {
97                                  case 's':
98                                  case 'r':
99                                        s = exc->exc_argv[i].v_str;
100                                        if (s == NULL)
101                                                s = "(null)";
102                                        sm_io_fputs(stream, SM_TIME_DEFAULT, s);
103                                        continue;
104                                  case 'i':
105                                        sm_io_fprintf(stream,
106                                                SM_TIME_DEFAULT,
107                                                format == 'o' ? "%o"
108                                                : format == 'x' ? "%x"
109                                                                : "%d",
110                                                exc->exc_argv[i].v_int);
111                                        continue;
112                                  case 'l':
113                                        sm_io_fprintf(stream,
114                                                SM_TIME_DEFAULT,
115                                                format == 'o' ? "%lo"
116                                                : format == 'x' ? "%lx"
117                                                                : "%ld",
118                                                exc->exc_argv[i].v_long);
119                                        continue;
120                                  case 'e':
121                                        sm_exc_write(exc->exc_argv[i].v_exc,
122                                                stream);
123                                        continue;
124                                }
125                        }
126                }
127                (void) sm_io_putc(stream, SM_TIME_DEFAULT, '%');
128                if (format)
129                        (void) sm_io_putc(stream, SM_TIME_DEFAULT, format);
130                (void) sm_io_putc(stream, SM_TIME_DEFAULT, *p);
131        }
132}
133
134/*
135**  Standard exception types.
136*/
137
138/*
139**  SM_ETYPE_OS_PRINT -- Print OS related exception.
140**
141**      Parameters:
142**              exc -- exception.
143**              stream -- file for output.
144**
145**      Returns:
146**              none.
147*/
148
149static void
150sm_etype_os_print __P((
151        SM_EXC_T *exc,
152        SM_FILE_T *stream));
153
154static void
155sm_etype_os_print(exc, stream)
156        SM_EXC_T *exc;
157        SM_FILE_T *stream;
158{
159        int err = exc->exc_argv[0].v_int;
160        char *syscall = exc->exc_argv[1].v_str;
161        char *sysargs = exc->exc_argv[2].v_str;
162
163        if (sysargs)
164                sm_io_fprintf(stream, SM_TIME_DEFAULT, "%s: %s failed: %s",
165                              sysargs, syscall, sm_errstring(err));
166        else
167                sm_io_fprintf(stream, SM_TIME_DEFAULT, "%s failed: %s", syscall,
168                              sm_errstring(err));
169}
170
171/*
172**  SmEtypeOs represents the failure of a Unix system call.
173**  The three arguments are:
174**   int errno (eg, ENOENT)
175**   char *syscall (eg, "open")
176**   char *sysargs (eg, NULL or "/etc/mail/sendmail.cf")
177*/
178
179const SM_EXC_TYPE_T SmEtypeOs =
180{
181        SmExcTypeMagic,
182        "E:sm.os",
183        "isr",
184        sm_etype_os_print,
185        NULL,
186};
187
188/*
189**  SmEtypeErr is a completely generic error which should only be
190**  used in applications and test programs.  Libraries should use
191**  more specific exception codes.
192*/
193
194const SM_EXC_TYPE_T SmEtypeErr =
195{
196        SmExcTypeMagic,
197        "E:sm.err",
198        "r",
199        sm_etype_printf,
200        "%0",
201};
202
203/*
204**  SM_EXC_VNEW_X -- Construct a new exception object.
205**
206**      Parameters:
207**              etype -- type of exception.
208**              ap -- varargs.
209**
210**      Returns:
211**              pointer to exception object.
212*/
213
214/*
215**  This is an auxiliary function called by sm_exc_new_x and sm_exc_raisenew_x.
216**
217**  If an exception is raised, then to avoid a storage leak, we must:
218**  (a) Free all storage we have allocated.
219**  (b) Free all exception arguments in the varargs list.
220**  Getting this right is tricky.
221**
222**  To see why (b) is required, consider the code fragment
223**     SM_EXCEPT(exc, "*")
224**         sm_exc_raisenew_x(&MyEtype, exc);
225**     SM_END_TRY
226**  In the normal case, sm_exc_raisenew_x will allocate and raise a new
227**  exception E that owns exc.  When E is eventually freed, exc is also freed.
228**  In the exceptional case, sm_exc_raisenew_x must free exc before raising
229**  an out-of-memory exception so that exc is not leaked.
230*/
231
232SM_EXC_T *
233sm_exc_vnew_x(etype, ap)
234        const SM_EXC_TYPE_T *etype;
235        va_list SM_NONVOLATILE ap;
236{
237        /*
238        **  All variables that are modified in the SM_TRY clause and
239        **  referenced in the SM_EXCEPT clause must be declared volatile.
240        */
241
242        /* NOTE: Type of si, i, and argc *must* match */
243        SM_EXC_T * volatile exc = NULL;
244        int volatile si = 0;
245        SM_VAL_T * volatile argv = NULL;
246        int i, argc;
247
248        SM_REQUIRE_ISA(etype, SmExcTypeMagic);
249        argc = strlen(etype->etype_argformat);
250        SM_TRY
251        {
252                /*
253                **  Step 1.  Allocate the exception structure.
254                **  On failure, scan the varargs list and free all
255                **  exception arguments.
256                */
257
258                exc = sm_malloc_x(sizeof(SM_EXC_T));
259                exc->sm_magic = SmExcMagic;
260                exc->exc_refcount = 1;
261                exc->exc_type = etype;
262                exc->exc_argv = NULL;
263
264                /*
265                **  Step 2.  Allocate the argument vector.
266                **  On failure, free exc, scan the varargs list and free all
267                **  exception arguments.  On success, scan the varargs list,
268                **  and copy the arguments into argv.
269                */
270
271                argv = sm_malloc_x(argc * sizeof(SM_VAL_T));
272                exc->exc_argv = argv;
273                for (i = 0; i < argc; ++i)
274                {
275                        switch (etype->etype_argformat[i])
276                        {
277                          case 'i':
278                                argv[i].v_int = SM_VA_ARG(ap, int);
279                                break;
280                          case 'l':
281                                argv[i].v_long = SM_VA_ARG(ap, long);
282                                break;
283                          case 'e':
284                                argv[i].v_exc = SM_VA_ARG(ap, SM_EXC_T*);
285                                break;
286                          case 's':
287                                argv[i].v_str = SM_VA_ARG(ap, char*);
288                                break;
289                          case 'r':
290                                SM_REQUIRE(etype->etype_argformat[i+1] == '\0');
291                                argv[i].v_str = SM_VA_ARG(ap, char*);
292                                break;
293                          default:
294                                sm_abort("sm_exc_vnew_x: bad argformat '%c'",
295                                        etype->etype_argformat[i]);
296                        }
297                }
298
299                /*
300                **  Step 3.  Scan argv, and allocate space for all
301                **  string arguments.  si is the number of elements
302                **  of argv that have been processed so far.
303                **  On failure, free exc, argv, all the exception arguments
304                **  and all of the strings that have been copied.
305                */
306
307                for (si = 0; si < argc; ++si)
308                {
309                        switch (etype->etype_argformat[si])
310                        {
311                          case 's':
312                            {
313                                char *str = argv[si].v_str;
314                                if (str != NULL)
315                                    argv[si].v_str = sm_strdup_x(str);
316                            }
317                            break;
318                          case 'r':
319                            {
320                                char *fmt = argv[si].v_str;
321                                if (fmt != NULL)
322                                    argv[si].v_str = sm_vstringf_x(fmt, ap);
323                            }
324                            break;
325                        }
326                }
327        }
328        SM_EXCEPT(e, "*")
329        {
330                if (exc == NULL || argv == NULL)
331                {
332                        /*
333                        **  Failure in step 1 or step 2.
334                        **  Scan ap and free all exception arguments.
335                        */
336
337                        for (i = 0; i < argc; ++i)
338                        {
339                                switch (etype->etype_argformat[i])
340                                {
341                                  case 'i':
342                                        (void) SM_VA_ARG(ap, int);
343                                        break;
344                                  case 'l':
345                                        (void) SM_VA_ARG(ap, long);
346                                        break;
347                                  case 'e':
348                                        sm_exc_free(SM_VA_ARG(ap, SM_EXC_T*));
349                                        break;
350                                  case 's':
351                                  case 'r':
352                                        (void) SM_VA_ARG(ap, char*);
353                                        break;
354                                }
355                        }
356                }
357                else
358                {
359                        /*
360                        **  Failure in step 3.  Scan argv and free
361                        **  all exception arguments and all string
362                        **  arguments that have been duplicated.
363                        **  Then free argv.
364                        */
365
366                        for (i = 0; i < argc; ++i)
367                        {
368                                switch (etype->etype_argformat[i])
369                                {
370                                  case 'e':
371                                        sm_exc_free(argv[i].v_exc);
372                                        break;
373                                  case 's':
374                                  case 'r':
375                                        if (i < si)
376                                                sm_free(argv[i].v_str);
377                                        break;
378                                }
379                        }
380                        sm_free(argv);
381                }
382                sm_free(exc);
383                sm_exc_raise_x(e);
384        }
385        SM_END_TRY
386
387        return exc;
388}
389
390/*
391**  SM_EXC_NEW_X -- Construct a new exception object.
392**
393**      Parameters:
394**              etype -- type of exception.
395**              ... -- varargs.
396**
397**      Returns:
398**              pointer to exception object.
399*/
400
401SM_EXC_T *
402#if SM_VA_STD
403sm_exc_new_x(
404        const SM_EXC_TYPE_T *etype,
405        ...)
406#else /* SM_VA_STD */
407sm_exc_new_x(etype, va_alist)
408        const SM_EXC_TYPE_T *etype;
409        va_dcl
410#endif /* SM_VA_STD */
411{
412        SM_EXC_T *exc;
413        SM_VA_LOCAL_DECL
414
415        SM_VA_START(ap, etype);
416        exc = sm_exc_vnew_x(etype, ap);
417        SM_VA_END(ap);
418        return exc;
419}
420
421/*
422**  SM_ADDREF -- Add a reference to an exception object.
423**
424**      Parameters:
425**              exc -- exception object.
426**
427**      Returns:
428**              exc itself.
429*/
430
431SM_EXC_T *
432sm_addref(exc)
433        SM_EXC_T *exc;
434{
435        SM_REQUIRE_ISA(exc, SmExcMagic);
436        if (exc->exc_refcount != 0)
437                ++exc->exc_refcount;
438        return exc;
439}
440
441/*
442**  SM_EXC_FREE -- Destroy a reference to an exception object.
443**
444**      Parameters:
445**              exc -- exception object.
446**
447**      Returns:
448**              none.
449*/
450
451void
452sm_exc_free(exc)
453        SM_EXC_T *exc;
454{
455        if (exc == NULL)
456                return;
457        SM_REQUIRE(exc->sm_magic == SmExcMagic);
458        if (exc->exc_refcount == 0)
459                return;
460        if (--exc->exc_refcount == 0)
461        {
462                int i, c;
463
464                for (i = 0; (c = exc->exc_type->etype_argformat[i]) != '\0';
465                     ++i)
466                {
467                        switch (c)
468                        {
469                          case 's':
470                          case 'r':
471                                sm_free(exc->exc_argv[i].v_str);
472                                break;
473                          case 'e':
474                                sm_exc_free(exc->exc_argv[i].v_exc);
475                                break;
476                        }
477                }
478                exc->sm_magic = NULL;
479                sm_free(exc->exc_argv);
480                sm_free(exc);
481        }
482}
483
484/*
485**  SM_EXC_MATCH -- Match exception category against a glob pattern.
486**
487**      Parameters:
488**              exc -- exception.
489**              pattern -- glob pattern.
490**
491**      Returns:
492**              true iff match.
493*/
494
495bool
496sm_exc_match(exc, pattern)
497        SM_EXC_T *exc;
498        const char *pattern;
499{
500        if (exc == NULL)
501                return false;
502        SM_REQUIRE(exc->sm_magic == SmExcMagic);
503        return sm_match(exc->exc_type->etype_category, pattern);
504}
505
506/*
507**  SM_EXC_WRITE -- Write exception message to a stream (wo trailing newline).
508**
509**      Parameters:
510**              exc -- exception.
511**              stream -- file for output.
512**
513**      Returns:
514**              none.
515*/
516
517void
518sm_exc_write(exc, stream)
519        SM_EXC_T *exc;
520        SM_FILE_T *stream;
521{
522        SM_REQUIRE_ISA(exc, SmExcMagic);
523        exc->exc_type->etype_print(exc, stream);
524}
525
526/*
527**  SM_EXC_PRINT -- Print exception message to a stream (with trailing newline).
528**
529**      Parameters:
530**              exc -- exception.
531**              stream -- file for output.
532**
533**      Returns:
534**              none.
535*/
536
537void
538sm_exc_print(exc, stream)
539        SM_EXC_T *exc;
540        SM_FILE_T *stream;
541{
542        SM_REQUIRE_ISA(exc, SmExcMagic);
543        exc->exc_type->etype_print(exc, stream);
544        (void) sm_io_putc(stream, SM_TIME_DEFAULT, '\n');
545}
546
547SM_EXC_HANDLER_T *SmExcHandler = NULL;
548static SM_EXC_DEFAULT_HANDLER_T SmExcDefaultHandler = NULL;
549
550/*
551**  SM_EXC_NEWTHREAD -- Initialize exception handling for new process/thread.
552**
553**      Parameters:
554**              h -- default exception handler.
555**
556**      Returns:
557**              none.
558*/
559
560/*
561**  Initialize a new process or a new thread by clearing the
562**  exception handler stack and optionally setting a default
563**  exception handler function.  Call this at the beginning of main,
564**  or in a new process after calling fork, or in a new thread.
565**
566**  This function is a luxury, not a necessity.
567**  If h != NULL then you can get the same effect by
568**  wrapping the body of main, or the body of a forked child
569**  or a new thread in SM_TRY ... SM_EXCEPT(e,"*") h(e); SM_END_TRY.
570*/
571
572void
573sm_exc_newthread(h)
574        SM_EXC_DEFAULT_HANDLER_T h;
575{
576        SmExcHandler = NULL;
577        SmExcDefaultHandler = h;
578}
579
580/*
581**  SM_EXC_RAISE_X -- Raise an exception.
582**
583**      Parameters:
584**              exc -- exception.
585**
586**      Returns:
587**              doesn't.
588*/
589
590void
591sm_exc_raise_x(exc)
592        SM_EXC_T *exc;
593{
594        SM_REQUIRE_ISA(exc, SmExcMagic);
595
596        if (SmExcHandler == NULL)
597        {
598                if (SmExcDefaultHandler != NULL)
599                {
600                        SM_EXC_DEFAULT_HANDLER_T h;
601
602                        /*
603                        **  If defined, the default handler is expected
604                        **  to terminate the current thread of execution
605                        **  using exit() or pthread_exit().
606                        **  If it instead returns normally, then we fall
607                        **  through to the default case below.  If it
608                        **  raises an exception, then sm_exc_raise_x is
609                        **  re-entered and, because we set SmExcDefaultHandler
610                        **  to NULL before invoking h, we will again
611                        **  end up in the default case below.
612                        */
613
614                        h = SmExcDefaultHandler;
615                        SmExcDefaultHandler = NULL;
616                        (*h)(exc);
617                }
618
619                /*
620                **  No exception handler, so print the error and exit.
621                **  To override this behaviour on a program wide basis,
622                **  call sm_exc_newthread or put an exception handler in main().
623                **
624                **  XXX TODO: map the exception category to an exit code
625                **  XXX from <sysexits.h>.
626                */
627
628                sm_exc_print(exc, smioerr);
629                exit(255);
630        }
631
632        if (SmExcHandler->eh_value == NULL)
633                SmExcHandler->eh_value = exc;
634        else
635                sm_exc_free(exc);
636
637        sm_longjmp_nosig(SmExcHandler->eh_context, 1);
638}
639
640/*
641**  SM_EXC_RAISENEW_X -- shorthand for sm_exc_raise_x(sm_exc_new_x(...))
642**
643**      Parameters:
644**              etype -- type of exception.
645**              ap -- varargs.
646**
647**      Returns:
648**              none.
649*/
650
651void
652#if SM_VA_STD
653sm_exc_raisenew_x(
654        const SM_EXC_TYPE_T *etype,
655        ...)
656#else
657sm_exc_raisenew_x(etype, va_alist)
658        const SM_EXC_TYPE_T *etype;
659        va_dcl
660#endif
661{
662        SM_EXC_T *exc;
663        SM_VA_LOCAL_DECL
664
665        SM_VA_START(ap, etype);
666        exc = sm_exc_vnew_x(etype, ap);
667        SM_VA_END(ap);
668        sm_exc_raise_x(exc);
669}
Note: See TracBrowser for help on using the repository browser.