xref: /titanic_52/usr/src/lib/libc/port/gen/syslog.c (revision d1a17b3d9a7284d357ac39056f95a18d95106206)
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		8192		/* 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 	struct timeval 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 	int ret;
194 
195 /*
196  * Maximum tag length is 256 (the pad in outline) minus the size of the
197  * other things that can go in the pad.
198  */
199 #define	MAX_TAG		230
200 
201 	/* see if we should just throw out this message */
202 	if (pri < 0 || PRIFAC(pri) >= LOG_NFACILITIES ||
203 	    (PRIMASK(pri) & LogMask) == 0)
204 		return;
205 
206 	if (LogFileInvalid)
207 		return;
208 
209 	/*
210 	 * if openlog() has not been called by the application,
211 	 * try to get the name of the application and set it
212 	 * as the ident string for messages. If unable to get
213 	 * it for any reason, fall back to using the default
214 	 * of syslog. If we succeed in getting the name, also
215 	 * turn on LOG_PID, to provide greater detail.
216 	 */
217 	showpid = 0;
218 	if (OpenLogCalled == 0) {
219 		(void) sprintf(procfile, "/proc/%d/psinfo", (int)getpid());
220 		if ((procfd = open(procfile, O_RDONLY)) >= 0) {
221 			if (read(procfd, &p, sizeof (psinfo_t)) >= 0) {
222 				(void) strncpy(ProcName, p.pr_fname, PRFNSZ);
223 				LogTag = (const char *) &ProcName;
224 				showpid = LOG_PID;
225 			}
226 			(void) close(procfd);
227 		}
228 	}
229 	if (LogFile < 0)
230 		openlog(LogTag, LogStat|LOG_NDELAY|showpid, 0);
231 
232 	if ((fstat(LogFile, &statbuff) != 0) ||
233 	    (!S_ISCHR(statbuff.st_mode)) || (statbuff.st_rdev != LogDev)) {
234 		LogFileInvalid = TRUE;
235 		return;
236 	}
237 
238 	/* set default facility if none specified */
239 	if ((pri & LOG_FACMASK) == 0)
240 		pri |= LogFacility;
241 
242 	/* build the header */
243 	hdr.pri = pri;
244 	hdr.flags = SL_CONSOLE;
245 	hdr.level = 0;
246 
247 	/* build the message */
248 	/*
249 	 * To avoid potential security problems, bounds checking is done
250 	 * on outline and buf.
251 	 * The following code presumes that the header information will
252 	 * fit in 250-odd bytes, as was accounted for in the buffer size
253 	 * allocation.  This is dependent on the assumption that the LogTag
254 	 * and the string returned by sprintf() for getpid() will return
255 	 * be less than 230-odd characters combined.
256 	 */
257 	o = outline;
258 	(void) gettimeofday(&now, NULL);
259 	(void) sprintf(o, "%.15s.%03d ", ctime_r(&now.tv_sec, timestr, 26) + 4,
260 		now.tv_usec / 1000);
261 	o += strlen(o);
262 
263 	if (LogTag) {
264 		taglen = strlen(LogTag) < MAX_TAG ? strlen(LogTag) : MAX_TAG;
265 		(void) strncpy(o, LogTag, taglen);
266 		o[taglen] = '\0';
267 		o += strlen(o);
268 	}
269 	if (LogStat & LOG_PID) {
270 		(void) sprintf(o, "[%d]", (int)getpid());
271 		o += strlen(o);
272 	}
273 	if (LogTag) {
274 		(void) strcpy(o, ": ");
275 		o += 2;
276 	}
277 
278 	STRLOG_MAKE_MSGID(fmt, msgid);
279 	(void) sprintf(o, "[ID %u FACILITY_AND_PRIORITY] ", msgid);
280 	o += strlen(o);
281 
282 	b = buf;
283 	f = (char *)fmt;
284 	while ((c = *f++) != '\0' && b < &buf[MAXLINE]) {
285 		char *errmsg;
286 		if (c != '%') {
287 			*b++ = c;
288 			continue;
289 		}
290 		if ((c = *f++) != 'm') {
291 			*b++ = '%';
292 			*b++ = c;
293 			continue;
294 		}
295 		if ((errmsg = strerror(olderrno)) == NULL)
296 			(void) snprintf(b, &buf[MAXLINE] - b, "error %d",
297 			    olderrno);
298 		else {
299 			while (*errmsg != '\0' && b < &buf[MAXLINE]) {
300 				if (*errmsg == '%') {
301 					(void) strcpy(b, "%%");
302 					b += 2;
303 				}
304 				else
305 					*b++ = *errmsg;
306 				errmsg++;
307 			}
308 			*b = '\0';
309 		}
310 		b += strlen(b);
311 	}
312 	if (b > buf && *(b-1) != '\n')	/* ensure at least one newline */
313 		*b++ = '\n';
314 	*b = '\0';
315 	/* LINTED variable format specifier */
316 	(void) vsnprintf(o, &outline[sizeof (outline)] - o, buf, ap);
317 	clen  = (int)strlen(outline) + 1;	/* add one for NULL byte */
318 	if (clen > MAXLINE) {
319 		clen = MAXLINE;
320 		outline[MAXLINE-1] = '\0';
321 	}
322 
323 	/*
324 	 * 1136432 points out that the underlying log driver actually
325 	 * refuses to accept (ERANGE) messages longer than LOG_MAXPS
326 	 * bytes.  So it really doesn't make much sense to putmsg a
327 	 * longer message..
328 	 */
329 	if (clen > LOG_MAXPS) {
330 		clen = LOG_MAXPS;
331 		outline[LOG_MAXPS-1] = '\0';
332 	}
333 
334 	/* set up the strbufs */
335 	ctl.maxlen = sizeof (struct log_ctl);
336 	ctl.len = sizeof (struct log_ctl);
337 	ctl.buf = (caddr_t)&hdr;
338 	dat.maxlen = sizeof (outline);
339 	dat.len = clen;
340 	dat.buf = outline;
341 
342 	/* output the message to the local logger */
343 	ret = putmsg(LogFile, &ctl, &dat, 0);
344 	if (!(LogStat & LOG_CONS))
345 		return;
346 	if ((ret >= 0) && syslogd_ok())
347 		return;
348 
349 	/*
350 	 * Output the message to the console directly.  To reduce visual
351 	 * clutter, we strip out the message ID.
352 	 */
353 	if ((msgid_start = strstr(outline, "[ID ")) != NULL &&
354 	    (msgid_end = strstr(msgid_start, "] ")) != NULL)
355 		(void) strcpy(msgid_start, msgid_end + 2);
356 
357 	clen = strlen(outline) + 1;
358 
359 	nowait = (LogStat & LOG_NOWAIT);
360 	pid = forkx(nowait? 0 : (FORK_NOSIGCHLD | FORK_WAITPID));
361 	if (pid == -1)
362 		return;
363 
364 	if (pid == 0) {
365 		sigset_t sigs;
366 		int fd;
367 
368 		(void) sigset(SIGALRM, SIG_DFL);
369 		(void) sigemptyset(&sigs);
370 		(void) sigaddset(&sigs, SIGALRM);
371 		(void) sigprocmask(SIG_UNBLOCK, &sigs, NULL);
372 		(void) alarm(5);
373 		if (((fd = open(sysmsg, O_WRONLY)) >= 0) ||
374 		    (fd = open(ctty, O_WRONLY)) >= 0) {
375 			(void) alarm(0);
376 			outline[clen - 1] = '\r';
377 			(void) write(fd, outline, clen);
378 			(void) close(fd);
379 		}
380 		_exit(0);
381 	}
382 	if (!nowait)
383 		while (waitpid(pid, NULL, 0) == -1 && errno == EINTR)
384 			continue;
385 }
386 
387 /*
388  * Use a door call to syslogd to see if it's alive.
389  */
390 static int
391 syslogd_ok(void)
392 {
393 	int d;
394 	int s;
395 	door_arg_t darg;
396 	door_info_t info;
397 
398 	if ((d = open(DOORFILE, O_RDONLY)) < 0)
399 		return (0);
400 	/*
401 	 * see if our pid matches the pid of the door server.
402 	 * If so, syslogd has called syslog(), probably as
403 	 * a result of some name service library error, and
404 	 * we don't want to let syslog continue and possibly
405 	 * fork here.
406 	 */
407 	info.di_target = 0;
408 	if (__door_info(d, &info) < 0 || info.di_target == getpid()) {
409 		(void) close(d);
410 		return (0);
411 	}
412 	darg.data_ptr = NULL;
413 	darg.data_size = 0;
414 	darg.desc_ptr = NULL;
415 	darg.desc_num = 0;
416 	darg.rbuf = NULL;
417 	darg.rsize = 0;
418 	s = __door_call(d, &darg);
419 	(void) close(d);
420 	if (s < 0)
421 		return (0);		/* failure - syslogd dead */
422 	else
423 		return (1);
424 }
425 
426 /*
427  * OPENLOG -- open system log
428  */
429 
430 void
431 openlog(const char *ident, int logstat, int logfac)
432 {
433 	struct	stat	statbuff;
434 
435 	OpenLogCalled = 1;
436 	if (ident != NULL)
437 		LogTag = ident;
438 	LogStat = logstat;
439 	if (logfac != 0)
440 		LogFacility = logfac & LOG_FACMASK;
441 
442 	/*
443 	 * if the fstat(2) fails or the st_rdev has changed
444 	 * then we must open the file
445 	 */
446 	if ((fstat(LogFile, &statbuff) == 0) &&
447 	    (S_ISCHR(statbuff.st_mode)) && (statbuff.st_rdev == LogDev))
448 		return;
449 
450 	if (LogStat & LOG_NDELAY) {
451 		LogFile = open(logname, O_WRONLY);
452 		(void) fcntl(LogFile, F_SETFD, 1);
453 		(void) fstat(LogFile, &statbuff);
454 		LogDev = statbuff.st_rdev;
455 	}
456 }
457 
458 /*
459  * CLOSELOG -- close the system log
460  */
461 
462 void
463 closelog(void)
464 {
465 	struct	stat	statbuff;
466 
467 	OpenLogCalled = 0;
468 
469 	/* if the LogFile is invalid it can not be closed */
470 	if (LogFileInvalid)
471 		return;
472 
473 	/*
474 	 * if the fstat(2) fails or the st_rdev has changed
475 	 * then we can not close the file
476 	 */
477 	if ((fstat(LogFile, &statbuff) == 0) && (statbuff.st_rdev == LogDev)) {
478 		(void) close(LogFile);
479 		LogFile = -1;
480 		LogStat = 0;
481 	}
482 }
483 
484 /*
485  * SETLOGMASK -- set the log mask level
486  */
487 int
488 setlogmask(int pmask)
489 {
490 	int omask = 0;
491 
492 	omask = LogMask;
493 	if (pmask != 0)
494 		LogMask = pmask;
495 	return (omask);
496 }
497