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, 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 }