17c478bd9Sstevel@tonic-gate /* 27c478bd9Sstevel@tonic-gate * CDDL HEADER START 37c478bd9Sstevel@tonic-gate * 47c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5*99b44c3bSlianep * Common Development and Distribution License (the "License"). 6*99b44c3bSlianep * You may not use this file except in compliance with the License. 77c478bd9Sstevel@tonic-gate * 87c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 97c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 107c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions 117c478bd9Sstevel@tonic-gate * and limitations under the License. 127c478bd9Sstevel@tonic-gate * 137c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 147c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 157c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 167c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 177c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 187c478bd9Sstevel@tonic-gate * 197c478bd9Sstevel@tonic-gate * CDDL HEADER END 207c478bd9Sstevel@tonic-gate */ 217c478bd9Sstevel@tonic-gate /* 22*99b44c3bSlianep * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 237c478bd9Sstevel@tonic-gate * Use is subject to license terms. 247c478bd9Sstevel@tonic-gate */ 257c478bd9Sstevel@tonic-gate 267c478bd9Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 277c478bd9Sstevel@tonic-gate 287c478bd9Sstevel@tonic-gate /* 297c478bd9Sstevel@tonic-gate * log.c - debugging and logging functions 307c478bd9Sstevel@tonic-gate * 317c478bd9Sstevel@tonic-gate * Logging destinations 327c478bd9Sstevel@tonic-gate * svc.startd(1M) supports three logging destinations: the system log, a 337c478bd9Sstevel@tonic-gate * daemon-specific log (in the /var/svc/log hierarchy by default), and to the 34*99b44c3bSlianep * standard output (redirected to the /var/svc/log/svc.startd.log file by 35*99b44c3bSlianep * default). Any or all of these destinations may be used to 367c478bd9Sstevel@tonic-gate * communicate a specific message; the audiences for each destination differ. 377c478bd9Sstevel@tonic-gate * 387c478bd9Sstevel@tonic-gate * Generic messages associated with svc.startd(1M) are made by the 397c478bd9Sstevel@tonic-gate * log_framework() and log_error() functions. For these messages, svc.startd 407c478bd9Sstevel@tonic-gate * logs under its own name and under the LOG_DAEMON facility when issuing 417c478bd9Sstevel@tonic-gate * events to the system log. By design, severities below LOG_NOTICE are never 427c478bd9Sstevel@tonic-gate * issued to the system log. 437c478bd9Sstevel@tonic-gate * 447c478bd9Sstevel@tonic-gate * Messages associated with a specific service instance are logged using the 457c478bd9Sstevel@tonic-gate * log_instance() or log_instance_fmri() functions. These messages are always 467c478bd9Sstevel@tonic-gate * sent to the appropriate per-instance log file. 477c478bd9Sstevel@tonic-gate * 487c478bd9Sstevel@tonic-gate * In the case of verbose or debug boot, the log_transition() function 497c478bd9Sstevel@tonic-gate * displays messages regarding instance transitions to the system console, 507c478bd9Sstevel@tonic-gate * until the expected login services are available. 517c478bd9Sstevel@tonic-gate * 527c478bd9Sstevel@tonic-gate * Finally, log_console() displays messages to the system consoles and 537c478bd9Sstevel@tonic-gate * the master restarter log file. This is used when booting to a milestone 547c478bd9Sstevel@tonic-gate * other than 'all'. 557c478bd9Sstevel@tonic-gate * 567c478bd9Sstevel@tonic-gate * Logging detail 577c478bd9Sstevel@tonic-gate * The constants for severity from <syslog.h> are reused, with a specific 587c478bd9Sstevel@tonic-gate * convention here. (It is worth noting that the #define values for the LOG_ 597c478bd9Sstevel@tonic-gate * levels are such that more important severities have lower values.) The 607c478bd9Sstevel@tonic-gate * severity determines the importance of the event, and its addressibility by 617c478bd9Sstevel@tonic-gate * the administrator. Each severity level's use is defined below, along with 627c478bd9Sstevel@tonic-gate * an illustrative example. 637c478bd9Sstevel@tonic-gate * 647c478bd9Sstevel@tonic-gate * LOG_EMERG Not used presently. 657c478bd9Sstevel@tonic-gate * 667c478bd9Sstevel@tonic-gate * LOG_ALERT An unrecoverable operation requiring external 677c478bd9Sstevel@tonic-gate * intervention has occurred. Includes an inability to 687c478bd9Sstevel@tonic-gate * write to the smf(5) repository (due to svc.configd(1M) 697c478bd9Sstevel@tonic-gate * absence, due to permissions failures, etc.). Message 707c478bd9Sstevel@tonic-gate * should identify component at fault. 717c478bd9Sstevel@tonic-gate * 727c478bd9Sstevel@tonic-gate * LOG_CRIT An unrecoverable operation internal to svc.startd(1M) 737c478bd9Sstevel@tonic-gate * has occurred. Failure should be recoverable by restart 747c478bd9Sstevel@tonic-gate * of svc.startd(1M). 757c478bd9Sstevel@tonic-gate * 767c478bd9Sstevel@tonic-gate * LOG_ERR An smf(5) event requiring administrative intervention 777c478bd9Sstevel@tonic-gate * has occurred. Includes instance being moved to the 787c478bd9Sstevel@tonic-gate * maintenance state. 797c478bd9Sstevel@tonic-gate * 807c478bd9Sstevel@tonic-gate * LOG_WARNING A potentially destabilizing smf(5) event not requiring 817c478bd9Sstevel@tonic-gate * administrative intervention has occurred. 827c478bd9Sstevel@tonic-gate * 837c478bd9Sstevel@tonic-gate * LOG_NOTICE A noteworthy smf(5) event has occurred. Includes 847c478bd9Sstevel@tonic-gate * individual instance failures. 857c478bd9Sstevel@tonic-gate * 867c478bd9Sstevel@tonic-gate * LOG_INFO A noteworthy operation internal to svc.startd(1M) has 877c478bd9Sstevel@tonic-gate * occurred. Includes recoverable failures or otherwise 887c478bd9Sstevel@tonic-gate * unexpected outcomes. 897c478bd9Sstevel@tonic-gate * 907c478bd9Sstevel@tonic-gate * LOG_DEBUG An internal operation only of interest to a 917c478bd9Sstevel@tonic-gate * svc.startd(1M) developer has occurred. 927c478bd9Sstevel@tonic-gate * 937c478bd9Sstevel@tonic-gate * Logging configuration 947c478bd9Sstevel@tonic-gate * While the logging output can be configured using the -d and -v flags at 957c478bd9Sstevel@tonic-gate * invocation, the preferred approach is to set the logging property values 967c478bd9Sstevel@tonic-gate * in the options property group of the svc.startd default instance. The 977c478bd9Sstevel@tonic-gate * valid values are "quiet", "verbose", and "debug". "quiet" is the default; 987c478bd9Sstevel@tonic-gate * "verbose" and "debug" allow LOG_INFO and LOG_DEBUG logging requests to 997c478bd9Sstevel@tonic-gate * reach the daemon-specific log, respectively. 1007c478bd9Sstevel@tonic-gate */ 1017c478bd9Sstevel@tonic-gate 1027c478bd9Sstevel@tonic-gate #include <sys/stat.h> 1037c478bd9Sstevel@tonic-gate #include <sys/statvfs.h> 1047c478bd9Sstevel@tonic-gate #include <sys/time.h> 1057c478bd9Sstevel@tonic-gate #include <sys/types.h> 1067c478bd9Sstevel@tonic-gate #include <assert.h> 1077c478bd9Sstevel@tonic-gate #include <errno.h> 1087c478bd9Sstevel@tonic-gate #include <fcntl.h> 1097c478bd9Sstevel@tonic-gate #include <kstat.h> 1107c478bd9Sstevel@tonic-gate #include <libgen.h> 1117c478bd9Sstevel@tonic-gate #include <libintl.h> 1127c478bd9Sstevel@tonic-gate #include <libuutil.h> 1137c478bd9Sstevel@tonic-gate #include <locale.h> 1147c478bd9Sstevel@tonic-gate #include <malloc.h> 1157c478bd9Sstevel@tonic-gate #include <pthread.h> 1167c478bd9Sstevel@tonic-gate #include <stdarg.h> 1177c478bd9Sstevel@tonic-gate #include <stdio.h> 1187c478bd9Sstevel@tonic-gate #include <strings.h> 1197c478bd9Sstevel@tonic-gate #include <syslog.h> 1207c478bd9Sstevel@tonic-gate #include <unistd.h> 1217c478bd9Sstevel@tonic-gate #include <zone.h> 1227c478bd9Sstevel@tonic-gate 1237c478bd9Sstevel@tonic-gate #include "startd.h" 1247c478bd9Sstevel@tonic-gate 1257c478bd9Sstevel@tonic-gate 1267c478bd9Sstevel@tonic-gate #define LOGBUF_SZ (60 * 80) /* 60 lines */ 1277c478bd9Sstevel@tonic-gate 1287c478bd9Sstevel@tonic-gate static FILE *logfile = NULL; 1297c478bd9Sstevel@tonic-gate 1307c478bd9Sstevel@tonic-gate #ifndef NDEBUG 1317c478bd9Sstevel@tonic-gate /* 1327c478bd9Sstevel@tonic-gate * This is a circular buffer for all (even those not emitted externally) 1337c478bd9Sstevel@tonic-gate * logging messages. To read it properly you should start after the first 1347c478bd9Sstevel@tonic-gate * null, go until the second, and then go back to the beginning until the 1357c478bd9Sstevel@tonic-gate * first null. Or use ::startd_log in mdb. 1367c478bd9Sstevel@tonic-gate */ 1377c478bd9Sstevel@tonic-gate /* LINTED unused */ 1387c478bd9Sstevel@tonic-gate static const size_t logbuf_sz = LOGBUF_SZ; /* For mdb */ 1397c478bd9Sstevel@tonic-gate static char logbuf[LOGBUF_SZ] = ""; 1407c478bd9Sstevel@tonic-gate static pthread_mutex_t logbuf_mutex = PTHREAD_MUTEX_INITIALIZER; 1417c478bd9Sstevel@tonic-gate #endif 1427c478bd9Sstevel@tonic-gate 1437c478bd9Sstevel@tonic-gate static void 1447c478bd9Sstevel@tonic-gate xstrftime_poststart(char *buf, size_t bufsize, struct timeval *time) 1457c478bd9Sstevel@tonic-gate { 1467c478bd9Sstevel@tonic-gate long sec, usec; 1477c478bd9Sstevel@tonic-gate 1487c478bd9Sstevel@tonic-gate sec = time->tv_sec - st->st_start_time.tv_sec; 1497c478bd9Sstevel@tonic-gate usec = time->tv_usec - st->st_start_time.tv_usec; 1507c478bd9Sstevel@tonic-gate 1517c478bd9Sstevel@tonic-gate if (usec < 0) { 1527c478bd9Sstevel@tonic-gate sec -= 1; 1537c478bd9Sstevel@tonic-gate usec += 1000000; 1547c478bd9Sstevel@tonic-gate } 1557c478bd9Sstevel@tonic-gate 1567c478bd9Sstevel@tonic-gate (void) snprintf(buf, bufsize, "start + %d.%02ds", sec, usec / 10000); 1577c478bd9Sstevel@tonic-gate } 1587c478bd9Sstevel@tonic-gate 1597c478bd9Sstevel@tonic-gate static void 1607c478bd9Sstevel@tonic-gate vlog_prefix(int severity, const char *prefix, const char *format, va_list args) 1617c478bd9Sstevel@tonic-gate { 1627c478bd9Sstevel@tonic-gate char buf[512], *cp; 1637c478bd9Sstevel@tonic-gate char timebuf[LOG_DATE_SIZE]; 1647c478bd9Sstevel@tonic-gate struct timeval now; 1657c478bd9Sstevel@tonic-gate struct tm ltime; 1667c478bd9Sstevel@tonic-gate 1677c478bd9Sstevel@tonic-gate #ifdef NDEBUG 1687c478bd9Sstevel@tonic-gate if (severity > st->st_log_level_min) 1697c478bd9Sstevel@tonic-gate return; 1707c478bd9Sstevel@tonic-gate #endif 1717c478bd9Sstevel@tonic-gate 1727c478bd9Sstevel@tonic-gate if (gettimeofday(&now, NULL) != 0) 1737c478bd9Sstevel@tonic-gate (void) fprintf(stderr, "gettimeofday(3C) failed: %s\n", 1747c478bd9Sstevel@tonic-gate strerror(errno)); 1757c478bd9Sstevel@tonic-gate 1767c478bd9Sstevel@tonic-gate if (st->st_log_timezone_known) 1777c478bd9Sstevel@tonic-gate (void) strftime(timebuf, sizeof (timebuf), "%b %e %T", 1787c478bd9Sstevel@tonic-gate localtime_r(&now.tv_sec, <ime)); 1797c478bd9Sstevel@tonic-gate else 1807c478bd9Sstevel@tonic-gate xstrftime_poststart(timebuf, sizeof (timebuf), &now); 1817c478bd9Sstevel@tonic-gate 1827c478bd9Sstevel@tonic-gate (void) snprintf(buf, sizeof (buf), "%s/%d%s: ", timebuf, pthread_self(), 1837c478bd9Sstevel@tonic-gate prefix); 1847c478bd9Sstevel@tonic-gate cp = strchr(buf, '\0'); 1857c478bd9Sstevel@tonic-gate (void) vsnprintf(cp, sizeof (buf) - (cp - buf), format, args); 1867c478bd9Sstevel@tonic-gate 1877c478bd9Sstevel@tonic-gate #ifndef NDEBUG 1887c478bd9Sstevel@tonic-gate /* Copy into logbuf. */ 1897c478bd9Sstevel@tonic-gate (void) pthread_mutex_lock(&logbuf_mutex); 1907c478bd9Sstevel@tonic-gate if (strlen(logbuf) + strlen(buf) + 1 <= sizeof (logbuf)) 1917c478bd9Sstevel@tonic-gate (void) strcat(logbuf, buf); 1927c478bd9Sstevel@tonic-gate else 1937c478bd9Sstevel@tonic-gate (void) strlcpy(logbuf, buf, sizeof (logbuf)); 1947c478bd9Sstevel@tonic-gate (void) pthread_mutex_unlock(&logbuf_mutex); 1957c478bd9Sstevel@tonic-gate 1967c478bd9Sstevel@tonic-gate if (severity > st->st_log_level_min) 1977c478bd9Sstevel@tonic-gate return; 1987c478bd9Sstevel@tonic-gate #endif 1997c478bd9Sstevel@tonic-gate 200*99b44c3bSlianep if (st->st_log_flags & STARTD_LOG_FILE && logfile) { 2017c478bd9Sstevel@tonic-gate (void) fputs(buf, logfile); 202*99b44c3bSlianep (void) fflush(logfile); 203*99b44c3bSlianep } 2047c478bd9Sstevel@tonic-gate if (st->st_log_flags & STARTD_LOG_TERMINAL) 2057c478bd9Sstevel@tonic-gate (void) fputs(buf, stdout); 206*99b44c3bSlianep if (st->st_log_flags & STARTD_LOG_SYSLOG && st->st_log_timezone_known) 2077c478bd9Sstevel@tonic-gate vsyslog(severity, format, args); 2087c478bd9Sstevel@tonic-gate } 2097c478bd9Sstevel@tonic-gate 2107c478bd9Sstevel@tonic-gate /*PRINTFLIKE2*/ 2117c478bd9Sstevel@tonic-gate void 2127c478bd9Sstevel@tonic-gate log_error(int severity, const char *format, ...) 2137c478bd9Sstevel@tonic-gate { 2147c478bd9Sstevel@tonic-gate va_list args; 2157c478bd9Sstevel@tonic-gate 2167c478bd9Sstevel@tonic-gate va_start(args, format); 2177c478bd9Sstevel@tonic-gate vlog_prefix(severity, " ERROR", format, args); 2187c478bd9Sstevel@tonic-gate va_end(args); 2197c478bd9Sstevel@tonic-gate } 2207c478bd9Sstevel@tonic-gate 2217c478bd9Sstevel@tonic-gate /*PRINTFLIKE2*/ 2227c478bd9Sstevel@tonic-gate void 2237c478bd9Sstevel@tonic-gate log_framework(int severity, const char *format, ...) 2247c478bd9Sstevel@tonic-gate { 2257c478bd9Sstevel@tonic-gate va_list args; 2267c478bd9Sstevel@tonic-gate 2277c478bd9Sstevel@tonic-gate va_start(args, format); 2287c478bd9Sstevel@tonic-gate vlog_prefix(severity, "", format, args); 2297c478bd9Sstevel@tonic-gate va_end(args); 2307c478bd9Sstevel@tonic-gate } 2317c478bd9Sstevel@tonic-gate 2327c478bd9Sstevel@tonic-gate /* 2337c478bd9Sstevel@tonic-gate * void log_preexec() 2347c478bd9Sstevel@tonic-gate * 2357c478bd9Sstevel@tonic-gate * log_preexec() should be invoked prior to any exec(2) calls, to prevent the 2367c478bd9Sstevel@tonic-gate * logfile and syslogd file descriptors from being leaked to child processes. 2377c478bd9Sstevel@tonic-gate * Why openlog(3C) lacks a close-on-exec option is a minor mystery. 2387c478bd9Sstevel@tonic-gate */ 2397c478bd9Sstevel@tonic-gate void 2407c478bd9Sstevel@tonic-gate log_preexec() 2417c478bd9Sstevel@tonic-gate { 2427c478bd9Sstevel@tonic-gate closelog(); 2437c478bd9Sstevel@tonic-gate } 2447c478bd9Sstevel@tonic-gate 2457c478bd9Sstevel@tonic-gate /* 2467c478bd9Sstevel@tonic-gate * void setlog() 2477c478bd9Sstevel@tonic-gate * Close file descriptors and redirect output. 2487c478bd9Sstevel@tonic-gate */ 2497c478bd9Sstevel@tonic-gate void 2507c478bd9Sstevel@tonic-gate setlog(const char *logstem) 2517c478bd9Sstevel@tonic-gate { 2527c478bd9Sstevel@tonic-gate int fd; 2537c478bd9Sstevel@tonic-gate char logfile[PATH_MAX]; 2547c478bd9Sstevel@tonic-gate 2557c478bd9Sstevel@tonic-gate closefrom(0); 2567c478bd9Sstevel@tonic-gate 2577c478bd9Sstevel@tonic-gate (void) open("/dev/null", O_RDONLY); 2587c478bd9Sstevel@tonic-gate 2597c478bd9Sstevel@tonic-gate (void) snprintf(logfile, PATH_MAX, "%s/%s", st->st_log_prefix, logstem); 2607c478bd9Sstevel@tonic-gate 2617c478bd9Sstevel@tonic-gate (void) umask(fmask); 2627c478bd9Sstevel@tonic-gate fd = open(logfile, O_WRONLY|O_CREAT|O_APPEND, 2637c478bd9Sstevel@tonic-gate S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH); 2647c478bd9Sstevel@tonic-gate (void) umask(dmask); 2657c478bd9Sstevel@tonic-gate 2667c478bd9Sstevel@tonic-gate if (fd == -1) 2677c478bd9Sstevel@tonic-gate return; 2687c478bd9Sstevel@tonic-gate 2697c478bd9Sstevel@tonic-gate (void) dup2(fd, 1); 2707c478bd9Sstevel@tonic-gate (void) dup2(fd, 2); 2717c478bd9Sstevel@tonic-gate 2727c478bd9Sstevel@tonic-gate if (fd != 1 && fd != 2) 2737c478bd9Sstevel@tonic-gate startd_close(fd); 2747c478bd9Sstevel@tonic-gate } 2757c478bd9Sstevel@tonic-gate 2767c478bd9Sstevel@tonic-gate static int 2777c478bd9Sstevel@tonic-gate log_dir_writeable(const char *path) 2787c478bd9Sstevel@tonic-gate { 2797c478bd9Sstevel@tonic-gate int fd; 2807c478bd9Sstevel@tonic-gate struct statvfs svb; 2817c478bd9Sstevel@tonic-gate 2827c478bd9Sstevel@tonic-gate if ((fd = open(path, O_RDONLY, 0644)) == -1) 2837c478bd9Sstevel@tonic-gate return (-1); 2847c478bd9Sstevel@tonic-gate 2857c478bd9Sstevel@tonic-gate if (fstatvfs(fd, &svb) == -1) 2867c478bd9Sstevel@tonic-gate return (-1); 2877c478bd9Sstevel@tonic-gate 2887c478bd9Sstevel@tonic-gate if (svb.f_flag & ST_RDONLY) { 2897c478bd9Sstevel@tonic-gate (void) close(fd); 2907c478bd9Sstevel@tonic-gate 2917c478bd9Sstevel@tonic-gate fd = -1; 2927c478bd9Sstevel@tonic-gate } 2937c478bd9Sstevel@tonic-gate 2947c478bd9Sstevel@tonic-gate return (fd); 2957c478bd9Sstevel@tonic-gate } 2967c478bd9Sstevel@tonic-gate 2977c478bd9Sstevel@tonic-gate static void 2987c478bd9Sstevel@tonic-gate vlog_instance(const char *fmri, const char *logstem, boolean_t canlog, 2997c478bd9Sstevel@tonic-gate const char *format, va_list args) 3007c478bd9Sstevel@tonic-gate { 3017c478bd9Sstevel@tonic-gate char logfile[PATH_MAX]; 3027c478bd9Sstevel@tonic-gate char *message; 3037c478bd9Sstevel@tonic-gate char omessage[1024]; 3047c478bd9Sstevel@tonic-gate int fd, err; 3057c478bd9Sstevel@tonic-gate char timebuf[LOG_DATE_SIZE]; 3067c478bd9Sstevel@tonic-gate struct tm ltime; 3077c478bd9Sstevel@tonic-gate struct timeval now; 3087c478bd9Sstevel@tonic-gate 3097c478bd9Sstevel@tonic-gate (void) snprintf(logfile, PATH_MAX, "%s/%s", st->st_log_prefix, 3107c478bd9Sstevel@tonic-gate logstem); 3117c478bd9Sstevel@tonic-gate 3127c478bd9Sstevel@tonic-gate (void) umask(fmask); 3137c478bd9Sstevel@tonic-gate fd = open(logfile, O_WRONLY|O_CREAT|O_APPEND, 3147c478bd9Sstevel@tonic-gate S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH); 3157c478bd9Sstevel@tonic-gate err = errno; 3167c478bd9Sstevel@tonic-gate (void) umask(dmask); 3177c478bd9Sstevel@tonic-gate 3187c478bd9Sstevel@tonic-gate if (fd == -1) { 3197c478bd9Sstevel@tonic-gate if (canlog) 3207c478bd9Sstevel@tonic-gate log_error(LOG_NOTICE, "Could not log for %s: open(%s) " 3217c478bd9Sstevel@tonic-gate "failed with %s.\n", fmri, logfile, strerror(err)); 3227c478bd9Sstevel@tonic-gate 3237c478bd9Sstevel@tonic-gate return; 3247c478bd9Sstevel@tonic-gate } 3257c478bd9Sstevel@tonic-gate 3267c478bd9Sstevel@tonic-gate (void) vsnprintf(omessage, sizeof (omessage), format, args); 3277c478bd9Sstevel@tonic-gate 3287c478bd9Sstevel@tonic-gate if (gettimeofday(&now, NULL) != 0) 3297c478bd9Sstevel@tonic-gate (void) fprintf(stderr, "gettimeofday(3C) failed: %s\n", 3307c478bd9Sstevel@tonic-gate strerror(errno)); 3317c478bd9Sstevel@tonic-gate 3327c478bd9Sstevel@tonic-gate if (st->st_log_timezone_known) 3337c478bd9Sstevel@tonic-gate (void) strftime(timebuf, sizeof (timebuf), "%b %e %T", 3347c478bd9Sstevel@tonic-gate localtime_r(&now.tv_sec, <ime)); 3357c478bd9Sstevel@tonic-gate else 3367c478bd9Sstevel@tonic-gate xstrftime_poststart(timebuf, sizeof (timebuf), &now); 3377c478bd9Sstevel@tonic-gate 3387c478bd9Sstevel@tonic-gate message = uu_msprintf("[ %s %s ]\n", timebuf, omessage); 3397c478bd9Sstevel@tonic-gate 3407c478bd9Sstevel@tonic-gate if (message == NULL) { 3417c478bd9Sstevel@tonic-gate if (canlog) 3427c478bd9Sstevel@tonic-gate log_error(LOG_NOTICE, "Could not log for %s: %s.\n", 3437c478bd9Sstevel@tonic-gate fmri, uu_strerror(uu_error())); 3447c478bd9Sstevel@tonic-gate } else { 3457c478bd9Sstevel@tonic-gate if (write(fd, message, strlen(message)) < 0 && canlog) 3467c478bd9Sstevel@tonic-gate log_error(LOG_NOTICE, "Could not log for %s: write(%d) " 3477c478bd9Sstevel@tonic-gate "failed with %s.\n", fmri, fd, 3487c478bd9Sstevel@tonic-gate strerror(errno)); 3497c478bd9Sstevel@tonic-gate 3507c478bd9Sstevel@tonic-gate uu_free(message); 3517c478bd9Sstevel@tonic-gate } 3527c478bd9Sstevel@tonic-gate 3537c478bd9Sstevel@tonic-gate if (close(fd) != 0 && canlog) 3547c478bd9Sstevel@tonic-gate log_framework(LOG_NOTICE, "close(%d) failed: %s.\n", fd, 3557c478bd9Sstevel@tonic-gate strerror(errno)); 3567c478bd9Sstevel@tonic-gate } 3577c478bd9Sstevel@tonic-gate 3587c478bd9Sstevel@tonic-gate /* 3597c478bd9Sstevel@tonic-gate * void log_instance(const restarter_inst_t *, boolean_t, const char *, ...) 3607c478bd9Sstevel@tonic-gate * 3617c478bd9Sstevel@tonic-gate * The log_instance() format is "[ month day time message ]". (The 3627c478bd9Sstevel@tonic-gate * brackets distinguish svc.startd messages from method output.) We avoid 3637c478bd9Sstevel@tonic-gate * calling log_*() functions on error when canlog is not set, since we may 3647c478bd9Sstevel@tonic-gate * be called from a child process. 3657c478bd9Sstevel@tonic-gate * 3667c478bd9Sstevel@tonic-gate * When adding new calls to this function, consider: If this is called before 3677c478bd9Sstevel@tonic-gate * any instances have started, then it should be called with canlog clear, 3687c478bd9Sstevel@tonic-gate * lest we spew errors to the console when booted on the miniroot. 3697c478bd9Sstevel@tonic-gate */ 3707c478bd9Sstevel@tonic-gate /*PRINTFLIKE3*/ 3717c478bd9Sstevel@tonic-gate void 3727c478bd9Sstevel@tonic-gate log_instance(const restarter_inst_t *inst, boolean_t canlog, 3737c478bd9Sstevel@tonic-gate const char *format, ...) 3747c478bd9Sstevel@tonic-gate { 3757c478bd9Sstevel@tonic-gate va_list args; 3767c478bd9Sstevel@tonic-gate 3777c478bd9Sstevel@tonic-gate va_start(args, format); 3787c478bd9Sstevel@tonic-gate vlog_instance(inst->ri_i.i_fmri, inst->ri_logstem, canlog, format, 3797c478bd9Sstevel@tonic-gate args); 3807c478bd9Sstevel@tonic-gate va_end(args); 3817c478bd9Sstevel@tonic-gate } 3827c478bd9Sstevel@tonic-gate 3837c478bd9Sstevel@tonic-gate /* 3847c478bd9Sstevel@tonic-gate * void log_instance_fmri(const char *, const char *,boolean_t, const char *, 3857c478bd9Sstevel@tonic-gate * ...) 3867c478bd9Sstevel@tonic-gate * 3877c478bd9Sstevel@tonic-gate * The log_instance_fmri() format is "[ month day time message ]". (The 3887c478bd9Sstevel@tonic-gate * brackets distinguish svc.startd messages from method output.) We avoid 3897c478bd9Sstevel@tonic-gate * calling log_*() functions on error when canlog is not set, since we may 3907c478bd9Sstevel@tonic-gate * be called from a child process. 3917c478bd9Sstevel@tonic-gate * 3927c478bd9Sstevel@tonic-gate * For new calls to this function, see the warning in log_instance()'s 3937c478bd9Sstevel@tonic-gate * comment. 3947c478bd9Sstevel@tonic-gate */ 3957c478bd9Sstevel@tonic-gate /*PRINTFLIKE4*/ 3967c478bd9Sstevel@tonic-gate void 3977c478bd9Sstevel@tonic-gate log_instance_fmri(const char *fmri, const char *logstem, boolean_t canlog, 3987c478bd9Sstevel@tonic-gate const char *format, ...) 3997c478bd9Sstevel@tonic-gate { 4007c478bd9Sstevel@tonic-gate va_list args; 4017c478bd9Sstevel@tonic-gate 4027c478bd9Sstevel@tonic-gate va_start(args, format); 4037c478bd9Sstevel@tonic-gate vlog_instance(fmri, logstem, canlog, format, args); 4047c478bd9Sstevel@tonic-gate va_end(args); 4057c478bd9Sstevel@tonic-gate } 4067c478bd9Sstevel@tonic-gate 4077c478bd9Sstevel@tonic-gate /* 4087c478bd9Sstevel@tonic-gate * void log_transition(const restarter_inst_t *, start_outcome_t) 4097c478bd9Sstevel@tonic-gate * 4107c478bd9Sstevel@tonic-gate * The log_transition() format is 4117c478bd9Sstevel@tonic-gate * 4127c478bd9Sstevel@tonic-gate * [ _service_fmri_ _participle_ (_common_name_) ] 4137c478bd9Sstevel@tonic-gate * 4147c478bd9Sstevel@tonic-gate * Again, brackets separate messages from specific service instance output to 4157c478bd9Sstevel@tonic-gate * the console. 4167c478bd9Sstevel@tonic-gate */ 4177c478bd9Sstevel@tonic-gate void 4187c478bd9Sstevel@tonic-gate log_transition(const restarter_inst_t *inst, start_outcome_t outcome) 4197c478bd9Sstevel@tonic-gate { 4207c478bd9Sstevel@tonic-gate char *message; 4217c478bd9Sstevel@tonic-gate char omessage[1024]; 4227c478bd9Sstevel@tonic-gate char *action; 4237c478bd9Sstevel@tonic-gate int severity; 4247c478bd9Sstevel@tonic-gate 4257c478bd9Sstevel@tonic-gate if (outcome == START_REQUESTED) { 4267c478bd9Sstevel@tonic-gate char *cname = NULL; 4277c478bd9Sstevel@tonic-gate 4287c478bd9Sstevel@tonic-gate cname = inst->ri_common_name; 4297c478bd9Sstevel@tonic-gate if (cname == NULL) 4307c478bd9Sstevel@tonic-gate cname = inst->ri_C_common_name; 4317c478bd9Sstevel@tonic-gate 4327c478bd9Sstevel@tonic-gate if (!(st->st_boot_flags & STARTD_BOOT_VERBOSE)) 4337c478bd9Sstevel@tonic-gate return; 4347c478bd9Sstevel@tonic-gate 4357c478bd9Sstevel@tonic-gate if (inst->ri_start_index > 1) 4367c478bd9Sstevel@tonic-gate return; 4377c478bd9Sstevel@tonic-gate 4387c478bd9Sstevel@tonic-gate if (cname) 4397c478bd9Sstevel@tonic-gate (void) snprintf(omessage, sizeof (omessage), " (%s)", 4407c478bd9Sstevel@tonic-gate cname); 4417c478bd9Sstevel@tonic-gate else 4427c478bd9Sstevel@tonic-gate *omessage = '\0'; 4437c478bd9Sstevel@tonic-gate 4447c478bd9Sstevel@tonic-gate action = gettext("starting"); 4457c478bd9Sstevel@tonic-gate 4467c478bd9Sstevel@tonic-gate message = uu_msprintf("[ %s %s%s ]\n", 4477c478bd9Sstevel@tonic-gate inst->ri_i.i_fmri + strlen("svc:/"), action, 4487c478bd9Sstevel@tonic-gate omessage); 4497c478bd9Sstevel@tonic-gate 4507c478bd9Sstevel@tonic-gate severity = LOG_INFO; 4517c478bd9Sstevel@tonic-gate } else { 4527c478bd9Sstevel@tonic-gate switch (outcome) { 453*99b44c3bSlianep case MAINT_REQUESTED: 454*99b44c3bSlianep action = gettext("transitioned to maintenance by " 455*99b44c3bSlianep "request (see 'svcs -xv' for details)"); 456*99b44c3bSlianep break; 4577c478bd9Sstevel@tonic-gate case START_FAILED_REPEATEDLY: 458*99b44c3bSlianep action = gettext("failed repeatedly: transitioned to " 459*99b44c3bSlianep "maintenance (see 'svcs -xv' for details)"); 4607c478bd9Sstevel@tonic-gate break; 4617c478bd9Sstevel@tonic-gate case START_FAILED_CONFIGURATION: 462*99b44c3bSlianep action = gettext("misconfigured: transitioned to " 463*99b44c3bSlianep "maintenance (see 'svcs -xv' for details)"); 4647c478bd9Sstevel@tonic-gate break; 4657c478bd9Sstevel@tonic-gate case START_FAILED_FATAL: 466*99b44c3bSlianep action = gettext("failed fatally: transitioned to " 467*99b44c3bSlianep "maintenance (see 'svcs -xv' for details)"); 4687c478bd9Sstevel@tonic-gate break; 4697c478bd9Sstevel@tonic-gate case START_FAILED_TIMEOUT_FATAL: 470*99b44c3bSlianep action = gettext("timed out: transitioned to " 471*99b44c3bSlianep "maintenance (see 'svcs -xv' for details)"); 4727c478bd9Sstevel@tonic-gate break; 4737c478bd9Sstevel@tonic-gate case START_FAILED_OTHER: 474*99b44c3bSlianep action = gettext("failed: transitioned to " 475*99b44c3bSlianep "maintenance (see 'svcs -xv' for details)"); 4767c478bd9Sstevel@tonic-gate break; 4777c478bd9Sstevel@tonic-gate case START_REQUESTED: 4787c478bd9Sstevel@tonic-gate assert(outcome != START_REQUESTED); 4797c478bd9Sstevel@tonic-gate /*FALLTHROUGH*/ 4807c478bd9Sstevel@tonic-gate default: 4817c478bd9Sstevel@tonic-gate action = gettext("outcome unknown?"); 4827c478bd9Sstevel@tonic-gate } 4837c478bd9Sstevel@tonic-gate 484*99b44c3bSlianep message = uu_msprintf("[ %s %s ]\n", 485*99b44c3bSlianep inst->ri_i.i_fmri + strlen("svc:/"), action); 4867c478bd9Sstevel@tonic-gate 4877c478bd9Sstevel@tonic-gate severity = LOG_ERR; 4887c478bd9Sstevel@tonic-gate } 4897c478bd9Sstevel@tonic-gate 4907c478bd9Sstevel@tonic-gate 4917c478bd9Sstevel@tonic-gate if (message == NULL) { 4927c478bd9Sstevel@tonic-gate log_error(LOG_NOTICE, 4937c478bd9Sstevel@tonic-gate "Could not log boot message for %s: %s.\n", 4947c478bd9Sstevel@tonic-gate inst->ri_i.i_fmri, uu_strerror(uu_error())); 4957c478bd9Sstevel@tonic-gate } else { 496*99b44c3bSlianep /* 497*99b44c3bSlianep * All significant errors should to go to syslog to 498*99b44c3bSlianep * communicate appropriate information even for systems 499*99b44c3bSlianep * without a console connected during boot. Send the 500*99b44c3bSlianep * message to stderr only if the severity is lower than 501*99b44c3bSlianep * (indicated by >) LOG_ERR. 502*99b44c3bSlianep */ 503*99b44c3bSlianep if (!st->st_log_login_reached && severity > LOG_ERR) { 5047c478bd9Sstevel@tonic-gate /*LINTED*/ 5057c478bd9Sstevel@tonic-gate if (fprintf(stderr, message) < 0) 5067c478bd9Sstevel@tonic-gate log_error(LOG_NOTICE, "Could not log for %s: " 5077c478bd9Sstevel@tonic-gate "fprintf() failed with %s.\n", 5087c478bd9Sstevel@tonic-gate inst->ri_i.i_fmri, strerror(errno)); 5097c478bd9Sstevel@tonic-gate } else { 5107c478bd9Sstevel@tonic-gate log_framework(severity, "%s %s\n", 5117c478bd9Sstevel@tonic-gate inst->ri_i.i_fmri + strlen("svc:/"), action); 5127c478bd9Sstevel@tonic-gate } 5137c478bd9Sstevel@tonic-gate 5147c478bd9Sstevel@tonic-gate uu_free(message); 5157c478bd9Sstevel@tonic-gate } 5167c478bd9Sstevel@tonic-gate } 5177c478bd9Sstevel@tonic-gate 5187c478bd9Sstevel@tonic-gate /* 5197c478bd9Sstevel@tonic-gate * log_console - log a message to the consoles and to syslog 5207c478bd9Sstevel@tonic-gate * 5217c478bd9Sstevel@tonic-gate * This logs a message as-is to the console (and auxiliary consoles), 5227c478bd9Sstevel@tonic-gate * as well as to the master restarter log. 5237c478bd9Sstevel@tonic-gate */ 5247c478bd9Sstevel@tonic-gate /*PRINTFLIKE2*/ 5257c478bd9Sstevel@tonic-gate void 5267c478bd9Sstevel@tonic-gate log_console(int severity, const char *format, ...) 5277c478bd9Sstevel@tonic-gate { 5287c478bd9Sstevel@tonic-gate va_list args; 5297c478bd9Sstevel@tonic-gate 5307c478bd9Sstevel@tonic-gate va_start(args, format); 5317c478bd9Sstevel@tonic-gate vlog_prefix(severity, "", format, args); 5327c478bd9Sstevel@tonic-gate va_end(args); 5337c478bd9Sstevel@tonic-gate 5347c478bd9Sstevel@tonic-gate va_start(args, format); 5357c478bd9Sstevel@tonic-gate (void) vfprintf(stderr, format, args); 5367c478bd9Sstevel@tonic-gate va_end(args); 5377c478bd9Sstevel@tonic-gate } 5387c478bd9Sstevel@tonic-gate 5397c478bd9Sstevel@tonic-gate /* 5407c478bd9Sstevel@tonic-gate * void log_init() 5417c478bd9Sstevel@tonic-gate * 5427c478bd9Sstevel@tonic-gate * Set up the log files, if necessary, for the current invocation. This 5437c478bd9Sstevel@tonic-gate * function should be called before any other functions in this file. Set the 5447c478bd9Sstevel@tonic-gate * syslog(3C) logging mask such that severities of the importance of 5457c478bd9Sstevel@tonic-gate * LOG_NOTICE and above are passed through, but lower severity messages are 5467c478bd9Sstevel@tonic-gate * masked out. 5477c478bd9Sstevel@tonic-gate * 5487c478bd9Sstevel@tonic-gate * It may be called multiple times to change the logging configuration due to 5497c478bd9Sstevel@tonic-gate * administrative request. 5507c478bd9Sstevel@tonic-gate */ 5517c478bd9Sstevel@tonic-gate void 5527c478bd9Sstevel@tonic-gate log_init() 5537c478bd9Sstevel@tonic-gate { 5547c478bd9Sstevel@tonic-gate int dirfd, logfd; 5557c478bd9Sstevel@tonic-gate char *dir; 5567c478bd9Sstevel@tonic-gate struct stat sb; 5577c478bd9Sstevel@tonic-gate 5587c478bd9Sstevel@tonic-gate if (st->st_start_time.tv_sec == 0) { 5597c478bd9Sstevel@tonic-gate if (getzoneid() != GLOBAL_ZONEID) { 5607c478bd9Sstevel@tonic-gate st->st_start_time.tv_sec = time(NULL); 5617c478bd9Sstevel@tonic-gate } else { 5627c478bd9Sstevel@tonic-gate /* 5637c478bd9Sstevel@tonic-gate * We need to special-case the BOOT_TIME utmp entry, and 5647c478bd9Sstevel@tonic-gate * drag that value out of the kernel if it's there. 5657c478bd9Sstevel@tonic-gate */ 5667c478bd9Sstevel@tonic-gate kstat_ctl_t *kc; 5677c478bd9Sstevel@tonic-gate kstat_t *ks; 5687c478bd9Sstevel@tonic-gate kstat_named_t *boot; 5697c478bd9Sstevel@tonic-gate 5707c478bd9Sstevel@tonic-gate if (((kc = kstat_open()) != 0) && 5717c478bd9Sstevel@tonic-gate ((ks = kstat_lookup(kc, "unix", 0, "system_misc")) 5727c478bd9Sstevel@tonic-gate != NULL) && 5737c478bd9Sstevel@tonic-gate (kstat_read(kc, ks, NULL) != -1) && 5747c478bd9Sstevel@tonic-gate ((boot = kstat_data_lookup(ks, "boot_time")) != 5757c478bd9Sstevel@tonic-gate NULL)) { 5767c478bd9Sstevel@tonic-gate /* 5777c478bd9Sstevel@tonic-gate * If we're here, then we've successfully found 5787c478bd9Sstevel@tonic-gate * the boot_time kstat... use its value. 5797c478bd9Sstevel@tonic-gate */ 5807c478bd9Sstevel@tonic-gate st->st_start_time.tv_sec = boot->value.ul; 5817c478bd9Sstevel@tonic-gate } else { 5827c478bd9Sstevel@tonic-gate st->st_start_time.tv_sec = time(NULL); 5837c478bd9Sstevel@tonic-gate } 5847c478bd9Sstevel@tonic-gate 5857c478bd9Sstevel@tonic-gate if (kc) 5867c478bd9Sstevel@tonic-gate (void) kstat_close(kc); 5877c478bd9Sstevel@tonic-gate } 5887c478bd9Sstevel@tonic-gate } 5897c478bd9Sstevel@tonic-gate 5907c478bd9Sstevel@tonic-gate /* 5917c478bd9Sstevel@tonic-gate * Establish our timezone if the appropriate directory is available. 5927c478bd9Sstevel@tonic-gate */ 5937c478bd9Sstevel@tonic-gate if (!st->st_log_timezone_known && stat(FS_TIMEZONE_DIR, &sb) == 0) { 5947c478bd9Sstevel@tonic-gate tzset(); 5957c478bd9Sstevel@tonic-gate st->st_log_timezone_known = 1; 5967c478bd9Sstevel@tonic-gate } 5977c478bd9Sstevel@tonic-gate 5987c478bd9Sstevel@tonic-gate /* 5997c478bd9Sstevel@tonic-gate * Establish our locale if the appropriate directory is available. Set 6007c478bd9Sstevel@tonic-gate * the locale string from the environment so we can extract template 6017c478bd9Sstevel@tonic-gate * information correctly, if the locale directories aren't yet 6027c478bd9Sstevel@tonic-gate * available. 6037c478bd9Sstevel@tonic-gate */ 6047c478bd9Sstevel@tonic-gate if (st->st_locale != NULL) 6057c478bd9Sstevel@tonic-gate free(st->st_locale); 6067c478bd9Sstevel@tonic-gate 6077c478bd9Sstevel@tonic-gate if ((st->st_locale = getenv("LC_ALL")) == NULL) 6087c478bd9Sstevel@tonic-gate if ((st->st_locale = getenv("LC_MESSAGES")) == NULL) 6097c478bd9Sstevel@tonic-gate st->st_locale = getenv("LANG"); 6107c478bd9Sstevel@tonic-gate 6117c478bd9Sstevel@tonic-gate if (!st->st_log_locale_known && stat(FS_LOCALE_DIR, &sb) == 0) { 6127c478bd9Sstevel@tonic-gate (void) setlocale(LC_ALL, ""); 6137c478bd9Sstevel@tonic-gate st->st_locale = setlocale(LC_MESSAGES, ""); 6147c478bd9Sstevel@tonic-gate if (st->st_locale) 6157c478bd9Sstevel@tonic-gate st->st_log_locale_known = 1; 6167c478bd9Sstevel@tonic-gate 6177c478bd9Sstevel@tonic-gate (void) textdomain(TEXT_DOMAIN); 6187c478bd9Sstevel@tonic-gate } 6197c478bd9Sstevel@tonic-gate 6207c478bd9Sstevel@tonic-gate if (st->st_locale) { 6217c478bd9Sstevel@tonic-gate st->st_locale = safe_strdup(st->st_locale); 6227c478bd9Sstevel@tonic-gate xstr_sanitize(st->st_locale); 6237c478bd9Sstevel@tonic-gate } 6247c478bd9Sstevel@tonic-gate 6257c478bd9Sstevel@tonic-gate if (logfile) { 6267c478bd9Sstevel@tonic-gate (void) fclose(logfile); 6277c478bd9Sstevel@tonic-gate logfile = NULL; 6287c478bd9Sstevel@tonic-gate } 6297c478bd9Sstevel@tonic-gate 6307c478bd9Sstevel@tonic-gate /* 6317c478bd9Sstevel@tonic-gate * Set syslog(3C) behaviour in all cases. 6327c478bd9Sstevel@tonic-gate */ 6337c478bd9Sstevel@tonic-gate closelog(); 6347c478bd9Sstevel@tonic-gate openlog("svc.startd", LOG_PID | LOG_CONS, LOG_DAEMON); 6357c478bd9Sstevel@tonic-gate (void) setlogmask(LOG_UPTO(LOG_NOTICE)); 6367c478bd9Sstevel@tonic-gate 6377c478bd9Sstevel@tonic-gate if ((dirfd = log_dir_writeable(LOG_PREFIX_NORMAL)) == -1) { 6387c478bd9Sstevel@tonic-gate if ((dirfd = log_dir_writeable(LOG_PREFIX_EARLY)) == -1) 6397c478bd9Sstevel@tonic-gate return; 6407c478bd9Sstevel@tonic-gate else 6417c478bd9Sstevel@tonic-gate dir = LOG_PREFIX_EARLY; 6427c478bd9Sstevel@tonic-gate } else { 6437c478bd9Sstevel@tonic-gate dir = LOG_PREFIX_NORMAL; 6447c478bd9Sstevel@tonic-gate } 6457c478bd9Sstevel@tonic-gate 6467c478bd9Sstevel@tonic-gate st->st_log_prefix = dir; 6477c478bd9Sstevel@tonic-gate 6487c478bd9Sstevel@tonic-gate (void) umask(fmask); 6497c478bd9Sstevel@tonic-gate if ((logfd = openat(dirfd, STARTD_DEFAULT_LOG, O_CREAT | O_RDWR, 6507c478bd9Sstevel@tonic-gate 0644)) == -1) { 6517c478bd9Sstevel@tonic-gate (void) close(dirfd); 6527c478bd9Sstevel@tonic-gate (void) umask(dmask); 6537c478bd9Sstevel@tonic-gate return; 6547c478bd9Sstevel@tonic-gate } 6557c478bd9Sstevel@tonic-gate 6567c478bd9Sstevel@tonic-gate (void) close(dirfd); 6577c478bd9Sstevel@tonic-gate (void) umask(dmask); 6587c478bd9Sstevel@tonic-gate 6597c478bd9Sstevel@tonic-gate if ((logfile = fdopen(logfd, "a")) == NULL) 6607c478bd9Sstevel@tonic-gate if (errno != EROFS) 6617c478bd9Sstevel@tonic-gate log_error(LOG_WARNING, "can't open logfile %s/%s", 6627c478bd9Sstevel@tonic-gate dir, STARTD_DEFAULT_LOG); 6637c478bd9Sstevel@tonic-gate 6647c478bd9Sstevel@tonic-gate if (logfile && 6657c478bd9Sstevel@tonic-gate fcntl(fileno(logfile), F_SETFD, FD_CLOEXEC) == -1) 6667c478bd9Sstevel@tonic-gate log_error(LOG_WARNING, 6677c478bd9Sstevel@tonic-gate "couldn't mark logfile close-on-exec: %s\n", 6687c478bd9Sstevel@tonic-gate strerror(errno)); 6697c478bd9Sstevel@tonic-gate } 670