xref: /titanic_44/usr/src/cmd/svc/startd/log.c (revision 6dfee4834394825da35b977ca71cdc965bc7b6a4)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*
30  * log.c - debugging and logging functions
31  *
32  * Logging destinations
33  *   svc.startd(1M) supports three logging destinations:  the system log, a
34  *   daemon-specific log (in the /var/svc/log hierarchy by default), and to the
35  *   standard output.  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 	if (st->st_log_flags & STARTD_LOG_TERMINAL)
203 		(void) fputs(buf, stdout);
204 
205 	if (st->st_log_timezone_known)
206 		vsyslog(severity, format, args);
207 
208 	if (st->st_log_flags & STARTD_LOG_FILE && logfile)
209 		(void) fflush(logfile);
210 }
211 
212 /*PRINTFLIKE2*/
213 void
214 log_error(int severity, const char *format, ...)
215 {
216 	va_list args;
217 
218 	va_start(args, format);
219 	vlog_prefix(severity, " ERROR", format, args);
220 	va_end(args);
221 }
222 
223 /*PRINTFLIKE2*/
224 void
225 log_framework(int severity, const char *format, ...)
226 {
227 	va_list args;
228 
229 	va_start(args, format);
230 	vlog_prefix(severity, "", format, args);
231 	va_end(args);
232 }
233 
234 /*
235  * void log_preexec()
236  *
237  * log_preexec() should be invoked prior to any exec(2) calls, to prevent the
238  * logfile and syslogd file descriptors from being leaked to child processes.
239  * Why openlog(3C) lacks a close-on-exec option is a minor mystery.
240  */
241 void
242 log_preexec()
243 {
244 	closelog();
245 }
246 
247 /*
248  * void setlog()
249  *   Close file descriptors and redirect output.
250  */
251 void
252 setlog(const char *logstem)
253 {
254 	int fd;
255 	char logfile[PATH_MAX];
256 
257 	closefrom(0);
258 
259 	(void) open("/dev/null", O_RDONLY);
260 
261 	(void) snprintf(logfile, PATH_MAX, "%s/%s", st->st_log_prefix, logstem);
262 
263 	(void) umask(fmask);
264 	fd = open(logfile, O_WRONLY|O_CREAT|O_APPEND,
265 	    S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
266 	(void) umask(dmask);
267 
268 	if (fd == -1)
269 		return;
270 
271 	(void) dup2(fd, 1);
272 	(void) dup2(fd, 2);
273 
274 	if (fd != 1 && fd != 2)
275 		startd_close(fd);
276 }
277 
278 static int
279 log_dir_writeable(const char *path)
280 {
281 	int fd;
282 	struct statvfs svb;
283 
284 	if ((fd = open(path, O_RDONLY, 0644)) == -1)
285 		return (-1);
286 
287 	if (fstatvfs(fd, &svb) == -1)
288 		return (-1);
289 
290 	if (svb.f_flag & ST_RDONLY) {
291 		(void) close(fd);
292 
293 		fd = -1;
294 	}
295 
296 	return (fd);
297 }
298 
299 static void
300 vlog_instance(const char *fmri, const char *logstem, boolean_t canlog,
301     const char *format, va_list args)
302 {
303 	char logfile[PATH_MAX];
304 	char *message;
305 	char omessage[1024];
306 	int fd, err;
307 	char timebuf[LOG_DATE_SIZE];
308 	struct tm ltime;
309 	struct timeval now;
310 
311 	(void) snprintf(logfile, PATH_MAX, "%s/%s", st->st_log_prefix,
312 	    logstem);
313 
314 	(void) umask(fmask);
315 	fd = open(logfile, O_WRONLY|O_CREAT|O_APPEND,
316 	    S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
317 	err = errno;
318 	(void) umask(dmask);
319 
320 	if (fd == -1) {
321 		if (canlog)
322 			log_error(LOG_NOTICE, "Could not log for %s: open(%s) "
323 			    "failed with %s.\n", fmri, logfile, strerror(err));
324 
325 		return;
326 	}
327 
328 	(void) vsnprintf(omessage, sizeof (omessage), format, args);
329 
330 	if (gettimeofday(&now, NULL) != 0)
331 		(void) fprintf(stderr, "gettimeofday(3C) failed: %s\n",
332 		    strerror(errno));
333 
334 	if (st->st_log_timezone_known)
335 		(void) strftime(timebuf, sizeof (timebuf), "%b %e %T",
336 		    localtime_r(&now.tv_sec, &ltime));
337 	else
338 		xstrftime_poststart(timebuf, sizeof (timebuf), &now);
339 
340 	message = uu_msprintf("[ %s %s ]\n", timebuf, omessage);
341 
342 	if (message == NULL) {
343 		if (canlog)
344 			log_error(LOG_NOTICE, "Could not log for %s: %s.\n",
345 			    fmri, uu_strerror(uu_error()));
346 	} else {
347 		if (write(fd, message, strlen(message)) < 0 && canlog)
348 			log_error(LOG_NOTICE, "Could not log for %s: write(%d) "
349 			    "failed with %s.\n", fmri, fd,
350 			    strerror(errno));
351 
352 		uu_free(message);
353 	}
354 
355 	if (close(fd) != 0 && canlog)
356 		log_framework(LOG_NOTICE, "close(%d) failed: %s.\n", fd,
357 		    strerror(errno));
358 }
359 
360 /*
361  * void log_instance(const restarter_inst_t *, boolean_t, const char *, ...)
362  *
363  * The log_instance() format is "[ month day time message ]".  (The
364  * brackets distinguish svc.startd messages from method output.)  We avoid
365  * calling log_*() functions on error when canlog is not set, since we may
366  * be called from a child process.
367  *
368  * When adding new calls to this function, consider: If this is called before
369  * any instances have started, then it should be called with canlog clear,
370  * lest we spew errors to the console when booted on the miniroot.
371  */
372 /*PRINTFLIKE3*/
373 void
374 log_instance(const restarter_inst_t *inst, boolean_t canlog,
375     const char *format, ...)
376 {
377 	va_list args;
378 
379 	va_start(args, format);
380 	vlog_instance(inst->ri_i.i_fmri, inst->ri_logstem, canlog, format,
381 	    args);
382 	va_end(args);
383 }
384 
385 /*
386  * void log_instance_fmri(const char *, const char *,boolean_t, const char *,
387  *    ...)
388  *
389  * The log_instance_fmri() format is "[ month day time message ]".  (The
390  * brackets distinguish svc.startd messages from method output.)  We avoid
391  * calling log_*() functions on error when canlog is not set, since we may
392  * be called from a child process.
393  *
394  * For new calls to this function, see the warning in log_instance()'s
395  * comment.
396  */
397 /*PRINTFLIKE4*/
398 void
399 log_instance_fmri(const char *fmri, const char *logstem, boolean_t canlog,
400     const char *format, ...)
401 {
402 	va_list args;
403 
404 	va_start(args, format);
405 	vlog_instance(fmri, logstem, canlog, format, args);
406 	va_end(args);
407 }
408 
409 /*
410  * void log_transition(const restarter_inst_t *, start_outcome_t)
411  *
412  * The log_transition() format is
413  *
414  *   [ _service_fmri_ _participle_ (_common_name_) ]
415  *
416  * Again, brackets separate messages from specific service instance output to
417  * the console.
418  */
419 void
420 log_transition(const restarter_inst_t *inst, start_outcome_t outcome)
421 {
422 	char *message;
423 	char omessage[1024];
424 	char *action;
425 	int severity;
426 
427 	if (outcome == START_REQUESTED) {
428 		char *cname = NULL;
429 
430 		cname = inst->ri_common_name;
431 		if (cname == NULL)
432 			cname = inst->ri_C_common_name;
433 
434 		if (!(st->st_boot_flags & STARTD_BOOT_VERBOSE))
435 			return;
436 
437 		if (inst->ri_start_index > 1)
438 			return;
439 
440 		if (cname)
441 			(void) snprintf(omessage, sizeof (omessage), " (%s)",
442 			cname);
443 		else
444 			*omessage = '\0';
445 
446 		action = gettext("starting");
447 
448 		message = uu_msprintf("[ %s %s%s ]\n",
449 		    inst->ri_i.i_fmri + strlen("svc:/"), action,
450 		    omessage);
451 
452 		severity = LOG_INFO;
453 	} else {
454 		switch (outcome) {
455 		case START_FAILED_REPEATEDLY:
456 			action = gettext("failed repeatedly");
457 			break;
458 		case START_FAILED_CONFIGURATION:
459 			action = gettext("misconfigured");
460 			break;
461 		case START_FAILED_FATAL:
462 			action = gettext("failed fatally");
463 			break;
464 		case START_FAILED_TIMEOUT_FATAL:
465 			action = gettext("timed out, fault threshold reached");
466 			break;
467 		case START_FAILED_OTHER:
468 			action = gettext("failed");
469 			break;
470 		case START_REQUESTED:
471 			assert(outcome != START_REQUESTED);
472 			/*FALLTHROUGH*/
473 		default:
474 			action = gettext("outcome unknown?");
475 		}
476 
477 		message = uu_msprintf("[ %s %s %s ]\n",
478 		    inst->ri_i.i_fmri + strlen("svc:/"), action,
479 		    gettext("(see 'svcs -x' for details)"));
480 
481 		severity = LOG_ERR;
482 	}
483 
484 
485 	if (message == NULL) {
486 		log_error(LOG_NOTICE,
487 		    "Could not log boot message for %s: %s.\n",
488 		    inst->ri_i.i_fmri, uu_strerror(uu_error()));
489 	} else {
490 		if (!st->st_log_login_reached) {
491 			/*LINTED*/
492 			if (fprintf(stderr, message) < 0)
493 				log_error(LOG_NOTICE, "Could not log for %s: "
494 				    "fprintf() failed with %s.\n",
495 				    inst->ri_i.i_fmri, strerror(errno));
496 		} else {
497 			log_framework(severity, "%s %s\n",
498 			    inst->ri_i.i_fmri + strlen("svc:/"), action);
499 		}
500 
501 		uu_free(message);
502 	}
503 }
504 
505 /*
506  * log_console - log a message to the consoles and to syslog
507  *
508  * This logs a message as-is to the console (and auxiliary consoles),
509  * as well as to the master restarter log.
510  */
511 /*PRINTFLIKE2*/
512 void
513 log_console(int severity, const char *format, ...)
514 {
515 	va_list args;
516 
517 	va_start(args, format);
518 	vlog_prefix(severity, "", format, args);
519 	va_end(args);
520 
521 	va_start(args, format);
522 	(void) vfprintf(stderr, format, args);
523 	va_end(args);
524 }
525 
526 /*
527  * void log_init()
528  *
529  * Set up the log files, if necessary, for the current invocation.  This
530  * function should be called before any other functions in this file.  Set the
531  * syslog(3C) logging mask such that severities of the importance of
532  * LOG_NOTICE and above are passed through, but lower severity messages are
533  * masked out.
534  *
535  * It may be called multiple times to change the logging configuration due to
536  * administrative request.
537  */
538 void
539 log_init()
540 {
541 	int dirfd, logfd;
542 	char *dir;
543 	struct stat sb;
544 
545 	if (st->st_start_time.tv_sec == 0) {
546 		if (getzoneid() != GLOBAL_ZONEID) {
547 			st->st_start_time.tv_sec = time(NULL);
548 		} else {
549 			/*
550 			 * We need to special-case the BOOT_TIME utmp entry, and
551 			 * drag that value out of the kernel if it's there.
552 			 */
553 			kstat_ctl_t *kc;
554 			kstat_t *ks;
555 			kstat_named_t *boot;
556 
557 			if (((kc = kstat_open()) != 0) &&
558 			    ((ks = kstat_lookup(kc, "unix", 0, "system_misc"))
559 			    != NULL) &&
560 			    (kstat_read(kc, ks, NULL) != -1) &&
561 			    ((boot = kstat_data_lookup(ks, "boot_time")) !=
562 			    NULL)) {
563 				/*
564 				 * If we're here, then we've successfully found
565 				 * the boot_time kstat... use its value.
566 				 */
567 				st->st_start_time.tv_sec = boot->value.ul;
568 			} else {
569 				st->st_start_time.tv_sec = time(NULL);
570 			}
571 
572 			if (kc)
573 				(void) kstat_close(kc);
574 		}
575 	}
576 
577 	/*
578 	 * Establish our timezone if the appropriate directory is available.
579 	 */
580 	if (!st->st_log_timezone_known && stat(FS_TIMEZONE_DIR, &sb) == 0) {
581 		tzset();
582 		st->st_log_timezone_known = 1;
583 	}
584 
585 	/*
586 	 * Establish our locale if the appropriate directory is available.  Set
587 	 * the locale string from the environment so we can extract template
588 	 * information correctly, if the locale directories aren't yet
589 	 * available.
590 	 */
591 	if (st->st_locale != NULL)
592 		free(st->st_locale);
593 
594 	if ((st->st_locale = getenv("LC_ALL")) == NULL)
595 		if ((st->st_locale = getenv("LC_MESSAGES")) == NULL)
596 			st->st_locale = getenv("LANG");
597 
598 	if (!st->st_log_locale_known && stat(FS_LOCALE_DIR, &sb) == 0) {
599 		(void) setlocale(LC_ALL, "");
600 		st->st_locale = setlocale(LC_MESSAGES, "");
601 		if (st->st_locale)
602 			st->st_log_locale_known = 1;
603 
604 		(void) textdomain(TEXT_DOMAIN);
605 	}
606 
607 	if (st->st_locale) {
608 		st->st_locale = safe_strdup(st->st_locale);
609 		xstr_sanitize(st->st_locale);
610 	}
611 
612 	if (logfile) {
613 		(void) fclose(logfile);
614 		logfile = NULL;
615 	}
616 
617 	/*
618 	 * Set syslog(3C) behaviour in all cases.
619 	 */
620 	closelog();
621 	openlog("svc.startd", LOG_PID | LOG_CONS, LOG_DAEMON);
622 	(void) setlogmask(LOG_UPTO(LOG_NOTICE));
623 
624 	if ((dirfd = log_dir_writeable(LOG_PREFIX_NORMAL)) == -1) {
625 		if ((dirfd = log_dir_writeable(LOG_PREFIX_EARLY)) == -1)
626 			return;
627 		else
628 			dir = LOG_PREFIX_EARLY;
629 	} else {
630 		dir = LOG_PREFIX_NORMAL;
631 	}
632 
633 	st->st_log_prefix = dir;
634 
635 	(void) umask(fmask);
636 	if ((logfd = openat(dirfd, STARTD_DEFAULT_LOG, O_CREAT | O_RDWR,
637 	    0644)) == -1) {
638 		(void) close(dirfd);
639 		(void) umask(dmask);
640 		return;
641 	}
642 
643 	(void) close(dirfd);
644 	(void) umask(dmask);
645 
646 	if ((logfile = fdopen(logfd, "a")) == NULL)
647 		if (errno != EROFS)
648 			log_error(LOG_WARNING, "can't open logfile %s/%s",
649 			    dir, STARTD_DEFAULT_LOG);
650 
651 	if (logfile &&
652 	    fcntl(fileno(logfile), F_SETFD, FD_CLOEXEC) == -1)
653 		log_error(LOG_WARNING,
654 		    "couldn't mark logfile close-on-exec: %s\n",
655 		    strerror(errno));
656 }
657