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 /* 23 * Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved. 24 */ 25 26 #include <sys/types.h> 27 #include <sys/param.h> 28 #include <sys/varargs.h> 29 #include <sys/systm.h> 30 #include <sys/cmn_err.h> 31 #include <sys/stream.h> 32 #include <sys/strsubr.h> 33 #include <sys/strsun.h> 34 #include <sys/sysmacros.h> 35 #include <sys/kmem.h> 36 #include <sys/log.h> 37 #include <sys/spl.h> 38 #include <sys/syslog.h> 39 #include <sys/console.h> 40 #include <sys/debug.h> 41 #include <sys/utsname.h> 42 #include <sys/id_space.h> 43 #include <sys/zone.h> 44 45 log_zone_t log_global; 46 queue_t *log_consq; 47 queue_t *log_backlogq; 48 queue_t *log_intrq; 49 50 #define LOG_PRISIZE 8 /* max priority size: 7 characters + null */ 51 #define LOG_FACSIZE 9 /* max priority size: 8 characters + null */ 52 53 static krwlock_t log_rwlock; 54 static int log_rwlock_depth; 55 static int log_seq_no[SL_CONSOLE + 1]; 56 static stdata_t log_fakestr; 57 static id_space_t *log_minorspace; 58 static log_t log_backlog; 59 static struct kmem_cache *log_cons_cache; /* log_t cache */ 60 61 static queue_t *log_recentq; 62 static queue_t *log_freeq; 63 64 static zone_key_t log_zone_key; 65 66 static char log_overflow_msg[] = "message overflow on /dev/log minor #%d%s\n"; 67 68 static char log_pri[LOG_PRIMASK + 1][LOG_PRISIZE] = { 69 "emerg", "alert", "crit", "error", 70 "warning", "notice", "info", "debug" 71 }; 72 73 static char log_fac[LOG_NFACILITIES + 1][LOG_FACSIZE] = { 74 "kern", "user", "mail", "daemon", 75 "auth", "syslog", "lpr", "news", 76 "uucp", "resv9", "resv10", "resv11", 77 "resv12", "audit", "resv14", "cron", 78 "local0", "local1", "local2", "local3", 79 "local4", "local5", "local6", "local7", 80 "unknown" 81 }; 82 static int log_cons_constructor(void *, void *, int); 83 static void log_cons_destructor(void *, void *); 84 85 /* 86 * Get exclusive access to the logging system; this includes all minor 87 * devices. We use an rwlock rather than a mutex because hold times 88 * are potentially long, so we don't want to waste cycles in adaptive mutex 89 * spin (rwlocks always block when contended). Note that we explicitly 90 * support recursive calls (e.g. printf() calls foo() calls printf()). 91 * 92 * Clients may use log_enter() / log_exit() to guarantee that a group 93 * of messages is treated atomically (i.e. they appear in order and are 94 * not interspersed with any other messages), e.g. for multiline printf(). 95 * 96 * This could probably be changed to a per-zone lock if contention becomes 97 * an issue. 98 */ 99 void 100 log_enter(void) 101 { 102 if (rw_owner(&log_rwlock) != curthread) 103 rw_enter(&log_rwlock, RW_WRITER); 104 log_rwlock_depth++; 105 } 106 107 void 108 log_exit(void) 109 { 110 if (--log_rwlock_depth == 0) 111 rw_exit(&log_rwlock); 112 } 113 114 void 115 log_flushq(queue_t *q) 116 { 117 mblk_t *mp; 118 log_t *lp = (log_t *)q->q_ptr; 119 120 /* lp will be NULL if the queue was created via log_makeq */ 121 while ((mp = getq_noenab(q, 0)) != NULL) 122 log_sendmsg(mp, lp == NULL ? GLOBAL_ZONEID : lp->log_zoneid); 123 } 124 125 /* 126 * Create a minimal queue with just enough fields filled in to support 127 * canput(9F), putq(9F), and getq_noenab(9F). We set QNOENB to ensure 128 * that the queue will never be enabled. 129 */ 130 static queue_t * 131 log_makeq(size_t lowat, size_t hiwat, void *ibc) 132 { 133 queue_t *q; 134 135 q = kmem_zalloc(sizeof (queue_t), KM_SLEEP); 136 q->q_stream = &log_fakestr; 137 q->q_flag = QISDRV | QMTSAFE | QNOENB | QREADR | QUSE; 138 q->q_nfsrv = q; 139 q->q_lowat = lowat; 140 q->q_hiwat = hiwat; 141 mutex_init(QLOCK(q), NULL, MUTEX_DRIVER, ibc); 142 143 return (q); 144 } 145 146 /* 147 * Initialize the log structure for a new zone. 148 */ 149 static void * 150 log_zoneinit(zoneid_t zoneid) 151 { 152 int i; 153 log_zone_t *lzp; 154 155 if (zoneid == GLOBAL_ZONEID) 156 lzp = &log_global; /* use statically allocated struct */ 157 else 158 lzp = kmem_zalloc(sizeof (log_zone_t), KM_SLEEP); 159 160 for (i = 0; i < LOG_NUMCLONES; i++) { 161 lzp->lz_clones[i].log_minor = 162 (minor_t)id_alloc(log_minorspace); 163 lzp->lz_clones[i].log_zoneid = zoneid; 164 } 165 return (lzp); 166 } 167 168 /*ARGSUSED*/ 169 static void 170 log_zonefree(zoneid_t zoneid, void *arg) 171 { 172 log_zone_t *lzp = arg; 173 int i; 174 175 ASSERT(lzp != &log_global && zoneid != GLOBAL_ZONEID); 176 if (lzp == NULL) 177 return; 178 for (i = 0; i < LOG_NUMCLONES; i++) 179 id_free(log_minorspace, lzp->lz_clones[i].log_minor); 180 kmem_free(lzp, sizeof (log_zone_t)); 181 } 182 183 void 184 log_init(void) 185 { 186 int log_maxzones; 187 188 /* 189 * Create a backlog queue to consume console messages during periods 190 * when there is no console reader (e.g. before syslogd(1M) starts). 191 */ 192 log_backlogq = log_consq = log_makeq(0, LOG_HIWAT, NULL); 193 194 /* 195 * Create a queue to hold free message of size <= LOG_MSGSIZE. 196 * Calls from high-level interrupt handlers will do a getq_noenab() 197 * from this queue, so its q_lock must be a maximum SPL spin lock. 198 */ 199 log_freeq = log_makeq(LOG_MINFREE, LOG_MAXFREE, (void *)ipltospl(SPL8)); 200 201 /* 202 * Create a queue for messages from high-level interrupt context. 203 * These messages are drained via softcall, or explicitly by panic(). 204 */ 205 log_intrq = log_makeq(0, LOG_HIWAT, (void *)ipltospl(SPL8)); 206 207 /* 208 * Create a queue to hold the most recent 8K of console messages. 209 * Useful for debugging. Required by the "$<msgbuf" adb macro. 210 */ 211 log_recentq = log_makeq(0, LOG_RECENTSIZE, NULL); 212 213 /* 214 * Create an id space for clone devices opened via /dev/log. 215 * Need to limit the number of zones to avoid exceeding the 216 * available minor number space. 217 */ 218 log_maxzones = (L_MAXMIN32 - LOG_LOGMIN) / LOG_NUMCLONES - 1; 219 if (log_maxzones < maxzones) 220 maxzones = log_maxzones; 221 log_minorspace = id_space_create("logminor_space", LOG_LOGMIN + 1, 222 L_MAXMIN32); 223 /* 224 * Put ourselves on the ZSD list. Note that zones have not been 225 * initialized yet, but our constructor will be called on the global 226 * zone when they are. 227 */ 228 zone_key_create(&log_zone_key, log_zoneinit, NULL, log_zonefree); 229 230 /* 231 * Initialize backlog structure. 232 */ 233 log_backlog.log_zoneid = GLOBAL_ZONEID; 234 log_backlog.log_minor = LOG_BACKLOG; 235 236 /* Allocate kmem cache for conslog's log structures */ 237 log_cons_cache = kmem_cache_create("log_cons_cache", 238 sizeof (struct log), 0, log_cons_constructor, log_cons_destructor, 239 NULL, NULL, NULL, 0); 240 241 /* 242 * Let the logging begin. 243 */ 244 log_update(&log_backlog, log_backlogq, SL_CONSOLE, log_console); 245 246 /* 247 * Now that logging is enabled, emit the SunOS banner. 248 */ 249 printf("\rSunOS Release %s Version %s %u-bit\n", 250 utsname.release, utsname.version, NBBY * (uint_t)sizeof (void *)); 251 printf("Copyright (c) 1983, 2010, Oracle and/or its affiliates. " 252 "All rights reserved.\n"); 253 #ifdef DEBUG 254 printf("DEBUG enabled\n"); 255 #endif 256 } 257 258 /* 259 * Allocate a log device corresponding to supplied device type. 260 * Both devices are clonable. /dev/log devices are allocated per zone. 261 * /dev/conslog devices are allocated from kmem cache. 262 */ 263 log_t * 264 log_alloc(minor_t type) 265 { 266 zone_t *zptr = curproc->p_zone; 267 log_zone_t *lzp; 268 log_t *lp; 269 int i; 270 minor_t minor; 271 272 if (type == LOG_CONSMIN) { 273 274 /* 275 * Return a write-only /dev/conslog device. 276 * No point allocating log_t until there's a free minor number. 277 */ 278 minor = (minor_t)id_alloc(log_minorspace); 279 lp = kmem_cache_alloc(log_cons_cache, KM_SLEEP); 280 lp->log_minor = minor; 281 return (lp); 282 } else { 283 ASSERT(type == LOG_LOGMIN); 284 285 lzp = zone_getspecific(log_zone_key, zptr); 286 ASSERT(lzp != NULL); 287 288 /* search for an available /dev/log device for the zone */ 289 for (i = LOG_LOGMINIDX; i <= LOG_LOGMAXIDX; i++) { 290 lp = &lzp->lz_clones[i]; 291 if (lp->log_inuse == 0) 292 break; 293 } 294 if (i > LOG_LOGMAXIDX) 295 lp = NULL; 296 else 297 /* Indicate which device type */ 298 lp->log_major = LOG_LOGMIN; 299 return (lp); 300 } 301 } 302 303 void 304 log_free(log_t *lp) 305 { 306 id_free(log_minorspace, lp->log_minor); 307 kmem_cache_free(log_cons_cache, lp); 308 } 309 310 /* 311 * Move console messages from src to dst. The time of day isn't known 312 * early in boot, so fix up the message timestamps if necessary. 313 */ 314 static void 315 log_conswitch(log_t *src, log_t *dst) 316 { 317 mblk_t *mp; 318 mblk_t *hmp = NULL; 319 mblk_t *tmp = NULL; 320 log_ctl_t *hlc; 321 322 while ((mp = getq_noenab(src->log_q, 0)) != NULL) { 323 log_ctl_t *lc = (log_ctl_t *)mp->b_rptr; 324 lc->flags |= SL_LOGONLY; 325 326 /* 327 * The ttime is written with 0 in log_sensmsg() only when 328 * good gethrestime_sec() data is not available to store in 329 * the log_ctl_t in the early boot phase. 330 */ 331 if (lc->ttime == 0) { 332 /* 333 * Look ahead to first early boot message with time. 334 */ 335 if (hmp) { 336 tmp->b_next = mp; 337 tmp = mp; 338 } else 339 hmp = tmp = mp; 340 continue; 341 } 342 343 while (hmp) { 344 tmp = hmp->b_next; 345 hmp->b_next = NULL; 346 hlc = (log_ctl_t *)hmp->b_rptr; 347 /* 348 * Calculate hrestime for an early log message with 349 * an invalid time stamp. We know: 350 * - the lbolt of the invalid time stamp. 351 * - the hrestime and lbolt of the first valid 352 * time stamp. 353 */ 354 hlc->ttime = lc->ttime - (lc->ltime - hlc->ltime) / hz; 355 (void) putq(dst->log_q, hmp); 356 hmp = tmp; 357 } 358 (void) putq(dst->log_q, mp); 359 } 360 while (hmp) { 361 tmp = hmp->b_next; 362 hmp->b_next = NULL; 363 hlc = (log_ctl_t *)hmp->b_rptr; 364 hlc->ttime = gethrestime_sec() - 365 (ddi_get_lbolt() - hlc->ltime) / hz; 366 (void) putq(dst->log_q, hmp); 367 hmp = tmp; 368 } 369 dst->log_overflow = src->log_overflow; 370 src->log_flags = 0; 371 dst->log_flags = SL_CONSOLE; 372 log_consq = dst->log_q; 373 } 374 375 /* 376 * Set the fields in the 'target' clone to the specified values. 377 * Then, look at all clones to determine which message types are 378 * currently active and which clone is the primary console queue. 379 * If the primary console queue changes to or from the backlog 380 * queue, copy all messages from backlog to primary or vice versa. 381 */ 382 void 383 log_update(log_t *target, queue_t *q, short flags, log_filter_t *filter) 384 { 385 log_t *lp; 386 short active = SL_CONSOLE; 387 zone_t *zptr = NULL; 388 log_zone_t *lzp; 389 zoneid_t zoneid = target->log_zoneid; 390 int i; 391 392 log_enter(); 393 394 if (q != NULL) 395 target->log_q = q; 396 target->log_wanted = filter; 397 target->log_flags = flags; 398 target->log_overflow = 0; 399 400 /* 401 * Need to special case the global zone here since this may be 402 * called before zone_init. 403 */ 404 if (zoneid == GLOBAL_ZONEID) { 405 lzp = &log_global; 406 } else if ((zptr = zone_find_by_id(zoneid)) == NULL) { 407 log_exit(); 408 return; /* zone is being destroyed, ignore update */ 409 } else { 410 lzp = zone_getspecific(log_zone_key, zptr); 411 } 412 ASSERT(lzp != NULL); 413 for (i = LOG_LOGMAXIDX; i >= LOG_LOGMINIDX; i--) { 414 lp = &lzp->lz_clones[i]; 415 if (zoneid == GLOBAL_ZONEID && (lp->log_flags & SL_CONSOLE)) 416 log_consq = lp->log_q; 417 active |= lp->log_flags; 418 } 419 lzp->lz_active = active; 420 421 if (zptr) 422 zone_rele(zptr); 423 424 if (log_consq == target->log_q) { 425 if (flags & SL_CONSOLE) 426 log_conswitch(&log_backlog, target); 427 else 428 log_conswitch(target, &log_backlog); 429 } 430 target->log_q = q; 431 432 log_exit(); 433 } 434 435 /*ARGSUSED*/ 436 int 437 log_error(log_t *lp, log_ctl_t *lc) 438 { 439 if ((lc->pri & LOG_FACMASK) == LOG_KERN) 440 lc->pri = LOG_KERN | LOG_ERR; 441 return (1); 442 } 443 444 int 445 log_trace(log_t *lp, log_ctl_t *lc) 446 { 447 trace_ids_t *tid = (trace_ids_t *)lp->log_data->b_rptr; 448 trace_ids_t *tidend = (trace_ids_t *)lp->log_data->b_wptr; 449 450 /* 451 * We use `tid + 1 <= tidend' here rather than the more traditional 452 * `tid < tidend', since the former ensures that there's at least 453 * `sizeof (trace_ids_t)' bytes available before executing the 454 * loop, whereas the latter only ensures that there's a single byte. 455 */ 456 for (; tid + 1 <= tidend; tid++) { 457 if (tid->ti_level < lc->level && tid->ti_level >= 0) 458 continue; 459 if (tid->ti_mid != lc->mid && tid->ti_mid >= 0) 460 continue; 461 if (tid->ti_sid != lc->sid && tid->ti_sid >= 0) 462 continue; 463 if ((lc->pri & LOG_FACMASK) == LOG_KERN) 464 lc->pri = LOG_KERN | LOG_DEBUG; 465 return (1); 466 } 467 return (0); 468 } 469 470 /*ARGSUSED*/ 471 int 472 log_console(log_t *lp, log_ctl_t *lc) 473 { 474 if ((lc->pri & LOG_FACMASK) == LOG_KERN) { 475 if (lc->flags & SL_FATAL) 476 lc->pri = LOG_KERN | LOG_CRIT; 477 else if (lc->flags & SL_ERROR) 478 lc->pri = LOG_KERN | LOG_ERR; 479 else if (lc->flags & SL_WARN) 480 lc->pri = LOG_KERN | LOG_WARNING; 481 else if (lc->flags & SL_NOTE) 482 lc->pri = LOG_KERN | LOG_NOTICE; 483 else if (lc->flags & SL_TRACE) 484 lc->pri = LOG_KERN | LOG_DEBUG; 485 else 486 lc->pri = LOG_KERN | LOG_INFO; 487 } 488 return (1); 489 } 490 491 mblk_t * 492 log_makemsg(int mid, int sid, int level, int sl, int pri, void *msg, 493 size_t size, int on_intr) 494 { 495 mblk_t *mp = NULL; 496 mblk_t *mp2; 497 log_ctl_t *lc; 498 499 if (size <= LOG_MSGSIZE && 500 (on_intr || log_freeq->q_count > log_freeq->q_lowat)) 501 mp = getq_noenab(log_freeq, 0); 502 503 if (mp == NULL) { 504 if (on_intr || 505 (mp = allocb(sizeof (log_ctl_t), BPRI_HI)) == NULL || 506 (mp2 = allocb(MAX(size, LOG_MSGSIZE), BPRI_HI)) == NULL) { 507 freemsg(mp); 508 return (NULL); 509 } 510 DB_TYPE(mp) = M_PROTO; 511 mp->b_wptr += sizeof (log_ctl_t); 512 mp->b_cont = mp2; 513 } else { 514 mp2 = mp->b_cont; 515 mp2->b_wptr = mp2->b_rptr; 516 } 517 518 lc = (log_ctl_t *)mp->b_rptr; 519 lc->mid = mid; 520 lc->sid = sid; 521 lc->level = level; 522 lc->flags = sl; 523 lc->pri = pri; 524 525 bcopy(msg, mp2->b_wptr, size - 1); 526 mp2->b_wptr[size - 1] = '\0'; 527 mp2->b_wptr += strlen((char *)mp2->b_wptr) + 1; 528 529 return (mp); 530 } 531 532 void 533 log_freemsg(mblk_t *mp) 534 { 535 mblk_t *mp2 = mp->b_cont; 536 537 ASSERT(MBLKL(mp) == sizeof (log_ctl_t)); 538 ASSERT(mp2->b_rptr == mp2->b_datap->db_base); 539 540 if ((log_freeq->q_flag & QFULL) == 0 && 541 MBLKL(mp2) <= LOG_MSGSIZE && MBLKSIZE(mp2) >= LOG_MSGSIZE) 542 (void) putq(log_freeq, mp); 543 else 544 freemsg(mp); 545 } 546 547 void 548 log_sendmsg(mblk_t *mp, zoneid_t zoneid) 549 { 550 log_t *lp; 551 char *src, *dst; 552 mblk_t *mp2 = mp->b_cont; 553 log_ctl_t *lc = (log_ctl_t *)mp->b_rptr; 554 int flags, fac; 555 off_t facility = 0; 556 off_t body = 0; 557 zone_t *zptr = NULL; 558 log_zone_t *lzp; 559 int i; 560 int backlog; 561 562 /* 563 * Need to special case the global zone here since this may be 564 * called before zone_init. 565 */ 566 if (zoneid == GLOBAL_ZONEID) { 567 lzp = &log_global; 568 } else if ((zptr = zone_find_by_id(zoneid)) == NULL) { 569 /* specified zone doesn't exist, free message and return */ 570 log_freemsg(mp); 571 return; 572 } else { 573 lzp = zone_getspecific(log_zone_key, zptr); 574 } 575 ASSERT(lzp != NULL); 576 577 if ((lc->flags & lzp->lz_active) == 0) { 578 if (zptr) 579 zone_rele(zptr); 580 log_freemsg(mp); 581 return; 582 } 583 584 if (panicstr) { 585 /* 586 * Raise the console queue's q_hiwat to ensure that we 587 * capture all panic messages. 588 */ 589 log_consq->q_hiwat = 2 * LOG_HIWAT; 590 log_consq->q_flag &= ~QFULL; 591 592 /* Message was created while panicking. */ 593 lc->flags |= SL_PANICMSG; 594 } 595 596 src = (char *)mp2->b_rptr; 597 dst = strstr(src, "FACILITY_AND_PRIORITY] "); 598 if (dst != NULL) { 599 facility = dst - src; 600 body = facility + 23; /* strlen("FACILITY_AND_PRIORITY] ") */ 601 } 602 603 log_enter(); 604 605 /* 606 * In the early boot phase hrestime is invalid, then timechanged is 0. 607 * If hrestime is not valid, the ttime is set to 0 here and the correct 608 * ttime is calculated in log_conswitch() later. The log_conswitch() 609 * calculation to determine the correct ttime does not use ttime data 610 * from these log_ctl_t structures; it only uses ttime from log_ctl_t's 611 * that contain good data. 612 * 613 */ 614 lc->ltime = ddi_get_lbolt(); 615 if (timechanged) { 616 lc->ttime = gethrestime_sec(); 617 } else { 618 lc->ttime = 0; 619 } 620 621 flags = lc->flags & lzp->lz_active; 622 log_seq_no[flags & SL_ERROR]++; 623 log_seq_no[flags & SL_TRACE]++; 624 log_seq_no[flags & SL_CONSOLE]++; 625 626 /* 627 * If this is in the global zone, start with the backlog, then 628 * walk through the clone logs. If not, just do the clone logs. 629 */ 630 backlog = (zoneid == GLOBAL_ZONEID); 631 i = LOG_LOGMINIDX; 632 while (i <= LOG_LOGMAXIDX) { 633 if (backlog) { 634 /* 635 * Do the backlog this time, then start on the 636 * others. 637 */ 638 backlog = 0; 639 lp = &log_backlog; 640 } else { 641 lp = &lzp->lz_clones[i++]; 642 } 643 644 if ((lp->log_flags & flags) && lp->log_wanted(lp, lc)) { 645 if (canput(lp->log_q)) { 646 lp->log_overflow = 0; 647 lc->seq_no = log_seq_no[lp->log_flags]; 648 if ((mp2 = copymsg(mp)) == NULL) 649 break; 650 if (facility != 0) { 651 src = (char *)mp2->b_cont->b_rptr; 652 dst = src + facility; 653 fac = (lc->pri & LOG_FACMASK) >> 3; 654 dst += snprintf(dst, 655 LOG_FACSIZE + LOG_PRISIZE, "%s.%s", 656 log_fac[MIN(fac, LOG_NFACILITIES)], 657 log_pri[lc->pri & LOG_PRIMASK]); 658 src += body - 2; /* copy "] " too */ 659 while (*src != '\0') 660 *dst++ = *src++; 661 *dst++ = '\0'; 662 mp2->b_cont->b_wptr = (uchar_t *)dst; 663 } 664 (void) putq(lp->log_q, mp2); 665 } else if (++lp->log_overflow == 1) { 666 if (lp->log_q == log_consq) { 667 console_printf(log_overflow_msg, 668 lp->log_minor, 669 " -- is syslogd(1M) running?"); 670 } else { 671 printf(log_overflow_msg, 672 lp->log_minor, ""); 673 } 674 } 675 } 676 } 677 678 if (zptr) 679 zone_rele(zptr); 680 681 if ((flags & SL_CONSOLE) && (lc->pri & LOG_FACMASK) == LOG_KERN) { 682 if ((mp2 == NULL || log_consq == log_backlogq || panicstr) && 683 (lc->flags & SL_LOGONLY) == 0) 684 console_printf("%s", (char *)mp->b_cont->b_rptr + body); 685 if ((lc->flags & SL_CONSONLY) == 0 && 686 (mp2 = copymsg(mp)) != NULL) { 687 mp2->b_cont->b_rptr += body; 688 if (log_recentq->q_flag & QFULL) 689 freemsg(getq_noenab(log_recentq, 0)); 690 (void) putq(log_recentq, mp2); 691 } 692 } 693 694 log_freemsg(mp); 695 696 log_exit(); 697 } 698 699 /* 700 * Print queued messages to console. 701 */ 702 void 703 log_printq(queue_t *qfirst) 704 { 705 mblk_t *mp; 706 queue_t *q, *qlast; 707 char *cp, *msgp; 708 log_ctl_t *lc; 709 710 /* 711 * Look ahead to first queued message in the stream. 712 */ 713 qlast = NULL; 714 do { 715 for (q = qfirst; q->q_next != qlast; q = q->q_next) 716 continue; 717 for (mp = q->q_first; mp != NULL; mp = mp->b_next) { 718 lc = (log_ctl_t *)mp->b_rptr; 719 /* 720 * Check if message is already displayed at 721 * /dev/console. 722 */ 723 if (lc->flags & SL_PANICMSG) 724 continue; 725 726 cp = (char *)mp->b_cont->b_rptr; 727 728 /* Strip off the message ID. */ 729 if ((msgp = strstr(cp, "[ID ")) != NULL && 730 (msgp = strstr(msgp, "] ")) != NULL) { 731 cp = msgp + 2; 732 } 733 734 /* 735 * Using console_printf instead of printf to avoid 736 * queueing messages to log_consq. 737 */ 738 console_printf("%s", cp); 739 } 740 } while ((qlast = q) != qfirst); 741 } 742 743 /* ARGSUSED */ 744 static int 745 log_cons_constructor(void *buf, void *cdrarg, int kmflags) 746 { 747 struct log *lp = buf; 748 749 lp->log_zoneid = GLOBAL_ZONEID; 750 lp->log_major = LOG_CONSMIN; /* Indicate which device type */ 751 lp->log_data = NULL; 752 return (0); 753 } 754 755 /* ARGSUSED */ 756 static void 757 log_cons_destructor(void *buf, void *cdrarg) 758 { 759 struct log *lp = buf; 760 761 ASSERT(lp->log_zoneid == GLOBAL_ZONEID); 762 ASSERT(lp->log_major == LOG_CONSMIN); 763 ASSERT(lp->log_data == NULL); 764 } 765