diff --git a/distrib/sets/lists/minix/mi b/distrib/sets/lists/minix/mi index 99e5f7c30..ba41a70f7 100644 --- a/distrib/sets/lists/minix/mi +++ b/distrib/sets/lists/minix/mi @@ -73,6 +73,7 @@ ./boot/minix/.temp/mod11_init minix-sys ./boot/minix_default minix-sys ./dev minix-sys +./dev/pts minix-sys ./etc minix-sys ./etc/atf minix-sys atf ./etc/boot.cfg.default minix-sys @@ -201,6 +202,7 @@ ./service/pm minix-sys ./service/procfs minix-sys ./service/pty minix-sys +./service/ptyfs minix-sys ./service/readclock.drv minix-sys ./service/rs minix-sys ./service/sched minix-sys diff --git a/docs/UPDATING b/docs/UPDATING index c3f237f70..c9368766d 100644 --- a/docs/UPDATING +++ b/docs/UPDATING @@ -1,3 +1,9 @@ +20150623: + In order to use the new Unix98 PTYs, and to ensure that the test set + continues to pass, please add the following line to your /etc/fstab: + + "none /dev/pts ptyfs rw,rslabel=ptyfs 0 0" + 20140801: As the ABI went under heavy changes it is not possible to do a source upgrade (make build) between: diff --git a/etc/mtree/NetBSD.dist.base b/etc/mtree/NetBSD.dist.base index a7274e74e..3e2e59511 100644 --- a/etc/mtree/NetBSD.dist.base +++ b/etc/mtree/NetBSD.dist.base @@ -17,6 +17,7 @@ ./boot/minix ./boot/minix_default ./dev +./dev/pts ./etc ./etc/X11 ./etc/X11/fs diff --git a/etc/system.conf b/etc/system.conf index ae4730b22..d1a026cfb 100644 --- a/etc/system.conf +++ b/etc/system.conf @@ -739,6 +739,13 @@ service pty ; }; +service ptyfs +{ + ipc + SYSTEM pm vfs rs pty ds vm + ; +}; + service edfictl { ipc ALL; diff --git a/etc/usr/rc b/etc/usr/rc index 16d8b1a8e..9a758fcb0 100644 --- a/etc/usr/rc +++ b/etc/usr/rc @@ -196,7 +196,8 @@ start|autoboot) up inet -script /etc/rs.inet -dev /dev/ip fi - up pty -dev /dev/ptyp0 + # pty needs to know the "tty" group ID + up pty -dev /dev/ptmx -args "gid=`stat -f '%g' /dev/ptmx`" up uds -dev /dev/uds diff --git a/lib/libc/stdlib/Makefile.inc b/lib/libc/stdlib/Makefile.inc index a015dbf10..ddc6cfc67 100644 --- a/lib/libc/stdlib/Makefile.inc +++ b/lib/libc/stdlib/Makefile.inc @@ -4,14 +4,13 @@ # stdlib sources .PATH: ${ARCHDIR}/stdlib ${.CURDIR}/stdlib -#LSC: MINIX: pty.c not compiled SRCS+= _env.c _rand48.c \ a64l.c abort.c atexit.c atof.c atoi.c atol.c atoll.c \ bsearch.c drand48.c exit.c \ getenv.c getopt.c getopt_long.c getsubopt.c \ hcreate.c heapsort.c imaxdiv.c insque.c jrand48.c l64a.c lldiv.c \ lcong48.c lrand48.c lsearch.c merge.c mi_vector_hash.c mrand48.c \ - nrand48.c putenv.c qabs.c qdiv.c qsort.c posix_openpt.c \ + nrand48.c putenv.c qabs.c qdiv.c qsort.c posix_openpt.c pty.c \ quick_exit.c radixsort.c rand.c rand_r.c random.c remque.c \ seed48.c setenv.c srand48.c strsuftoll.c \ strtoimax.c strtol.c strtoll.c strtoq.c strtoul.c strtoull.c \ diff --git a/lib/libutil/pty.c b/lib/libutil/pty.c index 7ad1fb41d..06cd29089 100644 --- a/lib/libutil/pty.c +++ b/lib/libutil/pty.c @@ -42,6 +42,9 @@ __RCSID("$NetBSD: pty.c,v 1.31 2009/02/20 16:44:06 christos Exp $"); #include #include +#ifdef __minix +#include +#endif /* __minix */ #include #include #include @@ -89,7 +92,19 @@ openpty(int *amaster, int *aslave, char *name, struct termios *term, } (void)close(master); } -#endif /* !defined(__minix) */ +#else /* defined(__minix) */ + /* + * On MINIX3, we implement non-root openpty(3) using Unix98 PTYs. + * If this fails, the fallback code below works for root only. + */ + if ((master = posix_openpt(O_RDWR | O_NOCTTY)) != -1) { + if (grantpt(master) != -1 && unlockpt(master) != -1 && + (linep = ptsname(master)) != NULL && + (slave = open(linep, O_RDWR | O_NOCTTY)) != -1) + goto gotit; + (void)close(master); + } +#endif /* defined(__minix) */ (void)getgrnam_r("tty", &grs, grbuf, sizeof(grbuf), &grp); if (grp != NULL) { @@ -129,9 +144,7 @@ openpty(int *amaster, int *aslave, char *name, struct termios *term, #else (slave = open(line, O_RDWR, 0)) != -1) { #endif -#if !defined(__minix) gotit: -#endif /* !defined(__minix) */ *amaster = master; *aslave = slave; if (name) diff --git a/minix/commands/DESCRIBE/DESCRIBE.sh b/minix/commands/DESCRIBE/DESCRIBE.sh index 7f44975fe..e826052ab 100644 --- a/minix/commands/DESCRIBE/DESCRIBE.sh +++ b/minix/commands/DESCRIBE/DESCRIBE.sh @@ -159,6 +159,9 @@ do fi esac ;; + 9,0) + des="unix98 pseudoterminal master" dev=ptmx + ;; 9,12[89]|9,1[3-8]?|9,19[01]) p=`expr \\( $minor - 128 \\) / 16 | tr '0123' 'pqrs'` n=`expr $minor % 16` diff --git a/minix/commands/MAKEDEV/MAKEDEV.sh b/minix/commands/MAKEDEV/MAKEDEV.sh index bbd4c90b3..81330b6fa 100755 --- a/minix/commands/MAKEDEV/MAKEDEV.sh +++ b/minix/commands/MAKEDEV/MAKEDEV.sh @@ -28,10 +28,6 @@ RAMDISK_DEVICES=" fd0 fd1 fd0p0 fd1p0 pci ttyc1 ttyc2 ttyc3 tty00 tty01 tty02 tty03 - ttyp0 ttyp1 ttyp2 ttyp3 ttyp4 ttyp5 ttyp6 ttyp7 ttyp8 ttyp9 - ttypa ttypb ttypc ttypd ttype ttypf - ttyq0 ttyq1 ttyq2 ttyq3 ttyq4 ttyq5 ttyq6 ttyq7 ttyq8 ttyq9 - ttyqa ttyqb ttyqc ttyqd ttyqe ttyqf " #eth => ip tcp udp @@ -46,9 +42,13 @@ STD_DEVICES=" eepromb3s54 eepromb3s55 eepromb3s56 eepromb3s57 eth fb0 fbd filter hello i2c-1 i2c-2 i2c-3 - klog random + klog ptmx random sht21b1s40 sht21b2s40 sht21b3s40 tsl2550b1s39 tsl2550b2s39 tsl2550b3s39 + ttyp0 ttyp1 ttyp2 ttyp3 ttyp4 ttyp5 ttyp6 ttyp7 ttyp8 ttyp9 + ttypa ttypb ttypc ttypd ttype ttypf + ttyq0 ttyq1 ttyq2 ttyq3 ttyq4 ttyq5 ttyq6 ttyq7 ttyq8 ttyq9 + ttyqa ttyqb ttyqc ttyqd ttyqe ttyqf uds vnd0 vnd0p0 vnd0p0s0 vnd1 vnd1p0 vnd1p0s0 vnd2 vnd3 vnd4 vnd5 vnd6 vnd7 @@ -132,6 +132,7 @@ Where key is one of the following: eth ip tcp udp # One of these makes some TCP/IP devices audio mixer # Make audio devices klog # Make /dev/klog + ptmx # Make /dev/ptmx random # Make /dev/random, /dev/urandom uds # Make /dev/uds filter # Make /dev/filter @@ -369,6 +370,10 @@ do # PCI server, manages PCI buses makedev pci c 134 0 ${uname} ${gname} ${permissions} ;; + ptmx) + # Unix98 pseudoterminal master + makedev ptmx c 9 0 ${uname} tty 666 + ;; ram|mem|kmem|null|boot|zero|imgrd) # Memory devices. makedev ram b 1 0 ${uname} kmem ${permissions} diff --git a/minix/commands/setup/setup.sh b/minix/commands/setup/setup.sh index 0fa2501f1..c3c4bab40 100644 --- a/minix/commands/setup/setup.sh +++ b/minix/commands/setup/setup.sh @@ -713,10 +713,11 @@ ln -s /usr/log /mnt/var/log # CD remnants that aren't for the installed system rm /mnt/etc/issue /mnt/CD /mnt/.* 2>/dev/null -echo >/mnt/etc/fstab "/dev/$root / mfs rw 0 1 -/dev/$usr /usr $FSTYPE rw 0 2 +echo >/mnt/etc/fstab "/dev/$root / mfs rw 0 1 +/dev/$usr /usr $FSTYPE rw 0 2 $fshome -none /sys devman rw,rslabel=devman 0 0" +none /sys devman rw,rslabel=devman 0 0 +none /dev/pts ptyfs rw,rslabel=ptyfs 0 0" # National keyboard map. test -n "$keymap" && cp -p "/usr/lib/keymaps/$keymap.map" /mnt/etc/keymap diff --git a/minix/drivers/tty/pty/Makefile b/minix/drivers/tty/pty/Makefile index c2d1d0d29..6ab626d71 100644 --- a/minix/drivers/tty/pty/Makefile +++ b/minix/drivers/tty/pty/Makefile @@ -1,6 +1,6 @@ # Makefile for pseudo terminal driver (PTY) PROG= pty -SRCS= tty.c pty.c +SRCS= tty.c pty.c ptyfs.c DPADD+= ${LIBCHARDRIVER} ${LIBSYS} ${LIBTIMERS} LDADD+= -lchardriver -lsys -ltimers diff --git a/minix/drivers/tty/pty/pty.c b/minix/drivers/tty/pty/pty.c index 031d6082d..1a6b55696 100644 --- a/minix/drivers/tty/pty/pty.c +++ b/minix/drivers/tty/pty/pty.c @@ -14,14 +14,37 @@ * Be careful when reading this code, the terms "reading" and "writing" are * used both for the tty (slave) and the pty (master) end of the pseudo tty. * Writes to one end are to be read at the other end and vice-versa. + * + * In addition to the above, PTY service now also supports Unix98 pseudo- + * terminal pairs, thereby allowing non-root users to allocate pseudoterminals. + * It requires the presence for PTYFS for this, and supports only old-style + * ptys when PTYFS is not running. For Unix98 ptys, the general idea is that a + * userland program opens a pty master by opening /dev/ptmx through the use of + * posxix_openpt(3). A slave node is allocated on PTYFS when the program calls + * grantpt(3) on the master. The program can then obtain the path name for the + * slave end through ptsname(3), and open the slave end using this path. + * + * Implementation-wise, the Unix98 and non-Unix98 pseudoterminals share the + * same pool of data structures, but use different ranges of minor numbers. + * Access to the two types may not be mixed, and thus, some parts of the code + * have checks to make sure a traditional slave is not opened for a master + * allocated through /dev/ptmx, etcetera. */ #include +#include #include #include #include #include #include "tty.h" +#include "ptyfs.h" + +/* Device node attributes used for Unix98 slave nodes. */ +#define UNIX98_MODE (S_IFCHR | 0620) /* crw--w---- */ + +#define UNIX98_MASTER(index) (UNIX98_MINOR + (index) * 2) +#define UNIX98_SLAVE(index) (UNIX98_MINOR + (index) * 2 + 1) /* PTY bookkeeping structure, one per pty/tty pair. */ typedef struct pty { @@ -51,12 +74,14 @@ typedef struct pty { /* select() data. */ unsigned int select_ops; /* Which operations do we want to know about? */ endpoint_t select_proc; /* Who wants to know about it? */ + devminor_t select_minor; /* Which minor was being selected on? */ } pty_t; #define TTY_ACTIVE 0x01 /* tty is open/active */ #define PTY_ACTIVE 0x02 /* pty is open/active */ #define TTY_CLOSED 0x04 /* tty side has closed down */ #define PTY_CLOSED 0x08 /* pty side has closed down */ +#define PTY_UNIX98 0x10 /* pty pair is Unix98 */ static pty_t pty_table[NR_PTYS]; /* PTY bookkeeping */ @@ -72,6 +97,9 @@ static ssize_t pty_master_read(devminor_t minor, u64_t position, static ssize_t pty_master_write(devminor_t minor, u64_t position, endpoint_t endpt, cp_grant_id_t grant, size_t size, int flags, cdev_id_t id); +static int pty_master_ioctl(devminor_t minor, unsigned long request, + endpoint_t endpt, cp_grant_id_t grant, int flags, + endpoint_t user_endpt, cdev_id_t id); static int pty_master_cancel(devminor_t minor, endpoint_t endpt, cdev_id_t id); static int pty_master_select(devminor_t minor, unsigned int ops, endpoint_t endpt); @@ -81,10 +109,30 @@ static struct chardriver pty_master_tab = { .cdr_close = pty_master_close, .cdr_read = pty_master_read, .cdr_write = pty_master_write, + .cdr_ioctl = pty_master_ioctl, .cdr_cancel = pty_master_cancel, .cdr_select = pty_master_select }; +/*===========================================================================* + * get_free_pty * + *===========================================================================*/ +static tty_t *get_free_pty(void) +{ +/* Return a pointer to a free tty structure, or NULL if no tty is free. */ + tty_t *tp; + pty_t *pp; + + for (tp = &tty_table[0]; tp < &tty_table[NR_PTYS]; tp++) { + pp = tp->tty_priv; + + if (!(pp->state & (PTY_ACTIVE | TTY_ACTIVE))) + return tp; + } + + return NULL; +} + /*===========================================================================* * pty_master_open * *===========================================================================*/ @@ -93,21 +141,80 @@ static int pty_master_open(devminor_t minor, int UNUSED(access), { tty_t *tp; pty_t *pp; + int r; - assert(minor >= PTYPX_MINOR && minor < PTYPX_MINOR + NR_PTYS); + if (minor == PTMX_MINOR) { + /* /dev/ptmx acts as a cloning device. We return a free PTY master and + * mark it as a UNIX98 type. + */ + if ((tp = get_free_pty()) == NULL) + return EAGAIN; /* POSIX says this is the right error code */ - if ((tp = line2tty(minor)) == NULL) - return ENXIO; - pp = tp->tty_priv; + /* The following call has two purposes. First, we check right here + * whether PTYFS is running at all; if not, the PTMX device cannot be + * opened at all and userland can fall back to other allocation + * methods right away. Second, in the exceptional case that the PTY + * service is restarted while PTYFS keeps running, PTYFS may expose + * stale slave nodes, which are a security hole if not removed as soon + * as a new PTY pair is allocated. + */ + if (ptyfs_clear(tp->tty_index) != OK) + return EAGAIN; - if (pp->state & PTY_ACTIVE) - return EIO; + pp = tp->tty_priv; + pp->state |= PTY_UNIX98; + + minor = UNIX98_MASTER(tp->tty_index); + + r = CDEV_CLONED | minor; + } else { + /* There is no way to open Unix98 masters directly, except by messing + * with mknod. We disallow such tricks altogether, and thus, the rest + * of the code deals with opening a non-Unix98 master only. + */ + if (minor < PTYPX_MINOR || minor >= PTYPX_MINOR + NR_PTYS) + return EIO; + + if ((tp = line2tty(minor)) == NULL) + return ENXIO; + pp = tp->tty_priv; + + /* For non-Unix98 PTYs, we allow the slave to be opened before the + * master, but the master may be opened only once. This is how userland + * is able to find a free non-Unix98 PTY pair. + */ + if (pp->state & PTY_ACTIVE) + return EIO; + assert(!(pp->state & PTY_UNIX98)); + + r = OK; + } pp->state |= PTY_ACTIVE; + pp->rdcum = 0; pp->wrcum = 0; - return OK; + return r; +} + +/*===========================================================================* + * pty_reset * + *===========================================================================*/ +static void pty_reset(tty_t *tp) +{ +/* Both sides of a PTY pair have been closed. Clean up its state. */ + pty_t *pp; + + pp = tp->tty_priv; + + /* For Unix98 pairs, clean up the Unix98 slave node. It may never have been + * allocated, but we don't care. Ignore failures altogether. + */ + if (pp->state & PTY_UNIX98) + (void)ptyfs_clear(tp->tty_index); + + pp->state = 0; } /*===========================================================================* @@ -123,7 +230,7 @@ static int pty_master_close(devminor_t minor) pp = tp->tty_priv; if ((pp->state & (TTY_ACTIVE | TTY_CLOSED)) != TTY_ACTIVE) { - pp->state = 0; + pty_reset(tp); } else { pp->state |= PTY_CLOSED; tp->tty_termios.c_ospeed = B0; /* cause EOF on slave side */ @@ -228,6 +335,66 @@ static ssize_t pty_master_write(devminor_t minor, u64_t UNUSED(position), return EDONTREPLY; /* do suspend */ } +/*===========================================================================* + * pty_master_ioctl * + *===========================================================================*/ +static int pty_master_ioctl(devminor_t minor, unsigned long request, + endpoint_t endpt, cp_grant_id_t grant, int flags, + endpoint_t user_endpt, cdev_id_t id) +{ + tty_t *tp; + pty_t *pp; + uid_t uid; + struct ptmget pm; + size_t len; + + if ((tp = line2tty(minor)) == NULL) + return ENXIO; + pp = tp->tty_priv; + + /* Some IOCTLs are for the master side only. */ + switch (request) { + case TIOCGRANTPT: /* grantpt(3) */ + if (!(pp->state & PTY_UNIX98)) + break; + + if ((int)(uid = getnuid(user_endpt)) == -1) + return EACCES; + if (tty_gid == -1) { + printf("PTY: no tty group ID given at startup\n"); + return EACCES; + } + + /* Create or update the slave node. */ + if (ptyfs_set(tp->tty_index, UNIX98_MODE, uid, tty_gid, + makedev(PTY_MAJOR, UNIX98_SLAVE(tp->tty_index))) != OK) + return EACCES; + + return OK; + + case TIOCPTSNAME: /* ptsname(3) */ + if (!(pp->state & PTY_UNIX98)) + break; + + /* Since pm.sn is 16 bytes, we can have up to a million slaves. */ + memset(&pm, 0, sizeof(pm)); + + strlcpy(pm.sn, _PATH_DEV_PTS, sizeof(pm.sn)); + len = strlen(pm.sn); + + if (ptyfs_name(tp->tty_index, &pm.sn[len], sizeof(pm.sn) - len) != OK) + return EINVAL; + + return sys_safecopyto(endpt, grant, 0, (vir_bytes)&pm, sizeof(pm)); + } + + /* TODO: historically, all IOCTLs on the master are processed as if issued on + * the slave end. Make sure that this can not cause problems, in particular + * with blocking IOCTLs. + */ + return tty_ioctl(minor, request, endpt, grant, flags, user_endpt, id); +} + /*===========================================================================* * pty_master_cancel * *===========================================================================*/ @@ -292,13 +459,11 @@ static int select_try_pty(tty_t *tp, int ops) void select_retry_pty(tty_t *tp) { pty_t *pp = tp->tty_priv; - devminor_t minor; int r; /* See if the pty side of a pty is ready to return a select. */ if (pp->select_ops && (r = select_try_pty(tp, pp->select_ops))) { - minor = PTYPX_MINOR + (int) (pp - pty_table); - chardriver_reply_select(pp->select_proc, minor, r); + chardriver_reply_select(pp->select_proc, pp->select_minor, r); pp->select_ops &= ~r; } } @@ -326,6 +491,7 @@ static int pty_master_select(devminor_t minor, unsigned int ops, if (ops && watch) { pp->select_ops |= ops; pp->select_proc = endpt; + pp->select_minor = minor; } return ready_ops; @@ -534,6 +700,27 @@ static int pty_slave_read(tty_t *tp, int try) return 0; } +/*===========================================================================* + * pty_slave_mayopen * + *===========================================================================*/ +static int pty_slave_mayopen(tty_t *tp, devminor_t line) +{ +/* Check if the user is not mixing Unix98 and non-Unix98 terminal ends. */ + pty_t *pp; + int unix98_line, unix98_pty; + + pp = tp->tty_priv; + + /* A non-Unix98 slave may be opened even if the corresponding master is not + * opened yet, but PTY_UNIX98 is always clear for free ptys. A Unix98 slave + * may not be opened before its master, but this should not occur anyway. + */ + unix98_line = (line >= UNIX98_MINOR && line < UNIX98_MINOR + NR_PTYS * 2); + unix98_pty = !!(pp->state & PTY_UNIX98); + + return (unix98_line == unix98_pty); +} + /*===========================================================================* * pty_slave_open * *===========================================================================*/ @@ -542,8 +729,6 @@ static int pty_slave_open(tty_t *tp, int UNUSED(try)) /* The tty side has been opened. */ pty_t *pp = tp->tty_priv; - assert(tp->tty_minor >= TTYPX_MINOR && tp->tty_minor < TTYPX_MINOR + NR_PTYS); - /* TTY_ACTIVE may already be set, which would indicate that the slave is * reopened after being fully closed while the master is still open. In that * case TTY_CLOSED will also be set, so clear that one. @@ -576,7 +761,7 @@ static int pty_slave_close(tty_t *tp, int UNUSED(try)) pp->wrcaller = NONE; } - if (pp->state & PTY_CLOSED) pp->state = 0; + if (pp->state & PTY_CLOSED) pty_reset(tp); else pp->state |= TTY_CLOSED; return 0; @@ -638,6 +823,7 @@ void pty_init(tty_t *tp) tp->tty_echo = pty_slave_echo; tp->tty_icancel = pty_slave_icancel; tp->tty_ocancel = pty_slave_ocancel; + tp->tty_mayopen = pty_slave_mayopen; tp->tty_open = pty_slave_open; tp->tty_close = pty_slave_close; tp->tty_select_ops = 0; diff --git a/minix/drivers/tty/pty/ptyfs.c b/minix/drivers/tty/pty/ptyfs.c new file mode 100644 index 000000000..03a6becea --- /dev/null +++ b/minix/drivers/tty/pty/ptyfs.c @@ -0,0 +1,112 @@ +/* ptyfs.c - communication to PTYFS */ + +#include +#include + +#include "ptyfs.h" + +/* + * Perform synchronous communication with PTYFS, if PTYFS is actually running. + * This function is expected to return only once PTYFS has acknowledged + * processing the request, in order to avoid race conditions between PTYFS and + * userland. The function must always fail when PTYFS is not available for any + * reason. Return OK on success, or an IPC-level error on failure. + */ +static int +ptyfs_sendrec(message * m_ptr) +{ + endpoint_t endpt; + + /* + * New pseudoterminals are created sufficiently rarely that we need not + * optimize this by for example caching the PTYFS endpoint, especially + * since caching brings along new issues, such as having to reissue the + * request if the cached endpoint turns out to be outdated (e.g., when + * ptyfs is unmounted and remounted for whatever reason). + */ + if (ds_retrieve_label_endpt("ptyfs", &endpt) != OK) + return EDEADSRCDST; /* ptyfs is not available */ + + return ipc_sendrec(endpt, m_ptr); +} + +/* + * Add or update a node on PTYFS, with the given node index and attributes. + * Return OK on success, or an error code on failure. Errors may include + * communication failures and out-of-memory conditions. + */ +int +ptyfs_set(unsigned int index, mode_t mode, uid_t uid, gid_t gid, dev_t dev) +{ + message m; + int r; + + memset(&m, 0, sizeof(m)); + + m.m_type = PTYFS_SET; + m.m_pty_ptyfs_req.index = index; + m.m_pty_ptyfs_req.mode = mode; + m.m_pty_ptyfs_req.uid = uid; + m.m_pty_ptyfs_req.gid = gid; + m.m_pty_ptyfs_req.dev = dev; + + if ((r = ptyfs_sendrec(&m)) != OK) + return r; + + return m.m_type; +} + +/* + * Remove a node from PTYFS. Return OK on success, or an error code on + * failure. The function succeeds even if no node existed for the given index. + */ +int +ptyfs_clear(unsigned int index) +{ + message m; + int r; + + memset(&m, 0, sizeof(m)); + + m.m_type = PTYFS_CLEAR; + m.m_pty_ptyfs_req.index = index; + + if ((r = ptyfs_sendrec(&m)) != OK) + return r; + + return m.m_type; +} + +/* + * Obtain the file name for the PTYFS node with the given index, and store it + * in the given 'name' buffer which consists of 'size' bytes. On success, + * return OK, with the file name stored as a null-terminated string. The + * returned name does not include the PTYFS mount path. On failure, return an + * error code. Among other reasons, the function fails if no node is allocated + * for the given index, and if the name does not fit in the given buffer. + */ +int +ptyfs_name(unsigned int index, char * name, size_t size) +{ + message m; + int r; + + memset(&m, 0, sizeof(m)); + + m.m_type = PTYFS_NAME; + m.m_pty_ptyfs_req.index = index; + + if ((r = ptyfs_sendrec(&m)) != OK) + return r; + + if (m.m_type != OK) + return m.m_type; + + /* Ensure null termination, and make sure the string fits. */ + m.m_ptyfs_pty_name.name[sizeof(m.m_ptyfs_pty_name.name) - 1] = 0; + if (strlen(m.m_ptyfs_pty_name.name) >= size) + return ENAMETOOLONG; + + strlcpy(name, m.m_ptyfs_pty_name.name, size); + return OK; +} diff --git a/minix/drivers/tty/pty/ptyfs.h b/minix/drivers/tty/pty/ptyfs.h new file mode 100644 index 000000000..ebce59d7e --- /dev/null +++ b/minix/drivers/tty/pty/ptyfs.h @@ -0,0 +1,9 @@ +#ifndef _MINIX_PTY_PTYFS_H +#define _MINIX_PTY_PTYFS_H + +int ptyfs_set(unsigned int index, mode_t mode, uid_t uid, gid_t gid, + dev_t dev); +int ptyfs_clear(unsigned int index); +int ptyfs_name(unsigned int index, char * name, size_t size); + +#endif /* !_MINIX_PTY_PTYFS_H */ diff --git a/minix/drivers/tty/pty/tty.c b/minix/drivers/tty/pty/tty.c index 1f71a17d5..a513f8482 100644 --- a/minix/drivers/tty/pty/tty.c +++ b/minix/drivers/tty/pty/tty.c @@ -2,6 +2,7 @@ #include #include #include +#include #include #include #include @@ -22,8 +23,6 @@ /* A device exists if at least its 'devread' function is defined. */ #define tty_active(tp) ((tp)->tty_devread != NULL) -struct kmessages kmess; - static void tty_timed_out(minix_timer_t *tp); static void settimer(tty_t *tty_ptr, int enable); static void in_transfer(tty_t *tp); @@ -41,8 +40,6 @@ static ssize_t do_read(devminor_t minor, u64_t position, endpoint_t endpt, cp_grant_id_t grant, size_t size, int flags, cdev_id_t id); static ssize_t do_write(devminor_t minor, u64_t position, endpoint_t endpt, cp_grant_id_t grant, size_t size, int flags, cdev_id_t id); -static int do_ioctl(devminor_t minor, unsigned long request, endpoint_t endpt, - cp_grant_id_t grant, int flags, endpoint_t user_endpt, cdev_id_t id); static int do_cancel(devminor_t minor, endpoint_t endpt, cdev_id_t id); static int do_select(devminor_t minor, unsigned int ops, endpoint_t endpt); @@ -51,7 +48,7 @@ static struct chardriver tty_tab = { .cdr_close = do_close, .cdr_read = do_read, .cdr_write = do_write, - .cdr_ioctl = do_ioctl, + .cdr_ioctl = tty_ioctl, .cdr_cancel = do_cancel, .cdr_select = do_select }; @@ -89,14 +86,39 @@ static const char lined[TTLINEDNAMELEN] = "termios"; /* line discipline */ /* Global variables for the TTY task (declared extern in tty.h). */ tty_t tty_table[NR_PTYS]; u32_t system_hz; +int tty_gid; + +static struct optset optset_table[] = { + { "gid", OPT_INT, &tty_gid, 10 }, + { NULL, 0, NULL, 0 } +}; static void tty_startup(void); static int tty_init(int, sef_init_info_t *); +/*===========================================================================* + * is_pty * + *===========================================================================*/ +static int is_pty(devminor_t line) +{ +/* Return TRUE if the given minor device number refers to a pty (the master + * side of a pty/tty pair), and FALSE otherwise. + */ + + if (line == PTMX_MINOR) + return TRUE; + if (line >= PTYPX_MINOR && line < PTYPX_MINOR + NR_PTYS) + return TRUE; + if (line >= UNIX98_MINOR && line < UNIX98_MINOR + NR_PTYS * 2 && !(line & 1)) + return TRUE; + + return FALSE; +} + /*===========================================================================* * tty_task * *===========================================================================*/ -int main(void) +int main(int argc, char **argv) { /* Main routine of the terminal task. */ @@ -106,6 +128,8 @@ int main(void) int r; register tty_t *tp; + env_setargs(argc, argv); + tty_startup(); while (TRUE) { @@ -145,11 +169,10 @@ int main(void) if (OK != chardriver_get_minor(&tty_mess, &line)) continue; - if ((line >= PTYPX_MINOR) && (line < (PTYPX_MINOR + NR_PTYS)) && - tty_mess.m_type != CDEV_IOCTL) { + if (is_pty(line)) { /* Terminals and pseudo terminals belong together. We can only - * make a distinction between the two based on position in the - * tty_table and not on minor number. Hence this special case. + * make a distinction between the two based on minor number and + * not on position in the tty_table. Hence this special case. */ do_pty(&tty_mess, ipc_status); continue; @@ -173,6 +196,8 @@ line2tty(devminor_t line) tp = tty_addr(line - TTYPX_MINOR); } else if ((line - PTYPX_MINOR) < NR_PTYS) { tp = tty_addr(line - PTYPX_MINOR); + } else if ((line - UNIX98_MINOR) < NR_PTYS * 2) { + tp = tty_addr((line - UNIX98_MINOR) >> 1); } else { tp = NULL; } @@ -315,9 +340,9 @@ static ssize_t do_write(devminor_t minor, u64_t UNUSED(position), } /*===========================================================================* - * do_ioctl * + * tty_ioctl * *===========================================================================*/ -static int do_ioctl(devminor_t minor, unsigned long request, endpoint_t endpt, +int tty_ioctl(devminor_t minor, unsigned long request, endpoint_t endpt, cp_grant_id_t grant, int flags, endpoint_t user_endpt, cdev_id_t id) { /* Perform an IOCTL on this terminal. POSIX termios calls are handled @@ -444,6 +469,10 @@ static int do_open(devminor_t minor, int access, endpoint_t user_endpt) if ((tp = line2tty(minor)) == NULL) return ENXIO; + /* Make sure the user is not mixing Unix98 and old-style ends. */ + if ((*tp->tty_mayopen)(tp, minor) == FALSE) + return EIO; + if (!(access & CDEV_NOCTTY)) { tp->tty_pgrp = user_endpt; r = CDEV_CTTY; @@ -716,7 +745,7 @@ int count; /* number of input characters */ * the number of characters processed. */ - int ch, sig, ct; + int ch, ct; int timeset = FALSE; for (ct = 0; ct < count; ct++) { @@ -1170,9 +1199,6 @@ tty_t *tp; /* Setting the output speed to zero hangs up the phone. */ if (tp->tty_termios.c_ospeed == B0) sigchar(tp, SIGHUP, 1); - - /* Set new line speed, character size, etc at the device level. */ - (*tp->tty_ioctl)(tp, 0); } /*===========================================================================* @@ -1239,6 +1265,10 @@ static int tty_init(int UNUSED(type), sef_init_info_t *UNUSED(info)) system_hz = sys_hz(); + tty_gid = -1; + if (env_argc > 1) + optset_parse(optset_table, env_argv[1]); + /* Initialize the terminal lines. */ memset(tty_table, '\0' , sizeof(tty_table)); @@ -1251,10 +1281,9 @@ static int tty_init(int UNUSED(type), sef_init_info_t *UNUSED(info)) tp->tty_min = 1; tp->tty_incaller = tp->tty_outcaller = tp->tty_iocaller = NONE; tp->tty_termios = termios_defaults; - tp->tty_icancel = tp->tty_ocancel = tp->tty_ioctl = tp->tty_close = + tp->tty_icancel = tp->tty_ocancel = tp->tty_close = tp->tty_open = tty_devnop; pty_init(tp); - tp->tty_minor = s + TTYPX_MINOR; } return OK; diff --git a/minix/drivers/tty/pty/tty.h b/minix/drivers/tty/pty/tty.h index 8d353cfec..7dbb5f5c6 100644 --- a/minix/drivers/tty/pty/tty.h +++ b/minix/drivers/tty/pty/tty.h @@ -4,8 +4,10 @@ #include /* First minor numbers for the various classes of TTY devices. */ +#define PTMX_MINOR 0 /* minor of the Unix98 clone device */ #define TTYPX_MINOR 128 #define PTYPX_MINOR 192 +#define UNIX98_MINOR 256 /* start of Unix98 pairs */ #define TTY_IN_BYTES 256 /* tty input queue size */ #define TTY_OUT_BYTES 2048 /* tty output queue size */ @@ -15,13 +17,13 @@ #define ESC '\33' /* escape */ struct tty; -typedef int(*devfun_t) (struct tty *tp, int try_only); -typedef void(*devfunarg_t) (struct tty *tp, int c); +typedef int (*devfun_t)(struct tty *tp, int try_only); +typedef void (*devfunarg_t)(struct tty *tp, int c); +typedef int (*devfunline_t)(struct tty *tp, devminor_t line); typedef struct tty { int tty_events; /* set when TTY should inspect this line */ int tty_index; /* index into TTY table */ - devminor_t tty_minor; /* device minor number */ /* Input queue. Typed characters are stored here until read by a program. */ u16_t *tty_inhead; /* pointer to place where next char goes */ @@ -70,7 +72,7 @@ typedef struct tty { devminor_t tty_select_minor; /* minor used to start select query */ /* Miscellaneous. */ - devfun_t tty_ioctl; /* set line speed, etc. at the device level */ + devfunline_t tty_mayopen; /* check whether this tty may be opened */ devfun_t tty_open; /* tell the device that the tty is opened */ devfun_t tty_close; /* tell the device that the tty is closed */ void *tty_priv; /* pointer to per device private data */ @@ -84,6 +86,7 @@ typedef struct tty { /* Memory allocated in tty.c, so extern here. */ extern tty_t tty_table[NR_PTYS]; extern u32_t system_hz; /* system clock frequency */ +extern int tty_gid; /* group ID of the "tty" group */ /* Values for the fields. */ #define NOT_ESCAPED 0 /* previous character is not LNEXT (^V) */ @@ -115,6 +118,8 @@ void out_process(struct tty *tp, char *bstart, char *bpos, char *bend, void tty_wakeup(clock_t now); int select_try(struct tty *tp, int ops); int select_retry(struct tty *tp); +int tty_ioctl(devminor_t minor, unsigned long request, endpoint_t endpt, + cp_grant_id_t grant, int flags, endpoint_t user_endpt, cdev_id_t id); /* pty.c */ void do_pty(message *m_ptr, int ipc_status); diff --git a/minix/fs/Makefile b/minix/fs/Makefile index 5dec62ce8..6f18ad55c 100644 --- a/minix/fs/Makefile +++ b/minix/fs/Makefile @@ -7,6 +7,7 @@ SUBDIR+= pfs SUBDIR+= ext2 SUBDIR+= isofs SUBDIR+= procfs +SUBDIR+= ptyfs . if ${MACHINE_ARCH} == "i386" SUBDIR+= hgfs diff --git a/minix/fs/procfs/service.c b/minix/fs/procfs/service.c index 4385741aa..852332a48 100644 --- a/minix/fs/procfs/service.c +++ b/minix/fs/procfs/service.c @@ -112,6 +112,7 @@ service_get_policies(struct policies * pol, index_t slot) { .label = "mfs", .policy_str = "" }, { .label = "pfs", .policy_str = "" }, { .label = "procfs", .policy_str = "" }, + { .label = "ptyfs", .policy_str = "" }, { .label = "vbfs", .policy_str = "" }, /* net */ { .label = "inet", .policy_str = "reset" }, diff --git a/minix/fs/ptyfs/Makefile b/minix/fs/ptyfs/Makefile new file mode 100644 index 000000000..3858c861c --- /dev/null +++ b/minix/fs/ptyfs/Makefile @@ -0,0 +1,10 @@ +# Makefile for PTYFS server +.include + +PROG= ptyfs +SRCS= ptyfs.c node.c + +DPADD+= ${LIBFSDRIVER} +LDADD+= -lfsdriver + +.include diff --git a/minix/fs/ptyfs/node.c b/minix/fs/ptyfs/node.c new file mode 100644 index 000000000..0840eb669 --- /dev/null +++ b/minix/fs/ptyfs/node.c @@ -0,0 +1,84 @@ +/* PTYFS slave node management */ +/* + * While the interface of this module should be flexible enough to implement + * various memory management approaches, the current code simply relies on + * NR_PTYS being small enough to preallocate all data structures. In the + * future, NR_PTYS will no longer be a system-global definition, and future + * implementations of this module should not rely on NR_PTYS at all. + */ + +#include + +#include "node.h" + +static bitchunk_t node_map[BITMAP_CHUNKS(NR_PTYS)]; +static struct node_data node_data[NR_PTYS]; + +/* + * Initialize the node module. + */ +void +init_nodes(void) +{ + + memset(&node_map, 0, sizeof(node_map)); +} + +/* + * Allocate a node with a given node index number, and save node data for it. + * It is possible that the node is in use already; in that case, only update + * its associated data. Return OK on success, or an error code on failure. + */ +int +set_node(node_t index, struct node_data * data) +{ + + if (index >= NR_PTYS) + return ENOMEM; + + SET_BIT(node_map, index); + + node_data[index] = *data; + + return OK; +} + +/* + * Deallocate a node using its node index number. This function always + * succeeds, intentionally ignoring the case that the node was not allocated. + */ +void +clear_node(node_t index) +{ + + UNSET_BIT(node_map, index); +} + +/* + * Return a pointer to the node data associated with the given node index + * number. If the node is not allocated, return NULL. + */ +struct node_data * +get_node(node_t index) +{ + + if (index >= NR_PTYS || !GET_BIT(node_map, index)) + return NULL; + + return &node_data[index]; +} + +/* + * Return the highest allocated node index number, plus one. This value is + * used to check given node indices and limit linear iterations. + */ +node_t +get_max_node(void) +{ + + /* + * NR_PTYS is low enough that we can always return it instead of + * tracking the actual value. + */ + return NR_PTYS; +} diff --git a/minix/fs/ptyfs/node.h b/minix/fs/ptyfs/node.h new file mode 100644 index 000000000..5ea7e83b9 --- /dev/null +++ b/minix/fs/ptyfs/node.h @@ -0,0 +1,20 @@ +#ifndef _MINIX_PTYFS_NODE_H +#define _MINIX_PTYFS_NODE_H + +typedef unsigned int node_t; + +struct node_data { + dev_t dev; + mode_t mode; + uid_t uid; + gid_t gid; + time_t ctime; +}; + +void init_nodes(void); +int set_node(node_t index, struct node_data *data); +void clear_node(node_t index); +struct node_data *get_node(node_t index); +node_t get_max_node(void); + +#endif /* !_MINIX_PTYFS_NODE_H */ diff --git a/minix/fs/ptyfs/ptyfs.c b/minix/fs/ptyfs/ptyfs.c new file mode 100644 index 000000000..39a6c65bc --- /dev/null +++ b/minix/fs/ptyfs/ptyfs.c @@ -0,0 +1,434 @@ +/* PTYFS - file system for Unix98 pseudoterminal slave nodes (/dev/pts) */ + +#include +#include +#include +#include +#include +#include + +#include "node.h" + +#define ROOT_INO_NR 1 /* inode number of the root directory */ +#define BASE_INO_NR 2 /* first inode number for slave nodes */ + +#define GETDENTS_BUF 1024 /* size of the temporary buffer for getdents */ + +static struct node_data root_data = { + .mode = S_IFDIR | 0755, + .uid = 0, + .gid = 0, + .dev = NO_DEV +}; + +/* + * Mount the file system. + */ +static int +ptyfs_mount(dev_t __unused dev, unsigned int flags, + struct fsdriver_node * root_node, unsigned int * res_flags) +{ + + /* This file system can not be used as a root file system. */ + if (flags & REQ_ISROOT) + return EINVAL; + + /* Return the details of the root node. */ + root_node->fn_ino_nr = ROOT_INO_NR; + root_node->fn_mode = root_data.mode; + root_node->fn_uid = root_data.uid; + root_node->fn_gid = root_data.gid; + root_node->fn_size = 0; + root_node->fn_dev = root_data.dev; + + *res_flags = RES_NOFLAGS; + + return OK; +} + +/* + * Generate the name string of a slave node based on its node number. Return + * OK on success, with the null-terminated name stored in the buffer 'name' + * which is 'size' bytes in size. Return an error code on failure. + */ +static int +make_name(char * name, size_t size, node_t index) +{ + ssize_t r; + + if ((r = snprintf(name, sizeof(name), "%u", index)) < 0) + return EINVAL; + + if (r >= size) + return ENAMETOOLONG; + + return OK; +} + +/* + * Parse the name of a slave node as given by a user, and check whether it is a + * valid slave node number. A valid slave number is any name that can be + * produced by make_name(). Return TRUE if the string was successfully parsed + * as a slave node number (which may or may not actually be allocated), with + * the number stored in 'indexp'. Return FALSE if the name is not a number. + */ +static int +parse_name(const char * name, node_t * indexp) +{ + node_t index; + const char *p; + + index = 0; + for (p = name; *p; p++) { + /* Digits only. */ + if (*p < '0' || *p > '9') + return FALSE; + + /* No leading zeroes. */ + if (p != name && index == 0) + return FALSE; + + /* No overflow. */ + if (index * 10 < index) + return FALSE; + + index = index * 10 + *p - '0'; + } + + *indexp = index; + return TRUE; +} + +/* + * Look up a name in a directory, yielding a node on success. For a successful + * lookup, the given name must either be a single dot, which resolves to the + * file system root directory, or the number of an allocated slave node. + */ +static int +ptyfs_lookup(ino_t dir_nr, char * name, struct fsdriver_node * node, + int * is_mountpt) +{ + struct node_data *data; + node_t index; + ino_t ino_nr; + + assert(name[0] != '\0'); + + if (dir_nr != ROOT_INO_NR) + return ENOENT; + + if (name[0] == '.' && name[1] == '\0') { + /* The root directory itself is requested. */ + ino_nr = ROOT_INO_NR; + + data = &root_data; + } else { + /* Parse the user-provided name, which must be a number. */ + if (!parse_name(name, &index)) + return ENOENT; + + ino_nr = BASE_INO_NR + index; + + /* See if the number is in use, and get its details. */ + if ((data = get_node(index)) == NULL) + return ENOENT; + } + + node->fn_ino_nr = ino_nr; + node->fn_mode = data->mode; + node->fn_uid = data->uid; + node->fn_gid = data->gid; + node->fn_size = 0; + node->fn_dev = data->dev; + + *is_mountpt = FALSE; + + return OK; +} + +/* + * Enumerate directory contents. + */ +static ssize_t +ptyfs_getdents(ino_t ino_nr, struct fsdriver_data * data, + size_t bytes, off_t * posp) +{ + struct fsdriver_dentry fsdentry; + static char buf[GETDENTS_BUF]; + char name[NAME_MAX + 1]; + struct node_data *node_data; + unsigned int type; + off_t pos; + node_t index; + ssize_t r; + + if (ino_nr != ROOT_INO_NR) + return EINVAL; + + fsdriver_dentry_init(&fsdentry, data, bytes, buf, sizeof(buf)); + + for (;;) { + pos = (*posp)++; + + if (pos < 2) { + strlcpy(name, (pos == 0) ? "." : "..", sizeof(name)); + ino_nr = ROOT_INO_NR; + type = DT_DIR; + } else { + if (pos - 2 >= get_max_node()) + break; /* EOF */ + index = (node_t)(pos - 2); + + if ((node_data = get_node(index)) == NULL) + continue; /* index not in use */ + + if (make_name(name, sizeof(name), index) != OK) + continue; /* could not generate name string */ + ino_nr = BASE_INO_NR + index; + type = IFTODT(node_data->mode); + } + + if ((r = fsdriver_dentry_add(&fsdentry, ino_nr, name, + strlen(name), type)) < 0) + return r; + if (r == 0) + break; /* result buffer full */ + } + + return fsdriver_dentry_finish(&fsdentry); +} + +/* + * Return a pointer to the node data structure for the given inode number, or + * NULL if no node exists for the given inode number. + */ +static struct node_data * +get_data(ino_t ino_nr) +{ + node_t index; + + if (ino_nr == ROOT_INO_NR) + return &root_data; + + if (ino_nr < BASE_INO_NR || ino_nr >= BASE_INO_NR + get_max_node()) + return NULL; + + index = (node_t)(ino_nr - BASE_INO_NR); + + return get_node(index); +} + +/* + * Change file ownership. + */ +static int +ptyfs_chown(ino_t ino_nr, uid_t uid, gid_t gid, mode_t * mode) +{ + struct node_data *data; + + if ((data = get_data(ino_nr)) == NULL) + return EINVAL; + + data->uid = uid; + data->gid = gid; + data->mode &= ~(S_ISUID | S_ISGID); + + *mode = data->mode; + + return OK; +} + +/* + * Change file mode. + */ +static int +ptyfs_chmod(ino_t ino_nr, mode_t * mode) +{ + struct node_data *data; + + if ((data = get_data(ino_nr)) == NULL) + return EINVAL; + + data->mode = (data->mode & ~ALLPERMS) | (*mode & ALLPERMS); + + *mode = data->mode; + + return OK; +} + +/* + * Return node details. + */ +static int +ptyfs_stat(ino_t ino_nr, struct stat * buf) +{ + struct node_data *data; + + if ((data = get_data(ino_nr)) == NULL) + return EINVAL; + + buf->st_mode = data->mode; + buf->st_uid = data->uid; + buf->st_gid = data->gid; + buf->st_nlink = S_ISDIR(data->mode) ? 2 : 1; + buf->st_rdev = data->dev; + buf->st_atime = data->ctime; + buf->st_mtime = data->ctime; + buf->st_ctime = data->ctime; + + return OK; +} + +/* + * Return file system statistics. + */ +static int +ptyfs_statvfs(struct statvfs * buf) +{ + + buf->f_flag = ST_NOTRUNC; + buf->f_namemax = NAME_MAX; + + return OK; +} + +/* + * Process non-filesystem messages, in particular slave node creation and + * deletion requests from the PTY service. + */ +static void +ptyfs_other(const message * m_ptr, int ipc_status) +{ + char label[DS_MAX_KEYLEN]; + struct node_data data; + message m_reply; + int r; + + /* + * We only accept requests from the service with the label "pty". + * More sophisticated access checks are part of future work. + */ + if ((r = ds_retrieve_label_name(label, m_ptr->m_source)) != OK) { + printf("PTYFS: unable to obtain label for %u (%d)\n", + m_ptr->m_source, r); + return; + } + + if (strcmp(label, "pty")) { + printf("PTYFS: unexpected request %x from %s/%u\n", + m_ptr->m_type, label, m_ptr->m_source); + return; + } + + /* Process the request from PTY. */ + memset(&m_reply, 0, sizeof(m_reply)); + + switch (m_ptr->m_type) { + case PTYFS_SET: + memset(&data, 0, sizeof(data)); + data.dev = m_ptr->m_pty_ptyfs_req.dev; + data.mode = m_ptr->m_pty_ptyfs_req.mode; + data.uid = m_ptr->m_pty_ptyfs_req.uid; + data.gid = m_ptr->m_pty_ptyfs_req.gid; + data.ctime = clock_time(NULL); + + r = set_node(m_ptr->m_pty_ptyfs_req.index, &data); + + break; + + case PTYFS_CLEAR: + clear_node(m_ptr->m_pty_ptyfs_req.index); + r = OK; + + break; + + case PTYFS_NAME: + r = make_name(m_reply.m_ptyfs_pty_name.name, + sizeof(m_reply.m_ptyfs_pty_name.name), + m_ptr->m_pty_ptyfs_req.index); + + break; + + default: + printf("PTYFS: invalid request %x from PTY\n", m_ptr->m_type); + r = ENOSYS; + } + + /* + * Send a reply to the request. In particular slave node addition + * requests must be blocking for the PTY service, so as to avoid race + * conditions between PTYFS creating the slave node and userland trying + * to open it. + */ + m_reply.m_type = r; + + if (IPC_STATUS_CALL(ipc_status) == SENDREC) + r = ipc_sendnb(m_ptr->m_source, &m_reply); + else + r = asynsend3(m_ptr->m_source, &m_reply, AMF_NOREPLY); + + if (r != OK) + printf("PTYFS: unable to reply to PTY (%d)\n", r); +} + +/* + * Initialize the service. + */ +static int +ptyfs_init(int __unused type, sef_init_info_t * __unused info) +{ + + init_nodes(); + + root_data.ctime = clock_time(NULL); + + return OK; +} + +/* + * Process an incoming signal. + */ +static void +ptyfs_signal(int sig) +{ + + if (sig == SIGTERM) + fsdriver_terminate(); +} + +/* + * Perform SEF initialization. + */ +static void +ptyfs_startup(void) +{ + + sef_setcb_init_fresh(ptyfs_init); + sef_setcb_signal_handler(ptyfs_signal); + sef_startup(); +} + +static struct fsdriver ptyfs_table = { + .fdr_mount = ptyfs_mount, + .fdr_lookup = ptyfs_lookup, + .fdr_getdents = ptyfs_getdents, + .fdr_stat = ptyfs_stat, + .fdr_chown = ptyfs_chown, + .fdr_chmod = ptyfs_chmod, + .fdr_statvfs = ptyfs_statvfs, + .fdr_other = ptyfs_other +}; + +/* + * The PTYFS service. + */ +int +main(void) +{ + + ptyfs_startup(); + + fsdriver_task(&ptyfs_table); + + return 0; +} diff --git a/minix/include/minix/com.h b/minix/include/minix/com.h index fb4a11f8d..c3d42cc77 100644 --- a/minix/include/minix/com.h +++ b/minix/include/minix/com.h @@ -28,6 +28,7 @@ * 0x1400 - 0x14FF Real Time Clock requests and responses * 0x1500 - 0x15FF Input server messages * 0x1600 - 0x16FF VirtualBox (VBOX) requests (see vboxif.h) + * 0x1700 - 0x17FF PTYFS requests * * Zero and negative values are widely used for OK and error responses. */ @@ -848,6 +849,16 @@ #define INPUT_EVENT (INPUT_RS_BASE + 0) /* send input event */ +/*===========================================================================* + * Messages for PTYFS * + *===========================================================================*/ + +#define PTYFS_BASE 0x1700 + +#define PTYFS_SET (PTYFS_BASE + 0) /* add/update node */ +#define PTYFS_CLEAR (PTYFS_BASE + 1) /* delete node */ +#define PTYFS_NAME (PTYFS_BASE + 2) /* get node name */ + /*===========================================================================* * VFS-FS TRANSACTION IDs * *===========================================================================*/ diff --git a/minix/include/minix/ipc.h b/minix/include/minix/ipc.h index bc4e2cf3a..c05a5c113 100644 --- a/minix/include/minix/ipc.h +++ b/minix/include/minix/ipc.h @@ -1558,6 +1558,24 @@ typedef struct { } mess_pm_sched_scheduling_set_nice; _ASSERT_MSG_SIZE(mess_pm_sched_scheduling_set_nice); +typedef struct { + dev_t dev; + mode_t mode; + uid_t uid; + gid_t gid; + uint32_t index; + + uint8_t padding[32]; +} mess_pty_ptyfs_req; +_ASSERT_MSG_SIZE(mess_pty_ptyfs_req); + +typedef struct { + char name[20]; + + uint8_t padding[36]; +} mess_ptyfs_pty_name; +_ASSERT_MSG_SIZE(mess_ptyfs_pty_name); + typedef struct { int status; @@ -2176,6 +2194,8 @@ typedef struct { mess_pm_lsys_getprocnr m_pm_lsys_getprocnr; mess_pm_lsys_sigs_signal m_pm_lsys_sigs_signal; mess_pm_sched_scheduling_set_nice m_pm_sched_scheduling_set_nice; + mess_pty_ptyfs_req m_pty_ptyfs_req; + mess_ptyfs_pty_name m_ptyfs_pty_name; mess_readclock_lc_rtcdev m_readclock_lc_rtcdev; mess_rs_init m_rs_init; mess_rs_pm_exec_restart m_rs_pm_exec_restart; diff --git a/minix/tests/run b/minix/tests/run index fd24433c4..d46a3c3c5 100755 --- a/minix/tests/run +++ b/minix/tests/run @@ -21,7 +21,7 @@ badones= # list of tests that failed # Programs that require setuid setuids="test11 test33 test43 test44 test46 test56 test60 test61 test65 \ - test69 test76 test73 test74 test77 test78" + test69 test73 test74 test78" # Scripts that require to be run as root rootscripts="testisofs testvnd testrelpol" diff --git a/minix/tests/test76.c b/minix/tests/test76.c index 7b41d18a3..0ec2be4de 100644 --- a/minix/tests/test76.c +++ b/minix/tests/test76.c @@ -1,5 +1,4 @@ /* Tests for interrupting VFS calls - by D.C. van Moolenbroek */ -/* This test needs to be run as root; otherwise, openpty() won't work. */ #include #include #include diff --git a/minix/tests/test77.c b/minix/tests/test77.c index e1e6c7290..2ad246159 100644 --- a/minix/tests/test77.c +++ b/minix/tests/test77.c @@ -1,5 +1,12 @@ /* Tests for opening/closing pseudo terminals - by D.C. van Moolenbroek */ -/* This test needs to be run as root; otherwise, openpty() won't work. */ +/* + * As of the introduction of Unix98 PTY support, this test set actually relies + * on the ability to create Unix98 PTYs. The system still supports old-style + * PTYs but there is no way to force openpty(3) to use them. However, part of + * this test set can still be used to test old-style PTYs: first disable Unix98 + * PTYs, for example by unmounting PTYFS or temporarily removing /dev/ptmx, and + * then run the a-f subtests from this test set as root. + */ #include #include #include @@ -8,11 +15,15 @@ #include #include #include +#include +#include #include #include #define ITERATIONS 10 +#define MIN_PTYS 4 + #include "common.h" static int sighups; /* number of SIGHUP signals received */ @@ -36,11 +47,11 @@ make_raw(int slavefd) { struct termios tios; - if (tcgetattr(slavefd, &tios) < 0) e(100); + if (tcgetattr(slavefd, &tios) < 0) e(0); cfmakeraw(&tios); - if (tcsetattr(slavefd, TCSANOW, &tios) < 0) e(101); + if (tcsetattr(slavefd, TCSANOW, &tios) < 0) e(0); } /* @@ -55,149 +66,196 @@ test_comm(int masterfd, int slavefd) make_raw(slavefd); c = 'A'; - if (write(masterfd, &c, sizeof(c)) != sizeof(c)) e(200); - if (read(slavefd, &c, sizeof(c)) != sizeof(c)) e(201); - if (c != 'A') e(202); + if (write(masterfd, &c, sizeof(c)) != sizeof(c)) e(0); + if (read(slavefd, &c, sizeof(c)) != sizeof(c)) e(0); + if (c != 'A') e(0); c = 'B'; - if (write(slavefd, &c, sizeof(c)) != sizeof(c)) e(203); - if (read(masterfd, &c, sizeof(c)) != sizeof(c)) e(204); - if (c != 'B') e(205); + if (write(slavefd, &c, sizeof(c)) != sizeof(c)) e(0); + if (read(masterfd, &c, sizeof(c)) != sizeof(c)) e(0); + if (c != 'B') e(0); c = 'C'; - if (write(masterfd, &c, sizeof(c)) != sizeof(c)) e(206); - if (read(slavefd, &c, sizeof(c)) != sizeof(c)) e(207); - if (c != 'C') e(208); + if (write(masterfd, &c, sizeof(c)) != sizeof(c)) e(0); + if (read(slavefd, &c, sizeof(c)) != sizeof(c)) e(0); + if (c != 'C') e(0); c = 'D'; - if (write(slavefd, &c, sizeof(c)) != sizeof(c)) e(209); - if (read(masterfd, &c, sizeof(c)) != sizeof(c)) e(210); - if (c != 'D') e(211); + if (write(slavefd, &c, sizeof(c)) != sizeof(c)) e(0); + if (read(masterfd, &c, sizeof(c)) != sizeof(c)) e(0); + if (c != 'D') e(0); } /* - * Get device node names for the master and slave end of a free pseudo - * terminal. We don't want to replicate the entire openpty(3) logic here, so - * start by letting openpty(3) do the work for us. We make the assumption that - * nobody snatches the pair while we are running. + * Obtain a pseudo terminal. The master end is opened and its file descriptor + * stored in 'pfd'. The slave path name is stored in 'tname'. For old-style + * PTYs, the function returns 1 and stores the master name in 'pname' if not + * NULL. For Unix98 PTYs, the function returns 0, in which case no master name + * is available. For old-style PTYs, the caller may close and reopen the + * master. In that case, we make the assumption that nobody snatches the pair + * while we are running. For Unix98 PTYs, the master must be kept open. */ -static void -get_names(char pname[PATH_MAX], char tname[PATH_MAX]) +static int +get_pty(int *pfd, char pname[PATH_MAX], char tname[PATH_MAX]) { + char *name; int len, masterfd, slavefd; - if (openpty(&masterfd, &slavefd, tname, NULL, NULL) < 0) e(300); - /* - * openpty(3) gives us only the slave name, but we also need the master - * name. + * First try Unix98 PTY allocation, mainly to avoid opening the slave + * end immediately. If this fails, try openpty(3) as well. */ - strlcpy(pname, tname, PATH_MAX); - len = strlen(_PATH_DEV); + if ((masterfd = posix_openpt(O_RDWR | O_NOCTTY)) != -1) { + if (grantpt(masterfd) != -1 && unlockpt(masterfd) != -1 && + (name = ptsname(masterfd)) != NULL) { + *pfd = masterfd; + strlcpy(tname, name, PATH_MAX); - if (strncmp(pname, _PATH_DEV, len)) e(301); + return 0; + } + if (close(masterfd) < 0) e(0); + } - /* If this fails, this test needs to be updated. */ - if (pname[len] != 't') e(302); - - pname[len] = 'p'; + if (openpty(&masterfd, &slavefd, tname, NULL, NULL) < 0) e(0); test_comm(masterfd, slavefd); - if (close(masterfd) < 0) e(303); - if (close(slavefd) < 0) e(304); + *pfd = masterfd; + + if (close(slavefd) < 0) e(0); + + /* + * openpty(3) gives us only the slave name, but we also want the master + * name. + */ + len = strlen(_PATH_DEV); + if (strncmp(tname, _PATH_DEV, len)) e(0); + + if (strncmp(&tname[len], "tty", 3)) + return 0; /* Unix98 after all? Well okay, whatever.. */ + + if (pname != NULL) { + strlcpy(pname, tname, PATH_MAX); + pname[len] = 'p'; + } + + return 1; } /* * Test various orders of opening and closing the master and slave sides of a * pseudo terminal, as well as opening/closing one side without ever opening - * the other. + * the other. This test is meaningful mainly for old-style pseudoterminals. */ static void test77a(void) { struct sigaction act, oact; char pname[PATH_MAX], tname[PATH_MAX]; - int masterfd, slavefd; + int oldstyle, masterfd, slavefd; subtest = 1; /* We do not want to get SIGHUP signals in this test. */ memset(&act, 0, sizeof(act)); act.sa_handler = SIG_IGN; - if (sigaction(SIGHUP, &act, &oact) < 0) e(1); + if (sigaction(SIGHUP, &act, &oact) < 0) e(0); - /* Get master and slave device names for a free pseudo terminal. */ - get_names(pname, tname); + /* Obtain a pseudo terminal. */ + oldstyle = get_pty(&masterfd, pname, tname); - /* Try opening and then closing the master. */ - if ((masterfd = open(pname, O_RDWR | O_NOCTTY)) < 0) e(2); + if (oldstyle) { + /* Try closing the master. */ + if (close(masterfd) < 0) e(0); - if (close(masterfd) < 0) e(3); + /* See if we can reopen the master. */ + if ((masterfd = open(pname, O_RDWR | O_NOCTTY)) < 0) e(0); + } - /* Now see if we can reopen the master as well as the slave. */ - if ((masterfd = open(pname, O_RDWR | O_NOCTTY)) < 0) e(4); - if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(5); + if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(0); test_comm(masterfd, slavefd); /* In the meantime, test different closing orders. This is order A. */ - if (close(slavefd) < 0) e(6); - if (close(masterfd) < 0) e(7); + if (close(slavefd) < 0) e(0); + if (close(masterfd) < 0) e(0); - /* Now try opening the pair again. */ - if ((masterfd = open(pname, O_RDWR | O_NOCTTY)) < 0) e(8); - if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(9); + /* Now try opening the pair (or a new pair) again. */ + if (!oldstyle) + oldstyle = get_pty(&masterfd, pname, tname); + else + if ((masterfd = open(pname, O_RDWR | O_NOCTTY)) < 0) e(0); + + if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(0); test_comm(masterfd, slavefd); - if (close(slavefd) < 0) e(10); + if (close(slavefd) < 0) e(0); /* * Try reopening the slave after closing it. It is not very important * that this works, but the TTY driver should currently support it. */ - if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(11); + if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(0); test_comm(masterfd, slavefd); /* This is closing order B. This may or may not cause a SIGHUP. */ - if (close(masterfd) < 0) e(12); - if (close(slavefd) < 0) e(13); + if (close(masterfd) < 0) e(0); + if (close(slavefd) < 0) e(0); /* Try the normal open procedure. */ - if ((masterfd = open(pname, O_RDWR | O_NOCTTY)) < 0) e(14); - if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(15); + if (!oldstyle) + oldstyle = get_pty(&masterfd, pname, tname); + else + if ((masterfd = open(pname, O_RDWR | O_NOCTTY)) < 0) e(0); + + if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(0); test_comm(masterfd, slavefd); - if (close(slavefd) < 0) e(16); - if (close(masterfd) < 0) e(17); + if (close(slavefd) < 0) e(0); + if (close(masterfd) < 0) e(0); - /* Try reopening and closing the slave, without opening the master. */ - if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(18); + /* + * Try reopening and closing the slave, without opening the master. + * This should work on old-style PTYS, but not on Unix98 PTYs. + */ + if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) >= 0) { + if (!oldstyle) e(0); - if (close(slavefd) < 0) e(19); + if (close(slavefd) < 0) e(0); + } else + if (oldstyle) e(0); /* Again, try the normal open procedure. */ - if ((masterfd = open(pname, O_RDWR | O_NOCTTY)) < 0) e(20); - if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(21); + if (!oldstyle) + oldstyle = get_pty(&masterfd, pname, tname); + else + if ((masterfd = open(pname, O_RDWR | O_NOCTTY)) < 0) e(0); + + if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(0); test_comm(masterfd, slavefd); - if (close(slavefd) < 0) e(22); - if (close(masterfd) < 0) e(23); + if (close(slavefd) < 0) e(0); + if (close(masterfd) < 0) e(0); - /* Finally, try opening the slave first. */ - if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(24); - if ((masterfd = open(pname, O_RDWR | O_NOCTTY)) < 0) e(25); + /* + * Finally, try opening the slave first. This does not work with + * Unix98 PTYs. + */ + if (oldstyle) { + if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(0); + if ((masterfd = open(pname, O_RDWR | O_NOCTTY)) < 0) e(0); - test_comm(masterfd, slavefd); + test_comm(masterfd, slavefd); - if (close(slavefd) < 0) e(26); - if (close(masterfd) < 0) e(27); + if (close(slavefd) < 0) e(0); + if (close(masterfd) < 0) e(0); + } - if (sigaction(SIGHUP, &oact, NULL) < 0) e(28); + if (sigaction(SIGHUP, &oact, NULL) < 0) e(0); } /* @@ -207,41 +265,46 @@ static void test77b(void) { char pname[PATH_MAX], tname[PATH_MAX]; - int masterfd, slavefd, extrafd; + int oldstyle, masterfd, slavefd, extrafd; subtest = 2; - /* Get master and slave device names for a free pseudo terminal. */ - get_names(pname, tname); + /* Obtain a pseudo terminal. */ + oldstyle = get_pty(&masterfd, pname, tname); - /* It must not be possible to open the master multiple times. */ - if ((masterfd = open(pname, O_RDWR | O_NOCTTY)) < 0) e(1); - if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(2); + if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(0); + + /* + * It must not be possible to open the master multiple times. Doing so + * is possible only if we have a named master, i.e., an old-style PTY. + */ + test_comm(masterfd, slavefd); + + if (oldstyle) { + if ((extrafd = open(pname, O_RDWR | O_NOCTTY)) >= 0) e(0); + if (errno != EIO) e(0); + } test_comm(masterfd, slavefd); - if ((extrafd = open(pname, O_RDWR | O_NOCTTY)) >= 0) e(3); - if (errno != EIO) e(4); - - test_comm(masterfd, slavefd); - - if (close(slavefd) < 0) e(5); - if (close(masterfd) < 0) e(6); + if (close(slavefd) < 0) e(0); + if (close(masterfd) < 0) e(0); /* The slave can be opened multiple times, though. */ - if ((masterfd = open(pname, O_RDWR | O_NOCTTY)) < 0) e(7); - if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(8); + oldstyle = get_pty(&masterfd, pname, tname); + + if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(0); test_comm(masterfd, slavefd); - if ((extrafd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(9); + if ((extrafd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(0); test_comm(masterfd, extrafd); test_comm(masterfd, slavefd); - if (close(slavefd) < 0) e(10); - if (close(extrafd) < 0) e(11); - if (close(masterfd) < 0) e(12); + if (close(slavefd) < 0) e(0); + if (close(extrafd) < 0) e(0); + if (close(masterfd) < 0) e(0); } /* @@ -252,7 +315,7 @@ test77c(void) { struct sigaction act, oact; char pname[PATH_MAX], tname[PATH_MAX]; - int masterfd, slavefd; + int oldstyle, masterfd, slavefd; char c; subtest = 3; @@ -260,63 +323,78 @@ test77c(void) /* We do not want to get SIGHUP signals in this test. */ memset(&act, 0, sizeof(act)); act.sa_handler = SIG_IGN; - if (sigaction(SIGHUP, &act, &oact) < 0) e(1); + if (sigaction(SIGHUP, &act, &oact) < 0) e(0); - /* Get master and slave device names for a free pseudo terminal. */ - get_names(pname, tname); + /* Obtain a pseudo terminal. */ + oldstyle = get_pty(&masterfd, pname, tname); - if ((masterfd = open(pname, O_RDWR | O_NOCTTY)) < 0) e(2); + /* + * For old-style pseudo terminals, we have just opened and closed the + * slave end, which alters the behavior we are testing below. Close + * and reopen the master to start fresh. + */ + if (oldstyle) { + if (close(masterfd) < 0) e(0); + + if ((masterfd = open(pname, O_RDWR | O_NOCTTY)) < 0) e(0); + } /* Writes to the master should be buffered until there is a slave. */ c = 'E'; - if (write(masterfd, &c, sizeof(c)) != sizeof(c)) e(3); + if (write(masterfd, &c, sizeof(c)) != sizeof(c)) e(0); - if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(4); + if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(0); make_raw(slavefd); - if (read(slavefd, &c, sizeof(c)) != sizeof(c)) e(5); - if (c != 'E') e(6); + if (read(slavefd, &c, sizeof(c)) != sizeof(c)) e(0); + if (c != 'E') e(0); /* Discard the echo on the master. */ - if (tcflush(slavefd, TCOFLUSH) != 0) e(7); + if (tcflush(slavefd, TCOFLUSH) != 0) e(0); test_comm(masterfd, slavefd); - if (close(slavefd) < 0) e(8); + if (close(slavefd) < 0) e(0); /* Writes to the master after the slave has been closed should fail. */ - if (write(masterfd, &c, sizeof(c)) >= 0) e(9); - if (errno != EIO) e(10); + if (write(masterfd, &c, sizeof(c)) >= 0) e(0); + if (errno != EIO) e(0); - if (close(masterfd) < 0) e(11); + if (oldstyle) + if (close(masterfd) < 0) e(0); - /* Writes to the slave should be buffered until there is a master. */ - if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(12); + /* + * Writes to the slave should be buffered until there is a master. + * This applies to old-style PTYs only. + */ + if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(0); - make_raw(slavefd); + if (oldstyle) { + make_raw(slavefd); - c = 'F'; - if (write(slavefd, &c, sizeof(c)) != sizeof(c)) e(13); + c = 'F'; + if (write(slavefd, &c, sizeof(c)) != sizeof(c)) e(0); - if ((masterfd = open(pname, O_RDWR | O_NOCTTY)) < 0) e(14); + if ((masterfd = open(pname, O_RDWR | O_NOCTTY)) < 0) e(0); - if (read(masterfd, &c, sizeof(c)) != sizeof(c)) e(15); - if (c != 'F') e(16); + if (read(masterfd, &c, sizeof(c)) != sizeof(c)) e(0); + if (c != 'F') e(0); + } test_comm(masterfd, slavefd); - if (close(masterfd) < 0) e(17); + if (close(masterfd) < 0) e(0); - if (write(slavefd, &c, sizeof(c)) >= 0) e(18); - if (errno != EIO) e(19); + if (write(slavefd, &c, sizeof(c)) >= 0) e(0); + if (errno != EIO) e(0); /* Reads from the slave should return EOF if the master is gone. */ - if (read(slavefd, &c, sizeof(c)) != 0) e(20); + if (read(slavefd, &c, sizeof(c)) != 0) e(0); - if (close(slavefd) < 0) e(21); + if (close(slavefd) < 0) e(0); - if (sigaction(SIGHUP, &oact, NULL) < 0) e(22); + if (sigaction(SIGHUP, &oact, NULL) < 0) e(0); } /* @@ -346,13 +424,11 @@ test77d(void) subtest = 4; - /* Get master and slave device names for a free pseudo terminal. */ - get_names(pname, tname); - /* Make ourselves process group leader if we aren't already. */ - (void) setsid(); + (void)setsid(); - if ((masterfd = open(pname, O_RDWR | O_NOCTTY)) < 0) e(1); + /* Obtain a pseudo terminal. */ + (void)get_pty(&masterfd, NULL, tname); /* * Opening the slave with O_NOCTTY should not change its controlling @@ -360,25 +436,27 @@ test77d(void) */ switch (fork()) { case 0: - if (setsid() < 0) e(2); + if (close(masterfd) < 0) e(0); - if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(3); + if (setsid() < 0) e(0); - if (open("/dev/tty", O_RDWR) >= 0) e(4); - if (errno != ENXIO) e(5); + if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(0); + + if (open("/dev/tty", O_RDWR) >= 0) e(0); + if (errno != ENXIO) e(0); exit(errct); case -1: - e(6); + e(0); default: break; } - if (waitchild() < 0) e(7); + if (waitchild() < 0) e(0); - if (close(masterfd) < 0) e(8); + if (close(masterfd) < 0) e(0); - if ((masterfd = open(pname, O_RDWR | O_NOCTTY)) < 0) e(9); + (void)get_pty(&masterfd, pname, tname); /* * Opening the slave without O_NOCTTY should change its controlling @@ -386,22 +464,24 @@ test77d(void) */ switch (fork()) { case 0: - if (setsid() < 0) e(10); + if (close(masterfd) < 0) e(0); - if ((slavefd = open(tname, O_RDWR)) < 0) e(11); + if (setsid() < 0) e(0); - if (open("/dev/tty", O_RDWR) < 0) e(12); + if ((slavefd = open(tname, O_RDWR)) < 0) e(0); + + if (open("/dev/tty", O_RDWR) < 0) e(0); exit(errct); case -1: - e(13); + e(0); default: break; } - if (waitchild() < 0) e(14); + if (waitchild() < 0) e(0); - if (close(masterfd) < 0) e(15); + if (close(masterfd) < 0) e(0); } /* @@ -414,72 +494,70 @@ test77e(void) { struct sigaction act, hup_oact, usr_oact; sigset_t set, oset; - char pname[PATH_MAX], tname[PATH_MAX]; + char tname[PATH_MAX]; int masterfd, slavefd; subtest = 5; - /* Get master and slave device names for a free pseudo terminal. */ - get_names(pname, tname); + memset(&act, 0, sizeof(act)); + act.sa_handler = signal_handler; + if (sigaction(SIGHUP, &act, &hup_oact) < 0) e(0); memset(&act, 0, sizeof(act)); act.sa_handler = signal_handler; - if (sigaction(SIGHUP, &act, &hup_oact) < 0) e(1); - - memset(&act, 0, sizeof(act)); - act.sa_handler = signal_handler; - if (sigaction(SIGUSR1, &act, &usr_oact) < 0) e(2); + if (sigaction(SIGUSR1, &act, &usr_oact) < 0) e(0); sigemptyset(&set); sigaddset(&set, SIGHUP); sigaddset(&set, SIGUSR1); - if (sigprocmask(SIG_BLOCK, &set, &oset) < 0) e(3); + if (sigprocmask(SIG_BLOCK, &set, &oset) < 0) e(0); sighups = 0; /* Make ourselves process group leader if we aren't already. */ - (void) setsid(); + (void)setsid(); - if ((masterfd = open(pname, O_RDWR | O_NOCTTY)) < 0) e(4); + /* Obtain a pseudo terminal. */ + (void)get_pty(&masterfd, NULL, tname); switch (fork()) { case 0: - if (close(masterfd) < 0) e(5); + if (close(masterfd) < 0) e(0); /* Become session leader. */ - if (setsid() < 0) e(6); + if (setsid() < 0) e(0); - if ((slavefd = open(tname, O_RDWR)) < 0) e(7); + if ((slavefd = open(tname, O_RDWR)) < 0) e(0); /* Tell the parent we are ready. */ kill(getppid(), SIGUSR1); /* We should now get a SIGHUP. */ set = oset; - if (sigsuspend(&set) >= 0) e(8); + if (sigsuspend(&set) >= 0) e(0); - if (sighups != 1) e(9); + if (sighups != 1) e(0); exit(errct); case -1: - e(10); + e(0); default: break; } /* Wait for SIGUSR1 from the child. */ set = oset; - if (sigsuspend(&set) >= 0) e(11); + if (sigsuspend(&set) >= 0) e(0); /* Closing the master should now raise a SIGHUP signal in the child. */ - if (close(masterfd) < 0) e(12); + if (close(masterfd) < 0) e(0); - if (waitchild() < 0) e(13); + if (waitchild() < 0) e(0); - if (sigprocmask(SIG_SETMASK, &oset, NULL) < 0) e(14); + if (sigprocmask(SIG_SETMASK, &oset, NULL) < 0) e(0); - if (sigaction(SIGHUP, &hup_oact, NULL) < 0) e(15); - if (sigaction(SIGUSR1, &usr_oact, NULL) < 0) e(16); + if (sigaction(SIGHUP, &hup_oact, NULL) < 0) e(0); + if (sigaction(SIGUSR1, &usr_oact, NULL) < 0) e(0); } /* @@ -490,7 +568,7 @@ static void test77f(void) { struct sigaction act, oact; - char c, pname[PATH_MAX], tname[PATH_MAX]; + char c, tname[PATH_MAX]; struct timeval tv; fd_set fd_set; int fd, maxfd, masterfd, slavefd; @@ -500,22 +578,20 @@ test77f(void) /* We do not want to get SIGHUP signals in this test. */ memset(&act, 0, sizeof(act)); act.sa_handler = SIG_IGN; - if (sigaction(SIGHUP, &act, &oact) < 0) e(1); + if (sigaction(SIGHUP, &act, &oact) < 0) e(0); - /* Get master and slave device names for a free pseudo terminal. */ - get_names(pname, tname); - - if ((masterfd = open(pname, O_RDWR | O_NOCTTY)) < 0) e(2); + /* Obtain a pseudo terminal. */ + (void)get_pty(&masterfd, NULL, tname); switch (fork()) { case 0: - if (setsid() < 0) e(3); + if (close(masterfd) < 0) e(0); - close(masterfd); + if (setsid() < 0) e(0); - if ((slavefd = open(tname, O_RDWR)) < 0) e(4); + if ((slavefd = open(tname, O_RDWR)) < 0) e(0); - if ((fd = open("/dev/tty", O_RDWR)) < 0) e(5); + if ((fd = open("/dev/tty", O_RDWR)) < 0) e(0); make_raw(fd); @@ -525,21 +601,21 @@ test77f(void) tv.tv_sec = 0; tv.tv_usec = 0; - if (select(fd + 1, &fd_set, NULL, NULL, &tv) != 0) e(6); - if (FD_ISSET(fd, &fd_set)) e(7); + if (select(fd + 1, &fd_set, NULL, NULL, &tv) != 0) e(0); + if (FD_ISSET(fd, &fd_set)) e(0); FD_SET(fd, &fd_set); tv.tv_sec = 0; tv.tv_usec = 10000; - if (select(fd + 1, &fd_set, NULL, NULL, &tv) != 0) e(8); - if (FD_ISSET(fd, &fd_set)) e(9); + if (select(fd + 1, &fd_set, NULL, NULL, &tv) != 0) e(0); + if (FD_ISSET(fd, &fd_set)) e(0); /* It will be ready for writing, though. */ FD_SET(fd, &fd_set); - if (select(fd + 1, NULL, &fd_set, NULL, NULL) != 1) e(10); - if (!FD_ISSET(fd, &fd_set)) e(11); + if (select(fd + 1, NULL, &fd_set, NULL, NULL) != 1) e(0); + if (!FD_ISSET(fd, &fd_set)) e(0); /* Test mixing file descriptors to the same terminal. */ FD_ZERO(&fd_set); @@ -549,76 +625,76 @@ test77f(void) tv.tv_usec = 10000; maxfd = fd > slavefd ? fd : slavefd; - if (select(maxfd + 1, &fd_set, NULL, NULL, &tv) != 0) e(12); - if (FD_ISSET(fd, &fd_set)) e(13); - if (FD_ISSET(slavefd, &fd_set)) e(14); + if (select(maxfd + 1, &fd_set, NULL, NULL, &tv) != 0) e(0); + if (FD_ISSET(fd, &fd_set)) e(0); + if (FD_ISSET(slavefd, &fd_set)) e(0); /* The delayed echo on the master must wake up our select. */ c = 'A'; - if (write(slavefd, &c, sizeof(c)) != sizeof(c)) e(15); + if (write(slavefd, &c, sizeof(c)) != sizeof(c)) e(0); FD_ZERO(&fd_set); FD_SET(fd, &fd_set); - if (select(fd + 1, &fd_set, NULL, NULL, NULL) != 1) e(16); - if (!FD_ISSET(fd, &fd_set)) e(17); + if (select(fd + 1, &fd_set, NULL, NULL, NULL) != 1) e(0); + if (!FD_ISSET(fd, &fd_set)) e(0); /* Select must now still flag readiness for reading. */ tv.tv_sec = 0; tv.tv_usec = 0; - if (select(fd + 1, &fd_set, NULL, NULL, &tv) != 1) e(18); - if (!FD_ISSET(fd, &fd_set)) e(19); + if (select(fd + 1, &fd_set, NULL, NULL, &tv) != 1) e(0); + if (!FD_ISSET(fd, &fd_set)) e(0); /* That is, until we read the byte. */ - if (read(slavefd, &c, sizeof(c)) != sizeof(c)) e(20); - if (c != 'B') e(21); + if (read(slavefd, &c, sizeof(c)) != sizeof(c)) e(0); + if (c != 'B') e(0); - if (select(fd + 1, &fd_set, NULL, NULL, &tv) != 0) e(22); - if (FD_ISSET(fd, &fd_set)) e(23); + if (select(fd + 1, &fd_set, NULL, NULL, &tv) != 0) e(0); + if (FD_ISSET(fd, &fd_set)) e(0); /* Ask the parent to close the master. */ c = 'C'; - if (write(slavefd, &c, sizeof(c)) != sizeof(c)) e(24); + if (write(slavefd, &c, sizeof(c)) != sizeof(c)) e(0); FD_SET(fd, &fd_set); /* The closure must cause an EOF condition on the slave. */ - if (select(fd + 1, &fd_set, NULL, NULL, NULL) != 1) e(25); - if (!FD_ISSET(fd, &fd_set)) e(26); + if (select(fd + 1, &fd_set, NULL, NULL, NULL) != 1) e(0); + if (!FD_ISSET(fd, &fd_set)) e(0); - if (select(fd + 1, &fd_set, NULL, NULL, NULL) != 1) e(27); - if (!FD_ISSET(fd, &fd_set)) e(28); + if (select(fd + 1, &fd_set, NULL, NULL, NULL) != 1) e(0); + if (!FD_ISSET(fd, &fd_set)) e(0); - if (read(slavefd, &c, sizeof(c)) != 0) e(29); + if (read(slavefd, &c, sizeof(c)) != 0) e(0); exit(errct); case -1: - e(30); + e(0); default: /* Wait for the child to write something to the slave. */ FD_ZERO(&fd_set); FD_SET(masterfd, &fd_set); if (select(masterfd + 1, &fd_set, NULL, NULL, NULL) != 1) - e(31); - if (!FD_ISSET(masterfd, &fd_set)) e(32); + e(0); + if (!FD_ISSET(masterfd, &fd_set)) e(0); - if (read(masterfd, &c, sizeof(c)) != sizeof(c)) e(33); - if (c != 'A') e(34); + if (read(masterfd, &c, sizeof(c)) != sizeof(c)) e(0); + if (c != 'A') e(0); /* Write a reply once the child is blocked in its select. */ tv.tv_sec = 1; tv.tv_usec = 0; if (select(masterfd + 1, &fd_set, NULL, NULL, &tv) != 0) - e(35); + e(0); c = 'B'; - if (write(masterfd, &c, sizeof(c)) != sizeof(c)) e(36); + if (write(masterfd, &c, sizeof(c)) != sizeof(c)) e(0); /* Wait for the child to request closing the master. */ - if (read(masterfd, &c, sizeof(c)) != sizeof(c)) e(37); - if (c != 'C') e(38); + if (read(masterfd, &c, sizeof(c)) != sizeof(c)) e(0); + if (c != 'C') e(0); /* Close the master once the child is blocked in its select. */ sleep(1); @@ -628,9 +704,421 @@ test77f(void) break; } - if (waitchild() < 0) e(39); + if (waitchild() < 0) e(0); - if (sigaction(SIGHUP, &oact, NULL) < 0) e(28); + if (sigaction(SIGHUP, &oact, NULL) < 0) e(0); +} + +/* + * See if the directory contents of /dev/pts are as we expect. We have to keep + * in mind that other programs may have pseudo terminals open while we are + * running, although we assume that those programs do not open or close PTYs + * while we are running. + */ +static void +test_getdents(int nindex, int array[3], int present[3]) +{ + struct group *group; + DIR *dirp; + struct dirent *dp; + struct stat buf; + char path[PATH_MAX], *endp; + gid_t tty_gid; + int i, n, seen_dot, seen_dotdot, seen_index[3], *seen; + + seen_dot = seen_dotdot = 0; + for (i = 0; i < nindex; i++) + seen_index[i] = 0; + + if ((group = getgrnam("tty")) == NULL) e(0); + tty_gid = group->gr_gid; + + if ((dirp = opendir(_PATH_DEV_PTS)) == NULL) e(0); + + while ((dp = readdir(dirp)) != NULL) { + snprintf(path, sizeof(path), _PATH_DEV_PTS "%s", dp->d_name); + if (stat(path, &buf) < 0) e(0); + + if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) { + seen = + (dp->d_name[1] == '.') ? &seen_dot : &seen_dotdot; + if (*seen) e(0); + *seen = 1; + + /* Check basic dirent and stat fields. */ + if (dp->d_type != DT_DIR) e(0); + if (dp->d_name[1] == '\0' && + buf.st_ino != dp->d_fileno) e(0); + if (!S_ISDIR(buf.st_mode)) e(0); + if (buf.st_nlink < 2) e(0); + } else { + /* The file name must be a number. */ + errno = 0; + n = strtol(dp->d_name, &endp, 10); + if (errno != 0) e(0); + if (dp->d_name[0] == '\0' || *endp != '\0') e(0); + if (n < 0) e(0); + + /* Check basic dirent and stat fields. */ + if (dp->d_type != DT_CHR) e(0); + if (buf.st_ino != dp->d_fileno) e(0); + if (!S_ISCHR(buf.st_mode)) e(0); + if (buf.st_nlink != 1) e(0); + if (buf.st_size != 0) e(0); + if (buf.st_rdev == 0) e(0); + + /* Is this one of the PTYs we created? */ + for (i = 0; i < nindex; i++) { + if (array[i] == n) { + if (seen_index[i]) e(0); + seen_index[i] = 1; + + break; + } + } + + /* If so, perform some extra tests. */ + if (i < nindex) { + if ((buf.st_mode & ALLPERMS) != 0620) e(0); + if (buf.st_uid != getuid()) e(0); + if (buf.st_gid != tty_gid) e(0); + } + } + } + + if (closedir(dirp) < 0) e(0); + + if (!seen_dot) e(0); + if (!seen_dotdot) e(0); + for (i = 0; i < nindex; i++) + if (seen_index[i] != present[i]) e(0); +} + +/* + * Obtain a Unix98 PTY. Return an open file descriptor for the master side, + * and store the name of the slave side in 'tptr'. + */ +static int +get_unix98_pty(char ** tptr) +{ + int masterfd; + + if ((masterfd = posix_openpt(O_RDWR | O_NOCTTY)) < 0) e(0); + + if (grantpt(masterfd) < 0) e(0); + + /* This call is a no-op on MINIX3. */ + if (unlockpt(masterfd) < 0) e(0); + + if ((*tptr = ptsname(masterfd)) == NULL) e(0); + + return masterfd; +} + +/* + * Test for Unix98 PTY support and PTYFS. + */ +static void +test77g(void) +{ + char *tname; + struct stat buf; + size_t len; + int i, masterfd, slavefd, fd[3], array[3], present[3]; + + subtest = 7; + + /* + * Test basic operation, and verify that the slave node disappears + * after both sides of the pseudo terminal have been closed. We check + * different combinations of open master and slave ends (with 'i'): + * 0) opening and closing the master only, 1) closing a slave before + * the master, and 2) closing the slave after the master. + */ + for (i = 0; i <= 2; i++) { + masterfd = get_unix98_pty(&tname); + + if (access(tname, R_OK | W_OK) < 0) e(0); + + if (i > 0) { + if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) + e(0); + + if (access(tname, R_OK | W_OK) < 0) e(0); + + if (i > 1) { + if (close(masterfd) < 0) e(0); + + masterfd = slavefd; /* ugly but saving code */ + } else + if (close(slavefd) < 0) e(0); + } + + if (access(tname, R_OK | W_OK) < 0) e(0); + + if (close(masterfd) < 0) e(0); + + if (access(tname, R_OK | W_OK) == 0) e(0); + } + + /* + * Test whether we can open multiple pseudo terminals. We need to be + * able to open three PTYs. Verify that they are properly listed in + * the /dev/pts directory contents, and have proper attributes set. + */ + test_getdents(0, NULL, NULL); + + for (i = 0; i < 3; i++) { + fd[i] = get_unix98_pty(&tname); + + /* Figure out the slave index number. */ + len = strlen(_PATH_DEV_PTS); + if (strncmp(tname, _PATH_DEV_PTS, strlen(_PATH_DEV_PTS))) e(0); + array[i] = atoi(&tname[len]); + present[i] = 1; + } + + test_getdents(3, array, present); + + if (close(fd[0]) < 0) e(0); + present[0] = 0; + + test_getdents(3, array, present); + + if (close(fd[2]) < 0) e(0); + present[2] = 0; + + test_getdents(3, array, present); + + if (close(fd[1]) < 0) e(0); + present[1] = 0; + + test_getdents(3, array, present); + + /* + * Test chmod(2) on a slave node, and multiple calls to grantpt(3). + * The first grantpt(3) call should create the slave node (we currently + * can not test this: the slave node may be created earlier as well, + * but we do not know its name), whereas subsequent grantpt(3) calls + * should reset its mode, uid, and gid. Testing the latter two and + * chown(2) on the slave node requires root, so we skip that part. + * + * Finally, NetBSD revokes access to existing slave file descriptors + * upon a call to grantpt(3). This is not a POSIX requirement, but + * NetBSD needs this for security reasons because it already creates + * the slave node when the master is opened (and it does not lock the + * slave until a call to unlockpt(3)). MINIX3 does not implement + * revocation this way, because the slave node is created only upon the + * call to grantpt(3), thus leaving no insecure window for the slave + * side between posix_openpt(3) and grantpt(3). While this behavior + * may be changed later, we test for the lack of revocation here now. + */ + masterfd = get_unix98_pty(&tname); + + if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) e(0); + + if (stat(tname, &buf) != 0) e(0); + if (buf.st_mode != (S_IFCHR | 0620)) e(0); + + if (chmod(tname, S_IFCHR | 0630) != 0) e(0); + + if (stat(tname, &buf) != 0) e(0); + if (buf.st_mode != (S_IFCHR | 0630)) e(0); + + if (grantpt(masterfd) != 0) e(0); + + if (stat(tname, &buf) != 0) e(0); + if (buf.st_mode != (S_IFCHR | 0620)) e(0); + + test_comm(masterfd, slavefd); + + if (close(slavefd) < 0) e(0); + if (close(masterfd) < 0) e(0); + + test_getdents(0, NULL, NULL); +} + +/* + * Check that the given PTY index, which is in use for an old-style PTY, is not + * allocated through Unix98 PTY allocation. This test is not foolproof, but it + * does the job well enough. + */ +static void +test_overlap(int m) +{ + char *tname; + size_t len; + int i, n, fd[MIN_PTYS]; + + for (i = 0; i < MIN_PTYS; i++) { + if ((fd[i] = posix_openpt(O_RDWR | O_NOCTTY)) < 0) + break; /* out of PTYs */ + if (grantpt(fd[i]) < 0) e(0); + if (unlockpt(fd[i]) < 0) e(0); + if ((tname = ptsname(fd[i])) == NULL) e(0); + + len = strlen(_PATH_DEV_PTS); + if (strncmp(tname, _PATH_DEV_PTS, strlen(_PATH_DEV_PTS))) e(0); + n = atoi(&tname[len]); + if (n < 0 || n > 9) e(0); + + if (m == n) e(0); + } + + for (i--; i >= 0; i--) + if (close(fd[i]) < 0) e(0); +} + +/* + * Test for mixing access to old-style and Unix98 PTYs. Since the PTY service + * internally shares the set of pseudo terminals between the two types, it has + * to implement checks to prevent that a PTY opened as one type is also + * accessed through the other type. We test some of those checks here. + */ +static void +test77h(void) +{ + char *tname, ptest[PATH_MAX], ttest[PATH_MAX]; + struct sigaction act, oact; + size_t len; + int i, n, masterfd, slavefd; + + subtest = 8; + + /* We do not want to get SIGHUP signals in this test. */ + memset(&act, 0, sizeof(act)); + act.sa_handler = SIG_IGN; + if (sigaction(SIGHUP, &act, &oact) < 0) e(0); + + /* + * Check that Unix98 PTYs cannot be accessed through old-style device + * nodes. We check different combinations of open master and + * slave ends for the Unix98 side (with 'i'): 0) opening and closing + * the master only, 1) closing a slave before the master, and 2) + * closing the slave after the master. + * + * This test relies on the implementation aspect that /dev/ttypN and + * /dev/pts/N (with N in the range 0..9) map to the same PTY. It also + * relies on lack of concurrent PTY allocation outside the test. + */ + for (i = 0; i <= 2; i++) { + /* Open a Unix98 PTY and get the slave name. */ + masterfd = get_unix98_pty(&tname); + + /* Figure out the slave index number. */ + len = strlen(_PATH_DEV_PTS); + if (strncmp(tname, _PATH_DEV_PTS, strlen(_PATH_DEV_PTS))) e(0); + n = atoi(&tname[len]); + if (n < 0 || n > 9) e(0); + + /* Use this index number to create old-style device names. */ + snprintf(ptest, sizeof(ptest), _PATH_DEV "ptyp%u", n); + snprintf(ttest, sizeof(ttest), _PATH_DEV "ttyp%u", n); + + /* + * Now make sure that opening the old-style master and slave + * fails as long as either side of the Unix98 PTY is open. + */ + if (open(ptest, O_RDWR | O_NOCTTY) >= 0) e(0); + if (errno != EACCES && errno != EIO) e(0); + if (open(ttest, O_RDWR | O_NOCTTY) >= 0) e(0); + if (errno != EACCES && errno != EIO) e(0); + + if (i > 0) { + if ((slavefd = open(tname, O_RDWR | O_NOCTTY)) < 0) + e(0); + + if (open(ptest, O_RDWR | O_NOCTTY) >= 0) e(0); + if (errno != EACCES && errno != EIO) e(0); + if (open(ttest, O_RDWR | O_NOCTTY) >= 0) e(0); + if (errno != EACCES && errno != EIO) e(0); + + if (close(slavefd) < 0) e(0); + + if (i > 1) { + if (open(ptest, O_RDWR | O_NOCTTY) >= 0) e(0); + if (errno != EACCES && errno != EIO) e(0); + if (open(ttest, O_RDWR | O_NOCTTY) >= 0) e(0); + if (errno != EACCES && errno != EIO) e(0); + + if ((slavefd = + open(tname, O_RDWR | O_NOCTTY)) < 0) e(0); + + if (open(ptest, O_RDWR | O_NOCTTY) >= 0) e(0); + if (errno != EACCES && errno != EIO) e(0); + if (open(ttest, O_RDWR | O_NOCTTY) >= 0) e(0); + if (errno != EACCES && errno != EIO) e(0); + + if (close(masterfd) < 0) e(0); + + masterfd = slavefd; /* ugly but saving code */ + } + + if (open(ptest, O_RDWR | O_NOCTTY) >= 0) e(0); + if (errno != EACCES && errno != EIO) e(0); + if (open(ttest, O_RDWR | O_NOCTTY) >= 0) e(0); + if (errno != EACCES && errno != EIO) e(0); + } + + if (close(masterfd) < 0) e(0); + + /* + * Once both Unix98 sides are closed, the pseudo terminal can + * be reused. Thus, opening the old-style master should now + * succeed. However, it is possible that we do not have + * permission to open the master at all. + */ + if ((masterfd = open(ptest, O_RDWR | O_NOCTTY)) < 0 && + errno != EACCES) e(0); + + if (masterfd >= 0 && close(masterfd) < 0) e(0); + } + + /* + * The reverse test, which would check that old-style PTYs cannot be + * accessed through Unix98 device nodes, is impossible to perform + * properly without root privileges, as we would have to create device + * nodes manually with mknod(2). All we can do here is ensure that if + * an old-style PTY is opened, it will not also be allocated as a + * Unix98 PTY. We do a rather basic check, but only if we can open an + * old-style master at all. We check two closing orders (with 'i'): + * 0) the slave first, 1) the master first. Here, we make the hard + * assumption that the system supports at least four pseudo terminals, + * of which at least one is currently free. + */ + for (i = 0; i <= 1; i++) { + for (n = 0; n < MIN_PTYS; n++) { + snprintf(ptest, sizeof(ptest), _PATH_DEV "ptyp%u", n); + + if ((masterfd = open(ptest, O_RDWR | O_NOCTTY)) >= 0) + break; + } + + if (n >= MIN_PTYS) + break; + + test_overlap(n); + + snprintf(ttest, sizeof(ttest), _PATH_DEV "ttyp%u", n); + + /* We can do part of the test only if we can open the slave. */ + if ((slavefd = open(ttest, O_RDWR | O_NOCTTY)) >= 0) { + test_overlap(n); + + if (i > 0) { + if (close(masterfd) < 0) e(0); + + masterfd = slavefd; /* again, ugly */ + } else + if (close(slavefd) < 0) e(0); + + test_overlap(n); + } + + if (close(masterfd) < 0) e(0); + } + + if (sigaction(SIGHUP, &oact, NULL) < 0) e(0); } int @@ -652,6 +1140,8 @@ main(int argc, char **argv) if (m & 0x08) test77d(); if (m & 0x10) test77e(); if (m & 0x20) test77f(); + if (m & 0x40) test77g(); + if (m & 0x80) test77h(); } quit(); diff --git a/minix/usr.bin/trace/ioctl/char.c b/minix/usr.bin/trace/ioctl/char.c index a90a56151..a8ac00d0f 100644 --- a/minix/usr.bin/trace/ioctl/char.c +++ b/minix/usr.bin/trace/ioctl/char.c @@ -273,6 +273,7 @@ char_ioctl_arg(struct trace_proc * proc, unsigned long req, void * ptr, struct volume_level *level; struct inout_ctrl *inout; struct termios *tc; + struct ptmget *pm; struct winsize *ws; struct kio_bell *bell; struct kio_leds *leds; @@ -282,7 +283,6 @@ char_ioctl_arg(struct trace_proc * proc, unsigned long req, void * ptr, struct pciio_map *pci_iomap; struct pciio_acl *pci_acl; - switch (req) { case MINIX_I2C_IOCTL_EXEC: if ((iie = (minix_i2c_ioctl_exec_t *)ptr) == NULL) @@ -452,6 +452,14 @@ char_ioctl_arg(struct trace_proc * proc, unsigned long req, void * ptr, put_value(proc, NULL, "%d", *(int *)ptr); return IF_ALL; + case TIOCPTSNAME: + if ((pm = (struct ptmget *)ptr) == NULL) + return IF_IN; + + put_buf(proc, "sn", PF_LOCADDR | PF_STRING, (vir_bytes)pm->sn, + sizeof(pm->sn)); + return IF_ALL; + case TIOCSTI: if (ptr == NULL) return dir; diff --git a/releasetools/arm_sdimage.sh b/releasetools/arm_sdimage.sh index ae771d9f3..db1ae53fb 100755 --- a/releasetools/arm_sdimage.sh +++ b/releasetools/arm_sdimage.sh @@ -174,9 +174,10 @@ fi # setup phase on x86 # cat >${FSTAB} <${FSTAB} <