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