1/*++ 2/* NAME 3/* msg_output 3 4/* SUMMARY 5/* diagnostics output management 6/* SYNOPSIS 7/* #include <msg_output.h> 8/* 9/* typedef void (*MSG_OUTPUT_FN)(int level, char *text) 10/* 11/* void msg_output(output_fn) 12/* MSG_OUTPUT_FN output_fn; 13/* 14/* void msg_printf(level, format, ...) 15/* int level; 16/* const char *format; 17/* 18/* void msg_vprintf(level, format, ap) 19/* int level; 20/* const char *format; 21/* va_list ap; 22/* 23/* void msg_text(level, text) 24/* int level; 25/* const char *text; 26/* DESCRIPTION 27/* This module implements low-level output management for the 28/* msg(3) diagnostics interface. 29/* 30/* msg_output() registers an output handler for the diagnostics 31/* interface. An application can register multiple output handlers. 32/* Output handlers are called in the specified order. 33/* An output handler takes as arguments a severity level (MSG_INFO, 34/* MSG_WARN, MSG_ERROR, MSG_FATAL, MSG_PANIC, monotonically increasing 35/* integer values ranging from 0 to MSG_LAST) and pre-formatted, 36/* sanitized, text in the form of a null-terminated string. 37/* 38/* msg_printf() and msg_vprintf() format their arguments, sanitize the 39/* result, and call the output handlers registered with msg_output(). 40/* 41/* msg_text() copies a pre-formatted text, sanitizes the result, and 42/* calls the output handlers registered with msg_output(). 43/* REENTRANCY 44/* .ad 45/* .fi 46/* The above output routines are protected against ordinary 47/* recursive calls and against re-entry by signal 48/* handlers, with the following limitations: 49/* .IP \(bu 50/* The signal handlers must never return. In other words, the 51/* signal handlers must do one or more of the following: call 52/* _exit(), kill the process with a signal, and permanently 53/* block the process. 54/* .IP \(bu 55/* The signal handlers must call the above output routines not 56/* until after msg_output() completes initialization, and not 57/* until after the first formatted output to a VSTRING or 58/* VSTREAM. 59/* .IP \(bu 60/* Each msg_output() call-back function, and each Postfix or 61/* system function called by that call-back function, either 62/* must protect itself against recursive calls and re-entry 63/* by a terminating signal handler, or it must be called 64/* exclusively by functions in the msg_output(3) module. 65/* .PP 66/* When re-entrancy is detected, the requested output operation 67/* is skipped. This prevents memory corruption of VSTREAM_ERR 68/* data structures, and prevents deadlock on Linux releases 69/* that use mutexes within system library routines such as 70/* syslog(). This protection exists under the condition that 71/* these specific resources are accessed exclusively via 72/* msg_output() call-back functions. 73/* LICENSE 74/* .ad 75/* .fi 76/* The Secure Mailer license must be distributed with this software. 77/* AUTHOR(S) 78/* Wietse Venema 79/* IBM T.J. Watson Research 80/* P.O. Box 704 81/* Yorktown Heights, NY 10598, USA 82/*--*/ 83 84/* System library. */ 85 86#include <sys_defs.h> 87#include <stdarg.h> 88#include <errno.h> 89 90/* Utility library. */ 91 92#include <mymalloc.h> 93#include <vstring.h> 94#include <vstream.h> 95#include <msg_vstream.h> 96#include <stringops.h> 97#include <percentm.h> 98#include <msg_output.h> 99 100 /* 101 * Global scope, to discourage the compiler from doing smart things. 102 */ 103volatile int msg_vprintf_lock; 104volatile int msg_text_lock; 105 106 /* 107 * Private state. 108 */ 109static MSG_OUTPUT_FN *msg_output_fn = 0; 110static int msg_output_fn_count = 0; 111static VSTRING *msg_buffer = 0; 112 113/* msg_output - specify output handler */ 114 115void msg_output(MSG_OUTPUT_FN output_fn) 116{ 117 118 /* 119 * Allocate all resources during initialization. 120 */ 121 if (msg_buffer == 0) 122 msg_buffer = vstring_alloc(100); 123 124 /* 125 * We're not doing this often, so avoid complexity and allocate memory 126 * for an exact fit. 127 */ 128 if (msg_output_fn_count == 0) 129 msg_output_fn = (MSG_OUTPUT_FN *) mymalloc(sizeof(*msg_output_fn)); 130 else 131 msg_output_fn = (MSG_OUTPUT_FN *) myrealloc((char *) msg_output_fn, 132 (msg_output_fn_count + 1) * sizeof(*msg_output_fn)); 133 msg_output_fn[msg_output_fn_count++] = output_fn; 134} 135 136/* msg_printf - format text and log it */ 137 138void msg_printf(int level, const char *format,...) 139{ 140 va_list ap; 141 142 va_start(ap, format); 143 msg_vprintf(level, format, ap); 144 va_end(ap); 145} 146 147/* msg_vprintf - format text and log it */ 148 149void msg_vprintf(int level, const char *format, va_list ap) 150{ 151 int saved_errno = errno; 152 153 if (msg_vprintf_lock == 0) { 154 msg_vprintf_lock = 1; 155 /* On-the-fly initialization for debugging test programs only. */ 156 if (msg_output_fn_count == 0) 157 msg_vstream_init("unknown", VSTREAM_ERR); 158 /* OK if terminating signal handler hijacks control before next stmt. */ 159 vstring_vsprintf(msg_buffer, percentm(format, errno), ap); 160 msg_text(level, vstring_str(msg_buffer)); 161 msg_vprintf_lock = 0; 162 } 163 errno = saved_errno; 164} 165 166/* msg_text - sanitize and log pre-formatted text */ 167 168void msg_text(int level, const char *text) 169{ 170 int i; 171 172 /* 173 * Sanitize the text. Use a private copy if necessary. 174 */ 175 if (msg_text_lock == 0) { 176 msg_text_lock = 1; 177 /* OK if terminating signal handler hijacks control before next stmt. */ 178 if (text != vstring_str(msg_buffer)) 179 vstring_strcpy(msg_buffer, text); 180 printable(vstring_str(msg_buffer), '?'); 181 /* On-the-fly initialization for debugging test programs only. */ 182 if (msg_output_fn_count == 0) 183 msg_vstream_init("unknown", VSTREAM_ERR); 184 for (i = 0; i < msg_output_fn_count; i++) 185 msg_output_fn[i] (level, vstring_str(msg_buffer)); 186 msg_text_lock = 0; 187 } 188} 189