aboutsummaryrefslogtreecommitdiff
path: root/term/users.c
diff options
context:
space:
mode:
Diffstat (limited to 'term/users.c')
-rw-r--r--term/users.c2287
1 files changed, 2287 insertions, 0 deletions
diff --git a/term/users.c b/term/users.c
new file mode 100644
index 00000000..1168ca10
--- /dev/null
+++ b/term/users.c
@@ -0,0 +1,2287 @@
+/*
+ Copyright (C) 1995, 96, 97, 98, 1999 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 <stdio.h>
+#include <hurd/iohelp.h>
+#include <hurd/fshelp.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;
+
+/* Number of peropens that have set the ICKY_ASYNC flags. */
+static int num_icky_async_peropens = 0;
+
+mach_port_t async_icky_id;
+mach_port_t async_id;
+struct port_info *cttyid;
+int foreground_id;
+
+struct winsize window_size;
+
+static int sigs_in_progress;
+static struct condition input_sig_wait = CONDITION_INITIALIZER;
+static int input_sig_wakeup;
+
+static error_t carrier_error;
+
+/* Attach this on the hook of any protid that is a ctty. */
+struct protid_hook
+{
+ int refcnt;
+ pid_t pid, pgrp;
+};
+
+void
+init_users ()
+{
+ errno = ports_create_port (cttyid_class, term_bucket,
+ sizeof (struct port_info), &cttyid);
+ if (errno)
+ {
+ perror ("Allocating cttyid");
+ exit (1);
+ }
+
+ mach_port_allocate (mach_task_self (), MACH_PORT_RIGHT_RECEIVE,
+ &async_icky_id);
+ /* Add a send right, since hurd_sig_post needs one. */
+ mach_port_insert_right (mach_task_self (),
+ async_icky_id, async_icky_id,
+ MACH_MSG_TYPE_MAKE_SEND);
+
+ mach_port_allocate (mach_task_self (), MACH_PORT_RIGHT_RECEIVE, &async_id);
+ /* Add a send right, since hurd_sig_post needs one. */
+ mach_port_insert_right (mach_task_self (),
+ async_id, async_id, MACH_MSG_TYPE_MAKE_SEND);
+}
+
+
+static error_t
+check_access_hook (struct trivfs_control *cntl,
+ struct iouser *user,
+ mach_port_t realnode,
+ int *allowed)
+{
+ struct stat st;
+
+ mutex_lock (&global_lock);
+
+ st.st_uid = term_owner;
+ st.st_gid = term_group;
+ st.st_mode = term_mode;
+
+ *allowed = 0;
+ if (fshelp_access (&st, S_IREAD, user) == 0)
+ *allowed |= O_READ;
+ if (fshelp_access (&st, S_IWRITE, user) == 0)
+ *allowed |= O_WRITE;
+
+ mutex_unlock (&global_lock);
+ return 0;
+}
+error_t (*trivfs_check_access_hook) (struct trivfs_control *, struct iouser *,
+ mach_port_t, int *)
+ = check_access_hook;
+
+static error_t
+open_hook (struct trivfs_control *cntl,
+ struct iouser *user,
+ int flags)
+{
+ static int open_count = 0; /* XXX debugging */
+ int cancel = 0;
+ error_t err;
+
+ if (cntl == ptyctl)
+ return pty_open_hook (cntl, user, flags);
+
+ if ((flags & (O_READ|O_WRITE)) == 0)
+ return 0;
+
+ 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);
+
+ bzero (&window_size, sizeof window_size);
+
+ termflags |= NO_OWNER;
+ }
+ else
+ {
+ assert (open_count > 0); /* XXX debugging */
+
+ if (termflags & EXCL_USE)
+ {
+ mutex_unlock (&global_lock);
+ return EBUSY;
+ }
+ }
+
+ open_count++; /* XXX debugging */
+
+ /* XXX debugging */
+ assert (! (termstate.c_oflag & OTILDE));
+
+ /* Assert DTR if necessary. */
+ if (termflags & NO_CARRIER)
+ {
+ err = (*bottom->assert_dtr) ();
+ if (err)
+ {
+ mutex_unlock (&global_lock);
+ return err;
+ }
+ }
+
+ /* Wait for carrier to turn on. */
+ while (((termflags & NO_CARRIER) && !(termstate.c_cflag & CLOCAL))
+ && !(flags & O_NONBLOCK)
+ && !cancel)
+ cancel = hurd_condition_wait (&carrier_alert, &global_lock);
+
+ if (cancel)
+ {
+ mutex_unlock (&global_lock);
+ return EINTR;
+ }
+
+ err = carrier_error;
+ carrier_error = 0;
+
+ if (!err)
+ {
+ termflags |= TTY_OPEN;
+ (*bottom->set_bits) ();
+ }
+
+ mutex_unlock (&global_lock);
+ return err;
+}
+error_t (*trivfs_check_open_hook) (struct trivfs_control *,
+ struct iouser *, int)
+ = open_hook;
+
+static error_t
+pi_create_hook (struct trivfs_protid *cred)
+{
+ if (cred->pi.class == pty_class)
+ return 0;
+
+ 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)
+{
+ if (cred->pi.class == pty_class)
+ return;
+
+ mutex_lock (&global_lock);
+ if (cred->hook)
+ {
+ assert (((struct protid_hook *)cred->hook)->refcnt > 0);
+ if (--((struct protid_hook *)cred->hook)->refcnt == 0)
+ /* XXX don't free for now, so we can try and catch a multiple-freeing
+ bug. */
+ /* 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)
+{
+ if (po->cntl == ptyctl)
+ return pty_po_create_hook (po);
+
+ mutex_lock (&global_lock);
+ nperopens++;
+ if (po->openmodes & O_ASYNC)
+ {
+ termflags |= ICKY_ASYNC;
+ num_icky_async_peropens++;
+ call_asyncs (O_READ | O_WRITE);
+ }
+ 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)
+{
+ if (po->cntl == ptyctl)
+ {
+ pty_po_destroy_hook (po);
+ return;
+ }
+
+ mutex_lock (&global_lock);
+
+ if ((po->openmodes & O_ASYNC) && --num_icky_async_peropens == 0)
+ termflags &= ~ICKY_ASYNC;
+
+ nperopens--;
+ if (!nperopens && (termflags & TTY_OPEN))
+ {
+ /* Empty queues */
+ clear_queue (inputq);
+ clear_queue (rawq);
+ (*bottom->notice_input_flushed) ();
+
+ drain_output ();
+
+ /* Possibly drop carrier */
+ if ((termstate.c_cflag & HUPCL) || (termflags & NO_CARRIER))
+ (*bottom->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 = term_mode;
+ st->st_uid = term_owner;
+ st->st_gid = term_group;
+}
+
+/* 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,
+ iohelp_create_iouser (make_idvec (), make_idvec ()),
+ 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;
+}
+
+/* Implement chown locally; don't pass the value down to the
+ underlying node. */
+error_t
+trivfs_S_file_chown (struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t reply_type,
+ uid_t uid,
+ gid_t gid)
+{
+ struct stat st;
+ error_t err;
+
+ if (!cred)
+ return EOPNOTSUPP;
+
+ mutex_lock (&global_lock);
+
+ /* XXX */
+ st.st_uid = term_owner;
+ st.st_gid = term_group;
+
+ if (!cred->isroot)
+ {
+ err = fshelp_isowner (&st, cred->user);
+ if (err)
+ goto out;
+
+ if ((uid != (uid_t) -1 && !idvec_contains (cred->user->uids, uid))
+ || (gid != (gid_t) -1 && !idvec_contains (cred->user->gids, gid)))
+ {
+ err = EPERM;
+ goto out;
+ }
+ }
+
+ /* Make the change */
+ if (uid != (uid_t) -1)
+ term_owner = uid;
+ if (gid != (gid_t) -1)
+ term_group = gid;
+ err = 0;
+
+out:
+ mutex_unlock (&global_lock);
+ return err;
+}
+
+/* Implement chmod locally */
+error_t
+trivfs_S_file_chmod (struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t reply_type,
+ mode_t mode)
+{
+ error_t err;
+ struct stat st;
+
+ if (!cred)
+ return EOPNOTSUPP;
+
+ mutex_lock (&global_lock);
+ if (!cred->isroot)
+ {
+ /* XXX */
+ st.st_uid = term_owner;
+ st.st_gid = term_group;
+
+ err = fshelp_isowner (&st, cred->user);
+ if (err)
+ goto out;
+
+ mode &= ~S_ISVTX;
+
+ if (!idvec_contains (cred->user->uids, term_owner))
+ mode &= ~S_ISUID;
+
+ if (!idvec_contains (cred->user->gids, term_group))
+ mode &= ~S_ISUID;
+ }
+
+ term_mode = ((mode & ~S_IFMT & ~S_ITRANS & ~S_ISPARE) | S_IFCHR | S_IROOT);
+ err = 0;
+
+out:
+ mutex_unlock (&global_lock);
+ 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;
+
+ if (cred->pi.class == pty_class)
+ return pty_io_write (cred, data, datalen, amt);
+
+ 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)
+ {
+ (*bottom->start_output) ();
+ if (!qavail (outputq))
+ cancel = hurd_condition_wait (outputq->wait, &global_lock);
+ }
+ if (cancel)
+ break;
+
+ write_character (data[i]);
+ }
+
+ *amt = i;
+
+ (*bottom->start_output) ();
+
+ trivfs_set_mtime (termctl);
+
+ call_asyncs (O_WRITE);
+
+ 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;
+ int avail;
+
+ if (!cred)
+ return EOPNOTSUPP;
+
+ if (cred->pi.class == pty_class)
+ return pty_io_read (cred, data, datalen, amount);
+
+ 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;
+ }
+
+ /* If a signal is being delivered, and we got woken up by
+ arriving input, then there's a possible race; we have to not
+ read from the queue as long as the signal is in progress.
+ Now, you might think that we should not read from the queue
+ when a signal is in progress even if we didn't block, but
+ that's not so. It's specifically that we have to get
+ *interrupted* by signals in progress (when the signallee is
+ this thread and wants to interrupt is) that is the race we
+ are avoiding. A reader who gets in after the signal begins,
+ while the signal is in progress, is harmless, because this
+ case is indiscernable from one where the reader gets in after
+ the signal has completed. */
+ if (sigs_in_progress)
+ {
+ input_sig_wakeup++;
+ if (hurd_condition_wait (&input_sig_wait, &global_lock))
+ {
+ mutex_unlock (&global_lock);
+ return EINTR;
+ }
+ }
+ }
+
+ avail = qsize (inputq);
+ if (remote_input_mode)
+ avail--;
+
+ max = (amount < avail) ? amount : avail;
+
+ if (max > *datalen)
+ *data = mmap (0, max, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+
+ cancel = 0;
+ cp = *data;
+ for (i = 0; i < max; i++)
+ {
+ char c = dequeue (inputq);
+
+ if (remote_input_mode)
+ *cp++ = c;
+ else
+ {
+ /* 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;
+ }
+ }
+ }
+
+ if (remote_input_mode && qsize (inputq) == 1)
+ dequeue (inputq);
+
+ *datalen = cp - *data;
+
+ /* If we really read something, set atime */
+ if (*datalen || !cancel)
+ trivfs_set_atime (termctl);
+
+ call_asyncs (O_READ);
+
+ mutex_unlock (&global_lock);
+
+ return !*datalen && cancel ? EINTR : 0;
+}
+
+error_t
+trivfs_S_io_pathconf (struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t reply_type,
+ int name,
+ int *val)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+
+ switch (name)
+ {
+ case _PC_LINK_MAX:
+ case _PC_NAME_MAX:
+ case _PC_PATH_MAX:
+ case _PC_PIPE_BUF:
+ case _PC_NO_TRUNC:
+ default:
+ return io_pathconf (cred->realnode, name, val);
+
+ case _PC_MAX_CANON:
+ *val = rawq->hiwat;
+ return 0;
+
+ case _PC_MAX_INPUT:
+ *val = inputq->hiwat;
+ return 0;
+
+ case _PC_CHOWN_RESTRICTED:
+ /* We implement this locally, remember... */
+ *val = 1;
+ return 0;
+
+ case _PC_VDISABLE:
+ *val = _POSIX_VDISABLE;
+ return 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;
+
+ if (cred->pi.class == pty_class)
+ return pty_io_readable (amt);
+
+ mutex_lock (&global_lock);
+ if ((cred->po->openmodes & O_READ) == 0)
+ {
+ mutex_unlock (&global_lock);
+ return EBADF;
+ }
+ *amt = qsize (inputq);
+ if (remote_input_mode && *amt)
+ --*amt;
+ mutex_unlock (&global_lock);
+
+ return 0;
+}
+
+error_t
+trivfs_S_io_revoke (struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t replytype)
+{
+ struct stat st;
+
+ error_t iterator_function (void *port)
+ {
+ struct trivfs_protid *user = port;
+
+ if (user != cred)
+ ports_destroy_right (user);
+ return 0;
+ }
+
+ if (!cred)
+ return EOPNOTSUPP;
+
+ mutex_lock (&global_lock);
+
+ if (!cred->isroot)
+ {
+ error_t err;
+
+ /* XXX */
+ st.st_uid = term_owner;
+ st.st_gid = term_group;
+
+ err = fshelp_isowner (&st, cred->user);
+ if (err)
+ {
+ mutex_unlock (&global_lock);
+ return err;
+ }
+ }
+
+ mutex_unlock (&global_lock);
+
+ ports_inhibit_bucket_rpcs (term_bucket);
+ ports_class_iterate (cred->pi.class, iterator_function);
+ ports_resume_bucket_rpcs (term_bucket);
+
+ 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, 0);
+ if (!cred)
+ return EOPNOTSUPP;
+
+ if (cred->pi.class != pty_class
+ && cred->pi.class != tty_class)
+ {
+ ports_port_deref (cred);
+ return EOPNOTSUPP;
+ }
+
+ mutex_lock (&global_lock);
+ *state = (*bottom->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, 0);
+ error_t err;
+ if (!cred)
+ return EOPNOTSUPP;
+
+ if (cred->pi.class != pty_class
+ && cred->pi.class != tty_class)
+ {
+ ports_port_deref (cred);
+ return EOPNOTSUPP;
+ }
+
+ mutex_lock (&global_lock);
+
+ if (!(cred->po->openmodes & (O_READ|O_WRITE)))
+ err = EBADF;
+ else
+ {
+ (*bottom->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, 0);
+ error_t err;
+ if (!cred)
+ return EOPNOTSUPP;
+
+ if (cred->pi.class != pty_class
+ && cred->pi.class != tty_class)
+ {
+ ports_port_deref (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, 0);
+ error_t err;
+
+ if (!cred)
+ return EOPNOTSUPP;
+
+ if (cred->pi.class != pty_class
+ && cred->pi.class != tty_class)
+ {
+ ports_port_deref (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, 0);
+
+ error_t err;
+ if (!cred)
+ return EOPNOTSUPP;
+
+ if (cred->pi.class != pty_class
+ && cred->pi.class != tty_class)
+ {
+ ports_port_deref (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);
+ (*bottom->notice_input_flushed) ();
+ }
+
+ 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, 0);
+
+ if (!cred)
+ return EOPNOTSUPP;
+
+ if (cred->pi.class != pty_class
+ && cred->pi.class != tty_class)
+ {
+ ports_port_deref (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, 0);
+ error_t err;
+ int oldlflag;
+
+ if (!cred)
+ return EOPNOTSUPP;
+
+ if (cred->pi.class != pty_class
+ && cred->pi.class != tty_class)
+ {
+ ports_port_deref (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 (cred->pi.class == pty_class)
+ {
+ clear_queue (outputq);
+ (*bottom->abandon_physical_output) ();
+ }
+
+ if (draino)
+ {
+ err = drain_output ();
+ if (err)
+ {
+ mutex_unlock (&global_lock);
+ ports_port_deref (cred);
+ return err;
+ }
+ }
+
+ if (flushi)
+ {
+ clear_queue (inputq);
+ (*bottom->notice_input_flushed) ();
+ }
+
+ 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 (external_processing)
+ termstate.c_lflag |= EXTPROC;
+ else
+ termstate.c_lflag &= ~EXTPROC;
+
+ (*bottom->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, 0);
+
+ if (!cred)
+ return EOPNOTSUPP;
+
+ if (cred->pi.class != pty_class
+ && cred->pi.class != tty_class)
+ {
+ ports_port_deref (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, 0);
+ error_t err;
+
+ if (!cred)
+ return EOPNOTSUPP;
+
+ if (cred->pi.class != pty_class
+ && cred->pi.class != tty_class)
+ {
+ ports_port_deref (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, 0);
+ error_t err;
+
+ if (!cred)
+ return EOPNOTSUPP;
+
+ if (cred->pi.class != pty_class
+ && cred->pi.class != tty_class)
+ {
+ ports_port_deref (cred);
+ return EOPNOTSUPP;
+ }
+
+ mutex_lock (&global_lock);
+ if (!(cred->po->openmodes & O_WRITE))
+ {
+ mutex_unlock (&global_lock);
+ ports_port_deref (cred);
+ return EBADF;
+ }
+
+ err = drain_output ();
+ mutex_unlock (&global_lock);
+ ports_port_deref (cred);
+ return err;
+}
+
+/* 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, 0);
+ error_t err;
+
+ if (!cred)
+ return EOPNOTSUPP;
+
+ if (cred->pi.class != pty_class
+ && cred->pi.class != tty_class)
+ {
+ ports_port_deref (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))
+ {
+ /* 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, 0);
+
+ if (!cred)
+ return EOPNOTSUPP;
+
+ if (cred->pi.class != pty_class
+ && cred->pi.class != tty_class)
+ {
+ ports_port_deref (cred);
+ return EOPNOTSUPP;
+ }
+
+ mutex_lock (&global_lock);
+ *size = window_size;
+ mutex_unlock (&global_lock);
+
+ ports_port_deref (cred);
+ return 0;
+}
+
+/* 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, 0);
+
+ if (!cred)
+ return EOPNOTSUPP;
+
+ if (cred->pi.class != pty_class
+ && cred->pi.class != tty_class)
+ {
+ ports_port_deref (cred);
+ return EOPNOTSUPP;
+ }
+
+ mutex_lock (&global_lock);
+ *bits = (*bottom->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, 0);
+ error_t err;
+
+ if (!cred)
+ return EOPNOTSUPP;
+
+ if (cred->pi.class != pty_class
+ && cred->pi.class != tty_class)
+ {
+ ports_port_deref (cred);
+ return EOPNOTSUPP;
+ }
+
+ mutex_lock (&global_lock);
+ if (!(cred->po->openmodes & (O_READ|O_WRITE)))
+ err = EBADF;
+ else
+ {
+ (*bottom->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, 0);
+ error_t err;
+
+ if (!cred)
+ return EOPNOTSUPP;
+
+ if (cred->pi.class != pty_class
+ && cred->pi.class != tty_class)
+ {
+ ports_port_deref (cred);
+ return EOPNOTSUPP;
+ }
+
+ mutex_lock (&global_lock);
+ if (!(cred->po->openmodes & (O_READ|O_WRITE)))
+ err = EBADF;
+ else
+ {
+ (*bottom->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, 0);
+ error_t err;
+
+ if (!cred)
+ return EOPNOTSUPP;
+
+ if (cred->pi.class != pty_class
+ && cred->pi.class != tty_class)
+ {
+ ports_port_deref (cred);
+ return EOPNOTSUPP;
+ }
+
+ mutex_lock (&global_lock);
+
+ if (!(cred->po->openmodes & (O_READ|O_WRITE)))
+ err = EBADF;
+ else
+ {
+ (*bottom->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, 0);
+ error_t err;
+
+ if (!cred)
+ return EOPNOTSUPP;
+
+ if (cred->pi.class != pty_class
+ && cred->pi.class != tty_class)
+ {
+ ports_port_deref (cred);
+ return EOPNOTSUPP;
+ }
+
+ mutex_lock (&global_lock);
+
+ if (!(cred->po->openmodes & (O_READ|O_WRITE)))
+ err = EBADF;
+ else
+ {
+ termflags &= ~USER_OUTPUT_SUSP;
+ (*bottom->start_output) ();
+ 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, 0);
+ error_t err;
+
+ if (!cred)
+ return EOPNOTSUPP;
+
+ if (cred->pi.class != pty_class
+ && cred->pi.class != tty_class)
+ {
+ ports_port_deref (cred);
+ return EOPNOTSUPP;
+ }
+ mutex_lock (&global_lock);
+
+ if (!(cred->po->openmodes & (O_READ|O_WRITE)))
+ err = EBADF;
+ else
+ {
+ termflags |= USER_OUTPUT_SUSP;
+ (*bottom->suspend_physical_output) ();
+ err = 0;
+ }
+ mutex_unlock (&global_lock);
+
+ ports_port_deref (cred);
+ return err;
+}
+
+/* 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, 0);
+ error_t err;
+
+ if (!cred)
+ return EOPNOTSUPP;
+
+ if (cred->pi.class != pty_class
+ && cred->pi.class != tty_class)
+ {
+ ports_port_deref (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, 0);
+ error_t err;
+
+ if (!cred)
+ return EOPNOTSUPP;
+
+ if (cred->pi.class != pty_class
+ && cred->pi.class != tty_class)
+ {
+ ports_port_deref (cred);
+ return EOPNOTSUPP;
+ }
+
+ mutex_lock (&global_lock);
+
+ if (!(cred->po->openmodes & (O_READ|O_WRITE)))
+ err = EBADF;
+ else
+ {
+ *queue_size = qsize (outputq) + (*bottom->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, 0);
+ error_t err;
+
+ if (!cred)
+ return EOPNOTSUPP;
+
+ if (cred->pi.class != pty_class
+ && cred->pi.class != tty_class)
+ {
+ ports_port_deref (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, 0);
+ error_t ret;
+
+ if (!cred)
+ return EOPNOTSUPP;
+
+ if (cred->pi.class != pty_class
+ && cred->pi.class != tty_class)
+ {
+ ports_port_deref (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, 0);
+ error_t err;
+
+ if (!cred)
+ return EOPNOTSUPP;
+
+ if (cred->pi.class != pty_class
+ && cred->pi.class != tty_class)
+ {
+ ports_port_deref (cred);
+ return EOPNOTSUPP;
+ }
+
+ mutex_lock (&global_lock);
+ if (!(cred->po->openmodes & (O_READ|O_WRITE)))
+ err = EBADF;
+ else
+ {
+ (*bottom->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, 0);
+ error_t err;
+
+ if (!cred)
+ return EOPNOTSUPP;
+
+ if (cred->pi.class != pty_class
+ && cred->pi.class != tty_class)
+ {
+ ports_port_deref (cred);
+ return EOPNOTSUPP;
+ }
+
+ mutex_lock (&global_lock);
+ if (!(cred->po->openmodes & (O_READ|O_WRITE)))
+ err = EBADF;
+ else
+ {
+ (*bottom->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, 0);
+ error_t err;
+
+ if (!cred)
+ return EOPNOTSUPP;
+
+ if (cred->pi.class != pty_class
+ && cred->pi.class != tty_class)
+ {
+ ports_port_deref (cred);
+ return EOPNOTSUPP;
+ }
+
+ mutex_lock (&global_lock);
+ if (!(cred->po->openmodes & (O_READ|O_WRITE)))
+ err = EBADF;
+ else
+ {
+ (*bottom->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, 0);
+ error_t err;
+
+ if (!cred)
+ return EOPNOTSUPP;
+
+ if (cred->pi.class != pty_class
+ && cred->pi.class != tty_class)
+ {
+ ports_port_deref (cred);
+ return EOPNOTSUPP;
+ }
+
+ mutex_lock (&global_lock);
+ if (!(cred->po->openmodes & (O_READ|O_WRITE)))
+ err = EBADF;
+ else
+ {
+ (*bottom->set_break) ();
+ err = 0;
+ }
+ mutex_unlock (&global_lock);
+
+ ports_port_deref (cred);
+ return err;
+}
+
+error_t
+trivfs_S_file_set_size (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ 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,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ 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)
+{
+ int obits;
+
+ if (!cred)
+ return EOPNOTSUPP;
+
+ mutex_lock (&global_lock);
+
+ obits = cred->po->openmodes;
+ if ((obits & O_ASYNC) && --num_icky_async_peropens == 0)
+ termflags &= ~ICKY_ASYNC;
+
+ cred->po->openmodes &= ~HONORED_STATE_MODES;
+ cred->po->openmodes |= (bits & HONORED_STATE_MODES);
+
+ if ((bits & O_ASYNC) && !(obits & O_ASYNC))
+ {
+ termflags |= ICKY_ASYNC;
+ num_icky_async_peropens++;
+ call_asyncs (O_READ | O_WRITE);
+ }
+
+ 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)
+{
+ int obits;
+
+ if (!cred)
+ return EOPNOTSUPP;
+
+ mutex_lock (&global_lock);
+ obits = cred->po->openmodes;
+ cred->po->openmodes |= (bits & HONORED_STATE_MODES);
+ if ((bits & O_ASYNC) && !(obits & O_ASYNC))
+ {
+ termflags |= ICKY_ASYNC;
+ num_icky_async_peropens++;
+ call_asyncs (O_READ | O_WRITE);
+ }
+ 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);
+ if ((cred->po->openmodes & O_ASYNC) && --num_icky_async_peropens == 0)
+ termflags &= ~ICKY_ASYNC;
+ 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_icky_async_id (struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t reply_type,
+ 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 reply, mach_msg_type_name_t reply_type,
+ 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;
+
+ if (cred->pi.class == pty_class)
+ return pty_io_select (cred, reply, type, idtag);
+
+ /* 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;
+ }
+
+ ports_interrupt_self_on_port_death (cred, reply);
+ if (hurd_condition_wait (&select_alert, &global_lock))
+ {
+ *type = 0;
+ mutex_unlock (&global_lock);
+ return EINTR;
+ }
+ }
+}
+
+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;
+}
+
+static void
+report_sig_start ()
+{
+ sigs_in_progress++;
+}
+
+static void
+report_sig_end ()
+{
+ sigs_in_progress--;
+ if ((sigs_in_progress == 0) && input_sig_wakeup)
+ {
+ input_sig_wakeup = 0;
+ condition_broadcast (&input_sig_wait);
+ }
+}
+
+/* Call all the scheduled async I/O handlers. DIR is a mask of O_READ &
+ O_WRITE; the asyncs will only be called if output is possible in one of
+ the directions given in DIR. */
+void
+call_asyncs (int dir)
+{
+ struct async_req *ar, *nxt, **prevp;
+ mach_port_t err;
+
+ /* If nobody wants async messages, don't bother further. */
+ if (!(termflags & ICKY_ASYNC) && !async_requests)
+ return;
+
+ if ((!(dir & O_READ) || qsize (inputq) == 0)
+ && (!(dir & O_WRITE) && qavail (outputq) == 0))
+ /* Output isn't possible in the desired directions. */
+ return;
+
+ if ((termflags & ICKY_ASYNC) && !(termflags & NO_OWNER))
+ {
+ report_sig_start ();
+ mutex_unlock (&global_lock);
+ hurd_sig_post (foreground_id, SIGIO, async_icky_id);
+ mutex_lock (&global_lock);
+ report_sig_end ();
+ }
+
+ for (ar = async_requests, prevp = &async_requests;
+ ar;
+ ar = nxt)
+ {
+ nxt = ar->next;
+ err = nowait_msg_sig_post (ar->notify, SIGIO, 0, 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);
+ report_sig_start ();
+ mutex_unlock (&global_lock);
+ hurd_sig_post (foreground_id, signo, right);
+ mutex_lock (&global_lock);
+ report_sig_end ();
+ mach_port_deallocate (mach_task_self (), right);
+ }
+}
+
+void
+report_carrier_off ()
+{
+ clear_queue (inputq);
+ (*bottom->notice_input_flushed) ();
+ drop_output ();
+ termflags |= NO_CARRIER;
+ if (!(termstate.c_cflag & CLOCAL))
+ send_signal (SIGHUP);
+}
+
+void
+report_carrier_on ()
+{
+ termflags &= ~NO_CARRIER;
+ condition_broadcast (&carrier_alert);
+}
+
+void
+report_carrier_error (error_t err)
+{
+ carrier_error = err;
+ 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;
+
+ if (!cred->po->cntl->hook)
+ {
+ ports_port_deref (cred);
+ return ENOENT;
+ }
+
+ strcpy (name, (char *)cred->po->cntl->hook);
+
+ ports_port_deref (cred);
+ return 0;
+}
+
+kern_return_t
+S_term_set_nodename (io_t arg,
+ char *name)
+{
+ error_t err = 0;
+ struct trivfs_protid *cred = ports_lookup_port (term_bucket, arg, tty_class);
+ if (!cred)
+ return EOPNOTSUPP;
+
+ if (strcmp (name, (char *)cred->po->cntl->hook) != 0)
+ err = EINVAL;
+
+ ports_port_deref (cred);
+ return err;
+}
+
+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_peername (io_t arg,
+ char *name)
+{
+ struct trivfs_protid *cred = ports_lookup_port (term_bucket, arg, 0);
+ struct trivfs_control *peer;
+
+ if (!cred || (cred->pi.class != tty_class && cred->pi.class != pty_class))
+ {
+ if (cred)
+ ports_port_deref (cred);
+ return EOPNOTSUPP;
+ }
+
+ peer = (cred->pi.class == tty_class) ? ptyctl : termctl;
+
+ if (bottom != &ptyio_bottom || !peer->hook)
+ {
+ ports_port_deref (cred);
+ return ENOENT;
+ }
+
+ strcpy (name, (char *) peer->hook);
+ ports_port_deref (cred);
+
+ return 0;
+}
+
+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);
+ if (bottom == &devio_bottom)
+ *ttype = TERM_ON_MACHDEV;
+ else
+ *ttype = TERM_ON_MASTERPTY;
+ 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;
+}