syslog.c revision 158426
1171831Skan/* 2171831Skan * Copyright (c) 1983, 1988, 1993 3171831Skan * The Regents of the University of California. All rights reserved. 4171831Skan * 5169695Skan * Redistribution and use in source and binary forms, with or without 6169695Skan * modification, are permitted provided that the following conditions 7169695Skan * are met: 8169695Skan * 1. Redistributions of source code must retain the above copyright 9169695Skan * notice, this list of conditions and the following disclaimer. 10169695Skan * 2. Redistributions in binary form must reproduce the above copyright 11169695Skan * notice, this list of conditions and the following disclaimer in the 12169695Skan * documentation and/or other materials provided with the distribution. 13169695Skan * 3. All advertising materials mentioning features or use of this software 14169695Skan * must display the following acknowledgement: 15169695Skan * This product includes software developed by the University of 16169695Skan * California, Berkeley and its contributors. 17169695Skan * 4. Neither the name of the University nor the names of its contributors 18169695Skan * may be used to endorse or promote products derived from this software 19169695Skan * without specific prior written permission. 20169695Skan * 21169695Skan * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22169695Skan * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23169695Skan * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24169695Skan * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25169695Skan * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26169695Skan * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27169695Skan * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28169695Skan * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29169695Skan * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30169695Skan * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31169695Skan * SUCH DAMAGE. 32169695Skan */ 33169695Skan 34169695Skan#if defined(LIBC_SCCS) && !defined(lint) 35169695Skanstatic char sccsid[] = "@(#)syslog.c 8.5 (Berkeley) 4/29/95"; 36169695Skan#endif /* LIBC_SCCS and not lint */ 37169695Skan#include <sys/cdefs.h> 38169695Skan__FBSDID("$FreeBSD: head/lib/libc/gen/syslog.c 158426 2006-05-11 09:10:33Z davidxu $"); 39169695Skan 40169695Skan#include "namespace.h" 41169695Skan#include <sys/types.h> 42169695Skan#include <sys/socket.h> 43169695Skan#include <sys/syslog.h> 44169695Skan#include <sys/uio.h> 45169695Skan#include <sys/un.h> 46169695Skan#include <netdb.h> 47169695Skan 48169695Skan#include <errno.h> 49169695Skan#include <fcntl.h> 50169695Skan#include <paths.h> 51169695Skan#include <pthread.h> 52169695Skan#include <stdio.h> 53169695Skan#include <stdlib.h> 54169695Skan#include <string.h> 55169695Skan#include <time.h> 56169695Skan#include <unistd.h> 57169695Skan 58169695Skan#include <stdarg.h> 59169695Skan#include "un-namespace.h" 60169695Skan 61169695Skan#include "libc_private.h" 62169695Skan 63169695Skanstatic int LogFile = -1; /* fd for log */ 64169695Skanstatic int status; /* connection status */ 65169695Skanstatic int opened; /* have done openlog() */ 66169695Skanstatic int LogStat = 0; /* status bits, set by openlog() */ 67169695Skanstatic const char *LogTag = NULL; /* string to tag the entry with */ 68169695Skanstatic int LogFacility = LOG_USER; /* default facility code */ 69169695Skanstatic int LogMask = 0xff; /* mask of priorities to be logged */ 70169695Skanstatic pthread_mutex_t syslog_mutex = PTHREAD_MUTEX_INITIALIZER; 71169695Skan 72169695Skan#define THREAD_LOCK() \ 73169695Skan do { \ 74169695Skan if (__isthreaded) _pthread_mutex_lock(&syslog_mutex); \ 75169695Skan } while(0) 76169695Skan#define THREAD_UNLOCK() \ 77169695Skan do { \ 78169695Skan if (__isthreaded) _pthread_mutex_unlock(&syslog_mutex); \ 79169695Skan } while(0) 80169695Skan 81169695Skanstatic void disconnectlog(void); /* disconnect from syslogd */ 82169695Skanstatic void connectlog(void); /* (re)connect to syslogd */ 83169695Skanstatic void openlog_unlocked(const char *, int, int); 84169695Skan 85169695Skanenum { 86169695Skan NOCONN = 0, 87169695Skan CONNDEF, 88169695Skan CONNPRIV, 89169695Skan}; 90169695Skan 91169695Skan/* 92169695Skan * Format of the magic cookie passed through the stdio hook 93169695Skan */ 94169695Skanstruct bufcookie { 95169695Skan char *base; /* start of buffer */ 96169695Skan int left; 97169695Skan}; 98169695Skan 99169695Skan/* 100169695Skan * stdio write hook for writing to a static string buffer 101169695Skan * XXX: Maybe one day, dynamically allocate it so that the line length 102169695Skan * is `unlimited'. 103169695Skan */ 104169695Skanstatic int 105169695Skanwritehook(void *cookie, const char *buf, int len) 106169695Skan{ 107169695Skan struct bufcookie *h; /* private `handle' */ 108169695Skan 109169695Skan h = (struct bufcookie *)cookie; 110169695Skan if (len > h->left) { 111169695Skan /* clip in case of wraparound */ 112169695Skan len = h->left; 113169695Skan } 114169695Skan if (len > 0) { 115169695Skan (void)memcpy(h->base, buf, len); /* `write' it. */ 116169695Skan h->base += len; 117169695Skan h->left -= len; 118169695Skan } 119169695Skan return 0; 120169695Skan} 121169695Skan 122169695Skan/* 123169695Skan * syslog, vsyslog -- 124169695Skan * print message on log file; output is intended for syslogd(8). 125169695Skan */ 126169695Skanvoid 127169695Skansyslog(int pri, const char *fmt, ...) 128169695Skan{ 129169695Skan va_list ap; 130169695Skan 131169695Skan va_start(ap, fmt); 132169695Skan vsyslog(pri, fmt, ap); 133169695Skan va_end(ap); 134169695Skan} 135169695Skan 136169695Skanvoid 137169695Skanvsyslog(int pri, const char *fmt, va_list ap) 138169695Skan{ 139169695Skan int cnt; 140169695Skan char ch, *p; 141169695Skan time_t now; 142169695Skan int fd, saved_errno; 143169695Skan char *stdp, tbuf[2048], fmt_cpy[1024], timbuf[26], errstr[64]; 144169695Skan FILE *fp, *fmt_fp; 145169695Skan struct bufcookie tbuf_cookie; 146169695Skan struct bufcookie fmt_cookie; 147169695Skan 148169695Skan#define INTERNALLOG LOG_ERR|LOG_CONS|LOG_PERROR|LOG_PID 149169695Skan /* Check for invalid bits. */ 150169695Skan if (pri & ~(LOG_PRIMASK|LOG_FACMASK)) { 151169695Skan syslog(INTERNALLOG, 152169695Skan "syslog: unknown facility/priority: %x", pri); 153169695Skan pri &= LOG_PRIMASK|LOG_FACMASK; 154169695Skan } 155169695Skan 156169695Skan saved_errno = errno; 157169695Skan 158169695Skan THREAD_LOCK(); 159169695Skan 160169695Skan /* Check priority against setlogmask values. */ 161169695Skan if (!(LOG_MASK(LOG_PRI(pri)) & LogMask)) { 162169695Skan THREAD_UNLOCK(); 163169695Skan return; 164169695Skan } 165169695Skan 166169695Skan /* Set default facility if none specified. */ 167169695Skan if ((pri & LOG_FACMASK) == 0) 168169695Skan pri |= LogFacility; 169169695Skan 170169695Skan /* Create the primary stdio hook */ 171169695Skan tbuf_cookie.base = tbuf; 172169695Skan tbuf_cookie.left = sizeof(tbuf); 173169695Skan fp = fwopen(&tbuf_cookie, writehook); 174169695Skan if (fp == NULL) { 175169695Skan THREAD_UNLOCK(); 176169695Skan return; 177169695Skan } 178169695Skan 179169695Skan /* Build the message. */ 180169695Skan (void)time(&now); 181169695Skan (void)fprintf(fp, "<%d>", pri); 182169695Skan (void)fprintf(fp, "%.15s ", ctime_r(&now, timbuf) + 4); 183169695Skan if (LogStat & LOG_PERROR) { 184169695Skan /* Transfer to string buffer */ 185169695Skan (void)fflush(fp); 186169695Skan stdp = tbuf + (sizeof(tbuf) - tbuf_cookie.left); 187169695Skan } 188169695Skan if (LogTag == NULL) 189169695Skan LogTag = _getprogname(); 190169695Skan if (LogTag != NULL) 191169695Skan (void)fprintf(fp, "%s", LogTag); 192169695Skan if (LogStat & LOG_PID) 193169695Skan (void)fprintf(fp, "[%d]", getpid()); 194169695Skan if (LogTag != NULL) { 195169695Skan (void)fprintf(fp, ": "); 196169695Skan } 197169695Skan 198169695Skan /* Check to see if we can skip expanding the %m */ 199169695Skan if (strstr(fmt, "%m")) { 200169695Skan 201169695Skan /* Create the second stdio hook */ 202169695Skan fmt_cookie.base = fmt_cpy; 203169695Skan fmt_cookie.left = sizeof(fmt_cpy) - 1; 204169695Skan fmt_fp = fwopen(&fmt_cookie, writehook); 205169695Skan if (fmt_fp == NULL) { 206169695Skan fclose(fp); 207169695Skan THREAD_UNLOCK(); 208169695Skan return; 209169695Skan } 210169695Skan 211169695Skan /* 212169695Skan * Substitute error message for %m. Be careful not to 213169695Skan * molest an escaped percent "%%m". We want to pass it 214169695Skan * on untouched as the format is later parsed by vfprintf. 215169695Skan */ 216169695Skan for ( ; (ch = *fmt); ++fmt) { 217169695Skan if (ch == '%' && fmt[1] == 'm') { 218169695Skan ++fmt; 219169695Skan strerror_r(saved_errno, errstr, sizeof(errstr)); 220169695Skan fputs(errstr, fmt_fp); 221169695Skan } else if (ch == '%' && fmt[1] == '%') { 222169695Skan ++fmt; 223169695Skan fputc(ch, fmt_fp); 224169695Skan fputc(ch, fmt_fp); 225169695Skan } else { 226169695Skan fputc(ch, fmt_fp); 227169695Skan } 228169695Skan } 229169695Skan 230169695Skan /* Null terminate if room */ 231169695Skan fputc(0, fmt_fp); 232169695Skan fclose(fmt_fp); 233169695Skan 234169695Skan /* Guarantee null termination */ 235169695Skan fmt_cpy[sizeof(fmt_cpy) - 1] = '\0'; 236169695Skan 237169695Skan fmt = fmt_cpy; 238169695Skan } 239169695Skan 240169695Skan (void)vfprintf(fp, fmt, ap); 241169695Skan (void)fclose(fp); 242169695Skan 243169695Skan cnt = sizeof(tbuf) - tbuf_cookie.left; 244169695Skan 245169695Skan /* Remove a trailing newline */ 246169695Skan if (tbuf[cnt - 1] == '\n') 247169695Skan cnt--; 248169695Skan 249169695Skan /* Output to stderr if requested. */ 250169695Skan if (LogStat & LOG_PERROR) { 251169695Skan struct iovec iov[2]; 252169695Skan struct iovec *v = iov; 253169695Skan 254169695Skan v->iov_base = stdp; 255169695Skan v->iov_len = cnt - (stdp - tbuf); 256169695Skan ++v; 257169695Skan v->iov_base = "\n"; 258169695Skan v->iov_len = 1; 259169695Skan (void)_writev(STDERR_FILENO, iov, 2); 260169695Skan } 261169695Skan 262169695Skan /* Get connected, output the message to the local logger. */ 263169695Skan if (!opened) 264169695Skan openlog_unlocked(LogTag, LogStat | LOG_NDELAY, 0); 265169695Skan connectlog(); 266169695Skan 267169695Skan /* 268169695Skan * If the send() failed, there are two likely scenarios: 269169695Skan * 1) syslogd was restarted 270169695Skan * 2) /var/run/log is out of socket buffer space, which 271169695Skan * in most cases means local DoS. 272169695Skan * We attempt to reconnect to /var/run/log to take care of 273169695Skan * case #1 and keep send()ing data to cover case #2 274169695Skan * to give syslogd a chance to empty its socket buffer. 275169695Skan * 276169695Skan * If we are working with a priveleged socket, then take 277169695Skan * only one attempt, because we don't want to freeze a 278169695Skan * critical application like su(1) or sshd(8). 279169695Skan * 280169695Skan */ 281169695Skan 282169695Skan if (send(LogFile, tbuf, cnt, 0) < 0) { 283169695Skan if (errno != ENOBUFS) { 284169695Skan disconnectlog(); 285169695Skan connectlog(); 286169695Skan } 287169695Skan do { 288169695Skan _usleep(1); 289169695Skan if (send(LogFile, tbuf, cnt, 0) >= 0) { 290169695Skan THREAD_UNLOCK(); 291169695Skan return; 292169695Skan } 293169695Skan if (status == CONNPRIV) 294169695Skan break; 295169695Skan } while (errno == ENOBUFS); 296169695Skan } else { 297169695Skan THREAD_UNLOCK(); 298169695Skan return; 299169695Skan } 300169695Skan 301169695Skan /* 302169695Skan * Output the message to the console; try not to block 303169695Skan * as a blocking console should not stop other processes. 304169695Skan * Make sure the error reported is the one from the syslogd failure. 305169695Skan */ 306169695Skan if (LogStat & LOG_CONS && 307169695Skan (fd = _open(_PATH_CONSOLE, O_WRONLY|O_NONBLOCK, 0)) >= 0) { 308169695Skan struct iovec iov[2]; 309169695Skan struct iovec *v = iov; 310169695Skan 311169695Skan p = strchr(tbuf, '>') + 1; 312169695Skan v->iov_base = p; 313169695Skan v->iov_len = cnt - (p - tbuf); 314169695Skan ++v; 315169695Skan v->iov_base = "\r\n"; 316169695Skan v->iov_len = 2; 317169695Skan (void)_writev(fd, iov, 2); 318169695Skan (void)_close(fd); 319169695Skan } 320169695Skan 321169695Skan THREAD_UNLOCK(); 322169695Skan} 323169695Skan 324169695Skan/* Should be called with mutex acquired */ 325169695Skanstatic void 326169695Skandisconnectlog(void) 327169695Skan{ 328169695Skan /* 329169695Skan * If the user closed the FD and opened another in the same slot, 330169695Skan * that's their problem. They should close it before calling on 331169695Skan * system services. 332169695Skan */ 333169695Skan if (LogFile != -1) { 334169695Skan _close(LogFile); 335169695Skan LogFile = -1; 336169695Skan } 337169695Skan status = NOCONN; /* retry connect */ 338169695Skan} 339169695Skan 340169695Skan/* Should be called with mutex acquired */ 341169695Skanstatic void 342169695Skanconnectlog(void) 343169695Skan{ 344169695Skan struct sockaddr_un SyslogAddr; /* AF_UNIX address of local logger */ 345169695Skan 346169695Skan if (LogFile == -1) { 347169695Skan if ((LogFile = _socket(AF_UNIX, SOCK_DGRAM, 0)) == -1) 348169695Skan return; 349169695Skan (void)_fcntl(LogFile, F_SETFD, 1); 350169695Skan } 351169695Skan if (LogFile != -1 && status == NOCONN) { 352169695Skan SyslogAddr.sun_len = sizeof(SyslogAddr); 353169695Skan SyslogAddr.sun_family = AF_UNIX; 354169695Skan 355169695Skan /* 356169695Skan * First try priveleged socket. If no success, 357169695Skan * then try default socket. 358169695Skan */ 359169695Skan (void)strncpy(SyslogAddr.sun_path, _PATH_LOG_PRIV, 360169695Skan sizeof SyslogAddr.sun_path); 361169695Skan if (_connect(LogFile, (struct sockaddr *)&SyslogAddr, 362169695Skan sizeof(SyslogAddr)) != -1) 363169695Skan status = CONNPRIV; 364169695Skan 365169695Skan if (status == NOCONN) { 366169695Skan (void)strncpy(SyslogAddr.sun_path, _PATH_LOG, 367169695Skan sizeof SyslogAddr.sun_path); 368169695Skan if (_connect(LogFile, (struct sockaddr *)&SyslogAddr, 369169695Skan sizeof(SyslogAddr)) != -1) 370169695Skan status = CONNDEF; 371169695Skan } 372169695Skan 373169695Skan if (status == NOCONN) { 374169695Skan /* 375169695Skan * Try the old "/dev/log" path, for backward 376169695Skan * compatibility. 377169695Skan */ 378169695Skan (void)strncpy(SyslogAddr.sun_path, _PATH_OLDLOG, 379169695Skan sizeof SyslogAddr.sun_path); 380169695Skan if (_connect(LogFile, (struct sockaddr *)&SyslogAddr, 381169695Skan sizeof(SyslogAddr)) != -1) 382169695Skan status = CONNDEF; 383169695Skan } 384169695Skan 385169695Skan if (status == NOCONN) { 386169695Skan (void)_close(LogFile); 387169695Skan LogFile = -1; 388169695Skan } 389169695Skan } 390169695Skan} 391169695Skan 392169695Skanstatic void 393169695Skanopenlog_unlocked(const char *ident, int logstat, int logfac) 394169695Skan{ 395169695Skan if (ident != NULL) 396169695Skan LogTag = ident; 397169695Skan LogStat = logstat; 398169695Skan if (logfac != 0 && (logfac &~ LOG_FACMASK) == 0) 399169695Skan LogFacility = logfac; 400169695Skan 401169695Skan if (LogStat & LOG_NDELAY) /* open immediately */ 402169695Skan connectlog(); 403169695Skan 404169695Skan opened = 1; /* ident and facility has been set */ 405169695Skan} 406169695Skan 407169695Skanvoid 408169695Skanopenlog(const char *ident, int logstat, int logfac) 409169695Skan{ 410169695Skan THREAD_LOCK(); 411169695Skan openlog_unlocked(ident, logstat, logfac); 412169695Skan THREAD_UNLOCK(); 413169695Skan} 414169695Skan 415169695Skan 416169695Skanvoid 417169695Skancloselog(void) 418169695Skan{ 419169695Skan THREAD_LOCK(); 420169695Skan (void)_close(LogFile); 421169695Skan LogFile = -1; 422169695Skan LogTag = NULL; 423169695Skan status = NOCONN; 424169695Skan THREAD_UNLOCK(); 425169695Skan} 426169695Skan 427169695Skan/* setlogmask -- set the log mask level */ 428169695Skanint 429169695Skansetlogmask(int pmask) 430169695Skan{ 431169695Skan int omask; 432169695Skan 433169695Skan THREAD_LOCK(); 434169695Skan omask = LogMask; 435169695Skan if (pmask != 0) 436169695Skan LogMask = pmask; 437169695Skan THREAD_UNLOCK(); 438169695Skan return (omask); 439169695Skan} 440169695Skan