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