1 /* $NetBSD: syslog.c,v 1.39 2006/11/22 17:23:25 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.39 2006/11/22 17:23:25 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 #include "extern.h" 60 61 #ifdef __weak_alias 62 __weak_alias(closelog,_closelog) 63 __weak_alias(openlog,_openlog) 64 __weak_alias(setlogmask,_setlogmask) 65 __weak_alias(syslog,_syslog) 66 __weak_alias(vsyslog,_vsyslog) 67 68 __weak_alias(closelog_r,_closelog_r) 69 __weak_alias(openlog_r,_openlog_r) 70 __weak_alias(setlogmask_r,_setlogmask_r) 71 __weak_alias(syslog_r,_syslog_r) 72 __weak_alias(vsyslog_r,_vsyslog_r) 73 __weak_alias(syslog_ss,_syslog_ss) 74 __weak_alias(vsyslog_ss,_vsyslog_ss) 75 #endif 76 77 static struct syslog_data sdata = SYSLOG_DATA_INIT; 78 79 static void openlog_unlocked_r(const char *, int, int, 80 struct syslog_data *); 81 static void disconnectlog_r(struct syslog_data *); 82 static void connectlog_r(struct syslog_data *); 83 84 #define LOG_SIGNAL_SAFE (int)0x80000000 85 86 87 #ifdef _REENTRANT 88 static mutex_t syslog_mutex = MUTEX_INITIALIZER; 89 #endif 90 91 /* 92 * syslog, vsyslog -- 93 * print message on log file; output is intended for syslogd(8). 94 */ 95 void 96 syslog(int pri, const char *fmt, ...) 97 { 98 va_list ap; 99 100 va_start(ap, fmt); 101 vsyslog(pri, fmt, ap); 102 va_end(ap); 103 } 104 105 void 106 vsyslog(int pri, const char *fmt, va_list ap) 107 { 108 vsyslog_r(pri, &sdata, fmt, ap); 109 } 110 111 void 112 openlog(const char *ident, int logstat, int logfac) 113 { 114 openlog_r(ident, logstat, logfac, &sdata); 115 } 116 117 void 118 closelog(void) 119 { 120 closelog_r(&sdata); 121 } 122 123 /* setlogmask -- set the log mask level */ 124 int 125 setlogmask(int pmask) 126 { 127 return setlogmask_r(pmask, &sdata); 128 } 129 130 /* Reentrant version of syslog, i.e. syslog_r() */ 131 132 void 133 syslog_r(int pri, struct syslog_data *data, const char *fmt, ...) 134 { 135 va_list ap; 136 137 va_start(ap, fmt); 138 vsyslog_r(pri, data, fmt, ap); 139 va_end(ap); 140 } 141 142 void 143 syslog_ss(int pri, struct syslog_data *data, const char *fmt, ...) 144 { 145 va_list ap; 146 147 va_start(ap, fmt); 148 vsyslog_r(pri | LOG_SIGNAL_SAFE, data, fmt, ap); 149 va_end(ap); 150 } 151 152 void 153 vsyslog_ss(int pri, struct syslog_data *data, const char *fmt, va_list ap) 154 { 155 vsyslog_r(pri | LOG_SIGNAL_SAFE, data, fmt, ap); 156 } 157 158 void 159 vsyslog_r(int pri, struct syslog_data *data, const char *fmt, va_list ap) 160 { 161 size_t cnt, prlen; 162 char ch, *p, *t; 163 time_t now; 164 struct tm tmnow; 165 int fd, saved_errno; 166 #define TBUF_LEN 2048 167 #define FMT_LEN 1024 168 char *stdp = NULL; /* pacify gcc */ 169 char tbuf[TBUF_LEN], fmt_cpy[FMT_LEN]; 170 size_t tbuf_left, fmt_left; 171 int signal_safe = pri & LOG_SIGNAL_SAFE; 172 173 pri &= ~LOG_SIGNAL_SAFE; 174 175 #define INTERNALLOG LOG_ERR|LOG_CONS|LOG_PERROR|LOG_PID 176 /* Check for invalid bits. */ 177 if (pri & ~(LOG_PRIMASK|LOG_FACMASK)) { 178 syslog_r(INTERNALLOG | signal_safe, data, 179 "syslog_r: unknown facility/priority: %x", pri); 180 pri &= LOG_PRIMASK|LOG_FACMASK; 181 } 182 183 /* Check priority against setlogmask values. */ 184 if (!(LOG_MASK(LOG_PRI(pri)) & data->log_mask)) 185 return; 186 187 saved_errno = errno; 188 189 /* Set default facility if none specified. */ 190 if ((pri & LOG_FACMASK) == 0) 191 pri |= data->log_fac; 192 193 /* Build the message. */ 194 195 /* 196 * Although it's tempting, we can't ignore the possibility of 197 * overflowing the buffer when assembling the "fixed" portion 198 * of the message. Strftime's "%h" directive expands to the 199 * locale's abbreviated month name, but if the user has the 200 * ability to construct to his own locale files, it may be 201 * arbitrarily long. 202 */ 203 if (!signal_safe) 204 (void)time(&now); 205 206 p = tbuf; 207 tbuf_left = TBUF_LEN; 208 209 #define DEC() \ 210 do { \ 211 if (prlen >= tbuf_left) \ 212 prlen = tbuf_left - 1; \ 213 p += prlen; \ 214 tbuf_left -= prlen; \ 215 } while (/*CONSTCOND*/0) 216 217 prlen = snprintf_ss(p, tbuf_left, "<%d>", pri); 218 DEC(); 219 220 if (!signal_safe) { 221 /* strftime() implies tzset(), localtime_r() doesn't. */ 222 tzset(); 223 prlen = strftime(p, tbuf_left, "%h %e %T ", 224 localtime_r(&now, &tmnow)); 225 DEC(); 226 } 227 228 if (data->log_stat & LOG_PERROR) 229 stdp = p; 230 if (data->log_tag == NULL) 231 data->log_tag = getprogname(); 232 if (data->log_tag != NULL) { 233 prlen = snprintf_ss(p, tbuf_left, "%s", data->log_tag); 234 DEC(); 235 } 236 if (data->log_stat & LOG_PID) { 237 prlen = snprintf_ss(p, tbuf_left, "[%d]", getpid()); 238 DEC(); 239 } 240 if (data->log_tag != NULL) { 241 if (tbuf_left > 1) { 242 *p++ = ':'; 243 tbuf_left--; 244 } 245 if (tbuf_left > 1) { 246 *p++ = ' '; 247 tbuf_left--; 248 } 249 } 250 251 /* 252 * We wouldn't need this mess if printf handled %m, or if 253 * strerror() had been invented before syslog(). 254 */ 255 for (t = fmt_cpy, fmt_left = FMT_LEN; (ch = *fmt) != '\0'; ++fmt) { 256 if (ch == '%' && fmt[1] == 'm') { 257 char ebuf[128]; 258 ++fmt; 259 if (signal_safe || 260 strerror_r(saved_errno, ebuf, sizeof(ebuf))) 261 prlen = snprintf_ss(t, fmt_left, "Error %d", 262 saved_errno); 263 else 264 prlen = snprintf_ss(t, fmt_left, "%s", ebuf); 265 if (prlen >= fmt_left) 266 prlen = fmt_left - 1; 267 t += prlen; 268 fmt_left -= prlen; 269 } else if (ch == '%' && fmt[1] == '%' && fmt_left > 2) { 270 *t++ = '%'; 271 *t++ = '%'; 272 fmt++; 273 fmt_left -= 2; 274 } else { 275 if (fmt_left > 1) { 276 *t++ = ch; 277 fmt_left--; 278 } 279 } 280 } 281 *t = '\0'; 282 283 if (signal_safe) 284 prlen = vsnprintf_ss(p, tbuf_left, fmt_cpy, ap); 285 else 286 prlen = vsnprintf(p, tbuf_left, fmt_cpy, ap); 287 DEC(); 288 cnt = p - tbuf; 289 290 /* Output to stderr if requested. */ 291 if (data->log_stat & LOG_PERROR) { 292 struct iovec iov[2]; 293 294 iov[0].iov_base = stdp; 295 iov[0].iov_len = cnt - (stdp - tbuf); 296 iov[1].iov_base = __UNCONST("\n"); 297 iov[1].iov_len = 1; 298 (void)writev(STDERR_FILENO, iov, 2); 299 } 300 301 /* Get connected, output the message to the local logger. */ 302 if (data == &sdata) 303 mutex_lock(&syslog_mutex); 304 if (!data->opened) 305 openlog_unlocked_r(data->log_tag, data->log_stat, 0, data); 306 connectlog_r(data); 307 308 /* 309 * If the send() failed, there are two likely scenarios: 310 * 1) syslogd was restarted 311 * 2) /dev/log is out of socket buffer space 312 * We attempt to reconnect to /dev/log to take care of 313 * case #1 and keep send()ing data to cover case #2 314 * to give syslogd a chance to empty its socket buffer. 315 */ 316 if (send(data->log_file, tbuf, cnt, 0) == -1) { 317 if (errno != ENOBUFS) { 318 disconnectlog_r(data); 319 connectlog_r(data); 320 } 321 do { 322 usleep(1); 323 if (send(data->log_file, tbuf, cnt, 0) != -1) 324 break; 325 } while (errno == ENOBUFS); 326 } 327 if (data == &sdata) 328 mutex_unlock(&syslog_mutex); 329 330 /* 331 * Output the message to the console; try not to block 332 * as a blocking console should not stop other processes. 333 * Make sure the error reported is the one from the syslogd failure. 334 */ 335 if ((data->log_stat & LOG_CONS) && 336 (fd = open(_PATH_CONSOLE, O_WRONLY|O_NONBLOCK, 0)) >= 0) { 337 struct iovec iov[2]; 338 339 p = strchr(tbuf, '>') + 1; 340 iov[0].iov_base = p; 341 iov[0].iov_len = cnt - (p - tbuf); 342 iov[1].iov_base = __UNCONST("\r\n"); 343 iov[1].iov_len = 2; 344 (void)writev(fd, iov, 2); 345 (void)close(fd); 346 } 347 if (data != &sdata) 348 closelog_r(data); 349 } 350 351 static void 352 disconnectlog_r(struct syslog_data *data) 353 { 354 /* 355 * If the user closed the FD and opened another in the same slot, 356 * that's their problem. They should close it before calling on 357 * system services. 358 */ 359 if (data->log_file != -1) { 360 (void)close(data->log_file); 361 data->log_file = -1; 362 } 363 data->connected = 0; /* retry connect */ 364 } 365 366 static void 367 connectlog_r(struct syslog_data *data) 368 { 369 /* AF_UNIX address of local logger */ 370 static const struct sockaddr_un sun = { 371 .sun_family = AF_LOCAL, 372 .sun_len = sizeof(sun), 373 .sun_path = _PATH_LOG, 374 }; 375 376 if (data->log_file == -1 || fcntl(data->log_file, F_GETFL, 0) == -1) { 377 if ((data->log_file = socket(AF_UNIX, SOCK_DGRAM, 0)) == -1) 378 return; 379 (void)fcntl(data->log_file, F_SETFD, FD_CLOEXEC); 380 data->connected = 0; 381 } 382 if (!data->connected) { 383 if (connect(data->log_file, 384 (const struct sockaddr *)(const void *)&sun, 385 sizeof(sun)) == -1) { 386 (void)close(data->log_file); 387 data->log_file = -1; 388 } else 389 data->connected = 1; 390 } 391 } 392 393 static void 394 openlog_unlocked_r(const char *ident, int logstat, int logfac, 395 struct syslog_data *data) 396 { 397 if (ident != NULL) 398 data->log_tag = ident; 399 data->log_stat = logstat; 400 if (logfac != 0 && (logfac &~ LOG_FACMASK) == 0) 401 data->log_fac = logfac; 402 403 if (data->log_stat & LOG_NDELAY) /* open immediately */ 404 connectlog_r(data); 405 } 406 407 void 408 openlog_r(const char *ident, int logstat, int logfac, struct syslog_data *data) 409 { 410 if (data == &sdata) 411 mutex_lock(&syslog_mutex); 412 openlog_unlocked_r(ident, logstat, logfac, data); 413 if (data == &sdata) 414 mutex_unlock(&syslog_mutex); 415 } 416 417 void 418 closelog_r(struct syslog_data *data) 419 { 420 if (data == &sdata) 421 mutex_lock(&syslog_mutex); 422 (void)close(data->log_file); 423 data->log_file = -1; 424 data->connected = 0; 425 data->log_tag = NULL; 426 if (data == &sdata) 427 mutex_unlock(&syslog_mutex); 428 } 429 430 int 431 setlogmask_r(int pmask, struct syslog_data *data) 432 { 433 int omask; 434 435 omask = data->log_mask; 436 if (pmask != 0) 437 data->log_mask = pmask; 438 return omask; 439 } 440