int do_pty(void) { struct kevent ev[4]; struct termios tt; int kq, massa, slave; char buf[1024]; tcgetattr(STDIN_FILENO, &tt); cfmakeraw(&tt); tt.c_lflag &= ~ECHO; if (openpty(&massa, &slave, NULL, &tt, NULL) < 0) err(1, "openpty"); if (fcntl(massa, F_SETFL, O_NONBLOCK) < 0) err(1, "massa: fcntl"); if (fcntl(slave, F_SETFL, O_NONBLOCK) < 0) err(1, "massa: fcntl"); if ((kq = kqueue()) == -1) err(1, "kqueue"); /* test the read from the slave works */ EV_SET(&ev[0], massa, EVFILT_READ, EV_ADD|EV_ENABLE, 0, 0, NULL); EV_SET(&ev[1], massa, EVFILT_WRITE, EV_ADD|EV_ENABLE, 0, 0, NULL); EV_SET(&ev[2], slave, EVFILT_READ, EV_ADD|EV_ENABLE, 0, 0, NULL); EV_SET(&ev[3], slave, EVFILT_WRITE, EV_ADD|EV_ENABLE, 0, 0, NULL); if (kevent(kq, ev, 4, NULL, 0, NULL) < 0) err(1, "slave: kevent add"); memset(buf, 0, sizeof buf); if (write(massa, " ", 1) != 1) err(1, "massa: write"); if (pty_check(kq, ev, 4, -massa, slave, massa, slave)) return (1); read(slave, buf, sizeof(buf)); if (pty_check(kq, ev, 4, -massa, -slave, massa, slave)) return (1); while (write(massa, buf, sizeof(buf)) > 0) continue; if (pty_check(kq, ev, 4, -massa, slave, -massa, slave)) return (1); read(slave, buf, 1); if (pty_check(kq, ev, 4, -massa, slave, massa, slave)) return (1); while (read(slave, buf, sizeof(buf)) > 0) continue; if (pty_check(kq, ev, 4, -massa, -slave, massa, slave)) return (1); return (0); }
static int pty_alloc_master(struct lwp *l, int *fd, dev_t *dev, struct mount *mp) { int error; struct file *fp; struct vnode *vp; int md; if ((error = fd_allocfile(&fp, fd)) != 0) { DPRINTF(("fd_allocfile %d\n", error)); return error; } retry: /* Find and open a free master pty. */ *dev = pty_getfree(); md = minor(*dev); if ((error = pty_check(md)) != 0) { DPRINTF(("pty_check %d\n", error)); goto bad; } if (ptm == NULL) { DPRINTF(("no ptm\n")); error = EOPNOTSUPP; goto bad; } /* * XXX Since PTYFS has now multiple instance support, if we mounted * more than one PTYFS we must check here the ptyfs_used_tbl, to find * out if the ptyfsnode is under the appropriate mount and skip the * node if not, because the pty could has been released, but * ptyfs_reclaim didn't get a chance to release the corresponding * node other mount point yet. * * It's important to have only one mount point's ptyfsnode for each * appropriate device in ptyfs_used_tbl, else we will have a security * problem, because every entry will have access to this device. * * Also we will not have not efficient vnode and memory usage. * You can test this by changing a_recycle from true to false * in ptyfs_inactive. */ if ((error = (*ptm->allocvp)(mp, l, &vp, *dev, 'p')) != 0) { DPRINTF(("pty_allocvp %d\n", error)); goto bad; } if ((error = pty_vn_open(vp, l)) != 0) { DPRINTF(("pty_vn_open %d\n", error)); /* * Check if the master open failed because we lost * the race to grab it. */ if (error != EIO) goto bad; error = !pty_isfree(md, 1); DPRINTF(("pty_isfree %d\n", error)); if (error) goto retry; else goto bad; } fp->f_flag = FREAD|FWRITE; fp->f_type = DTYPE_VNODE; fp->f_ops = &vnops; fp->f_data = vp; VOP_UNLOCK(vp); fd_affix(curproc, fp, *fd); return 0; bad: fd_abort(curproc, fp, *fd); return error; }