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