diff options
Diffstat (limited to 'term/users.c')
-rw-r--r-- | term/users.c | 1659 |
1 files changed, 1659 insertions, 0 deletions
diff --git a/term/users.c b/term/users.c new file mode 100644 index 00000000..4eb61be8 --- /dev/null +++ b/term/users.c @@ -0,0 +1,1659 @@ +/* + Copyright (C) 1995 Free Software Foundation, Inc. + Written by Michael I. Bushnell, p/BSG. + + This file is part of the GNU Hurd. + + The GNU Hurd is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + The GNU Hurd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ + +#include "term.h" +#include <sys/stat.h> +#include <unistd.h> +#include <sys/types.h> +#include <string.h> +#include <fcntl.h> +#include <hurd/trivfs.h> +#include <cthreads.h> +#include <hurd.h> +#include "ourmsg_U.h" + +#undef ECHO +#undef MDMBUF +#undef TOSTOP +#undef FLUSHO +#undef PENDIN +#undef NOFLSH + +#include "term_S.h" +#include "tioctl_S.h" +#include <sys/ioctl.h> + +#define TTYDEFCHARS +#include <sys/ttydefaults.h> + +/* Count of active opens */ +int nperopens; + +/* io_async requests */ +struct async_req +{ + mach_port_t notify; + struct async_req *next; +}; +struct async_req *async_requests; + +mach_port_t async_icky_id; +mach_port_t async_id; +struct port_info *cttyid; +int foreground_id; + +struct winsize window_size; + +static void call_asyncs (void); + +/* Attach this on the hook of any protid that is a ctty. */ +struct protid_hook +{ + int refcnt; + pid_t pid, pgrp; +}; + +static error_t +open_hook (struct trivfs_control *cntl, + uid_t *uids, u_int nuids, + uid_t *gids, u_int ngids, + int flags) +{ + int cancel = 0; + error_t err; + + mutex_lock (&global_lock); + + if (!(termflags & TTY_OPEN)) + { + bzero (&termstate, sizeof termstate); + + /* This is different from BSD: we don't turn on ISTRIP, + and we use CS8 rather than CS7|PARENB. */ + termstate.c_iflag |= BRKINT | ICRNL | IMAXBEL | IXON | IXANY; + termstate.c_oflag |= OPOST | ONLCR | OXTABS; + termstate.c_lflag |= (ECHO | ICANON | ISIG | IEXTEN + | ECHOE|ECHOKE|ECHOCTL); + termstate.c_cflag |= CREAD | CS8 | HUPCL; + + bcopy (ttydefchars, termstate.c_cc, NCCS); + } + else if (termflags & EXCL_USE) + { + mutex_unlock (&global_lock); + return EBUSY; + } + + /* Wait for carrier to turn on. */ + while (((termflags & NO_CARRIER) && !(termstate.c_cflag & CLOCAL)) + && !(flags & O_NONBLOCK) + && !cancel) + { + err = assert_dtr (); + if (err) + { + mutex_unlock (&global_lock); + return err; + } + cancel = hurd_condition_wait (&carrier_alert, &global_lock); + } + + if ((termflags & NO_CARRIER) && !(termstate.c_cflag & CLOCAL)) + { + mutex_unlock (&global_lock); + return EWOULDBLOCK; + } + if (cancel) + { + mutex_unlock (&global_lock); + return EINTR; + } + + termflags |= TTY_OPEN; + if (!(termstate.c_cflag & CIGNORE)) + set_bits (); + + mutex_unlock (&global_lock); + return 0; +} +error_t (*trivfs_check_open_hook) (struct trivfs_control *, uid_t *, + u_int, uid_t *, u_int, int) + = open_hook; + +static error_t +pi_create_hook (struct trivfs_protid *cred) +{ + mutex_lock (&global_lock); + if (cred->hook) + ((struct protid_hook *)cred->hook)->refcnt++; + mutex_unlock (&global_lock); + + return 0; +} +error_t (*trivfs_protid_create_hook) (struct trivfs_protid *) = pi_create_hook; + +static void +pi_destroy_hook (struct trivfs_protid *cred) +{ + mutex_lock (&global_lock); + if (cred->hook && !--((struct protid_hook *)cred->hook)->refcnt) + free (cred->hook); + mutex_unlock (&global_lock); +} +void (*trivfs_protid_destroy_hook) (struct trivfs_protid *) = pi_destroy_hook; + +static error_t +po_create_hook (struct trivfs_peropen *po) +{ + mutex_lock (&global_lock); + nperopens++; + if (po->openmodes & O_ASYNC) + termflags |= ICKY_ASYNC; + mutex_unlock (&global_lock); + return 0; +} +error_t (*trivfs_peropen_create_hook) (struct trivfs_peropen *) = + po_create_hook; + +static void +po_destroy_hook (struct trivfs_peropen *po) +{ + mutex_lock (&global_lock); + nperopens--; + if (!nperopens) + { + /* Empty queues */ + clear_queue (inputq); + clear_queue (rawq); + drain_output (); + + /* Possibly drop carrier */ + if ((termstate.c_cflag & HUPCL) || (termflags & NO_CARRIER)) + desert_dtr (); + + termflags &= ~TTY_OPEN; + } + mutex_unlock (&global_lock); +} +void (*trivfs_peropen_destroy_hook) (struct trivfs_peropen *) + = po_destroy_hook; + +/* Tell if CRED can do foreground terminal operations */ +static inline int +fg_p (struct trivfs_protid *cred) +{ + struct protid_hook *hook = cred->hook; + + if (!hook || (termflags & NO_OWNER)) + return 1; + + if (hook->pid == foreground_id + || hook->pgrp == -foreground_id) + return 1; + + return 0; +} + +void +trivfs_modify_stat (struct trivfs_protid *cred, struct stat *st) +{ + st->st_blksize = 512; + st->st_fstype = FSTYPE_TERM; + st->st_fsid = getpid (); + st->st_ino = 0; + st->st_mode &= ~S_IFMT; + st->st_mode |= S_IFCHR; +} + +/* Implement term_getctty as described in <hurd/term.defs>. */ +kern_return_t +S_term_getctty (mach_port_t arg, + mach_port_t *id, + mach_msg_type_name_t *idtype) +{ + struct trivfs_protid *cred = ports_lookup_port (term_bucket, + arg, tty_class); + error_t err; + + if (!cred) + return EOPNOTSUPP; + + mutex_lock (&global_lock); + + if (!(cred->po->openmodes & (O_READ|O_WRITE))) + err = EBADF; + else + { + *id = ports_get_right (cttyid); + *idtype = MACH_MSG_TYPE_MAKE_SEND; + err = 0; + } + ports_port_deref (cred); + mutex_unlock (&global_lock); + return err; +} + +/* Implement termctty_open_terminal as described in <hurd/term.defs>. */ +kern_return_t +S_termctty_open_terminal (mach_port_t arg, + int flags, + mach_port_t *result, + mach_msg_type_name_t *resulttype) +{ + error_t err; + mach_port_t new_realnode; + struct trivfs_protid *newcred; + struct port_info *pi = ports_lookup_port (term_bucket, arg, cttyid_class); + + if (!pi) + return EOPNOTSUPP; + + assert (pi == cttyid); + + err = io_restrict_auth (termctl->underlying, &new_realnode, 0, 0, 0, 0); + + if (!err) + { + err = trivfs_open (termctl, 0, 0, 0, 0, flags, new_realnode, &newcred); + if (!err) + { + *result = ports_get_right (newcred); + *resulttype = MACH_MSG_TYPE_MAKE_SEND; + ports_port_deref (newcred); + } + } + + ports_port_deref (pi); + return err; +} + +/* Implement term_become_ctty as described in <hurd/term.defs>. */ +kern_return_t +S_term_open_ctty (mach_port_t arg, + pid_t pid, + pid_t pgrp, + mach_port_t *newpt, + mach_msg_type_name_t *newpttype) +{ + error_t err; + struct trivfs_protid *newcred; + struct trivfs_protid *cred = ports_lookup_port (term_bucket, arg, tty_class); + + if (!cred) + return EOPNOTSUPP; + + mutex_lock (&global_lock); + + if (!cred->po->openmodes & (O_READ|O_WRITE)) + { + mutex_unlock (&global_lock); + err = EBADF; + } + else + { + mutex_unlock (&global_lock); + err = trivfs_protid_dup (cred, &newcred); + + if (!err) + { + struct protid_hook *hook = malloc (sizeof (struct protid_hook)); + + hook->pid = pid; + hook->pgrp = pgrp; + hook->refcnt = 1; + + if (newcred->hook) + /* We inherited CRED's hook, get rid of our ref to it. */ + pi_destroy_hook (newcred); + newcred->hook = hook; + + *newpt = ports_get_right (newcred); + *newpttype = MACH_MSG_TYPE_MAKE_SEND; + + ports_port_deref (newcred); + } + } + + ports_port_deref (cred); + + return err; +} + +/* Called for user writes to the terminal as described + in <hurd/io.defs>. */ +error_t +trivfs_S_io_write (struct trivfs_protid *cred, + mach_port_t reply, + mach_msg_type_name_t replytype, + char *data, + u_int datalen, + off_t offset, + int *amt) +{ + int i; + int cancel; + + if (!cred) + return EOPNOTSUPP; + + mutex_lock (&global_lock); + + /* Check for errors first. */ + + if ((cred->po->openmodes & O_WRITE) == 0) + { + mutex_unlock (&global_lock); + return EBADF; + } + + if ((termstate.c_lflag & TOSTOP) && !fg_p (cred)) + { + mutex_unlock (&global_lock); + return EBACKGROUND; + } + + if ((termflags & NO_CARRIER) && !(termstate.c_cflag & CLOCAL)) + { + mutex_unlock (&global_lock); + return EIO; + + } + + cancel = 0; + for (i = 0; i < datalen; i++) + { + while (!qavail (outputq) && !cancel) + cancel = hurd_condition_wait (outputq->wait, &global_lock); + if (cancel) + break; + + output_character (data[i]); + } + + *amt = i; + + start_output (); + + trivfs_set_mtime (termctl); + + call_asyncs (); + + mutex_unlock (&global_lock); + + return ((cancel && datalen && !*amt) ? EINTR : 0); +} + +/* Called for user reads from the terminal. */ +error_t +trivfs_S_io_read (struct trivfs_protid *cred, + mach_port_t reply, + mach_msg_type_name_t replytype, + char **data, + u_int *datalen, + off_t offset, + int amount) +{ + int cancel; + int i, max; + char *cp; + + if (!cred) + return EOPNOTSUPP; + + mutex_lock (&global_lock); + + if ((cred->po->openmodes & O_READ) == 0) + { + mutex_unlock (&global_lock); + return EBADF; + } + + if (!fg_p (cred)) + { + mutex_unlock (&global_lock); + return EBACKGROUND; + } + + while (!qsize (inputq)) + { + if ((termflags & NO_CARRIER) && !(termstate.c_cflag & CLOCAL)) + { + /* Return EOF, Posix.1 7.1.1.10. */ + mutex_unlock (&global_lock); + *datalen = 0; + return 0; + } + + if (cred->po->openmodes & O_NONBLOCK) + { + mutex_unlock (&global_lock); + return EWOULDBLOCK; + } + + if (hurd_condition_wait (inputq->wait, &global_lock)) + { + mutex_unlock (&global_lock); + return EINTR; + } + } + + max = (amount < qsize (inputq)) ? amount : qsize (inputq); + + if (max > *datalen) + vm_allocate (mach_task_self (), (vm_address_t *)data, max, 1); + + cancel = 0; + cp = *data; + for (i = 0; i < max; i++) + { + char c = dequeue (inputq); + + /* Unless this is EOF, add it to the response. */ + if (!(termstate.c_lflag & ICANON) + || !CCEQ (termstate.c_cc[VEOF], c)) + *cp++ = c; + + /* If this is a break character, then finish now. */ + if ((termstate.c_lflag & ICANON) + && (c == '\n' + || CCEQ (termstate.c_cc[VEOF], c) + || CCEQ (termstate.c_cc[VEOL], c) + || CCEQ (termstate.c_cc[VEOL2], c))) + break; + + /* If this is the delayed suspend character, then signal now. */ + if ((termstate.c_lflag & ISIG) + && CCEQ (termstate.c_cc[VDSUSP], c)) + { + /* The CANCEL flag is being used here to tell the return + below to make sure we don't signal EOF on a VDUSP that + happens at the front of a line. */ + send_signal (SIGTSTP); + cancel = 1; + break; + } + } + + *datalen = cp - *data; + + mutex_unlock (&global_lock); + + call_asyncs (); + + return !*datalen && cancel ? EINTR : 0; +} + +error_t +trivfs_S_io_readable (struct trivfs_protid *cred, + mach_port_t reply, + mach_msg_type_name_t replytype, + int *amt) +{ + if (!cred) + return EOPNOTSUPP; + + mutex_lock (&global_lock); + if ((cred->po->openmodes & O_READ) == 0) + return EBADF; + *amt = qsize (inputq); + mutex_unlock (&global_lock); + + return 0; +} + +/* TIOCMODG ioctl -- Get modem state */ +kern_return_t +S_tioctl_tiocmodg (io_t port, + int *state) +{ + struct trivfs_protid *cred = ports_lookup_port (term_bucket, + port, tty_class); + if (!cred) + return EOPNOTSUPP; + + mutex_lock (&global_lock); + *state = mdmstate (); + mutex_unlock (&global_lock); + + ports_port_deref (cred); + return 0; +} + +/* TIOCMODS ioctl -- Set modem state */ +kern_return_t +S_tioctl_tiocmods (io_t port, + int state) +{ + struct trivfs_protid *cred = ports_lookup_port (term_bucket, port, + tty_class); + error_t err; + if (!cred) + return EOPNOTSUPP; + + mutex_lock (&global_lock); + + if (!(cred->po->openmodes & (O_READ|O_WRITE))) + err = EBADF; + else + { + mdmctl (MDMCTL_SET, state); + err = 0; + } + + mutex_unlock (&global_lock); + + ports_port_deref (cred); + return err; +} + +/* TIOCEXCL ioctl -- Set exclusive use */ +kern_return_t +S_tioctl_tiocexcl (io_t port) +{ + struct trivfs_protid *cred = ports_lookup_port (term_bucket, port, + tty_class); + error_t err; + if (!cred) + return EOPNOTSUPP; + + mutex_lock (&global_lock); + + if (!(cred->po->openmodes & (O_READ|O_WRITE))) + err = EBADF; + else + { + termflags |= EXCL_USE; + err = 0; + } + + mutex_unlock (&global_lock); + ports_port_deref (cred); + return err; +} + +/* TIOCNXCL ioctl -- Clear exclusive use */ +kern_return_t +S_tioctl_tiocnxcl (io_t port) +{ + struct trivfs_protid *cred = ports_lookup_port (term_bucket, + port, tty_class); + error_t err; + + if (!cred) + return EOPNOTSUPP; + + mutex_lock (&global_lock); + if (!(cred->po->openmodes & (O_READ|O_WRITE))) + err = EBADF; + else + { + termflags &= ~EXCL_USE; + err = 0; + } + + mutex_unlock (&global_lock); + ports_port_deref (cred); + return err; +} + +/* TIOCFLUSH ioctl -- Flush input, output, or both */ +kern_return_t +S_tioctl_tiocflush (io_t port, + int flags) +{ + struct trivfs_protid *cred = ports_lookup_port (term_bucket, port, + tty_class); + error_t err; + if (!cred) + return EOPNOTSUPP; + + mutex_lock (&global_lock); + + if (!(cred->po->openmodes & (O_READ|O_WRITE))) + err = EBADF; + else + { + if (flags == 0) + flags = O_READ|O_WRITE; + + if (flags & O_READ) + clear_queue (inputq); + + if (flags & O_WRITE) + drop_output (); + + err = 0; + } + + mutex_unlock (&global_lock); + ports_port_deref (cred); + return err; +} + +/* TIOCGETA ioctl -- Get termios state */ +kern_return_t +S_tioctl_tiocgeta (io_t port, + tcflag_t *modes, + cc_t *ccs, + speed_t *speeds) +{ + struct trivfs_protid *cred = ports_lookup_port (term_bucket, port, + tty_class); + if (!cred) + return EOPNOTSUPP; + + mutex_lock (&global_lock); + modes[0] = termstate.c_iflag; + modes[1] = termstate.c_oflag; + modes[2] = termstate.c_cflag; + modes[3] = termstate.c_lflag; + bcopy (termstate.c_cc, ccs, NCCS); + speeds[0] = termstate.__ispeed; + speeds[1] = termstate.__ospeed; + mutex_unlock (&global_lock); + + ports_port_deref (cred); + return 0; +} + +/* Common code for the varios TIOCSET* commands. */ +static error_t +set_state (io_t port, + tcflag_t *modes, + cc_t *ccs, + speed_t *speeds, + int draino, + int flushi) +{ + struct trivfs_protid *cred = ports_lookup_port (term_bucket, port, + tty_class); + error_t err; + int oldlflag; + + if (!cred) + return EOPNOTSUPP; + + mutex_lock (&global_lock); + + if (!(cred->po->openmodes & (O_READ|O_WRITE))) + err = EBADF; + else if (!fg_p (cred)) + err = EBACKGROUND; + else + { + if (draino) + { + err = drain_output (); + if (err) + { + mutex_unlock (&global_lock); + ports_port_deref (cred); + return err; + } + } + + if (flushi) + clear_queue (inputq); + + oldlflag = termstate.c_lflag; + termstate.c_iflag = modes[0]; + termstate.c_oflag = modes[1]; + termstate.c_cflag = modes[2]; + termstate.c_lflag = modes[3]; + bcopy (ccs, termstate.c_cc, NCCS); + termstate.__ispeed = speeds[0]; + termstate.__ospeed = speeds[1]; + if (!(termstate.c_cflag & CIGNORE)) + set_bits (); + if (oldlflag & ICANON) + { + if (!(termstate.c_lflag & ICANON)) + copy_rawq (); + } + else + { + if (termstate.c_lflag & ICANON) + rescan_inputq (); + } + err = 0; + } + + mutex_unlock (&global_lock); + + ports_port_deref (cred); + return err; +} + + + +/* TIOCSETA -- Set termios state */ +kern_return_t +S_tioctl_tiocseta (io_t port, + tcflag_t *modes, + cc_t *ccs, + speed_t *speeds) +{ + return set_state (port, modes, ccs, speeds, 0, 0); +} + +/* Drain output, then set term state. */ +kern_return_t +S_tioctl_tiocsetaw (io_t port, + tcflag_t *modes, + cc_t *ccs, + speed_t *speeds) +{ + return set_state (port, modes, ccs, speeds, 1, 0); +} + +/* Flush input, drain output, then set term state. */ +kern_return_t +S_tioctl_tiocsetaf (io_t port, + tcflag_t *modes, + cc_t *ccs, + speed_t *speeds) + +{ + return set_state (port, modes, ccs, speeds, 1, 1); +} + +/* TIOCGETD -- Return line discipline */ +kern_return_t +S_tioctl_tiocgetd (io_t port, + int *disc) +{ + struct trivfs_protid *cred = ports_lookup_port (term_bucket, port, + tty_class); + if (!cred) + return EOPNOTSUPP; + + *disc = 0; + + ports_port_deref (cred); + return 0; +} + +/* TIOCSETD -- Set line discipline */ +kern_return_t +S_tioctl_tiocsetd (io_t port, + int disc) +{ + struct trivfs_protid *cred = ports_lookup_port (term_bucket, + port, tty_class); + error_t err; + if (!cred) + return EOPNOTSUPP; + + mutex_lock (&global_lock); + if (!(cred->po->openmodes & (O_READ|O_WRITE))) + err = EBADF; + mutex_unlock (&global_lock); + + if (disc != 0) + err = ENXIO; + else + err = 0; + + ports_port_deref (cred); + return 0; +} + +/* TIOCDRAIN -- Wait for output to drain */ +kern_return_t +S_tioctl_tiocdrain (io_t port) +{ + struct trivfs_protid *cred = ports_lookup_port (term_bucket, + port, tty_class); + error_t err; + + if (!cred) + return EOPNOTSUPP; + + mutex_lock (&global_lock); + if (!(cred->po->openmodes & O_WRITE)) + { + mutex_unlock (&global_lock); + return EBADF; + } + + err = drain_output (); + mutex_unlock (&global_lock); + return err; +} + +/* TIOCSIG -- PTY master generate signal */ +kern_return_t +S_tioctl_tiocsig (io_t port) +{ + return EOPNOTSUPP; +} + +/* TIOCEXT -- PTY master enter external mode */ +kern_return_t +S_tioctl_tiocext (io_t port) +{ + return EOPNOTSUPP; +} + +/* TIOCUCNTL -- PTY set/clear usr cntl mode */ +kern_return_t +S_tioctl_tiocucntl (io_t port, + int how) +{ + return EOPNOTSUPP; +} + +/* TIOCSWINSZ -- Set window size */ +kern_return_t +S_tioctl_tiocswinsz (io_t port, + struct winsize size) +{ + struct trivfs_protid *cred = ports_lookup_port (term_bucket, port, + tty_class); + error_t err; + if (!cred) + return EOPNOTSUPP; + + mutex_lock (&global_lock); + + if (!(cred->po->openmodes & (O_READ|O_WRITE))) + err = EBADF; + else + err = 0; + + ports_port_deref (cred); + + if (!err && (size.ws_row - window_size.ws_row + + size.ws_col - window_size.ws_col + + size.ws_xpixel - window_size.ws_xpixel + + size.ws_ypixel - window_size.ws_ypixel) != 0) + { + /* The size is actually changing. Record the new size and notify the + process group. */ + window_size = size; + send_signal (SIGWINCH); + } + + mutex_unlock (&global_lock); + + return err; +} + +/* TIOCGWINSZ -- Fetch window size */ +kern_return_t +S_tioctl_tiocgwinsz (io_t port, + struct winsize *size) +{ + struct trivfs_protid *cred = ports_lookup_port (term_bucket, port, + tty_class); + if (!cred) + return EOPNOTSUPP; + + mutex_lock (&global_lock); + *size = window_size; + mutex_unlock (&global_lock); + + ports_port_deref (cred); + return 0; +} + +/* TIOCREMOTE -- PTY enter remote mode */ +kern_return_t +S_tioctl_tiocremote (io_t port, + int how) +{ + return EOPNOTSUPP; +} + +/* TIOCMGET -- Fetch all modem bits */ +kern_return_t +S_tioctl_tiocmget (io_t port, + int *bits) +{ + struct trivfs_protid *cred = ports_lookup_port (term_bucket, port, + tty_class); + if (!cred) + return EOPNOTSUPP; + + mutex_lock (&global_lock); + *bits = mdmstate (); + mutex_unlock (&global_lock); + + ports_port_deref (cred); + return 0; +} + +/* TIOCMSET -- Set all modem bits */ +kern_return_t +S_tioctl_tiocmset (io_t port, + int bits) +{ + struct trivfs_protid *cred = ports_lookup_port (term_bucket, port, + tty_class); + error_t err; + if (!cred) + return EOPNOTSUPP; + + mutex_lock (&global_lock); + if (!(cred->po->openmodes & (O_READ|O_WRITE))) + err = EBADF; + else + { + mdmctl (MDMCTL_SET, bits); + err = 0; + } + + mutex_unlock (&global_lock); + ports_port_deref (cred); + return err; +} + +/* TIOCMBIC -- Clear some modem bits */ +kern_return_t +S_tioctl_tiocmbic (io_t port, + int bits) +{ + struct trivfs_protid *cred = ports_lookup_port (term_bucket, port, + tty_class); + error_t err; + if (!cred) + return EOPNOTSUPP; + + mutex_lock (&global_lock); + if (!(cred->po->openmodes & (O_READ|O_WRITE))) + err = EBADF; + else + { + mdmctl (MDMCTL_BIC, bits); + err = 0; + } + mutex_unlock (&global_lock); + + ports_port_deref (cred); + return err; +} + +/* TIOCMBIS -- Set some modem bits */ +kern_return_t +S_tioctl_tiocmbis (io_t port, + int bits) +{ + struct trivfs_protid *cred = ports_lookup_port (term_bucket, port, + tty_class); + error_t err; + if (!cred) + return EOPNOTSUPP; + mutex_lock (&global_lock); + + if (!(cred->po->openmodes & (O_READ|O_WRITE))) + err = EBADF; + else + { + mdmctl (MDMCTL_BIS, bits); + err = 0; + } + mutex_unlock (&global_lock); + ports_port_deref (cred); + return err; +} + +/* TIOCSTART -- start output as if VSTART were typed */ +kern_return_t +S_tioctl_tiocstart (io_t port) +{ + struct trivfs_protid *cred = ports_lookup_port (term_bucket, port, + tty_class); + error_t err; + if (!cred) + return EOPNOTSUPP; + + mutex_lock (&global_lock); + + if (!(cred->po->openmodes & (O_READ|O_WRITE))) + err = EBADF; + else + { + termflags &= ~USER_OUTPUT_SUSP; + err = 0; + } + mutex_unlock (&global_lock); + + ports_port_deref (cred); + return err; +} + +/* TIOCSTOP -- stop output as if VSTOP were typed */ +kern_return_t +S_tioctl_tiocstop (io_t port) +{ + struct trivfs_protid *cred = ports_lookup_port (term_bucket, + port, tty_class); + error_t err; + if (!cred) + return EOPNOTSUPP; + + mutex_lock (&global_lock); + + if (!(cred->po->openmodes & (O_READ|O_WRITE))) + err = EBADF; + else + { + termflags |= USER_OUTPUT_SUSP; + err = 0; + } + mutex_unlock (&global_lock); + + ports_port_deref (cred); + return err; +} + +/* TIOCPKT -- PTY Master packet mode */ +kern_return_t +S_tioctl_tiocpkt (io_t port, + int how) +{ + return EOPNOTSUPP; +} + +/* TIOCSTI -- Simulate terminal input */ +kern_return_t +S_tioctl_tiocsti (io_t port, + char c) +{ + struct trivfs_protid *cred = ports_lookup_port (term_bucket, + port, tty_class); + error_t err; + if (!cred) + return EOPNOTSUPP; + + mutex_lock (&global_lock); + + /* BSD returns EACCES if this is not our controlling terminal, + but we have no way to do that. (And I don't think it actually + provides any security there, either.) */ + + if (!(cred->po->openmodes & O_READ)) + err = EPERM; + else + { + input_character (c); + err = 0; + } + mutex_unlock (&global_lock); + + ports_port_deref (cred); + return err; +} + +/* TIOCOUTQ -- return output queue size */ +kern_return_t +S_tioctl_tiocoutq (io_t port, + int *queue_size) +{ + struct trivfs_protid *cred = ports_lookup_port (term_bucket, port, + tty_class); + error_t err; + if (!cred) + return EOPNOTSUPP; + + mutex_lock (&global_lock); + + if (!(cred->po->openmodes & (O_READ|O_WRITE))) + err = EBADF; + else + { + *queue_size = qsize (outputq) + pending_output_size (); + err = 0; + } + mutex_unlock (&global_lock); + + ports_port_deref (cred); + return err; +} + +/* TIOCSPGRP -- set pgrp of terminal */ +kern_return_t +S_tioctl_tiocspgrp (io_t port, + int pgrp) +{ + struct trivfs_protid *cred = ports_lookup_port (term_bucket, port, + tty_class); + error_t err; + if (!cred) + return EOPNOTSUPP; + + mutex_lock (&global_lock); + if (!(cred->po->openmodes & (O_READ|O_WRITE))) + err = EBADF; + else + { + termflags &= ~NO_OWNER; + foreground_id = -pgrp; + err = 0; + } + mutex_unlock (&global_lock); + + ports_port_deref (cred); + return err; +} + +/* TIOCGPGRP --- fetch pgrp of terminal */ +kern_return_t +S_tioctl_tiocgpgrp (io_t port, + int *pgrp) +{ + struct trivfs_protid *cred = ports_lookup_port (term_bucket, port, + tty_class); + error_t ret; + + if (!cred) + return EOPNOTSUPP; + + mutex_lock (&global_lock); + if (termflags & NO_OWNER) + ret = ENOTTY; /* that's what BSD says... */ + else + { + *pgrp = - foreground_id; + ret = 0; + } + mutex_unlock (&global_lock); + + ports_port_deref (cred); + return ret; +} + +/* TIOCCDTR -- clear DTR */ +kern_return_t +S_tioctl_tioccdtr (io_t port) +{ + struct trivfs_protid *cred = ports_lookup_port (term_bucket, port, + tty_class); + error_t err; + if (!cred) + return EOPNOTSUPP; + + mutex_lock (&global_lock); + if (!(cred->po->openmodes & (O_READ|O_WRITE))) + err = EBADF; + else + { + mdmctl (MDMCTL_BIC, TIOCM_DTR); + err = 0; + } + mutex_unlock (&global_lock); + + ports_port_deref (cred); + return err; +} + +/* TIOCSDTR -- set DTR */ +kern_return_t +S_tioctl_tiocsdtr (io_t port) +{ + struct trivfs_protid *cred = ports_lookup_port (term_bucket, port, + tty_class); + error_t err; + if (!cred) + return EOPNOTSUPP; + + mutex_lock (&global_lock); + if (!(cred->po->openmodes & (O_READ|O_WRITE))) + err = EBADF; + else + { + mdmctl (MDMCTL_BIS, TIOCM_DTR); + err = 0; + } + mutex_unlock (&global_lock); + + ports_port_deref (cred); + return err; +} + +/* TIOCCBRK -- Clear break condition */ +kern_return_t +S_tioctl_tioccbrk (io_t port) +{ + struct trivfs_protid *cred = ports_lookup_port (term_bucket, port, + tty_class); + error_t err; + if (!cred) + return EOPNOTSUPP; + + mutex_lock (&global_lock); + if (!(cred->po->openmodes & (O_READ|O_WRITE))) + err = EBADF; + else + { + clear_break (); + err = 0; + } + mutex_unlock (&global_lock); + + ports_port_deref (cred); + return err; +} + +/* TIOCSBRK -- Set break condition */ +kern_return_t +S_tioctl_tiocsbrk (io_t port) +{ + struct trivfs_protid *cred = ports_lookup_port (term_bucket, + port, tty_class); + error_t err; + if (!cred) + return EOPNOTSUPP; + + mutex_lock (&global_lock); + if (!(cred->po->openmodes & (O_READ|O_WRITE))) + err = EBADF; + else + { + set_break (); + err = 0; + } + mutex_unlock (&global_lock); + + ports_port_deref (cred); + return err; +} + +error_t +trivfs_S_file_truncate (struct trivfs_protid *cred, + off_t size) +{ + if (!cred) + return EOPNOTSUPP; + mutex_lock (&global_lock); + if ((cred->po->openmodes & O_WRITE) == 0) + { + mutex_unlock (&global_lock); + return EBADF; + } + mutex_unlock (&global_lock); + return 0; +} + +error_t +trivfs_S_io_seek (struct trivfs_protid *cred, + off_t off, + int whence, + off_t *newp) +{ + return ESPIPE; +} + +error_t +trivfs_S_io_get_openmodes (struct trivfs_protid *cred, + mach_port_t reply, + mach_msg_type_name_t replytype, + int *bits) +{ + if (!cred) + return EOPNOTSUPP; + mutex_lock (&global_lock); + *bits = cred->po->openmodes; + mutex_unlock (&global_lock); + return 0; +} + +#define HONORED_STATE_MODES (O_APPEND|O_ASYNC|O_FSYNC|O_NONBLOCK|O_NOATIME) + +error_t +trivfs_S_io_set_all_openmodes (struct trivfs_protid *cred, + mach_port_t reply, + mach_msg_type_name_t replytype, + int bits) +{ + + if (!cred) + return EOPNOTSUPP; + mutex_lock (&global_lock); + cred->po->openmodes &= ~HONORED_STATE_MODES; + cred->po->openmodes |= (bits & HONORED_STATE_MODES); + mutex_unlock (&global_lock); + return 0; +} + +error_t +trivfs_S_io_set_some_openmodes (struct trivfs_protid *cred, + mach_port_t reply, + mach_msg_type_name_t reply_type, + int bits) +{ + if (!cred) + return EOPNOTSUPP; + mutex_lock (&global_lock); + cred->po->openmodes |= (bits & HONORED_STATE_MODES); + mutex_unlock (&global_lock); + return 0; +} + +error_t +trivfs_S_io_clear_some_openmodes (struct trivfs_protid *cred, + mach_port_t reply, + mach_msg_type_name_t reply_type, + int bits) +{ + if (!cred) + return EOPNOTSUPP; + mutex_lock (&global_lock); + cred->po->openmodes &= ~(bits & HONORED_STATE_MODES); + mutex_unlock (&global_lock); + return 0; +} + +error_t +trivfs_S_io_mod_owner (struct trivfs_protid *cred, + mach_port_t reply, + mach_msg_type_name_t reply_type, + pid_t owner) +{ + if (!cred) + return EOPNOTSUPP; + mutex_lock (&global_lock); + termflags &= ~NO_OWNER; + foreground_id = owner; + mutex_unlock (&global_lock); + return 0; +} + +error_t +trivfs_S_io_get_owner (struct trivfs_protid *cred, + mach_port_t erply, + mach_msg_type_name_t reply_type, + pid_t *owner) +{ + if (!cred) + return EOPNOTSUPP; + mutex_lock (&global_lock); + if (termflags & NO_OWNER) + { + mutex_unlock (&global_lock); + return ENOTTY; + } + *owner = foreground_id; + mutex_unlock (&global_lock); + return 0; +} + +error_t +trivfs_S_io_get_async_icky (struct trivfs_protid *cred, + mach_port_t *id, + mach_msg_type_name_t *idtype) +{ + if (!cred) + return EOPNOTSUPP; + mutex_lock (&global_lock); + if (!(cred->po->openmodes & (O_READ|O_WRITE))) + { + mutex_unlock (&global_lock); + return EBADF; + } + *id = async_icky_id; + *idtype = MACH_MSG_TYPE_MAKE_SEND; + mutex_unlock (&global_lock); + return 0; +} + +error_t +trivfs_S_io_async (struct trivfs_protid *cred, + mach_port_t notify, + mach_port_t *id, + mach_msg_type_name_t *idtype) +{ + struct async_req *ar; + if (!cred) + return EOPNOTSUPP; + mutex_lock (&global_lock); + if (!(cred->po->openmodes & (O_READ|O_WRITE))) + { + mutex_unlock (&global_lock); + return EBADF; + } + ar = malloc (sizeof (struct async_req)); + ar->notify = notify; + ar->next = async_requests; + async_requests = ar; + *id = async_id; + *idtype = MACH_MSG_TYPE_MAKE_SEND; + mutex_unlock (&global_lock); + return 0; +} + +error_t +trivfs_S_io_select (struct trivfs_protid *cred, + mach_port_t reply, + mach_msg_type_name_t reply_type, + int *type, + int *idtag) +{ + int available; + + if (!cred) + return EOPNOTSUPP; + + /* We don't deal with SELECT_URG here. */ + if (*type & ~(SELECT_READ | SELECT_WRITE)) + return EINVAL; + + available = 0; + if (*type == 0) + return 0; + + mutex_lock (&global_lock); + + while (1) + { + if ((*type & SELECT_READ) && qsize (inputq)) + available |= SELECT_READ; + if ((*type & SELECT_WRITE) && qavail (outputq)) + available |= SELECT_WRITE; + + if (available) + { + *type = available; + mutex_unlock (&global_lock); + return 0; + } + + hurd_condition_wait (&select_alert, &global_lock); + } +} + +kern_return_t +trivfs_S_io_map (struct trivfs_protid *cred, + mach_port_t *rdobj, + mach_msg_type_name_t *rdtype, + mach_port_t *wrobj, + mach_msg_type_name_t *wrtype) +{ + return EOPNOTSUPP; +} + +/* Call all the scheduled async I/O handlers */ +static void +call_asyncs () +{ + struct async_req *ar, *nxt, **prevp; + mach_port_t err; + + /* If no I/O is possible or nobody wants async + messages, don't bother further. */ + if ((!(termflags & ICKY_ASYNC) && !async_requests) + || (!qsize (inputq) && !qavail (outputq))) + return; + + if ((termflags & ICKY_ASYNC) && !(termflags & NO_OWNER)) + hurd_sig_post (foreground_id, SIGIO, async_icky_id); + + for (ar = async_requests, prevp = &async_requests; + ar; + ar = nxt) + { + nxt = ar->next; + err = nowait_msg_sig_post (ar->notify, SIGIO, async_id); + if (err == MACH_SEND_INVALID_DEST) + { + /* Receiver died; remove the notification request. */ + *prevp = ar->next; + mach_port_deallocate (mach_task_self (), ar->notify); + free (ar); + } + else + prevp = &ar->next; + } +} + +/* Send a signal to the current process (group) of the terminal. */ +void +send_signal (int signo) +{ + mach_port_t right; + + if (!(termflags & NO_OWNER)) + { + right = ports_get_right (cttyid); + mach_port_insert_right (mach_task_self (), right, right, + MACH_MSG_TYPE_MAKE_SEND); + hurd_sig_post (foreground_id, signo, right); + mach_port_deallocate (mach_task_self (), right); + } +} + +error_t +trivfs_S_interrupt_operation (mach_port_t port) +{ + struct trivfs_protid *cred = ports_lookup_port (term_bucket, port, + tty_class); + if (!cred) + return EOPNOTSUPP; + + ports_interrupt_rpc (cred); + + ports_port_deref (cred); + return 0; +} + +void +report_carrier_off () +{ + termflags |= NO_CARRIER; + if (!(termstate.c_cflag & CLOCAL)) + send_signal (SIGHUP); +} + +void +report_carrier_on () +{ + termflags &= ~NO_CARRIER; + condition_broadcast (&carrier_alert); +} + +kern_return_t +S_term_get_nodename (io_t arg, + char *name) +{ + struct trivfs_protid *cred = ports_lookup_port (term_bucket, arg, + tty_class); + if (!cred) + return EOPNOTSUPP; + + strcpy (name, nodename); + + ports_port_deref (cred); + return 0; +} + +kern_return_t +S_term_set_nodename (io_t arg, + char *name) +{ + struct trivfs_protid *cred = ports_lookup_port (term_bucket, arg, + tty_class); + if (!cred) + return EOPNOTSUPP; + ports_port_deref (cred); + + if (strcmp (name, nodename)) + return EINVAL; + + return 0; +} + +kern_return_t +S_term_set_filenode (io_t arg, + file_t filenode) +{ + struct trivfs_protid *cred = ports_lookup_port (term_bucket, arg, + tty_class); + if (!cred) + return EOPNOTSUPP; + ports_port_deref (cred); + + return EINVAL; +} + +kern_return_t +S_term_get_bottom_type (io_t arg, + int *ttype) +{ + struct trivfs_protid *cred = ports_lookup_port (term_bucket, + arg, tty_class); + if (!cred) + return EOPNOTSUPP; + + ports_port_deref (cred); + *ttype = TERM_ON_MACHDEV; + return 0; +} + +kern_return_t +S_term_on_machdev (io_t arg, + device_t machdev) +{ + struct trivfs_protid *cred = ports_lookup_port (term_bucket, arg, + tty_class); + if (!cred) + return EOPNOTSUPP; + ports_port_deref (cred); + return EINVAL; +} + +kern_return_t +S_term_on_hurddev (io_t arg, + io_t hurddev) +{ + return EOPNOTSUPP; +} + +kern_return_t +S_term_on_pty (io_t arg, + mach_port_t *master) +{ + return EOPNOTSUPP; +} + +error_t +trivfs_goaway (struct trivfs_control *cntl, int flags) +{ + return EBUSY; +} |