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 }