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