Annotation of sys/kern/tty_pty.c, Revision 1.1
1.1 ! nbrk 1: /* $OpenBSD: tty_pty.c,v 1.34 2007/05/29 00:17:32 thib Exp $ */
! 2: /* $NetBSD: tty_pty.c,v 1.33.4.1 1996/06/02 09:08:11 mrg Exp $ */
! 3:
! 4: /*
! 5: * Copyright (c) 1982, 1986, 1989, 1993
! 6: * The Regents of the University of California. All rights reserved.
! 7: *
! 8: * Redistribution and use in source and binary forms, with or without
! 9: * modification, are permitted provided that the following conditions
! 10: * are met:
! 11: * 1. Redistributions of source code must retain the above copyright
! 12: * notice, this list of conditions and the following disclaimer.
! 13: * 2. Redistributions in binary form must reproduce the above copyright
! 14: * notice, this list of conditions and the following disclaimer in the
! 15: * documentation and/or other materials provided with the distribution.
! 16: * 3. Neither the name of the University nor the names of its contributors
! 17: * may be used to endorse or promote products derived from this software
! 18: * without specific prior written permission.
! 19: *
! 20: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
! 21: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
! 22: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
! 23: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
! 24: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
! 25: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
! 26: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
! 27: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
! 28: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
! 29: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
! 30: * SUCH DAMAGE.
! 31: *
! 32: * @(#)tty_pty.c 8.4 (Berkeley) 2/20/95
! 33: */
! 34:
! 35: /*
! 36: * Pseudo-teletype Driver
! 37: * (Actually two drivers, requiring two entries in 'cdevsw')
! 38: */
! 39:
! 40: #include <sys/param.h>
! 41: #include <sys/systm.h>
! 42: #include <sys/namei.h>
! 43: #include <sys/mount.h>
! 44: #include <sys/ioctl.h>
! 45: #include <sys/proc.h>
! 46: #include <sys/tty.h>
! 47: #include <sys/file.h>
! 48: #include <sys/filedesc.h>
! 49: #include <sys/uio.h>
! 50: #include <sys/kernel.h>
! 51: #include <sys/malloc.h>
! 52: #include <sys/vnode.h>
! 53: #include <sys/signalvar.h>
! 54: #include <sys/uio.h>
! 55: #include <sys/conf.h>
! 56: #include <sys/stat.h>
! 57: #include <sys/sysctl.h>
! 58: #include <sys/poll.h>
! 59: #include <sys/rwlock.h>
! 60:
! 61: #define BUFSIZ 100 /* Chunk size iomoved to/from user */
! 62:
! 63: /*
! 64: * pts == /dev/tty[p-zP-T][0-9a-zA-Z]
! 65: * ptc == /dev/pty[p-zP-T][0-9a-zA-Z]
! 66: */
! 67:
! 68: /* XXX this needs to come from somewhere sane, and work with MAKEDEV */
! 69: #define TTY_LETTERS "pqrstuvwxyzPQRST"
! 70: #define TTY_SUFFIX "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
! 71:
! 72: static int pts_major;
! 73:
! 74: struct pt_softc {
! 75: struct tty *pt_tty;
! 76: int pt_flags;
! 77: struct selinfo pt_selr, pt_selw;
! 78: u_char pt_send;
! 79: u_char pt_ucntl;
! 80: char pty_pn[11];
! 81: char pty_sn[11];
! 82: };
! 83:
! 84: #define NPTY_MIN 8 /* number of initial ptys */
! 85: #define NPTY_MAX 992 /* maximum number of ptys supported */
! 86:
! 87: static struct pt_softc **pt_softc = NULL; /* pty array */
! 88: static int npty = 0; /* size of pty array */
! 89: static int maxptys = NPTY_MAX; /* maximum number of ptys */
! 90: /* for pty array */
! 91: struct rwlock pt_softc_lock = RWLOCK_INITIALIZER("ptarrlk");
! 92:
! 93: #define PF_PKT 0x08 /* packet mode */
! 94: #define PF_STOPPED 0x10 /* user told stopped */
! 95: #define PF_REMOTE 0x20 /* remote and flow controlled input */
! 96: #define PF_NOSTOP 0x40
! 97: #define PF_UCNTL 0x80 /* user control mode */
! 98:
! 99: void ptyattach(int);
! 100: void ptcwakeup(struct tty *, int);
! 101: struct tty *ptytty(dev_t);
! 102: void ptsstart(struct tty *);
! 103: int sysctl_pty(int *, u_int, void *, size_t *, void *, size_t);
! 104:
! 105: void filt_ptcrdetach(struct knote *);
! 106: int filt_ptcread(struct knote *, long);
! 107: void filt_ptcwdetach(struct knote *);
! 108: int filt_ptcwrite(struct knote *, long);
! 109:
! 110: static struct pt_softc **ptyarralloc(int);
! 111: static int check_pty(int);
! 112:
! 113: static gid_t tty_gid = TTY_GID;
! 114:
! 115: void ptydevname(int, struct pt_softc *);
! 116: dev_t pty_getfree(void);
! 117:
! 118: void ptmattach(int);
! 119: int ptmopen(dev_t, int, int, struct proc *);
! 120: int ptmclose(dev_t, int, int, struct proc *);
! 121: int ptmread(dev_t, struct uio *, int);
! 122: int ptmwrite(dev_t, struct uio *, int);
! 123: int ptmwrite(dev_t, struct uio *, int);
! 124: int ptmioctl(dev_t, u_long, caddr_t, int, struct proc *p);
! 125: int ptmpoll(dev_t, int, struct proc *p);
! 126: static int ptm_vn_open(struct nameidata *);
! 127:
! 128: void
! 129: ptydevname(int minor, struct pt_softc *pti)
! 130: {
! 131: char buf[11] = "/dev/XtyXX";
! 132: int i, j;
! 133:
! 134: i = minor / (sizeof(TTY_SUFFIX) - 1);
! 135: j = minor % (sizeof(TTY_SUFFIX) - 1);
! 136: if (i >= sizeof(TTY_LETTERS) - 1) {
! 137: pti->pty_pn[0] = '\0';
! 138: pti->pty_sn[0] = '\0';
! 139: return;
! 140: }
! 141: buf[5] = 'p';
! 142: buf[8] = TTY_LETTERS[i];
! 143: buf[9] = TTY_SUFFIX[j];
! 144: memcpy(pti->pty_pn, buf, sizeof(buf));
! 145: buf[5] = 't';
! 146: memcpy(pti->pty_sn, buf, sizeof(buf));
! 147: }
! 148:
! 149: /*
! 150: * Allocate and zero array of nelem elements.
! 151: */
! 152: struct pt_softc **
! 153: ptyarralloc(int nelem)
! 154: {
! 155: struct pt_softc **pt;
! 156:
! 157: pt = malloc(nelem * sizeof(struct pt_softc *), M_DEVBUF, M_WAITOK);
! 158: bzero(pt, nelem * sizeof(struct pt_softc *));
! 159: return pt;
! 160: }
! 161:
! 162: /*
! 163: * Check if the minor is correct and ensure necessary structures
! 164: * are properly allocated.
! 165: */
! 166: int
! 167: check_pty(int minor)
! 168: {
! 169: struct pt_softc *pti;
! 170:
! 171: rw_enter_write(&pt_softc_lock);
! 172: if (minor >= npty) {
! 173: struct pt_softc **newpt;
! 174: int newnpty;
! 175:
! 176: /* check if the requested pty can be granted */
! 177: if (minor >= maxptys)
! 178: goto limit_reached;
! 179:
! 180: /* grow pty array by powers of two, up to maxptys */
! 181: for (newnpty = npty; newnpty <= minor; newnpty *= 2)
! 182: ;
! 183:
! 184: if (newnpty > maxptys)
! 185: newnpty = maxptys;
! 186: newpt = ptyarralloc(newnpty);
! 187:
! 188: if (maxptys == npty) {
! 189: goto limit_reached;
! 190: }
! 191:
! 192: memcpy(newpt, pt_softc, npty * sizeof(struct pt_softc *));
! 193: free(pt_softc, M_DEVBUF);
! 194: pt_softc = newpt;
! 195: npty = newnpty;
! 196: }
! 197:
! 198: /*
! 199: * If the entry is not yet allocated, allocate one.
! 200: */
! 201: if (!pt_softc[minor]) {
! 202: MALLOC(pti, struct pt_softc *, sizeof(struct pt_softc),
! 203: M_DEVBUF, M_WAITOK);
! 204: bzero(pti, sizeof(struct pt_softc));
! 205: pti->pt_tty = ttymalloc();
! 206: ptydevname(minor, pti);
! 207: pt_softc[minor] = pti;
! 208: }
! 209: rw_exit_write(&pt_softc_lock);
! 210: return (0);
! 211: limit_reached:
! 212: rw_exit_write(&pt_softc_lock);
! 213: tablefull("pty");
! 214: return (ENXIO);
! 215: }
! 216:
! 217: /*
! 218: * Establish n (or default if n is 1) ptys in the system.
! 219: */
! 220: void
! 221: ptyattach(int n)
! 222: {
! 223: /* maybe should allow 0 => none? */
! 224: if (n <= 1)
! 225: n = NPTY_MIN;
! 226: pt_softc = ptyarralloc(n);
! 227: npty = n;
! 228:
! 229: /*
! 230: * If we have pty, we need ptm too.
! 231: */
! 232: ptmattach(1);
! 233: }
! 234:
! 235: /*ARGSUSED*/
! 236: int
! 237: ptsopen(dev_t dev, int flag, int devtype, struct proc *p)
! 238: {
! 239: struct pt_softc *pti;
! 240: struct tty *tp;
! 241: int error;
! 242:
! 243: if ((error = check_pty(minor(dev))))
! 244: return (error);
! 245:
! 246: pti = pt_softc[minor(dev)];
! 247: if (!pti->pt_tty) {
! 248: tp = pti->pt_tty = ttymalloc();
! 249: } else
! 250: tp = pti->pt_tty;
! 251: if ((tp->t_state & TS_ISOPEN) == 0) {
! 252: tp->t_state |= TS_WOPEN;
! 253: ttychars(tp); /* Set up default chars */
! 254: tp->t_iflag = TTYDEF_IFLAG;
! 255: tp->t_oflag = TTYDEF_OFLAG;
! 256: tp->t_lflag = TTYDEF_LFLAG;
! 257: tp->t_cflag = TTYDEF_CFLAG;
! 258: tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED;
! 259: ttsetwater(tp); /* would be done in xxparam() */
! 260: } else if (tp->t_state&TS_XCLUDE && p->p_ucred->cr_uid != 0)
! 261: return (EBUSY);
! 262: if (tp->t_oproc) /* Ctrlr still around. */
! 263: tp->t_state |= TS_CARR_ON;
! 264: while ((tp->t_state & TS_CARR_ON) == 0) {
! 265: tp->t_state |= TS_WOPEN;
! 266: if (flag&FNONBLOCK)
! 267: break;
! 268: error = ttysleep(tp, &tp->t_rawq, TTIPRI | PCATCH,
! 269: ttopen, 0);
! 270: if (error)
! 271: return (error);
! 272: }
! 273: error = (*linesw[tp->t_line].l_open)(dev, tp);
! 274: ptcwakeup(tp, FREAD|FWRITE);
! 275: return (error);
! 276: }
! 277:
! 278: int
! 279: ptsclose(dev_t dev, int flag, int mode, struct proc *p)
! 280: {
! 281: struct pt_softc *pti = pt_softc[minor(dev)];
! 282: struct tty *tp = pti->pt_tty;
! 283: int error;
! 284:
! 285: error = (*linesw[tp->t_line].l_close)(tp, flag);
! 286: error |= ttyclose(tp);
! 287: ptcwakeup(tp, FREAD|FWRITE);
! 288: return (error);
! 289: }
! 290:
! 291: int
! 292: ptsread(dev_t dev, struct uio *uio, int flag)
! 293: {
! 294: struct proc *p = curproc;
! 295: struct pt_softc *pti = pt_softc[minor(dev)];
! 296: struct tty *tp = pti->pt_tty;
! 297: int error = 0;
! 298:
! 299: again:
! 300: if (pti->pt_flags & PF_REMOTE) {
! 301: while (isbackground(p, tp)) {
! 302: if ((p->p_sigignore & sigmask(SIGTTIN)) ||
! 303: (p->p_sigmask & sigmask(SIGTTIN)) ||
! 304: p->p_pgrp->pg_jobc == 0 ||
! 305: p->p_flag & P_PPWAIT)
! 306: return (EIO);
! 307: pgsignal(p->p_pgrp, SIGTTIN, 1);
! 308: error = ttysleep(tp, &lbolt,
! 309: TTIPRI | PCATCH, ttybg, 0);
! 310: if (error)
! 311: return (error);
! 312: }
! 313: if (tp->t_canq.c_cc == 0) {
! 314: if (flag & IO_NDELAY)
! 315: return (EWOULDBLOCK);
! 316: error = ttysleep(tp, &tp->t_canq,
! 317: TTIPRI | PCATCH, ttyin, 0);
! 318: if (error)
! 319: return (error);
! 320: goto again;
! 321: }
! 322: while (tp->t_canq.c_cc > 1 && uio->uio_resid > 0)
! 323: if (ureadc(getc(&tp->t_canq), uio) < 0) {
! 324: error = EFAULT;
! 325: break;
! 326: }
! 327: if (tp->t_canq.c_cc == 1)
! 328: (void) getc(&tp->t_canq);
! 329: if (tp->t_canq.c_cc)
! 330: return (error);
! 331: } else
! 332: if (tp->t_oproc)
! 333: error = (*linesw[tp->t_line].l_read)(tp, uio, flag);
! 334: ptcwakeup(tp, FWRITE);
! 335: return (error);
! 336: }
! 337:
! 338: /*
! 339: * Write to pseudo-tty.
! 340: * Wakeups of controlling tty will happen
! 341: * indirectly, when tty driver calls ptsstart.
! 342: */
! 343: int
! 344: ptswrite(dev_t dev, struct uio *uio, int flag)
! 345: {
! 346: struct pt_softc *pti = pt_softc[minor(dev)];
! 347: struct tty *tp = pti->pt_tty;
! 348:
! 349: if (tp->t_oproc == 0)
! 350: return (EIO);
! 351: return ((*linesw[tp->t_line].l_write)(tp, uio, flag));
! 352: }
! 353:
! 354: /*
! 355: * Start output on pseudo-tty.
! 356: * Wake up process polling or sleeping for input from controlling tty.
! 357: */
! 358: void
! 359: ptsstart(struct tty *tp)
! 360: {
! 361: struct pt_softc *pti = pt_softc[minor(tp->t_dev)];
! 362:
! 363: if (tp->t_state & TS_TTSTOP)
! 364: return;
! 365: if (pti->pt_flags & PF_STOPPED) {
! 366: pti->pt_flags &= ~PF_STOPPED;
! 367: pti->pt_send = TIOCPKT_START;
! 368: }
! 369: ptcwakeup(tp, FREAD);
! 370: }
! 371:
! 372: int
! 373: ptsstop(struct tty *tp, int flush)
! 374: {
! 375: struct pt_softc *pti = pt_softc[minor(tp->t_dev)];
! 376: int flag;
! 377:
! 378: /* note: FLUSHREAD and FLUSHWRITE already ok */
! 379: if (flush == 0) {
! 380: flush = TIOCPKT_STOP;
! 381: pti->pt_flags |= PF_STOPPED;
! 382: } else
! 383: pti->pt_flags &= ~PF_STOPPED;
! 384: pti->pt_send |= flush;
! 385: /* change of perspective */
! 386: flag = 0;
! 387: if (flush & FREAD)
! 388: flag |= FWRITE;
! 389: if (flush & FWRITE)
! 390: flag |= FREAD;
! 391: ptcwakeup(tp, flag);
! 392: return 0;
! 393: }
! 394:
! 395: void
! 396: ptcwakeup(struct tty *tp, int flag)
! 397: {
! 398: struct pt_softc *pti = pt_softc[minor(tp->t_dev)];
! 399:
! 400: if (flag & FREAD) {
! 401: selwakeup(&pti->pt_selr);
! 402: wakeup(&tp->t_outq.c_cf);
! 403: KNOTE(&pti->pt_selr.si_note, 0);
! 404: }
! 405: if (flag & FWRITE) {
! 406: selwakeup(&pti->pt_selw);
! 407: wakeup(&tp->t_rawq.c_cf);
! 408: KNOTE(&pti->pt_selw.si_note, 0);
! 409: }
! 410: }
! 411:
! 412: int ptcopen(dev_t, int, int, struct proc *);
! 413:
! 414: /*ARGSUSED*/
! 415: int
! 416: ptcopen(dev_t dev, int flag, int devtype, struct proc *p)
! 417: {
! 418: struct pt_softc *pti;
! 419: struct tty *tp;
! 420: int error;
! 421:
! 422: if ((error = check_pty(minor(dev))))
! 423: return (error);
! 424:
! 425: pti = pt_softc[minor(dev)];
! 426: if (!pti->pt_tty) {
! 427: tp = pti->pt_tty = ttymalloc();
! 428: } else
! 429: tp = pti->pt_tty;
! 430: if (tp->t_oproc)
! 431: return (EIO);
! 432: tp->t_oproc = ptsstart;
! 433: (void)(*linesw[tp->t_line].l_modem)(tp, 1);
! 434: tp->t_lflag &= ~EXTPROC;
! 435: pti->pt_flags = 0;
! 436: pti->pt_send = 0;
! 437: pti->pt_ucntl = 0;
! 438: return (0);
! 439: }
! 440:
! 441: /*ARGSUSED*/
! 442: int
! 443: ptcclose(dev_t dev, int flag, int devtype, struct proc *p)
! 444: {
! 445: struct pt_softc *pti = pt_softc[minor(dev)];
! 446: struct tty *tp = pti->pt_tty;
! 447:
! 448: (void)(*linesw[tp->t_line].l_modem)(tp, 0);
! 449: tp->t_state &= ~TS_CARR_ON;
! 450: tp->t_oproc = 0; /* mark closed */
! 451: return (0);
! 452: }
! 453:
! 454: int
! 455: ptcread(dev_t dev, struct uio *uio, int flag)
! 456: {
! 457: struct pt_softc *pti = pt_softc[minor(dev)];
! 458: struct tty *tp = pti->pt_tty;
! 459: char buf[BUFSIZ];
! 460: int error = 0, cc;
! 461:
! 462: /*
! 463: * We want to block until the slave
! 464: * is open, and there's something to read;
! 465: * but if we lost the slave or we're NBIO,
! 466: * then return the appropriate error instead.
! 467: */
! 468: for (;;) {
! 469: if (tp->t_state&TS_ISOPEN) {
! 470: if (pti->pt_flags&PF_PKT && pti->pt_send) {
! 471: error = ureadc((int)pti->pt_send, uio);
! 472: if (error)
! 473: return (error);
! 474: if (pti->pt_send & TIOCPKT_IOCTL) {
! 475: cc = min(uio->uio_resid,
! 476: sizeof(tp->t_termios));
! 477: uiomove(&tp->t_termios, cc, uio);
! 478: }
! 479: pti->pt_send = 0;
! 480: return (0);
! 481: }
! 482: if (pti->pt_flags&PF_UCNTL && pti->pt_ucntl) {
! 483: error = ureadc((int)pti->pt_ucntl, uio);
! 484: if (error)
! 485: return (error);
! 486: pti->pt_ucntl = 0;
! 487: return (0);
! 488: }
! 489: if (tp->t_outq.c_cc && (tp->t_state&TS_TTSTOP) == 0)
! 490: break;
! 491: }
! 492: if ((tp->t_state&TS_CARR_ON) == 0)
! 493: return (0); /* EOF */
! 494: if (flag & IO_NDELAY)
! 495: return (EWOULDBLOCK);
! 496: error = tsleep(&tp->t_outq.c_cf, TTIPRI | PCATCH,
! 497: ttyin, 0);
! 498: if (error)
! 499: return (error);
! 500: }
! 501: if (pti->pt_flags & (PF_PKT|PF_UCNTL))
! 502: error = ureadc(0, uio);
! 503: while (uio->uio_resid > 0 && error == 0) {
! 504: cc = q_to_b(&tp->t_outq, buf, min(uio->uio_resid, BUFSIZ));
! 505: if (cc <= 0)
! 506: break;
! 507: error = uiomove(buf, cc, uio);
! 508: }
! 509: if (tp->t_outq.c_cc <= tp->t_lowat) {
! 510: if (tp->t_state&TS_ASLEEP) {
! 511: tp->t_state &= ~TS_ASLEEP;
! 512: wakeup(&tp->t_outq);
! 513: }
! 514: selwakeup(&tp->t_wsel);
! 515: }
! 516: return (error);
! 517: }
! 518:
! 519:
! 520: int
! 521: ptcwrite(dev_t dev, struct uio *uio, int flag)
! 522: {
! 523: struct pt_softc *pti = pt_softc[minor(dev)];
! 524: struct tty *tp = pti->pt_tty;
! 525: u_char *cp = NULL;
! 526: int cc = 0;
! 527: u_char locbuf[BUFSIZ];
! 528: int cnt = 0;
! 529: int error = 0;
! 530:
! 531: again:
! 532: if ((tp->t_state&TS_ISOPEN) == 0)
! 533: goto block;
! 534: if (pti->pt_flags & PF_REMOTE) {
! 535: if (tp->t_canq.c_cc)
! 536: goto block;
! 537: while (uio->uio_resid > 0 && tp->t_canq.c_cc < TTYHOG - 1) {
! 538: if (cc == 0) {
! 539: cc = min(uio->uio_resid, BUFSIZ);
! 540: cc = min(cc, TTYHOG - 1 - tp->t_canq.c_cc);
! 541: cp = locbuf;
! 542: error = uiomove(cp, cc, uio);
! 543: if (error)
! 544: return (error);
! 545: /* check again for safety */
! 546: if ((tp->t_state&TS_ISOPEN) == 0)
! 547: return (EIO);
! 548: }
! 549: if (cc)
! 550: (void) b_to_q((char *)cp, cc, &tp->t_canq);
! 551: cc = 0;
! 552: }
! 553: (void) putc(0, &tp->t_canq);
! 554: ttwakeup(tp);
! 555: wakeup(&tp->t_canq);
! 556: return (0);
! 557: }
! 558: while (uio->uio_resid > 0) {
! 559: if (cc == 0) {
! 560: cc = min(uio->uio_resid, BUFSIZ);
! 561: cp = locbuf;
! 562: error = uiomove(cp, cc, uio);
! 563: if (error)
! 564: return (error);
! 565: /* check again for safety */
! 566: if ((tp->t_state&TS_ISOPEN) == 0)
! 567: return (EIO);
! 568: }
! 569: while (cc > 0) {
! 570: if ((tp->t_rawq.c_cc + tp->t_canq.c_cc) >= TTYHOG - 2 &&
! 571: (tp->t_canq.c_cc > 0 || !ISSET(tp->t_lflag, ICANON))) {
! 572: wakeup(&tp->t_rawq);
! 573: goto block;
! 574: }
! 575: (*linesw[tp->t_line].l_rint)(*cp++, tp);
! 576: cnt++;
! 577: cc--;
! 578: }
! 579: cc = 0;
! 580: }
! 581: return (0);
! 582: block:
! 583: /*
! 584: * Come here to wait for slave to open, for space
! 585: * in outq, or space in rawq.
! 586: */
! 587: if ((tp->t_state&TS_CARR_ON) == 0)
! 588: return (EIO);
! 589: if (flag & IO_NDELAY) {
! 590: /* adjust for data copied in but not written */
! 591: uio->uio_resid += cc;
! 592: if (cnt == 0)
! 593: return (EWOULDBLOCK);
! 594: return (0);
! 595: }
! 596: error = tsleep(&tp->t_rawq.c_cf, TTOPRI | PCATCH,
! 597: ttyout, 0);
! 598: if (error) {
! 599: /* adjust for data copied in but not written */
! 600: uio->uio_resid += cc;
! 601: return (error);
! 602: }
! 603: goto again;
! 604: }
! 605:
! 606: int
! 607: ptcpoll(dev_t dev, int events, struct proc *p)
! 608: {
! 609: struct pt_softc *pti = pt_softc[minor(dev)];
! 610: struct tty *tp = pti->pt_tty;
! 611: int revents = 0, s;
! 612:
! 613: if (!ISSET(tp->t_state, TS_CARR_ON))
! 614: return (POLLHUP);
! 615:
! 616: if (!ISSET(tp->t_state, TS_ISOPEN))
! 617: goto notopen;
! 618:
! 619: if (events & (POLLIN | POLLRDNORM)) {
! 620: /*
! 621: * Need to protect access to t_outq
! 622: */
! 623: s = spltty();
! 624: if ((tp->t_outq.c_cc && !ISSET(tp->t_state, TS_TTSTOP)) ||
! 625: ((pti->pt_flags & PF_PKT) && pti->pt_send) ||
! 626: ((pti->pt_flags & PF_UCNTL) && pti->pt_ucntl))
! 627: revents |= events & (POLLIN | POLLRDNORM);
! 628: splx(s);
! 629: }
! 630: if (events & (POLLOUT | POLLWRNORM)) {
! 631: if ((pti->pt_flags & PF_REMOTE) ?
! 632: (tp->t_canq.c_cc == 0) :
! 633: ((tp->t_rawq.c_cc + tp->t_canq.c_cc < TTYHOG - 2) ||
! 634: (tp->t_canq.c_cc == 0 && ISSET(tp->t_lflag, ICANON))))
! 635: revents |= events & (POLLOUT | POLLWRNORM);
! 636: }
! 637: if (events & (POLLPRI | POLLRDBAND)) {
! 638: /* If in packet or user control mode, check for data. */
! 639: if (((pti->pt_flags & PF_PKT) && pti->pt_send) ||
! 640: ((pti->pt_flags & PF_UCNTL) && pti->pt_ucntl))
! 641: revents |= events & (POLLPRI | POLLRDBAND);
! 642: }
! 643:
! 644: if (revents == 0) {
! 645: notopen:
! 646: if (events & (POLLIN | POLLPRI | POLLRDNORM | POLLRDBAND))
! 647: selrecord(p, &pti->pt_selr);
! 648: if (events & (POLLOUT | POLLWRNORM))
! 649: selrecord(p, &pti->pt_selw);
! 650: }
! 651:
! 652: return (revents);
! 653: }
! 654:
! 655: void
! 656: filt_ptcrdetach(struct knote *kn)
! 657: {
! 658: struct pt_softc *pti = (struct pt_softc *)kn->kn_hook;
! 659: int s;
! 660:
! 661: s = spltty();
! 662: SLIST_REMOVE(&pti->pt_selr.si_note, kn, knote, kn_selnext);
! 663: splx(s);
! 664: }
! 665:
! 666: int
! 667: filt_ptcread(struct knote *kn, long hint)
! 668: {
! 669: struct pt_softc *pti = (struct pt_softc *)kn->kn_hook;
! 670: struct tty *tp;
! 671:
! 672: tp = pti->pt_tty;
! 673: kn->kn_data = 0;
! 674:
! 675: if (ISSET(tp->t_state, TS_ISOPEN)) {
! 676: if (!ISSET(tp->t_state, TS_TTSTOP))
! 677: kn->kn_data = tp->t_outq.c_cc;
! 678: if (((pti->pt_flags & PF_PKT) && pti->pt_send) ||
! 679: ((pti->pt_flags & PF_UCNTL) && pti->pt_ucntl))
! 680: kn->kn_data++;
! 681: }
! 682: return (kn->kn_data > 0);
! 683: }
! 684:
! 685: void
! 686: filt_ptcwdetach(struct knote *kn)
! 687: {
! 688: struct pt_softc *pti = (struct pt_softc *)kn->kn_hook;
! 689: int s;
! 690:
! 691: s = spltty();
! 692: SLIST_REMOVE(&pti->pt_selw.si_note, kn, knote, kn_selnext);
! 693: splx(s);
! 694: }
! 695:
! 696: int
! 697: filt_ptcwrite(struct knote *kn, long hint)
! 698: {
! 699: struct pt_softc *pti = (struct pt_softc *)kn->kn_hook;
! 700: struct tty *tp;
! 701:
! 702: tp = pti->pt_tty;
! 703: kn->kn_data = 0;
! 704:
! 705: if (ISSET(tp->t_state, TS_ISOPEN)) {
! 706: if (ISSET(pti->pt_flags, PF_REMOTE)) {
! 707: if (tp->t_canq.c_cc == 0)
! 708: kn->kn_data = tp->t_canq.c_cn;
! 709: } else if (tp->t_rawq.c_cc + tp->t_canq.c_cc < TTYHOG-2)
! 710: kn->kn_data = tp->t_canq.c_cn -
! 711: (tp->t_rawq.c_cc + tp->t_canq.c_cc);
! 712: }
! 713:
! 714: return (kn->kn_data > 0);
! 715: }
! 716:
! 717: struct filterops ptcread_filtops =
! 718: { 1, NULL, filt_ptcrdetach, filt_ptcread };
! 719: struct filterops ptcwrite_filtops =
! 720: { 1, NULL, filt_ptcwdetach, filt_ptcwrite };
! 721:
! 722: int
! 723: ptckqfilter(dev_t dev, struct knote *kn)
! 724: {
! 725: struct pt_softc *pti = pt_softc[minor(dev)];
! 726: struct klist *klist;
! 727: int s;
! 728:
! 729: switch (kn->kn_filter) {
! 730: case EVFILT_READ:
! 731: klist = &pti->pt_selr.si_note;
! 732: kn->kn_fop = &ptcread_filtops;
! 733: break;
! 734: case EVFILT_WRITE:
! 735: klist = &pti->pt_selw.si_note;
! 736: kn->kn_fop = &ptcwrite_filtops;
! 737: break;
! 738: default:
! 739: return (1);
! 740: }
! 741:
! 742: kn->kn_hook = (caddr_t)pti;
! 743:
! 744: s = spltty();
! 745: SLIST_INSERT_HEAD(klist, kn, kn_selnext);
! 746: splx(s);
! 747:
! 748: return (0);
! 749: }
! 750:
! 751: struct tty *
! 752: ptytty(dev_t dev)
! 753: {
! 754: struct pt_softc *pti = pt_softc[minor(dev)];
! 755: struct tty *tp = pti->pt_tty;
! 756:
! 757: return (tp);
! 758: }
! 759:
! 760: /*ARGSUSED*/
! 761: int
! 762: ptyioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
! 763: {
! 764: struct pt_softc *pti = pt_softc[minor(dev)];
! 765: struct tty *tp = pti->pt_tty;
! 766: u_char *cc = tp->t_cc;
! 767: int stop, error;
! 768:
! 769: /*
! 770: * IF CONTROLLER STTY THEN MUST FLUSH TO PREVENT A HANG.
! 771: * ttywflush(tp) will hang if there are characters in the outq.
! 772: */
! 773: if (cmd == TIOCEXT) {
! 774: /*
! 775: * When the EXTPROC bit is being toggled, we need
! 776: * to send an TIOCPKT_IOCTL if the packet driver
! 777: * is turned on.
! 778: */
! 779: if (*(int *)data) {
! 780: if (pti->pt_flags & PF_PKT) {
! 781: pti->pt_send |= TIOCPKT_IOCTL;
! 782: ptcwakeup(tp, FREAD);
! 783: }
! 784: tp->t_lflag |= EXTPROC;
! 785: } else {
! 786: if ((tp->t_lflag & EXTPROC) &&
! 787: (pti->pt_flags & PF_PKT)) {
! 788: pti->pt_send |= TIOCPKT_IOCTL;
! 789: ptcwakeup(tp, FREAD);
! 790: }
! 791: tp->t_lflag &= ~EXTPROC;
! 792: }
! 793: return(0);
! 794: } else
! 795: if (cdevsw[major(dev)].d_open == ptcopen)
! 796: switch (cmd) {
! 797:
! 798: case TIOCGPGRP:
! 799: #ifdef COMPAT_SUNOS
! 800: {
! 801: /*
! 802: * I'm not sure about SunOS TIOCGPGRP semantics
! 803: * on PTYs, but it's something like this:
! 804: */
! 805: extern struct emul emul_sunos;
! 806: if (p->p_emul == &emul_sunos) {
! 807: if (tp->t_pgrp == 0)
! 808: return (EIO);
! 809: *(int *)data = tp->t_pgrp->pg_id;
! 810: return (0);
! 811: }
! 812: }
! 813: #endif
! 814: /*
! 815: * We avoid calling ttioctl on the controller since,
! 816: * in that case, tp must be the controlling terminal.
! 817: */
! 818: *(int *)data = tp->t_pgrp ? tp->t_pgrp->pg_id : 0;
! 819: return (0);
! 820:
! 821: case TIOCPKT:
! 822: if (*(int *)data) {
! 823: if (pti->pt_flags & PF_UCNTL)
! 824: return (EINVAL);
! 825: pti->pt_flags |= PF_PKT;
! 826: } else
! 827: pti->pt_flags &= ~PF_PKT;
! 828: return (0);
! 829:
! 830: case TIOCUCNTL:
! 831: if (*(int *)data) {
! 832: if (pti->pt_flags & PF_PKT)
! 833: return (EINVAL);
! 834: pti->pt_flags |= PF_UCNTL;
! 835: } else
! 836: pti->pt_flags &= ~PF_UCNTL;
! 837: return (0);
! 838:
! 839: case TIOCREMOTE:
! 840: if (*(int *)data)
! 841: pti->pt_flags |= PF_REMOTE;
! 842: else
! 843: pti->pt_flags &= ~PF_REMOTE;
! 844: ttyflush(tp, FREAD|FWRITE);
! 845: return (0);
! 846:
! 847: #ifdef COMPAT_OLDTTY
! 848: case TIOCSETP:
! 849: case TIOCSETN:
! 850: #endif
! 851: case TIOCSETD:
! 852: case TIOCSETA:
! 853: case TIOCSETAW:
! 854: case TIOCSETAF:
! 855: ndflush(&tp->t_outq, tp->t_outq.c_cc);
! 856: break;
! 857:
! 858: case TIOCSIG:
! 859: if (*(unsigned int *)data >= NSIG)
! 860: return(EINVAL);
! 861: if ((tp->t_lflag&NOFLSH) == 0)
! 862: ttyflush(tp, FREAD|FWRITE);
! 863: pgsignal(tp->t_pgrp, *(unsigned int *)data, 1);
! 864: if ((*(unsigned int *)data == SIGINFO) &&
! 865: ((tp->t_lflag&NOKERNINFO) == 0))
! 866: ttyinfo(tp);
! 867: return(0);
! 868: }
! 869: error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p);
! 870: if (error < 0)
! 871: error = ttioctl(tp, cmd, data, flag, p);
! 872: if (error < 0) {
! 873: if (pti->pt_flags & PF_UCNTL &&
! 874: (cmd & ~0xff) == UIOCCMD(0)) {
! 875: if (cmd & 0xff) {
! 876: pti->pt_ucntl = (u_char)cmd;
! 877: ptcwakeup(tp, FREAD);
! 878: }
! 879: return (0);
! 880: }
! 881: error = ENOTTY;
! 882: }
! 883: /*
! 884: * If external processing and packet mode send ioctl packet.
! 885: */
! 886: if ((tp->t_lflag&EXTPROC) && (pti->pt_flags & PF_PKT)) {
! 887: switch (cmd) {
! 888: case TIOCSETA:
! 889: case TIOCSETAW:
! 890: case TIOCSETAF:
! 891: #ifdef COMPAT_OLDTTY
! 892: case TIOCSETP:
! 893: case TIOCSETN:
! 894: case TIOCSETC:
! 895: case TIOCSLTC:
! 896: case TIOCLBIS:
! 897: case TIOCLBIC:
! 898: case TIOCLSET:
! 899: #endif
! 900: pti->pt_send |= TIOCPKT_IOCTL;
! 901: ptcwakeup(tp, FREAD);
! 902: default:
! 903: break;
! 904: }
! 905: }
! 906: stop = (tp->t_iflag & IXON) && CCEQ(cc[VSTOP], CTRL('s')) &&
! 907: CCEQ(cc[VSTART], CTRL('q'));
! 908: if (pti->pt_flags & PF_NOSTOP) {
! 909: if (stop) {
! 910: pti->pt_send &= ~TIOCPKT_NOSTOP;
! 911: pti->pt_send |= TIOCPKT_DOSTOP;
! 912: pti->pt_flags &= ~PF_NOSTOP;
! 913: ptcwakeup(tp, FREAD);
! 914: }
! 915: } else {
! 916: if (!stop) {
! 917: pti->pt_send &= ~TIOCPKT_DOSTOP;
! 918: pti->pt_send |= TIOCPKT_NOSTOP;
! 919: pti->pt_flags |= PF_NOSTOP;
! 920: ptcwakeup(tp, FREAD);
! 921: }
! 922: }
! 923: return (error);
! 924: }
! 925:
! 926: /*
! 927: * Return pty-related information.
! 928: */
! 929: int
! 930: sysctl_pty(int *name, u_int namelen, void *oldp, size_t *oldlenp, void *newp,
! 931: size_t newlen)
! 932: {
! 933: int error, oldmax;
! 934:
! 935: if (namelen != 1)
! 936: return (ENOTDIR);
! 937:
! 938: switch (name[0]) {
! 939: case KERN_TTY_MAXPTYS:
! 940: if (!newp)
! 941: return (sysctl_rdint(oldp, oldlenp, newp, maxptys));
! 942: rw_enter_write(&pt_softc_lock);
! 943: oldmax = maxptys;
! 944: error = sysctl_int(oldp, oldlenp, newp, newlen, &maxptys);
! 945: /*
! 946: * We can't set the max lower than the current active
! 947: * value or to a value bigger than NPTY_MAX.
! 948: */
! 949: if (error == 0 && (maxptys > NPTY_MAX || maxptys < npty)) {
! 950: maxptys = oldmax;
! 951: error = ERANGE;
! 952: }
! 953: rw_exit_write(&pt_softc_lock);
! 954: return (error);
! 955: case KERN_TTY_NPTYS:
! 956: return (sysctl_rdint(oldp, oldlenp, newp, npty));
! 957: #ifdef notyet
! 958: case KERN_TTY_GID:
! 959: return (sysctl_int(oldp, oldlenp, newp, newlen, &tty_gid));
! 960: #endif
! 961: default:
! 962: return (EOPNOTSUPP);
! 963: }
! 964: /* NOTREACHED */
! 965: }
! 966:
! 967: /*
! 968: * Check if a pty is free to use.
! 969: */
! 970: static __inline int
! 971: pty_isfree_locked(int minor)
! 972: {
! 973: struct pt_softc *pt = pt_softc[minor];
! 974: return (pt == NULL || pt->pt_tty == NULL ||
! 975: pt->pt_tty->t_oproc == NULL);
! 976: }
! 977:
! 978: static int
! 979: pty_isfree(int minor)
! 980: {
! 981: int isfree;
! 982:
! 983: rw_enter_read(&pt_softc_lock);
! 984: isfree = pty_isfree_locked(minor);
! 985: rw_exit_read(&pt_softc_lock);
! 986: return(isfree);
! 987: }
! 988:
! 989: dev_t
! 990: pty_getfree(void)
! 991: {
! 992: int i;
! 993:
! 994: rw_enter_read(&pt_softc_lock);
! 995: for (i = 0; i < npty; i++) {
! 996: if (pty_isfree_locked(i))
! 997: break;
! 998: }
! 999: rw_exit_read(&pt_softc_lock);
! 1000: return (makedev(pts_major, i));
! 1001: }
! 1002:
! 1003: /*
! 1004: * Hacked up version of vn_open. We _only_ handle ptys and only open
! 1005: * them with FREAD|FWRITE and never deal with creat or stuff like that.
! 1006: *
! 1007: * We need it because we have to fake up root credentials to open the pty.
! 1008: */
! 1009: static int
! 1010: ptm_vn_open(struct nameidata *ndp)
! 1011: {
! 1012: struct proc *p = ndp->ni_cnd.cn_proc;
! 1013: struct ucred *cred;
! 1014: struct vattr vattr;
! 1015: struct vnode *vp;
! 1016: int error;
! 1017:
! 1018: if ((error = namei(ndp)) != 0)
! 1019: return (error);
! 1020: vp = ndp->ni_vp;
! 1021: if (vp->v_type != VCHR) {
! 1022: error = EINVAL;
! 1023: goto bad;
! 1024: }
! 1025:
! 1026: /*
! 1027: * Get us a fresh cred with root privileges.
! 1028: */
! 1029: cred = crget();
! 1030: error = VOP_OPEN(vp, FREAD|FWRITE, cred, p);
! 1031: if (!error) {
! 1032: /* update atime/mtime */
! 1033: VATTR_NULL(&vattr);
! 1034: getnanotime(&vattr.va_atime);
! 1035: vattr.va_mtime = vattr.va_atime;
! 1036: vattr.va_vaflags |= VA_UTIMES_NULL;
! 1037: (void)VOP_SETATTR(vp, &vattr, p->p_ucred, p);
! 1038: }
! 1039: crfree(cred);
! 1040:
! 1041: if (error)
! 1042: goto bad;
! 1043:
! 1044: vp->v_writecount++;
! 1045:
! 1046: return (0);
! 1047: bad:
! 1048: vput(vp);
! 1049: return (error);
! 1050: }
! 1051:
! 1052: void
! 1053: ptmattach(int n)
! 1054: {
! 1055: /* find the major and minor of the pty devices */
! 1056: int i;
! 1057:
! 1058: for (i = 0; i < nchrdev; i++)
! 1059: if (cdevsw[i].d_open == ptsopen)
! 1060: break;
! 1061:
! 1062: if (i == nchrdev)
! 1063: panic("ptmattach: Can't find pty slave in cdevsw");
! 1064:
! 1065: pts_major = i;
! 1066: }
! 1067:
! 1068: int
! 1069: ptmopen(dev_t dev, int flag, int mode, struct proc *p)
! 1070: {
! 1071: return(0);
! 1072: }
! 1073:
! 1074:
! 1075: int
! 1076: ptmclose(dev_t dev, int flag, int mode, struct proc *p)
! 1077: {
! 1078: return (0);
! 1079: }
! 1080:
! 1081: int
! 1082: ptmread(dev_t dev, struct uio *uio, int ioflag)
! 1083: {
! 1084: return (EIO);
! 1085: }
! 1086:
! 1087: int
! 1088: ptmwrite(dev_t dev, struct uio *uio, int ioflag)
! 1089: {
! 1090: return (EIO);
! 1091: }
! 1092:
! 1093: int
! 1094: ptmioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
! 1095: {
! 1096: dev_t newdev, error;
! 1097: struct pt_softc * pti;
! 1098: struct nameidata cnd, snd;
! 1099: struct filedesc *fdp = p->p_fd;
! 1100: struct file *cfp = NULL, *sfp = NULL;
! 1101: int cindx, sindx;
! 1102: uid_t uid;
! 1103: gid_t gid;
! 1104: struct vattr vattr;
! 1105: struct ucred *cred;
! 1106: struct ptmget *ptm = (struct ptmget *)data;
! 1107:
! 1108: error = 0;
! 1109: switch (cmd) {
! 1110: case PTMGET:
! 1111: fdplock(fdp);
! 1112: /* Grab two filedescriptors. */
! 1113: if ((error = falloc(p, &cfp, &cindx)) != 0) {
! 1114: fdpunlock(fdp);
! 1115: break;
! 1116: }
! 1117: if ((error = falloc(p, &sfp, &sindx)) != 0) {
! 1118: fdremove(fdp, cindx);
! 1119: closef(cfp, p);
! 1120: fdpunlock(fdp);
! 1121: break;
! 1122: }
! 1123:
! 1124: retry:
! 1125: /* Find and open a free master pty. */
! 1126: newdev = pty_getfree();
! 1127: if ((error = check_pty(minor(newdev))))
! 1128: goto bad;
! 1129: pti = pt_softc[minor(newdev)];
! 1130: NDINIT(&cnd, LOOKUP, NOFOLLOW|LOCKLEAF, UIO_SYSSPACE,
! 1131: pti->pty_pn, p);
! 1132: if ((error = ptm_vn_open(&cnd)) != 0) {
! 1133: /*
! 1134: * Check if the master open failed because we lost
! 1135: * the race to grab it.
! 1136: */
! 1137: if (error == EIO && !pty_isfree(minor(newdev)))
! 1138: goto retry;
! 1139: goto bad;
! 1140: }
! 1141: cfp->f_flag = FREAD|FWRITE;
! 1142: cfp->f_type = DTYPE_VNODE;
! 1143: cfp->f_ops = &vnops;
! 1144: cfp->f_data = (caddr_t) cnd.ni_vp;
! 1145: VOP_UNLOCK(cnd.ni_vp, 0, p);
! 1146:
! 1147: /*
! 1148: * Open the slave.
! 1149: * namei -> setattr -> unlock -> revoke -> vrele ->
! 1150: * namei -> open -> unlock
! 1151: * Three stage rocket:
! 1152: * 1. Change the owner and permissions on the slave.
! 1153: * 2. Revoke all the users of the slave.
! 1154: * 3. open the slave.
! 1155: */
! 1156: NDINIT(&snd, LOOKUP, NOFOLLOW|LOCKLEAF, UIO_SYSSPACE,
! 1157: pti->pty_sn, p);
! 1158: if ((error = namei(&snd)) != 0)
! 1159: goto bad;
! 1160: if ((snd.ni_vp->v_mount->mnt_flag & MNT_RDONLY) == 0) {
! 1161: gid = tty_gid;
! 1162: /* get real uid */
! 1163: uid = p->p_cred->p_ruid;
! 1164:
! 1165: VATTR_NULL(&vattr);
! 1166: vattr.va_uid = uid;
! 1167: vattr.va_gid = gid;
! 1168: vattr.va_mode = (S_IRUSR|S_IWUSR|S_IWGRP) & ALLPERMS;
! 1169: /* Get a fake cred to pretend we're root. */
! 1170: cred = crget();
! 1171: error = VOP_SETATTR(snd.ni_vp, &vattr, cred, p);
! 1172: crfree(cred);
! 1173: if (error) {
! 1174: vput(snd.ni_vp);
! 1175: goto bad;
! 1176: }
! 1177: }
! 1178: VOP_UNLOCK(snd.ni_vp, 0, p);
! 1179: if (snd.ni_vp->v_usecount > 1 ||
! 1180: (snd.ni_vp->v_flag & (VALIASED)))
! 1181: VOP_REVOKE(snd.ni_vp, REVOKEALL);
! 1182:
! 1183: /*
! 1184: * The vnode is useless after the revoke, we need to
! 1185: * namei again.
! 1186: */
! 1187: vrele(snd.ni_vp);
! 1188:
! 1189: NDINIT(&snd, LOOKUP, NOFOLLOW|LOCKLEAF, UIO_SYSSPACE,
! 1190: pti->pty_sn, p);
! 1191: /* now open it */
! 1192: if ((error = ptm_vn_open(&snd)) != 0)
! 1193: goto bad;
! 1194: sfp->f_flag = FREAD|FWRITE;
! 1195: sfp->f_type = DTYPE_VNODE;
! 1196: sfp->f_ops = &vnops;
! 1197: sfp->f_data = (caddr_t) snd.ni_vp;
! 1198: VOP_UNLOCK(snd.ni_vp, 0, p);
! 1199:
! 1200: /* now, put the indexen and names into struct ptmget */
! 1201: ptm->cfd = cindx;
! 1202: ptm->sfd = sindx;
! 1203: memcpy(ptm->cn, pti->pty_pn, sizeof(pti->pty_pn));
! 1204: memcpy(ptm->sn, pti->pty_sn, sizeof(pti->pty_sn));
! 1205:
! 1206: /* mark the files mature now that we've passed all errors */
! 1207: FILE_SET_MATURE(cfp);
! 1208: FILE_SET_MATURE(sfp);
! 1209:
! 1210: fdpunlock(fdp);
! 1211: break;
! 1212: default:
! 1213: error = EINVAL;
! 1214: break;
! 1215: }
! 1216: return (error);
! 1217: bad:
! 1218: fdremove(fdp, cindx);
! 1219: closef(cfp, p);
! 1220: fdremove(fdp, sindx);
! 1221: closef(sfp, p);
! 1222: fdpunlock(fdp);
! 1223: return (error);
! 1224: }
! 1225:
! 1226: int
! 1227: ptmpoll(dev_t dev, int events, struct proc *p)
! 1228: {
! 1229: return (seltrue(dev, events, p));
! 1230: }
CVSweb