xref: /openbsd-src/usr.bin/cvs/log.c (revision c5f4fad510dd427c0c20c0f4d164f60ce24651b6)
1 /*	$OpenBSD: log.c,v 1.15 2005/04/06 21:42:32 jfb Exp $	*/
2 /*
3  * Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org>
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. The name of the author may not be used to endorse or promote products
13  *    derived from this software without specific prior written permission.
14  *
15  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
16  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
17  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
18  * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19  * EXEMPLARY, OR CONSEQUENTIAL  DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
21  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
22  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
23  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
24  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26 
27 #include <sys/types.h>
28 
29 #include <errno.h>
30 #include <stdio.h>
31 #include <string.h>
32 #include <stdlib.h>
33 #include <unistd.h>
34 #include <stdarg.h>
35 #include <syslog.h>
36 
37 #include "log.h"
38 #include "cvs.h"
39 
40 extern char *__progname;
41 
42 
43 #ifdef unused
44 static char *cvs_log_levels[LP_MAX + 1] = {
45 	"debug",
46 	"info",
47 	"notice",
48 	"warning",
49 	"error",
50 	"alert",
51 	"error",
52 	"abort",
53 	"trace",
54 };
55 #endif
56 
57 static int cvs_slpriomap[LP_MAX + 1] = {
58 	LOG_DEBUG,
59 	LOG_INFO,
60 	LOG_NOTICE,
61 	LOG_WARNING,
62 	LOG_ERR,
63 	LOG_ALERT,
64 	LOG_ERR,
65 	LOG_ERR,
66 	LOG_DEBUG,
67 };
68 
69 static u_int cvs_log_dest = LD_STD;
70 static u_int cvs_log_flags = 0;
71 
72 static struct syslog_data cvs_sl = SYSLOG_DATA_INIT;
73 
74 /* filter manipulation macros */
75 #define CVS_LOG_FLTRRST()    (cvs_log_filters = 0)
76 #define CVS_LOG_FLTRSET(l)   (cvs_log_filters |= (1 << l))
77 #define CVS_LOG_FLTRGET(l)   (cvs_log_filters & (1 << l))
78 #define CVS_LOG_FLTRCLR(l)   (cvs_log_filters &= ~(1 << l))
79 
80 static u_int cvs_log_filters;
81 
82 
83 /*
84  * cvs_log_init()
85  *
86  * Initialize the logging facility of the server.
87  * Returns 0 on success, or -1 on failure.
88  */
89 int
90 cvs_log_init(u_int dest, u_int flags)
91 {
92 	int slopt;
93 
94 	cvs_log_dest = dest;
95 	cvs_log_flags = flags;
96 
97 	/* by default, filter only LP_DEBUG and LP_INFO levels */
98 	CVS_LOG_FLTRRST();
99 	CVS_LOG_FLTRSET(LP_DEBUG);
100 	CVS_LOG_FLTRSET(LP_INFO);
101 
102 	/* traces are enabled with the -t command-line option */
103 	CVS_LOG_FLTRSET(LP_TRACE);
104 
105 	if (dest & LD_SYSLOG) {
106 		slopt = 0;
107 
108 		if (dest & LD_CONS)
109 			slopt |= LOG_CONS;
110 		if (flags & LF_PID)
111 			slopt |= LOG_PID;
112 
113 		openlog_r(__progname, slopt, LOG_DAEMON, &cvs_sl);
114 	}
115 
116 	return (0);
117 }
118 
119 
120 /*
121  * cvs_log_cleanup()
122  *
123  * Cleanup the logging facility.
124  */
125 void
126 cvs_log_cleanup(void)
127 {
128 
129 	closelog_r(&cvs_sl);
130 }
131 
132 
133 /*
134  * cvs_log_filter()
135  *
136  * Apply or remove filters on the logging facility.  The exact operation is
137  * specified by the <how> and <level> arguments.  The <how> arguments tells
138  * how the filters will be affected, and <level> gives the log levels that
139  * will be affected by the change.
140  * Returns 0 on success, or -1 on failure.
141  */
142 
143 int
144 cvs_log_filter(u_int how, u_int level)
145 {
146 	u_int i;
147 
148 	if ((level > LP_MAX) && (level != LP_ALL)) {
149 		cvs_log(LP_ERR, "invalid log level for filter");
150 		return (-1);
151 	}
152 
153 	switch (how) {
154 	case LP_FILTER_SET:
155 		if (level == LP_ALL)
156 			for (i = 0; i <= LP_MAX; i++)
157 				CVS_LOG_FLTRSET(i);
158 		else
159 			CVS_LOG_FLTRSET(level);
160 		break;
161 	case LP_FILTER_UNSET:
162 		if (level == LP_ALL)
163 			CVS_LOG_FLTRRST();
164 		else
165 			CVS_LOG_FLTRCLR(level);
166 		break;
167 	default:
168 		return (-1);
169 	}
170 
171 	return (0);
172 }
173 
174 
175 /*
176  * cvs_log()
177  *
178  * Log the format-string message
179  * The <fmt> argument should not have a terminating newline, as this is taken
180  * care of by the logging facility.
181  */
182 int
183 cvs_log(u_int level, const char *fmt, ...)
184 {
185 	int ret;
186 	va_list vap;
187 
188 	va_start(vap, fmt);
189 	ret = cvs_vlog(level, fmt, vap);
190 	va_end(vap);
191 
192 	return (ret);
193 }
194 
195 
196 /*
197  * cvs_vlog()
198  *
199  * The <fmt> argument should not have a terminating newline, as this is taken
200  * care of by the logging facility.
201  */
202 int
203 cvs_vlog(u_int level, const char *fmt, va_list vap)
204 {
205 	int ecp;
206 	char prefix[64], buf[1024], ebuf[32];
207 	FILE *out;
208 
209 	if (level > LP_MAX)
210 		return (-1);
211 
212 	/* apply any filters */
213 	if (CVS_LOG_FLTRGET(level))
214 		return (0);
215 
216 	if (level == LP_ERRNO)
217 		ecp = errno;
218 	else
219 		ecp = 0;
220 
221 #ifdef CVS
222 	/* The cvs program appends the command name to the program name */
223 	if (level == LP_TRACE) {
224 		strlcpy(prefix, " -> ", sizeof(prefix));
225 		if (cvs_cmdop == CVS_OP_SERVER)
226 			prefix[0] = 'S';
227 	} else if (cvs_command != NULL) {
228 		if (level == LP_ABORT)
229 			snprintf(prefix, sizeof(prefix), "%s [%s aborted]",
230 			    __progname, cvs_command);
231 		else
232 			snprintf(prefix, sizeof(prefix), "%s %s", __progname,
233 			    cvs_command);
234 	} else /* just use the standard strlcpy */
235 #endif
236 		strlcpy(prefix, __progname, sizeof(prefix));
237 
238 	if ((cvs_log_flags & LF_PID) && (level != LP_TRACE)) {
239 		snprintf(buf, sizeof(buf), "[%d]", (int)getpid());
240 		strlcat(prefix, buf, sizeof(prefix));
241 	}
242 
243 	vsnprintf(buf, sizeof(buf), fmt, vap);
244 	if (level == LP_ERRNO) {
245 		snprintf(ebuf, sizeof(ebuf), ": %s", strerror(errno));
246 		strlcat(buf, ebuf, sizeof(buf));
247 	}
248 
249 	if (cvs_log_dest & LD_STD) {
250 		if (level <= LP_NOTICE)
251 			out = stdout;
252 		else
253 			out = stderr;
254 
255 #ifdef CVS
256 		if (cvs_cmdop == CVS_OP_SERVER) {
257 			if (out == stdout)
258 				putc('M', out);
259 			else {
260 				out = stdout;
261 				putc('E', out);
262 			}
263 			putc(' ', out);
264 		}
265 #endif
266 
267 		fputs(prefix, out);
268 		if (level != LP_TRACE)
269 			fputs(": ", out);
270 		fputs(buf, out);
271 		fputc('\n', out);
272 	}
273 
274 	if (cvs_log_dest & LD_SYSLOG)
275 		syslog_r(cvs_slpriomap[level], &cvs_sl, "%s", buf);
276 
277 	/* preserve it just in case we changed it? */
278 	if (level == LP_ERRNO)
279 		errno = ecp;
280 
281 	return (0);
282 }
283 
284 
285 #ifdef CVS
286 /*
287  * cvs_printf()
288  *
289  * Wrapper function around printf() that prepends a 'M' command when
290  * the program is acting as server.
291  */
292 int
293 cvs_printf(const char *fmt, ...)
294 {
295 	int ret;
296 	char *nstr, *dp, *sp;
297 	va_list vap;
298 
299 	va_start(vap, fmt);
300 
301 	if (cvs_cmdop == CVS_OP_SERVER) {
302 		ret = vasprintf(&nstr, fmt, vap);
303 		if (ret != -1) {
304 			for (dp = nstr; *dp != '\0';) {
305 				sp = strchr(dp, '\n');
306 				if (sp == NULL)
307 					for (sp = dp; *sp != '\0'; sp++)
308 						;
309 
310 				putc('M', stdout);
311 				putc(' ', stdout);
312 				fwrite(dp, sizeof(char), (size_t)(sp - dp),
313 				    stdout);
314 
315 				if (*sp != '\n')
316 					break;
317 
318 				putc('\n', stdout);
319 				dp = sp + 1;
320 			}
321 			free(nstr);
322 		}
323 	} else
324 		ret = vprintf(fmt, vap);
325 
326 	va_end(vap);
327 	return (ret);
328 }
329 #endif
330