xref: /illumos-gate/usr/src/cmd/svc/startd/log.c (revision 99b44c3bba5a515aba1e56c2104f53af320a848d)
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  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 /*
29  * log.c - debugging and logging functions
30  *
31  * Logging destinations
32  *   svc.startd(1M) supports three logging destinations:  the system log, a
33  *   daemon-specific log (in the /var/svc/log hierarchy by default), and to the
34  *   standard output (redirected to the /var/svc/log/svc.startd.log file by
35  *   default).  Any or all of these destinations may be used to
36  *   communicate a specific message; the audiences for each destination differ.
37  *
38  *   Generic messages associated with svc.startd(1M) are made by the
39  *   log_framework() and log_error() functions.  For these messages, svc.startd
40  *   logs under its own name and under the LOG_DAEMON facility when issuing
41  *   events to the system log.  By design, severities below LOG_NOTICE are never
42  *   issued to the system log.
43  *
44  *   Messages associated with a specific service instance are logged using the
45  *   log_instance() or log_instance_fmri() functions.  These messages are always
46  *   sent to the appropriate per-instance log file.
47  *
48  *   In the case of verbose or debug boot, the log_transition() function
49  *   displays messages regarding instance transitions to the system console,
50  *   until the expected login services are available.
51  *
52  *   Finally, log_console() displays messages to the system consoles and
53  *   the master restarter log file.  This is used when booting to a milestone
54  *   other than 'all'.
55  *
56  * Logging detail
57  *   The constants for severity from <syslog.h> are reused, with a specific
58  *   convention here.  (It is worth noting that the #define values for the LOG_
59  *   levels are such that more important severities have lower values.)  The
60  *   severity determines the importance of the event, and its addressibility by
61  *   the administrator.  Each severity level's use is defined below, along with
62  *   an illustrative example.
63  *
64  *   LOG_EMERG		Not used presently.
65  *
66  *   LOG_ALERT		An unrecoverable operation requiring external
67  *			intervention has occurred.   Includes an inability to
68  *			write to the smf(5) repository (due to svc.configd(1M)
69  *			absence, due to permissions failures, etc.).  Message
70  *			should identify component at fault.
71  *
72  *   LOG_CRIT		An unrecoverable operation internal to svc.startd(1M)
73  *			has occurred.  Failure should be recoverable by restart
74  *			of svc.startd(1M).
75  *
76  *   LOG_ERR		An smf(5) event requiring administrative intervention
77  *			has occurred.  Includes instance being moved to the
78  *			maintenance state.
79  *
80  *   LOG_WARNING	A potentially destabilizing smf(5) event not requiring
81  *			administrative intervention has occurred.
82  *
83  *   LOG_NOTICE		A noteworthy smf(5) event has occurred.  Includes
84  *			individual instance failures.
85  *
86  *   LOG_INFO		A noteworthy operation internal to svc.startd(1M) has
87  *			occurred.  Includes recoverable failures or otherwise
88  *			unexpected outcomes.
89  *
90  *   LOG_DEBUG		An internal operation only of interest to a
91  *			svc.startd(1M) developer has occurred.
92  *
93  *  Logging configuration
94  *    While the logging output can be configured using the -d and -v flags at
95  *    invocation, the preferred approach is to set the logging property values
96  *    in the options property group of the svc.startd default instance.  The
97  *    valid values are "quiet", "verbose", and "debug".  "quiet" is the default;
98  *    "verbose" and "debug" allow LOG_INFO and LOG_DEBUG logging requests to
99  *    reach the daemon-specific log, respectively.
100  */
101 
102 #include <sys/stat.h>
103 #include <sys/statvfs.h>
104 #include <sys/time.h>
105 #include <sys/types.h>
106 #include <assert.h>
107 #include <errno.h>
108 #include <fcntl.h>
109 #include <kstat.h>
110 #include <libgen.h>
111 #include <libintl.h>
112 #include <libuutil.h>
113 #include <locale.h>
114 #include <malloc.h>
115 #include <pthread.h>
116 #include <stdarg.h>
117 #include <stdio.h>
118 #include <strings.h>
119 #include <syslog.h>
120 #include <unistd.h>
121 #include <zone.h>
122 
123 #include "startd.h"
124 
125 
126 #define	LOGBUF_SZ	(60 * 80)			/* 60 lines */
127 
128 static FILE *logfile = NULL;
129 
130 #ifndef NDEBUG
131 /*
132  * This is a circular buffer for all (even those not emitted externally)
133  * logging messages.  To read it properly you should start after the first
134  * null, go until the second, and then go back to the beginning until the
135  * first null.  Or use ::startd_log in mdb.
136  */
137 /* LINTED unused */
138 static const size_t logbuf_sz = LOGBUF_SZ;		/* For mdb */
139 static char logbuf[LOGBUF_SZ] = "";
140 static pthread_mutex_t logbuf_mutex = PTHREAD_MUTEX_INITIALIZER;
141 #endif
142 
143 static void
144 xstrftime_poststart(char *buf, size_t bufsize, struct timeval *time)
145 {
146 	long sec, usec;
147 
148 	sec = time->tv_sec - st->st_start_time.tv_sec;
149 	usec = time->tv_usec - st->st_start_time.tv_usec;
150 
151 	if (usec < 0) {
152 		sec -= 1;
153 		usec += 1000000;
154 	}
155 
156 	(void) snprintf(buf, bufsize, "start + %d.%02ds", sec, usec / 10000);
157 }
158 
159 static void
160 vlog_prefix(int severity, const char *prefix, const char *format, va_list args)
161 {
162 	char buf[512], *cp;
163 	char timebuf[LOG_DATE_SIZE];
164 	struct timeval now;
165 	struct tm ltime;
166 
167 #ifdef NDEBUG
168 	if (severity > st->st_log_level_min)
169 		return;
170 #endif
171 
172 	if (gettimeofday(&now, NULL) != 0)
173 		(void) fprintf(stderr, "gettimeofday(3C) failed: %s\n",
174 		    strerror(errno));
175 
176 	if (st->st_log_timezone_known)
177 		(void) strftime(timebuf, sizeof (timebuf), "%b %e %T",
178 		    localtime_r(&now.tv_sec, &ltime));
179 	else
180 		xstrftime_poststart(timebuf, sizeof (timebuf), &now);
181 
182 	(void) snprintf(buf, sizeof (buf), "%s/%d%s: ", timebuf, pthread_self(),
183 	    prefix);
184 	cp = strchr(buf, '\0');
185 	(void) vsnprintf(cp, sizeof (buf) - (cp - buf), format, args);
186 
187 #ifndef NDEBUG
188 	/* Copy into logbuf. */
189 	(void) pthread_mutex_lock(&logbuf_mutex);
190 	if (strlen(logbuf) + strlen(buf) + 1 <= sizeof (logbuf))
191 		(void) strcat(logbuf, buf);
192 	else
193 		(void) strlcpy(logbuf, buf, sizeof (logbuf));
194 	(void) pthread_mutex_unlock(&logbuf_mutex);
195 
196 	if (severity > st->st_log_level_min)
197 		return;
198 #endif
199 
200 	if (st->st_log_flags & STARTD_LOG_FILE && logfile) {
201 		(void) fputs(buf, logfile);
202 		(void) fflush(logfile);
203 	}
204 	if (st->st_log_flags & STARTD_LOG_TERMINAL)
205 		(void) fputs(buf, stdout);
206 	if (st->st_log_flags & STARTD_LOG_SYSLOG && st->st_log_timezone_known)
207 		vsyslog(severity, format, args);
208 }
209 
210 /*PRINTFLIKE2*/
211 void
212 log_error(int severity, const char *format, ...)
213 {
214 	va_list args;
215 
216 	va_start(args, format);
217 	vlog_prefix(severity, " ERROR", format, args);
218 	va_end(args);
219 }
220 
221 /*PRINTFLIKE2*/
222 void
223 log_framework(int severity, const char *format, ...)
224 {
225 	va_list args;
226 
227 	va_start(args, format);
228 	vlog_prefix(severity, "", format, args);
229 	va_end(args);
230 }
231 
232 /*
233  * void log_preexec()
234  *
235  * log_preexec() should be invoked prior to any exec(2) calls, to prevent the
236  * logfile and syslogd file descriptors from being leaked to child processes.
237  * Why openlog(3C) lacks a close-on-exec option is a minor mystery.
238  */
239 void
240 log_preexec()
241 {
242 	closelog();
243 }
244 
245 /*
246  * void setlog()
247  *   Close file descriptors and redirect output.
248  */
249 void
250 setlog(const char *logstem)
251 {
252 	int fd;
253 	char logfile[PATH_MAX];
254 
255 	closefrom(0);
256 
257 	(void) open("/dev/null", O_RDONLY);
258 
259 	(void) snprintf(logfile, PATH_MAX, "%s/%s", st->st_log_prefix, logstem);
260 
261 	(void) umask(fmask);
262 	fd = open(logfile, O_WRONLY|O_CREAT|O_APPEND,
263 	    S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
264 	(void) umask(dmask);
265 
266 	if (fd == -1)
267 		return;
268 
269 	(void) dup2(fd, 1);
270 	(void) dup2(fd, 2);
271 
272 	if (fd != 1 && fd != 2)
273 		startd_close(fd);
274 }
275 
276 static int
277 log_dir_writeable(const char *path)
278 {
279 	int fd;
280 	struct statvfs svb;
281 
282 	if ((fd = open(path, O_RDONLY, 0644)) == -1)
283 		return (-1);
284 
285 	if (fstatvfs(fd, &svb) == -1)
286 		return (-1);
287 
288 	if (svb.f_flag & ST_RDONLY) {
289 		(void) close(fd);
290 
291 		fd = -1;
292 	}
293 
294 	return (fd);
295 }
296 
297 static void
298 vlog_instance(const char *fmri, const char *logstem, boolean_t canlog,
299     const char *format, va_list args)
300 {
301 	char logfile[PATH_MAX];
302 	char *message;
303 	char omessage[1024];
304 	int fd, err;
305 	char timebuf[LOG_DATE_SIZE];
306 	struct tm ltime;
307 	struct timeval now;
308 
309 	(void) snprintf(logfile, PATH_MAX, "%s/%s", st->st_log_prefix,
310 	    logstem);
311 
312 	(void) umask(fmask);
313 	fd = open(logfile, O_WRONLY|O_CREAT|O_APPEND,
314 	    S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
315 	err = errno;
316 	(void) umask(dmask);
317 
318 	if (fd == -1) {
319 		if (canlog)
320 			log_error(LOG_NOTICE, "Could not log for %s: open(%s) "
321 			    "failed with %s.\n", fmri, logfile, strerror(err));
322 
323 		return;
324 	}
325 
326 	(void) vsnprintf(omessage, sizeof (omessage), format, args);
327 
328 	if (gettimeofday(&now, NULL) != 0)
329 		(void) fprintf(stderr, "gettimeofday(3C) failed: %s\n",
330 		    strerror(errno));
331 
332 	if (st->st_log_timezone_known)
333 		(void) strftime(timebuf, sizeof (timebuf), "%b %e %T",
334 		    localtime_r(&now.tv_sec, &ltime));
335 	else
336 		xstrftime_poststart(timebuf, sizeof (timebuf), &now);
337 
338 	message = uu_msprintf("[ %s %s ]\n", timebuf, omessage);
339 
340 	if (message == NULL) {
341 		if (canlog)
342 			log_error(LOG_NOTICE, "Could not log for %s: %s.\n",
343 			    fmri, uu_strerror(uu_error()));
344 	} else {
345 		if (write(fd, message, strlen(message)) < 0 && canlog)
346 			log_error(LOG_NOTICE, "Could not log for %s: write(%d) "
347 			    "failed with %s.\n", fmri, fd,
348 			    strerror(errno));
349 
350 		uu_free(message);
351 	}
352 
353 	if (close(fd) != 0 && canlog)
354 		log_framework(LOG_NOTICE, "close(%d) failed: %s.\n", fd,
355 		    strerror(errno));
356 }
357 
358 /*
359  * void log_instance(const restarter_inst_t *, boolean_t, const char *, ...)
360  *
361  * The log_instance() format is "[ month day time message ]".  (The
362  * brackets distinguish svc.startd messages from method output.)  We avoid
363  * calling log_*() functions on error when canlog is not set, since we may
364  * be called from a child process.
365  *
366  * When adding new calls to this function, consider: If this is called before
367  * any instances have started, then it should be called with canlog clear,
368  * lest we spew errors to the console when booted on the miniroot.
369  */
370 /*PRINTFLIKE3*/
371 void
372 log_instance(const restarter_inst_t *inst, boolean_t canlog,
373     const char *format, ...)
374 {
375 	va_list args;
376 
377 	va_start(args, format);
378 	vlog_instance(inst->ri_i.i_fmri, inst->ri_logstem, canlog, format,
379 	    args);
380 	va_end(args);
381 }
382 
383 /*
384  * void log_instance_fmri(const char *, const char *,boolean_t, const char *,
385  *    ...)
386  *
387  * The log_instance_fmri() format is "[ month day time message ]".  (The
388  * brackets distinguish svc.startd messages from method output.)  We avoid
389  * calling log_*() functions on error when canlog is not set, since we may
390  * be called from a child process.
391  *
392  * For new calls to this function, see the warning in log_instance()'s
393  * comment.
394  */
395 /*PRINTFLIKE4*/
396 void
397 log_instance_fmri(const char *fmri, const char *logstem, boolean_t canlog,
398     const char *format, ...)
399 {
400 	va_list args;
401 
402 	va_start(args, format);
403 	vlog_instance(fmri, logstem, canlog, format, args);
404 	va_end(args);
405 }
406 
407 /*
408  * void log_transition(const restarter_inst_t *, start_outcome_t)
409  *
410  * The log_transition() format is
411  *
412  *   [ _service_fmri_ _participle_ (_common_name_) ]
413  *
414  * Again, brackets separate messages from specific service instance output to
415  * the console.
416  */
417 void
418 log_transition(const restarter_inst_t *inst, start_outcome_t outcome)
419 {
420 	char *message;
421 	char omessage[1024];
422 	char *action;
423 	int severity;
424 
425 	if (outcome == START_REQUESTED) {
426 		char *cname = NULL;
427 
428 		cname = inst->ri_common_name;
429 		if (cname == NULL)
430 			cname = inst->ri_C_common_name;
431 
432 		if (!(st->st_boot_flags & STARTD_BOOT_VERBOSE))
433 			return;
434 
435 		if (inst->ri_start_index > 1)
436 			return;
437 
438 		if (cname)
439 			(void) snprintf(omessage, sizeof (omessage), " (%s)",
440 			cname);
441 		else
442 			*omessage = '\0';
443 
444 		action = gettext("starting");
445 
446 		message = uu_msprintf("[ %s %s%s ]\n",
447 		    inst->ri_i.i_fmri + strlen("svc:/"), action,
448 		    omessage);
449 
450 		severity = LOG_INFO;
451 	} else {
452 		switch (outcome) {
453 		case MAINT_REQUESTED:
454 			action = gettext("transitioned to maintenance by "
455 			    "request (see 'svcs -xv' for details)");
456 			break;
457 		case START_FAILED_REPEATEDLY:
458 			action = gettext("failed repeatedly: transitioned to "
459 			    "maintenance (see 'svcs -xv' for details)");
460 			break;
461 		case START_FAILED_CONFIGURATION:
462 			action = gettext("misconfigured: transitioned to "
463 			    "maintenance (see 'svcs -xv' for details)");
464 			break;
465 		case START_FAILED_FATAL:
466 			action = gettext("failed fatally: transitioned to "
467 			    "maintenance (see 'svcs -xv' for details)");
468 			break;
469 		case START_FAILED_TIMEOUT_FATAL:
470 			action = gettext("timed out: transitioned to "
471 			    "maintenance (see 'svcs -xv' for details)");
472 			break;
473 		case START_FAILED_OTHER:
474 			action = gettext("failed: transitioned to "
475 			    "maintenance (see 'svcs -xv' for details)");
476 			break;
477 		case START_REQUESTED:
478 			assert(outcome != START_REQUESTED);
479 			/*FALLTHROUGH*/
480 		default:
481 			action = gettext("outcome unknown?");
482 		}
483 
484 		message = uu_msprintf("[ %s %s ]\n",
485 		    inst->ri_i.i_fmri + strlen("svc:/"), action);
486 
487 		severity = LOG_ERR;
488 	}
489 
490 
491 	if (message == NULL) {
492 		log_error(LOG_NOTICE,
493 		    "Could not log boot message for %s: %s.\n",
494 		    inst->ri_i.i_fmri, uu_strerror(uu_error()));
495 	} else {
496 		/*
497 		 * All significant errors should to go to syslog to
498 		 * communicate appropriate information even for systems
499 		 * without a console connected during boot.  Send the
500 		 * message to stderr only if the severity is lower than
501 		 * (indicated by >) LOG_ERR.
502 		 */
503 		if (!st->st_log_login_reached && severity > LOG_ERR) {
504 			/*LINTED*/
505 			if (fprintf(stderr, message) < 0)
506 				log_error(LOG_NOTICE, "Could not log for %s: "
507 				    "fprintf() failed with %s.\n",
508 				    inst->ri_i.i_fmri, strerror(errno));
509 		} else {
510 			log_framework(severity, "%s %s\n",
511 			    inst->ri_i.i_fmri + strlen("svc:/"), action);
512 		}
513 
514 		uu_free(message);
515 	}
516 }
517 
518 /*
519  * log_console - log a message to the consoles and to syslog
520  *
521  * This logs a message as-is to the console (and auxiliary consoles),
522  * as well as to the master restarter log.
523  */
524 /*PRINTFLIKE2*/
525 void
526 log_console(int severity, const char *format, ...)
527 {
528 	va_list args;
529 
530 	va_start(args, format);
531 	vlog_prefix(severity, "", format, args);
532 	va_end(args);
533 
534 	va_start(args, format);
535 	(void) vfprintf(stderr, format, args);
536 	va_end(args);
537 }
538 
539 /*
540  * void log_init()
541  *
542  * Set up the log files, if necessary, for the current invocation.  This
543  * function should be called before any other functions in this file.  Set the
544  * syslog(3C) logging mask such that severities of the importance of
545  * LOG_NOTICE and above are passed through, but lower severity messages are
546  * masked out.
547  *
548  * It may be called multiple times to change the logging configuration due to
549  * administrative request.
550  */
551 void
552 log_init()
553 {
554 	int dirfd, logfd;
555 	char *dir;
556 	struct stat sb;
557 
558 	if (st->st_start_time.tv_sec == 0) {
559 		if (getzoneid() != GLOBAL_ZONEID) {
560 			st->st_start_time.tv_sec = time(NULL);
561 		} else {
562 			/*
563 			 * We need to special-case the BOOT_TIME utmp entry, and
564 			 * drag that value out of the kernel if it's there.
565 			 */
566 			kstat_ctl_t *kc;
567 			kstat_t *ks;
568 			kstat_named_t *boot;
569 
570 			if (((kc = kstat_open()) != 0) &&
571 			    ((ks = kstat_lookup(kc, "unix", 0, "system_misc"))
572 			    != NULL) &&
573 			    (kstat_read(kc, ks, NULL) != -1) &&
574 			    ((boot = kstat_data_lookup(ks, "boot_time")) !=
575 			    NULL)) {
576 				/*
577 				 * If we're here, then we've successfully found
578 				 * the boot_time kstat... use its value.
579 				 */
580 				st->st_start_time.tv_sec = boot->value.ul;
581 			} else {
582 				st->st_start_time.tv_sec = time(NULL);
583 			}
584 
585 			if (kc)
586 				(void) kstat_close(kc);
587 		}
588 	}
589 
590 	/*
591 	 * Establish our timezone if the appropriate directory is available.
592 	 */
593 	if (!st->st_log_timezone_known && stat(FS_TIMEZONE_DIR, &sb) == 0) {
594 		tzset();
595 		st->st_log_timezone_known = 1;
596 	}
597 
598 	/*
599 	 * Establish our locale if the appropriate directory is available.  Set
600 	 * the locale string from the environment so we can extract template
601 	 * information correctly, if the locale directories aren't yet
602 	 * available.
603 	 */
604 	if (st->st_locale != NULL)
605 		free(st->st_locale);
606 
607 	if ((st->st_locale = getenv("LC_ALL")) == NULL)
608 		if ((st->st_locale = getenv("LC_MESSAGES")) == NULL)
609 			st->st_locale = getenv("LANG");
610 
611 	if (!st->st_log_locale_known && stat(FS_LOCALE_DIR, &sb) == 0) {
612 		(void) setlocale(LC_ALL, "");
613 		st->st_locale = setlocale(LC_MESSAGES, "");
614 		if (st->st_locale)
615 			st->st_log_locale_known = 1;
616 
617 		(void) textdomain(TEXT_DOMAIN);
618 	}
619 
620 	if (st->st_locale) {
621 		st->st_locale = safe_strdup(st->st_locale);
622 		xstr_sanitize(st->st_locale);
623 	}
624 
625 	if (logfile) {
626 		(void) fclose(logfile);
627 		logfile = NULL;
628 	}
629 
630 	/*
631 	 * Set syslog(3C) behaviour in all cases.
632 	 */
633 	closelog();
634 	openlog("svc.startd", LOG_PID | LOG_CONS, LOG_DAEMON);
635 	(void) setlogmask(LOG_UPTO(LOG_NOTICE));
636 
637 	if ((dirfd = log_dir_writeable(LOG_PREFIX_NORMAL)) == -1) {
638 		if ((dirfd = log_dir_writeable(LOG_PREFIX_EARLY)) == -1)
639 			return;
640 		else
641 			dir = LOG_PREFIX_EARLY;
642 	} else {
643 		dir = LOG_PREFIX_NORMAL;
644 	}
645 
646 	st->st_log_prefix = dir;
647 
648 	(void) umask(fmask);
649 	if ((logfd = openat(dirfd, STARTD_DEFAULT_LOG, O_CREAT | O_RDWR,
650 	    0644)) == -1) {
651 		(void) close(dirfd);
652 		(void) umask(dmask);
653 		return;
654 	}
655 
656 	(void) close(dirfd);
657 	(void) umask(dmask);
658 
659 	if ((logfile = fdopen(logfd, "a")) == NULL)
660 		if (errno != EROFS)
661 			log_error(LOG_WARNING, "can't open logfile %s/%s",
662 			    dir, STARTD_DEFAULT_LOG);
663 
664 	if (logfile &&
665 	    fcntl(fileno(logfile), F_SETFD, FD_CLOEXEC) == -1)
666 		log_error(LOG_WARNING,
667 		    "couldn't mark logfile close-on-exec: %s\n",
668 		    strerror(errno));
669 }
670