1 /* $NetBSD: syslog.c,v 1.28 2002/05/26 14:03:20 wiz Exp $ */ 2 3 /* 4 * Copyright (c) 1983, 1988, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. All advertising materials mentioning features or use of this software 16 * must display the following acknowledgement: 17 * This product includes software developed by the University of 18 * California, Berkeley and its contributors. 19 * 4. Neither the name of the University nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 35 36 #include <sys/cdefs.h> 37 #if defined(LIBC_SCCS) && !defined(lint) 38 #if 0 39 static char sccsid[] = "@(#)syslog.c 8.5 (Berkeley) 4/29/95"; 40 #else 41 __RCSID("$NetBSD: syslog.c,v 1.28 2002/05/26 14:03:20 wiz Exp $"); 42 #endif 43 #endif /* LIBC_SCCS and not lint */ 44 45 #include "namespace.h" 46 #include <sys/types.h> 47 #include <sys/socket.h> 48 #include <sys/syslog.h> 49 #include <sys/uio.h> 50 #include <sys/un.h> 51 #include <netdb.h> 52 53 #include <errno.h> 54 #include <fcntl.h> 55 #include <paths.h> 56 #include <stdarg.h> 57 #include <stdio.h> 58 #include <stdlib.h> 59 #include <string.h> 60 #include <time.h> 61 #include <unistd.h> 62 #include "reentrant.h" 63 64 #ifdef __weak_alias 65 __weak_alias(closelog,_closelog) 66 __weak_alias(openlog,_openlog) 67 __weak_alias(setlogmask,_setlogmask) 68 __weak_alias(syslog,_syslog) 69 __weak_alias(vsyslog,_vsyslog) 70 #endif 71 72 static int LogFile = -1; /* fd for log */ 73 static int connected; /* have done connect */ 74 static int LogStat = 0; /* status bits, set by openlog() */ 75 static const char *LogTag = NULL; /* string to tag the entry with */ 76 static int LogFacility = LOG_USER; /* default facility code */ 77 static int LogMask = 0xff; /* mask of priorities to be logged */ 78 79 static void openlog_unlocked __P((const char *, int, int)); 80 static void closelog_unlocked __P((void)); 81 82 #ifdef _REENT 83 static mutex_t syslog_mutex = MUTEX_INITIALIZER; 84 #endif 85 86 #ifdef lint 87 static const int ZERO = 0; 88 #else 89 #define ZERO 0 90 #endif 91 92 /* 93 * syslog, vsyslog -- 94 * print message on log file; output is intended for syslogd(8). 95 */ 96 void 97 syslog(int pri, const char *fmt, ...) 98 { 99 va_list ap; 100 101 va_start(ap, fmt); 102 vsyslog(pri, fmt, ap); 103 va_end(ap); 104 } 105 106 void 107 vsyslog(pri, fmt, ap) 108 int pri; 109 const char *fmt; 110 _BSD_VA_LIST_ ap; 111 { 112 size_t cnt; 113 char ch, *p, *t; 114 time_t now; 115 struct tm tmnow; 116 int fd, saved_errno, tries; 117 #define TBUF_LEN 2048 118 #define FMT_LEN 1024 119 char *stdp = NULL; /* pacify gcc */ 120 char tbuf[TBUF_LEN], fmt_cpy[FMT_LEN]; 121 size_t tbuf_left, fmt_left, prlen; 122 123 #define INTERNALLOG LOG_ERR|LOG_CONS|LOG_PERROR|LOG_PID 124 /* Check for invalid bits. */ 125 if (pri & ~(LOG_PRIMASK|LOG_FACMASK)) { 126 syslog(INTERNALLOG, 127 "syslog: unknown facility/priority: %x", pri); 128 pri &= LOG_PRIMASK|LOG_FACMASK; 129 } 130 131 /* Check priority against setlogmask values. */ 132 if (!(LOG_MASK(LOG_PRI(pri)) & LogMask)) 133 return; 134 135 saved_errno = errno; 136 137 /* Set default facility if none specified. */ 138 if ((pri & LOG_FACMASK) == 0) 139 pri |= LogFacility; 140 141 /* Build the message. */ 142 143 /* 144 * Although it's tempting, we can't ignore the possibility of 145 * overflowing the buffer when assembling the "fixed" portion 146 * of the message. Strftime's "%h" directive expands to the 147 * locale's abbreviated month name, but if the user has the 148 * ability to construct to his own locale files, it may be 149 * arbitrarily long. 150 */ 151 (void)time(&now); 152 153 p = tbuf; 154 tbuf_left = TBUF_LEN; 155 156 #define DEC() \ 157 do { \ 158 if (prlen >= tbuf_left) \ 159 prlen = tbuf_left - 1; \ 160 p += prlen; \ 161 tbuf_left -= prlen; \ 162 } while (ZERO) 163 164 prlen = snprintf(p, tbuf_left, "<%d>", pri); 165 DEC(); 166 167 tzset(); /* strftime() implies tzset(), localtime_r() doesn't. */ 168 prlen = strftime(p, tbuf_left, "%h %e %T ", localtime_r(&now, &tmnow)); 169 DEC(); 170 171 if (LogStat & LOG_PERROR) 172 stdp = p; 173 if (LogTag == NULL) 174 LogTag = getprogname(); 175 if (LogTag != NULL) { 176 prlen = snprintf(p, tbuf_left, "%s", LogTag); 177 DEC(); 178 } 179 if (LogStat & LOG_PID) { 180 prlen = snprintf(p, tbuf_left, "[%d]", getpid()); 181 DEC(); 182 } 183 if (LogTag != NULL) { 184 if (tbuf_left > 1) { 185 *p++ = ':'; 186 tbuf_left--; 187 } 188 if (tbuf_left > 1) { 189 *p++ = ' '; 190 tbuf_left--; 191 } 192 } 193 194 /* 195 * We wouldn't need this mess if printf handled %m, or if 196 * strerror() had been invented before syslog(). 197 */ 198 for (t = fmt_cpy, fmt_left = FMT_LEN; (ch = *fmt) != '\0'; ++fmt) { 199 if (ch == '%' && fmt[1] == 'm') { 200 ++fmt; 201 prlen = snprintf(t, fmt_left, "%s", 202 strerror(saved_errno)); 203 if (prlen >= fmt_left) 204 prlen = fmt_left - 1; 205 t += prlen; 206 fmt_left -= prlen; 207 } else { 208 if (fmt_left > 1) { 209 *t++ = ch; 210 fmt_left--; 211 } 212 } 213 } 214 *t = '\0'; 215 216 prlen = vsnprintf(p, tbuf_left, fmt_cpy, ap); 217 DEC(); 218 cnt = p - tbuf; 219 220 /* Output to stderr if requested. */ 221 if (LogStat & LOG_PERROR) { 222 struct iovec iov[2]; 223 224 iov[0].iov_base = stdp; 225 iov[0].iov_len = cnt - (stdp - tbuf); 226 iov[1].iov_base = "\n"; 227 iov[1].iov_len = 1; 228 (void)writev(STDERR_FILENO, iov, 2); 229 } 230 231 /* Get connected, output the message to the local logger. */ 232 mutex_lock(&syslog_mutex); 233 /* 234 * Try to send it twice. If the first try fails, we might 235 * be able to simply reconnect and send the message (which 236 * means syslogd was restarted since we connected the first 237 * time). If the second attempt doesn't work, then something 238 * else is wrong and there's probably not much we can do 239 * here. 240 */ 241 for (tries = 1; tries <= 2; tries++) { 242 if (!connected) 243 openlog_unlocked(LogTag, LogStat | LOG_NDELAY, 0); 244 if (send(LogFile, tbuf, cnt, 0) >= 0) { 245 mutex_unlock(&syslog_mutex); 246 return; 247 } 248 else 249 closelog_unlocked(); 250 } 251 mutex_unlock(&syslog_mutex); 252 253 /* 254 * Output the message to the console; don't worry about blocking, 255 * if console blocks everything will. Make sure the error reported 256 * is the one from the syslogd failure. 257 */ 258 if (LogStat & LOG_CONS && 259 (fd = open(_PATH_CONSOLE, O_WRONLY, 0)) >= 0) { 260 struct iovec iov[2]; 261 262 p = strchr(tbuf, '>') + 1; 263 iov[0].iov_base = p; 264 iov[0].iov_len = cnt - (p - tbuf); 265 iov[1].iov_base = "\r\n"; 266 iov[1].iov_len = 2; 267 (void)writev(fd, iov, 2); 268 (void)close(fd); 269 } 270 } 271 272 static struct sockaddr_un SyslogAddr; /* AF_LOCAL address of local logger */ 273 274 static void 275 openlog_unlocked(ident, logstat, logfac) 276 const char *ident; 277 int logstat, logfac; 278 { 279 280 if (ident != NULL) 281 LogTag = ident; 282 LogStat = logstat; 283 if (logfac != 0 && (logfac &~ LOG_FACMASK) == 0) 284 LogFacility = logfac; 285 286 if (LogFile == -1) { 287 SyslogAddr.sun_family = AF_LOCAL; 288 (void)strncpy(SyslogAddr.sun_path, _PATH_LOG, 289 sizeof(SyslogAddr.sun_path)); 290 if (LogStat & LOG_NDELAY) { 291 if ((LogFile = socket(AF_LOCAL, SOCK_DGRAM, 0)) == -1) 292 return; 293 (void)fcntl(LogFile, F_SETFD, 1); 294 } 295 } 296 if (LogFile != -1 && !connected) { 297 if (connect(LogFile, (struct sockaddr *)(void *)&SyslogAddr, 298 SUN_LEN(&SyslogAddr)) == -1) { 299 (void)close(LogFile); 300 LogFile = -1; 301 } else 302 connected = 1; 303 } 304 } 305 306 void 307 openlog(ident, logstat, logfac) 308 const char *ident; 309 int logstat, logfac; 310 { 311 312 mutex_lock(&syslog_mutex); 313 openlog_unlocked(ident, logstat, logfac); 314 mutex_unlock(&syslog_mutex); 315 } 316 317 static void 318 closelog_unlocked() 319 { 320 (void)close(LogFile); 321 LogFile = -1; 322 connected = 0; 323 } 324 325 void 326 closelog() 327 { 328 329 mutex_lock(&syslog_mutex); 330 closelog_unlocked(); 331 mutex_unlock(&syslog_mutex); 332 } 333 334 /* setlogmask -- set the log mask level */ 335 int 336 setlogmask(pmask) 337 int pmask; 338 { 339 int omask; 340 341 omask = LogMask; 342 if (pmask != 0) 343 LogMask = pmask; 344 return (omask); 345 } 346