1 /* $NetBSD: syslog.c,v 1.40 2008/06/12 20:43:14 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.40 2008/06/12 20:43:14 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, tries; 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 #define MAXTRIES 10 169 char *stdp = NULL; /* pacify gcc */ 170 char tbuf[TBUF_LEN], fmt_cpy[FMT_LEN]; 171 size_t tbuf_left, fmt_left; 172 int signal_safe = pri & LOG_SIGNAL_SAFE; 173 174 pri &= ~LOG_SIGNAL_SAFE; 175 176 #define INTERNALLOG LOG_ERR|LOG_CONS|LOG_PERROR|LOG_PID 177 /* Check for invalid bits. */ 178 if (pri & ~(LOG_PRIMASK|LOG_FACMASK)) { 179 syslog_r(INTERNALLOG | signal_safe, data, 180 "syslog_r: unknown facility/priority: %x", pri); 181 pri &= LOG_PRIMASK|LOG_FACMASK; 182 } 183 184 /* Check priority against setlogmask values. */ 185 if (!(LOG_MASK(LOG_PRI(pri)) & data->log_mask)) 186 return; 187 188 saved_errno = errno; 189 190 /* Set default facility if none specified. */ 191 if ((pri & LOG_FACMASK) == 0) 192 pri |= data->log_fac; 193 194 /* Build the message. */ 195 196 /* 197 * Although it's tempting, we can't ignore the possibility of 198 * overflowing the buffer when assembling the "fixed" portion 199 * of the message. Strftime's "%h" directive expands to the 200 * locale's abbreviated month name, but if the user has the 201 * ability to construct to his own locale files, it may be 202 * arbitrarily long. 203 */ 204 if (!signal_safe) 205 (void)time(&now); 206 207 p = tbuf; 208 tbuf_left = TBUF_LEN; 209 210 #define DEC() \ 211 do { \ 212 if (prlen >= tbuf_left) \ 213 prlen = tbuf_left - 1; \ 214 p += prlen; \ 215 tbuf_left -= prlen; \ 216 } while (/*CONSTCOND*/0) 217 218 prlen = snprintf_ss(p, tbuf_left, "<%d>", pri); 219 DEC(); 220 221 if (!signal_safe) { 222 /* strftime() implies tzset(), localtime_r() doesn't. */ 223 tzset(); 224 prlen = strftime(p, tbuf_left, "%h %e %T ", 225 localtime_r(&now, &tmnow)); 226 DEC(); 227 } 228 229 if (data->log_stat & LOG_PERROR) 230 stdp = p; 231 if (data->log_tag == NULL) 232 data->log_tag = getprogname(); 233 if (data->log_tag != NULL) { 234 prlen = snprintf_ss(p, tbuf_left, "%s", data->log_tag); 235 DEC(); 236 } 237 if (data->log_stat & LOG_PID) { 238 prlen = snprintf_ss(p, tbuf_left, "[%d]", getpid()); 239 DEC(); 240 } 241 if (data->log_tag != NULL) { 242 if (tbuf_left > 1) { 243 *p++ = ':'; 244 tbuf_left--; 245 } 246 if (tbuf_left > 1) { 247 *p++ = ' '; 248 tbuf_left--; 249 } 250 } 251 252 /* 253 * We wouldn't need this mess if printf handled %m, or if 254 * strerror() had been invented before syslog(). 255 */ 256 for (t = fmt_cpy, fmt_left = FMT_LEN; (ch = *fmt) != '\0'; ++fmt) { 257 if (ch == '%' && fmt[1] == 'm') { 258 char ebuf[128]; 259 ++fmt; 260 if (signal_safe || 261 strerror_r(saved_errno, ebuf, sizeof(ebuf))) 262 prlen = snprintf_ss(t, fmt_left, "Error %d", 263 saved_errno); 264 else 265 prlen = snprintf_ss(t, fmt_left, "%s", ebuf); 266 if (prlen >= fmt_left) 267 prlen = fmt_left - 1; 268 t += prlen; 269 fmt_left -= prlen; 270 } else if (ch == '%' && fmt[1] == '%' && fmt_left > 2) { 271 *t++ = '%'; 272 *t++ = '%'; 273 fmt++; 274 fmt_left -= 2; 275 } else { 276 if (fmt_left > 1) { 277 *t++ = ch; 278 fmt_left--; 279 } 280 } 281 } 282 *t = '\0'; 283 284 if (signal_safe) 285 prlen = vsnprintf_ss(p, tbuf_left, fmt_cpy, ap); 286 else 287 prlen = vsnprintf(p, tbuf_left, fmt_cpy, ap); 288 DEC(); 289 cnt = p - tbuf; 290 291 /* Output to stderr if requested. */ 292 if (data->log_stat & LOG_PERROR) { 293 struct iovec iov[2]; 294 295 iov[0].iov_base = stdp; 296 iov[0].iov_len = cnt - (stdp - tbuf); 297 iov[1].iov_base = __UNCONST("\n"); 298 iov[1].iov_len = 1; 299 (void)writev(STDERR_FILENO, iov, 2); 300 } 301 302 /* Get connected, output the message to the local logger. */ 303 if (data == &sdata) 304 mutex_lock(&syslog_mutex); 305 if (!data->opened) 306 openlog_unlocked_r(data->log_tag, data->log_stat, 0, data); 307 connectlog_r(data); 308 309 /* 310 * If the send() failed, there are two likely scenarios: 311 * 1) syslogd was restarted 312 * 2) /dev/log is out of socket buffer space 313 * We attempt to reconnect to /dev/log to take care of 314 * case #1 and keep send()ing data to cover case #2 315 * to give syslogd a chance to empty its socket buffer. 316 */ 317 for (tries = 0; tries < MAXTRIES; tries++) { 318 if (send(data->log_file, tbuf, cnt, 0) != -1) 319 break; 320 if (errno != ENOBUFS) { 321 disconnectlog_r(data); 322 connectlog_r(data); 323 } else 324 (void)usleep(1); 325 } 326 327 /* 328 * Output the message to the console; try not to block 329 * as a blocking console should not stop other processes. 330 * Make sure the error reported is the one from the syslogd failure. 331 */ 332 if (tries == MAXTRIES && (data->log_stat & LOG_CONS) && 333 (fd = open(_PATH_CONSOLE, O_WRONLY|O_NONBLOCK, 0)) >= 0 && 334 (p = strchr(tbuf, '>')) != NULL) { 335 struct iovec iov[2]; 336 iov[0].iov_base = ++p; 337 iov[0].iov_len = cnt - (p - tbuf); 338 iov[1].iov_base = __UNCONST("\r\n"); 339 iov[1].iov_len = 2; 340 (void)writev(fd, iov, 2); 341 (void)close(fd); 342 } 343 344 if (data == &sdata) 345 mutex_unlock(&syslog_mutex); 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