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
105static 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
138static 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 */
158void
159syslog(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
169void
170vsyslog(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 */
387static int
388syslogd_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
427void
428openlog(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
459void
460closelog(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 */
484int
485setlogmask(int pmask)
486{
487	int omask = 0;
488
489	omask = LogMask;
490	if (pmask != 0)
491		LogMask = pmask;
492	return (omask);
493}
494