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