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