/*++ /* NAME /* msg 3 /* SUMMARY /* diagnostic interface /* SYNOPSIS /* #include /* /* int msg_verbose; /* /* void msg_info(format, ...) /* const char *format; /* /* void vmsg_info(format, ap) /* const char *format; /* va_list ap; /* /* void msg_warn(format, ...) /* const char *format; /* /* void vmsg_warn(format, ap) /* const char *format; /* va_list ap; /* /* void msg_error(format, ...) /* const char *format; /* /* void vmsg_error(format, ap) /* const char *format; /* va_list ap; /* /* NORETURN msg_fatal(format, ...) /* const char *format; /* /* NORETURN vmsg_fatal(format, ap) /* const char *format; /* va_list ap; /* /* NORETURN msg_fatal_status(status, format, ...) /* int status; /* const char *format; /* /* NORETURN vmsg_fatal_status(status, format, ap) /* int status; /* const char *format; /* va_list ap; /* /* NORETURN msg_panic(format, ...) /* const char *format; /* /* NORETURN vmsg_panic(format, ap) /* const char *format; /* va_list ap; /* /* MSG_CLEANUP_FN msg_cleanup(cleanup) /* void (*cleanup)(void); /* AUXILIARY FUNCTIONS /* int msg_error_limit(count) /* int count; /* /* void msg_error_clear() /* DESCRIPTION /* This module reports diagnostics. By default, diagnostics are sent /* to the standard error stream, but the disposition can be changed /* by the user. See the hints below in the SEE ALSO section. /* /* msg_info(), msg_warn(), msg_error(), msg_fatal*() and msg_panic() /* produce a one-line record with the program name, a severity code /* (except for msg_info()), and an informative message. The program /* name must have been set by calling one of the msg_XXX_init() /* functions (see the SEE ALSO section). /* /* msg_error() reports a recoverable error and increments the error /* counter. When the error count exceeds a pre-set limit (default: 13) /* the program terminates by calling msg_fatal(). /* /* msg_fatal() reports an unrecoverable error and terminates the program /* with a non-zero exit status. /* /* msg_fatal_status() reports an unrecoverable error and terminates the /* program with the specified exit status. /* /* msg_panic() reports an internal inconsistency, terminates the /* program immediately (i.e. without calling the optional user-specified /* cleanup routine), and forces a core dump when possible. /* /* msg_cleanup() specifies a function that msg_fatal[_status]() should /* invoke before terminating the program, and returns the /* current function pointer. Specify a null argument to disable /* this feature. /* /* msg_error_limit() sets the error message count limit, and returns. /* the old limit. /* /* msg_error_clear() sets the error message count to zero. /* /* msg_verbose is a global flag that can be set to make software /* more verbose about what it is doing. By default the flag is zero. /* By convention, a larger value means more noise. /* REENTRANCY /* .ad /* .fi /* The msg_info() etc. output routines are protected against /* ordinary recursive calls and against re-entry by signal /* handlers. /* /* Protection against re-entry by signal handlers is subject /* to the following limitations: /* .IP \(bu /* The signal handlers must never return. In other words, the /* signal handlers must do one or more of the following: call /* _exit(), kill the process with a signal, and permanently block /* the process. /* .IP \(bu /* The signal handlers must invoke msg_info() etc. not until /* after the msg_XXX_init() functions complete initialization, /* and not until after the first formatted output to a VSTRING /* or VSTREAM. /* .IP \(bu /* Each msg_cleanup() call-back function, and each Postfix or /* system function invoked by that call-back function, either /* protects itself against recursive calls and re-entry by a /* terminating signal handler, or is called exclusively by the /* msg(3) module. /* .PP /* When re-entrancy is detected, the requested output and /* optional cleanup operations are skipped. Skipping the output /* operations prevents memory corruption of VSTREAM_ERR data /* structures, and prevents deadlock on Linux releases that /* use mutexes within system library routines such as syslog(). /* This protection exists under the condition that these /* specific resources are accessed exclusively via the msg_info() /* etc. functions. /* SEE ALSO /* msg_output(3) specify diagnostics disposition /* msg_stdio(3) direct diagnostics to standard I/O stream /* msg_vstream(3) direct diagnostics to VSTREAM. /* msg_syslog(3) direct diagnostics to syslog daemon /* BUGS /* Some output functions may suffer from intentional or accidental /* record length restrictions that are imposed by library routines /* and/or by the runtime environment. /* /* Code that spawns a child process should almost always reset /* the cleanup handler. The exception is when the parent exits /* immediately and the child continues. /* /* msg_cleanup() may be unsafe in code that changes process /* privileges, because the call-back routine may run with the /* wrong privileges. /* LICENSE /* .ad /* .fi /* The Secure Mailer license must be distributed with this software. /* AUTHOR(S) /* Wietse Venema /* IBM T.J. Watson Research /* P.O. Box 704 /* Yorktown Heights, NY 10598, USA /*--*/ /* System libraries. */ #include #include #include #include /* Application-specific. */ #include "msg.h" #include "msg_output.h" /* * Default is verbose logging off. */ int msg_verbose = 0; /* * Private state. */ static MSG_CLEANUP_FN msg_cleanup_fn = 0; static int msg_error_count = 0; static int msg_error_bound = 13; /* * The msg_exiting flag prevents us from recursively reporting an error with * msg_fatal*() or msg_panic(), and provides a first-level safety net for * optional cleanup actions against signal handler re-entry problems. Note * that msg_vprintf() implements its own guard against re-entry. * * XXX We specify global scope, to discourage the compiler from doing smart * things. */ volatile int msg_exiting = 0; /* msg_info - report informative message */ void msg_info(const char *fmt,...) { va_list ap; va_start(ap, fmt); vmsg_info(fmt, ap); va_end(ap); } void vmsg_info(const char *fmt, va_list ap) { msg_vprintf(MSG_INFO, fmt, ap); } /* msg_warn - report warning message */ void msg_warn(const char *fmt,...) { va_list ap; va_start(ap, fmt); vmsg_warn(fmt, ap); va_end(ap); } void vmsg_warn(const char *fmt, va_list ap) { msg_vprintf(MSG_WARN, fmt, ap); } /* msg_error - report recoverable error */ void msg_error(const char *fmt,...) { va_list ap; va_start(ap, fmt); vmsg_error(fmt, ap); va_end(ap); } void vmsg_error(const char *fmt, va_list ap) { msg_vprintf(MSG_ERROR, fmt, ap); if (++msg_error_count >= msg_error_bound) msg_fatal("too many errors - program terminated"); } /* msg_fatal - report error and terminate gracefully */ NORETURN msg_fatal(const char *fmt,...) { va_list ap; va_start(ap, fmt); vmsg_fatal(fmt, ap); /* NOTREACHED */ } NORETURN vmsg_fatal(const char *fmt, va_list ap) { if (msg_exiting++ == 0) { msg_vprintf(MSG_FATAL, fmt, ap); if (msg_cleanup_fn) msg_cleanup_fn(); } sleep(1); /* In case we're running as a signal handler. */ _exit(1); } /* msg_fatal_status - report error and terminate gracefully */ NORETURN msg_fatal_status(int status, const char *fmt,...) { va_list ap; va_start(ap, fmt); vmsg_fatal_status(status, fmt, ap); /* NOTREACHED */ } NORETURN vmsg_fatal_status(int status, const char *fmt, va_list ap) { if (msg_exiting++ == 0) { msg_vprintf(MSG_FATAL, fmt, ap); if (msg_cleanup_fn) msg_cleanup_fn(); } sleep(1); /* In case we're running as a signal handler. */ _exit(status); } /* msg_panic - report error and dump core */ NORETURN msg_panic(const char *fmt,...) { va_list ap; va_start(ap, fmt); vmsg_panic(fmt, ap); /* NOTREACHED */ } NORETURN vmsg_panic(const char *fmt, va_list ap) { if (msg_exiting++ == 0) { msg_vprintf(MSG_PANIC, fmt, ap); } sleep(1); abort(); /* Die! */ /* In case we're running as a signal handler. */ _exit(1); /* DIE!! */ } /* msg_cleanup - specify cleanup routine */ MSG_CLEANUP_FN msg_cleanup(MSG_CLEANUP_FN cleanup_fn) { MSG_CLEANUP_FN old_fn = msg_cleanup_fn; msg_cleanup_fn = cleanup_fn; return (old_fn); } /* msg_error_limit - set error message counter limit */ int msg_error_limit(int limit) { int old = msg_error_bound; msg_error_bound = limit; return (old); } /* msg_error_clear - reset error message counter */ void msg_error_clear(void) { msg_error_count = 0; }