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