11573Srgrimes/* 21573Srgrimes * Copyright (c) 1983, 1988, 1993 31573Srgrimes * The Regents of the University of California. All rights reserved. 41573Srgrimes * 51573Srgrimes * Redistribution and use in source and binary forms, with or without 61573Srgrimes * modification, are permitted provided that the following conditions 71573Srgrimes * are met: 81573Srgrimes * 1. Redistributions of source code must retain the above copyright 91573Srgrimes * notice, this list of conditions and the following disclaimer. 101573Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 111573Srgrimes * notice, this list of conditions and the following disclaimer in the 121573Srgrimes * documentation and/or other materials provided with the distribution. 131573Srgrimes * 4. Neither the name of the University nor the names of its contributors 141573Srgrimes * may be used to endorse or promote products derived from this software 151573Srgrimes * without specific prior written permission. 161573Srgrimes * 171573Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 181573Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 191573Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 201573Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 211573Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 221573Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 231573Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 241573Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 251573Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 261573Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 271573Srgrimes * SUCH DAMAGE. 281573Srgrimes */ 291573Srgrimes 301573Srgrimes#if defined(LIBC_SCCS) && !defined(lint) 3123668Speterstatic char sccsid[] = "@(#)syslog.c 8.5 (Berkeley) 4/29/95"; 321573Srgrimes#endif /* LIBC_SCCS and not lint */ 3390045Sobrien#include <sys/cdefs.h> 3490045Sobrien__FBSDID("$FreeBSD$"); 351573Srgrimes 3671579Sdeischen#include "namespace.h" 371573Srgrimes#include <sys/types.h> 381573Srgrimes#include <sys/socket.h> 391573Srgrimes#include <sys/syslog.h> 401573Srgrimes#include <sys/uio.h> 4134059Sbrian#include <sys/un.h> 421573Srgrimes#include <netdb.h> 431573Srgrimes 441573Srgrimes#include <errno.h> 451573Srgrimes#include <fcntl.h> 461573Srgrimes#include <paths.h> 47139440Sglebius#include <pthread.h> 481573Srgrimes#include <stdio.h> 4993399Smarkm#include <stdlib.h> 501573Srgrimes#include <string.h> 511573Srgrimes#include <time.h> 521573Srgrimes#include <unistd.h> 531573Srgrimes 541573Srgrimes#include <stdarg.h> 5571579Sdeischen#include "un-namespace.h" 561573Srgrimes 5793399Smarkm#include "libc_private.h" 5893399Smarkm 591573Srgrimesstatic int LogFile = -1; /* fd for log */ 60137233Sglebiusstatic int status; /* connection status */ 6114332Speterstatic int opened; /* have done openlog() */ 621573Srgrimesstatic int LogStat = 0; /* status bits, set by openlog() */ 631573Srgrimesstatic const char *LogTag = NULL; /* string to tag the entry with */ 641573Srgrimesstatic int LogFacility = LOG_USER; /* default facility code */ 651573Srgrimesstatic int LogMask = 0xff; /* mask of priorities to be logged */ 66139440Sglebiusstatic pthread_mutex_t syslog_mutex = PTHREAD_MUTEX_INITIALIZER; 671573Srgrimes 68139440Sglebius#define THREAD_LOCK() \ 69139440Sglebius do { \ 70139440Sglebius if (__isthreaded) _pthread_mutex_lock(&syslog_mutex); \ 71139440Sglebius } while(0) 72139440Sglebius#define THREAD_UNLOCK() \ 73139440Sglebius do { \ 74139440Sglebius if (__isthreaded) _pthread_mutex_unlock(&syslog_mutex); \ 75139440Sglebius } while(0) 76139440Sglebius 7790045Sobrienstatic void disconnectlog(void); /* disconnect from syslogd */ 7890045Sobrienstatic void connectlog(void); /* (re)connect to syslogd */ 79139440Sglebiusstatic void openlog_unlocked(const char *, int, int); 8014332Speter 81137233Sglebiusenum { 82137233Sglebius NOCONN = 0, 83137233Sglebius CONNDEF, 84137233Sglebius CONNPRIV, 85137233Sglebius}; 86137233Sglebius 871573Srgrimes/* 8810794Speter * Format of the magic cookie passed through the stdio hook 8910794Speter */ 9010794Speterstruct bufcookie { 9110794Speter char *base; /* start of buffer */ 9210794Speter int left; 9310794Speter}; 9410794Speter 9510794Speter/* 9610794Speter * stdio write hook for writing to a static string buffer 9710794Speter * XXX: Maybe one day, dynamically allocate it so that the line length 9810794Speter * is `unlimited'. 9910794Speter */ 100144815Sstefanfstatic int 101144815Sstefanfwritehook(void *cookie, const char *buf, int len) 10210794Speter{ 10310794Speter struct bufcookie *h; /* private `handle' */ 10410794Speter 10510794Speter h = (struct bufcookie *)cookie; 10610794Speter if (len > h->left) { 10710794Speter /* clip in case of wraparound */ 10810794Speter len = h->left; 10910794Speter } 11010794Speter if (len > 0) { 11110794Speter (void)memcpy(h->base, buf, len); /* `write' it. */ 11210794Speter h->base += len; 11310794Speter h->left -= len; 11410794Speter } 115161053Sphk return len; 11610794Speter} 11710794Speter 11810794Speter/* 1191573Srgrimes * syslog, vsyslog -- 1201573Srgrimes * print message on log file; output is intended for syslogd(8). 1211573Srgrimes */ 1221573Srgrimesvoid 1231573Srgrimessyslog(int pri, const char *fmt, ...) 1241573Srgrimes{ 1251573Srgrimes va_list ap; 1261573Srgrimes 1271573Srgrimes va_start(ap, fmt); 1281573Srgrimes vsyslog(pri, fmt, ap); 1291573Srgrimes va_end(ap); 1301573Srgrimes} 1311573Srgrimes 1321573Srgrimesvoid 133144815Sstefanfvsyslog(int pri, const char *fmt, va_list ap) 1341573Srgrimes{ 13590045Sobrien int cnt; 13690045Sobrien char ch, *p; 1371573Srgrimes time_t now; 1381573Srgrimes int fd, saved_errno; 139139440Sglebius char *stdp, tbuf[2048], fmt_cpy[1024], timbuf[26], errstr[64]; 14010794Speter FILE *fp, *fmt_fp; 14110794Speter struct bufcookie tbuf_cookie; 14210794Speter struct bufcookie fmt_cookie; 1431573Srgrimes 1441573Srgrimes#define INTERNALLOG LOG_ERR|LOG_CONS|LOG_PERROR|LOG_PID 1451573Srgrimes /* Check for invalid bits. */ 1461573Srgrimes if (pri & ~(LOG_PRIMASK|LOG_FACMASK)) { 1471573Srgrimes syslog(INTERNALLOG, 1481573Srgrimes "syslog: unknown facility/priority: %x", pri); 1491573Srgrimes pri &= LOG_PRIMASK|LOG_FACMASK; 1501573Srgrimes } 1511573Srgrimes 152158426Sdavidxu saved_errno = errno; 153158426Sdavidxu 154139440Sglebius THREAD_LOCK(); 155139440Sglebius 1561573Srgrimes /* Check priority against setlogmask values. */ 157139440Sglebius if (!(LOG_MASK(LOG_PRI(pri)) & LogMask)) { 158139440Sglebius THREAD_UNLOCK(); 1591573Srgrimes return; 160139440Sglebius } 1611573Srgrimes 1621573Srgrimes /* Set default facility if none specified. */ 1631573Srgrimes if ((pri & LOG_FACMASK) == 0) 1641573Srgrimes pri |= LogFacility; 1651573Srgrimes 16610794Speter /* Create the primary stdio hook */ 16710794Speter tbuf_cookie.base = tbuf; 16810794Speter tbuf_cookie.left = sizeof(tbuf); 16910794Speter fp = fwopen(&tbuf_cookie, writehook); 170139440Sglebius if (fp == NULL) { 171139440Sglebius THREAD_UNLOCK(); 17210794Speter return; 173139440Sglebius } 17410794Speter 1751573Srgrimes /* Build the message. */ 1761573Srgrimes (void)time(&now); 17710794Speter (void)fprintf(fp, "<%d>", pri); 17889805Sdwmalone (void)fprintf(fp, "%.15s ", ctime_r(&now, timbuf) + 4); 17910794Speter if (LogStat & LOG_PERROR) { 18010794Speter /* Transfer to string buffer */ 18110794Speter (void)fflush(fp); 18210794Speter stdp = tbuf + (sizeof(tbuf) - tbuf_cookie.left); 18310794Speter } 1841573Srgrimes if (LogTag == NULL) 18593399Smarkm LogTag = _getprogname(); 1861573Srgrimes if (LogTag != NULL) 18710794Speter (void)fprintf(fp, "%s", LogTag); 1881573Srgrimes if (LogStat & LOG_PID) 18910794Speter (void)fprintf(fp, "[%d]", getpid()); 1901573Srgrimes if (LogTag != NULL) { 19110794Speter (void)fprintf(fp, ": "); 1921573Srgrimes } 1931573Srgrimes 19410794Speter /* Check to see if we can skip expanding the %m */ 19510794Speter if (strstr(fmt, "%m")) { 1961573Srgrimes 19710794Speter /* Create the second stdio hook */ 19810794Speter fmt_cookie.base = fmt_cpy; 19910794Speter fmt_cookie.left = sizeof(fmt_cpy) - 1; 20010794Speter fmt_fp = fwopen(&fmt_cookie, writehook); 20110794Speter if (fmt_fp == NULL) { 20210794Speter fclose(fp); 203139440Sglebius THREAD_UNLOCK(); 20410794Speter return; 20510794Speter } 2061573Srgrimes 207110635Salfred /* 208110635Salfred * Substitute error message for %m. Be careful not to 209110635Salfred * molest an escaped percent "%%m". We want to pass it 210110635Salfred * on untouched as the format is later parsed by vfprintf. 211110635Salfred */ 212110635Salfred for ( ; (ch = *fmt); ++fmt) { 21310794Speter if (ch == '%' && fmt[1] == 'm') { 21410794Speter ++fmt; 215139440Sglebius strerror_r(saved_errno, errstr, sizeof(errstr)); 216139440Sglebius fputs(errstr, fmt_fp); 217110635Salfred } else if (ch == '%' && fmt[1] == '%') { 218110635Salfred ++fmt; 21910794Speter fputc(ch, fmt_fp); 220110635Salfred fputc(ch, fmt_fp); 221110635Salfred } else { 222110635Salfred fputc(ch, fmt_fp); 223110635Salfred } 224110635Salfred } 22510794Speter 22610794Speter /* Null terminate if room */ 22710794Speter fputc(0, fmt_fp); 22810794Speter fclose(fmt_fp); 22910794Speter 23010794Speter /* Guarantee null termination */ 23110794Speter fmt_cpy[sizeof(fmt_cpy) - 1] = '\0'; 23210794Speter 23310794Speter fmt = fmt_cpy; 23410794Speter } 23510794Speter 23610794Speter (void)vfprintf(fp, fmt, ap); 23710794Speter (void)fclose(fp); 23810794Speter 23910794Speter cnt = sizeof(tbuf) - tbuf_cookie.left; 24010794Speter 241129091Sdds /* Remove a trailing newline */ 242129091Sdds if (tbuf[cnt - 1] == '\n') 243129091Sdds cnt--; 244129091Sdds 2451573Srgrimes /* Output to stderr if requested. */ 2461573Srgrimes if (LogStat & LOG_PERROR) { 2471573Srgrimes struct iovec iov[2]; 24890045Sobrien struct iovec *v = iov; 2491573Srgrimes 2501573Srgrimes v->iov_base = stdp; 2511573Srgrimes v->iov_len = cnt - (stdp - tbuf); 2521573Srgrimes ++v; 2531573Srgrimes v->iov_base = "\n"; 2541573Srgrimes v->iov_len = 1; 25571579Sdeischen (void)_writev(STDERR_FILENO, iov, 2); 2561573Srgrimes } 2571573Srgrimes 2581573Srgrimes /* Get connected, output the message to the local logger. */ 25914332Speter if (!opened) 260139440Sglebius openlog_unlocked(LogTag, LogStat | LOG_NDELAY, 0); 26114332Speter connectlog(); 2621573Srgrimes 2631573Srgrimes /* 264136283Sglebius * If the send() failed, there are two likely scenarios: 265136283Sglebius * 1) syslogd was restarted 266137233Sglebius * 2) /var/run/log is out of socket buffer space, which 267137233Sglebius * in most cases means local DoS. 268228193Sobrien * We attempt to reconnect to /var/run/log[priv] to take care of 269136283Sglebius * case #1 and keep send()ing data to cover case #2 270136283Sglebius * to give syslogd a chance to empty its socket buffer. 271137233Sglebius * 272137233Sglebius * If we are working with a priveleged socket, then take 273137233Sglebius * only one attempt, because we don't want to freeze a 274137233Sglebius * critical application like su(1) or sshd(8). 275137233Sglebius * 27614332Speter */ 27714332Speter 278136283Sglebius if (send(LogFile, tbuf, cnt, 0) < 0) { 279136283Sglebius if (errno != ENOBUFS) { 280136283Sglebius disconnectlog(); 281136283Sglebius connectlog(); 282136283Sglebius } 283136283Sglebius do { 284228193Sobrien if (status == CONNPRIV) 285228193Sobrien break; 286148657Sdeischen _usleep(1); 287139440Sglebius if (send(LogFile, tbuf, cnt, 0) >= 0) { 288139440Sglebius THREAD_UNLOCK(); 289139256Sglebius return; 290139440Sglebius } 291136283Sglebius } while (errno == ENOBUFS); 292139440Sglebius } else { 293139440Sglebius THREAD_UNLOCK(); 294139256Sglebius return; 295139440Sglebius } 296136283Sglebius 29714332Speter /* 29891940Sdwmalone * Output the message to the console; try not to block 29991940Sdwmalone * as a blocking console should not stop other processes. 30091940Sdwmalone * Make sure the error reported is the one from the syslogd failure. 3011573Srgrimes */ 3021573Srgrimes if (LogStat & LOG_CONS && 303241046Sjilles (fd = _open(_PATH_CONSOLE, O_WRONLY|O_NONBLOCK|O_CLOEXEC, 0)) >= 304241046Sjilles 0) { 30511192Speter struct iovec iov[2]; 30690045Sobrien struct iovec *v = iov; 30711192Speter 30811192Speter p = strchr(tbuf, '>') + 1; 30911192Speter v->iov_base = p; 31011192Speter v->iov_len = cnt - (p - tbuf); 31111192Speter ++v; 31211192Speter v->iov_base = "\r\n"; 31311192Speter v->iov_len = 2; 31471579Sdeischen (void)_writev(fd, iov, 2); 31556698Sjasone (void)_close(fd); 3161573Srgrimes } 317139440Sglebius 318139440Sglebius THREAD_UNLOCK(); 3191573Srgrimes} 320139440Sglebius 321139440Sglebius/* Should be called with mutex acquired */ 32214332Speterstatic void 323144815Sstefanfdisconnectlog(void) 3241573Srgrimes{ 32514332Speter /* 32614332Speter * If the user closed the FD and opened another in the same slot, 32714332Speter * that's their problem. They should close it before calling on 32814332Speter * system services. 32914332Speter */ 33014332Speter if (LogFile != -1) { 33156698Sjasone _close(LogFile); 33214332Speter LogFile = -1; 33314332Speter } 334137233Sglebius status = NOCONN; /* retry connect */ 33514332Speter} 3361573Srgrimes 337139440Sglebius/* Should be called with mutex acquired */ 33814332Speterstatic void 339144815Sstefanfconnectlog(void) 34014332Speter{ 34134059Sbrian struct sockaddr_un SyslogAddr; /* AF_UNIX address of local logger */ 34224068Sjdp 3431573Srgrimes if (LogFile == -1) { 34471579Sdeischen if ((LogFile = _socket(AF_UNIX, SOCK_DGRAM, 0)) == -1) 34514332Speter return; 346220157Spluknet (void)_fcntl(LogFile, F_SETFD, FD_CLOEXEC); 3471573Srgrimes } 348137233Sglebius if (LogFile != -1 && status == NOCONN) { 34934059Sbrian SyslogAddr.sun_len = sizeof(SyslogAddr); 35034059Sbrian SyslogAddr.sun_family = AF_UNIX; 351137233Sglebius 352137233Sglebius /* 353137233Sglebius * First try priveleged socket. If no success, 354137233Sglebius * then try default socket. 355137233Sglebius */ 356137233Sglebius (void)strncpy(SyslogAddr.sun_path, _PATH_LOG_PRIV, 35734075Sbrian sizeof SyslogAddr.sun_path); 358137233Sglebius if (_connect(LogFile, (struct sockaddr *)&SyslogAddr, 359137233Sglebius sizeof(SyslogAddr)) != -1) 360137233Sglebius status = CONNPRIV; 36124068Sjdp 362137233Sglebius if (status == NOCONN) { 363137233Sglebius (void)strncpy(SyslogAddr.sun_path, _PATH_LOG, 364137233Sglebius sizeof SyslogAddr.sun_path); 365137233Sglebius if (_connect(LogFile, (struct sockaddr *)&SyslogAddr, 366137233Sglebius sizeof(SyslogAddr)) != -1) 367137233Sglebius status = CONNDEF; 368137233Sglebius } 369137233Sglebius 370137233Sglebius if (status == NOCONN) { 37124068Sjdp /* 37224068Sjdp * Try the old "/dev/log" path, for backward 37324068Sjdp * compatibility. 37424068Sjdp */ 37534059Sbrian (void)strncpy(SyslogAddr.sun_path, _PATH_OLDLOG, 37634075Sbrian sizeof SyslogAddr.sun_path); 377137233Sglebius if (_connect(LogFile, (struct sockaddr *)&SyslogAddr, 378137233Sglebius sizeof(SyslogAddr)) != -1) 379137233Sglebius status = CONNDEF; 38024068Sjdp } 38124068Sjdp 382137233Sglebius if (status == NOCONN) { 38356698Sjasone (void)_close(LogFile); 3841573Srgrimes LogFile = -1; 38524068Sjdp } 38614332Speter } 3871573Srgrimes} 3881573Srgrimes 389139440Sglebiusstatic void 390144815Sstefanfopenlog_unlocked(const char *ident, int logstat, int logfac) 39114332Speter{ 39214332Speter if (ident != NULL) 39314332Speter LogTag = ident; 39414332Speter LogStat = logstat; 39514332Speter if (logfac != 0 && (logfac &~ LOG_FACMASK) == 0) 39614332Speter LogFacility = logfac; 39714332Speter 39814332Speter if (LogStat & LOG_NDELAY) /* open immediately */ 39914332Speter connectlog(); 40014332Speter 40114332Speter opened = 1; /* ident and facility has been set */ 40214332Speter} 40314332Speter 40414332Spetervoid 405144815Sstefanfopenlog(const char *ident, int logstat, int logfac) 406139440Sglebius{ 407139440Sglebius THREAD_LOCK(); 408139440Sglebius openlog_unlocked(ident, logstat, logfac); 409139440Sglebius THREAD_UNLOCK(); 410139440Sglebius} 411139440Sglebius 412139440Sglebius 413139440Sglebiusvoid 414144815Sstefanfcloselog(void) 4151573Srgrimes{ 416139440Sglebius THREAD_LOCK(); 417237286Seadler if (LogFile != -1) { 418237286Seadler (void)_close(LogFile); 419237286Seadler LogFile = -1; 420237286Seadler } 421106911Sru LogTag = NULL; 422137233Sglebius status = NOCONN; 423139440Sglebius THREAD_UNLOCK(); 4241573Srgrimes} 4251573Srgrimes 4261573Srgrimes/* setlogmask -- set the log mask level */ 4271573Srgrimesint 428144815Sstefanfsetlogmask(int pmask) 4291573Srgrimes{ 4301573Srgrimes int omask; 4311573Srgrimes 432139440Sglebius THREAD_LOCK(); 4331573Srgrimes omask = LogMask; 4341573Srgrimes if (pmask != 0) 4351573Srgrimes LogMask = pmask; 436139440Sglebius THREAD_UNLOCK(); 4371573Srgrimes return (omask); 4381573Srgrimes} 439