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, NULL } 125 }; 126 127 128 int 129 _init(void) 130 { 131 return (mod_install(&modlinkage)); 132 } 133 134 135 int 136 _fini(void) 137 { 138 return (mod_remove(&modlinkage)); 139 } 140 141 int 142 _info(struct modinfo *modinfop) 143 { 144 return (mod_info(&modlinkage, modinfop)); 145 } 146 147 /* ARGSUSED */ 148 static int 149 sy_attach(dev_info_t *devi, ddi_attach_cmd_t cmd) 150 { 151 if (ddi_create_minor_node(devi, "tty", S_IFCHR, 152 0, DDI_PSEUDO, NULL) == DDI_FAILURE) { 153 ddi_remove_minor_node(devi, NULL); 154 return (-1); 155 } 156 sy_dip = devi; 157 return (DDI_SUCCESS); 158 } 159 160 /* ARGSUSED */ 161 static int 162 sy_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result) 163 { 164 dev_t dev = (dev_t)arg; 165 int error; 166 167 switch (infocmd) { 168 case DDI_INFO_DEVT2DEVINFO: 169 if (sy_dip == NULL) { 170 *result = (void *)NULL; 171 error = DDI_FAILURE; 172 } else { 173 *result = (void *) sy_dip; 174 error = DDI_SUCCESS; 175 } 176 break; 177 case DDI_INFO_DEVT2INSTANCE: 178 if (getminor(dev) != 0) { 179 *result = (void *)-1; 180 error = DDI_FAILURE; 181 } else { 182 *result = (void *)0; 183 error = DDI_SUCCESS; 184 } 185 break; 186 default: 187 error = DDI_FAILURE; 188 } 189 return (error); 190 } 191 192 193 /* ARGSUSED */ 194 int 195 syopen(dev_t *devp, int flag, int otyp, struct cred *cr) 196 { 197 dev_t ttyd; 198 vnode_t *ttyvp; 199 sess_t *sp; 200 int error; 201 202 if ((sp = tty_hold()) == NULL) 203 return (EINTR); 204 205 if (sp->s_dev == NODEV) { 206 tty_rele(sp); 207 return (ENXIO); 208 } 209 210 ttyd = sp->s_dev; 211 ttyvp = sp->s_vp; 212 213 /* 214 * Open the control terminal. The control terminal may be 215 * opened multiple times and it is closed in freectty(). 216 * The multi-open, single-clone means that no cloning 217 * can happen via this open, hence the assertion. 218 */ 219 error = VOP_OPEN(&ttyvp, FNOCTTY | flag, cr, NULL); 220 if (error == 0) { 221 struct snode *csp; 222 223 /* 224 * XXX: This driver binds a single minor number to the 225 * current controlling tty of the process issueing the 226 * open / close. If we implement a traditional close 227 * for this driver then specfs will only invoke this driver 228 * on the last close of our one minor number - which is not 229 * what we want. Since we already get the open / close 230 * semantic that we want from makectty and freectty, we reach 231 * back into the common snode and decrease the open count so 232 * that the specfs filtering of all but the last close 233 * does not get in our way. To clean this up, a new cb_flag 234 * that causes specfs to call the driver on each close 235 * should be considered. 236 */ 237 ASSERT(ttyd == ttyvp->v_rdev); 238 ASSERT(vn_matchops(ttyvp, spec_getvnodeops())); 239 csp = VTOS(VTOS(ttyvp)->s_commonvp); 240 mutex_enter(&csp->s_lock); 241 ASSERT(csp->s_count > 1); 242 csp->s_count--; 243 mutex_exit(&csp->s_lock); 244 } 245 246 tty_rele(sp); 247 return (error); 248 } 249 250 /* ARGSUSED */ 251 int 252 syclose(dev_t dev, int flag, int otyp, struct cred *cr) 253 { 254 return (0); 255 } 256 257 /* ARGSUSED */ 258 int 259 syread(dev_t dev, struct uio *uiop, struct cred *cr) 260 { 261 sess_t *sp; 262 int error; 263 264 if ((sp = tty_hold()) == NULL) 265 return (EINTR); 266 267 if (sp->s_dev == NODEV) { 268 tty_rele(sp); 269 return (ENXIO); 270 } 271 272 error = VOP_READ(sp->s_vp, uiop, 0, cr, NULL); 273 274 tty_rele(sp); 275 return (error); 276 } 277 278 /* ARGSUSED */ 279 int 280 sywrite(dev_t dev, struct uio *uiop, struct cred *cr) 281 { 282 sess_t *sp; 283 int error; 284 285 if ((sp = tty_hold()) == NULL) 286 return (EINTR); 287 288 if (sp->s_dev == NODEV) { 289 tty_rele(sp); 290 return (ENXIO); 291 } 292 293 error = VOP_WRITE(sp->s_vp, uiop, 0, cr, NULL); 294 295 tty_rele(sp); 296 return (error); 297 } 298 299 300 /* ARGSUSED */ 301 int 302 syioctl(dev_t dev, int cmd, intptr_t arg, int mode, struct cred *cr, 303 int *rvalp) 304 { 305 sess_t *sp; 306 int error; 307 308 if (cmd == TIOCNOTTY) { 309 /* 310 * we can't allow this ioctl. the reason is that it 311 * attempts to remove the ctty for a session. to do 312 * this the ctty can't be in use but we grab a hold on 313 * the current ctty (via tty_hold) to perform this ioctl. 314 * if we were to allow this ioctl to pass through we 315 * would deadlock with ourselves. 316 */ 317 return (EINVAL); 318 } 319 320 if ((sp = tty_hold()) == NULL) 321 return (EINTR); 322 323 if (sp->s_dev == NODEV) { 324 tty_rele(sp); 325 return (ENXIO); 326 } 327 328 error = VOP_IOCTL(sp->s_vp, cmd, arg, mode, cr, rvalp, NULL); 329 330 tty_rele(sp); 331 return (error); 332 } 333 334 335 336 /* ARGSUSED */ 337 int 338 sypoll(dev_t dev, short events, int anyyet, short *reventsp, 339 struct pollhead **phpp) 340 { 341 sess_t *sp; 342 int error; 343 344 if ((sp = tty_hold()) == NULL) 345 return (EINTR); 346 347 if (sp->s_dev == NODEV) { 348 tty_rele(sp); 349 return (ENXIO); 350 } 351 352 error = VOP_POLL(sp->s_vp, events, anyyet, reventsp, phpp, NULL); 353 354 tty_rele(sp); 355 return (error); 356 }