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
48log_zone_t log_global;
49queue_t *log_consq;
50queue_t *log_backlogq;
51queue_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
56static krwlock_t log_rwlock;
57static int log_rwlock_depth;
58static int log_seq_no[SL_CONSOLE + 1];
59static stdata_t log_fakestr;
60static id_space_t *log_minorspace;
61static log_t log_backlog;
62static struct kmem_cache *log_cons_cache;	/* log_t cache */
63
64static queue_t *log_recentq;
65static queue_t *log_freeq;
66
67static zone_key_t log_zone_key;
68
69static char log_overflow_msg[] = "message overflow on /dev/log minor #%d%s\n";
70
71static char log_pri[LOG_PRIMASK + 1][LOG_PRISIZE] = {
72	"emerg",	"alert",	"crit",		"error",
73	"warning",	"notice",	"info",		"debug"
74};
75
76static 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};
85static int log_cons_constructor(void *, void *, int);
86static 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 */
102void
103log_enter(void)
104{
105	if (rw_owner(&log_rwlock) != curthread)
106		rw_enter(&log_rwlock, RW_WRITER);
107	log_rwlock_depth++;
108}
109
110void
111log_exit(void)
112{
113	if (--log_rwlock_depth == 0)
114		rw_exit(&log_rwlock);
115}
116
117void
118log_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 */
133static queue_t *
134log_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 */
152static void *
153log_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*/
172static void
173log_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
186static void
187log_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
194void
195log_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(1M) 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 8K 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 */
278log_t *
279log_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
318void
319log_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 */
329static void
330log_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 */
397void
398log_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*/
451int
452log_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
459int
460log_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*/
486int
487log_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
506mblk_t *
507log_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
547void
548log_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
562void
563log_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(1M) 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 */
717void
718log_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 */
759static int
760log_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 */
771static void
772log_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