xref: /onnv-gate/usr/src/uts/common/io/sysmsg.c (revision 560:fab7db0411ac)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
50Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
60Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
70Sstevel@tonic-gate  * with the License.
80Sstevel@tonic-gate  *
90Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
100Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
110Sstevel@tonic-gate  * See the License for the specific language governing permissions
120Sstevel@tonic-gate  * and limitations under the License.
130Sstevel@tonic-gate  *
140Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
150Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
160Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
170Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
180Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
190Sstevel@tonic-gate  *
200Sstevel@tonic-gate  * CDDL HEADER END
210Sstevel@tonic-gate  */
220Sstevel@tonic-gate /*
23*560Smeem  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
240Sstevel@tonic-gate  * Use is subject to license terms.
250Sstevel@tonic-gate  */
260Sstevel@tonic-gate 
270Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
280Sstevel@tonic-gate 
290Sstevel@tonic-gate /*
300Sstevel@tonic-gate  * System message redirection driver for Sun.
310Sstevel@tonic-gate  *
320Sstevel@tonic-gate  * Redirects system message output to the device designated as the underlying
330Sstevel@tonic-gate  * "hardware" console, as given by the value of sysmvp.  The implementation
340Sstevel@tonic-gate  * assumes that sysmvp denotes a STREAMS device; the assumption is justified
350Sstevel@tonic-gate  * since consoles must be capable of effecting tty semantics.
360Sstevel@tonic-gate  */
370Sstevel@tonic-gate 
380Sstevel@tonic-gate #include <sys/types.h>
390Sstevel@tonic-gate #include <sys/kmem.h>
400Sstevel@tonic-gate #include <sys/open.h>
410Sstevel@tonic-gate #include <sys/param.h>
420Sstevel@tonic-gate #include <sys/systm.h>
430Sstevel@tonic-gate #include <sys/signal.h>
440Sstevel@tonic-gate #include <sys/cred.h>
450Sstevel@tonic-gate #include <sys/user.h>
460Sstevel@tonic-gate #include <sys/proc.h>
470Sstevel@tonic-gate #include <sys/vnode.h>
480Sstevel@tonic-gate #include <sys/uio.h>
490Sstevel@tonic-gate #include <sys/stat.h>
500Sstevel@tonic-gate #include <sys/file.h>
51*560Smeem #include <sys/session.h>
520Sstevel@tonic-gate #include <sys/stream.h>
530Sstevel@tonic-gate #include <sys/strsubr.h>
540Sstevel@tonic-gate #include <sys/poll.h>
550Sstevel@tonic-gate #include <sys/debug.h>
560Sstevel@tonic-gate #include <sys/sysmsg_impl.h>
570Sstevel@tonic-gate #include <sys/conf.h>
580Sstevel@tonic-gate #include <sys/termios.h>
590Sstevel@tonic-gate #include <sys/errno.h>
600Sstevel@tonic-gate #include <sys/modctl.h>
610Sstevel@tonic-gate #include <sys/pathname.h>
620Sstevel@tonic-gate #include <sys/ddi.h>
630Sstevel@tonic-gate #include <sys/sunddi.h>
640Sstevel@tonic-gate #include <sys/consdev.h>
650Sstevel@tonic-gate #include <sys/policy.h>
660Sstevel@tonic-gate 
670Sstevel@tonic-gate /*
680Sstevel@tonic-gate  * internal functions
690Sstevel@tonic-gate  */
700Sstevel@tonic-gate static int sysmopen(dev_t *, int, int, cred_t *);
710Sstevel@tonic-gate static int sysmclose(dev_t, int, int, cred_t *);
720Sstevel@tonic-gate static int sysmread(dev_t, struct uio *, cred_t *);
730Sstevel@tonic-gate static int sysmwrite(dev_t, struct uio *, cred_t *);
740Sstevel@tonic-gate static int sysmioctl(dev_t, int, intptr_t, int, cred_t *, int *);
750Sstevel@tonic-gate static int sysmpoll(dev_t, short, int, short *, struct pollhead **);
760Sstevel@tonic-gate static int sysm_info(dev_info_t *, ddi_info_cmd_t, void *, void **);
770Sstevel@tonic-gate static int sysm_attach(dev_info_t *, ddi_attach_cmd_t);
780Sstevel@tonic-gate static int sysm_detach(dev_info_t *, ddi_detach_cmd_t);
790Sstevel@tonic-gate static void bind_consadm_conf(char *);
800Sstevel@tonic-gate static int checkarg(dev_t);
810Sstevel@tonic-gate 
820Sstevel@tonic-gate static dev_info_t *sysm_dip;		/* private copy of devinfo pointer */
830Sstevel@tonic-gate 
840Sstevel@tonic-gate static struct cb_ops sysm_cb_ops = {
850Sstevel@tonic-gate 
860Sstevel@tonic-gate 	sysmopen,		/* open */
870Sstevel@tonic-gate 	sysmclose,		/* close */
880Sstevel@tonic-gate 	nodev,			/* strategy */
890Sstevel@tonic-gate 	nodev,			/* print */
900Sstevel@tonic-gate 	nodev,			/* dump */
910Sstevel@tonic-gate 	sysmread,		/* read */
920Sstevel@tonic-gate 	sysmwrite,		/* write */
930Sstevel@tonic-gate 	sysmioctl,		/* ioctl */
940Sstevel@tonic-gate 	nodev,			/* devmap */
950Sstevel@tonic-gate 	nodev,			/* mmap */
960Sstevel@tonic-gate 	nodev, 			/* segmap */
970Sstevel@tonic-gate 	sysmpoll,		/* poll */
980Sstevel@tonic-gate 	ddi_prop_op,		/* cb_prop_op */
990Sstevel@tonic-gate 	NULL,			/* streamtab  */
1000Sstevel@tonic-gate 	D_NEW | D_MP,		/* Driver compatibility flag */
1010Sstevel@tonic-gate 	CB_REV,			/* cb_rev */
1020Sstevel@tonic-gate 	nodev,			/* aread */
1030Sstevel@tonic-gate 	nodev			/* awrite */
1040Sstevel@tonic-gate };
1050Sstevel@tonic-gate 
1060Sstevel@tonic-gate static struct dev_ops sysm_ops = {
1070Sstevel@tonic-gate 
1080Sstevel@tonic-gate 	DEVO_REV,		/* devo_rev, */
1090Sstevel@tonic-gate 	0,			/* refcnt  */
1100Sstevel@tonic-gate 	sysm_info,		/* info */
1110Sstevel@tonic-gate 	nulldev,		/* identify */
1120Sstevel@tonic-gate 	nulldev,		/* probe */
1130Sstevel@tonic-gate 	sysm_attach,		/* attach */
1140Sstevel@tonic-gate 	sysm_detach,		/* detach */
1150Sstevel@tonic-gate 	nodev,			/* reset */
1160Sstevel@tonic-gate 	&sysm_cb_ops,		/* driver operations */
1170Sstevel@tonic-gate 	(struct bus_ops *)0,	/* bus operations */
1180Sstevel@tonic-gate 	nulldev			/* power */
1190Sstevel@tonic-gate 
1200Sstevel@tonic-gate };
1210Sstevel@tonic-gate 
1220Sstevel@tonic-gate /*
1230Sstevel@tonic-gate  * Global variables associated with the console device:
1240Sstevel@tonic-gate  */
1250Sstevel@tonic-gate 
1260Sstevel@tonic-gate #define	SYS_SYSMIN	0	/* sysmsg minor number */
1270Sstevel@tonic-gate #define	SYS_MSGMIN	1	/* msglog minor number */
1280Sstevel@tonic-gate #define	SYSPATHLEN	255	/* length of device path */
1290Sstevel@tonic-gate 
1300Sstevel@tonic-gate /*
1310Sstevel@tonic-gate  * Private driver state:
1320Sstevel@tonic-gate  */
1330Sstevel@tonic-gate 
1340Sstevel@tonic-gate #define	MAXDEVS 5
1350Sstevel@tonic-gate 
1360Sstevel@tonic-gate typedef struct {
1370Sstevel@tonic-gate 	dev_t	dca_devt;
1380Sstevel@tonic-gate 	int	dca_flags;
1390Sstevel@tonic-gate 	vnode_t	*dca_vp;
1400Sstevel@tonic-gate 	krwlock_t	dca_lock;
1410Sstevel@tonic-gate 	char	dca_name[SYSPATHLEN];
1420Sstevel@tonic-gate } devicecache_t;
1430Sstevel@tonic-gate 
1440Sstevel@tonic-gate /* list of dyn. + persist. config'ed dev's */
1450Sstevel@tonic-gate static devicecache_t sysmcache[MAXDEVS];
1460Sstevel@tonic-gate static kmutex_t	dcvp_mutex;
1470Sstevel@tonic-gate static vnode_t	*dcvp = NULL;
1480Sstevel@tonic-gate 
1490Sstevel@tonic-gate /* flags for device cache */
1500Sstevel@tonic-gate #define	SYSM_DISABLED	0x0
1510Sstevel@tonic-gate #define	SYSM_ENABLED	0x1
1520Sstevel@tonic-gate 
1530Sstevel@tonic-gate /*
1540Sstevel@tonic-gate  * Module linkage information for the kernel.
1550Sstevel@tonic-gate  */
1560Sstevel@tonic-gate 
1570Sstevel@tonic-gate static struct modldrv modldrv = {
1580Sstevel@tonic-gate 	&mod_driverops, /* Type of module.  This one is a pseudo driver */
1590Sstevel@tonic-gate 	"System message redirection (fanout) driver %I%",
1600Sstevel@tonic-gate 	&sysm_ops,	/* driver ops */
1610Sstevel@tonic-gate };
1620Sstevel@tonic-gate 
1630Sstevel@tonic-gate static struct modlinkage modlinkage = {
1640Sstevel@tonic-gate 	MODREV_1,
1650Sstevel@tonic-gate 	&modldrv,
1660Sstevel@tonic-gate 	NULL
1670Sstevel@tonic-gate };
1680Sstevel@tonic-gate 
1690Sstevel@tonic-gate int
1700Sstevel@tonic-gate _init(void)
1710Sstevel@tonic-gate {
1720Sstevel@tonic-gate 	return (mod_install(&modlinkage));
1730Sstevel@tonic-gate }
1740Sstevel@tonic-gate 
1750Sstevel@tonic-gate int
1760Sstevel@tonic-gate _fini(void)
1770Sstevel@tonic-gate {
1780Sstevel@tonic-gate 	return (mod_remove(&modlinkage));
1790Sstevel@tonic-gate }
1800Sstevel@tonic-gate 
1810Sstevel@tonic-gate int
1820Sstevel@tonic-gate _info(struct modinfo *modinfop)
1830Sstevel@tonic-gate {
1840Sstevel@tonic-gate 	return (mod_info(&modlinkage, modinfop));
1850Sstevel@tonic-gate }
1860Sstevel@tonic-gate 
1870Sstevel@tonic-gate /*
1880Sstevel@tonic-gate  * DDI glue routines
1890Sstevel@tonic-gate  */
1900Sstevel@tonic-gate static int
1910Sstevel@tonic-gate sysm_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
1920Sstevel@tonic-gate {
1930Sstevel@tonic-gate 	int i;
1940Sstevel@tonic-gate 
1950Sstevel@tonic-gate 	switch (cmd) {
1960Sstevel@tonic-gate 	case DDI_ATTACH:
1970Sstevel@tonic-gate 		ASSERT(sysm_dip == NULL);
1980Sstevel@tonic-gate 
1990Sstevel@tonic-gate 		if (ddi_create_minor_node(devi, "sysmsg", S_IFCHR,
2000Sstevel@tonic-gate 		    SYS_SYSMIN, DDI_PSEUDO, NULL) == DDI_FAILURE ||
2010Sstevel@tonic-gate 		    ddi_create_minor_node(devi, "msglog", S_IFCHR,
2020Sstevel@tonic-gate 		    SYS_MSGMIN, DDI_PSEUDO, NULL) == DDI_FAILURE) {
2030Sstevel@tonic-gate 			ddi_remove_minor_node(devi, NULL);
2040Sstevel@tonic-gate 			return (DDI_FAILURE);
2050Sstevel@tonic-gate 		}
2060Sstevel@tonic-gate 
2070Sstevel@tonic-gate 		for (i = 0; i < MAXDEVS; i++) {
2080Sstevel@tonic-gate 			rw_init(&sysmcache[i].dca_lock, NULL, RW_DRIVER, NULL);
2090Sstevel@tonic-gate 		}
2100Sstevel@tonic-gate 
2110Sstevel@tonic-gate 		/* set everything up .. */
2120Sstevel@tonic-gate 		bind_consadm_conf("/etc/consadm.conf");
2130Sstevel@tonic-gate 		sysm_dip = devi;
2140Sstevel@tonic-gate 		return (DDI_SUCCESS);
2150Sstevel@tonic-gate 	case DDI_SUSPEND:
2160Sstevel@tonic-gate 	case DDI_PM_SUSPEND:
2170Sstevel@tonic-gate 		return (DDI_SUCCESS);
2180Sstevel@tonic-gate 	default:
2190Sstevel@tonic-gate 		return (DDI_FAILURE);
2200Sstevel@tonic-gate 	}
2210Sstevel@tonic-gate }
2220Sstevel@tonic-gate 
2230Sstevel@tonic-gate static int
2240Sstevel@tonic-gate sysm_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
2250Sstevel@tonic-gate {
2260Sstevel@tonic-gate 	int i;
2270Sstevel@tonic-gate 
2280Sstevel@tonic-gate 	switch (cmd) {
2290Sstevel@tonic-gate 	case DDI_DETACH:
2300Sstevel@tonic-gate 		ASSERT(sysm_dip == devi);
2310Sstevel@tonic-gate 
2320Sstevel@tonic-gate 		if (dcvp) {
2330Sstevel@tonic-gate 			(void) VOP_CLOSE(dcvp, FWRITE, 1, (offset_t)0, kcred);
2340Sstevel@tonic-gate 			VN_RELE(dcvp);
2350Sstevel@tonic-gate 			dcvp = NULL;
2360Sstevel@tonic-gate 		}
2370Sstevel@tonic-gate 		for (i = 0; i < MAXDEVS; i++) {
2380Sstevel@tonic-gate 			if (sysmcache[i].dca_vp != NULL) {
2390Sstevel@tonic-gate 				(void) VOP_CLOSE(sysmcache[i].dca_vp, 0,
2400Sstevel@tonic-gate 				    1, (offset_t)0, 0);
2410Sstevel@tonic-gate 				VN_RELE(sysmcache[i].dca_vp);
2420Sstevel@tonic-gate 			}
2430Sstevel@tonic-gate 			sysmcache[i].dca_vp = NULL;
2440Sstevel@tonic-gate 			rw_destroy(&sysmcache[i].dca_lock);
2450Sstevel@tonic-gate 		}
2460Sstevel@tonic-gate 
2470Sstevel@tonic-gate 		ddi_remove_minor_node(devi, NULL);
2480Sstevel@tonic-gate 		sysm_dip = NULL;
2490Sstevel@tonic-gate 		return (DDI_SUCCESS);
2500Sstevel@tonic-gate 
2510Sstevel@tonic-gate 	case DDI_SUSPEND:
2520Sstevel@tonic-gate 	case DDI_PM_SUSPEND:
2530Sstevel@tonic-gate 		return (DDI_SUCCESS);
2540Sstevel@tonic-gate 	default:
2550Sstevel@tonic-gate 		return (DDI_FAILURE);
2560Sstevel@tonic-gate 	}
2570Sstevel@tonic-gate 
2580Sstevel@tonic-gate }
2590Sstevel@tonic-gate 
2600Sstevel@tonic-gate /* ARGSUSED */
2610Sstevel@tonic-gate static int
2620Sstevel@tonic-gate sysm_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
2630Sstevel@tonic-gate {
2640Sstevel@tonic-gate 	int rval = DDI_FAILURE;
2650Sstevel@tonic-gate 	minor_t instance;
2660Sstevel@tonic-gate 
2670Sstevel@tonic-gate 	instance = getminor((dev_t)arg);
2680Sstevel@tonic-gate 
2690Sstevel@tonic-gate 	switch (infocmd) {
2700Sstevel@tonic-gate 	case DDI_INFO_DEVT2DEVINFO:
2710Sstevel@tonic-gate 		if (sysm_dip != NULL &&
2720Sstevel@tonic-gate 		    (instance == SYS_SYSMIN || instance == SYS_MSGMIN)) {
2730Sstevel@tonic-gate 			*result = sysm_dip;
2740Sstevel@tonic-gate 			rval = DDI_SUCCESS;
2750Sstevel@tonic-gate 		}
2760Sstevel@tonic-gate 		break;
2770Sstevel@tonic-gate 
2780Sstevel@tonic-gate 	case DDI_INFO_DEVT2INSTANCE:
2790Sstevel@tonic-gate 		if (instance == SYS_SYSMIN || instance == SYS_MSGMIN) {
2800Sstevel@tonic-gate 			*result = NULL;
2810Sstevel@tonic-gate 			rval = DDI_SUCCESS;
2820Sstevel@tonic-gate 		}
2830Sstevel@tonic-gate 		break;
2840Sstevel@tonic-gate 
2850Sstevel@tonic-gate 	default:
2860Sstevel@tonic-gate 		break;
2870Sstevel@tonic-gate 	}
2880Sstevel@tonic-gate 
2890Sstevel@tonic-gate 	return (rval);
2900Sstevel@tonic-gate }
2910Sstevel@tonic-gate 
2920Sstevel@tonic-gate /*
2930Sstevel@tonic-gate  * Parse the contents of the buffer, and bind the named
2940Sstevel@tonic-gate  * devices as auxiliary consoles using our own ioctl routine.
2950Sstevel@tonic-gate  *
2960Sstevel@tonic-gate  * Comments begin with '#' and are terminated only by a newline
2970Sstevel@tonic-gate  * Device names begin with a '/', and are terminated by a newline,
2980Sstevel@tonic-gate  * space, '#' or tab.
2990Sstevel@tonic-gate  */
3000Sstevel@tonic-gate static void
3010Sstevel@tonic-gate parse_buffer(char *buf, ssize_t fsize)
3020Sstevel@tonic-gate {
3030Sstevel@tonic-gate 	char *ebuf = buf + fsize;
3040Sstevel@tonic-gate 	char *devname = NULL;
3050Sstevel@tonic-gate 	int eatcomments = 0;
3060Sstevel@tonic-gate 
3070Sstevel@tonic-gate 	while (buf < ebuf) {
3080Sstevel@tonic-gate 		if (eatcomments) {
3090Sstevel@tonic-gate 			if (*buf++ == '\n')
3100Sstevel@tonic-gate 				eatcomments = 0;
3110Sstevel@tonic-gate 			continue;
3120Sstevel@tonic-gate 		}
3130Sstevel@tonic-gate 		switch (*buf) {
3140Sstevel@tonic-gate 		case '/':
3150Sstevel@tonic-gate 			if (devname == NULL)
3160Sstevel@tonic-gate 				devname = buf;
3170Sstevel@tonic-gate 			break;
3180Sstevel@tonic-gate 		case '#':
3190Sstevel@tonic-gate 			eatcomments = 1;
3200Sstevel@tonic-gate 			/*FALLTHROUGH*/
3210Sstevel@tonic-gate 		case ' ':
3220Sstevel@tonic-gate 		case '\t':
3230Sstevel@tonic-gate 		case '\n':
3240Sstevel@tonic-gate 			*buf = '\0';
3250Sstevel@tonic-gate 			if (devname == NULL)
3260Sstevel@tonic-gate 				break;
3270Sstevel@tonic-gate 			(void) sysmioctl(NODEV, CIOCSETCONSOLE,
3280Sstevel@tonic-gate 			    (intptr_t)devname, FNATIVE|FKIOCTL|FREAD|FWRITE,
3290Sstevel@tonic-gate 			    kcred, NULL);
3300Sstevel@tonic-gate 			devname = NULL;
3310Sstevel@tonic-gate 			break;
3320Sstevel@tonic-gate 		default:
3330Sstevel@tonic-gate 			break;
3340Sstevel@tonic-gate 		}
3350Sstevel@tonic-gate 		buf++;
3360Sstevel@tonic-gate 	}
3370Sstevel@tonic-gate }
3380Sstevel@tonic-gate 
3390Sstevel@tonic-gate #define	CNSADM_BYTES_MAX	2000	/* XXX  nasty fixed size */
3400Sstevel@tonic-gate 
3410Sstevel@tonic-gate static void
3420Sstevel@tonic-gate bind_consadm_conf(char *path)
3430Sstevel@tonic-gate {
3440Sstevel@tonic-gate 	struct vattr vattr;
3450Sstevel@tonic-gate 	vnode_t *vp;
3460Sstevel@tonic-gate 	void *buf;
3470Sstevel@tonic-gate 	size_t size;
3480Sstevel@tonic-gate 	ssize_t resid;
3490Sstevel@tonic-gate 	int err = 0;
3500Sstevel@tonic-gate 
3510Sstevel@tonic-gate 	if (vn_open(path, UIO_SYSSPACE, FREAD, 0, &vp, 0, 0) != 0)
3520Sstevel@tonic-gate 		return;
3530Sstevel@tonic-gate 	vattr.va_mask = AT_SIZE;
3540Sstevel@tonic-gate 	if ((err = VOP_GETATTR(vp, &vattr, 0, kcred)) != 0) {
3550Sstevel@tonic-gate 		cmn_err(CE_WARN, "sysmsg: getattr: '%s': error %d",
3560Sstevel@tonic-gate 		    path, err);
3570Sstevel@tonic-gate 		goto closevp;
3580Sstevel@tonic-gate 	}
3590Sstevel@tonic-gate 
3600Sstevel@tonic-gate 	size = vattr.va_size > CNSADM_BYTES_MAX ?
3610Sstevel@tonic-gate 	    CNSADM_BYTES_MAX : (ssize_t)vattr.va_size;
3620Sstevel@tonic-gate 	buf = kmem_alloc(size, KM_SLEEP);
3630Sstevel@tonic-gate 
3640Sstevel@tonic-gate 	if ((err = vn_rdwr(UIO_READ, vp, buf, size, (offset_t)0,
3650Sstevel@tonic-gate 	    UIO_SYSSPACE, 0, (rlim64_t)0, kcred, &resid)) != 0)
3660Sstevel@tonic-gate 		cmn_err(CE_WARN, "sysmsg: vn_rdwr: '%s': error %d",
3670Sstevel@tonic-gate 		    path, err);
3680Sstevel@tonic-gate 	else
3690Sstevel@tonic-gate 		parse_buffer(buf, size - resid);
3700Sstevel@tonic-gate 
3710Sstevel@tonic-gate 	kmem_free(buf, size);
3720Sstevel@tonic-gate closevp:
3730Sstevel@tonic-gate 	(void) VOP_CLOSE(vp, FREAD, 1, (offset_t)0, kcred);
3740Sstevel@tonic-gate 	VN_RELE(vp);
3750Sstevel@tonic-gate }
3760Sstevel@tonic-gate 
3770Sstevel@tonic-gate /* ARGSUSED */
3780Sstevel@tonic-gate static int
3790Sstevel@tonic-gate sysmopen(dev_t *dev, int flag, int state, cred_t *cred)
3800Sstevel@tonic-gate {
3810Sstevel@tonic-gate 	int	i;
3820Sstevel@tonic-gate 	vnode_t	*vp;
3830Sstevel@tonic-gate 	minor_t instance;
3840Sstevel@tonic-gate 
3850Sstevel@tonic-gate 	instance = getminor(*dev);
3860Sstevel@tonic-gate 
3870Sstevel@tonic-gate 	if (state != OTYP_CHR || (instance != 0 && instance != 1))
3880Sstevel@tonic-gate 		return (ENXIO);
3890Sstevel@tonic-gate 
3900Sstevel@tonic-gate 	mutex_enter(&dcvp_mutex);
3910Sstevel@tonic-gate 	if ((dcvp == NULL) && (vn_open("/dev/console",
3920Sstevel@tonic-gate 	    UIO_SYSSPACE, FWRITE, 0, &dcvp, 0, 0) != 0)) {
3930Sstevel@tonic-gate 		mutex_exit(&dcvp_mutex);
3940Sstevel@tonic-gate 		return (ENXIO);
3950Sstevel@tonic-gate 	}
3960Sstevel@tonic-gate 	mutex_exit(&dcvp_mutex);
3970Sstevel@tonic-gate 
3980Sstevel@tonic-gate 	for (i = 0; i < MAXDEVS; i++) {
3990Sstevel@tonic-gate 		rw_enter(&sysmcache[i].dca_lock, RW_WRITER);
4000Sstevel@tonic-gate 		if ((sysmcache[i].dca_flags & SYSM_ENABLED) &&
4010Sstevel@tonic-gate 		    sysmcache[i].dca_vp == NULL) {
4020Sstevel@tonic-gate 			/*
4030Sstevel@tonic-gate 			 * 4196476 - FTRUNC was causing E10K to return EINVAL
4040Sstevel@tonic-gate 			 * on open
4050Sstevel@tonic-gate 			 */
4060Sstevel@tonic-gate 			flag = flag & ~FTRUNC;
4070Sstevel@tonic-gate 			/*
4080Sstevel@tonic-gate 			 * Open failures on the auxiliary consoles are
4090Sstevel@tonic-gate 			 * not returned because we don't care if some
4100Sstevel@tonic-gate 			 * subset get an error. We know the default console
4110Sstevel@tonic-gate 			 * is okay, and preserve the semantics of the
4120Sstevel@tonic-gate 			 * open for the default console.
4130Sstevel@tonic-gate 			 * Set NONBLOCK|NDELAY in case there's no carrier.
4140Sstevel@tonic-gate 			 */
4150Sstevel@tonic-gate 			if (vn_open(sysmcache[i].dca_name, UIO_SYSSPACE,
4160Sstevel@tonic-gate 			    flag | FNONBLOCK | FNDELAY, 0, &vp, 0, 0) == 0)
4170Sstevel@tonic-gate 				sysmcache[i].dca_vp = vp;
4180Sstevel@tonic-gate 		}
4190Sstevel@tonic-gate 		rw_exit(&sysmcache[i].dca_lock);
4200Sstevel@tonic-gate 	}
4210Sstevel@tonic-gate 
4220Sstevel@tonic-gate 	return (0);
4230Sstevel@tonic-gate }
4240Sstevel@tonic-gate 
4250Sstevel@tonic-gate /* ARGSUSED */
4260Sstevel@tonic-gate static int
4270Sstevel@tonic-gate sysmclose(dev_t dev, int flag, int state, cred_t *cred)
4280Sstevel@tonic-gate {
4290Sstevel@tonic-gate 	int	i;
4300Sstevel@tonic-gate 
4310Sstevel@tonic-gate 	if (state != OTYP_CHR)
4320Sstevel@tonic-gate 		return (ENXIO);
4330Sstevel@tonic-gate 
4340Sstevel@tonic-gate 	/*
4350Sstevel@tonic-gate 	 * Close the auxiliary consoles, we're not concerned with
4360Sstevel@tonic-gate 	 * passing up the errors.
4370Sstevel@tonic-gate 	 */
4380Sstevel@tonic-gate 	for (i = 0; i < MAXDEVS; i++) {
4390Sstevel@tonic-gate 		rw_enter(&sysmcache[i].dca_lock, RW_WRITER);
4400Sstevel@tonic-gate 		if (sysmcache[i].dca_vp != NULL) {
4410Sstevel@tonic-gate 			(void) VOP_CLOSE(sysmcache[i].dca_vp, flag,
4420Sstevel@tonic-gate 			    1, (offset_t)0, cred);
4430Sstevel@tonic-gate 			VN_RELE(sysmcache[i].dca_vp);
4440Sstevel@tonic-gate 			sysmcache[i].dca_vp = NULL;
4450Sstevel@tonic-gate 		}
4460Sstevel@tonic-gate 		rw_exit(&sysmcache[i].dca_lock);
4470Sstevel@tonic-gate 	}
4480Sstevel@tonic-gate 
4490Sstevel@tonic-gate 	return (0);
4500Sstevel@tonic-gate }
4510Sstevel@tonic-gate 
4520Sstevel@tonic-gate /* Reads occur only on the default console */
4530Sstevel@tonic-gate 
4540Sstevel@tonic-gate /* ARGSUSED */
4550Sstevel@tonic-gate static int
4560Sstevel@tonic-gate sysmread(dev_t dev, struct uio *uio, cred_t *cred)
4570Sstevel@tonic-gate {
4580Sstevel@tonic-gate 	ASSERT(dcvp != NULL);
4590Sstevel@tonic-gate 	return (VOP_READ(dcvp, uio, 0, cred, NULL));
4600Sstevel@tonic-gate }
4610Sstevel@tonic-gate 
4620Sstevel@tonic-gate /* ARGSUSED */
4630Sstevel@tonic-gate static int
4640Sstevel@tonic-gate sysmwrite(dev_t dev, struct uio *uio, cred_t *cred)
4650Sstevel@tonic-gate {
4660Sstevel@tonic-gate 	int	i = 0;
4670Sstevel@tonic-gate 	iovec_t	uio_iov;
4680Sstevel@tonic-gate 	struct uio	tuio;
4690Sstevel@tonic-gate 
4700Sstevel@tonic-gate 	ASSERT(dcvp != NULL);
4710Sstevel@tonic-gate 	ASSERT(uio != NULL);
4720Sstevel@tonic-gate 
4730Sstevel@tonic-gate 	for (i = 0; i < MAXDEVS; i++) {
4740Sstevel@tonic-gate 		rw_enter(&sysmcache[i].dca_lock, RW_READER);
4750Sstevel@tonic-gate 		if (sysmcache[i].dca_vp != NULL &&
4760Sstevel@tonic-gate 		    (sysmcache[i].dca_flags & SYSM_ENABLED)) {
4770Sstevel@tonic-gate 			tuio = *uio;
4780Sstevel@tonic-gate 			uio_iov = *(uio->uio_iov);
4790Sstevel@tonic-gate 			tuio.uio_iov = &uio_iov;
4800Sstevel@tonic-gate 			(void) VOP_WRITE(sysmcache[i].dca_vp, &tuio, 0, cred,
4810Sstevel@tonic-gate 				NULL);
4820Sstevel@tonic-gate 		}
4830Sstevel@tonic-gate 		rw_exit(&sysmcache[i].dca_lock);
4840Sstevel@tonic-gate 	}
4850Sstevel@tonic-gate 	return (VOP_WRITE(dcvp, uio, 0, cred, NULL));
4860Sstevel@tonic-gate }
4870Sstevel@tonic-gate 
4880Sstevel@tonic-gate /* ARGSUSED */
4890Sstevel@tonic-gate static int
4900Sstevel@tonic-gate sysmioctl(dev_t dev, int cmd, intptr_t arg, int flag, cred_t *cred, int *rvalp)
4910Sstevel@tonic-gate {
4920Sstevel@tonic-gate 	int	rval = 0;
4930Sstevel@tonic-gate 	int	error = 0;
4940Sstevel@tonic-gate 	size_t	size = 0;
4950Sstevel@tonic-gate 	int	i;
4960Sstevel@tonic-gate 	char	*infop;
4970Sstevel@tonic-gate 	char	found = 0;
4980Sstevel@tonic-gate 	dev_t	newdevt = (dev_t)NODEV;	/* because 0 == /dev/console */
4990Sstevel@tonic-gate 	vnode_t	*vp;
5000Sstevel@tonic-gate 
5010Sstevel@tonic-gate 	switch (cmd) {
5020Sstevel@tonic-gate 	case CIOCGETCONSOLE:
5030Sstevel@tonic-gate 		/* Sum over the number of enabled devices */
5040Sstevel@tonic-gate 		for (i = 0; i < MAXDEVS; i++) {
5050Sstevel@tonic-gate 			if (sysmcache[i].dca_flags & SYSM_ENABLED)
5060Sstevel@tonic-gate 				/* list is space separated, followed by NULL */
5070Sstevel@tonic-gate 				size += strlen(sysmcache[i].dca_name) + 1;
5080Sstevel@tonic-gate 		}
5090Sstevel@tonic-gate 		if (size == 0)
5100Sstevel@tonic-gate 			return (0);
5110Sstevel@tonic-gate 		break;
5120Sstevel@tonic-gate 	case CIOCSETCONSOLE:
5130Sstevel@tonic-gate 	case CIOCRMCONSOLE:
5140Sstevel@tonic-gate 		size = sizeof (sysmcache[0].dca_name);
5150Sstevel@tonic-gate 		break;
5160Sstevel@tonic-gate 	case CIOCTTYCONSOLE:
5170Sstevel@tonic-gate 	{
5180Sstevel@tonic-gate 		dev_t	d;
5190Sstevel@tonic-gate 		dev32_t	d32;
5200Sstevel@tonic-gate 		extern dev_t rwsconsdev, rconsdev, uconsdev;
5210Sstevel@tonic-gate 		proc_t	*p;
5220Sstevel@tonic-gate 
5230Sstevel@tonic-gate 		if (drv_getparm(UPROCP, &p) != 0)
5240Sstevel@tonic-gate 			return (ENODEV);
5250Sstevel@tonic-gate 		else
5260Sstevel@tonic-gate 			d = cttydev(p);
5270Sstevel@tonic-gate 		/*
5280Sstevel@tonic-gate 		 * If the controlling terminal is the real
5290Sstevel@tonic-gate 		 * or workstation console device, map to what the
5300Sstevel@tonic-gate 		 * user thinks is the console device.
5310Sstevel@tonic-gate 		 */
5320Sstevel@tonic-gate 		if (d == rwsconsdev || d == rconsdev)
5330Sstevel@tonic-gate 			d = uconsdev;
5340Sstevel@tonic-gate 		if ((flag & FMODELS) != FNATIVE) {
5350Sstevel@tonic-gate 			if (!cmpldev(&d32, d))
5360Sstevel@tonic-gate 				return (EOVERFLOW);
5370Sstevel@tonic-gate 			if (ddi_copyout(&d32, (caddr_t)arg, sizeof (d32),
5380Sstevel@tonic-gate 			    flag))
5390Sstevel@tonic-gate 				return (EFAULT);
5400Sstevel@tonic-gate 		} else {
5410Sstevel@tonic-gate 			if (ddi_copyout(&d, (caddr_t)arg, sizeof (d), flag))
5420Sstevel@tonic-gate 				return (EFAULT);
5430Sstevel@tonic-gate 		}
5440Sstevel@tonic-gate 		return (0);
5450Sstevel@tonic-gate 	}
5460Sstevel@tonic-gate 	default:
5470Sstevel@tonic-gate 		/* everything else is sent to the console device */
5480Sstevel@tonic-gate 		return (VOP_IOCTL(dcvp, cmd, arg, flag, cred, rvalp));
5490Sstevel@tonic-gate 	}
5500Sstevel@tonic-gate 
5510Sstevel@tonic-gate 	if ((rval = secpolicy_console(cred)) != 0)
5520Sstevel@tonic-gate 		return (EPERM);
5530Sstevel@tonic-gate 
5540Sstevel@tonic-gate 	infop = kmem_alloc(size, KM_SLEEP);
5550Sstevel@tonic-gate 	if (flag & FKIOCTL)
5560Sstevel@tonic-gate 		error = copystr((caddr_t)arg, infop, size, NULL);
5570Sstevel@tonic-gate 	else
5580Sstevel@tonic-gate 		error = copyinstr((caddr_t)arg, infop, size, NULL);
5590Sstevel@tonic-gate 
5600Sstevel@tonic-gate 	if (error) {
5610Sstevel@tonic-gate 		switch (cmd) {
5620Sstevel@tonic-gate 		case CIOCGETCONSOLE:
5630Sstevel@tonic-gate 			/*
5640Sstevel@tonic-gate 			 * If the buffer is null, then return a byte count
5650Sstevel@tonic-gate 			 * to user land.
5660Sstevel@tonic-gate 			 */
5670Sstevel@tonic-gate 			*rvalp = size;
5680Sstevel@tonic-gate 			goto err_exit;
5690Sstevel@tonic-gate 		default:
5700Sstevel@tonic-gate 			rval = EFAULT;
5710Sstevel@tonic-gate 			goto err_exit;
5720Sstevel@tonic-gate 		}
5730Sstevel@tonic-gate 	}
5740Sstevel@tonic-gate 
5750Sstevel@tonic-gate 	if (infop[0] != NULL) {
5760Sstevel@tonic-gate 		if ((rval = lookupname(infop, UIO_SYSSPACE, FOLLOW,
5770Sstevel@tonic-gate 		    NULLVPP, &vp)) == 0) {
5780Sstevel@tonic-gate 			if (vp->v_type != VCHR) {
5790Sstevel@tonic-gate 				VN_RELE(vp);
5800Sstevel@tonic-gate 				rval = EINVAL;
5810Sstevel@tonic-gate 				goto err_exit;
5820Sstevel@tonic-gate 			}
5830Sstevel@tonic-gate 			newdevt = vp->v_rdev;
5840Sstevel@tonic-gate 			VN_RELE(vp);
5850Sstevel@tonic-gate 		} else
5860Sstevel@tonic-gate 			goto err_exit;
5870Sstevel@tonic-gate 	}
5880Sstevel@tonic-gate 
5890Sstevel@tonic-gate 	switch (cmd) {
5900Sstevel@tonic-gate 	case CIOCGETCONSOLE:
5910Sstevel@tonic-gate 		/*
5920Sstevel@tonic-gate 		 * Return the list of device names that are enabled.
5930Sstevel@tonic-gate 		 */
5940Sstevel@tonic-gate 		for (i = 0; i < MAXDEVS; i++) {
5950Sstevel@tonic-gate 			rw_enter(&sysmcache[i].dca_lock, RW_READER);
5960Sstevel@tonic-gate 			if (sysmcache[i].dca_flags & SYSM_ENABLED) {
5970Sstevel@tonic-gate 				if (infop[0] != NULL)
5980Sstevel@tonic-gate 					(void) strcat(infop, " ");
5990Sstevel@tonic-gate 				(void) strcat(infop, sysmcache[i].dca_name);
6000Sstevel@tonic-gate 			}
6010Sstevel@tonic-gate 			rw_exit(&sysmcache[i].dca_lock);
6020Sstevel@tonic-gate 		}
6030Sstevel@tonic-gate 		if (rval == 0 && copyoutstr(infop, (void *)arg, size, NULL))
6040Sstevel@tonic-gate 			rval = EFAULT;
6050Sstevel@tonic-gate 		break;
6060Sstevel@tonic-gate 
6070Sstevel@tonic-gate 	case CIOCSETCONSOLE:
6080Sstevel@tonic-gate 		if ((rval = checkarg(newdevt)) != 0)
6090Sstevel@tonic-gate 			break;
6100Sstevel@tonic-gate 		/*
6110Sstevel@tonic-gate 		 * The device does not have to be open or disabled to
6120Sstevel@tonic-gate 		 * perform the set console.
6130Sstevel@tonic-gate 		 */
6140Sstevel@tonic-gate 		for (i = 0; i < MAXDEVS; i++) {
6150Sstevel@tonic-gate 			rw_enter(&sysmcache[i].dca_lock, RW_WRITER);
6160Sstevel@tonic-gate 			if (sysmcache[i].dca_devt == newdevt &&
6170Sstevel@tonic-gate 			    (sysmcache[i].dca_flags & SYSM_ENABLED)) {
6180Sstevel@tonic-gate 				(void) strcpy(sysmcache[i].dca_name, infop);
6190Sstevel@tonic-gate 				rval = EEXIST;
6200Sstevel@tonic-gate 				rw_exit(&sysmcache[i].dca_lock);
6210Sstevel@tonic-gate 				break;
6220Sstevel@tonic-gate 			} else if (sysmcache[i].dca_devt == newdevt &&
6230Sstevel@tonic-gate 			    sysmcache[i].dca_flags == SYSM_DISABLED) {
6240Sstevel@tonic-gate 				sysmcache[i].dca_flags |= SYSM_ENABLED;
6250Sstevel@tonic-gate 				(void) strcpy(sysmcache[i].dca_name, infop);
6260Sstevel@tonic-gate 				rw_exit(&sysmcache[i].dca_lock);
6270Sstevel@tonic-gate 				found = 1;
6280Sstevel@tonic-gate 				break;
6290Sstevel@tonic-gate 			} else if (sysmcache[i].dca_devt == 0) {
6300Sstevel@tonic-gate 				ASSERT(sysmcache[i].dca_vp == NULL &&
6310Sstevel@tonic-gate 				    sysmcache[i].dca_flags == SYSM_DISABLED);
6320Sstevel@tonic-gate 				(void) strcpy(sysmcache[i].dca_name, infop);
6330Sstevel@tonic-gate 				sysmcache[i].dca_flags = SYSM_ENABLED;
6340Sstevel@tonic-gate 				sysmcache[i].dca_devt = newdevt;
6350Sstevel@tonic-gate 				rw_exit(&sysmcache[i].dca_lock);
6360Sstevel@tonic-gate 				found = 1;
6370Sstevel@tonic-gate 				break;
6380Sstevel@tonic-gate 			}
6390Sstevel@tonic-gate 			rw_exit(&sysmcache[i].dca_lock);
6400Sstevel@tonic-gate 		}
6410Sstevel@tonic-gate 		if (found == 0 && rval == 0)
6420Sstevel@tonic-gate 			rval = ENOENT;
6430Sstevel@tonic-gate 		break;
6440Sstevel@tonic-gate 
6450Sstevel@tonic-gate 	case CIOCRMCONSOLE:
6460Sstevel@tonic-gate 		for (i = 0; i < MAXDEVS; i++) {
6470Sstevel@tonic-gate 			rw_enter(&sysmcache[i].dca_lock, RW_WRITER);
6480Sstevel@tonic-gate 			if (sysmcache[i].dca_devt == newdevt) {
6490Sstevel@tonic-gate 				sysmcache[i].dca_flags = SYSM_DISABLED;
6500Sstevel@tonic-gate 				sysmcache[i].dca_name[0] = '\0';
6510Sstevel@tonic-gate 				rw_exit(&sysmcache[i].dca_lock);
6520Sstevel@tonic-gate 				found = 1;
6530Sstevel@tonic-gate 				break;
6540Sstevel@tonic-gate 			}
6550Sstevel@tonic-gate 			rw_exit(&sysmcache[i].dca_lock);
6560Sstevel@tonic-gate 		}
6570Sstevel@tonic-gate 		if (found == 0)
6580Sstevel@tonic-gate 			rval = ENOENT;
6590Sstevel@tonic-gate 		break;
6600Sstevel@tonic-gate 
6610Sstevel@tonic-gate 	default:
6620Sstevel@tonic-gate 		break;
6630Sstevel@tonic-gate 	}
6640Sstevel@tonic-gate 
6650Sstevel@tonic-gate err_exit:
6660Sstevel@tonic-gate 	kmem_free(infop, size);
6670Sstevel@tonic-gate 	return (rval);
6680Sstevel@tonic-gate }
6690Sstevel@tonic-gate 
6700Sstevel@tonic-gate /* As with the read, we poll only the default console */
6710Sstevel@tonic-gate 
6720Sstevel@tonic-gate /* ARGSUSED */
6730Sstevel@tonic-gate static int
6740Sstevel@tonic-gate sysmpoll(dev_t dev, short events, int anyyet, short *reventsp,
6750Sstevel@tonic-gate 	struct pollhead **phpp)
6760Sstevel@tonic-gate {
6770Sstevel@tonic-gate 	return (VOP_POLL(dcvp, events, anyyet, reventsp, phpp));
6780Sstevel@tonic-gate }
6790Sstevel@tonic-gate 
6800Sstevel@tonic-gate /* Sanity check that the device is good */
6810Sstevel@tonic-gate static int
6820Sstevel@tonic-gate checkarg(dev_t devt)
6830Sstevel@tonic-gate {
6840Sstevel@tonic-gate 	int rval = 0;
6850Sstevel@tonic-gate 	vnode_t	*vp;
6860Sstevel@tonic-gate 	extern dev_t rwsconsdev, rconsdev, uconsdev;
6870Sstevel@tonic-gate 
6880Sstevel@tonic-gate 	if (devt == rconsdev || devt == rwsconsdev || devt == uconsdev) {
6890Sstevel@tonic-gate 		rval = EBUSY;
6900Sstevel@tonic-gate 		goto err_exit;
6910Sstevel@tonic-gate 	}
6920Sstevel@tonic-gate 	if ((rval = lookupname("/dev/sysmsg", UIO_SYSSPACE, FOLLOW,
6930Sstevel@tonic-gate 	    NULLVPP, &vp)) == 0) {
6940Sstevel@tonic-gate 		if (devt == vp->v_rdev) {
6950Sstevel@tonic-gate 			VN_RELE(vp);
6960Sstevel@tonic-gate 			rval = EINVAL;
6970Sstevel@tonic-gate 			goto err_exit;
6980Sstevel@tonic-gate 		}
6990Sstevel@tonic-gate 		VN_RELE(vp);
7000Sstevel@tonic-gate 	}
7010Sstevel@tonic-gate 	if ((rval = lookupname("/dev/msglog", UIO_SYSSPACE, FOLLOW,
7020Sstevel@tonic-gate 	    NULLVPP, &vp)) == 0) {
7030Sstevel@tonic-gate 		if (devt == vp->v_rdev) {
7040Sstevel@tonic-gate 			VN_RELE(vp);
7050Sstevel@tonic-gate 			rval = EINVAL;
7060Sstevel@tonic-gate 			goto err_exit;
7070Sstevel@tonic-gate 		}
7080Sstevel@tonic-gate 		VN_RELE(vp);
7090Sstevel@tonic-gate 	}
7100Sstevel@tonic-gate 
7110Sstevel@tonic-gate err_exit:
7120Sstevel@tonic-gate 	return (rval);
7130Sstevel@tonic-gate }
714