1 /* $NetBSD: msg.c,v 1.1.1.1 2009/06/23 10:09:00 tron Exp $ */ 2 3 /*++ 4 /* NAME 5 /* msg 3 6 /* SUMMARY 7 /* diagnostic interface 8 /* SYNOPSIS 9 /* #include <msg.h> 10 /* 11 /* int msg_verbose; 12 /* 13 /* void msg_info(format, ...) 14 /* const char *format; 15 /* 16 /* void msg_warn(format, ...) 17 /* const char *format; 18 /* 19 /* void msg_error(format, ...) 20 /* const char *format; 21 /* 22 /* NORETURN msg_fatal(format, ...) 23 /* const char *format; 24 /* 25 /* NORETURN msg_fatal_status(status, format, ...) 26 /* int status; 27 /* const char *format; 28 /* 29 /* NORETURN msg_panic(format, ...) 30 /* const char *format; 31 /* 32 /* MSG_CLEANUP_FN msg_cleanup(cleanup) 33 /* void (*cleanup)(void); 34 /* AUXILIARY FUNCTIONS 35 /* int msg_error_limit(count) 36 /* int count; 37 /* 38 /* void msg_error_clear() 39 /* DESCRIPTION 40 /* This module reports diagnostics. By default, diagnostics are sent 41 /* to the standard error stream, but the disposition can be changed 42 /* by the user. See the hints below in the SEE ALSO section. 43 /* 44 /* msg_info(), msg_warn(), msg_error(), msg_fatal*() and msg_panic() 45 /* produce a one-line record with the program name, a severity code 46 /* (except for msg_info()), and an informative message. The program 47 /* name must have been set by calling one of the msg_XXX_init() 48 /* functions (see the SEE ALSO section). 49 /* 50 /* msg_error() reports a recoverable error and increments the error 51 /* counter. When the error count exceeds a pre-set limit (default: 13) 52 /* the program terminates by calling msg_fatal(). 53 /* 54 /* msg_fatal() reports an unrecoverable error and terminates the program 55 /* with a non-zero exit status. 56 /* 57 /* msg_fatal_status() reports an unrecoverable error and terminates the 58 /* program with the specified exit status. 59 /* 60 /* msg_panic() reports an internal inconsistency, terminates the 61 /* program immediately (i.e. without calling the optional user-specified 62 /* cleanup routine), and forces a core dump when possible. 63 /* 64 /* msg_cleanup() specifies a function that msg_fatal[_status]() should 65 /* invoke before terminating the program, and returns the 66 /* current function pointer. Specify a null argument to disable 67 /* this feature. 68 /* 69 /* msg_error_limit() sets the error message count limit, and returns. 70 /* the old limit. 71 /* 72 /* msg_error_clear() sets the error message count to zero. 73 /* 74 /* msg_verbose is a global flag that can be set to make software 75 /* more verbose about what it is doing. By default the flag is zero. 76 /* By convention, a larger value means more noise. 77 /* REENTRANCY 78 /* .ad 79 /* .fi 80 /* The msg_info() etc. output routines are protected against 81 /* ordinary recursive calls and against re-entry by signal 82 /* handlers. 83 /* 84 /* Protection against re-entry by signal handlers is subject 85 /* to the following limitations: 86 /* .IP \(bu 87 /* The signal handlers must never return. In other words, the 88 /* signal handlers must do one or more of the following: call 89 /* _exit(), kill the process with a signal, and permanently block 90 /* the process. 91 /* .IP \(bu 92 /* The signal handlers must invoke msg_info() etc. not until 93 /* after the msg_XXX_init() functions complete initialization, 94 /* and not until after the first formatted output to a VSTRING 95 /* or VSTREAM. 96 /* .IP \(bu 97 /* Each msg_cleanup() call-back function, and each Postfix or 98 /* system function invoked by that call-back function, either 99 /* protects itself against recursive calls and re-entry by a 100 /* terminating signal handler, or is called exclusively by the 101 /* msg(3) module. 102 /* .PP 103 /* When re-entrancy is detected, the requested output and 104 /* optional cleanup operations are skipped. Skipping the output 105 /* operations prevents memory corruption of VSTREAM_ERR data 106 /* structures, and prevents deadlock on Linux releases that 107 /* use mutexes within system library routines such as syslog(). 108 /* This protection exists under the condition that these 109 /* specific resources are accessed exclusively via the msg_info() 110 /* etc. functions. 111 /* SEE ALSO 112 /* msg_output(3) specify diagnostics disposition 113 /* msg_stdio(3) direct diagnostics to standard I/O stream 114 /* msg_vstream(3) direct diagnostics to VSTREAM. 115 /* msg_syslog(3) direct diagnostics to syslog daemon 116 /* BUGS 117 /* Some output functions may suffer from intentional or accidental 118 /* record length restrictions that are imposed by library routines 119 /* and/or by the runtime environment. 120 /* 121 /* Code that spawns a child process should almost always reset 122 /* the cleanup handler. The exception is when the parent exits 123 /* immediately and the child continues. 124 /* 125 /* msg_cleanup() may be unsafe in code that changes process 126 /* privileges, because the call-back routine may run with the 127 /* wrong privileges. 128 /* LICENSE 129 /* .ad 130 /* .fi 131 /* The Secure Mailer license must be distributed with this software. 132 /* AUTHOR(S) 133 /* Wietse Venema 134 /* IBM T.J. Watson Research 135 /* P.O. Box 704 136 /* Yorktown Heights, NY 10598, USA 137 /*--*/ 138 139 /* System libraries. */ 140 141 #include <sys_defs.h> 142 #include <stdlib.h> 143 #include <stdarg.h> 144 #include <unistd.h> 145 146 /* Application-specific. */ 147 148 #include "msg.h" 149 #include "msg_output.h" 150 151 /* 152 * Default is verbose logging off. 153 */ 154 int msg_verbose = 0; 155 156 /* 157 * Private state. 158 */ 159 static MSG_CLEANUP_FN msg_cleanup_fn = 0; 160 static int msg_error_count = 0; 161 static int msg_error_bound = 13; 162 163 /* 164 * The msg_exiting flag prevents us from recursively reporting an error with 165 * msg_fatal*() or msg_panic(), and provides a first-level safety net for 166 * optional cleanup actions against signal handler re-entry problems. Note 167 * that msg_vprintf() implements its own guard against re-entry. 168 * 169 * XXX We specify global scope, to discourage the compiler from doing smart 170 * things. 171 */ 172 volatile int msg_exiting = 0; 173 174 /* msg_info - report informative message */ 175 176 void msg_info(const char *fmt,...) 177 { 178 va_list ap; 179 180 va_start(ap, fmt); 181 msg_vprintf(MSG_INFO, fmt, ap); 182 va_end(ap); 183 } 184 185 /* msg_warn - report warning message */ 186 187 void msg_warn(const char *fmt,...) 188 { 189 va_list ap; 190 191 va_start(ap, fmt); 192 msg_vprintf(MSG_WARN, fmt, ap); 193 va_end(ap); 194 } 195 196 /* msg_error - report recoverable error */ 197 198 void msg_error(const char *fmt,...) 199 { 200 va_list ap; 201 202 va_start(ap, fmt); 203 msg_vprintf(MSG_ERROR, fmt, ap); 204 va_end(ap); 205 if (++msg_error_count >= msg_error_bound) 206 msg_fatal("too many errors - program terminated"); 207 } 208 209 /* msg_fatal - report error and terminate gracefully */ 210 211 NORETURN msg_fatal(const char *fmt,...) 212 { 213 va_list ap; 214 215 if (msg_exiting++ == 0) { 216 va_start(ap, fmt); 217 msg_vprintf(MSG_FATAL, fmt, ap); 218 va_end(ap); 219 if (msg_cleanup_fn) 220 msg_cleanup_fn(); 221 } 222 sleep(1); 223 /* In case we're running as a signal handler. */ 224 _exit(1); 225 } 226 227 /* msg_fatal_status - report error and terminate gracefully */ 228 229 NORETURN msg_fatal_status(int status, const char *fmt,...) 230 { 231 va_list ap; 232 233 if (msg_exiting++ == 0) { 234 va_start(ap, fmt); 235 msg_vprintf(MSG_FATAL, fmt, ap); 236 va_end(ap); 237 if (msg_cleanup_fn) 238 msg_cleanup_fn(); 239 } 240 sleep(1); 241 /* In case we're running as a signal handler. */ 242 _exit(status); 243 } 244 245 /* msg_panic - report error and dump core */ 246 247 NORETURN msg_panic(const char *fmt,...) 248 { 249 va_list ap; 250 251 if (msg_exiting++ == 0) { 252 va_start(ap, fmt); 253 msg_vprintf(MSG_PANIC, fmt, ap); 254 va_end(ap); 255 } 256 sleep(1); 257 abort(); /* Die! */ 258 /* In case we're running as a signal handler. */ 259 _exit(1); /* DIE!! */ 260 } 261 262 /* msg_cleanup - specify cleanup routine */ 263 264 MSG_CLEANUP_FN msg_cleanup(MSG_CLEANUP_FN cleanup_fn) 265 { 266 MSG_CLEANUP_FN old_fn = msg_cleanup_fn; 267 268 msg_cleanup_fn = cleanup_fn; 269 return (old_fn); 270 } 271 272 /* msg_error_limit - set error message counter limit */ 273 274 int msg_error_limit(int limit) 275 { 276 int old = msg_error_bound; 277 278 msg_error_bound = limit; 279 return (old); 280 } 281 282 /* msg_error_clear - reset error message counter */ 283 284 void msg_error_clear(void) 285 { 286 msg_error_count = 0; 287 } 288