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