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 * Copyright 2007 Sun Microsystems, Inc. 23 * 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 #pragma ident "%Z%%M% %I% %E% SMI" 32 /* from S5R4 1.22 */ 33 34 /* 35 * Indirect driver for controlling tty. 36 */ 37 #include <sys/types.h> 38 #include <sys/errno.h> 39 #include <sys/conf.h> 40 #include <sys/proc.h> 41 #include <sys/tty.h> 42 #include <sys/stream.h> 43 #include <sys/strsubr.h> 44 #include <sys/cred.h> 45 #include <sys/uio.h> 46 #include <sys/session.h> 47 #include <sys/ddi.h> 48 #include <sys/debug.h> 49 #include <sys/stat.h> 50 #include <sys/sunddi.h> 51 #include <sys/param.h> 52 #include <sys/systm.h> 53 #include <sys/modctl.h> 54 #include <sys/fs/snode.h> 55 #include <sys/file.h> 56 57 #define IS_STREAM(dev) (devopsp[getmajor(dev)]->devo_cb_ops->cb_str != NULL) 58 59 int syopen(dev_t *, int, int, cred_t *); 60 int syclose(dev_t, int, int, cred_t *); 61 int syread(dev_t, struct uio *, cred_t *); 62 int sywrite(dev_t, struct uio *, cred_t *); 63 int sypoll(dev_t, short, int, short *, struct pollhead **); 64 int syioctl(dev_t, int, intptr_t, int, cred_t *, int *); 65 66 static int sy_info(dev_info_t *, ddi_info_cmd_t, void *, void **); 67 static int sy_attach(dev_info_t *, ddi_attach_cmd_t); 68 static dev_info_t *sy_dip; /* private copy of devinfo pointer */ 69 70 struct cb_ops sy_cb_ops = { 71 72 syopen, /* open */ 73 syclose, /* close */ 74 nodev, /* strategy */ 75 nodev, /* print */ 76 nodev, /* dump */ 77 syread, /* read */ 78 sywrite, /* write */ 79 syioctl, /* ioctl */ 80 nodev, /* devmap */ 81 nodev, /* mmap */ 82 nodev, /* segmap */ 83 sypoll, /* poll */ 84 ddi_prop_op, /* cb_prop_op */ 85 0, /* streamtab */ 86 D_NEW | D_MP /* Driver compatibility flag */ 87 88 }; 89 90 struct dev_ops sy_ops = { 91 92 DEVO_REV, /* devo_rev, */ 93 0, /* refcnt */ 94 sy_info, /* info */ 95 nulldev, /* identify */ 96 nulldev, /* probe */ 97 sy_attach, /* attach */ 98 nodev, /* detach */ 99 nodev, /* reset */ 100 &sy_cb_ops, /* driver operations */ 101 (struct bus_ops *)0 /* bus operations */ 102 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' %I%", 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, NULL) == 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