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