1/*	$NetBSD: postlog.c,v 1.1.1.2 2012/06/09 11:27:17 tron Exp $	*/
2
3/*++
4/* NAME
5/*	postlog 1
6/* SUMMARY
7/*	Postfix-compatible logging utility
8/* SYNOPSIS
9/* .fi
10/* .ad
11/*	\fBpostlog\fR [\fB-iv\fR] [\fB-c \fIconfig_dir\fR]
12/*	[\fB-p \fIpriority\fB] [\fB-t \fItag\fR] [\fItext...\fR]
13/* DESCRIPTION
14/*	The \fBpostlog\fR(1) command implements a Postfix-compatible logging
15/*	interface for use in, for example, shell scripts.
16/*
17/*	By default, \fBpostlog\fR(1) logs the \fItext\fR given on the command
18/*	line as one record. If no \fItext\fR is specified on the command
19/*	line, \fBpostlog\fR(1) reads from standard input and logs each input
20/*	line as one record.
21/*
22/*	Logging is sent to \fBsyslogd\fR(8); when the standard error stream
23/*	is connected to a terminal, logging is sent there as well.
24/*
25/*	The following options are implemented:
26/* .IP "\fB-c \fIconfig_dir\fR"
27/*	Read the \fBmain.cf\fR configuration file in the named directory
28/*	instead of the default configuration directory.
29/* .IP \fB-i\fR
30/*	Include the process ID in the logging tag.
31/* .IP "\fB-p \fIpriority\fR"
32/*	Specifies the logging severity: \fBinfo\fR (default), \fBwarn\fR,
33/*	\fBerror\fR, \fBfatal\fR, or \fBpanic\fR.
34/* .IP "\fB-t \fItag\fR"
35/*	Specifies the logging tag, that is, the identifying name that
36/*	appears at the beginning of each logging record. A default tag
37/*	is used when none is specified.
38/* .IP \fB-v\fR
39/*	Enable verbose logging for debugging purposes. Multiple \fB-v\fR
40/*	options make the software increasingly verbose.
41/* ENVIRONMENT
42/* .ad
43/* .fi
44/* .IP MAIL_CONFIG
45/*	Directory with the \fBmain.cf\fR file.
46/* CONFIGURATION PARAMETERS
47/* .ad
48/* .fi
49/*	The following \fBmain.cf\fR parameters are especially relevant to
50/*	this program.
51/*
52/*	The text below provides only a parameter summary. See
53/*	\fBpostconf\fR(5) for more details including examples.
54/* .IP "\fBconfig_directory (see 'postconf -d' output)\fR"
55/*	The default location of the Postfix main.cf and master.cf
56/*	configuration files.
57/* .IP "\fBsyslog_facility (mail)\fR"
58/*	The syslog facility of Postfix logging.
59/* .IP "\fBsyslog_name (see 'postconf -d' output)\fR"
60/*	The mail system name that is prepended to the process name in syslog
61/*	records, so that "smtpd" becomes, for example, "postfix/smtpd".
62/* SEE ALSO
63/*	postconf(5), configuration parameters
64/*	syslogd(8), syslog daemon
65/* LICENSE
66/* .ad
67/* .fi
68/*	The Secure Mailer license must be distributed with this software.
69/* AUTHOR(S)
70/*	Wietse Venema
71/*	IBM T.J. Watson Research
72/*	P.O. Box 704
73/*	Yorktown Heights, NY 10598, USA
74/*--*/
75
76/* System library. */
77
78#include <sys_defs.h>
79#include <sys/stat.h>
80#include <string.h>
81#include <syslog.h>
82#include <fcntl.h>
83#include <stdlib.h>
84#include <unistd.h>
85
86#ifdef STRCASECMP_IN_STRINGS_H
87#include <strings.h>
88#endif
89
90/* Utility library. */
91
92#include <msg.h>
93#include <vstring.h>
94#include <vstream.h>
95#include <vstring_vstream.h>
96#include <msg_output.h>
97#include <msg_vstream.h>
98#include <msg_syslog.h>
99
100/* Global library. */
101
102#include <mail_params.h>		/* XXX right place for LOG_FACILITY? */
103#include <mail_version.h>
104#include <mail_conf.h>
105#include <mail_task.h>
106
107/* Application-specific. */
108
109 /*
110  * Support for the severity level mapping.
111  */
112struct level_table {
113    char   *name;
114    int     level;
115};
116
117static struct level_table level_table[] = {
118    "info", MSG_INFO,
119    "warn", MSG_WARN,
120    "warning", MSG_WARN,
121    "error", MSG_ERROR,
122    "err", MSG_ERROR,
123    "fatal", MSG_FATAL,
124    "crit", MSG_FATAL,
125    "panic", MSG_PANIC,
126    0,
127};
128
129/* level_map - lookup facility or severity value */
130
131static int level_map(char *name)
132{
133    struct level_table *t;
134
135    for (t = level_table; t->name; t++)
136	if (strcasecmp(t->name, name) == 0)
137	    return (t->level);
138    msg_fatal("bad severity: \"%s\"", name);
139}
140
141/* log_argv - log the command line */
142
143static void log_argv(int level, char **argv)
144{
145    VSTRING *buf = vstring_alloc(100);
146
147    while (*argv) {
148	vstring_strcat(buf, *argv++);
149	if (*argv)
150	    vstring_strcat(buf, " ");
151    }
152    msg_text(level, vstring_str(buf));
153    vstring_free(buf);
154}
155
156/* log_stream - log lines from a stream */
157
158static void log_stream(int level, VSTREAM *fp)
159{
160    VSTRING *buf = vstring_alloc(100);
161
162    while (vstring_get_nonl(buf, fp) != VSTREAM_EOF)
163	msg_text(level, vstring_str(buf));
164    vstring_free(buf);
165}
166
167MAIL_VERSION_STAMP_DECLARE;
168
169/* main - logger */
170
171int     main(int argc, char **argv)
172{
173    struct stat st;
174    char   *slash;
175    int     fd;
176    int     ch;
177    const char *tag;
178    int     log_flags = 0;
179    int     level = MSG_INFO;
180
181    /*
182     * Fingerprint executables and core dumps.
183     */
184    MAIL_VERSION_STAMP_ALLOCATE;
185
186    /*
187     * Be consistent with file permissions.
188     */
189    umask(022);
190
191    /*
192     * To minimize confusion, make sure that the standard file descriptors
193     * are open before opening anything else. XXX Work around for 44BSD where
194     * fstat can return EBADF on an open file descriptor.
195     */
196    for (fd = 0; fd < 3; fd++)
197	if (fstat(fd, &st) == -1
198	    && (close(fd), open("/dev/null", O_RDWR, 0)) != fd)
199	    msg_fatal("open /dev/null: %m");
200
201    /*
202     * Set up diagnostics.
203     */
204    if ((slash = strrchr(argv[0], '/')) != 0 && slash[1])
205	tag = mail_task(slash + 1);
206    else
207	tag = mail_task(argv[0]);
208    if (isatty(STDERR_FILENO))
209	msg_vstream_init(tag, VSTREAM_ERR);
210    msg_syslog_init(tag, LOG_PID, LOG_FACILITY);
211
212    /*
213     * Parse switches.
214     */
215    while ((ch = GETOPT(argc, argv, "c:ip:t:v")) > 0) {
216	switch (ch) {
217	default:
218	    msg_fatal("usage: %s [-c config_dir] [-i] [-p priority] [-t tag] [-v] [text]", tag);
219	    break;
220	case 'c':
221	    if (setenv(CONF_ENV_PATH, optarg, 1) < 0)
222		msg_fatal("out of memory");
223	    break;
224	case 'i':
225	    log_flags |= LOG_PID;
226	    break;
227	case 'p':
228	    level = level_map(optarg);
229	    break;
230	case 't':
231	    tag = optarg;
232	    break;
233	case 'v':
234	    msg_verbose++;
235	    break;
236	}
237    }
238
239    /*
240     * Process the main.cf file. This overrides any logging facility that was
241     * specified with msg_syslog_init();
242     */
243    mail_conf_read();
244    if (tag == 0 && strcmp(var_syslog_name, DEF_SYSLOG_NAME) != 0) {
245	if ((slash = strrchr(argv[0], '/')) != 0 && slash[1])
246	    tag = mail_task(slash + 1);
247	else
248	    tag = mail_task(argv[0]);
249    }
250
251    /*
252     * Re-initialize the logging, this time with the tag specified in main.cf
253     * or on the command line.
254     */
255    if (tag != 0) {
256	if (isatty(STDERR_FILENO))
257	    msg_vstream_init(tag, VSTREAM_ERR);
258	msg_syslog_init(tag, LOG_PID, LOG_FACILITY);
259    }
260
261    /*
262     * Log the command line or log lines from standard input.
263     */
264    if (argc > optind) {
265	log_argv(level, argv + optind);
266    } else {
267	log_stream(level, VSTREAM_IN);
268    }
269    exit(0);
270}
271