xref: /illumos-gate/usr/src/uts/common/os/logsubr.c (revision c464e8d1c46f2eb4848495bcfccd020b8481787d)
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, KM_SLEEP);
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