1 /* $NetBSD: msg_output.c,v 1.1.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 */ 105 volatile int msg_vprintf_lock; 106 volatile int msg_text_lock; 107 108 /* 109 * Private state. 110 */ 111 static MSG_OUTPUT_FN *msg_output_fn = 0; 112 static int msg_output_fn_count = 0; 113 static VSTRING *msg_buffer = 0; 114 115 /* msg_output - specify output handler */ 116 117 void 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 140 void 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 151 void 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 167 void 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