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