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