1/* $NetBSD: msg_output.c,v 1.4 2022/10/08 16:12:50 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 <msg_output.h> 101 102 /* 103 * Global scope, to discourage the compiler from doing smart things. 104 */ 105volatile int msg_vprintf_level; 106 107 /* 108 * Private state. Allow one nested call, so that one logging error can be 109 * reported to stderr before bailing out. 110 */ 111#define MSG_OUT_NESTING_LIMIT 2 112static MSG_OUTPUT_FN *msg_output_fn = 0; 113static int msg_output_fn_count = 0; 114static VSTRING *msg_buffers[MSG_OUT_NESTING_LIMIT]; 115 116/* msg_output - specify output handler */ 117 118void msg_output(MSG_OUTPUT_FN output_fn) 119{ 120 int i; 121 122 /* 123 * Allocate all resources during initialization. This may result in a 124 * recursive call due to memory allocation error. 125 */ 126 if (msg_buffers[MSG_OUT_NESTING_LIMIT - 1] == 0) { 127 for (i = 0; i < MSG_OUT_NESTING_LIMIT; i++) 128 msg_buffers[i] = vstring_alloc(100); 129 } 130 131 /* 132 * We're not doing this often, so avoid complexity and allocate memory 133 * for an exact fit. 134 */ 135 if (msg_output_fn_count == 0) 136 msg_output_fn = (MSG_OUTPUT_FN *) mymalloc(sizeof(*msg_output_fn)); 137 else 138 msg_output_fn = (MSG_OUTPUT_FN *) myrealloc((void *) msg_output_fn, 139 (msg_output_fn_count + 1) * sizeof(*msg_output_fn)); 140 msg_output_fn[msg_output_fn_count++] = output_fn; 141} 142 143/* msg_printf - format text and log it */ 144 145void msg_printf(int level, const char *format,...) 146{ 147 va_list ap; 148 149 va_start(ap, format); 150 msg_vprintf(level, format, ap); 151 va_end(ap); 152} 153 154/* msg_vprintf - format text and log it */ 155 156void msg_vprintf(int level, const char *format, va_list ap) 157{ 158 int saved_errno = errno; 159 VSTRING *vp; 160 int i; 161 162 if (msg_vprintf_level < MSG_OUT_NESTING_LIMIT) { 163 msg_vprintf_level += 1; 164 /* On-the-fly initialization for test programs and startup errors. */ 165 if (msg_output_fn_count == 0) 166 msg_vstream_init("unknown", VSTREAM_ERR); 167 vp = msg_buffers[msg_vprintf_level - 1]; 168 /* OK if terminating signal handler hijacks control before next stmt. */ 169 vstring_vsprintf(vp, format, ap); 170 printable(vstring_str(vp), '?'); 171 for (i = 0; i < msg_output_fn_count; i++) 172 msg_output_fn[i] (level, vstring_str(vp)); 173 msg_vprintf_level -= 1; 174 } 175 errno = saved_errno; 176} 177