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.
12 SM_RCSID("@(#)$Id: exc.c,v 1.48 2003/12/05 22:45:24 ca Exp $")
16 ** For documentation, see exc.html
22 #include <sm/errstring.h>
25 #include <sm/string.h>
26 #include <sm/varargs.h>
29 const char SmExcMagic
[] = "sm_exc";
30 const char SmExcTypeMagic
[] = "sm_exc_type";
33 ** SM_ETYPE_PRINTF -- printf for exception types.
37 ** stream -- file for output.
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.
52 sm_etype_printf(exc
, stream
)
56 size_t n
= strlen(exc
->exc_type
->etype_argformat
);
60 for (p
= exc
->exc_type
->etype_printcontext
; *p
!= '\0'; ++p
)
64 (void) sm_io_putc(stream
, SM_TIME_DEFAULT
, *p
);
70 (void) sm_io_putc(stream
, SM_TIME_DEFAULT
, '%');
75 (void) sm_io_putc(stream
, SM_TIME_DEFAULT
, '%');
84 (void) sm_io_putc(stream
, SM_TIME_DEFAULT
, '%');
85 (void) sm_io_putc(stream
, SM_TIME_DEFAULT
,
95 switch (exc
->exc_type
->etype_argformat
[i
])
99 s
= exc
->exc_argv
[i
].v_str
;
102 sm_io_fputs(stream
, SM_TIME_DEFAULT
, s
);
105 sm_io_fprintf(stream
,
108 : format
== 'x' ? "%x"
110 exc
->exc_argv
[i
].v_int
);
113 sm_io_fprintf(stream
,
115 format
== 'o' ? "%lo"
116 : format
== 'x' ? "%lx"
118 exc
->exc_argv
[i
].v_long
);
121 sm_exc_write(exc
->exc_argv
[i
].v_exc
,
127 (void) sm_io_putc(stream
, SM_TIME_DEFAULT
, '%');
129 (void) sm_io_putc(stream
, SM_TIME_DEFAULT
, format
);
130 (void) sm_io_putc(stream
, SM_TIME_DEFAULT
, *p
);
135 ** Standard exception types.
139 ** SM_ETYPE_OS_PRINT -- Print OS related exception.
143 ** stream -- file for output.
150 sm_etype_os_print
__P((
155 sm_etype_os_print(exc
, stream
)
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
;
164 sm_io_fprintf(stream
, SM_TIME_DEFAULT
, "%s: %s failed: %s",
165 sysargs
, syscall
, sm_errstring(err
));
167 sm_io_fprintf(stream
, SM_TIME_DEFAULT
, "%s failed: %s", syscall
,
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")
179 const SM_EXC_TYPE_T SmEtypeOs
=
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.
194 const SM_EXC_TYPE_T SmEtypeErr
=
204 ** SM_EXC_VNEW_X -- Construct a new exception object.
207 ** etype -- type of exception.
211 ** pointer to exception object.
215 ** This is an auxiliary function called by sm_exc_new_x and sm_exc_raisenew_x.
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.
222 ** To see why (b) is required, consider the code fragment
223 ** SM_EXCEPT(exc, "*")
224 ** sm_exc_raisenew_x(&MyEtype, exc);
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.
233 sm_exc_vnew_x(etype
, ap
)
234 const SM_EXC_TYPE_T
*etype
;
235 va_list SM_NONVOLATILE ap
;
238 ** All variables that are modified in the SM_TRY clause and
239 ** referenced in the SM_EXCEPT clause must be declared volatile.
242 /* NOTE: Type of si, i, and argc *must* match */
243 SM_EXC_T
* volatile exc
= NULL
;
245 SM_VAL_T
* volatile argv
= NULL
;
248 SM_REQUIRE_ISA(etype
, SmExcTypeMagic
);
249 argc
= strlen(etype
->etype_argformat
);
253 ** Step 1. Allocate the exception structure.
254 ** On failure, scan the varargs list and free all
255 ** exception arguments.
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
;
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.
271 argv
= sm_malloc_x(argc
* sizeof(SM_VAL_T
));
272 exc
->exc_argv
= argv
;
273 for (i
= 0; i
< argc
; ++i
)
275 switch (etype
->etype_argformat
[i
])
278 argv
[i
].v_int
= SM_VA_ARG(ap
, int);
281 argv
[i
].v_long
= SM_VA_ARG(ap
, long);
284 argv
[i
].v_exc
= SM_VA_ARG(ap
, SM_EXC_T
*);
287 argv
[i
].v_str
= SM_VA_ARG(ap
, char*);
290 SM_REQUIRE(etype
->etype_argformat
[i
+1] == '\0');
291 argv
[i
].v_str
= SM_VA_ARG(ap
, char*);
294 sm_abort("sm_exc_vnew_x: bad argformat '%c'",
295 etype
->etype_argformat
[i
]);
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.
307 for (si
= 0; si
< argc
; ++si
)
309 switch (etype
->etype_argformat
[si
])
313 char *str
= argv
[si
].v_str
;
315 argv
[si
].v_str
= sm_strdup_x(str
);
320 char *fmt
= argv
[si
].v_str
;
322 argv
[si
].v_str
= sm_vstringf_x(fmt
, ap
);
330 if (exc
== NULL
|| argv
== NULL
)
333 ** Failure in step 1 or step 2.
334 ** Scan ap and free all exception arguments.
337 for (i
= 0; i
< argc
; ++i
)
339 switch (etype
->etype_argformat
[i
])
342 (void) SM_VA_ARG(ap
, int);
345 (void) SM_VA_ARG(ap
, long);
348 sm_exc_free(SM_VA_ARG(ap
, SM_EXC_T
*));
352 (void) SM_VA_ARG(ap
, char*);
360 ** Failure in step 3. Scan argv and free
361 ** all exception arguments and all string
362 ** arguments that have been duplicated.
366 for (i
= 0; i
< argc
; ++i
)
368 switch (etype
->etype_argformat
[i
])
371 sm_exc_free(argv
[i
].v_exc
);
376 sm_free(argv
[i
].v_str
);
391 ** SM_EXC_NEW_X -- Construct a new exception object.
394 ** etype -- type of exception.
398 ** pointer to exception object.
404 const SM_EXC_TYPE_T
*etype
,
406 #else /* SM_VA_STD */
407 sm_exc_new_x(etype
, va_alist
)
408 const SM_EXC_TYPE_T
*etype
;
410 #endif /* SM_VA_STD */
415 SM_VA_START(ap
, etype
);
416 exc
= sm_exc_vnew_x(etype
, ap
);
422 ** SM_ADDREF -- Add a reference to an exception object.
425 ** exc -- exception object.
435 SM_REQUIRE_ISA(exc
, SmExcMagic
);
436 if (exc
->exc_refcount
!= 0)
442 ** SM_EXC_FREE -- Destroy a reference to an exception object.
445 ** exc -- exception object.
457 SM_REQUIRE(exc
->sm_magic
== SmExcMagic
);
458 if (exc
->exc_refcount
== 0)
460 if (--exc
->exc_refcount
== 0)
464 for (i
= 0; (c
= exc
->exc_type
->etype_argformat
[i
]) != '\0';
471 sm_free(exc
->exc_argv
[i
].v_str
);
474 sm_exc_free(exc
->exc_argv
[i
].v_exc
);
478 exc
->sm_magic
= NULL
;
479 sm_free(exc
->exc_argv
);
485 ** SM_EXC_MATCH -- Match exception category against a glob pattern.
489 ** pattern -- glob pattern.
496 sm_exc_match(exc
, pattern
)
502 SM_REQUIRE(exc
->sm_magic
== SmExcMagic
);
503 return sm_match(exc
->exc_type
->etype_category
, pattern
);
507 ** SM_EXC_WRITE -- Write exception message to a stream (wo trailing newline).
511 ** stream -- file for output.
518 sm_exc_write(exc
, stream
)
522 SM_REQUIRE_ISA(exc
, SmExcMagic
);
523 exc
->exc_type
->etype_print(exc
, stream
);
527 ** SM_EXC_PRINT -- Print exception message to a stream (with trailing newline).
531 ** stream -- file for output.
538 sm_exc_print(exc
, stream
)
542 SM_REQUIRE_ISA(exc
, SmExcMagic
);
543 exc
->exc_type
->etype_print(exc
, stream
);
544 (void) sm_io_putc(stream
, SM_TIME_DEFAULT
, '\n');
547 SM_EXC_HANDLER_T
*SmExcHandler
= NULL
;
548 static SM_EXC_DEFAULT_HANDLER_T SmExcDefaultHandler
= NULL
;
551 ** SM_EXC_NEWTHREAD -- Initialize exception handling for new process/thread.
554 ** h -- default exception handler.
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.
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.
574 SM_EXC_DEFAULT_HANDLER_T h
;
577 SmExcDefaultHandler
= h
;
581 ** SM_EXC_RAISE_X -- Raise an exception.
594 SM_REQUIRE_ISA(exc
, SmExcMagic
);
596 if (SmExcHandler
== NULL
)
598 if (SmExcDefaultHandler
!= NULL
)
600 SM_EXC_DEFAULT_HANDLER_T h
;
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.
614 h
= SmExcDefaultHandler
;
615 SmExcDefaultHandler
= NULL
;
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().
624 ** XXX TODO: map the exception category to an exit code
625 ** XXX from <sysexits.h>.
628 sm_exc_print(exc
, smioerr
);
632 if (SmExcHandler
->eh_value
== NULL
)
633 SmExcHandler
->eh_value
= exc
;
637 sm_longjmp_nosig(SmExcHandler
->eh_context
, 1);
641 ** SM_EXC_RAISENEW_X -- shorthand for sm_exc_raise_x(sm_exc_new_x(...))
644 ** etype -- type of exception.
654 const SM_EXC_TYPE_T
*etype
,
657 sm_exc_raisenew_x(etype
, va_alist
)
658 const SM_EXC_TYPE_T
*etype
;
665 SM_VA_START(ap
, etype
);
666 exc
= sm_exc_vnew_x(etype
, ap
);