Add PTYFS, Unix98 pseudo terminal support
This patch adds support for Unix98 pseudo terminals, that is, posix_openpt(3), grantpt(3), unlockpt(3), /dev/ptmx, and /dev/pts/. The latter is implemented with a new pseudo file system, PTYFS. In effect, this patch adds secure support for unprivileged pseudo terminal allocation, allowing programs such as tmux(1) to be used by non-root users as well. Test77 has been extended with new tests, and no longer needs to run as root. The new functionality is optional. To revert to the old behavior, remove the "ptyfs" entry from /etc/fstab. Technical nodes: o The reason for not implementing the NetBSD /dev/ptm approach is that implementing the corresponding ioctl (TIOCPTMGET) would require adding a number of extremely hairy exceptions to VFS, including the PTY driver having to create new file descriptors for its own device nodes. o PTYFS is required for Unix98 PTYs in order to avoid that the PTY driver has to be aware of old-style PTY naming schemes and even has to call chmod(2) on a disk-backed file system. PTY cannot be its own PTYFS since a character driver may currently not also be a file system. However, PTYFS may be subsumed into a DEVFS in the future. o The Unix98 PTY behavior differs somewhat from NetBSD's, in that slave nodes are created on ptyfs only upon the first call to grantpt(3). This approach obviates the need to revoke access as part of the grantpt(3) call. o Shutting down PTY may leave slave nodes on PTYFS, but once PTY is restarted, these leftover slave nodes will be removed before they create a security risk. Unmounting PTYFS will make existing PTY slaves permanently unavailable, and absence of PTYFS will block allocation of new Unix98 PTYs until PTYFS is (re)mounted. Change-Id: I822b43ba32707c8815fd0f7d5bb7a438f51421c1
This commit is contained in:
parent
0a2a08739e
commit
da21d85025
30 changed files with 1725 additions and 266 deletions
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
./boot/minix
|
||||
./boot/minix_default
|
||||
./dev
|
||||
./dev/pts
|
||||
./etc
|
||||
./etc/X11
|
||||
./etc/X11/fs
|
||||
|
|
|
@ -739,6 +739,13 @@ service pty
|
|||
;
|
||||
};
|
||||
|
||||
service ptyfs
|
||||
{
|
||||
ipc
|
||||
SYSTEM pm vfs rs pty ds vm
|
||||
;
|
||||
};
|
||||
|
||||
service edfictl
|
||||
{
|
||||
ipc ALL;
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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 \
|
||||
|
|
|
@ -42,6 +42,9 @@ __RCSID("$NetBSD: pty.c,v 1.31 2009/02/20 16:44:06 christos Exp $");
|
|||
#include <sys/ioctl.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#ifdef __minix
|
||||
#include <stdlib.h>
|
||||
#endif /* __minix */
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
|
@ -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)
|
||||
|
|
|
@ -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`
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 <minix/drivers.h>
|
||||
#include <paths.h>
|
||||
#include <termios.h>
|
||||
#include <assert.h>
|
||||
#include <sys/termios.h>
|
||||
#include <signal.h>
|
||||
#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;
|
||||
|
|
112
minix/drivers/tty/pty/ptyfs.c
Normal file
112
minix/drivers/tty/pty/ptyfs.c
Normal file
|
@ -0,0 +1,112 @@
|
|||
/* ptyfs.c - communication to PTYFS */
|
||||
|
||||
#include <minix/driver.h>
|
||||
#include <minix/ds.h>
|
||||
|
||||
#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;
|
||||
}
|
9
minix/drivers/tty/pty/ptyfs.h
Normal file
9
minix/drivers/tty/pty/ptyfs.h
Normal file
|
@ -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 */
|
|
@ -2,6 +2,7 @@
|
|||
#include <assert.h>
|
||||
#include <minix/drivers.h>
|
||||
#include <minix/driver.h>
|
||||
#include <minix/optset.h>
|
||||
#include <termios.h>
|
||||
#include <sys/ttycom.h>
|
||||
#include <sys/ttydefaults.h>
|
||||
|
@ -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;
|
||||
|
|
|
@ -4,8 +4,10 @@
|
|||
#include <minix/timers.h>
|
||||
|
||||
/* 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);
|
||||
|
|
|
@ -7,6 +7,7 @@ SUBDIR+= pfs
|
|||
SUBDIR+= ext2
|
||||
SUBDIR+= isofs
|
||||
SUBDIR+= procfs
|
||||
SUBDIR+= ptyfs
|
||||
|
||||
. if ${MACHINE_ARCH} == "i386"
|
||||
SUBDIR+= hgfs
|
||||
|
|
|
@ -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" },
|
||||
|
|
10
minix/fs/ptyfs/Makefile
Normal file
10
minix/fs/ptyfs/Makefile
Normal file
|
@ -0,0 +1,10 @@
|
|||
# Makefile for PTYFS server
|
||||
.include <bsd.own.mk>
|
||||
|
||||
PROG= ptyfs
|
||||
SRCS= ptyfs.c node.c
|
||||
|
||||
DPADD+= ${LIBFSDRIVER}
|
||||
LDADD+= -lfsdriver
|
||||
|
||||
.include <minix.service.mk>
|
84
minix/fs/ptyfs/node.c
Normal file
84
minix/fs/ptyfs/node.c
Normal file
|
@ -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 <minix/drivers.h>
|
||||
|
||||
#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;
|
||||
}
|
20
minix/fs/ptyfs/node.h
Normal file
20
minix/fs/ptyfs/node.h
Normal file
|
@ -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 */
|
434
minix/fs/ptyfs/ptyfs.c
Normal file
434
minix/fs/ptyfs/ptyfs.c
Normal file
|
@ -0,0 +1,434 @@
|
|||
/* PTYFS - file system for Unix98 pseudoterminal slave nodes (/dev/pts) */
|
||||
|
||||
#include <minix/drivers.h>
|
||||
#include <minix/fsdriver.h>
|
||||
#include <minix/vfsif.h>
|
||||
#include <minix/ds.h>
|
||||
#include <sys/dirent.h>
|
||||
#include <assert.h>
|
||||
|
||||
#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;
|
||||
}
|
|
@ -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 *
|
||||
*===========================================================================*/
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
|
@ -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 <stdio.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/wait.h>
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -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;
|
||||
|
|
|
@ -174,9 +174,10 @@ fi
|
|||
# setup phase on x86
|
||||
#
|
||||
cat >${FSTAB} <<END_FSTAB
|
||||
/dev/c0d0p2 /usr mfs rw 0 2
|
||||
/dev/c0d0p3 /home mfs rw 0 2
|
||||
none /sys devman rw,rslabel=devman 0 0
|
||||
/dev/c0d0p2 /usr mfs rw 0 2
|
||||
/dev/c0d0p3 /home mfs rw 0 2
|
||||
none /sys devman rw,rslabel=devman 0 0
|
||||
none /dev/pts ptyfs rw,rslabel=ptyfs 0 0
|
||||
END_FSTAB
|
||||
|
||||
rm -f ${DESTDIR}/SETS.*
|
||||
|
|
|
@ -114,9 +114,10 @@ fi
|
|||
# setup phase on x86
|
||||
#
|
||||
cat >${FSTAB} <<END_FSTAB
|
||||
/dev/c0d0p2 /usr mfs rw 0 2
|
||||
/dev/c0d0p3 /home mfs rw 0 2
|
||||
none /sys devman rw,rslabel=devman 0 0
|
||||
/dev/c0d0p2 /usr mfs rw 0 2
|
||||
/dev/c0d0p3 /home mfs rw 0 2
|
||||
none /sys devman rw,rslabel=devman 0 0
|
||||
none /dev/pts ptyfs rw,rslabel=ptyfs 0 0
|
||||
END_FSTAB
|
||||
|
||||
rm -f ${DESTDIR}/SETS.*
|
||||
|
|
Loading…
Reference in a new issue