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