1/* $OpenBSD: syslog.c,v 1.28 2005/08/08 08:05:34 espie Exp $ */ 2/* 3 * Copyright (c) 1983, 1988, 1993 4 * The Regents of the University of California. All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 3. Neither the name of the University nor the names of its contributors 15 * may be used to endorse or promote products derived from this software 16 * without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 * SUCH DAMAGE. 29 */ 30 31#include <sys/types.h> 32#include <sys/socket.h> 33#include <sys/uio.h> 34#include <syslog.h> 35#include <sys/un.h> 36#include <netdb.h> 37 38#include <errno.h> 39#include <fcntl.h> 40#include <paths.h> 41#include <stdio.h> 42#include <string.h> 43#include <time.h> 44#include <unistd.h> 45#include <stdarg.h> 46 47static struct syslog_data sdata = SYSLOG_DATA_INIT; 48 49extern char *__progname; /* Program name, from crt0. */ 50 51static void disconnectlog_r(struct syslog_data *); /* disconnect from syslogd */ 52static void connectlog_r(struct syslog_data *); /* (re)connect to syslogd */ 53 54/* 55 * syslog, vsyslog -- 56 * print message on log file; output is intended for syslogd(8). 57 */ 58void 59syslog(int pri, const char *fmt, ...) 60{ 61 va_list ap; 62 63 va_start(ap, fmt); 64 vsyslog(pri, fmt, ap); 65 va_end(ap); 66} 67 68void 69vsyslog(int pri, const char *fmt, va_list ap) 70{ 71 vsyslog_r(pri, &sdata, fmt, ap); 72} 73 74void 75openlog(const char *ident, int logstat, int logfac) 76{ 77 openlog_r(ident, logstat, logfac, &sdata); 78} 79 80void 81closelog(void) 82{ 83 closelog_r(&sdata); 84} 85 86/* setlogmask -- set the log mask level */ 87int 88setlogmask(int pmask) 89{ 90 return setlogmask_r(pmask, &sdata); 91} 92 93/* Reentrant version of syslog, i.e. syslog_r() */ 94 95void 96syslog_r(int pri, struct syslog_data *data, const char *fmt, ...) 97{ 98 va_list ap; 99 100 va_start(ap, fmt); 101 vsyslog_r(pri, data, fmt, ap); 102 va_end(ap); 103} 104 105void 106vsyslog_r(int pri, struct syslog_data *data, const char *fmt, va_list ap) 107{ 108 int cnt; 109 char ch, *p, *t; 110 time_t now; 111 int fd, saved_errno, error; 112#define TBUF_LEN 2048 113#define FMT_LEN 1024 114 char *stdp = NULL, tbuf[TBUF_LEN], fmt_cpy[FMT_LEN]; 115 int tbuf_left, fmt_left, prlen; 116 117#define INTERNALLOG LOG_ERR|LOG_CONS|LOG_PERROR|LOG_PID 118 /* Check for invalid bits. */ 119 if (pri & ~(LOG_PRIMASK|LOG_FACMASK)) { 120 if (data == &sdata) { 121 syslog(INTERNALLOG, 122 "syslog: unknown facility/priority: %x", pri); 123 } else { 124 syslog_r(INTERNALLOG, data, 125 "syslog_r: unknown facility/priority: %x", pri); 126 } 127 pri &= LOG_PRIMASK|LOG_FACMASK; 128 } 129 130 /* Check priority against setlogmask values. */ 131 if (!(LOG_MASK(LOG_PRI(pri)) & data->log_mask)) 132 return; 133 134 saved_errno = errno; 135 136 /* Set default facility if none specified. */ 137 if ((pri & LOG_FACMASK) == 0) 138 pri |= data->log_fac; 139 140 /* If we have been called through syslog(), no need for reentrancy. */ 141 if (data == &sdata) 142 (void)time(&now); 143 144 p = tbuf; 145 tbuf_left = TBUF_LEN; 146 147#define DEC() \ 148 do { \ 149 if (prlen < 0) \ 150 prlen = 0; \ 151 if (prlen >= tbuf_left) \ 152 prlen = tbuf_left - 1; \ 153 p += prlen; \ 154 tbuf_left -= prlen; \ 155 } while (0) 156 157 prlen = snprintf(p, tbuf_left, "<%d>", pri); 158 DEC(); 159 160 /* 161 * syslogd will expand time automagically for reentrant case, and 162 * for normal case, just do like before 163 */ 164 if (data == &sdata) { 165 prlen = strftime(p, tbuf_left, "%h %e %T ", localtime(&now)); 166 DEC(); 167 } 168 169 if (data->log_stat & LOG_PERROR) 170 stdp = p; 171 if (data->log_tag == NULL) 172 data->log_tag = __progname; 173 if (data->log_tag != NULL) { 174 prlen = snprintf(p, tbuf_left, "%s", data->log_tag); 175 DEC(); 176 } 177 if (data->log_stat & LOG_PID) { 178 prlen = snprintf(p, tbuf_left, "[%ld]", (long)getpid()); 179 DEC(); 180 } 181 if (data->log_tag != NULL) { 182 if (tbuf_left > 1) { 183 *p++ = ':'; 184 tbuf_left--; 185 } 186 if (tbuf_left > 1) { 187 *p++ = ' '; 188 tbuf_left--; 189 } 190 } 191 192 /* strerror() is not reentrant */ 193 194 for (t = fmt_cpy, fmt_left = FMT_LEN; (ch = *fmt); ++fmt) { 195 if (ch == '%' && fmt[1] == 'm') { 196 ++fmt; 197 if (data == &sdata) { 198 prlen = snprintf(t, fmt_left, "%s", 199 strerror(saved_errno)); 200 } else { 201 prlen = snprintf(t, fmt_left, "Error %d", 202 saved_errno); 203 } 204 if (prlen < 0) 205 prlen = 0; 206 if (prlen >= fmt_left) 207 prlen = fmt_left - 1; 208 t += prlen; 209 fmt_left -= prlen; 210 } else if (ch == '%' && fmt[1] == '%' && fmt_left > 2) { 211 *t++ = '%'; 212 *t++ = '%'; 213 fmt++; 214 fmt_left -= 2; 215 } else { 216 if (fmt_left > 1) { 217 *t++ = ch; 218 fmt_left--; 219 } 220 } 221 } 222 *t = '\0'; 223 224 prlen = vsnprintf(p, tbuf_left, fmt_cpy, ap); 225 DEC(); 226 cnt = p - tbuf; 227 228 /* Output to stderr if requested. */ 229 if (data->log_stat & LOG_PERROR) { 230 struct iovec iov[2]; 231 232 iov[0].iov_base = stdp; 233 iov[0].iov_len = cnt - (stdp - tbuf); 234 iov[1].iov_base = "\n"; 235 iov[1].iov_len = 1; 236 (void)writev(STDERR_FILENO, iov, 2); 237 } 238 239 /* Get connected, output the message to the local logger. */ 240 if (!data->opened) 241 openlog_r(data->log_tag, data->log_stat, 0, data); 242 connectlog_r(data); 243 244 /* 245 * If the send() failed, there are two likely scenarios: 246 * 1) syslogd was restarted 247 * 2) /dev/log is out of socket buffer space 248 * We attempt to reconnect to /dev/log to take care of 249 * case #1 and keep send()ing data to cover case #2 250 * to give syslogd a chance to empty its socket buffer. 251 */ 252 if ((error = send(data->log_file, tbuf, cnt, 0)) < 0) { 253 if (errno != ENOBUFS) { 254 disconnectlog_r(data); 255 connectlog_r(data); 256 } 257 do { 258 usleep(1); 259 if ((error = send(data->log_file, tbuf, cnt, 0)) >= 0) 260 break; 261 } while (errno == ENOBUFS); 262 } 263 264 /* 265 * Output the message to the console; try not to block 266 * as a blocking console should not stop other processes. 267 * Make sure the error reported is the one from the syslogd failure. 268 */ 269 if (error == -1 && (data->log_stat & LOG_CONS) && 270 (fd = open(_PATH_CONSOLE, O_WRONLY|O_NONBLOCK, 0)) >= 0) { 271 struct iovec iov[2]; 272 273 p = strchr(tbuf, '>') + 1; 274 iov[0].iov_base = p; 275 iov[0].iov_len = cnt - (p - tbuf); 276 iov[1].iov_base = "\r\n"; 277 iov[1].iov_len = 2; 278 (void)writev(fd, iov, 2); 279 (void)close(fd); 280 } 281 282 if (data != &sdata) 283 closelog_r(data); 284} 285 286static void 287disconnectlog_r(struct syslog_data *data) 288{ 289 /* 290 * If the user closed the FD and opened another in the same slot, 291 * that's their problem. They should close it before calling on 292 * system services. 293 */ 294 if (data->log_file != -1) { 295 close(data->log_file); 296 data->log_file = -1; 297 } 298 data->connected = 0; /* retry connect */ 299} 300 301static void 302connectlog_r(struct syslog_data *data) 303{ 304 union { 305 struct sockaddr syslogAddr; 306 struct sockaddr_un syslogAddrUn; 307 } u; 308 309#define SyslogAddr u.syslogAddrUn 310 311 if (data->log_file == -1) { 312 if ((data->log_file = socket(AF_UNIX, SOCK_DGRAM, 0)) == -1) 313 return; 314 (void)fcntl(data->log_file, F_SETFD, 1); 315 } 316 if (data->log_file != -1 && !data->connected) { 317 memset(&SyslogAddr, '\0', sizeof(SyslogAddr)); 318#if 0 319 /* BIONIC: no sun_len field to fill on Linux */ 320 SyslogAddr.sun_len = sizeof(SyslogAddr); 321#endif 322 SyslogAddr.sun_family = AF_UNIX; 323 strlcpy(SyslogAddr.sun_path, _PATH_LOG, 324 sizeof(SyslogAddr.sun_path)); 325 if (connect(data->log_file, &u.syslogAddr, 326 sizeof(SyslogAddr)) == -1) { 327 (void)close(data->log_file); 328 data->log_file = -1; 329 } else 330 data->connected = 1; 331 } 332} 333 334void 335openlog_r(const char *ident, int logstat, int logfac, struct syslog_data *data) 336{ 337 if (ident != NULL) 338 data->log_tag = ident; 339 data->log_stat = logstat; 340 if (logfac != 0 && (logfac &~ LOG_FACMASK) == 0) 341 data->log_fac = logfac; 342 343 if (data->log_stat & LOG_NDELAY) /* open immediately */ 344 connectlog_r(data); 345 346 data->opened = 1; /* ident and facility has been set */ 347} 348 349void 350closelog_r(struct syslog_data *data) 351{ 352 (void)close(data->log_file); 353 data->log_file = -1; 354 data->connected = 0; 355 data->log_tag = NULL; 356} 357 358/* setlogmask -- set the log mask level */ 359int 360setlogmask_r(int pmask, struct syslog_data *data) 361{ 362 int omask; 363 364 omask = data->log_mask; 365 if (pmask != 0) 366 data->log_mask = pmask; 367 return (omask); 368} 369