2 * Copyright (c) 2000-2002 Sendmail, Inc. and its suppliers.
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.
11 #pragma ident "%Z%%M% %I% %E% SMI"
14 SM_RCSID("@(#)$Id: exc.c,v 1.49 2006/12/19 19:28:09 ca Exp $")
18 ** For documentation, see exc.html
24 #include <sm/errstring.h>
27 #include <sm/string.h>
28 #include <sm/varargs.h>
31 const char SmExcMagic
[] = "sm_exc";
32 const char SmExcTypeMagic
[] = "sm_exc_type";
35 ** SM_ETYPE_PRINTF -- printf for exception types.
39 ** stream -- file for output.
46 ** A simple formatted print function that can be used as the print function
47 ** by most exception types. It prints the printcontext string, interpreting
48 ** occurrences of %0 through %9 as references to the argument vector.
49 ** If exception argument 3 is an int or long, then %3 will print the
50 ** argument in decimal, and %o3 or %x3 will print it in octal or hex.
54 sm_etype_printf(exc
, stream
)
58 size_t n
= strlen(exc
->exc_type
->etype_argformat
);
62 for (p
= exc
->exc_type
->etype_printcontext
; *p
!= '\0'; ++p
)
66 (void) sm_io_putc(stream
, SM_TIME_DEFAULT
, *p
);
72 (void) sm_io_putc(stream
, SM_TIME_DEFAULT
, '%');
77 (void) sm_io_putc(stream
, SM_TIME_DEFAULT
, '%');
86 (void) sm_io_putc(stream
, SM_TIME_DEFAULT
, '%');
87 (void) sm_io_putc(stream
, SM_TIME_DEFAULT
,
97 switch (exc
->exc_type
->etype_argformat
[i
])
101 s
= exc
->exc_argv
[i
].v_str
;
104 sm_io_fputs(stream
, SM_TIME_DEFAULT
, s
);
107 sm_io_fprintf(stream
,
110 : format
== 'x' ? "%x"
112 exc
->exc_argv
[i
].v_int
);
115 sm_io_fprintf(stream
,
117 format
== 'o' ? "%lo"
118 : format
== 'x' ? "%lx"
120 exc
->exc_argv
[i
].v_long
);
123 sm_exc_write(exc
->exc_argv
[i
].v_exc
,
129 (void) sm_io_putc(stream
, SM_TIME_DEFAULT
, '%');
131 (void) sm_io_putc(stream
, SM_TIME_DEFAULT
, format
);
132 (void) sm_io_putc(stream
, SM_TIME_DEFAULT
, *p
);
137 ** Standard exception types.
141 ** SM_ETYPE_OS_PRINT -- Print OS related exception.
145 ** stream -- file for output.
152 sm_etype_os_print
__P((
157 sm_etype_os_print(exc
, stream
)
161 int err
= exc
->exc_argv
[0].v_int
;
162 char *syscall
= exc
->exc_argv
[1].v_str
;
163 char *sysargs
= exc
->exc_argv
[2].v_str
;
166 sm_io_fprintf(stream
, SM_TIME_DEFAULT
, "%s: %s failed: %s",
167 sysargs
, syscall
, sm_errstring(err
));
169 sm_io_fprintf(stream
, SM_TIME_DEFAULT
, "%s failed: %s", syscall
,
174 ** SmEtypeOs represents the failure of a Unix system call.
175 ** The three arguments are:
176 ** int errno (eg, ENOENT)
177 ** char *syscall (eg, "open")
178 ** char *sysargs (eg, NULL or "/etc/mail/sendmail.cf")
181 const SM_EXC_TYPE_T SmEtypeOs
=
191 ** SmEtypeErr is a completely generic error which should only be
192 ** used in applications and test programs. Libraries should use
193 ** more specific exception codes.
196 const SM_EXC_TYPE_T SmEtypeErr
=
206 ** SM_EXC_VNEW_X -- Construct a new exception object.
209 ** etype -- type of exception.
213 ** pointer to exception object.
217 ** This is an auxiliary function called by sm_exc_new_x and sm_exc_raisenew_x.
219 ** If an exception is raised, then to avoid a storage leak, we must:
220 ** (a) Free all storage we have allocated.
221 ** (b) Free all exception arguments in the varargs list.
222 ** Getting this right is tricky.
224 ** To see why (b) is required, consider the code fragment
225 ** SM_EXCEPT(exc, "*")
226 ** sm_exc_raisenew_x(&MyEtype, exc);
228 ** In the normal case, sm_exc_raisenew_x will allocate and raise a new
229 ** exception E that owns exc. When E is eventually freed, exc is also freed.
230 ** In the exceptional case, sm_exc_raisenew_x must free exc before raising
231 ** an out-of-memory exception so that exc is not leaked.
234 static SM_EXC_T
*sm_exc_vnew_x
__P((const SM_EXC_TYPE_T
*, va_list SM_NONVOLATILE
));
237 sm_exc_vnew_x(etype
, ap
)
238 const SM_EXC_TYPE_T
*etype
;
239 va_list SM_NONVOLATILE ap
;
242 ** All variables that are modified in the SM_TRY clause and
243 ** referenced in the SM_EXCEPT clause must be declared volatile.
246 /* NOTE: Type of si, i, and argc *must* match */
247 SM_EXC_T
* volatile exc
= NULL
;
249 SM_VAL_T
* volatile argv
= NULL
;
252 SM_REQUIRE_ISA(etype
, SmExcTypeMagic
);
253 argc
= strlen(etype
->etype_argformat
);
257 ** Step 1. Allocate the exception structure.
258 ** On failure, scan the varargs list and free all
259 ** exception arguments.
262 exc
= sm_malloc_x(sizeof(SM_EXC_T
));
263 exc
->sm_magic
= SmExcMagic
;
264 exc
->exc_refcount
= 1;
265 exc
->exc_type
= etype
;
266 exc
->exc_argv
= NULL
;
269 ** Step 2. Allocate the argument vector.
270 ** On failure, free exc, scan the varargs list and free all
271 ** exception arguments. On success, scan the varargs list,
272 ** and copy the arguments into argv.
275 argv
= sm_malloc_x(argc
* sizeof(SM_VAL_T
));
276 exc
->exc_argv
= argv
;
277 for (i
= 0; i
< argc
; ++i
)
279 switch (etype
->etype_argformat
[i
])
282 argv
[i
].v_int
= SM_VA_ARG(ap
, int);
285 argv
[i
].v_long
= SM_VA_ARG(ap
, long);
288 argv
[i
].v_exc
= SM_VA_ARG(ap
, SM_EXC_T
*);
291 argv
[i
].v_str
= SM_VA_ARG(ap
, char*);
294 SM_REQUIRE(etype
->etype_argformat
[i
+1] == '\0');
295 argv
[i
].v_str
= SM_VA_ARG(ap
, char*);
298 sm_abort("sm_exc_vnew_x: bad argformat '%c'",
299 etype
->etype_argformat
[i
]);
304 ** Step 3. Scan argv, and allocate space for all
305 ** string arguments. si is the number of elements
306 ** of argv that have been processed so far.
307 ** On failure, free exc, argv, all the exception arguments
308 ** and all of the strings that have been copied.
311 for (si
= 0; si
< argc
; ++si
)
313 switch (etype
->etype_argformat
[si
])
317 char *str
= argv
[si
].v_str
;
319 argv
[si
].v_str
= sm_strdup_x(str
);
324 char *fmt
= argv
[si
].v_str
;
326 argv
[si
].v_str
= sm_vstringf_x(fmt
, ap
);
334 if (exc
== NULL
|| argv
== NULL
)
337 ** Failure in step 1 or step 2.
338 ** Scan ap and free all exception arguments.
341 for (i
= 0; i
< argc
; ++i
)
343 switch (etype
->etype_argformat
[i
])
346 (void) SM_VA_ARG(ap
, int);
349 (void) SM_VA_ARG(ap
, long);
352 sm_exc_free(SM_VA_ARG(ap
, SM_EXC_T
*));
356 (void) SM_VA_ARG(ap
, char*);
364 ** Failure in step 3. Scan argv and free
365 ** all exception arguments and all string
366 ** arguments that have been duplicated.
370 for (i
= 0; i
< argc
; ++i
)
372 switch (etype
->etype_argformat
[i
])
375 sm_exc_free(argv
[i
].v_exc
);
380 sm_free(argv
[i
].v_str
);
395 ** SM_EXC_NEW_X -- Construct a new exception object.
398 ** etype -- type of exception.
402 ** pointer to exception object.
408 const SM_EXC_TYPE_T
*etype
,
410 #else /* SM_VA_STD */
411 sm_exc_new_x(etype
, va_alist
)
412 const SM_EXC_TYPE_T
*etype
;
414 #endif /* SM_VA_STD */
419 SM_VA_START(ap
, etype
);
420 exc
= sm_exc_vnew_x(etype
, ap
);
426 ** SM_EXC_FREE -- Destroy a reference to an exception object.
429 ** exc -- exception object.
441 SM_REQUIRE(exc
->sm_magic
== SmExcMagic
);
442 if (exc
->exc_refcount
== 0)
444 if (--exc
->exc_refcount
== 0)
448 for (i
= 0; (c
= exc
->exc_type
->etype_argformat
[i
]) != '\0';
455 sm_free(exc
->exc_argv
[i
].v_str
);
458 sm_exc_free(exc
->exc_argv
[i
].v_exc
);
462 exc
->sm_magic
= NULL
;
463 sm_free(exc
->exc_argv
);
469 ** SM_EXC_MATCH -- Match exception category against a glob pattern.
473 ** pattern -- glob pattern.
480 sm_exc_match(exc
, pattern
)
486 SM_REQUIRE(exc
->sm_magic
== SmExcMagic
);
487 return sm_match(exc
->exc_type
->etype_category
, pattern
);
491 ** SM_EXC_WRITE -- Write exception message to a stream (wo trailing newline).
495 ** stream -- file for output.
502 sm_exc_write(exc
, stream
)
506 SM_REQUIRE_ISA(exc
, SmExcMagic
);
507 exc
->exc_type
->etype_print(exc
, stream
);
511 ** SM_EXC_PRINT -- Print exception message to a stream (with trailing newline).
515 ** stream -- file for output.
522 sm_exc_print(exc
, stream
)
526 SM_REQUIRE_ISA(exc
, SmExcMagic
);
527 exc
->exc_type
->etype_print(exc
, stream
);
528 (void) sm_io_putc(stream
, SM_TIME_DEFAULT
, '\n');
531 SM_EXC_HANDLER_T
*SmExcHandler
= NULL
;
532 static SM_EXC_DEFAULT_HANDLER_T SmExcDefaultHandler
= NULL
;
535 ** SM_EXC_NEWTHREAD -- Initialize exception handling for new process/thread.
538 ** h -- default exception handler.
545 ** Initialize a new process or a new thread by clearing the
546 ** exception handler stack and optionally setting a default
547 ** exception handler function. Call this at the beginning of main,
548 ** or in a new process after calling fork, or in a new thread.
550 ** This function is a luxury, not a necessity.
551 ** If h != NULL then you can get the same effect by
552 ** wrapping the body of main, or the body of a forked child
553 ** or a new thread in SM_TRY ... SM_EXCEPT(e,"*") h(e); SM_END_TRY.
558 SM_EXC_DEFAULT_HANDLER_T h
;
561 SmExcDefaultHandler
= h
;
565 ** SM_EXC_RAISE_X -- Raise an exception.
578 SM_REQUIRE_ISA(exc
, SmExcMagic
);
580 if (SmExcHandler
== NULL
)
582 if (SmExcDefaultHandler
!= NULL
)
584 SM_EXC_DEFAULT_HANDLER_T h
;
587 ** If defined, the default handler is expected
588 ** to terminate the current thread of execution
589 ** using exit() or pthread_exit().
590 ** If it instead returns normally, then we fall
591 ** through to the default case below. If it
592 ** raises an exception, then sm_exc_raise_x is
593 ** re-entered and, because we set SmExcDefaultHandler
594 ** to NULL before invoking h, we will again
595 ** end up in the default case below.
598 h
= SmExcDefaultHandler
;
599 SmExcDefaultHandler
= NULL
;
604 ** No exception handler, so print the error and exit.
605 ** To override this behaviour on a program wide basis,
606 ** call sm_exc_newthread or put an exception handler in main().
608 ** XXX TODO: map the exception category to an exit code
609 ** XXX from <sysexits.h>.
612 sm_exc_print(exc
, smioerr
);
616 if (SmExcHandler
->eh_value
== NULL
)
617 SmExcHandler
->eh_value
= exc
;
621 sm_longjmp_nosig(SmExcHandler
->eh_context
, 1);
625 ** SM_EXC_RAISENEW_X -- shorthand for sm_exc_raise_x(sm_exc_new_x(...))
628 ** etype -- type of exception.
638 const SM_EXC_TYPE_T
*etype
,
641 sm_exc_raisenew_x(etype
, va_alist
)
642 const SM_EXC_TYPE_T
*etype
;
649 SM_VA_START(ap
, etype
);
650 exc
= sm_exc_vnew_x(etype
, ap
);