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, <ime)); 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, <ime)); 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