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