Merge from vendor branch WPA_SUPPLICANT:
[dragonfly.git] / contrib / sendmail-8.13.4 / libsm / exc.c
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>
12 SM_RCSID("@(#)$Id: exc.c,v 1.48 2003/12/05 22:45:24 ca 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
29 const char SmExcMagic[] = "sm_exc";
30 const 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
51 void
52 sm_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
149 static void
150 sm_etype_os_print __P((
151         SM_EXC_T *exc,
152         SM_FILE_T *stream));
153
154 static void
155 sm_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
179 const 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
194 const 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
232 SM_EXC_T *
233 sm_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
401 SM_EXC_T *
402 #if SM_VA_STD
403 sm_exc_new_x(
404         const SM_EXC_TYPE_T *etype,
405         ...)
406 #else /* SM_VA_STD */
407 sm_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
431 SM_EXC_T *
432 sm_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
451 void
452 sm_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
495 bool
496 sm_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
517 void
518 sm_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
537 void
538 sm_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
547 SM_EXC_HANDLER_T *SmExcHandler = NULL;
548 static 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
572 void
573 sm_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
590 void SM_DEAD_D
591 sm_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
651 void SM_DEAD_D
652 #if SM_VA_STD
653 sm_exc_raisenew_x(
654         const SM_EXC_TYPE_T *etype,
655         ...)
656 #else
657 sm_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 }