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