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