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