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