xref: /titanic_41/usr/src/uts/common/os/logsubr.c (revision c138f478d2bc94e73ab8f6a084e323bec25e62f5)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <sys/types.h>
30 #include <sys/param.h>
31 #include <sys/varargs.h>
32 #include <sys/systm.h>
33 #include <sys/cmn_err.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 
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 log_t log_conslog;
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",		"resv9",	"resv10",	"resv11",
80 	"resv12",	"audit",	"resv14",	"cron",
81 	"local0",	"local1",	"local2",	"local3",
82 	"local4",	"local5",	"local6",	"local7",
83 	"unknown"
84 };
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)) != 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 	/*
238 	 * Initialize conslog structure.
239 	 */
240 	log_conslog.log_zoneid = GLOBAL_ZONEID;
241 	log_conslog.log_minor = LOG_CONSMIN;
242 
243 	/*
244 	 * Let the logging begin.
245 	 */
246 	log_update(&log_backlog, log_backlogq, SL_CONSOLE, log_console);
247 
248 	/*
249 	 * Now that logging is enabled, emit the SunOS banner.
250 	 */
251 	printf("\rSunOS Release %s Version %s %u-bit\n",
252 	    utsname.release, utsname.version, NBBY * (uint_t)sizeof (void *));
253 	printf("Copyright 1983-2005 Sun Microsystems, Inc.  "
254 		"All rights reserved.\nUse is subject to license terms.\n");
255 #ifdef DEBUG
256 	printf("DEBUG enabled\n");
257 #endif
258 }
259 
260 /*
261  * Allocate a log device corresponding to supplied device type.  All
262  * processes within a given zone that open /dev/conslog share the same
263  * device; processes opening /dev/log get distinct devices (if
264  * available).
265  */
266 log_t *
267 log_alloc(minor_t type)
268 {
269 	zone_t *zptr = curproc->p_zone;
270 	log_zone_t *lzp;
271 	log_t *lp;
272 	int i;
273 
274 	if (type == LOG_CONSMIN) {
275 		/* return the dedicated /dev/conslog device */
276 		return (&log_conslog);
277 	}
278 
279 	ASSERT(type == LOG_LOGMIN);
280 
281 	lzp = zone_getspecific(log_zone_key, zptr);
282 	ASSERT(lzp != NULL);
283 
284 	/* search for an available /dev/log device for the zone */
285 	for (i = LOG_LOGMINIDX; i <= LOG_LOGMAXIDX; i++) {
286 		lp = &lzp->lz_clones[i];
287 		if (lp->log_inuse == 0)
288 			break;
289 	}
290 	if (i > LOG_LOGMAXIDX)
291 		lp = NULL;
292 	return (lp);
293 }
294 
295 /*
296  * Move console messages from src to dst.  The time of day isn't known
297  * early in boot, so fix up the message timestamps if necessary.
298  */
299 static void
300 log_conswitch(log_t *src, log_t *dst)
301 {
302 	mblk_t *mp;
303 	mblk_t *hmp = NULL;
304 	mblk_t *tmp = NULL;
305 	log_ctl_t *hlc;
306 
307 	while ((mp = getq_noenab(src->log_q)) != NULL) {
308 		log_ctl_t *lc = (log_ctl_t *)mp->b_rptr;
309 		lc->flags |= SL_LOGONLY;
310 
311 		/*
312 		 * In the early boot phase hrestime is invalid.
313 		 * hrestime becomes valid when clock() runs for the first time.
314 		 * At this time is lbolt == 1. log_sendmsg() saves the lbolt
315 		 * value in ltime.
316 		 */
317 		if (lc->ltime < 2) {
318 			/*
319 			 * Look ahead to first early boot message with time.
320 			 */
321 			if (hmp) {
322 				tmp->b_next = mp;
323 				tmp = mp;
324 			} else
325 				hmp = tmp = mp;
326 			continue;
327 		}
328 
329 		while (hmp) {
330 			tmp = hmp->b_next;
331 			hmp->b_next = NULL;
332 			hlc = (log_ctl_t *)hmp->b_rptr;
333 			/*
334 			 * Calculate hrestime for an early log message with
335 			 * an invalid time stamp. We know:
336 			 *  - the lbolt of the invalid time stamp.
337 			 *  - the hrestime and lbolt of the first valid
338 			 *    time stamp.
339 			 */
340 			hlc->ttime = lc->ttime - (lc->ltime - hlc->ltime) / hz;
341 			(void) putq(dst->log_q, hmp);
342 			hmp = tmp;
343 		}
344 		(void) putq(dst->log_q, mp);
345 	}
346 	while (hmp) {
347 		tmp = hmp->b_next;
348 		hmp->b_next = NULL;
349 		hlc = (log_ctl_t *)hmp->b_rptr;
350 		hlc->ttime = gethrestime_sec() - (lbolt - hlc->ltime) / hz;
351 		(void) putq(dst->log_q, hmp);
352 		hmp = tmp;
353 	}
354 	dst->log_overflow = src->log_overflow;
355 	src->log_flags = 0;
356 	dst->log_flags = SL_CONSOLE;
357 	log_consq = dst->log_q;
358 }
359 
360 /*
361  * Set the fields in the 'target' clone to the specified values.
362  * Then, look at all clones to determine which message types are
363  * currently active and which clone is the primary console queue.
364  * If the primary console queue changes to or from the backlog
365  * queue, copy all messages from backlog to primary or vice versa.
366  */
367 void
368 log_update(log_t *target, queue_t *q, short flags, log_filter_t *filter)
369 {
370 	log_t *lp;
371 	short active = SL_CONSOLE;
372 	zone_t *zptr = NULL;
373 	log_zone_t *lzp;
374 	zoneid_t zoneid = target->log_zoneid;
375 	int i;
376 
377 	log_enter();
378 
379 	if (q != NULL)
380 		target->log_q = q;
381 	target->log_wanted = filter;
382 	target->log_flags = flags;
383 	target->log_overflow = 0;
384 
385 	/*
386 	 * Need to special case the global zone here since this may be
387 	 * called before zone_init.
388 	 */
389 	if (zoneid == GLOBAL_ZONEID) {
390 		lzp = &log_global;
391 	} else if ((zptr = zone_find_by_id(zoneid)) == NULL) {
392 		log_exit();
393 		return;		/* zone is being destroyed, ignore update */
394 	} else {
395 		lzp = zone_getspecific(log_zone_key, zptr);
396 	}
397 	ASSERT(lzp != NULL);
398 
399 	for (i = LOG_LOGMAXIDX; i >= LOG_LOGMINIDX; i--) {
400 		lp = &lzp->lz_clones[i];
401 		if (zoneid == GLOBAL_ZONEID && (lp->log_flags & SL_CONSOLE))
402 			log_consq = lp->log_q;
403 		active |= lp->log_flags;
404 	}
405 	lzp->lz_active = active;
406 
407 	if (zptr)
408 		zone_rele(zptr);
409 
410 	if (log_consq == target->log_q) {
411 		if (flags & SL_CONSOLE)
412 			log_conswitch(&log_backlog, target);
413 		else
414 			log_conswitch(target, &log_backlog);
415 	}
416 	target->log_q = q;
417 
418 	log_exit();
419 }
420 
421 /*ARGSUSED*/
422 int
423 log_error(log_t *lp, log_ctl_t *lc)
424 {
425 	if ((lc->pri & LOG_FACMASK) == LOG_KERN)
426 		lc->pri = LOG_KERN | LOG_ERR;
427 	return (1);
428 }
429 
430 int
431 log_trace(log_t *lp, log_ctl_t *lc)
432 {
433 	trace_ids_t *tid = (trace_ids_t *)lp->log_data->b_rptr;
434 	trace_ids_t *tidend = (trace_ids_t *)lp->log_data->b_wptr;
435 
436 	/*
437 	 * We use `tid + 1 <= tidend' here rather than the more traditional
438 	 * `tid < tidend', since the former ensures that there's at least
439 	 * `sizeof (trace_ids_t)' bytes available before executing the
440 	 * loop, whereas the latter only ensures that there's a single byte.
441 	 */
442 	for (; tid + 1 <= tidend; tid++) {
443 		if (tid->ti_level < lc->level && tid->ti_level >= 0)
444 			continue;
445 		if (tid->ti_mid != lc->mid && tid->ti_mid >= 0)
446 			continue;
447 		if (tid->ti_sid != lc->sid && tid->ti_sid >= 0)
448 			continue;
449 		if ((lc->pri & LOG_FACMASK) == LOG_KERN)
450 			lc->pri = LOG_KERN | LOG_DEBUG;
451 		return (1);
452 	}
453 	return (0);
454 }
455 
456 /*ARGSUSED*/
457 int
458 log_console(log_t *lp, log_ctl_t *lc)
459 {
460 	if ((lc->pri & LOG_FACMASK) == LOG_KERN) {
461 		if (lc->flags & SL_FATAL)
462 			lc->pri = LOG_KERN | LOG_CRIT;
463 		else if (lc->flags & SL_ERROR)
464 			lc->pri = LOG_KERN | LOG_ERR;
465 		else if (lc->flags & SL_WARN)
466 			lc->pri = LOG_KERN | LOG_WARNING;
467 		else if (lc->flags & SL_NOTE)
468 			lc->pri = LOG_KERN | LOG_NOTICE;
469 		else if (lc->flags & SL_TRACE)
470 			lc->pri = LOG_KERN | LOG_DEBUG;
471 		else
472 			lc->pri = LOG_KERN | LOG_INFO;
473 	}
474 	return (1);
475 }
476 
477 mblk_t *
478 log_makemsg(int mid, int sid, int level, int sl, int pri, void *msg,
479 	size_t size, int on_intr)
480 {
481 	mblk_t *mp = NULL;
482 	mblk_t *mp2;
483 	log_ctl_t *lc;
484 
485 	if (size <= LOG_MSGSIZE &&
486 	    (on_intr || log_freeq->q_count > log_freeq->q_lowat))
487 		mp = getq_noenab(log_freeq);
488 
489 	if (mp == NULL) {
490 		if (on_intr ||
491 		    (mp = allocb(sizeof (log_ctl_t), BPRI_HI)) == NULL ||
492 		    (mp2 = allocb(MAX(size, LOG_MSGSIZE), BPRI_HI)) == NULL) {
493 			freemsg(mp);
494 			return (NULL);
495 		}
496 		DB_TYPE(mp) = M_PROTO;
497 		mp->b_wptr += sizeof (log_ctl_t);
498 		mp->b_cont = mp2;
499 	} else {
500 		mp2 = mp->b_cont;
501 		mp2->b_wptr = mp2->b_rptr;
502 	}
503 
504 	lc = (log_ctl_t *)mp->b_rptr;
505 	lc->mid = mid;
506 	lc->sid = sid;
507 	lc->level = level;
508 	lc->flags = sl;
509 	lc->pri = pri;
510 
511 	bcopy(msg, mp2->b_wptr, size - 1);
512 	mp2->b_wptr[size - 1] = '\0';
513 	mp2->b_wptr += strlen((char *)mp2->b_wptr) + 1;
514 
515 	return (mp);
516 }
517 
518 void
519 log_freemsg(mblk_t *mp)
520 {
521 	mblk_t *mp2 = mp->b_cont;
522 
523 	ASSERT(MBLKL(mp) == sizeof (log_ctl_t));
524 	ASSERT(mp2->b_rptr == mp2->b_datap->db_base);
525 
526 	if ((log_freeq->q_flag & QFULL) == 0 &&
527 	    MBLKL(mp2) <= LOG_MSGSIZE && MBLKSIZE(mp2) >= LOG_MSGSIZE)
528 		(void) putq(log_freeq, mp);
529 	else
530 		freemsg(mp);
531 }
532 
533 void
534 log_sendmsg(mblk_t *mp, zoneid_t zoneid)
535 {
536 	log_t *lp;
537 	char *src, *dst;
538 	mblk_t *mp2 = mp->b_cont;
539 	log_ctl_t *lc = (log_ctl_t *)mp->b_rptr;
540 	int flags, fac;
541 	off_t facility = 0;
542 	off_t body = 0;
543 	zone_t *zptr = NULL;
544 	log_zone_t *lzp;
545 	int i;
546 	int backlog;
547 
548 	/*
549 	 * Need to special case the global zone here since this may be
550 	 * called before zone_init.
551 	 */
552 	if (zoneid == GLOBAL_ZONEID) {
553 		lzp = &log_global;
554 	} else if ((zptr = zone_find_by_id(zoneid)) == NULL) {
555 		/* specified zone doesn't exist, free message and return */
556 		log_freemsg(mp);
557 		return;
558 	} else {
559 		lzp = zone_getspecific(log_zone_key, zptr);
560 	}
561 	ASSERT(lzp != NULL);
562 
563 	if ((lc->flags & lzp->lz_active) == 0) {
564 		if (zptr)
565 			zone_rele(zptr);
566 		log_freemsg(mp);
567 		return;
568 	}
569 
570 	if (panicstr) {
571 		/*
572 		 * Raise the console queue's q_hiwat to ensure that we
573 		 * capture all panic messages.
574 		 */
575 		log_consq->q_hiwat = 2 * LOG_HIWAT;
576 		log_consq->q_flag &= ~QFULL;
577 
578 		/* Message was created while panicking. */
579 		lc->flags |= SL_PANICMSG;
580 	}
581 
582 	src = (char *)mp2->b_rptr;
583 	dst = strstr(src, "FACILITY_AND_PRIORITY] ");
584 	if (dst != NULL) {
585 		facility = dst - src;
586 		body = facility + 23; /* strlen("FACILITY_AND_PRIORITY] ") */
587 	}
588 
589 	log_enter();
590 
591 	lc->ltime = lbolt;
592 	lc->ttime = gethrestime_sec();
593 
594 	flags = lc->flags & lzp->lz_active;
595 	log_seq_no[flags & SL_ERROR]++;
596 	log_seq_no[flags & SL_TRACE]++;
597 	log_seq_no[flags & SL_CONSOLE]++;
598 
599 	/*
600 	 * If this is in the global zone, start with the backlog, then
601 	 * walk through the clone logs.  If not, just do the clone logs.
602 	 */
603 	backlog = (zoneid == GLOBAL_ZONEID);
604 	i = LOG_LOGMINIDX;
605 	while (i <= LOG_LOGMAXIDX) {
606 		if (backlog) {
607 			/*
608 			 * Do the backlog this time, then start on the
609 			 * others.
610 			 */
611 			backlog = 0;
612 			lp = &log_backlog;
613 		} else {
614 			lp = &lzp->lz_clones[i++];
615 		}
616 
617 		if ((lp->log_flags & flags) && lp->log_wanted(lp, lc)) {
618 			if (canput(lp->log_q)) {
619 				lp->log_overflow = 0;
620 				lc->seq_no = log_seq_no[lp->log_flags];
621 				if ((mp2 = copymsg(mp)) == NULL)
622 					break;
623 				if (facility != 0) {
624 					src = (char *)mp2->b_cont->b_rptr;
625 					dst = src + facility;
626 					fac = (lc->pri & LOG_FACMASK) >> 3;
627 					dst += snprintf(dst,
628 					    LOG_FACSIZE + LOG_PRISIZE, "%s.%s",
629 					    log_fac[MIN(fac, LOG_NFACILITIES)],
630 					    log_pri[lc->pri & LOG_PRIMASK]);
631 					src += body - 2; /* copy "] " too */
632 					while (*src != '\0')
633 						*dst++ = *src++;
634 					*dst++ = '\0';
635 					mp2->b_cont->b_wptr = (uchar_t *)dst;
636 				}
637 				(void) putq(lp->log_q, mp2);
638 			} else if (++lp->log_overflow == 1) {
639 				if (lp->log_q == log_consq) {
640 					console_printf(log_overflow_msg,
641 					    lp->log_minor,
642 					    " -- is syslogd(1M) running?");
643 				} else {
644 					printf(log_overflow_msg,
645 					    lp->log_minor, "");
646 				}
647 			}
648 		}
649 	}
650 
651 	if (zptr)
652 		zone_rele(zptr);
653 
654 	if ((flags & SL_CONSOLE) && (lc->pri & LOG_FACMASK) == LOG_KERN) {
655 		if ((mp2 == NULL || log_consq == log_backlogq || panicstr) &&
656 		    (lc->flags & SL_LOGONLY) == 0)
657 			console_printf("%s", (char *)mp->b_cont->b_rptr + body);
658 		if ((lc->flags & SL_CONSONLY) == 0 &&
659 		    (mp2 = copymsg(mp)) != NULL) {
660 			mp2->b_cont->b_rptr += body;
661 			if (log_recentq->q_flag & QFULL)
662 				freemsg(getq_noenab(log_recentq));
663 			(void) putq(log_recentq, mp2);
664 		}
665 	}
666 
667 	log_freemsg(mp);
668 
669 	log_exit();
670 }
671 
672 /*
673  * Print queued messages to console.
674  */
675 void
676 log_printq(queue_t *qfirst)
677 {
678 	mblk_t *mp;
679 	queue_t *q, *qlast;
680 	char *cp, *msgp;
681 	log_ctl_t *lc;
682 
683 	/*
684 	 * Look ahead to first queued message in the stream.
685 	 */
686 	qlast = NULL;
687 	do {
688 		for (q = qfirst; q->q_next != qlast; q = q->q_next)
689 			continue;
690 		for (mp = q->q_first; mp != NULL; mp = mp->b_next) {
691 			lc = (log_ctl_t *)mp->b_rptr;
692 			/*
693 			 * Check if message is already displayed at
694 			 * /dev/console.
695 			 */
696 			if (lc->flags & SL_PANICMSG)
697 				continue;
698 
699 			cp = (char *)mp->b_cont->b_rptr;
700 
701 			/* Strip off the message ID. */
702 			if ((msgp = strstr(cp, "[ID ")) != NULL &&
703 			    (msgp = strstr(msgp,  "] ")) != NULL) {
704 				cp = msgp + 2;
705 			}
706 
707 			/*
708 			 * Using console_printf instead of printf to avoid
709 			 * queueing messages to log_consq.
710 			 */
711 			console_printf("%s", cp);
712 		}
713 	} while ((qlast = q) != qfirst);
714 }
715