Home | History | Annotate | Download | only in gen
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9  * or http://www.opensolaris.org/os/licensing.
     10  * See the License for the specific language governing permissions
     11  * and limitations under the License.
     12  *
     13  * When distributing Covered Code, include this CDDL HEADER in each
     14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15  * If applicable, add the following below this CDDL HEADER, with the
     16  * fields enclosed by brackets "[]" replaced with your own identifying
     17  * information: Portions Copyright [yyyy] [name of copyright owner]
     18  *
     19  * CDDL HEADER END
     20  */
     21 
     22 /*
     23  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
     24  * Use is subject to license terms.
     25  */
     26 
     27 /*	Copyright (c) 1988 AT&T	*/
     28 /*	  All Rights Reserved  	*/
     29 
     30 /*
     31  * University Copyright- Copyright (c) 1982, 1986, 1988
     32  * The Regents of the University of California
     33  * All Rights Reserved
     34  *
     35  * University Acknowledgment- Portions of this document are derived from
     36  * software developed by the University of California, Berkeley, and its
     37  * contributors.
     38  */
     39 
     40 
     41 /*
     42  * SYSLOG -- print message on log file
     43  *
     44  * This routine looks a lot like printf, except that it
     45  * outputs to the log file instead of the standard output.
     46  * Also:
     47  *	adds a timestamp,
     48  *	prints the module name in front of the message,
     49  *	has some other formatting types (or will sometime),
     50  *	adds a newline on the end of the message.
     51  *
     52  * The output of this routine is intended to be read by /etc/syslogd.
     53  */
     54 
     55 #pragma weak _syslog = syslog
     56 
     57 #include "lint.h"
     58 #include <sys/types.h>
     59 #include <sys/types32.h>
     60 #include <sys/mman.h>
     61 #include <sys/stropts.h>
     62 #include <sys/strlog.h>
     63 #include <sys/log.h>		/* for LOG_MAXPS */
     64 #include <stdlib.h>
     65 #include <procfs.h>
     66 #include <syslog.h>
     67 #include <signal.h>
     68 #include <fcntl.h>
     69 #include <string.h>
     70 #include <stdarg.h>
     71 #include <unistd.h>
     72 #include <wait.h>
     73 #include <stdio.h>
     74 #include <string.h>
     75 #include <errno.h>
     76 #include <thread.h>
     77 #include <synch.h>
     78 #include <sys/door.h>
     79 #include <sys/stat.h>
     80 #include <stropts.h>
     81 #include <sys/fork.h>
     82 #include <sys/wait.h>
     83 #include "libc.h"
     84 
     85 #define	MAXLINE		1024		/* max message size (but see below) */
     86 
     87 #define	PRIMASK(p)	(1 << ((p) & LOG_PRIMASK))
     88 #define	PRIFAC(p)	(((p) & LOG_FACMASK) >> 3)
     89 #define	IMPORTANT 	LOG_ERR
     90 
     91 #ifndef FALSE
     92 #define	FALSE 	0
     93 #endif
     94 
     95 #ifndef TRUE
     96 #define	TRUE	1
     97 #endif
     98 
     99 #define	logname		"/dev/conslog"
    100 #define	ctty		"/dev/syscon"
    101 #define	sysmsg		"/dev/sysmsg"
    102 
    103 #define	DOORFILE	"/var/run/syslog_door"
    104 
    105 static struct __syslog {
    106 	int	_LogFile;
    107 	int	_LogStat;
    108 	const char *_LogTag;
    109 	int	_LogMask;
    110 	char	*_SyslogHost;
    111 	int	_LogFacility;
    112 	int	_LogFileInvalid;
    113 	int	_OpenLogCalled;
    114 	dev_t   _LogDev;
    115 	char	_ProcName[PRFNSZ + 1];
    116 } __syslog = {
    117 	-1,		/* fd for log */
    118 	0,		/* status bits, set by openlog() */
    119 	"syslog",	/* string to tag the entry with */
    120 	0xff,		/* mask of priorities to be logged */
    121 	NULL,
    122 	LOG_USER,	/* default facility code */
    123 	FALSE,		/* check for validity of fd for log */
    124 	0,		/* openlog has not yet been called */
    125 };
    126 
    127 #define	LogFile (__syslog._LogFile)
    128 #define	LogStat (__syslog._LogStat)
    129 #define	LogTag (__syslog._LogTag)
    130 #define	LogMask (__syslog._LogMask)
    131 #define	SyslogHost (__syslog._SyslogHost)
    132 #define	LogFacility (__syslog._LogFacility)
    133 #define	LogFileInvalid (__syslog._LogFileInvalid)
    134 #define	OpenLogCalled (__syslog._OpenLogCalled)
    135 #define	LogDev (__syslog._LogDev)
    136 #define	ProcName (__syslog._ProcName)
    137 
    138 static int syslogd_ok(void);
    139 
    140 /*
    141  * Regrettably, there are several instances inside libc where
    142  * syslog() is called from the bottom of a deep call stack
    143  * and a critical lock was acquired near the top of the stack.
    144  *
    145  * Because syslog() uses stdio (and it is called from within stdio)
    146  * it runs the danger of deadlocking, perhaps with an interposed
    147  * malloc() when fork() is occurring concurrently, perhaps with
    148  * some other lock within libc.
    149  *
    150  * The only fix for this problem is to restructure libc not to do
    151  * this thing and always to call syslog() with no locks held.
    152  * This restructuring will require a substantial effort.
    153  *
    154  * Meanwhile, we just hope that on the rare occasion that syslog()
    155  * is called from within libc (such occurrences should "never happen")
    156  * that we don't get caught in a race condition deadlock.
    157  */
    158 void
    159 syslog(int pri, const char *fmt, ...)
    160 {
    161 	va_list ap;
    162 
    163 	va_start(ap, fmt);
    164 	vsyslog(pri, fmt, ap);
    165 	va_end(ap);
    166 }
    167 
    168 
    169 void
    170 vsyslog(int pri, const char *fmt, va_list ap)
    171 {
    172 	char *b, *f, *o;
    173 	char c;
    174 	int clen;
    175 	char buf[MAXLINE + 2];
    176 	char outline[MAXLINE + 256];  /* pad to allow date, system name... */
    177 	time_t now;
    178 	pid_t pid;
    179 	struct log_ctl hdr;
    180 	struct strbuf dat;
    181 	struct strbuf ctl;
    182 	char timestr[26];	/* hardwired value 26 due to Posix */
    183 	size_t taglen;
    184 	int olderrno = errno;
    185 	struct stat statbuff;
    186 	int procfd;
    187 	char procfile[32];
    188 	psinfo_t p;
    189 	int showpid;
    190 	uint32_t msgid;
    191 	char *msgid_start, *msgid_end;
    192 	int nowait;
    193 
    194 /*
    195  * Maximum tag length is 256 (the pad in outline) minus the size of the
    196  * other things that can go in the pad.
    197  */
    198 #define	MAX_TAG		230
    199 
    200 	/* see if we should just throw out this message */
    201 	if (pri < 0 || PRIFAC(pri) >= LOG_NFACILITIES ||
    202 	    (PRIMASK(pri) & LogMask) == 0)
    203 		return;
    204 
    205 	if (LogFileInvalid)
    206 		return;
    207 
    208 	/*
    209 	 * if openlog() has not been called by the application,
    210 	 * try to get the name of the application and set it
    211 	 * as the ident string for messages. If unable to get
    212 	 * it for any reason, fall back to using the default
    213 	 * of syslog. If we succeed in getting the name, also
    214 	 * turn on LOG_PID, to provide greater detail.
    215 	 */
    216 	showpid = 0;
    217 	if (OpenLogCalled == 0) {
    218 		(void) sprintf(procfile, "/proc/%d/psinfo", (int)getpid());
    219 		if ((procfd = open(procfile, O_RDONLY)) >= 0) {
    220 			if (read(procfd, &p, sizeof (psinfo_t)) >= 0) {
    221 				(void) strncpy(ProcName, p.pr_fname, PRFNSZ);
    222 				LogTag = (const char *) &ProcName;
    223 				showpid = LOG_PID;
    224 			}
    225 			(void) close(procfd);
    226 		}
    227 	}
    228 	if (LogFile < 0)
    229 		openlog(LogTag, LogStat|LOG_NDELAY|showpid, 0);
    230 
    231 	if ((fstat(LogFile, &statbuff) != 0) ||
    232 	    (!S_ISCHR(statbuff.st_mode)) || (statbuff.st_rdev != LogDev)) {
    233 		LogFileInvalid = TRUE;
    234 		return;
    235 	}
    236 
    237 	/* set default facility if none specified */
    238 	if ((pri & LOG_FACMASK) == 0)
    239 		pri |= LogFacility;
    240 
    241 	/* build the header */
    242 	hdr.pri = pri;
    243 	hdr.flags = SL_CONSOLE;
    244 	hdr.level = 0;
    245 
    246 	/* build the message */
    247 	/*
    248 	 * To avoid potential security problems, bounds checking is done
    249 	 * on outline and buf.
    250 	 * The following code presumes that the header information will
    251 	 * fit in 250-odd bytes, as was accounted for in the buffer size
    252 	 * allocation.  This is dependent on the assumption that the LogTag
    253 	 * and the string returned by sprintf() for getpid() will return
    254 	 * be less than 230-odd characters combined.
    255 	 */
    256 	o = outline;
    257 	(void) time(&now);
    258 	(void) sprintf(o, "%.15s ", ctime_r(&now, timestr, 26) + 4);
    259 	o += strlen(o);
    260 
    261 	if (LogTag) {
    262 		taglen = strlen(LogTag) < MAX_TAG ? strlen(LogTag) : MAX_TAG;
    263 		(void) strncpy(o, LogTag, taglen);
    264 		o[taglen] = '\0';
    265 		o += strlen(o);
    266 	}
    267 	if (LogStat & LOG_PID) {
    268 		(void) sprintf(o, "[%d]", (int)getpid());
    269 		o += strlen(o);
    270 	}
    271 	if (LogTag) {
    272 		(void) strcpy(o, ": ");
    273 		o += 2;
    274 	}
    275 
    276 	STRLOG_MAKE_MSGID(fmt, msgid);
    277 	(void) sprintf(o, "[ID %u FACILITY_AND_PRIORITY] ", msgid);
    278 	o += strlen(o);
    279 
    280 	b = buf;
    281 	f = (char *)fmt;
    282 	while ((c = *f++) != '\0' && b < &buf[MAXLINE]) {
    283 		char *errmsg;
    284 		if (c != '%') {
    285 			*b++ = c;
    286 			continue;
    287 		}
    288 		if ((c = *f++) != 'm') {
    289 			*b++ = '%';
    290 			*b++ = c;
    291 			continue;
    292 		}
    293 		if ((errmsg = strerror(olderrno)) == NULL)
    294 			(void) snprintf(b, &buf[MAXLINE] - b, "error %d",
    295 			    olderrno);
    296 		else {
    297 			while (*errmsg != '\0' && b < &buf[MAXLINE]) {
    298 				if (*errmsg == '%') {
    299 					(void) strcpy(b, "%%");
    300 					b += 2;
    301 				}
    302 				else
    303 					*b++ = *errmsg;
    304 				errmsg++;
    305 			}
    306 			*b = '\0';
    307 		}
    308 		b += strlen(b);
    309 	}
    310 	if (b > buf && *(b-1) != '\n')	/* ensure at least one newline */
    311 		*b++ = '\n';
    312 	*b = '\0';
    313 	/* LINTED variable format specifier */
    314 	(void) vsnprintf(o, &outline[sizeof (outline)] - o, buf, ap);
    315 	clen  = (int)strlen(outline) + 1;	/* add one for NULL byte */
    316 	if (clen > MAXLINE) {
    317 		clen = MAXLINE;
    318 		outline[MAXLINE-1] = '\0';
    319 	}
    320 
    321 	/*
    322 	 * 1136432 points out that the underlying log driver actually
    323 	 * refuses to accept (ERANGE) messages longer than LOG_MAXPS
    324 	 * bytes.  So it really doesn't make much sense to putmsg a
    325 	 * longer message..
    326 	 */
    327 	if (clen > LOG_MAXPS) {
    328 		clen = LOG_MAXPS;
    329 		outline[LOG_MAXPS-1] = '\0';
    330 	}
    331 
    332 	/* set up the strbufs */
    333 	ctl.maxlen = sizeof (struct log_ctl);
    334 	ctl.len = sizeof (struct log_ctl);
    335 	ctl.buf = (caddr_t)&hdr;
    336 	dat.maxlen = sizeof (outline);
    337 	dat.len = clen;
    338 	dat.buf = outline;
    339 
    340 	/* output the message to the local logger */
    341 	if ((putmsg(LogFile, &ctl, &dat, 0) >= 0) && syslogd_ok())
    342 		return;
    343 	if (!(LogStat & LOG_CONS))
    344 		return;
    345 
    346 	/*
    347 	 * Output the message to the console directly.  To reduce visual
    348 	 * clutter, we strip out the message ID.
    349 	 */
    350 	if ((msgid_start = strstr(outline, "[ID ")) != NULL &&
    351 	    (msgid_end = strstr(msgid_start, "] ")) != NULL)
    352 		(void) strcpy(msgid_start, msgid_end + 2);
    353 
    354 	clen = strlen(outline) + 1;
    355 
    356 	nowait = (LogStat & LOG_NOWAIT);
    357 	pid = forkx(nowait? 0 : (FORK_NOSIGCHLD | FORK_WAITPID));
    358 	if (pid == -1)
    359 		return;
    360 
    361 	if (pid == 0) {
    362 		sigset_t sigs;
    363 		int fd;
    364 
    365 		(void) sigset(SIGALRM, SIG_DFL);
    366 		(void) sigemptyset(&sigs);
    367 		(void) sigaddset(&sigs, SIGALRM);
    368 		(void) sigprocmask(SIG_UNBLOCK, &sigs, NULL);
    369 		(void) alarm(5);
    370 		if (((fd = open(sysmsg, O_WRONLY)) >= 0) ||
    371 		    (fd = open(ctty, O_WRONLY)) >= 0) {
    372 			(void) alarm(0);
    373 			outline[clen - 1] = '\r';
    374 			(void) write(fd, outline, clen);
    375 			(void) close(fd);
    376 		}
    377 		_exit(0);
    378 	}
    379 	if (!nowait)
    380 		while (waitpid(pid, NULL, 0) == -1 && errno == EINTR)
    381 			continue;
    382 }
    383 
    384 /*
    385  * Use a door call to syslogd to see if it's alive.
    386  */
    387 static int
    388 syslogd_ok(void)
    389 {
    390 	int d;
    391 	int s;
    392 	door_arg_t darg;
    393 	door_info_t info;
    394 
    395 	if ((d = open(DOORFILE, O_RDONLY)) < 0)
    396 		return (0);
    397 	/*
    398 	 * see if our pid matches the pid of the door server.
    399 	 * If so, syslogd has called syslog(), probably as
    400 	 * a result of some name service library error, and
    401 	 * we don't want to let syslog continue and possibly
    402 	 * fork here.
    403 	 */
    404 	info.di_target = 0;
    405 	if (__door_info(d, &info) < 0 || info.di_target == getpid()) {
    406 		(void) close(d);
    407 		return (0);
    408 	}
    409 	darg.data_ptr = NULL;
    410 	darg.data_size = 0;
    411 	darg.desc_ptr = NULL;
    412 	darg.desc_num = 0;
    413 	darg.rbuf = NULL;
    414 	darg.rsize = 0;
    415 	s = __door_call(d, &darg);
    416 	(void) close(d);
    417 	if (s < 0)
    418 		return (0);		/* failure - syslogd dead */
    419 	else
    420 		return (1);
    421 }
    422 
    423 /*
    424  * OPENLOG -- open system log
    425  */
    426 
    427 void
    428 openlog(const char *ident, int logstat, int logfac)
    429 {
    430 	struct	stat	statbuff;
    431 
    432 	OpenLogCalled = 1;
    433 	if (ident != NULL)
    434 		LogTag = ident;
    435 	LogStat = logstat;
    436 	if (logfac != 0)
    437 		LogFacility = logfac & LOG_FACMASK;
    438 
    439 	/*
    440 	 * if the fstat(2) fails or the st_rdev has changed
    441 	 * then we must open the file
    442 	 */
    443 	if ((fstat(LogFile, &statbuff) == 0) &&
    444 	    (S_ISCHR(statbuff.st_mode)) && (statbuff.st_rdev == LogDev))
    445 		return;
    446 
    447 	if (LogStat & LOG_NDELAY) {
    448 		LogFile = open(logname, O_WRONLY);
    449 		(void) fcntl(LogFile, F_SETFD, 1);
    450 		(void) fstat(LogFile, &statbuff);
    451 		LogDev = statbuff.st_rdev;
    452 	}
    453 }
    454 
    455 /*
    456  * CLOSELOG -- close the system log
    457  */
    458 
    459 void
    460 closelog(void)
    461 {
    462 	struct	stat	statbuff;
    463 
    464 	OpenLogCalled = 0;
    465 
    466 	/* if the LogFile is invalid it can not be closed */
    467 	if (LogFileInvalid)
    468 		return;
    469 
    470 	/*
    471 	 * if the fstat(2) fails or the st_rdev has changed
    472 	 * then we can not close the file
    473 	 */
    474 	if ((fstat(LogFile, &statbuff) == 0) && (statbuff.st_rdev == LogDev)) {
    475 		(void) close(LogFile);
    476 		LogFile = -1;
    477 		LogStat = 0;
    478 	}
    479 }
    480 
    481 /*
    482  * SETLOGMASK -- set the log mask level
    483  */
    484 int
    485 setlogmask(int pmask)
    486 {
    487 	int omask = 0;
    488 
    489 	omask = LogMask;
    490 	if (pmask != 0)
    491 		LogMask = pmask;
    492 	return (omask);
    493 }
    494