diff options
author | Todd C. Miller <millert@cvs.openbsd.org> | 2001-09-11 18:55:53 +0000 |
---|---|---|
committer | Todd C. Miller <millert@cvs.openbsd.org> | 2001-09-11 18:55:53 +0000 |
commit | 4643c616c81fb1198b29c3eaff4d697392c8dc4b (patch) | |
tree | 4afa26a5f1a2ef0e47061eb08b6d339bc7e8dad1 /gnu/usr.sbin/sendmail/libsm/exc.c | |
parent | 8afe339a41c898cc4a2d42d03d115b16f2053bad (diff) |
sendmail 8.12.0 with $Id tags converted to $Sendmail
Diffstat (limited to 'gnu/usr.sbin/sendmail/libsm/exc.c')
-rw-r--r-- | gnu/usr.sbin/sendmail/libsm/exc.c | 668 |
1 files changed, 668 insertions, 0 deletions
diff --git a/gnu/usr.sbin/sendmail/libsm/exc.c b/gnu/usr.sbin/sendmail/libsm/exc.c new file mode 100644 index 00000000000..1ce07f65ad7 --- /dev/null +++ b/gnu/usr.sbin/sendmail/libsm/exc.c @@ -0,0 +1,668 @@ +/* + * Copyright (c) 2000-2001 Sendmail, Inc. and its suppliers. + * All rights reserved. + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the sendmail distribution. + * + */ + +#include <sm/gen.h> +SM_RCSID("@(#)$Sendmail: exc.c,v 1.43 2001/09/04 22:41:27 ca Exp $") + +/* +** exception handling +** For documentation, see exc.html +*/ + +#include <ctype.h> +#include <string.h> + +#include <sm/exc.h> +#include <sm/heap.h> +#include <sm/string.h> +#include <sm/varargs.h> +#include <sm/io.h> + +const char SmExcMagic[] = "sm_exc"; +const char SmExcTypeMagic[] = "sm_exc_type"; + +/* +** SM_ETYPE_PRINTF -- printf for exception types. +** +** Parameters: +** exc -- exception. +** stream -- file for output. +** +** Returns: +** none. +*/ + +/* +** A simple formatted print function that can be used as the print function +** by most exception types. It prints the printcontext string, interpreting +** occurrences of %0 through %9 as references to the argument vector. +** If exception argument 3 is an int or long, then %3 will print the +** argument in decimal, and %o3 or %x3 will print it in octal or hex. +*/ + +void +sm_etype_printf(exc, stream) + SM_EXC_T *exc; + SM_FILE_T *stream; +{ + size_t n = strlen(exc->exc_type->etype_argformat); + const char *p, *s; + char format; + + for (p = exc->exc_type->etype_printcontext; *p != '\0'; ++p) + { + if (*p != '%') + { + (void) sm_io_putc(stream, SM_TIME_DEFAULT, *p); + continue; + } + ++p; + if (*p == '\0') + { + (void) sm_io_putc(stream, SM_TIME_DEFAULT, '%'); + break; + } + if (*p == '%') + { + (void) sm_io_putc(stream, SM_TIME_DEFAULT, '%'); + continue; + } + format = '\0'; + if (isalpha(*p)) + { + format = *p++; + if (*p == '\0') + { + (void) sm_io_putc(stream, SM_TIME_DEFAULT, '%'); + (void) sm_io_putc(stream, SM_TIME_DEFAULT, + format); + break; + } + } + if (isdigit(*p)) + { + size_t i = *p - '0'; + if (i < n) + { + switch (exc->exc_type->etype_argformat[i]) + { + case 's': + case 'r': + s = exc->exc_argv[i].v_str; + if (s == NULL) + s = "(null)"; + sm_io_fputs(stream, SM_TIME_DEFAULT, s); + continue; + case 'i': + sm_io_fprintf(stream, + SM_TIME_DEFAULT, + format == 'o' ? "%o" + : format == 'x' ? "%x" + : "%d", + exc->exc_argv[i].v_int); + continue; + case 'l': + sm_io_fprintf(stream, + SM_TIME_DEFAULT, + format == 'o' ? "%lo" + : format == 'x' ? "%lx" + : "%ld", + exc->exc_argv[i].v_long); + continue; + case 'e': + sm_exc_write(exc->exc_argv[i].v_exc, + stream); + continue; + } + } + } + (void) sm_io_putc(stream, SM_TIME_DEFAULT, '%'); + if (format) + (void) sm_io_putc(stream, SM_TIME_DEFAULT, format); + (void) sm_io_putc(stream, SM_TIME_DEFAULT, *p); + } +} + +/* +** Standard exception types. +*/ + +/* +** SM_ETYPE_OS_PRINT -- Print OS related exception. +** +** Parameters: +** exc -- exception. +** stream -- file for output. +** +** Returns: +** none. +*/ + +static void +sm_etype_os_print __P(( + SM_EXC_T *exc, + SM_FILE_T *stream)); + +static void +sm_etype_os_print(exc, stream) + SM_EXC_T *exc; + SM_FILE_T *stream; +{ + int err = exc->exc_argv[0].v_int; + char *syscall = exc->exc_argv[1].v_str; + char *sysargs = exc->exc_argv[2].v_str; + + if (sysargs) + sm_io_fprintf(stream, SM_TIME_DEFAULT, "%s: %s failed: %s", + sysargs, syscall, strerror(err)); + else + sm_io_fprintf(stream, SM_TIME_DEFAULT, "%s failed: %s", syscall, + strerror(err)); +} + +/* +** SmEtypeOs represents the failure of a Unix system call. +** The three arguments are: +** int errno (eg, ENOENT) +** char *syscall (eg, "open") +** char *sysargs (eg, NULL or "/etc/mail/sendmail.cf") +*/ + +const SM_EXC_TYPE_T SmEtypeOs = +{ + SmExcTypeMagic, + "E:sm.os", + "isr", + sm_etype_os_print, + NULL, +}; + +/* +** SmEtypeErr is a completely generic error which should only be +** used in applications and test programs. Libraries should use +** more specific exception codes. +*/ + +const SM_EXC_TYPE_T SmEtypeErr = +{ + SmExcTypeMagic, + "E:sm.err", + "r", + sm_etype_printf, + "%0", +}; + +/* +** SM_EXC_VNEW_X -- Construct a new exception object. +** +** Parameters: +** etype -- type of exception. +** ap -- varargs. +** +** Returns: +** pointer to exception object. +*/ + +/* +** This is an auxiliary function called by sm_exc_new_x and sm_exc_raisenew_x. +** +** If an exception is raised, then to avoid a storage leak, we must: +** (a) Free all storage we have allocated. +** (b) Free all exception arguments in the varargs list. +** Getting this right is tricky. +** +** To see why (b) is required, consider the code fragment +** SM_EXCEPT(exc, "*") +** sm_exc_raisenew_x(&MyEtype, exc); +** SM_END_TRY +** In the normal case, sm_exc_raisenew_x will allocate and raise a new +** exception E that owns exc. When E is eventually freed, exc is also freed. +** In the exceptional case, sm_exc_raisenew_x must free exc before raising +** an out-of-memory exception so that exc is not leaked. +*/ + +SM_EXC_T * +sm_exc_vnew_x(etype, ap) + const SM_EXC_TYPE_T *etype; + va_list SM_NONVOLATILE ap; +{ + /* + ** All variables that are modified in the SM_TRY clause and + ** referenced in the SM_EXCEPT clause must be declared volatile. + */ + + /* NOTE: Type of si, i, and argc *must* match */ + SM_EXC_T * volatile exc = NULL; + int volatile si = 0; + SM_VAL_T * volatile argv = NULL; + int i, argc; + + SM_REQUIRE_ISA(etype, SmExcTypeMagic); + argc = strlen(etype->etype_argformat); + SM_TRY + { + /* + ** Step 1. Allocate the exception structure. + ** On failure, scan the varargs list and free all + ** exception arguments. + */ + + exc = sm_malloc_x(sizeof(SM_EXC_T)); + exc->sm_magic = SmExcMagic; + exc->exc_refcount = 1; + exc->exc_type = etype; + exc->exc_argv = NULL; + + /* + ** Step 2. Allocate the argument vector. + ** On failure, free exc, scan the varargs list and free all + ** exception arguments. On success, scan the varargs list, + ** and copy the arguments into argv. + */ + + argv = sm_malloc_x(argc * sizeof(SM_VAL_T)); + exc->exc_argv = argv; + for (i = 0; i < argc; ++i) + { + switch (etype->etype_argformat[i]) + { + case 'i': + argv[i].v_int = SM_VA_ARG(ap, int); + break; + case 'l': + argv[i].v_long = SM_VA_ARG(ap, long); + break; + case 'e': + argv[i].v_exc = SM_VA_ARG(ap, SM_EXC_T*); + break; + case 's': + argv[i].v_str = SM_VA_ARG(ap, char*); + break; + case 'r': + SM_REQUIRE(etype->etype_argformat[i+1] == '\0'); + argv[i].v_str = SM_VA_ARG(ap, char*); + break; + default: + sm_abort("sm_exc_vnew_x: bad argformat '%c'", + etype->etype_argformat[i]); + } + } + + /* + ** Step 3. Scan argv, and allocate space for all + ** string arguments. si is the number of elements + ** of argv that have been processed so far. + ** On failure, free exc, argv, all the exception arguments + ** and all of the strings that have been copied. + */ + + for (si = 0; si < argc; ++si) + { + switch (etype->etype_argformat[si]) + { + case 's': + { + char *str = argv[si].v_str; + if (str != NULL) + argv[si].v_str = sm_strdup_x(str); + } + break; + case 'r': + { + char *fmt = argv[si].v_str; + if (fmt != NULL) + argv[si].v_str = sm_vstringf_x(fmt, ap); + } + break; + } + } + } + SM_EXCEPT(e, "*") + { + if (exc == NULL || argv == NULL) + { + /* + ** Failure in step 1 or step 2. + ** Scan ap and free all exception arguments. + */ + + for (i = 0; i < argc; ++i) + { + switch (etype->etype_argformat[i]) + { + case 'i': + (void) SM_VA_ARG(ap, int); + break; + case 'l': + (void) SM_VA_ARG(ap, long); + break; + case 'e': + sm_exc_free(SM_VA_ARG(ap, SM_EXC_T*)); + break; + case 's': + case 'r': + (void) SM_VA_ARG(ap, char*); + break; + } + } + } + else + { + /* + ** Failure in step 3. Scan argv and free + ** all exception arguments and all string + ** arguments that have been duplicated. + ** Then free argv. + */ + + for (i = 0; i < argc; ++i) + { + switch (etype->etype_argformat[i]) + { + case 'e': + sm_exc_free(argv[i].v_exc); + break; + case 's': + case 'r': + if (i < si) + sm_free(argv[i].v_str); + break; + } + } + sm_free(argv); + } + sm_free(exc); + sm_exc_raise_x(e); + } + SM_END_TRY + + return exc; +} + +/* +** SM_EXC_NEW_X -- Construct a new exception object. +** +** Parameters: +** etype -- type of exception. +** ... -- varargs. +** +** Returns: +** pointer to exception object. +*/ + +SM_EXC_T * +#if SM_VA_STD +sm_exc_new_x( + const SM_EXC_TYPE_T *etype, + ...) +#else /* SM_VA_STD */ +sm_exc_new_x(etype, va_alist) + const SM_EXC_TYPE_T *etype; + va_dcl +#endif /* SM_VA_STD */ +{ + SM_EXC_T *exc; + SM_VA_LOCAL_DECL + + SM_VA_START(ap, etype); + exc = sm_exc_vnew_x(etype, ap); + SM_VA_END(ap); + return exc; +} + +/* +** SM_ADDREF -- Add a reference to an exception object. +** +** Parameters: +** exc -- exception object. +** +** Returns: +** exc itself. +*/ + +SM_EXC_T * +sm_addref(exc) + SM_EXC_T *exc; +{ + SM_REQUIRE_ISA(exc, SmExcMagic); + if (exc->exc_refcount != 0) + ++exc->exc_refcount; + return exc; +} + +/* +** SM_EXC_FREE -- Destroy a reference to an exception object. +** +** Parameters: +** exc -- exception object. +** +** Returns: +** none. +*/ + +void +sm_exc_free(exc) + SM_EXC_T *exc; +{ + if (exc == NULL) + return; + SM_REQUIRE(exc->sm_magic == SmExcMagic); + if (exc->exc_refcount == 0) + return; + if (--exc->exc_refcount == 0) + { + int i, c; + + for (i = 0; (c = exc->exc_type->etype_argformat[i]) != '\0'; + ++i) + { + switch (c) + { + case 's': + case 'r': + sm_free(exc->exc_argv[i].v_str); + break; + case 'e': + sm_exc_free(exc->exc_argv[i].v_exc); + break; + } + } + exc->sm_magic = NULL; + sm_free(exc->exc_argv); + sm_free(exc); + } +} + +/* +** SM_EXC_MATCH -- Match exception category against a glob pattern. +** +** Parameters: +** exc -- exception. +** pattern -- glob pattern. +** +** Returns: +** true iff match. +*/ + +bool +sm_exc_match(exc, pattern) + SM_EXC_T *exc; + const char *pattern; +{ + if (exc == NULL) + return false; + SM_REQUIRE(exc->sm_magic == SmExcMagic); + return sm_match(exc->exc_type->etype_category, pattern); +} + +/* +** SM_EXC_WRITE -- Write exception message to a stream (wo trailing newline). +** +** Parameters: +** exc -- exception. +** stream -- file for output. +** +** Returns: +** none. +*/ + +void +sm_exc_write(exc, stream) + SM_EXC_T *exc; + SM_FILE_T *stream; +{ + SM_REQUIRE_ISA(exc, SmExcMagic); + exc->exc_type->etype_print(exc, stream); +} + +/* +** SM_EXC_PRINT -- Print exception message to a stream (with trailing newline). +** +** Parameters: +** exc -- exception. +** stream -- file for output. +** +** Returns: +** none. +*/ + +void +sm_exc_print(exc, stream) + SM_EXC_T *exc; + SM_FILE_T *stream; +{ + SM_REQUIRE_ISA(exc, SmExcMagic); + exc->exc_type->etype_print(exc, stream); + (void) sm_io_putc(stream, SM_TIME_DEFAULT, '\n'); +} + +SM_EXC_HANDLER_T *SmExcHandler = NULL; +static SM_EXC_DEFAULT_HANDLER_T SmExcDefaultHandler = NULL; + +/* +** SM_EXC_NEWTHREAD -- Initialize exception handling for new process/thread. +** +** Parameters: +** h -- default exception handler. +** +** Returns: +** none. +*/ + +/* +** Initialize a new process or a new thread by clearing the +** exception handler stack and optionally setting a default +** exception handler function. Call this at the beginning of main, +** or in a new process after calling fork, or in a new thread. +** +** This function is a luxury, not a necessity. +** If h != NULL then you can get the same effect by +** wrapping the body of main, or the body of a forked child +** or a new thread in SM_TRY ... SM_EXCEPT(e,"*") h(e); SM_END_TRY. +*/ + +void +sm_exc_newthread(h) + SM_EXC_DEFAULT_HANDLER_T h; +{ + SmExcHandler = NULL; + SmExcDefaultHandler = h; +} + +/* +** SM_EXC_RAISE_X -- Raise an exception. +** +** Parameters: +** exc -- exception. +** +** Returns: +** doesn't. +*/ + +void +sm_exc_raise_x(exc) + SM_EXC_T *exc; +{ + SM_REQUIRE_ISA(exc, SmExcMagic); + + if (SmExcHandler == NULL) + { + if (SmExcDefaultHandler != NULL) + { + SM_EXC_DEFAULT_HANDLER_T h; + + /* + ** If defined, the default handler is expected + ** to terminate the current thread of execution + ** using exit() or pthread_exit(). + ** If it instead returns normally, then we fall + ** through to the default case below. If it + ** raises an exception, then sm_exc_raise_x is + ** re-entered and, because we set SmExcDefaultHandler + ** to NULL before invoking h, we will again + ** end up in the default case below. + */ + + h = SmExcDefaultHandler; + SmExcDefaultHandler = NULL; + (*h)(exc); + } + + /* + ** No exception handler, so print the error and exit. + ** To override this behaviour on a program wide basis, + ** call sm_exc_newthread or put an exception handler in main(). + ** + ** XXX TODO: map the exception category to an exit code + ** XXX from <sysexits.h>. + */ + + sm_exc_print(exc, smioerr); + exit(255); + } + + if (SmExcHandler->eh_value == NULL) + SmExcHandler->eh_value = exc; + else + sm_exc_free(exc); + + sm_longjmp_nosig(SmExcHandler->eh_context, 1); +} + +/* +** SM_EXC_RAISENEW_X -- shorthand for sm_exc_raise_x(sm_exc_new_x(...)) +** +** Parameters: +** etype -- type of exception. +** ap -- varargs. +** +** Returns: +** none. +*/ + +void +#if SM_VA_STD +sm_exc_raisenew_x( + const SM_EXC_TYPE_T *etype, + ...) +#else +sm_exc_raisenew_x(etype, va_alist) + const SM_EXC_TYPE_T *etype; + va_dcl +#endif +{ + SM_EXC_T *exc; + SM_VA_LOCAL_DECL + + SM_VA_START(ap, etype); + exc = sm_exc_vnew_x(etype, ap); + SM_VA_END(ap); + sm_exc_raise_x(exc); +} |