xref: /illumos-gate/usr/src/uts/common/io/gentty.c (revision 88f8b78a88cbdc6d8c1af5c3e54bc49d25095c98)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 1990-1992,1996,1998-2003 Sun Microsystems, Inc.
24  * All rights reserved.
25  * Use is subject to license terms.
26  */
27 
28 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
29 /*	  All Rights Reserved  	*/
30 
31 
32 #pragma ident	"%Z%%M%	%I%	%E% SMI"
33 					/* from S5R4 1.22 */
34 
35 /*
36  * Indirect driver for controlling tty.
37  */
38 #include <sys/types.h>
39 #include <sys/errno.h>
40 #include <sys/conf.h>
41 #include <sys/proc.h>
42 #include <sys/tty.h>
43 #include <sys/stream.h>
44 #include <sys/strsubr.h>
45 #include <sys/cred.h>
46 #include <sys/uio.h>
47 #include <sys/session.h>
48 #include <sys/ddi.h>
49 #include <sys/debug.h>
50 #include <sys/stat.h>
51 #include <sys/sunddi.h>
52 #include <sys/param.h>
53 #include <sys/systm.h>
54 #include <sys/modctl.h>
55 #include <sys/fs/snode.h>
56 #include <sys/file.h>
57 
58 #define	IS_STREAM(dev) (devopsp[getmajor(dev)]->devo_cb_ops->cb_str != NULL)
59 
60 int syopen(dev_t *, int, int, cred_t *);
61 int syclose(dev_t, int, int, cred_t *);
62 int syread(dev_t, struct uio *, cred_t *);
63 int sywrite(dev_t, struct uio *, cred_t *);
64 int sypoll(dev_t, short, int, short *, struct pollhead **);
65 int syioctl(dev_t, int, intptr_t, int, cred_t *, int *);
66 
67 static int sy_info(dev_info_t *, ddi_info_cmd_t, void *, void **);
68 static int sy_attach(dev_info_t *, ddi_attach_cmd_t);
69 static dev_info_t *sy_dip;		/* private copy of devinfo pointer */
70 
71 struct cb_ops	sy_cb_ops = {
72 
73 	syopen,			/* open */
74 	syclose,		/* close */
75 	nodev,			/* strategy */
76 	nodev,			/* print */
77 	nodev,			/* dump */
78 	syread,			/* read */
79 	sywrite,		/* write */
80 	syioctl,		/* ioctl */
81 	nodev,			/* devmap */
82 	nodev,			/* mmap */
83 	nodev, 			/* segmap */
84 	sypoll,			/* poll */
85 	ddi_prop_op,		/* cb_prop_op */
86 	0,			/* streamtab  */
87 	D_NEW | D_MP		/* Driver compatibility flag */
88 
89 };
90 
91 struct dev_ops	sy_ops = {
92 
93 	DEVO_REV,		/* devo_rev, */
94 	0,			/* refcnt  */
95 	sy_info,		/* info */
96 	nulldev,		/* identify */
97 	nulldev,		/* probe */
98 	sy_attach,		/* attach */
99 	nodev,			/* detach */
100 	nodev,			/* reset */
101 	&sy_cb_ops,		/* driver operations */
102 	(struct bus_ops *)0	/* bus operations */
103 
104 };
105 
106 
107 extern int nodev(void);
108 extern int nulldev(void);
109 extern int dseekneg_flag;
110 extern struct mod_ops mod_driverops;
111 extern struct dev_ops sy_ops;
112 
113 /*
114  * Module linkage information for the kernel.
115  */
116 
117 static struct modldrv modldrv = {
118 	&mod_driverops, /* Type of module.  This one is a pseudo driver */
119 	"Indirect driver for tty 'sy' %I%",
120 	&sy_ops,	/* driver ops */
121 };
122 
123 static struct modlinkage modlinkage = {
124 	MODREV_1,
125 	&modldrv,
126 	NULL
127 };
128 
129 
130 int
131 _init(void)
132 {
133 	return (mod_install(&modlinkage));
134 }
135 
136 
137 int
138 _fini(void)
139 {
140 	return (mod_remove(&modlinkage));
141 }
142 
143 int
144 _info(struct modinfo *modinfop)
145 {
146 	return (mod_info(&modlinkage, modinfop));
147 }
148 
149 /* ARGSUSED */
150 static int
151 sy_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
152 {
153 	if (ddi_create_minor_node(devi, "tty", S_IFCHR,
154 	    0, DDI_PSEUDO, NULL) == DDI_FAILURE) {
155 		ddi_remove_minor_node(devi, NULL);
156 		return (-1);
157 	}
158 	sy_dip = devi;
159 	return (DDI_SUCCESS);
160 }
161 
162 /* ARGSUSED */
163 static int
164 sy_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
165 {
166 	dev_t dev = (dev_t)arg;
167 	int error;
168 
169 	switch (infocmd) {
170 	case DDI_INFO_DEVT2DEVINFO:
171 		if (sy_dip == NULL) {
172 			*result = (void *)NULL;
173 			error = DDI_FAILURE;
174 		} else {
175 			*result = (void *) sy_dip;
176 			error = DDI_SUCCESS;
177 		}
178 		break;
179 	case DDI_INFO_DEVT2INSTANCE:
180 		if (getminor(dev) != 0) {
181 			*result = (void *)-1;
182 			error = DDI_FAILURE;
183 		} else {
184 			*result = (void *)0;
185 			error = DDI_SUCCESS;
186 		}
187 		break;
188 	default:
189 		error = DDI_FAILURE;
190 	}
191 	return (error);
192 }
193 
194 
195 /* ARGSUSED */
196 int
197 syopen(dev_t *devp, int flag, int otyp, struct cred *cr)
198 {
199 	dev_t	ttyd;
200 	vnode_t	*ttyvp;
201 	sess_t	*sp = curproc->p_sessp;
202 	int	error;
203 
204 	if ((ttyd = sp->s_dev) == NODEV)
205 		return (ENXIO);
206 	TTY_HOLD(sp);
207 	if ((ttyvp = sp->s_vp) == NULL) {
208 		TTY_RELE(sp);
209 		return (EIO);
210 	}
211 
212 	/*
213 	 * Open the control terminal. The control terminal may be
214 	 * opened multiple times and it is closed in freectty().
215 	 * The multi-open, single-clone means that no cloning
216 	 * can happen via this open, hence the assertion.
217 	 */
218 	error = VOP_OPEN(&ttyvp, FNOCTTY | flag, cr);
219 	if (error == 0) {
220 		struct snode *csp;
221 
222 		/*
223 		 * XXX: This driver binds a single minor number to the
224 		 * current controlling tty of the process issueing the
225 		 * open / close.  If we implement a traditional close
226 		 * for this driver then specfs will only invoke this driver
227 		 * on the last close of our one minor number - which is not
228 		 * what we want.  Since we already get the open / close
229 		 * semantic that we want from makectty and freectty, we reach
230 		 * back into the common snode and decrease the open count so
231 		 * that the specfs filtering of all but the last close
232 		 * does not get in our way.  To clean this up, a new cb_flag
233 		 * that causes specfs to call the driver on each close
234 		 * should be considered.
235 		 */
236 		ASSERT(ttyd == ttyvp->v_rdev);
237 		ASSERT(vn_matchops(ttyvp, spec_getvnodeops()));
238 		csp = VTOS(VTOS(ttyvp)->s_commonvp);
239 		mutex_enter(&csp->s_lock);
240 		csp->s_count--;
241 		mutex_exit(&csp->s_lock);
242 	}
243 	TTY_RELE(sp);
244 	return (error);
245 }
246 
247 /* ARGSUSED */
248 int
249 syclose(dev_t dev, int flag, int otyp, struct cred *cr)
250 {
251 	return (0);
252 }
253 
254 /* ARGSUSED */
255 int
256 syread(dev_t dev, struct uio *uiop, struct cred *cr)
257 {
258 	vnode_t *ttyvp;
259 	sess_t	*sp = curproc->p_sessp;
260 	int	error;
261 
262 	if (sp->s_dev == NODEV)
263 		return (ENXIO);
264 	TTY_HOLD(sp);
265 	if ((ttyvp = sp->s_vp) == NULL) {
266 		TTY_RELE(sp);
267 		return (EIO);
268 	}
269 	error = VOP_READ(ttyvp, uiop, 0, cr, NULL);
270 	TTY_RELE(sp);
271 	return (error);
272 
273 }
274 
275 /* ARGSUSED */
276 int
277 sywrite(dev_t dev, struct uio *uiop, struct cred *cr)
278 {
279 	vnode_t *ttyvp;
280 	sess_t	*sp = curproc->p_sessp;
281 	int	error;
282 
283 	if (sp->s_dev == NODEV)
284 		return (ENXIO);
285 	TTY_HOLD(sp);
286 	if ((ttyvp = sp->s_vp) == NULL) {
287 		TTY_RELE(sp);
288 		return (EIO);
289 	}
290 
291 	error = VOP_WRITE(ttyvp, uiop, 0, cr, NULL);
292 	TTY_RELE(sp);
293 	return (error);
294 }
295 
296 
297 /* ARGSUSED */
298 int
299 syioctl(dev_t dev, int cmd, intptr_t arg, int mode, struct cred *cr,
300 	int *rvalp)
301 {
302 	vnode_t *ttyvp;
303 	sess_t	*sp = curproc->p_sessp;
304 	int	error;
305 
306 	if (sp->s_dev == NODEV)
307 		return (ENXIO);
308 	TTY_HOLD(sp);
309 	if ((ttyvp = sp->s_vp) == NULL) {
310 		TTY_RELE(sp);
311 		return (EIO);
312 	}
313 	error = VOP_IOCTL(ttyvp, cmd, arg, mode, cr, rvalp);
314 	TTY_RELE(sp);
315 	return (error);
316 }
317 
318 
319 
320 /* ARGSUSED */
321 int
322 sypoll(dev_t dev, short events, int anyyet, short *reventsp,
323 	struct pollhead **phpp)
324 {
325 	vnode_t *ttyvp;
326 	sess_t  *sp = curproc->p_sessp;
327 	int	error;
328 
329 	if (sp->s_dev == NODEV)
330 		return (ENXIO);
331 	TTY_HOLD(sp);
332 	if ((ttyvp = sp->s_vp) == NULL) {
333 		TTY_RELE(sp);
334 		return (EIO);
335 	}
336 	error = VOP_POLL(ttyvp, events, anyyet, reventsp, phpp);
337 	TTY_RELE(sp);
338 	return (error);
339 }
340