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