xref: /illumos-gate/usr/src/uts/common/io/sysmsg.c (revision 24da5b34f49324ed742a340010ed5bd3d4e06625)
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 2005 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 /*
30  * System message redirection driver for Sun.
31  *
32  * Redirects system message output to the device designated as the underlying
33  * "hardware" console, as given by the value of sysmvp.  The implementation
34  * assumes that sysmvp denotes a STREAMS device; the assumption is justified
35  * since consoles must be capable of effecting tty semantics.
36  */
37 
38 #include <sys/types.h>
39 #include <sys/kmem.h>
40 #include <sys/open.h>
41 #include <sys/param.h>
42 #include <sys/systm.h>
43 #include <sys/signal.h>
44 #include <sys/cred.h>
45 #include <sys/user.h>
46 #include <sys/proc.h>
47 #include <sys/vnode.h>
48 #include <sys/uio.h>
49 #include <sys/stat.h>
50 #include <sys/file.h>
51 #include <sys/session.h>
52 #include <sys/stream.h>
53 #include <sys/strsubr.h>
54 #include <sys/poll.h>
55 #include <sys/debug.h>
56 #include <sys/sysmsg_impl.h>
57 #include <sys/conf.h>
58 #include <sys/termios.h>
59 #include <sys/errno.h>
60 #include <sys/modctl.h>
61 #include <sys/pathname.h>
62 #include <sys/ddi.h>
63 #include <sys/sunddi.h>
64 #include <sys/consdev.h>
65 #include <sys/policy.h>
66 
67 /*
68  * internal functions
69  */
70 static int sysmopen(dev_t *, int, int, cred_t *);
71 static int sysmclose(dev_t, int, int, cred_t *);
72 static int sysmread(dev_t, struct uio *, cred_t *);
73 static int sysmwrite(dev_t, struct uio *, cred_t *);
74 static int sysmioctl(dev_t, int, intptr_t, int, cred_t *, int *);
75 static int sysmpoll(dev_t, short, int, short *, struct pollhead **);
76 static int sysm_info(dev_info_t *, ddi_info_cmd_t, void *, void **);
77 static int sysm_attach(dev_info_t *, ddi_attach_cmd_t);
78 static int sysm_detach(dev_info_t *, ddi_detach_cmd_t);
79 static void bind_consadm_conf(char *);
80 static int checkarg(dev_t);
81 
82 static dev_info_t *sysm_dip;		/* private copy of devinfo pointer */
83 
84 static struct cb_ops sysm_cb_ops = {
85 
86 	sysmopen,		/* open */
87 	sysmclose,		/* close */
88 	nodev,			/* strategy */
89 	nodev,			/* print */
90 	nodev,			/* dump */
91 	sysmread,		/* read */
92 	sysmwrite,		/* write */
93 	sysmioctl,		/* ioctl */
94 	nodev,			/* devmap */
95 	nodev,			/* mmap */
96 	nodev, 			/* segmap */
97 	sysmpoll,		/* poll */
98 	ddi_prop_op,		/* cb_prop_op */
99 	NULL,			/* streamtab  */
100 	D_NEW | D_MP,		/* Driver compatibility flag */
101 	CB_REV,			/* cb_rev */
102 	nodev,			/* aread */
103 	nodev			/* awrite */
104 };
105 
106 static struct dev_ops sysm_ops = {
107 
108 	DEVO_REV,		/* devo_rev, */
109 	0,			/* refcnt  */
110 	sysm_info,		/* info */
111 	nulldev,		/* identify */
112 	nulldev,		/* probe */
113 	sysm_attach,		/* attach */
114 	sysm_detach,		/* detach */
115 	nodev,			/* reset */
116 	&sysm_cb_ops,		/* driver operations */
117 	(struct bus_ops *)0,	/* bus operations */
118 	nulldev			/* power */
119 
120 };
121 
122 /*
123  * Global variables associated with the console device:
124  */
125 
126 #define	SYS_SYSMIN	0	/* sysmsg minor number */
127 #define	SYS_MSGMIN	1	/* msglog minor number */
128 #define	SYSPATHLEN	255	/* length of device path */
129 
130 /*
131  * Private driver state:
132  */
133 
134 #define	MAXDEVS 5
135 
136 typedef struct {
137 	dev_t	dca_devt;
138 	int	dca_flags;
139 	vnode_t	*dca_vp;
140 	krwlock_t	dca_lock;
141 	char	dca_name[SYSPATHLEN];
142 } devicecache_t;
143 
144 /* list of dyn. + persist. config'ed dev's */
145 static devicecache_t sysmcache[MAXDEVS];
146 static kmutex_t	dcvp_mutex;
147 static vnode_t	*dcvp = NULL;
148 static boolean_t sysmsg_opened;
149 static boolean_t msglog_opened;
150 
151 /* flags for device cache */
152 #define	SYSM_DISABLED	0x0
153 #define	SYSM_ENABLED	0x1
154 
155 /*
156  * Module linkage information for the kernel.
157  */
158 
159 static struct modldrv modldrv = {
160 	&mod_driverops, /* Type of module.  This one is a pseudo driver */
161 	"System message redirection (fanout) driver %I%",
162 	&sysm_ops,	/* driver ops */
163 };
164 
165 static struct modlinkage modlinkage = {
166 	MODREV_1,
167 	&modldrv,
168 	NULL
169 };
170 
171 int
172 _init(void)
173 {
174 	return (mod_install(&modlinkage));
175 }
176 
177 int
178 _fini(void)
179 {
180 	return (mod_remove(&modlinkage));
181 }
182 
183 int
184 _info(struct modinfo *modinfop)
185 {
186 	return (mod_info(&modlinkage, modinfop));
187 }
188 
189 /*
190  * DDI glue routines
191  */
192 static int
193 sysm_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
194 {
195 	int i;
196 
197 	switch (cmd) {
198 	case DDI_ATTACH:
199 		ASSERT(sysm_dip == NULL);
200 
201 		if (ddi_create_minor_node(devi, "sysmsg", S_IFCHR,
202 			SYS_SYSMIN, DDI_PSEUDO, NULL) == DDI_FAILURE ||
203 			ddi_create_minor_node(devi, "msglog", S_IFCHR,
204 			SYS_MSGMIN, DDI_PSEUDO, NULL) == DDI_FAILURE) {
205 			ddi_remove_minor_node(devi, NULL);
206 			return (DDI_FAILURE);
207 		}
208 
209 		for (i = 0; i < MAXDEVS; i++) {
210 			rw_init(&sysmcache[i].dca_lock, NULL, RW_DRIVER, NULL);
211 		}
212 
213 		sysm_dip = devi;
214 		return (DDI_SUCCESS);
215 	case DDI_SUSPEND:
216 	case DDI_PM_SUSPEND:
217 		return (DDI_SUCCESS);
218 	default:
219 		return (DDI_FAILURE);
220 	}
221 }
222 
223 static int
224 sysm_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
225 {
226 	int i;
227 
228 	switch (cmd) {
229 	case DDI_DETACH:
230 		ASSERT(sysm_dip == devi);
231 
232 		for (i = 0; i < MAXDEVS; i++)
233 			rw_destroy(&sysmcache[i].dca_lock);
234 
235 		ddi_remove_minor_node(devi, NULL);
236 		sysm_dip = NULL;
237 		return (DDI_SUCCESS);
238 
239 	case DDI_SUSPEND:
240 	case DDI_PM_SUSPEND:
241 		return (DDI_SUCCESS);
242 	default:
243 		return (DDI_FAILURE);
244 	}
245 
246 }
247 
248 /* ARGSUSED */
249 static int
250 sysm_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
251 {
252 	int rval = DDI_FAILURE;
253 	minor_t instance;
254 
255 	instance = getminor((dev_t)arg);
256 
257 	switch (infocmd) {
258 	case DDI_INFO_DEVT2DEVINFO:
259 		if (sysm_dip != NULL &&
260 			(instance == SYS_SYSMIN || instance == SYS_MSGMIN)) {
261 			*result = sysm_dip;
262 			rval = DDI_SUCCESS;
263 		}
264 		break;
265 
266 	case DDI_INFO_DEVT2INSTANCE:
267 		if (instance == SYS_SYSMIN || instance == SYS_MSGMIN) {
268 			*result = NULL;
269 			rval = DDI_SUCCESS;
270 		}
271 		break;
272 
273 	default:
274 		break;
275 	}
276 
277 	return (rval);
278 }
279 
280 /*
281  * Parse the contents of the buffer, and bind the named
282  * devices as auxiliary consoles using our own ioctl routine.
283  *
284  * Comments begin with '#' and are terminated only by a newline
285  * Device names begin with a '/', and are terminated by a newline,
286  * space, '#' or tab.
287  */
288 static void
289 parse_buffer(char *buf, ssize_t fsize)
290 {
291 	char *ebuf = buf + fsize;
292 	char *devname = NULL;
293 	int eatcomments = 0;
294 
295 	while (buf < ebuf) {
296 		if (eatcomments) {
297 			if (*buf++ == '\n')
298 				eatcomments = 0;
299 			continue;
300 		}
301 		switch (*buf) {
302 		case '/':
303 			if (devname == NULL)
304 				devname = buf;
305 			break;
306 		case '#':
307 			eatcomments = 1;
308 			/*FALLTHROUGH*/
309 		case ' ':
310 		case '\t':
311 		case '\n':
312 			*buf = '\0';
313 			if (devname == NULL)
314 				break;
315 			(void) sysmioctl(NODEV, CIOCSETCONSOLE,
316 				(intptr_t)devname, FNATIVE|FKIOCTL|FREAD|FWRITE,
317 				kcred, NULL);
318 			devname = NULL;
319 			break;
320 		default:
321 			break;
322 		}
323 		buf++;
324 	}
325 }
326 
327 #define	CNSADM_BYTES_MAX	2000	/* XXX  nasty fixed size */
328 
329 static void
330 bind_consadm_conf(char *path)
331 {
332 	struct vattr vattr;
333 	vnode_t *vp;
334 	void *buf;
335 	size_t size;
336 	ssize_t resid;
337 	int err = 0;
338 
339 	if (vn_open(path, UIO_SYSSPACE, FREAD, 0, &vp, 0, 0) != 0)
340 		return;
341 	vattr.va_mask = AT_SIZE;
342 	if ((err = VOP_GETATTR(vp, &vattr, 0, kcred)) != 0) {
343 		cmn_err(CE_WARN, "sysmsg: getattr: '%s': error %d",
344 			path, err);
345 		goto closevp;
346 	}
347 
348 	size = vattr.va_size > CNSADM_BYTES_MAX ?
349 		CNSADM_BYTES_MAX : (ssize_t)vattr.va_size;
350 	buf = kmem_alloc(size, KM_SLEEP);
351 
352 	if ((err = vn_rdwr(UIO_READ, vp, buf, size, (offset_t)0,
353 		UIO_SYSSPACE, 0, (rlim64_t)0, kcred, &resid)) != 0)
354 		cmn_err(CE_WARN, "sysmsg: vn_rdwr: '%s': error %d",
355 		    path, err);
356 	else
357 		parse_buffer(buf, size - resid);
358 
359 	kmem_free(buf, size);
360 closevp:
361 	(void) VOP_CLOSE(vp, FREAD, 1, (offset_t)0, kcred);
362 	VN_RELE(vp);
363 }
364 
365 /* ARGSUSED */
366 static int
367 sysmopen(dev_t *dev, int flag, int state, cred_t *cred)
368 {
369 	int	i;
370 	vnode_t	*vp;
371 	minor_t instance;
372 	static boolean_t initialized;
373 
374 	instance = getminor(*dev);
375 
376 	if (state != OTYP_CHR || (instance != 0 && instance != 1))
377 		return (ENXIO);
378 
379 	mutex_enter(&dcvp_mutex);
380 	if ((dcvp == NULL) && (vn_open("/dev/console",
381 		UIO_SYSSPACE, FWRITE, 0, &dcvp, 0, 0) != 0)) {
382 		mutex_exit(&dcvp_mutex);
383 		return (ENXIO);
384 	}
385 
386 	if (instance == SYS_SYSMIN)
387 		sysmsg_opened = B_TRUE;
388 	else
389 		msglog_opened = B_TRUE;
390 
391 	if (!initialized) {
392 		bind_consadm_conf("/etc/consadm.conf");
393 		initialized = B_TRUE;
394 	}
395 	mutex_exit(&dcvp_mutex);
396 
397 	for (i = 0; i < MAXDEVS; i++) {
398 		rw_enter(&sysmcache[i].dca_lock, RW_WRITER);
399 		if ((sysmcache[i].dca_flags & SYSM_ENABLED) &&
400 			sysmcache[i].dca_vp == NULL) {
401 			/*
402 			 * 4196476 - FTRUNC was causing E10K to return EINVAL
403 			 * on open
404 			 */
405 			flag = flag & ~FTRUNC;
406 			/*
407 			 * Open failures on the auxiliary consoles are
408 			 * not returned because we don't care if some
409 			 * subset get an error. We know the default console
410 			 * is okay, and preserve the semantics of the
411 			 * open for the default console.
412 			 * Set NONBLOCK|NDELAY in case there's no carrier.
413 			 */
414 			if (vn_open(sysmcache[i].dca_name, UIO_SYSSPACE,
415 				flag | FNONBLOCK | FNDELAY, 0, &vp, 0, 0) == 0)
416 				sysmcache[i].dca_vp = vp;
417 		}
418 		rw_exit(&sysmcache[i].dca_lock);
419 	}
420 
421 	return (0);
422 }
423 
424 /* ARGSUSED */
425 static int
426 sysmclose(dev_t dev, int flag, int state, cred_t *cred)
427 {
428 	int	i;
429 	minor_t instance;
430 
431 	ASSERT(dcvp != NULL);
432 
433 	if (state != OTYP_CHR)
434 		return (ENXIO);
435 
436 	instance = getminor(dev);
437 
438 	mutex_enter(&dcvp_mutex);
439 	if (instance == SYS_SYSMIN)
440 		sysmsg_opened = B_FALSE;
441 	else
442 		msglog_opened = B_FALSE;
443 
444 	if (sysmsg_opened || msglog_opened) {
445 		mutex_exit(&dcvp_mutex);
446 		return (0);
447 	}
448 
449 	(void) VOP_CLOSE(dcvp, FWRITE, 1, (offset_t)0, kcred);
450 	VN_RELE(dcvp);
451 	dcvp = NULL;
452 	mutex_exit(&dcvp_mutex);
453 
454 	/*
455 	 * Close the auxiliary consoles, we're not concerned with
456 	 * passing up the errors.
457 	 */
458 	for (i = 0; i < MAXDEVS; i++) {
459 		rw_enter(&sysmcache[i].dca_lock, RW_WRITER);
460 		if (sysmcache[i].dca_vp != NULL) {
461 			(void) VOP_CLOSE(sysmcache[i].dca_vp, flag,
462 				1, (offset_t)0, cred);
463 			VN_RELE(sysmcache[i].dca_vp);
464 			sysmcache[i].dca_vp = NULL;
465 		}
466 		rw_exit(&sysmcache[i].dca_lock);
467 	}
468 
469 	return (0);
470 }
471 
472 /* Reads occur only on the default console */
473 
474 /* ARGSUSED */
475 static int
476 sysmread(dev_t dev, struct uio *uio, cred_t *cred)
477 {
478 	ASSERT(dcvp != NULL);
479 	return (VOP_READ(dcvp, uio, 0, cred, NULL));
480 }
481 
482 /* ARGSUSED */
483 static int
484 sysmwrite(dev_t dev, struct uio *uio, cred_t *cred)
485 {
486 	int	i = 0;
487 	iovec_t	uio_iov;
488 	struct uio	tuio;
489 
490 	ASSERT(dcvp != NULL);
491 	ASSERT(uio != NULL);
492 
493 	for (i = 0; i < MAXDEVS; i++) {
494 		rw_enter(&sysmcache[i].dca_lock, RW_READER);
495 		if (sysmcache[i].dca_vp != NULL &&
496 			(sysmcache[i].dca_flags & SYSM_ENABLED)) {
497 			tuio = *uio;
498 			uio_iov = *(uio->uio_iov);
499 			tuio.uio_iov = &uio_iov;
500 			(void) VOP_WRITE(sysmcache[i].dca_vp, &tuio, 0, cred,
501 				NULL);
502 		}
503 		rw_exit(&sysmcache[i].dca_lock);
504 	}
505 	return (VOP_WRITE(dcvp, uio, 0, cred, NULL));
506 }
507 
508 /* ARGSUSED */
509 static int
510 sysmioctl(dev_t dev, int cmd, intptr_t arg, int flag, cred_t *cred, int *rvalp)
511 {
512 	int	rval = 0;
513 	int	error = 0;
514 	size_t	size = 0;
515 	int	i;
516 	char	*infop;
517 	char	found = 0;
518 	dev_t	newdevt = (dev_t)NODEV;	/* because 0 == /dev/console */
519 	vnode_t	*vp;
520 
521 	switch (cmd) {
522 	case CIOCGETCONSOLE:
523 		/* Sum over the number of enabled devices */
524 		for (i = 0; i < MAXDEVS; i++) {
525 			if (sysmcache[i].dca_flags & SYSM_ENABLED)
526 				/* list is space separated, followed by NULL */
527 				size += strlen(sysmcache[i].dca_name) + 1;
528 		}
529 		if (size == 0)
530 			return (0);
531 		break;
532 	case CIOCSETCONSOLE:
533 	case CIOCRMCONSOLE:
534 		size = sizeof (sysmcache[0].dca_name);
535 		break;
536 	case CIOCTTYCONSOLE:
537 	{
538 		dev_t	d;
539 		dev32_t	d32;
540 		extern dev_t rwsconsdev, rconsdev, uconsdev;
541 		proc_t	*p;
542 
543 		if (drv_getparm(UPROCP, &p) != 0)
544 			return (ENODEV);
545 		else
546 			d = cttydev(p);
547 		/*
548 		 * If the controlling terminal is the real
549 		 * or workstation console device, map to what the
550 		 * user thinks is the console device.
551 		 */
552 		if (d == rwsconsdev || d == rconsdev)
553 			d = uconsdev;
554 		if ((flag & FMODELS) != FNATIVE) {
555 			if (!cmpldev(&d32, d))
556 				return (EOVERFLOW);
557 			if (ddi_copyout(&d32, (caddr_t)arg, sizeof (d32),
558 			    flag))
559 				return (EFAULT);
560 		} else {
561 			if (ddi_copyout(&d, (caddr_t)arg, sizeof (d), flag))
562 				return (EFAULT);
563 		}
564 		return (0);
565 	}
566 	default:
567 		/* everything else is sent to the console device */
568 		return (VOP_IOCTL(dcvp, cmd, arg, flag, cred, rvalp));
569 	}
570 
571 	if ((rval = secpolicy_console(cred)) != 0)
572 		return (EPERM);
573 
574 	infop = kmem_alloc(size, KM_SLEEP);
575 	if (flag & FKIOCTL)
576 		error = copystr((caddr_t)arg, infop, size, NULL);
577 	else
578 		error = copyinstr((caddr_t)arg, infop, size, NULL);
579 
580 	if (error) {
581 		switch (cmd) {
582 		case CIOCGETCONSOLE:
583 			/*
584 			 * If the buffer is null, then return a byte count
585 			 * to user land.
586 			 */
587 			*rvalp = size;
588 			goto err_exit;
589 		default:
590 			rval = EFAULT;
591 			goto err_exit;
592 		}
593 	}
594 
595 	if (infop[0] != NULL) {
596 		if ((rval = lookupname(infop, UIO_SYSSPACE, FOLLOW,
597 			NULLVPP, &vp)) == 0) {
598 			if (vp->v_type != VCHR) {
599 				VN_RELE(vp);
600 				rval = EINVAL;
601 				goto err_exit;
602 			}
603 			newdevt = vp->v_rdev;
604 			VN_RELE(vp);
605 		} else
606 			goto err_exit;
607 	}
608 
609 	switch (cmd) {
610 	case CIOCGETCONSOLE:
611 		/*
612 		 * Return the list of device names that are enabled.
613 		 */
614 		for (i = 0; i < MAXDEVS; i++) {
615 			rw_enter(&sysmcache[i].dca_lock, RW_READER);
616 			if (sysmcache[i].dca_flags & SYSM_ENABLED) {
617 				if (infop[0] != NULL)
618 					(void) strcat(infop, " ");
619 				(void) strcat(infop, sysmcache[i].dca_name);
620 			}
621 			rw_exit(&sysmcache[i].dca_lock);
622 		}
623 		if (rval == 0 && copyoutstr(infop, (void *)arg, size, NULL))
624 			rval = EFAULT;
625 		break;
626 
627 	case CIOCSETCONSOLE:
628 		if ((rval = checkarg(newdevt)) != 0)
629 			break;
630 		/*
631 		 * The device does not have to be open or disabled to
632 		 * perform the set console.
633 		 */
634 		for (i = 0; i < MAXDEVS; i++) {
635 			rw_enter(&sysmcache[i].dca_lock, RW_WRITER);
636 			if (sysmcache[i].dca_devt == newdevt &&
637 				(sysmcache[i].dca_flags & SYSM_ENABLED)) {
638 				(void) strcpy(sysmcache[i].dca_name, infop);
639 				rval = EEXIST;
640 				rw_exit(&sysmcache[i].dca_lock);
641 				break;
642 			} else if (sysmcache[i].dca_devt == newdevt &&
643 				sysmcache[i].dca_flags == SYSM_DISABLED) {
644 				sysmcache[i].dca_flags |= SYSM_ENABLED;
645 				(void) strcpy(sysmcache[i].dca_name, infop);
646 				rw_exit(&sysmcache[i].dca_lock);
647 				found = 1;
648 				break;
649 			} else if (sysmcache[i].dca_devt == 0) {
650 				ASSERT(sysmcache[i].dca_vp == NULL &&
651 				sysmcache[i].dca_flags == SYSM_DISABLED);
652 				(void) strcpy(sysmcache[i].dca_name, infop);
653 				sysmcache[i].dca_flags = SYSM_ENABLED;
654 				sysmcache[i].dca_devt = newdevt;
655 				rw_exit(&sysmcache[i].dca_lock);
656 				found = 1;
657 				break;
658 			}
659 			rw_exit(&sysmcache[i].dca_lock);
660 		}
661 		if (found == 0 && rval == 0)
662 			rval = ENOENT;
663 		break;
664 
665 	case CIOCRMCONSOLE:
666 		for (i = 0; i < MAXDEVS; i++) {
667 			rw_enter(&sysmcache[i].dca_lock, RW_WRITER);
668 			if (sysmcache[i].dca_devt == newdevt) {
669 				sysmcache[i].dca_flags = SYSM_DISABLED;
670 				sysmcache[i].dca_name[0] = '\0';
671 				rw_exit(&sysmcache[i].dca_lock);
672 				found = 1;
673 				break;
674 			}
675 			rw_exit(&sysmcache[i].dca_lock);
676 		}
677 		if (found == 0)
678 			rval = ENOENT;
679 		break;
680 
681 	default:
682 		break;
683 	}
684 
685 err_exit:
686 	kmem_free(infop, size);
687 	return (rval);
688 }
689 
690 /* As with the read, we poll only the default console */
691 
692 /* ARGSUSED */
693 static int
694 sysmpoll(dev_t dev, short events, int anyyet, short *reventsp,
695 	struct pollhead **phpp)
696 {
697 	return (VOP_POLL(dcvp, events, anyyet, reventsp, phpp));
698 }
699 
700 /* Sanity check that the device is good */
701 static int
702 checkarg(dev_t devt)
703 {
704 	int rval = 0;
705 	dev_t sysmsg_dev, msglog_dev;
706 	extern dev_t rwsconsdev, rconsdev, uconsdev;
707 
708 	if (devt == rconsdev || devt == rwsconsdev || devt == uconsdev) {
709 		rval = EBUSY;
710 	} else {
711 		sysmsg_dev = makedevice(ddi_driver_major(sysm_dip), SYS_SYSMIN);
712 		msglog_dev = makedevice(ddi_driver_major(sysm_dip), SYS_MSGMIN);
713 		if (devt == sysmsg_dev || devt == msglog_dev)
714 			rval = EINVAL;
715 	}
716 
717 	return (rval);
718 }
719