xref: /onnv-gate/usr/src/cmd/svc/startd/log.c (revision 11829:ce38b41173bc)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
51958Slianep  * Common Development and Distribution License (the "License").
61958Slianep  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
210Sstevel@tonic-gate /*
22*11829SPetr.Sumbera@Sun.COM  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
230Sstevel@tonic-gate  * Use is subject to license terms.
240Sstevel@tonic-gate  */
250Sstevel@tonic-gate 
260Sstevel@tonic-gate /*
270Sstevel@tonic-gate  * log.c - debugging and logging functions
280Sstevel@tonic-gate  *
290Sstevel@tonic-gate  * Logging destinations
300Sstevel@tonic-gate  *   svc.startd(1M) supports three logging destinations:  the system log, a
310Sstevel@tonic-gate  *   daemon-specific log (in the /var/svc/log hierarchy by default), and to the
321958Slianep  *   standard output (redirected to the /var/svc/log/svc.startd.log file by
331958Slianep  *   default).  Any or all of these destinations may be used to
340Sstevel@tonic-gate  *   communicate a specific message; the audiences for each destination differ.
350Sstevel@tonic-gate  *
360Sstevel@tonic-gate  *   Generic messages associated with svc.startd(1M) are made by the
370Sstevel@tonic-gate  *   log_framework() and log_error() functions.  For these messages, svc.startd
380Sstevel@tonic-gate  *   logs under its own name and under the LOG_DAEMON facility when issuing
390Sstevel@tonic-gate  *   events to the system log.  By design, severities below LOG_NOTICE are never
400Sstevel@tonic-gate  *   issued to the system log.
410Sstevel@tonic-gate  *
420Sstevel@tonic-gate  *   Messages associated with a specific service instance are logged using the
430Sstevel@tonic-gate  *   log_instance() or log_instance_fmri() functions.  These messages are always
440Sstevel@tonic-gate  *   sent to the appropriate per-instance log file.
450Sstevel@tonic-gate  *
460Sstevel@tonic-gate  *   In the case of verbose or debug boot, the log_transition() function
470Sstevel@tonic-gate  *   displays messages regarding instance transitions to the system console,
480Sstevel@tonic-gate  *   until the expected login services are available.
490Sstevel@tonic-gate  *
500Sstevel@tonic-gate  *   Finally, log_console() displays messages to the system consoles and
510Sstevel@tonic-gate  *   the master restarter log file.  This is used when booting to a milestone
520Sstevel@tonic-gate  *   other than 'all'.
530Sstevel@tonic-gate  *
540Sstevel@tonic-gate  * Logging detail
550Sstevel@tonic-gate  *   The constants for severity from <syslog.h> are reused, with a specific
560Sstevel@tonic-gate  *   convention here.  (It is worth noting that the #define values for the LOG_
570Sstevel@tonic-gate  *   levels are such that more important severities have lower values.)  The
580Sstevel@tonic-gate  *   severity determines the importance of the event, and its addressibility by
590Sstevel@tonic-gate  *   the administrator.  Each severity level's use is defined below, along with
600Sstevel@tonic-gate  *   an illustrative example.
610Sstevel@tonic-gate  *
620Sstevel@tonic-gate  *   LOG_EMERG		Not used presently.
630Sstevel@tonic-gate  *
640Sstevel@tonic-gate  *   LOG_ALERT		An unrecoverable operation requiring external
650Sstevel@tonic-gate  *			intervention has occurred.   Includes an inability to
660Sstevel@tonic-gate  *			write to the smf(5) repository (due to svc.configd(1M)
670Sstevel@tonic-gate  *			absence, due to permissions failures, etc.).  Message
680Sstevel@tonic-gate  *			should identify component at fault.
690Sstevel@tonic-gate  *
700Sstevel@tonic-gate  *   LOG_CRIT		An unrecoverable operation internal to svc.startd(1M)
710Sstevel@tonic-gate  *			has occurred.  Failure should be recoverable by restart
720Sstevel@tonic-gate  *			of svc.startd(1M).
730Sstevel@tonic-gate  *
740Sstevel@tonic-gate  *   LOG_ERR		An smf(5) event requiring administrative intervention
750Sstevel@tonic-gate  *			has occurred.  Includes instance being moved to the
760Sstevel@tonic-gate  *			maintenance state.
770Sstevel@tonic-gate  *
780Sstevel@tonic-gate  *   LOG_WARNING	A potentially destabilizing smf(5) event not requiring
790Sstevel@tonic-gate  *			administrative intervention has occurred.
800Sstevel@tonic-gate  *
810Sstevel@tonic-gate  *   LOG_NOTICE		A noteworthy smf(5) event has occurred.  Includes
820Sstevel@tonic-gate  *			individual instance failures.
830Sstevel@tonic-gate  *
840Sstevel@tonic-gate  *   LOG_INFO		A noteworthy operation internal to svc.startd(1M) has
850Sstevel@tonic-gate  *			occurred.  Includes recoverable failures or otherwise
860Sstevel@tonic-gate  *			unexpected outcomes.
870Sstevel@tonic-gate  *
880Sstevel@tonic-gate  *   LOG_DEBUG		An internal operation only of interest to a
890Sstevel@tonic-gate  *			svc.startd(1M) developer has occurred.
900Sstevel@tonic-gate  *
910Sstevel@tonic-gate  *  Logging configuration
922258Srm88369  *    The preferred approach is to set the logging property values
930Sstevel@tonic-gate  *    in the options property group of the svc.startd default instance.  The
940Sstevel@tonic-gate  *    valid values are "quiet", "verbose", and "debug".  "quiet" is the default;
950Sstevel@tonic-gate  *    "verbose" and "debug" allow LOG_INFO and LOG_DEBUG logging requests to
962258Srm88369  *    reach the svc.startd.log file, respectively.
970Sstevel@tonic-gate  */
980Sstevel@tonic-gate 
990Sstevel@tonic-gate #include <sys/stat.h>
1000Sstevel@tonic-gate #include <sys/statvfs.h>
1010Sstevel@tonic-gate #include <sys/time.h>
1020Sstevel@tonic-gate #include <sys/types.h>
1030Sstevel@tonic-gate #include <assert.h>
1040Sstevel@tonic-gate #include <errno.h>
1050Sstevel@tonic-gate #include <fcntl.h>
1060Sstevel@tonic-gate #include <kstat.h>
1070Sstevel@tonic-gate #include <libgen.h>
1080Sstevel@tonic-gate #include <libintl.h>
1090Sstevel@tonic-gate #include <libuutil.h>
1100Sstevel@tonic-gate #include <locale.h>
1110Sstevel@tonic-gate #include <malloc.h>
1120Sstevel@tonic-gate #include <pthread.h>
1130Sstevel@tonic-gate #include <stdarg.h>
1140Sstevel@tonic-gate #include <stdio.h>
1150Sstevel@tonic-gate #include <strings.h>
1160Sstevel@tonic-gate #include <syslog.h>
1170Sstevel@tonic-gate #include <unistd.h>
1180Sstevel@tonic-gate #include <zone.h>
1190Sstevel@tonic-gate 
1200Sstevel@tonic-gate #include "startd.h"
1210Sstevel@tonic-gate 
1220Sstevel@tonic-gate 
1230Sstevel@tonic-gate #define	LOGBUF_SZ	(60 * 80)			/* 60 lines */
1240Sstevel@tonic-gate 
1250Sstevel@tonic-gate static FILE *logfile = NULL;
1260Sstevel@tonic-gate 
1275680Srm88369 /*
1285680Srm88369  * This parameter can be modified using mdb to turn on & off extended
1295680Srm88369  * internal debug logging. Although a performance loss can be expected.
1305680Srm88369  */
1315680Srm88369 static int internal_debug_flags = 0x0;
1325680Srm88369 
1330Sstevel@tonic-gate #ifndef NDEBUG
1340Sstevel@tonic-gate /*
1350Sstevel@tonic-gate  * This is a circular buffer for all (even those not emitted externally)
1360Sstevel@tonic-gate  * logging messages.  To read it properly you should start after the first
1370Sstevel@tonic-gate  * null, go until the second, and then go back to the beginning until the
1380Sstevel@tonic-gate  * first null.  Or use ::startd_log in mdb.
1390Sstevel@tonic-gate  */
1400Sstevel@tonic-gate static char logbuf[LOGBUF_SZ] = "";
1410Sstevel@tonic-gate static pthread_mutex_t logbuf_mutex = PTHREAD_MUTEX_INITIALIZER;
1420Sstevel@tonic-gate #endif
1430Sstevel@tonic-gate 
1440Sstevel@tonic-gate static void
xstrftime_poststart(char * buf,size_t bufsize,struct timeval * time)1450Sstevel@tonic-gate xstrftime_poststart(char *buf, size_t bufsize, struct timeval *time)
1460Sstevel@tonic-gate {
1470Sstevel@tonic-gate 	long sec, usec;
1480Sstevel@tonic-gate 
1490Sstevel@tonic-gate 	sec = time->tv_sec - st->st_start_time.tv_sec;
1500Sstevel@tonic-gate 	usec = time->tv_usec - st->st_start_time.tv_usec;
1510Sstevel@tonic-gate 
1520Sstevel@tonic-gate 	if (usec < 0) {
1530Sstevel@tonic-gate 		sec -= 1;
1540Sstevel@tonic-gate 		usec += 1000000;
1550Sstevel@tonic-gate 	}
1560Sstevel@tonic-gate 
1570Sstevel@tonic-gate 	(void) snprintf(buf, bufsize, "start + %d.%02ds", sec, usec / 10000);
1580Sstevel@tonic-gate }
1590Sstevel@tonic-gate 
1600Sstevel@tonic-gate static void
vlog_prefix(int severity,const char * prefix,const char * format,va_list args)1610Sstevel@tonic-gate vlog_prefix(int severity, const char *prefix, const char *format, va_list args)
1620Sstevel@tonic-gate {
1630Sstevel@tonic-gate 	char buf[512], *cp;
1640Sstevel@tonic-gate 	char timebuf[LOG_DATE_SIZE];
1650Sstevel@tonic-gate 	struct timeval now;
1660Sstevel@tonic-gate 	struct tm ltime;
1670Sstevel@tonic-gate 
1680Sstevel@tonic-gate #ifdef NDEBUG
1690Sstevel@tonic-gate 	if (severity > st->st_log_level_min)
1700Sstevel@tonic-gate 		return;
1710Sstevel@tonic-gate #endif
1720Sstevel@tonic-gate 
1730Sstevel@tonic-gate 	if (gettimeofday(&now, NULL) != 0)
1740Sstevel@tonic-gate 		(void) fprintf(stderr, "gettimeofday(3C) failed: %s\n",
1750Sstevel@tonic-gate 		    strerror(errno));
1760Sstevel@tonic-gate 
1770Sstevel@tonic-gate 	if (st->st_log_timezone_known)
1780Sstevel@tonic-gate 		(void) strftime(timebuf, sizeof (timebuf), "%b %e %T",
1790Sstevel@tonic-gate 		    localtime_r(&now.tv_sec, &ltime));
1800Sstevel@tonic-gate 	else
1810Sstevel@tonic-gate 		xstrftime_poststart(timebuf, sizeof (timebuf), &now);
1820Sstevel@tonic-gate 
1830Sstevel@tonic-gate 	(void) snprintf(buf, sizeof (buf), "%s/%d%s: ", timebuf, pthread_self(),
1840Sstevel@tonic-gate 	    prefix);
1850Sstevel@tonic-gate 	cp = strchr(buf, '\0');
1860Sstevel@tonic-gate 	(void) vsnprintf(cp, sizeof (buf) - (cp - buf), format, args);
1870Sstevel@tonic-gate 
1880Sstevel@tonic-gate #ifndef NDEBUG
1890Sstevel@tonic-gate 	/* Copy into logbuf. */
1900Sstevel@tonic-gate 	(void) pthread_mutex_lock(&logbuf_mutex);
1910Sstevel@tonic-gate 	if (strlen(logbuf) + strlen(buf) + 1 <= sizeof (logbuf))
1920Sstevel@tonic-gate 		(void) strcat(logbuf, buf);
1930Sstevel@tonic-gate 	else
1940Sstevel@tonic-gate 		(void) strlcpy(logbuf, buf, sizeof (logbuf));
1950Sstevel@tonic-gate 	(void) pthread_mutex_unlock(&logbuf_mutex);
1960Sstevel@tonic-gate 
1970Sstevel@tonic-gate 	if (severity > st->st_log_level_min)
1980Sstevel@tonic-gate 		return;
1990Sstevel@tonic-gate #endif
2000Sstevel@tonic-gate 
2011958Slianep 	if (st->st_log_flags & STARTD_LOG_FILE && logfile) {
2020Sstevel@tonic-gate 		(void) fputs(buf, logfile);
2031958Slianep 		(void) fflush(logfile);
2041958Slianep 	}
2050Sstevel@tonic-gate 	if (st->st_log_flags & STARTD_LOG_TERMINAL)
2060Sstevel@tonic-gate 		(void) fputs(buf, stdout);
2071958Slianep 	if (st->st_log_flags & STARTD_LOG_SYSLOG && st->st_log_timezone_known)
2080Sstevel@tonic-gate 		vsyslog(severity, format, args);
2090Sstevel@tonic-gate }
2100Sstevel@tonic-gate 
2110Sstevel@tonic-gate /*PRINTFLIKE2*/
2120Sstevel@tonic-gate void
log_error(int severity,const char * format,...)2130Sstevel@tonic-gate log_error(int severity, const char *format, ...)
2140Sstevel@tonic-gate {
2150Sstevel@tonic-gate 	va_list args;
2160Sstevel@tonic-gate 
2170Sstevel@tonic-gate 	va_start(args, format);
2180Sstevel@tonic-gate 	vlog_prefix(severity, " ERROR", format, args);
2190Sstevel@tonic-gate 	va_end(args);
2200Sstevel@tonic-gate }
2210Sstevel@tonic-gate 
2220Sstevel@tonic-gate /*PRINTFLIKE2*/
2230Sstevel@tonic-gate void
log_framework(int severity,const char * format,...)2240Sstevel@tonic-gate log_framework(int severity, const char *format, ...)
2250Sstevel@tonic-gate {
2260Sstevel@tonic-gate 	va_list args;
2270Sstevel@tonic-gate 
2280Sstevel@tonic-gate 	va_start(args, format);
2290Sstevel@tonic-gate 	vlog_prefix(severity, "", format, args);
2300Sstevel@tonic-gate 	va_end(args);
2310Sstevel@tonic-gate }
2320Sstevel@tonic-gate 
2330Sstevel@tonic-gate /*
2345680Srm88369  * log_framework2() differs from log_framework() by the fact that
2355680Srm88369  * some checking are done before logging the messages in the internal
2365680Srm88369  * buffer for performance reasons.
2375680Srm88369  * The messages aren't logged if:
2385680Srm88369  * - severity >= LOG_DEBUG and
2395680Srm88369  * - st_log_level_min < LOG_DEBUG and
2405680Srm88369  * - internal_debug_flags is not set for 'flags'
2415680Srm88369  */
2425680Srm88369 void
log_framework2(int severity,int flags,const char * format,...)2435680Srm88369 log_framework2(int severity, int flags, const char *format, ...)
2445680Srm88369 {
2455680Srm88369 	va_list args;
2465680Srm88369 
2475680Srm88369 	if ((severity < LOG_DEBUG) ||
2485680Srm88369 	    (internal_debug_flags & flags) ||
2495680Srm88369 	    st->st_log_level_min >= LOG_DEBUG) {
2505680Srm88369 		va_start(args, format);
2515680Srm88369 		vlog_prefix(severity, "", format, args);
2525680Srm88369 		va_end(args);
2535680Srm88369 	}
2545680Srm88369 }
2555680Srm88369 
2565680Srm88369 /*
2570Sstevel@tonic-gate  * void log_preexec()
2580Sstevel@tonic-gate  *
2590Sstevel@tonic-gate  * log_preexec() should be invoked prior to any exec(2) calls, to prevent the
2600Sstevel@tonic-gate  * logfile and syslogd file descriptors from being leaked to child processes.
2610Sstevel@tonic-gate  * Why openlog(3C) lacks a close-on-exec option is a minor mystery.
2620Sstevel@tonic-gate  */
2630Sstevel@tonic-gate void
log_preexec()2640Sstevel@tonic-gate log_preexec()
2650Sstevel@tonic-gate {
2660Sstevel@tonic-gate 	closelog();
2670Sstevel@tonic-gate }
2680Sstevel@tonic-gate 
2690Sstevel@tonic-gate /*
2700Sstevel@tonic-gate  * void setlog()
2710Sstevel@tonic-gate  *   Close file descriptors and redirect output.
2720Sstevel@tonic-gate  */
2730Sstevel@tonic-gate void
setlog(const char * logstem)2740Sstevel@tonic-gate setlog(const char *logstem)
2750Sstevel@tonic-gate {
2760Sstevel@tonic-gate 	int fd;
2770Sstevel@tonic-gate 	char logfile[PATH_MAX];
2780Sstevel@tonic-gate 
2790Sstevel@tonic-gate 	closefrom(0);
2800Sstevel@tonic-gate 
2810Sstevel@tonic-gate 	(void) open("/dev/null", O_RDONLY);
2820Sstevel@tonic-gate 
2830Sstevel@tonic-gate 	(void) snprintf(logfile, PATH_MAX, "%s/%s", st->st_log_prefix, logstem);
2840Sstevel@tonic-gate 
2850Sstevel@tonic-gate 	(void) umask(fmask);
2860Sstevel@tonic-gate 	fd = open(logfile, O_WRONLY|O_CREAT|O_APPEND,
2870Sstevel@tonic-gate 	    S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
2880Sstevel@tonic-gate 	(void) umask(dmask);
2890Sstevel@tonic-gate 
2900Sstevel@tonic-gate 	if (fd == -1)
2910Sstevel@tonic-gate 		return;
2920Sstevel@tonic-gate 
2935617Sacruz 	(void) dup2(fd, STDOUT_FILENO);
2945617Sacruz 	(void) dup2(fd, STDERR_FILENO);
2950Sstevel@tonic-gate 
2965617Sacruz 	if (fd != STDOUT_FILENO && fd != STDERR_FILENO)
2970Sstevel@tonic-gate 		startd_close(fd);
2980Sstevel@tonic-gate }
2990Sstevel@tonic-gate 
3000Sstevel@tonic-gate static int
log_dir_writeable(const char * path)3010Sstevel@tonic-gate log_dir_writeable(const char *path)
3020Sstevel@tonic-gate {
3030Sstevel@tonic-gate 	int fd;
3040Sstevel@tonic-gate 	struct statvfs svb;
3050Sstevel@tonic-gate 
3060Sstevel@tonic-gate 	if ((fd = open(path, O_RDONLY, 0644)) == -1)
3070Sstevel@tonic-gate 		return (-1);
3080Sstevel@tonic-gate 
3090Sstevel@tonic-gate 	if (fstatvfs(fd, &svb) == -1)
3100Sstevel@tonic-gate 		return (-1);
3110Sstevel@tonic-gate 
3120Sstevel@tonic-gate 	if (svb.f_flag & ST_RDONLY) {
3130Sstevel@tonic-gate 		(void) close(fd);
3140Sstevel@tonic-gate 
3150Sstevel@tonic-gate 		fd = -1;
3160Sstevel@tonic-gate 	}
3170Sstevel@tonic-gate 
3180Sstevel@tonic-gate 	return (fd);
3190Sstevel@tonic-gate }
3200Sstevel@tonic-gate 
3210Sstevel@tonic-gate static void
vlog_instance(const char * fmri,const char * logstem,boolean_t canlog,const char * format,va_list args)3220Sstevel@tonic-gate vlog_instance(const char *fmri, const char *logstem, boolean_t canlog,
3230Sstevel@tonic-gate     const char *format, va_list args)
3240Sstevel@tonic-gate {
3250Sstevel@tonic-gate 	char logfile[PATH_MAX];
3260Sstevel@tonic-gate 	char *message;
3270Sstevel@tonic-gate 	char omessage[1024];
3280Sstevel@tonic-gate 	int fd, err;
3290Sstevel@tonic-gate 	char timebuf[LOG_DATE_SIZE];
3300Sstevel@tonic-gate 	struct tm ltime;
3310Sstevel@tonic-gate 	struct timeval now;
3320Sstevel@tonic-gate 
3330Sstevel@tonic-gate 	(void) snprintf(logfile, PATH_MAX, "%s/%s", st->st_log_prefix,
3340Sstevel@tonic-gate 	    logstem);
3350Sstevel@tonic-gate 
3360Sstevel@tonic-gate 	(void) umask(fmask);
3370Sstevel@tonic-gate 	fd = open(logfile, O_WRONLY|O_CREAT|O_APPEND,
3380Sstevel@tonic-gate 	    S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
3390Sstevel@tonic-gate 	err = errno;
3400Sstevel@tonic-gate 	(void) umask(dmask);
3410Sstevel@tonic-gate 
3420Sstevel@tonic-gate 	if (fd == -1) {
3430Sstevel@tonic-gate 		if (canlog)
3440Sstevel@tonic-gate 			log_error(LOG_NOTICE, "Could not log for %s: open(%s) "
3450Sstevel@tonic-gate 			    "failed with %s.\n", fmri, logfile, strerror(err));
3460Sstevel@tonic-gate 
3470Sstevel@tonic-gate 		return;
3480Sstevel@tonic-gate 	}
3490Sstevel@tonic-gate 
3500Sstevel@tonic-gate 	(void) vsnprintf(omessage, sizeof (omessage), format, args);
3510Sstevel@tonic-gate 
3520Sstevel@tonic-gate 	if (gettimeofday(&now, NULL) != 0)
3530Sstevel@tonic-gate 		(void) fprintf(stderr, "gettimeofday(3C) failed: %s\n",
3540Sstevel@tonic-gate 		    strerror(errno));
3550Sstevel@tonic-gate 
3560Sstevel@tonic-gate 	if (st->st_log_timezone_known)
3570Sstevel@tonic-gate 		(void) strftime(timebuf, sizeof (timebuf), "%b %e %T",
3580Sstevel@tonic-gate 		    localtime_r(&now.tv_sec, &ltime));
3590Sstevel@tonic-gate 	else
3600Sstevel@tonic-gate 		xstrftime_poststart(timebuf, sizeof (timebuf), &now);
3610Sstevel@tonic-gate 
3620Sstevel@tonic-gate 	message = uu_msprintf("[ %s %s ]\n", timebuf, omessage);
3630Sstevel@tonic-gate 
3640Sstevel@tonic-gate 	if (message == NULL) {
3650Sstevel@tonic-gate 		if (canlog)
3660Sstevel@tonic-gate 			log_error(LOG_NOTICE, "Could not log for %s: %s.\n",
3670Sstevel@tonic-gate 			    fmri, uu_strerror(uu_error()));
3680Sstevel@tonic-gate 	} else {
3690Sstevel@tonic-gate 		if (write(fd, message, strlen(message)) < 0 && canlog)
3700Sstevel@tonic-gate 			log_error(LOG_NOTICE, "Could not log for %s: write(%d) "
3710Sstevel@tonic-gate 			    "failed with %s.\n", fmri, fd,
3720Sstevel@tonic-gate 			    strerror(errno));
3730Sstevel@tonic-gate 
3740Sstevel@tonic-gate 		uu_free(message);
3750Sstevel@tonic-gate 	}
3760Sstevel@tonic-gate 
3770Sstevel@tonic-gate 	if (close(fd) != 0 && canlog)
3780Sstevel@tonic-gate 		log_framework(LOG_NOTICE, "close(%d) failed: %s.\n", fd,
3790Sstevel@tonic-gate 		    strerror(errno));
3800Sstevel@tonic-gate }
3810Sstevel@tonic-gate 
3820Sstevel@tonic-gate /*
3830Sstevel@tonic-gate  * void log_instance(const restarter_inst_t *, boolean_t, const char *, ...)
3840Sstevel@tonic-gate  *
3850Sstevel@tonic-gate  * The log_instance() format is "[ month day time message ]".  (The
3860Sstevel@tonic-gate  * brackets distinguish svc.startd messages from method output.)  We avoid
3870Sstevel@tonic-gate  * calling log_*() functions on error when canlog is not set, since we may
3880Sstevel@tonic-gate  * be called from a child process.
3890Sstevel@tonic-gate  *
3900Sstevel@tonic-gate  * When adding new calls to this function, consider: If this is called before
3910Sstevel@tonic-gate  * any instances have started, then it should be called with canlog clear,
3920Sstevel@tonic-gate  * lest we spew errors to the console when booted on the miniroot.
3930Sstevel@tonic-gate  */
3940Sstevel@tonic-gate /*PRINTFLIKE3*/
3950Sstevel@tonic-gate void
log_instance(const restarter_inst_t * inst,boolean_t canlog,const char * format,...)3960Sstevel@tonic-gate log_instance(const restarter_inst_t *inst, boolean_t canlog,
3970Sstevel@tonic-gate     const char *format, ...)
3980Sstevel@tonic-gate {
3990Sstevel@tonic-gate 	va_list args;
4000Sstevel@tonic-gate 
4010Sstevel@tonic-gate 	va_start(args, format);
4020Sstevel@tonic-gate 	vlog_instance(inst->ri_i.i_fmri, inst->ri_logstem, canlog, format,
4030Sstevel@tonic-gate 	    args);
4040Sstevel@tonic-gate 	va_end(args);
4050Sstevel@tonic-gate }
4060Sstevel@tonic-gate 
4070Sstevel@tonic-gate /*
4080Sstevel@tonic-gate  * void log_instance_fmri(const char *, const char *,boolean_t, const char *,
4090Sstevel@tonic-gate  *    ...)
4100Sstevel@tonic-gate  *
4110Sstevel@tonic-gate  * The log_instance_fmri() format is "[ month day time message ]".  (The
4120Sstevel@tonic-gate  * brackets distinguish svc.startd messages from method output.)  We avoid
4130Sstevel@tonic-gate  * calling log_*() functions on error when canlog is not set, since we may
4140Sstevel@tonic-gate  * be called from a child process.
4150Sstevel@tonic-gate  *
4160Sstevel@tonic-gate  * For new calls to this function, see the warning in log_instance()'s
4170Sstevel@tonic-gate  * comment.
4180Sstevel@tonic-gate  */
4190Sstevel@tonic-gate /*PRINTFLIKE4*/
4200Sstevel@tonic-gate void
log_instance_fmri(const char * fmri,const char * logstem,boolean_t canlog,const char * format,...)4210Sstevel@tonic-gate log_instance_fmri(const char *fmri, const char *logstem, boolean_t canlog,
4220Sstevel@tonic-gate     const char *format, ...)
4230Sstevel@tonic-gate {
4240Sstevel@tonic-gate 	va_list args;
4250Sstevel@tonic-gate 
4260Sstevel@tonic-gate 	va_start(args, format);
4270Sstevel@tonic-gate 	vlog_instance(fmri, logstem, canlog, format, args);
4280Sstevel@tonic-gate 	va_end(args);
4290Sstevel@tonic-gate }
4300Sstevel@tonic-gate 
4310Sstevel@tonic-gate /*
4320Sstevel@tonic-gate  * void log_transition(const restarter_inst_t *, start_outcome_t)
4330Sstevel@tonic-gate  *
4340Sstevel@tonic-gate  * The log_transition() format is
4350Sstevel@tonic-gate  *
4360Sstevel@tonic-gate  *   [ _service_fmri_ _participle_ (_common_name_) ]
4370Sstevel@tonic-gate  *
4380Sstevel@tonic-gate  * Again, brackets separate messages from specific service instance output to
4390Sstevel@tonic-gate  * the console.
4400Sstevel@tonic-gate  */
4410Sstevel@tonic-gate void
log_transition(const restarter_inst_t * inst,start_outcome_t outcome)4420Sstevel@tonic-gate log_transition(const restarter_inst_t *inst, start_outcome_t outcome)
4430Sstevel@tonic-gate {
4440Sstevel@tonic-gate 	char *message;
4450Sstevel@tonic-gate 	char omessage[1024];
4460Sstevel@tonic-gate 	char *action;
4470Sstevel@tonic-gate 	int severity;
4480Sstevel@tonic-gate 
4490Sstevel@tonic-gate 	if (outcome == START_REQUESTED) {
4500Sstevel@tonic-gate 		char *cname = NULL;
4510Sstevel@tonic-gate 
4520Sstevel@tonic-gate 		cname = inst->ri_common_name;
4530Sstevel@tonic-gate 		if (cname == NULL)
4540Sstevel@tonic-gate 			cname = inst->ri_C_common_name;
4550Sstevel@tonic-gate 
4560Sstevel@tonic-gate 		if (!(st->st_boot_flags & STARTD_BOOT_VERBOSE))
4570Sstevel@tonic-gate 			return;
4580Sstevel@tonic-gate 
4590Sstevel@tonic-gate 		if (inst->ri_start_index > 1)
4600Sstevel@tonic-gate 			return;
4610Sstevel@tonic-gate 
4620Sstevel@tonic-gate 		if (cname)
4630Sstevel@tonic-gate 			(void) snprintf(omessage, sizeof (omessage), " (%s)",
4645617Sacruz 			    cname);
4650Sstevel@tonic-gate 		else
4660Sstevel@tonic-gate 			*omessage = '\0';
4670Sstevel@tonic-gate 
4680Sstevel@tonic-gate 		action = gettext("starting");
4690Sstevel@tonic-gate 
4700Sstevel@tonic-gate 		message = uu_msprintf("[ %s %s%s ]\n",
4710Sstevel@tonic-gate 		    inst->ri_i.i_fmri + strlen("svc:/"), action,
4720Sstevel@tonic-gate 		    omessage);
4730Sstevel@tonic-gate 
4740Sstevel@tonic-gate 		severity = LOG_INFO;
4750Sstevel@tonic-gate 	} else {
4760Sstevel@tonic-gate 		switch (outcome) {
4771958Slianep 		case MAINT_REQUESTED:
4781958Slianep 			action = gettext("transitioned to maintenance by "
4791958Slianep 			    "request (see 'svcs -xv' for details)");
4801958Slianep 			break;
4810Sstevel@tonic-gate 		case START_FAILED_REPEATEDLY:
4821958Slianep 			action = gettext("failed repeatedly: transitioned to "
4831958Slianep 			    "maintenance (see 'svcs -xv' for details)");
4840Sstevel@tonic-gate 			break;
4850Sstevel@tonic-gate 		case START_FAILED_CONFIGURATION:
4861958Slianep 			action = gettext("misconfigured: transitioned to "
4871958Slianep 			    "maintenance (see 'svcs -xv' for details)");
4880Sstevel@tonic-gate 			break;
4890Sstevel@tonic-gate 		case START_FAILED_FATAL:
4901958Slianep 			action = gettext("failed fatally: transitioned to "
4911958Slianep 			    "maintenance (see 'svcs -xv' for details)");
4920Sstevel@tonic-gate 			break;
4930Sstevel@tonic-gate 		case START_FAILED_TIMEOUT_FATAL:
4941958Slianep 			action = gettext("timed out: transitioned to "
4951958Slianep 			    "maintenance (see 'svcs -xv' for details)");
4960Sstevel@tonic-gate 			break;
4970Sstevel@tonic-gate 		case START_FAILED_OTHER:
4981958Slianep 			action = gettext("failed: transitioned to "
4991958Slianep 			    "maintenance (see 'svcs -xv' for details)");
5000Sstevel@tonic-gate 			break;
5010Sstevel@tonic-gate 		case START_REQUESTED:
5020Sstevel@tonic-gate 			assert(outcome != START_REQUESTED);
5030Sstevel@tonic-gate 			/*FALLTHROUGH*/
5040Sstevel@tonic-gate 		default:
5050Sstevel@tonic-gate 			action = gettext("outcome unknown?");
5060Sstevel@tonic-gate 		}
5070Sstevel@tonic-gate 
5081958Slianep 		message = uu_msprintf("[ %s %s ]\n",
5091958Slianep 		    inst->ri_i.i_fmri + strlen("svc:/"), action);
5100Sstevel@tonic-gate 
5110Sstevel@tonic-gate 		severity = LOG_ERR;
5120Sstevel@tonic-gate 	}
5130Sstevel@tonic-gate 
5140Sstevel@tonic-gate 
5150Sstevel@tonic-gate 	if (message == NULL) {
5160Sstevel@tonic-gate 		log_error(LOG_NOTICE,
5170Sstevel@tonic-gate 		    "Could not log boot message for %s: %s.\n",
5180Sstevel@tonic-gate 		    inst->ri_i.i_fmri, uu_strerror(uu_error()));
5190Sstevel@tonic-gate 	} else {
5201958Slianep 		/*
5211958Slianep 		 * All significant errors should to go to syslog to
5221958Slianep 		 * communicate appropriate information even for systems
5231958Slianep 		 * without a console connected during boot.  Send the
5241958Slianep 		 * message to stderr only if the severity is lower than
5251958Slianep 		 * (indicated by >) LOG_ERR.
5261958Slianep 		 */
5271958Slianep 		if (!st->st_log_login_reached && severity > LOG_ERR) {
5280Sstevel@tonic-gate 			/*LINTED*/
5290Sstevel@tonic-gate 			if (fprintf(stderr, message) < 0)
5300Sstevel@tonic-gate 				log_error(LOG_NOTICE, "Could not log for %s: "
5310Sstevel@tonic-gate 				    "fprintf() failed with %s.\n",
5320Sstevel@tonic-gate 				    inst->ri_i.i_fmri, strerror(errno));
5330Sstevel@tonic-gate 		} else {
5340Sstevel@tonic-gate 			log_framework(severity, "%s %s\n",
5350Sstevel@tonic-gate 			    inst->ri_i.i_fmri + strlen("svc:/"), action);
5360Sstevel@tonic-gate 		}
5370Sstevel@tonic-gate 
5380Sstevel@tonic-gate 		uu_free(message);
5390Sstevel@tonic-gate 	}
5400Sstevel@tonic-gate }
5410Sstevel@tonic-gate 
5420Sstevel@tonic-gate /*
5430Sstevel@tonic-gate  * log_console - log a message to the consoles and to syslog
5440Sstevel@tonic-gate  *
5450Sstevel@tonic-gate  * This logs a message as-is to the console (and auxiliary consoles),
5460Sstevel@tonic-gate  * as well as to the master restarter log.
5470Sstevel@tonic-gate  */
5480Sstevel@tonic-gate /*PRINTFLIKE2*/
5490Sstevel@tonic-gate void
log_console(int severity,const char * format,...)5500Sstevel@tonic-gate log_console(int severity, const char *format, ...)
5510Sstevel@tonic-gate {
5520Sstevel@tonic-gate 	va_list args;
5530Sstevel@tonic-gate 
5540Sstevel@tonic-gate 	va_start(args, format);
5550Sstevel@tonic-gate 	vlog_prefix(severity, "", format, args);
5560Sstevel@tonic-gate 	va_end(args);
5570Sstevel@tonic-gate 
5580Sstevel@tonic-gate 	va_start(args, format);
5590Sstevel@tonic-gate 	(void) vfprintf(stderr, format, args);
5600Sstevel@tonic-gate 	va_end(args);
5610Sstevel@tonic-gate }
5620Sstevel@tonic-gate 
5630Sstevel@tonic-gate /*
5640Sstevel@tonic-gate  * void log_init()
5650Sstevel@tonic-gate  *
5660Sstevel@tonic-gate  * Set up the log files, if necessary, for the current invocation.  This
5670Sstevel@tonic-gate  * function should be called before any other functions in this file.  Set the
5680Sstevel@tonic-gate  * syslog(3C) logging mask such that severities of the importance of
5690Sstevel@tonic-gate  * LOG_NOTICE and above are passed through, but lower severity messages are
5700Sstevel@tonic-gate  * masked out.
5710Sstevel@tonic-gate  *
5720Sstevel@tonic-gate  * It may be called multiple times to change the logging configuration due to
5730Sstevel@tonic-gate  * administrative request.
5740Sstevel@tonic-gate  */
5750Sstevel@tonic-gate void
log_init()5760Sstevel@tonic-gate log_init()
5770Sstevel@tonic-gate {
5780Sstevel@tonic-gate 	int dirfd, logfd;
5790Sstevel@tonic-gate 	char *dir;
5800Sstevel@tonic-gate 	struct stat sb;
5810Sstevel@tonic-gate 
5820Sstevel@tonic-gate 	if (st->st_start_time.tv_sec == 0) {
5830Sstevel@tonic-gate 		if (getzoneid() != GLOBAL_ZONEID) {
5840Sstevel@tonic-gate 			st->st_start_time.tv_sec = time(NULL);
5850Sstevel@tonic-gate 		} else {
5860Sstevel@tonic-gate 			/*
5870Sstevel@tonic-gate 			 * We need to special-case the BOOT_TIME utmp entry, and
5880Sstevel@tonic-gate 			 * drag that value out of the kernel if it's there.
5890Sstevel@tonic-gate 			 */
5900Sstevel@tonic-gate 			kstat_ctl_t *kc;
5910Sstevel@tonic-gate 			kstat_t *ks;
5920Sstevel@tonic-gate 			kstat_named_t *boot;
5930Sstevel@tonic-gate 
5940Sstevel@tonic-gate 			if (((kc = kstat_open()) != 0) &&
5950Sstevel@tonic-gate 			    ((ks = kstat_lookup(kc, "unix", 0, "system_misc"))
5960Sstevel@tonic-gate 			    != NULL) &&
5970Sstevel@tonic-gate 			    (kstat_read(kc, ks, NULL) != -1) &&
5980Sstevel@tonic-gate 			    ((boot = kstat_data_lookup(ks, "boot_time")) !=
5990Sstevel@tonic-gate 			    NULL)) {
6000Sstevel@tonic-gate 				/*
6010Sstevel@tonic-gate 				 * If we're here, then we've successfully found
6020Sstevel@tonic-gate 				 * the boot_time kstat... use its value.
6030Sstevel@tonic-gate 				 */
6040Sstevel@tonic-gate 				st->st_start_time.tv_sec = boot->value.ul;
6050Sstevel@tonic-gate 			} else {
6060Sstevel@tonic-gate 				st->st_start_time.tv_sec = time(NULL);
6070Sstevel@tonic-gate 			}
6080Sstevel@tonic-gate 
6090Sstevel@tonic-gate 			if (kc)
6100Sstevel@tonic-gate 				(void) kstat_close(kc);
6110Sstevel@tonic-gate 		}
6120Sstevel@tonic-gate 	}
6130Sstevel@tonic-gate 
6140Sstevel@tonic-gate 	/*
6150Sstevel@tonic-gate 	 * Establish our timezone if the appropriate directory is available.
6160Sstevel@tonic-gate 	 */
6170Sstevel@tonic-gate 	if (!st->st_log_timezone_known && stat(FS_TIMEZONE_DIR, &sb) == 0) {
6180Sstevel@tonic-gate 		tzset();
6190Sstevel@tonic-gate 		st->st_log_timezone_known = 1;
6200Sstevel@tonic-gate 	}
6210Sstevel@tonic-gate 
6220Sstevel@tonic-gate 	/*
6230Sstevel@tonic-gate 	 * Establish our locale if the appropriate directory is available.  Set
6240Sstevel@tonic-gate 	 * the locale string from the environment so we can extract template
6250Sstevel@tonic-gate 	 * information correctly, if the locale directories aren't yet
6260Sstevel@tonic-gate 	 * available.
6270Sstevel@tonic-gate 	 */
6280Sstevel@tonic-gate 	if (st->st_locale != NULL)
6290Sstevel@tonic-gate 		free(st->st_locale);
6300Sstevel@tonic-gate 
6310Sstevel@tonic-gate 	if ((st->st_locale = getenv("LC_ALL")) == NULL)
6320Sstevel@tonic-gate 		if ((st->st_locale = getenv("LC_MESSAGES")) == NULL)
6330Sstevel@tonic-gate 			st->st_locale = getenv("LANG");
6340Sstevel@tonic-gate 
6350Sstevel@tonic-gate 	if (!st->st_log_locale_known && stat(FS_LOCALE_DIR, &sb) == 0) {
6360Sstevel@tonic-gate 		(void) setlocale(LC_ALL, "");
6370Sstevel@tonic-gate 		st->st_locale = setlocale(LC_MESSAGES, "");
6380Sstevel@tonic-gate 		if (st->st_locale)
6390Sstevel@tonic-gate 			st->st_log_locale_known = 1;
6400Sstevel@tonic-gate 
6410Sstevel@tonic-gate 		(void) textdomain(TEXT_DOMAIN);
6420Sstevel@tonic-gate 	}
6430Sstevel@tonic-gate 
6440Sstevel@tonic-gate 	if (st->st_locale) {
6450Sstevel@tonic-gate 		st->st_locale = safe_strdup(st->st_locale);
6460Sstevel@tonic-gate 		xstr_sanitize(st->st_locale);
6470Sstevel@tonic-gate 	}
6480Sstevel@tonic-gate 
6490Sstevel@tonic-gate 	if (logfile) {
6500Sstevel@tonic-gate 		(void) fclose(logfile);
6510Sstevel@tonic-gate 		logfile = NULL;
6520Sstevel@tonic-gate 	}
6530Sstevel@tonic-gate 
6540Sstevel@tonic-gate 	/*
6550Sstevel@tonic-gate 	 * Set syslog(3C) behaviour in all cases.
6560Sstevel@tonic-gate 	 */
6570Sstevel@tonic-gate 	closelog();
6580Sstevel@tonic-gate 	openlog("svc.startd", LOG_PID | LOG_CONS, LOG_DAEMON);
6590Sstevel@tonic-gate 	(void) setlogmask(LOG_UPTO(LOG_NOTICE));
6600Sstevel@tonic-gate 
6610Sstevel@tonic-gate 	if ((dirfd = log_dir_writeable(LOG_PREFIX_NORMAL)) == -1) {
6620Sstevel@tonic-gate 		if ((dirfd = log_dir_writeable(LOG_PREFIX_EARLY)) == -1)
6630Sstevel@tonic-gate 			return;
6640Sstevel@tonic-gate 		else
6650Sstevel@tonic-gate 			dir = LOG_PREFIX_EARLY;
6660Sstevel@tonic-gate 	} else {
6670Sstevel@tonic-gate 		dir = LOG_PREFIX_NORMAL;
6680Sstevel@tonic-gate 	}
6690Sstevel@tonic-gate 
6700Sstevel@tonic-gate 	st->st_log_prefix = dir;
6710Sstevel@tonic-gate 
6720Sstevel@tonic-gate 	(void) umask(fmask);
673*11829SPetr.Sumbera@Sun.COM 	if ((logfd = openat(dirfd, STARTD_DEFAULT_LOG,
674*11829SPetr.Sumbera@Sun.COM 	    O_CREAT | O_RDWR | O_APPEND, 0644)) == -1) {
6750Sstevel@tonic-gate 		(void) close(dirfd);
6760Sstevel@tonic-gate 		(void) umask(dmask);
6770Sstevel@tonic-gate 		return;
6780Sstevel@tonic-gate 	}
6790Sstevel@tonic-gate 
6800Sstevel@tonic-gate 	(void) close(dirfd);
6810Sstevel@tonic-gate 	(void) umask(dmask);
6820Sstevel@tonic-gate 
6830Sstevel@tonic-gate 	if ((logfile = fdopen(logfd, "a")) == NULL)
6840Sstevel@tonic-gate 		if (errno != EROFS)
6850Sstevel@tonic-gate 			log_error(LOG_WARNING, "can't open logfile %s/%s",
6860Sstevel@tonic-gate 			    dir, STARTD_DEFAULT_LOG);
6870Sstevel@tonic-gate 
6880Sstevel@tonic-gate 	if (logfile &&
6890Sstevel@tonic-gate 	    fcntl(fileno(logfile), F_SETFD, FD_CLOEXEC) == -1)
6900Sstevel@tonic-gate 		log_error(LOG_WARNING,
6910Sstevel@tonic-gate 		    "couldn't mark logfile close-on-exec: %s\n",
6920Sstevel@tonic-gate 		    strerror(errno));
6930Sstevel@tonic-gate }
694