Annotation of src/usr.bin/cvs/log.c, Revision 1.28
1.28 ! joris 1: /* $OpenBSD: log.c,v 1.27 2005/10/09 23:59:17 joris Exp $ */
1.1 jfb 2: /*
1.2 jfb 3: * Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org>
1.1 jfb 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>
1.16 xsa 31: #include <stdlib.h>
1.1 jfb 32: #include <string.h>
1.16 xsa 33: #include <syslog.h>
1.1 jfb 34: #include <unistd.h>
35:
1.16 xsa 36: #include "cvs.h"
1.1 jfb 37: #include "log.h"
38:
39: extern char *__progname;
40:
1.3 jfb 41:
1.2 jfb 42: #ifdef unused
1.13 jfb 43: static char *cvs_log_levels[LP_MAX + 1] = {
1.1 jfb 44: "debug",
45: "info",
46: "notice",
47: "warning",
48: "error",
49: "alert",
1.4 jfb 50: "error",
51: "abort",
1.13 jfb 52: "trace",
1.1 jfb 53: };
1.2 jfb 54: #endif
1.1 jfb 55:
1.13 jfb 56: static int cvs_slpriomap[LP_MAX + 1] = {
1.1 jfb 57: LOG_DEBUG,
1.24 xsa 58: LOG_INFO,
1.23 xsa 59: LOG_NOTICE,
1.1 jfb 60: LOG_WARNING,
61: LOG_ERR,
62: LOG_ALERT,
63: LOG_ERR,
1.4 jfb 64: LOG_ERR,
1.13 jfb 65: LOG_DEBUG,
1.1 jfb 66: };
67:
1.26 niallo 68: #if !defined(RCSPROG)
1.19 joris 69: static int send_m = 1;
1.26 niallo 70: #endif
1.1 jfb 71: static u_int cvs_log_dest = LD_STD;
72: static u_int cvs_log_flags = 0;
73:
1.10 jfb 74: static struct syslog_data cvs_sl = SYSLOG_DATA_INIT;
1.1 jfb 75:
1.9 jfb 76: /* filter manipulation macros */
1.22 xsa 77: #define CVS_LOG_FLTRRST() (cvs_log_filters = 0)
78: #define CVS_LOG_FLTRSET(l) (cvs_log_filters |= (1 << l))
79: #define CVS_LOG_FLTRGET(l) (cvs_log_filters & (1 << l))
80: #define CVS_LOG_FLTRCLR(l) (cvs_log_filters &= ~(1 << l))
1.1 jfb 81:
1.9 jfb 82: static u_int cvs_log_filters;
1.1 jfb 83:
84:
85: /*
86: * cvs_log_init()
87: *
88: * Initialize the logging facility of the server.
89: * Returns 0 on success, or -1 on failure.
90: */
91: int
92: cvs_log_init(u_int dest, u_int flags)
93: {
94: int slopt;
95:
96: cvs_log_dest = dest;
97: cvs_log_flags = flags;
98:
99: /* by default, filter only LP_DEBUG and LP_INFO levels */
1.9 jfb 100: CVS_LOG_FLTRRST();
101: CVS_LOG_FLTRSET(LP_DEBUG);
102: CVS_LOG_FLTRSET(LP_INFO);
1.1 jfb 103:
1.8 jfb 104: /* traces are enabled with the -t command-line option */
1.9 jfb 105: CVS_LOG_FLTRSET(LP_TRACE);
1.8 jfb 106:
1.1 jfb 107: if (dest & LD_SYSLOG) {
108: slopt = 0;
109:
110: if (dest & LD_CONS)
111: slopt |= LOG_CONS;
112: if (flags & LF_PID)
113: slopt |= LOG_PID;
114:
115: openlog_r(__progname, slopt, LOG_DAEMON, &cvs_sl);
116: }
117:
118: return (0);
119: }
120:
121:
122: /*
123: * cvs_log_cleanup()
124: *
125: * Cleanup the logging facility.
126: */
127: void
128: cvs_log_cleanup(void)
129: {
1.7 tedu 130:
1.1 jfb 131: closelog_r(&cvs_sl);
132: }
133:
134:
135: /*
136: * cvs_log_filter()
137: *
138: * Apply or remove filters on the logging facility. The exact operation is
139: * specified by the <how> and <level> arguments. The <how> arguments tells
140: * how the filters will be affected, and <level> gives the log levels that
141: * will be affected by the change.
142: * Returns 0 on success, or -1 on failure.
143: */
144:
145: int
146: cvs_log_filter(u_int how, u_int level)
147: {
148: u_int i;
149:
150: if ((level > LP_MAX) && (level != LP_ALL)) {
151: cvs_log(LP_ERR, "invalid log level for filter");
152: return (-1);
153: }
154:
155: switch (how) {
156: case LP_FILTER_SET:
157: if (level == LP_ALL)
1.9 jfb 158: for (i = 0; i <= LP_MAX; i++)
159: CVS_LOG_FLTRSET(i);
1.1 jfb 160: else
1.9 jfb 161: CVS_LOG_FLTRSET(level);
1.1 jfb 162: break;
163: case LP_FILTER_UNSET:
164: if (level == LP_ALL)
1.9 jfb 165: CVS_LOG_FLTRRST();
1.1 jfb 166: else
1.9 jfb 167: CVS_LOG_FLTRCLR(level);
1.1 jfb 168: break;
169: default:
170: return (-1);
171: }
172:
173: return (0);
174: }
175:
176:
177: /*
178: * cvs_log()
179: *
180: * Log the format-string message
181: * The <fmt> argument should not have a terminating newline, as this is taken
182: * care of by the logging facility.
183: */
184: int
185: cvs_log(u_int level, const char *fmt, ...)
186: {
187: int ret;
188: va_list vap;
189:
190: va_start(vap, fmt);
191: ret = cvs_vlog(level, fmt, vap);
192: va_end(vap);
193:
194: return (ret);
195: }
196:
197:
198: /*
199: * cvs_vlog()
200: *
201: * The <fmt> argument should not have a terminating newline, as this is taken
202: * care of by the logging facility.
203: */
204: int
205: cvs_vlog(u_int level, const char *fmt, va_list vap)
206: {
207: int ecp;
1.25 xsa 208: char prefix[64], buf[1024], ebuf[255];
1.1 jfb 209: FILE *out;
1.26 niallo 210: #if !defined(RCSPROG)
1.27 joris 211: char *cmdname;
1.20 joris 212: struct cvs_cmd *cmdp;
1.26 niallo 213: #endif
1.1 jfb 214:
1.13 jfb 215: if (level > LP_MAX)
1.4 jfb 216: return (-1);
1.1 jfb 217:
218: /* apply any filters */
1.9 jfb 219: if (CVS_LOG_FLTRGET(level))
1.1 jfb 220: return (0);
221:
222: if (level == LP_ERRNO)
223: ecp = errno;
1.4 jfb 224: else
225: ecp = 0;
1.1 jfb 226:
1.17 xsa 227: /* always use the command name in error messages, not aliases */
1.26 niallo 228: #if !defined(RCSPROG)
1.27 joris 229: if (cvs_command == NULL)
230: cmdname = " ";
231: else {
232: cmdp = cvs_findcmd(cvs_command);
233: cmdname = cmdp->cmd_name;
234: }
1.17 xsa 235:
1.3 jfb 236: /* The cvs program appends the command name to the program name */
1.8 jfb 237: if (level == LP_TRACE) {
238: strlcpy(prefix, " -> ", sizeof(prefix));
239: if (cvs_cmdop == CVS_OP_SERVER)
240: prefix[0] = 'S';
241: } else if (cvs_command != NULL) {
1.4 jfb 242: if (level == LP_ABORT)
243: snprintf(prefix, sizeof(prefix), "%s [%s aborted]",
1.27 joris 244: __progname, cmdname);
1.4 jfb 245: else
246: snprintf(prefix, sizeof(prefix), "%s %s", __progname,
1.27 joris 247: cmdname);
1.6 deraadt 248: } else /* just use the standard strlcpy */
1.26 niallo 249: #endif
1.3 jfb 250: strlcpy(prefix, __progname, sizeof(prefix));
251:
1.8 jfb 252: if ((cvs_log_flags & LF_PID) && (level != LP_TRACE)) {
1.1 jfb 253: snprintf(buf, sizeof(buf), "[%d]", (int)getpid());
254: strlcat(prefix, buf, sizeof(prefix));
255: }
256:
257: vsnprintf(buf, sizeof(buf), fmt, vap);
258: if (level == LP_ERRNO) {
259: snprintf(ebuf, sizeof(ebuf), ": %s", strerror(errno));
260: strlcat(buf, ebuf, sizeof(buf));
261: }
262:
263: if (cvs_log_dest & LD_STD) {
1.24 xsa 264: if (level < LP_NOTICE)
1.1 jfb 265: out = stdout;
266: else
267: out = stderr;
1.3 jfb 268:
1.26 niallo 269: #if !defined(RCSPROG)
1.3 jfb 270: if (cvs_cmdop == CVS_OP_SERVER) {
271: if (out == stdout)
272: putc('M', out);
1.11 jfb 273: else {
274: out = stdout;
1.3 jfb 275: putc('E', out);
1.11 jfb 276: }
1.3 jfb 277: putc(' ', out);
278: }
1.26 niallo 279: #endif
1.1 jfb 280:
1.8 jfb 281: fputs(prefix, out);
282: if (level != LP_TRACE)
283: fputs(": ", out);
284: fputs(buf, out);
285: fputc('\n', out);
1.1 jfb 286: }
287:
288: if (cvs_log_dest & LD_SYSLOG)
289: syslog_r(cvs_slpriomap[level], &cvs_sl, "%s", buf);
290:
291: /* preserve it just in case we changed it? */
292: if (level == LP_ERRNO)
293: errno = ecp;
294:
295: return (0);
1.2 jfb 296: }
297:
298:
299: /*
300: * cvs_printf()
301: *
1.14 jfb 302: * Wrapper function around printf() that prepends a 'M' command when
1.2 jfb 303: * the program is acting as server.
304: */
305: int
306: cvs_printf(const char *fmt, ...)
307: {
308: int ret;
1.26 niallo 309: #if !defined(RCSPROG)
1.14 jfb 310: char *nstr, *dp, *sp;
1.26 niallo 311: #endif
1.2 jfb 312: va_list vap;
313:
314: va_start(vap, fmt);
1.14 jfb 315:
1.26 niallo 316: #if !defined(RCSPROG)
1.14 jfb 317: if (cvs_cmdop == CVS_OP_SERVER) {
318: ret = vasprintf(&nstr, fmt, vap);
319: if (ret != -1) {
320: for (dp = nstr; *dp != '\0';) {
321: sp = strchr(dp, '\n');
322: if (sp == NULL)
323: for (sp = dp; *sp != '\0'; sp++)
324: ;
325:
1.21 joris 326: if (send_m) {
1.18 joris 327: send_m = 0;
328: putc('M', stdout);
329: putc(' ', stdout);
330: }
331:
1.14 jfb 332: fwrite(dp, sizeof(char), (size_t)(sp - dp),
333: stdout);
334:
335: if (*sp != '\n')
336: break;
337:
338: putc('\n', stdout);
1.18 joris 339: send_m = 1;
1.14 jfb 340: dp = sp + 1;
341: }
1.28 ! joris 342: xfree(nstr);
1.14 jfb 343: }
344: } else
1.26 niallo 345: #endif
1.14 jfb 346: ret = vprintf(fmt, vap);
347:
1.2 jfb 348: va_end(vap);
349: return (ret);
1.1 jfb 350: }
1.19 joris 351: void
352: cvs_putchar(int c)
353: {
1.26 niallo 354: #if !defined(RCSPROG)
1.19 joris 355: if (cvs_cmdop == CVS_OP_SERVER && send_m) {
356: send_m = 0;
357: putc('M', stdout);
358: putc(' ', stdout);
359: }
1.26 niallo 360: #endif
1.19 joris 361:
362: putc(c, stdout);
363:
1.26 niallo 364: #if !defined(RCSPROG)
1.19 joris 365: if (cvs_cmdop == CVS_OP_SERVER && c == '\n')
366: send_m = 1;
1.26 niallo 367: #endif
1.19 joris 368: }