xref: /onnv-gate/usr/src/uts/common/io/sysmsg.c (revision 742:588610e3e562)
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 /*
23560Smeem  * 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>
51560Smeem #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;
148*742Sfl147353 static boolean_t sysmsg_opened;
149*742Sfl147353 static boolean_t msglog_opened;
1500Sstevel@tonic-gate 
1510Sstevel@tonic-gate /* flags for device cache */
1520Sstevel@tonic-gate #define	SYSM_DISABLED	0x0
1530Sstevel@tonic-gate #define	SYSM_ENABLED	0x1
1540Sstevel@tonic-gate 
1550Sstevel@tonic-gate /*
1560Sstevel@tonic-gate  * Module linkage information for the kernel.
1570Sstevel@tonic-gate  */
1580Sstevel@tonic-gate 
1590Sstevel@tonic-gate static struct modldrv modldrv = {
1600Sstevel@tonic-gate 	&mod_driverops, /* Type of module.  This one is a pseudo driver */
1610Sstevel@tonic-gate 	"System message redirection (fanout) driver %I%",
1620Sstevel@tonic-gate 	&sysm_ops,	/* driver ops */
1630Sstevel@tonic-gate };
1640Sstevel@tonic-gate 
1650Sstevel@tonic-gate static struct modlinkage modlinkage = {
1660Sstevel@tonic-gate 	MODREV_1,
1670Sstevel@tonic-gate 	&modldrv,
1680Sstevel@tonic-gate 	NULL
1690Sstevel@tonic-gate };
1700Sstevel@tonic-gate 
1710Sstevel@tonic-gate int
1720Sstevel@tonic-gate _init(void)
1730Sstevel@tonic-gate {
1740Sstevel@tonic-gate 	return (mod_install(&modlinkage));
1750Sstevel@tonic-gate }
1760Sstevel@tonic-gate 
1770Sstevel@tonic-gate int
1780Sstevel@tonic-gate _fini(void)
1790Sstevel@tonic-gate {
1800Sstevel@tonic-gate 	return (mod_remove(&modlinkage));
1810Sstevel@tonic-gate }
1820Sstevel@tonic-gate 
1830Sstevel@tonic-gate int
1840Sstevel@tonic-gate _info(struct modinfo *modinfop)
1850Sstevel@tonic-gate {
1860Sstevel@tonic-gate 	return (mod_info(&modlinkage, modinfop));
1870Sstevel@tonic-gate }
1880Sstevel@tonic-gate 
1890Sstevel@tonic-gate /*
1900Sstevel@tonic-gate  * DDI glue routines
1910Sstevel@tonic-gate  */
1920Sstevel@tonic-gate static int
1930Sstevel@tonic-gate sysm_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
1940Sstevel@tonic-gate {
1950Sstevel@tonic-gate 	int i;
1960Sstevel@tonic-gate 
1970Sstevel@tonic-gate 	switch (cmd) {
1980Sstevel@tonic-gate 	case DDI_ATTACH:
1990Sstevel@tonic-gate 		ASSERT(sysm_dip == NULL);
2000Sstevel@tonic-gate 
2010Sstevel@tonic-gate 		if (ddi_create_minor_node(devi, "sysmsg", S_IFCHR,
202*742Sfl147353 			SYS_SYSMIN, DDI_PSEUDO, NULL) == DDI_FAILURE ||
203*742Sfl147353 			ddi_create_minor_node(devi, "msglog", S_IFCHR,
204*742Sfl147353 			SYS_MSGMIN, DDI_PSEUDO, NULL) == DDI_FAILURE) {
2050Sstevel@tonic-gate 			ddi_remove_minor_node(devi, NULL);
2060Sstevel@tonic-gate 			return (DDI_FAILURE);
2070Sstevel@tonic-gate 		}
2080Sstevel@tonic-gate 
2090Sstevel@tonic-gate 		for (i = 0; i < MAXDEVS; i++) {
2100Sstevel@tonic-gate 			rw_init(&sysmcache[i].dca_lock, NULL, RW_DRIVER, NULL);
2110Sstevel@tonic-gate 		}
2120Sstevel@tonic-gate 
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 
232*742Sfl147353 		for (i = 0; i < MAXDEVS; i++)
2330Sstevel@tonic-gate 			rw_destroy(&sysmcache[i].dca_lock);
2340Sstevel@tonic-gate 
2350Sstevel@tonic-gate 		ddi_remove_minor_node(devi, NULL);
2360Sstevel@tonic-gate 		sysm_dip = NULL;
2370Sstevel@tonic-gate 		return (DDI_SUCCESS);
2380Sstevel@tonic-gate 
2390Sstevel@tonic-gate 	case DDI_SUSPEND:
2400Sstevel@tonic-gate 	case DDI_PM_SUSPEND:
2410Sstevel@tonic-gate 		return (DDI_SUCCESS);
2420Sstevel@tonic-gate 	default:
2430Sstevel@tonic-gate 		return (DDI_FAILURE);
2440Sstevel@tonic-gate 	}
2450Sstevel@tonic-gate 
2460Sstevel@tonic-gate }
2470Sstevel@tonic-gate 
2480Sstevel@tonic-gate /* ARGSUSED */
2490Sstevel@tonic-gate static int
2500Sstevel@tonic-gate sysm_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
2510Sstevel@tonic-gate {
2520Sstevel@tonic-gate 	int rval = DDI_FAILURE;
2530Sstevel@tonic-gate 	minor_t instance;
2540Sstevel@tonic-gate 
2550Sstevel@tonic-gate 	instance = getminor((dev_t)arg);
2560Sstevel@tonic-gate 
2570Sstevel@tonic-gate 	switch (infocmd) {
2580Sstevel@tonic-gate 	case DDI_INFO_DEVT2DEVINFO:
2590Sstevel@tonic-gate 		if (sysm_dip != NULL &&
260*742Sfl147353 			(instance == SYS_SYSMIN || instance == SYS_MSGMIN)) {
2610Sstevel@tonic-gate 			*result = sysm_dip;
2620Sstevel@tonic-gate 			rval = DDI_SUCCESS;
2630Sstevel@tonic-gate 		}
2640Sstevel@tonic-gate 		break;
2650Sstevel@tonic-gate 
2660Sstevel@tonic-gate 	case DDI_INFO_DEVT2INSTANCE:
2670Sstevel@tonic-gate 		if (instance == SYS_SYSMIN || instance == SYS_MSGMIN) {
2680Sstevel@tonic-gate 			*result = NULL;
2690Sstevel@tonic-gate 			rval = DDI_SUCCESS;
2700Sstevel@tonic-gate 		}
2710Sstevel@tonic-gate 		break;
2720Sstevel@tonic-gate 
2730Sstevel@tonic-gate 	default:
2740Sstevel@tonic-gate 		break;
2750Sstevel@tonic-gate 	}
2760Sstevel@tonic-gate 
2770Sstevel@tonic-gate 	return (rval);
2780Sstevel@tonic-gate }
2790Sstevel@tonic-gate 
2800Sstevel@tonic-gate /*
2810Sstevel@tonic-gate  * Parse the contents of the buffer, and bind the named
2820Sstevel@tonic-gate  * devices as auxiliary consoles using our own ioctl routine.
2830Sstevel@tonic-gate  *
2840Sstevel@tonic-gate  * Comments begin with '#' and are terminated only by a newline
2850Sstevel@tonic-gate  * Device names begin with a '/', and are terminated by a newline,
2860Sstevel@tonic-gate  * space, '#' or tab.
2870Sstevel@tonic-gate  */
2880Sstevel@tonic-gate static void
2890Sstevel@tonic-gate parse_buffer(char *buf, ssize_t fsize)
2900Sstevel@tonic-gate {
2910Sstevel@tonic-gate 	char *ebuf = buf + fsize;
2920Sstevel@tonic-gate 	char *devname = NULL;
2930Sstevel@tonic-gate 	int eatcomments = 0;
2940Sstevel@tonic-gate 
2950Sstevel@tonic-gate 	while (buf < ebuf) {
2960Sstevel@tonic-gate 		if (eatcomments) {
2970Sstevel@tonic-gate 			if (*buf++ == '\n')
2980Sstevel@tonic-gate 				eatcomments = 0;
2990Sstevel@tonic-gate 			continue;
3000Sstevel@tonic-gate 		}
3010Sstevel@tonic-gate 		switch (*buf) {
3020Sstevel@tonic-gate 		case '/':
3030Sstevel@tonic-gate 			if (devname == NULL)
3040Sstevel@tonic-gate 				devname = buf;
3050Sstevel@tonic-gate 			break;
3060Sstevel@tonic-gate 		case '#':
3070Sstevel@tonic-gate 			eatcomments = 1;
3080Sstevel@tonic-gate 			/*FALLTHROUGH*/
3090Sstevel@tonic-gate 		case ' ':
3100Sstevel@tonic-gate 		case '\t':
3110Sstevel@tonic-gate 		case '\n':
3120Sstevel@tonic-gate 			*buf = '\0';
3130Sstevel@tonic-gate 			if (devname == NULL)
3140Sstevel@tonic-gate 				break;
3150Sstevel@tonic-gate 			(void) sysmioctl(NODEV, CIOCSETCONSOLE,
316*742Sfl147353 				(intptr_t)devname, FNATIVE|FKIOCTL|FREAD|FWRITE,
317*742Sfl147353 				kcred, NULL);
3180Sstevel@tonic-gate 			devname = NULL;
3190Sstevel@tonic-gate 			break;
3200Sstevel@tonic-gate 		default:
3210Sstevel@tonic-gate 			break;
3220Sstevel@tonic-gate 		}
3230Sstevel@tonic-gate 		buf++;
3240Sstevel@tonic-gate 	}
3250Sstevel@tonic-gate }
3260Sstevel@tonic-gate 
3270Sstevel@tonic-gate #define	CNSADM_BYTES_MAX	2000	/* XXX  nasty fixed size */
3280Sstevel@tonic-gate 
3290Sstevel@tonic-gate static void
3300Sstevel@tonic-gate bind_consadm_conf(char *path)
3310Sstevel@tonic-gate {
3320Sstevel@tonic-gate 	struct vattr vattr;
3330Sstevel@tonic-gate 	vnode_t *vp;
3340Sstevel@tonic-gate 	void *buf;
3350Sstevel@tonic-gate 	size_t size;
3360Sstevel@tonic-gate 	ssize_t resid;
3370Sstevel@tonic-gate 	int err = 0;
3380Sstevel@tonic-gate 
3390Sstevel@tonic-gate 	if (vn_open(path, UIO_SYSSPACE, FREAD, 0, &vp, 0, 0) != 0)
3400Sstevel@tonic-gate 		return;
3410Sstevel@tonic-gate 	vattr.va_mask = AT_SIZE;
3420Sstevel@tonic-gate 	if ((err = VOP_GETATTR(vp, &vattr, 0, kcred)) != 0) {
3430Sstevel@tonic-gate 		cmn_err(CE_WARN, "sysmsg: getattr: '%s': error %d",
344*742Sfl147353 			path, err);
3450Sstevel@tonic-gate 		goto closevp;
3460Sstevel@tonic-gate 	}
3470Sstevel@tonic-gate 
3480Sstevel@tonic-gate 	size = vattr.va_size > CNSADM_BYTES_MAX ?
349*742Sfl147353 		CNSADM_BYTES_MAX : (ssize_t)vattr.va_size;
3500Sstevel@tonic-gate 	buf = kmem_alloc(size, KM_SLEEP);
3510Sstevel@tonic-gate 
3520Sstevel@tonic-gate 	if ((err = vn_rdwr(UIO_READ, vp, buf, size, (offset_t)0,
353*742Sfl147353 		UIO_SYSSPACE, 0, (rlim64_t)0, kcred, &resid)) != 0)
3540Sstevel@tonic-gate 		cmn_err(CE_WARN, "sysmsg: vn_rdwr: '%s': error %d",
3550Sstevel@tonic-gate 		    path, err);
3560Sstevel@tonic-gate 	else
3570Sstevel@tonic-gate 		parse_buffer(buf, size - resid);
3580Sstevel@tonic-gate 
3590Sstevel@tonic-gate 	kmem_free(buf, size);
3600Sstevel@tonic-gate closevp:
3610Sstevel@tonic-gate 	(void) VOP_CLOSE(vp, FREAD, 1, (offset_t)0, kcred);
3620Sstevel@tonic-gate 	VN_RELE(vp);
3630Sstevel@tonic-gate }
3640Sstevel@tonic-gate 
3650Sstevel@tonic-gate /* ARGSUSED */
3660Sstevel@tonic-gate static int
3670Sstevel@tonic-gate sysmopen(dev_t *dev, int flag, int state, cred_t *cred)
3680Sstevel@tonic-gate {
3690Sstevel@tonic-gate 	int	i;
3700Sstevel@tonic-gate 	vnode_t	*vp;
3710Sstevel@tonic-gate 	minor_t instance;
372*742Sfl147353 	static boolean_t initialized;
3730Sstevel@tonic-gate 
3740Sstevel@tonic-gate 	instance = getminor(*dev);
3750Sstevel@tonic-gate 
3760Sstevel@tonic-gate 	if (state != OTYP_CHR || (instance != 0 && instance != 1))
3770Sstevel@tonic-gate 		return (ENXIO);
3780Sstevel@tonic-gate 
3790Sstevel@tonic-gate 	mutex_enter(&dcvp_mutex);
3800Sstevel@tonic-gate 	if ((dcvp == NULL) && (vn_open("/dev/console",
381*742Sfl147353 		UIO_SYSSPACE, FWRITE, 0, &dcvp, 0, 0) != 0)) {
3820Sstevel@tonic-gate 		mutex_exit(&dcvp_mutex);
3830Sstevel@tonic-gate 		return (ENXIO);
3840Sstevel@tonic-gate 	}
385*742Sfl147353 
386*742Sfl147353 	if (instance == SYS_SYSMIN)
387*742Sfl147353 		sysmsg_opened = B_TRUE;
388*742Sfl147353 	else
389*742Sfl147353 		msglog_opened = B_TRUE;
390*742Sfl147353 
391*742Sfl147353 	if (!initialized) {
392*742Sfl147353 		bind_consadm_conf("/etc/consadm.conf");
393*742Sfl147353 		initialized = B_TRUE;
394*742Sfl147353 	}
3950Sstevel@tonic-gate 	mutex_exit(&dcvp_mutex);
3960Sstevel@tonic-gate 
3970Sstevel@tonic-gate 	for (i = 0; i < MAXDEVS; i++) {
3980Sstevel@tonic-gate 		rw_enter(&sysmcache[i].dca_lock, RW_WRITER);
3990Sstevel@tonic-gate 		if ((sysmcache[i].dca_flags & SYSM_ENABLED) &&
400*742Sfl147353 			sysmcache[i].dca_vp == NULL) {
4010Sstevel@tonic-gate 			/*
4020Sstevel@tonic-gate 			 * 4196476 - FTRUNC was causing E10K to return EINVAL
4030Sstevel@tonic-gate 			 * on open
4040Sstevel@tonic-gate 			 */
4050Sstevel@tonic-gate 			flag = flag & ~FTRUNC;
4060Sstevel@tonic-gate 			/*
4070Sstevel@tonic-gate 			 * Open failures on the auxiliary consoles are
4080Sstevel@tonic-gate 			 * not returned because we don't care if some
4090Sstevel@tonic-gate 			 * subset get an error. We know the default console
4100Sstevel@tonic-gate 			 * is okay, and preserve the semantics of the
4110Sstevel@tonic-gate 			 * open for the default console.
4120Sstevel@tonic-gate 			 * Set NONBLOCK|NDELAY in case there's no carrier.
4130Sstevel@tonic-gate 			 */
4140Sstevel@tonic-gate 			if (vn_open(sysmcache[i].dca_name, UIO_SYSSPACE,
415*742Sfl147353 				flag | FNONBLOCK | FNDELAY, 0, &vp, 0, 0) == 0)
4160Sstevel@tonic-gate 				sysmcache[i].dca_vp = vp;
4170Sstevel@tonic-gate 		}
4180Sstevel@tonic-gate 		rw_exit(&sysmcache[i].dca_lock);
4190Sstevel@tonic-gate 	}
4200Sstevel@tonic-gate 
4210Sstevel@tonic-gate 	return (0);
4220Sstevel@tonic-gate }
4230Sstevel@tonic-gate 
4240Sstevel@tonic-gate /* ARGSUSED */
4250Sstevel@tonic-gate static int
4260Sstevel@tonic-gate sysmclose(dev_t dev, int flag, int state, cred_t *cred)
4270Sstevel@tonic-gate {
4280Sstevel@tonic-gate 	int	i;
429*742Sfl147353 	minor_t instance;
430*742Sfl147353 
431*742Sfl147353 	ASSERT(dcvp != NULL);
4320Sstevel@tonic-gate 
4330Sstevel@tonic-gate 	if (state != OTYP_CHR)
4340Sstevel@tonic-gate 		return (ENXIO);
4350Sstevel@tonic-gate 
436*742Sfl147353 	instance = getminor(dev);
437*742Sfl147353 
438*742Sfl147353 	mutex_enter(&dcvp_mutex);
439*742Sfl147353 	if (instance == SYS_SYSMIN)
440*742Sfl147353 		sysmsg_opened = B_FALSE;
441*742Sfl147353 	else
442*742Sfl147353 		msglog_opened = B_FALSE;
443*742Sfl147353 
444*742Sfl147353 	if (sysmsg_opened || msglog_opened) {
445*742Sfl147353 		mutex_exit(&dcvp_mutex);
446*742Sfl147353 		return (0);
447*742Sfl147353 	}
448*742Sfl147353 
449*742Sfl147353 	(void) VOP_CLOSE(dcvp, FWRITE, 1, (offset_t)0, kcred);
450*742Sfl147353 	VN_RELE(dcvp);
451*742Sfl147353 	dcvp = NULL;
452*742Sfl147353 	mutex_exit(&dcvp_mutex);
453*742Sfl147353 
4540Sstevel@tonic-gate 	/*
4550Sstevel@tonic-gate 	 * Close the auxiliary consoles, we're not concerned with
4560Sstevel@tonic-gate 	 * passing up the errors.
4570Sstevel@tonic-gate 	 */
4580Sstevel@tonic-gate 	for (i = 0; i < MAXDEVS; i++) {
4590Sstevel@tonic-gate 		rw_enter(&sysmcache[i].dca_lock, RW_WRITER);
4600Sstevel@tonic-gate 		if (sysmcache[i].dca_vp != NULL) {
4610Sstevel@tonic-gate 			(void) VOP_CLOSE(sysmcache[i].dca_vp, flag,
462*742Sfl147353 				1, (offset_t)0, cred);
4630Sstevel@tonic-gate 			VN_RELE(sysmcache[i].dca_vp);
4640Sstevel@tonic-gate 			sysmcache[i].dca_vp = NULL;
4650Sstevel@tonic-gate 		}
4660Sstevel@tonic-gate 		rw_exit(&sysmcache[i].dca_lock);
4670Sstevel@tonic-gate 	}
4680Sstevel@tonic-gate 
4690Sstevel@tonic-gate 	return (0);
4700Sstevel@tonic-gate }
4710Sstevel@tonic-gate 
4720Sstevel@tonic-gate /* Reads occur only on the default console */
4730Sstevel@tonic-gate 
4740Sstevel@tonic-gate /* ARGSUSED */
4750Sstevel@tonic-gate static int
4760Sstevel@tonic-gate sysmread(dev_t dev, struct uio *uio, cred_t *cred)
4770Sstevel@tonic-gate {
4780Sstevel@tonic-gate 	ASSERT(dcvp != NULL);
4790Sstevel@tonic-gate 	return (VOP_READ(dcvp, uio, 0, cred, NULL));
4800Sstevel@tonic-gate }
4810Sstevel@tonic-gate 
4820Sstevel@tonic-gate /* ARGSUSED */
4830Sstevel@tonic-gate static int
4840Sstevel@tonic-gate sysmwrite(dev_t dev, struct uio *uio, cred_t *cred)
4850Sstevel@tonic-gate {
4860Sstevel@tonic-gate 	int	i = 0;
4870Sstevel@tonic-gate 	iovec_t	uio_iov;
4880Sstevel@tonic-gate 	struct uio	tuio;
4890Sstevel@tonic-gate 
4900Sstevel@tonic-gate 	ASSERT(dcvp != NULL);
4910Sstevel@tonic-gate 	ASSERT(uio != NULL);
4920Sstevel@tonic-gate 
4930Sstevel@tonic-gate 	for (i = 0; i < MAXDEVS; i++) {
4940Sstevel@tonic-gate 		rw_enter(&sysmcache[i].dca_lock, RW_READER);
4950Sstevel@tonic-gate 		if (sysmcache[i].dca_vp != NULL &&
496*742Sfl147353 			(sysmcache[i].dca_flags & SYSM_ENABLED)) {
4970Sstevel@tonic-gate 			tuio = *uio;
4980Sstevel@tonic-gate 			uio_iov = *(uio->uio_iov);
4990Sstevel@tonic-gate 			tuio.uio_iov = &uio_iov;
5000Sstevel@tonic-gate 			(void) VOP_WRITE(sysmcache[i].dca_vp, &tuio, 0, cred,
5010Sstevel@tonic-gate 				NULL);
5020Sstevel@tonic-gate 		}
5030Sstevel@tonic-gate 		rw_exit(&sysmcache[i].dca_lock);
5040Sstevel@tonic-gate 	}
5050Sstevel@tonic-gate 	return (VOP_WRITE(dcvp, uio, 0, cred, NULL));
5060Sstevel@tonic-gate }
5070Sstevel@tonic-gate 
5080Sstevel@tonic-gate /* ARGSUSED */
5090Sstevel@tonic-gate static int
5100Sstevel@tonic-gate sysmioctl(dev_t dev, int cmd, intptr_t arg, int flag, cred_t *cred, int *rvalp)
5110Sstevel@tonic-gate {
5120Sstevel@tonic-gate 	int	rval = 0;
5130Sstevel@tonic-gate 	int	error = 0;
5140Sstevel@tonic-gate 	size_t	size = 0;
5150Sstevel@tonic-gate 	int	i;
5160Sstevel@tonic-gate 	char	*infop;
5170Sstevel@tonic-gate 	char	found = 0;
5180Sstevel@tonic-gate 	dev_t	newdevt = (dev_t)NODEV;	/* because 0 == /dev/console */
5190Sstevel@tonic-gate 	vnode_t	*vp;
5200Sstevel@tonic-gate 
5210Sstevel@tonic-gate 	switch (cmd) {
5220Sstevel@tonic-gate 	case CIOCGETCONSOLE:
5230Sstevel@tonic-gate 		/* Sum over the number of enabled devices */
5240Sstevel@tonic-gate 		for (i = 0; i < MAXDEVS; i++) {
5250Sstevel@tonic-gate 			if (sysmcache[i].dca_flags & SYSM_ENABLED)
5260Sstevel@tonic-gate 				/* list is space separated, followed by NULL */
5270Sstevel@tonic-gate 				size += strlen(sysmcache[i].dca_name) + 1;
5280Sstevel@tonic-gate 		}
5290Sstevel@tonic-gate 		if (size == 0)
5300Sstevel@tonic-gate 			return (0);
5310Sstevel@tonic-gate 		break;
5320Sstevel@tonic-gate 	case CIOCSETCONSOLE:
5330Sstevel@tonic-gate 	case CIOCRMCONSOLE:
5340Sstevel@tonic-gate 		size = sizeof (sysmcache[0].dca_name);
5350Sstevel@tonic-gate 		break;
5360Sstevel@tonic-gate 	case CIOCTTYCONSOLE:
5370Sstevel@tonic-gate 	{
5380Sstevel@tonic-gate 		dev_t	d;
5390Sstevel@tonic-gate 		dev32_t	d32;
5400Sstevel@tonic-gate 		extern dev_t rwsconsdev, rconsdev, uconsdev;
5410Sstevel@tonic-gate 		proc_t	*p;
5420Sstevel@tonic-gate 
5430Sstevel@tonic-gate 		if (drv_getparm(UPROCP, &p) != 0)
5440Sstevel@tonic-gate 			return (ENODEV);
5450Sstevel@tonic-gate 		else
5460Sstevel@tonic-gate 			d = cttydev(p);
5470Sstevel@tonic-gate 		/*
5480Sstevel@tonic-gate 		 * If the controlling terminal is the real
5490Sstevel@tonic-gate 		 * or workstation console device, map to what the
5500Sstevel@tonic-gate 		 * user thinks is the console device.
5510Sstevel@tonic-gate 		 */
5520Sstevel@tonic-gate 		if (d == rwsconsdev || d == rconsdev)
5530Sstevel@tonic-gate 			d = uconsdev;
5540Sstevel@tonic-gate 		if ((flag & FMODELS) != FNATIVE) {
5550Sstevel@tonic-gate 			if (!cmpldev(&d32, d))
5560Sstevel@tonic-gate 				return (EOVERFLOW);
5570Sstevel@tonic-gate 			if (ddi_copyout(&d32, (caddr_t)arg, sizeof (d32),
5580Sstevel@tonic-gate 			    flag))
5590Sstevel@tonic-gate 				return (EFAULT);
5600Sstevel@tonic-gate 		} else {
5610Sstevel@tonic-gate 			if (ddi_copyout(&d, (caddr_t)arg, sizeof (d), flag))
5620Sstevel@tonic-gate 				return (EFAULT);
5630Sstevel@tonic-gate 		}
5640Sstevel@tonic-gate 		return (0);
5650Sstevel@tonic-gate 	}
5660Sstevel@tonic-gate 	default:
5670Sstevel@tonic-gate 		/* everything else is sent to the console device */
5680Sstevel@tonic-gate 		return (VOP_IOCTL(dcvp, cmd, arg, flag, cred, rvalp));
5690Sstevel@tonic-gate 	}
5700Sstevel@tonic-gate 
5710Sstevel@tonic-gate 	if ((rval = secpolicy_console(cred)) != 0)
5720Sstevel@tonic-gate 		return (EPERM);
5730Sstevel@tonic-gate 
5740Sstevel@tonic-gate 	infop = kmem_alloc(size, KM_SLEEP);
5750Sstevel@tonic-gate 	if (flag & FKIOCTL)
5760Sstevel@tonic-gate 		error = copystr((caddr_t)arg, infop, size, NULL);
5770Sstevel@tonic-gate 	else
5780Sstevel@tonic-gate 		error = copyinstr((caddr_t)arg, infop, size, NULL);
5790Sstevel@tonic-gate 
5800Sstevel@tonic-gate 	if (error) {
5810Sstevel@tonic-gate 		switch (cmd) {
5820Sstevel@tonic-gate 		case CIOCGETCONSOLE:
5830Sstevel@tonic-gate 			/*
5840Sstevel@tonic-gate 			 * If the buffer is null, then return a byte count
5850Sstevel@tonic-gate 			 * to user land.
5860Sstevel@tonic-gate 			 */
5870Sstevel@tonic-gate 			*rvalp = size;
5880Sstevel@tonic-gate 			goto err_exit;
5890Sstevel@tonic-gate 		default:
5900Sstevel@tonic-gate 			rval = EFAULT;
5910Sstevel@tonic-gate 			goto err_exit;
5920Sstevel@tonic-gate 		}
5930Sstevel@tonic-gate 	}
5940Sstevel@tonic-gate 
5950Sstevel@tonic-gate 	if (infop[0] != NULL) {
5960Sstevel@tonic-gate 		if ((rval = lookupname(infop, UIO_SYSSPACE, FOLLOW,
597*742Sfl147353 			NULLVPP, &vp)) == 0) {
5980Sstevel@tonic-gate 			if (vp->v_type != VCHR) {
5990Sstevel@tonic-gate 				VN_RELE(vp);
6000Sstevel@tonic-gate 				rval = EINVAL;
6010Sstevel@tonic-gate 				goto err_exit;
6020Sstevel@tonic-gate 			}
6030Sstevel@tonic-gate 			newdevt = vp->v_rdev;
6040Sstevel@tonic-gate 			VN_RELE(vp);
6050Sstevel@tonic-gate 		} else
6060Sstevel@tonic-gate 			goto err_exit;
6070Sstevel@tonic-gate 	}
6080Sstevel@tonic-gate 
6090Sstevel@tonic-gate 	switch (cmd) {
6100Sstevel@tonic-gate 	case CIOCGETCONSOLE:
6110Sstevel@tonic-gate 		/*
6120Sstevel@tonic-gate 		 * Return the list of device names that are enabled.
6130Sstevel@tonic-gate 		 */
6140Sstevel@tonic-gate 		for (i = 0; i < MAXDEVS; i++) {
6150Sstevel@tonic-gate 			rw_enter(&sysmcache[i].dca_lock, RW_READER);
6160Sstevel@tonic-gate 			if (sysmcache[i].dca_flags & SYSM_ENABLED) {
6170Sstevel@tonic-gate 				if (infop[0] != NULL)
6180Sstevel@tonic-gate 					(void) strcat(infop, " ");
6190Sstevel@tonic-gate 				(void) strcat(infop, sysmcache[i].dca_name);
6200Sstevel@tonic-gate 			}
6210Sstevel@tonic-gate 			rw_exit(&sysmcache[i].dca_lock);
6220Sstevel@tonic-gate 		}
6230Sstevel@tonic-gate 		if (rval == 0 && copyoutstr(infop, (void *)arg, size, NULL))
6240Sstevel@tonic-gate 			rval = EFAULT;
6250Sstevel@tonic-gate 		break;
6260Sstevel@tonic-gate 
6270Sstevel@tonic-gate 	case CIOCSETCONSOLE:
6280Sstevel@tonic-gate 		if ((rval = checkarg(newdevt)) != 0)
6290Sstevel@tonic-gate 			break;
6300Sstevel@tonic-gate 		/*
6310Sstevel@tonic-gate 		 * The device does not have to be open or disabled to
6320Sstevel@tonic-gate 		 * perform the set console.
6330Sstevel@tonic-gate 		 */
6340Sstevel@tonic-gate 		for (i = 0; i < MAXDEVS; i++) {
6350Sstevel@tonic-gate 			rw_enter(&sysmcache[i].dca_lock, RW_WRITER);
6360Sstevel@tonic-gate 			if (sysmcache[i].dca_devt == newdevt &&
637*742Sfl147353 				(sysmcache[i].dca_flags & SYSM_ENABLED)) {
6380Sstevel@tonic-gate 				(void) strcpy(sysmcache[i].dca_name, infop);
6390Sstevel@tonic-gate 				rval = EEXIST;
6400Sstevel@tonic-gate 				rw_exit(&sysmcache[i].dca_lock);
6410Sstevel@tonic-gate 				break;
6420Sstevel@tonic-gate 			} else if (sysmcache[i].dca_devt == newdevt &&
643*742Sfl147353 				sysmcache[i].dca_flags == SYSM_DISABLED) {
6440Sstevel@tonic-gate 				sysmcache[i].dca_flags |= SYSM_ENABLED;
6450Sstevel@tonic-gate 				(void) strcpy(sysmcache[i].dca_name, infop);
6460Sstevel@tonic-gate 				rw_exit(&sysmcache[i].dca_lock);
6470Sstevel@tonic-gate 				found = 1;
6480Sstevel@tonic-gate 				break;
6490Sstevel@tonic-gate 			} else if (sysmcache[i].dca_devt == 0) {
6500Sstevel@tonic-gate 				ASSERT(sysmcache[i].dca_vp == NULL &&
651*742Sfl147353 				sysmcache[i].dca_flags == SYSM_DISABLED);
6520Sstevel@tonic-gate 				(void) strcpy(sysmcache[i].dca_name, infop);
6530Sstevel@tonic-gate 				sysmcache[i].dca_flags = SYSM_ENABLED;
6540Sstevel@tonic-gate 				sysmcache[i].dca_devt = newdevt;
6550Sstevel@tonic-gate 				rw_exit(&sysmcache[i].dca_lock);
6560Sstevel@tonic-gate 				found = 1;
6570Sstevel@tonic-gate 				break;
6580Sstevel@tonic-gate 			}
6590Sstevel@tonic-gate 			rw_exit(&sysmcache[i].dca_lock);
6600Sstevel@tonic-gate 		}
6610Sstevel@tonic-gate 		if (found == 0 && rval == 0)
6620Sstevel@tonic-gate 			rval = ENOENT;
6630Sstevel@tonic-gate 		break;
6640Sstevel@tonic-gate 
6650Sstevel@tonic-gate 	case CIOCRMCONSOLE:
6660Sstevel@tonic-gate 		for (i = 0; i < MAXDEVS; i++) {
6670Sstevel@tonic-gate 			rw_enter(&sysmcache[i].dca_lock, RW_WRITER);
6680Sstevel@tonic-gate 			if (sysmcache[i].dca_devt == newdevt) {
6690Sstevel@tonic-gate 				sysmcache[i].dca_flags = SYSM_DISABLED;
6700Sstevel@tonic-gate 				sysmcache[i].dca_name[0] = '\0';
6710Sstevel@tonic-gate 				rw_exit(&sysmcache[i].dca_lock);
6720Sstevel@tonic-gate 				found = 1;
6730Sstevel@tonic-gate 				break;
6740Sstevel@tonic-gate 			}
6750Sstevel@tonic-gate 			rw_exit(&sysmcache[i].dca_lock);
6760Sstevel@tonic-gate 		}
6770Sstevel@tonic-gate 		if (found == 0)
6780Sstevel@tonic-gate 			rval = ENOENT;
6790Sstevel@tonic-gate 		break;
6800Sstevel@tonic-gate 
6810Sstevel@tonic-gate 	default:
6820Sstevel@tonic-gate 		break;
6830Sstevel@tonic-gate 	}
6840Sstevel@tonic-gate 
6850Sstevel@tonic-gate err_exit:
6860Sstevel@tonic-gate 	kmem_free(infop, size);
6870Sstevel@tonic-gate 	return (rval);
6880Sstevel@tonic-gate }
6890Sstevel@tonic-gate 
6900Sstevel@tonic-gate /* As with the read, we poll only the default console */
6910Sstevel@tonic-gate 
6920Sstevel@tonic-gate /* ARGSUSED */
6930Sstevel@tonic-gate static int
6940Sstevel@tonic-gate sysmpoll(dev_t dev, short events, int anyyet, short *reventsp,
6950Sstevel@tonic-gate 	struct pollhead **phpp)
6960Sstevel@tonic-gate {
6970Sstevel@tonic-gate 	return (VOP_POLL(dcvp, events, anyyet, reventsp, phpp));
6980Sstevel@tonic-gate }
6990Sstevel@tonic-gate 
7000Sstevel@tonic-gate /* Sanity check that the device is good */
7010Sstevel@tonic-gate static int
7020Sstevel@tonic-gate checkarg(dev_t devt)
7030Sstevel@tonic-gate {
7040Sstevel@tonic-gate 	int rval = 0;
705*742Sfl147353 	dev_t sysmsg_dev, msglog_dev;
7060Sstevel@tonic-gate 	extern dev_t rwsconsdev, rconsdev, uconsdev;
7070Sstevel@tonic-gate 
7080Sstevel@tonic-gate 	if (devt == rconsdev || devt == rwsconsdev || devt == uconsdev) {
7090Sstevel@tonic-gate 		rval = EBUSY;
710*742Sfl147353 	} else {
711*742Sfl147353 		sysmsg_dev = makedevice(ddi_driver_major(sysm_dip), SYS_SYSMIN);
712*742Sfl147353 		msglog_dev = makedevice(ddi_driver_major(sysm_dip), SYS_MSGMIN);
713*742Sfl147353 		if (devt == sysmsg_dev || devt == msglog_dev)
7140Sstevel@tonic-gate 			rval = EINVAL;
7150Sstevel@tonic-gate 	}
7160Sstevel@tonic-gate 
7170Sstevel@tonic-gate 	return (rval);
7180Sstevel@tonic-gate }
719