diff options
author | Michael I. Bushnell <mib@gnu.org> | 1995-08-31 20:01:44 +0000 |
---|---|---|
committer | Michael I. Bushnell <mib@gnu.org> | 1995-08-31 20:01:44 +0000 |
commit | 941c81aa67cdaf9c63f4835e61d33c182db5237b (patch) | |
tree | 05f62a9fc70a5eb5cf58d54bd98ffe11ee7cb8f7 /term | |
parent | 8832237df37b394f990e0746f2702f6400d1359e (diff) |
Initial revision
Diffstat (limited to 'term')
-rw-r--r-- | term/Makefile | 45 | ||||
-rw-r--r-- | term/devio.c | 554 | ||||
-rw-r--r-- | term/main.c | 147 | ||||
-rw-r--r-- | term/munge.c | 725 | ||||
-rw-r--r-- | term/term.h | 264 | ||||
-rw-r--r-- | term/users.c | 1659 |
6 files changed, 3394 insertions, 0 deletions
diff --git a/term/Makefile b/term/Makefile new file mode 100644 index 00000000..40c06ba4 --- /dev/null +++ b/term/Makefile @@ -0,0 +1,45 @@ +# +# 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. + +dir := term +makemode := server + +target = term +SRCS = devio.c munge.c users.c main.c +LCLHDRS = term.h + +OBJS = $(subst .c,.o,$(SRCS)) termServer.o device_replyServer.o tioctlServer.o ourmsgUser.o ../libtrivfs/libtrivfs.a ../libports/libports.a ../libihash/libihash.a ../libthreads/libthreads.a + +include ../Makeconf + +# The reason for -Dout= is to prevent errors for get_init_port, +# get_init_ports, get_init_int, get_init_ints, get_dtable, and get_fd. +# We don't use those, so we're safe in breaking them. +ourmsg_U.h ourmsgUser.c: ourmsg.defs ../hurd/hurd_types.defs + $(CPP) $(CPPFLAGS) -Droutine=simpleroutine -Dout= $< \ + | $(MIGCOM) -prefix nowait_ -server /dev/null \ + -user ourmsgUser.c -header ourmsg_U.h + mv ourmsg_U.h tmp.h + sed -e 's/_msg_user_/_ourmsg_user/' < tmp.h > ourmsg_U.h + rm -f tmp.h + +ourmsg.defs: $(includedir)/hurd/msg.defs + rm -f $@ + ln -s $< $@ diff --git a/term/devio.c b/term/devio.c new file mode 100644 index 00000000..e24e34fb --- /dev/null +++ b/term/devio.c @@ -0,0 +1,554 @@ +/* + 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 <device/device.h> +#include <device/device_request.h> +#include <hurd/ports.h> +#include <string.h> +#include <sys/types.h> +#include <hurd.h> +#include <stdio.h> + +#undef B50 +#undef B75 +#undef B110 +#undef B134 +#undef B150 +#undef B200 +#undef B300 +#undef B600 +#undef B1200 +#undef B1800 +#undef B2400 +#undef B4800 +#undef B9600 +#undef EXTA +#undef EXTB +#include <device/tty_status.h> + +/* This flag is set if there is an outstanding device_write. */ +static int output_pending; + +/* This flag is set if there is an outstanding device_read. */ +static int input_pending; + +/* This flag is set if there is an outstanding device_open. */ +static int open_pending; + +static char pending_output[IO_INBAND_MAX]; +static int npending_output; + +static struct port_class *phys_reply_class; + +/* The Mach device_t representing the terminal. */ +static device_t phys_device = MACH_PORT_NULL; + +/* The ports we get replies on for device calls. */ +static mach_port_t phys_reply_writes = MACH_PORT_NULL; +static mach_port_t phys_reply = MACH_PORT_NULL; + +/* The port-info structs. */ +static struct port_info *phys_reply_writes_pi; +static struct port_info *phys_reply_pi; + +static device_t device_master; + +static void init_devio (void) __attribute__ ((constructor)); +static void +init_devio () +{ + mach_port_t host_priv; + errno = get_privileged_ports (&host_priv, &device_master); + if (errno) + { + perror ("Getting priviliged ports"); + exit (1); + } + mach_port_deallocate (mach_task_self (), host_priv); + phys_reply_class = ports_create_class (0, 0); +} + +/* XXX Convert a real speed to a bogus Mach speed. Return + -1 if the real speed was bogus, else 0. */ +static int +real_speed_to_bogus_speed (int rspeed, int *bspeed) +{ + switch (rspeed) + { + case 0: + *bspeed = B0; + break; + case 50: + *bspeed = B50; + break; + case 75: + *bspeed = B75; + break; + case 110: + *bspeed = B110; + break; + case 134: + *bspeed = B134; + break; + case 150: + *bspeed = B150; + break; + case 200: + *bspeed = B200; + break; + case 300: + *bspeed = B300; + break; + case 600: + *bspeed = B600; + break; + case 1200: + *bspeed = B1200; + break; + case 1800: + *bspeed = B1800; + break; + case 2400: + *bspeed = B2400; + break; + case 4800: + *bspeed = B4800; + break; + case 9600: + *bspeed = B9600; + break; + case 19200: + *bspeed = EXTA; + break; + case 28400: + *bspeed = EXTB; + break; + default: + return -1; + } + return 0; +} + +/* XXX Convert a bogus speed to a real speed. */ +static int +bogus_speed_to_real_speed (int bspeed) +{ + switch (bspeed) + { + case B0: + default: + return 0; + case B50: + return 50; + case B75: + return 75; + case B110: + return 110; + case B134: + return 134; + case B150: + return 150; + case B200: + return 200; + case B300: + return 300; + case B600: + return 600; + case B1200: + return 1200; + case B1800: + return 1800; + case B2400: + return 2400; + case B4800: + return 4800; + case B9600: + return 9600; + case EXTA: + return 19200; + case EXTB: + return 38400; + } +} + + +/* If there are characters on the output queue and no + pending output requests, then send them. */ +void +start_output () +{ + char *cp; + int size; + error_t err; + + size = qsize (outputq); + + if (!size || output_pending) + return; + + /* Copy characters onto PENDING_OUTPUT, not bothering + those already there. */ + + if (size + npending_output > IO_INBAND_MAX) + size = IO_INBAND_MAX - npending_output; + + cp = pending_output + npending_output; + npending_output += size; + + while (size--) + *cp++ = dequeue (outputq); + + /* Submit all the outstanding characters to the device. */ + /* The D_NOWAIT flag does not, in fact, prevent blocks. Instead, + it merely causes D_WOULD_BLOCK errors when carrier is down... + whee.... */ + err = device_write_request_inband (phys_device, phys_reply_writes, D_NOWAIT, + 0, pending_output, npending_output); + + if (err == MACH_SEND_INVALID_DEST) + desert_dtr (); + else if (!err) + output_pending = 1; +} + +error_t +device_write_reply_inband (mach_port_t replypt, + error_t return_code, + int amount) +{ + if (replypt != phys_reply_writes) + return EOPNOTSUPP; + + mutex_lock (&global_lock); + + output_pending = 0; + + if (return_code == 0) + { + if (amount >= npending_output) + { + npending_output = 0; + condition_broadcast (outputq->wait); + } + else + { + /* Copy the characters that didn't get output + to the front of the array. */ + npending_output -= amount; + memmove (pending_output, pending_output + amount, npending_output); + } + start_output (); + } + else if (return_code == D_WOULD_BLOCK) + /* Carrier has dropped. */ + desert_dtr (); + else + start_output (); + + mutex_unlock (&global_lock); + return 0; +} + +error_t +device_read_reply_inband (mach_port_t replypt, + error_t error_code, + char *data, + u_int datalen) +{ + int i, flush; + error_t err; + + if (replypt != phys_reply) + return EOPNOTSUPP; + + mutex_lock (&global_lock); + + input_pending = 0; + + if (!error_code && (termstate.c_cflag & CREAD)) + for (i = 0; i < datalen; i++) + { + flush = input_character (data[i]); + if (flush) + break; + } + else if (error_code == D_WOULD_BLOCK) + { + desert_dtr (); + mutex_unlock (&global_lock); + return 0; + } + + /* D_NOWAIT does not actually prevent blocks; it merely causes + D_WOULD_BLOCK errors when carrier drops. */ + err = device_read_request_inband (phys_device, phys_reply, D_NOWAIT, + 0, vm_page_size); + if (err) + desert_dtr (); + else + input_pending = 1; + + mutex_unlock (&global_lock); + return 0; +} + +void +set_break () +{ + device_set_status (phys_device, TTY_SET_BREAK, 0, 0); +} + +void +clear_break () +{ + device_set_status (phys_device, TTY_CLEAR_BREAK, 0, 0); +} + +void +abandon_physical_output () +{ + int val = D_WRITE; + + mach_port_deallocate (mach_task_self (), phys_reply_writes); + ports_reallocate_port (phys_reply_writes_pi); + phys_reply_writes = ports_get_right (phys_reply_writes_pi); + mach_port_insert_right (mach_task_self (), phys_reply_writes, + phys_reply_writes, MACH_MSG_TYPE_MAKE_SEND); + + device_set_status (phys_device, TTY_FLUSH, &val, TTY_FLUSH_COUNT); + npending_output = 0; + output_pending = 0; +} + +int +pending_output_size () +{ + /* Unfortunately, there's no way to get the amount back from Mach + that has actually been written from this... */ + return npending_output; +} + +void +desert_dtr () +{ + /* This will work, because we set the TF_HUPCLS bit earlier. */ + device_close (phys_device); + mach_port_deallocate (mach_task_self (), phys_device); + phys_device = MACH_PORT_NULL; + + mach_port_deallocate (mach_task_self (), phys_reply); + mach_port_deallocate (mach_task_self (), phys_reply_writes); + phys_reply = phys_reply_writes = MACH_PORT_NULL; + + ports_port_deref (phys_reply_pi); + ports_port_deref (phys_reply_writes_pi); + phys_reply_pi = phys_reply_writes_pi = 0; + + report_carrier_off (); +} + +error_t +assert_dtr () +{ + error_t err; + + if (open_pending || (phys_device != MACH_PORT_NULL)) + return 0; + + assert (phys_reply == MACH_PORT_NULL); + assert (phys_reply_pi == 0); + phys_reply_pi = ports_allocate_port (term_bucket, sizeof (struct port_info), + phys_reply_class); + phys_reply = ports_get_right (phys_reply_pi); + mach_port_insert_right (mach_task_self (), phys_reply, phys_reply, + MACH_MSG_TYPE_MAKE_SEND); + + err = device_open_request (device_master, phys_reply, + D_READ|D_WRITE, pterm_name); + if (err) + { + mach_port_deallocate (mach_task_self (), phys_reply); + phys_reply = MACH_PORT_NULL; + ports_port_deref (phys_reply_pi); + phys_reply_pi = 0; + } + return err; +} + + +kern_return_t +device_open_reply (mach_port_t replyport, + int returncode, + mach_port_t device) +{ + struct tty_status ttystat; + int count = TTY_STATUS_COUNT; + error_t err; + + if (replyport != phys_reply) + return EOPNOTSUPP; + + mutex_lock (&global_lock); + + open_pending = 0; + + if (returncode != 0) + { + /* Bogus. */ + report_carrier_on (); + report_carrier_off (); + + mach_port_deallocate (mach_task_self (), phys_reply); + phys_reply = MACH_PORT_NULL; + ports_port_deref (phys_reply_pi); + phys_reply_pi = 0; + mutex_unlock (&global_lock); + return 0; + } + + assert (phys_device == MACH_PORT_NULL); + assert (phys_reply_writes == MACH_PORT_NULL); + assert (phys_reply_writes_pi == 0); + phys_device = device; + phys_reply_writes_pi = ports_allocate_port (term_bucket, + sizeof (struct port_info), + phys_reply_class); + phys_reply_writes = ports_get_right (phys_reply_writes_pi); + mach_port_insert_right (mach_task_self (), phys_reply_writes, + phys_reply_writes, MACH_MSG_TYPE_MAKE_SEND); + + device_get_status (phys_device, TTY_STATUS, + (dev_status_t)&ttystat, &count); + ttystat.tt_breakc = 0; + ttystat.tt_flags = TF_ANYP | TF_LITOUT | TF_NOHANG | TF_HUPCLS; + device_set_status (phys_device, TTY_STATUS, + (dev_status_t)&ttystat, TTY_STATUS_COUNT); + + err = device_read_request_inband (phys_device, phys_reply, D_NOWAIT, + 0, vm_page_size); + input_pending = 1; + report_carrier_on (); + if (err) + desert_dtr (); + + mutex_unlock (&global_lock); + + return 0; +} + +/* Adjust physical state on the basis of the terminal state. + Where it isn't possible, mutate terminal state to match + reality. */ +void +set_bits () +{ + struct tty_status ttystat; + int cnt = TTY_STATUS_COUNT; + + assert (!(termstate.c_cflag & CIGNORE)); + + if (phys_device == MACH_PORT_NULL) + return; + + /* Find the current state. */ + device_get_status (phys_device, TTY_STATUS, (dev_status_t) &ttystat, &cnt); + if (termstate.__ispeed) + real_speed_to_bogus_speed (termstate.__ispeed, &ttystat.tt_ispeed); + if (termstate.__ospeed) + real_speed_to_bogus_speed (termstate.__ospeed, &ttystat.tt_ospeed); + + /* Try and set it. */ + device_set_status (phys_device, TTY_STATUS, + (dev_status_t) &ttystat, TTY_STATUS_COUNT); + + /* And now make termstate match reality. */ + cnt = TTY_STATUS_COUNT; + device_get_status (phys_device, TTY_STATUS, (dev_status_t) &ttystat, &cnt); + termstate.__ispeed = bogus_speed_to_real_speed (ttystat.tt_ispeed); + termstate.__ospeed = bogus_speed_to_real_speed (ttystat.tt_ospeed); + + /* Mach forces us to use the normal stop bit convention: + two bits at 110 bps; 1 bit otherwise. */ + if (termstate.__ispeed == 110) + termstate.c_cflag |= CSTOPB; + else + termstate.c_cflag &= ~CSTOPB; + + /* Mach only supports 8 bit channels. So wark the CSIZE to conform. */ + termstate.c_cflag = ((termstate.c_cflag & ~CSIZE) + | ((termstate.c_cflag & PARENB) ? CS7 : CS8)); +} + +void +mdmctl (int how, int bits) +{ + int oldbits, newbits; + int cnt; + if ((how == MDMCTL_BIS) || (how == MDMCTL_BIC)) + { + cnt = TTY_MODEM_COUNT; + device_get_status (phys_device, TTY_MODEM, + (dev_status_t) &oldbits, &cnt); + if (cnt < TTY_MODEM_COUNT) + oldbits = 0; /* what else can we do? */ + } + + if (how == MDMCTL_BIS) + newbits = (oldbits | bits); + else if (how == MDMCTL_BIC) + newbits = (oldbits &= ~bits); + else + newbits = bits; + + device_set_status (phys_device, TTY_MODEM, + (dev_status_t) &newbits, TTY_MODEM_COUNT); +} + +int +mdmstate () +{ + int bits, cnt; + + cnt = TTY_MODEM_COUNT; + device_get_status (phys_device, TTY_MODEM, (dev_status_t) &bits, &cnt); + if (cnt != TTY_MODEM_COUNT) + return 0; + else + return bits; +} + +/* Unused stubs */ +kern_return_t +device_read_reply (mach_port_t port, + kern_return_t retcode, + io_buf_ptr_t data, + mach_msg_type_number_t cnt) +{ + return EOPNOTSUPP; +} + +kern_return_t +device_write_reply (mach_port_t replyport, + kern_return_t retcode, + int written) +{ + return EOPNOTSUPP; +} diff --git a/term/main.c b/term/main.c new file mode 100644 index 00000000..b1e384c3 --- /dev/null +++ b/term/main.c @@ -0,0 +1,147 @@ +/* + 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 <hurd.h> +#include <fcntl.h> +#include <hurd/trivfs.h> +#include <stdio.h> +#include <hurd/fsys.h> +#include <string.h> + +int trivfs_fstype = FSTYPE_TERM; +int trivfs_fsid = 0; /* pid?? */ +int trivfs_support_read = 1; +int trivfs_support_write = 1; +int trivfs_support_exec = 0; + +int trivfs_allow_open = O_READ|O_WRITE; + +struct port_class *trivfs_protid_portclasses[1]; +struct port_class *trivfs_cntl_portclasses[1]; +int trivfs_protid_nportclasses = 1; +int trivfs_cntl_nportclasses = 1; + +int +demuxer (mach_msg_header_t *inp, mach_msg_header_t *outp) +{ + extern int term_server (mach_msg_header_t *, mach_msg_header_t *); + extern int tioctl_server (mach_msg_header_t *, mach_msg_header_t *); + extern int device_reply_server (mach_msg_header_t *, mach_msg_header_t *); + + return (trivfs_demuxer (inp, outp) + || term_server (inp, outp) + || tioctl_server (inp, outp) + || device_reply_server (inp, outp)); +} + +int +main (int argc, char **argv) +{ + file_t file; + mach_port_t ctlport, bootstrap; + + term_bucket = ports_create_bucket (); + + tty_cntl_class = ports_create_class (trivfs_clean_cntl, 0); + tty_class = ports_create_class (trivfs_clean_protid, 0); + cttyid_class = ports_create_class (0, 0); + + trivfs_protid_portclasses[0] = tty_class; + trivfs_cntl_portclasses[0] = tty_cntl_class; + + task_get_bootstrap_port (mach_task_self (), &bootstrap); + + if (bootstrap == MACH_PORT_NULL) + { + if (argc != 3) + { + fprintf (stderr, "Usage: term mach-dev-name ttyname\n"); + exit (1); + } + pterm_name = argv[1]; + nodename = argv[2]; + + /* Install control port in filesystem */ + file = file_name_lookup (nodename, O_CREAT|O_NOTRANS, 0666); + if (file == MACH_PORT_NULL) + { + perror (nodename); + exit (1); + } + + ctlport = trivfs_handle_port (file, tty_cntl_class, term_bucket, + tty_class, term_bucket); + termctl = ports_lookup_port (term_bucket, ctlport, tty_cntl_class); + assert (termctl); + + errno = file_set_translator (file, 0, FS_TRANS_EXCL | FS_TRANS_SET, + 0, 0, 0, ctlport, MACH_MSG_TYPE_MAKE_SEND); + if (errno) + { + perror ("setting translator"); + exit (1); + } + } + else + { + if (argc < 2 || argc > 3) + { + fprintf (stderr, "Usage (as translator): term mach-dev-name [ttyname]\n"); + exit (1); + } + pterm_name = argv[1]; + nodename = (argv[2] ?: ""); + + ctlport = trivfs_handle_port (MACH_PORT_NULL, tty_cntl_class, + term_bucket, tty_class, term_bucket); + errno = fsys_startup (bootstrap, ctlport, MACH_MSG_TYPE_MAKE_SEND, + &file); + if (errno) + { + perror ("Starting translator"); + exit (1); + } + termctl = ports_lookup_port (term_bucket, ctlport, 0); + assert (termctl); + termctl->underlying = file; + } + + bzero (&termstate, sizeof (termstate)); + termflags = NO_CARRIER; + output_psize = 0; + mutex_init (&global_lock); + + inputq = create_queue (256, 100, 300); + rawq = create_queue (256, 100, 300); + outputq = create_queue (256, 100, 300); + + condition_init (&carrier_alert); + condition_init (&select_alert); + condition_implies (inputq->wait, &select_alert); + condition_implies (outputq->wait, &select_alert); + + /* Launch */ + ports_manage_port_operations_multithread (term_bucket, demuxer, 0, 0, + 0, MACH_PORT_NULL); + + return 0; +} + diff --git a/term/munge.c b/term/munge.c new file mode 100644 index 00000000..31f0bdee --- /dev/null +++ b/term/munge.c @@ -0,0 +1,725 @@ +/* + 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 <termios.h> +#include <unistd.h> +#include <signal.h> +#include <ctype.h> +#include <string.h> + +/* Number of characters in the rawq which have been + echoed continuously without intervening output. */ +int echo_qsize; + +/* Where the output_psize was when echo_qsize was last 0. */ +int echo_pstart; + +/* Actually drop character onto output queue. This should be the + only place where we actually enqueue characters on the output queue; + it is responsible for keeping track of cursor positions. */ +inline void +poutput (int c) +{ + if (termflags & FLUSH_OUTPUT) + return; /* never mind */ + + if ((c >= ' ') && (c < '\177')) + output_psize++; + else if (c == '\r') + output_psize = 0; + else if (c == '\t') + output_psize += (output_psize + 8) % 8; + else if (c == '\b') + output_psize--; + + enqueue (&outputq, c); +} + +/* Place C on output queue, doing normal output processing. + This is used for write, never for echo. */ +void +output_character (int c) +{ + int oflag = termstate.c_oflag; + + /* One might think we should turn of INHDERASE here, but, no + in U*x it is only turned off by echoed characters. + See echo_char in input.c. */ + if (oflag & OPOST) + { + /* Characters we write specially */ + if ((oflag & ONLCR) && c == '\n') + { + poutput ('\r'); + poutput ('\n'); + } + else if ((oflag & OXTABS) && c == '\t') + { + poutput (' '); + while (output_psize % 8) + poutput (' '); + } + else if ((oflag & ONOEOT) && c == CHAR_EOT) + ; + else if ((oflag & OTILDE) && c == '~') + { + poutput ('\\'); + poutput ('`'); + } + else if ((oflag & OLCASE) && isalpha (c)) + { + if (isupper (c)) + poutput ('\\'); + else + c = toupper (c); + poutput (c); + } + else + poutput (c); + } + else + poutput (c); + echo_qsize = 0; + echo_pstart = output_psize; +} + +/* Report the width of character C as printed by output_character, + if output_psize were at LOC. . */ +int +output_width (int c, int loc) +{ + int oflag = termstate.c_oflag; + + if (oflag & OPOST) + { + if ((oflag & OTILDE) && c == '~') + return 2; + if ((oflag & OLCASE) && isalpha (c) && isupper (c)) + return 2; + } + if (c == '\t') + { + int n = loc + 1; + while (n % 8) + n++; + return n; + } + if ((c >= ' ') && (c < '\177')) + return 1; + return 0; +} + + + +/* For ICANON mode, this holds the edited line. */ +struct queue *rawq; + +/* For each character in this table, if the element is set, then + the character is EVEN */ +char const char_parity[] = +{ + 1, 0, 0, 1, 0, 1, 1, 0, /* nul - bel */ + 0, 1, 1, 0, 1, 0, 0, 1, /* bs - si */ + 0, 1, 1, 0, 1, 0, 0, 1, /* dle - etb */ + 1, 0, 0, 1, 0, 1, 1, 0, /* can - us */ + 0, 1, 1, 0, 1, 0, 0, 1, /* sp - ' */ + 1, 0, 0, 1, 0, 1, 1, 0, /* ( - / */ + 1, 0, 0, 1, 0, 1, 1, 0, /* 0 - 7 */ + 0, 1, 1, 0, 1, 0, 0, 1, /* 8 - ? */ + 0, 1, 1, 0, 1, 0, 0, 1, /* @ - G */ + 1, 0, 0, 1, 0, 1, 1, 0, /* H - O */ + 1, 0, 0, 1, 0, 1, 1, 0, /* P - W */ + 0, 1, 1, 0, 1, 0, 0, 1, /* X - _ */ + 1, 0, 0, 1, 0, 1, 1, 0, /* ` - g */ + 0, 1, 1, 0, 1, 0, 0, 1, /* h - o */ + 0, 1, 1, 0, 1, 0, 0, 1, /* p - w */ + 1, 0, 0, 1, 0, 1, 1, 0, /* x - del */ +}; +#define checkevenpar(c) (((c)&0x80) \ + ? !char_parity[(c)&0x7f] \ + : char_parity[(c)&0x7f]) +#define checkoddpar(c) (((c)&0x80) \ + ? char_parity[(c)&0x7f] \ + : !char_parity[(c)&0x7f]) + + + +/* These functions are used by both echo and erase code */ + +/* Tell if we should echo a character at all */ +static inline int +echo_p (char c, int quoted) +{ + return ((termstate.c_lflag & ECHO) + || (c == '\n' && (termstate.c_lflag & ECHONL) && !quoted)); +} + +/* Tell if this character deserves double-width cntrl processing */ +static inline int +echo_double (char c, int quoted) +{ + return (iscntrl (c) && (termstate.c_lflag & ECHOCTL) + && !((c == '\n' || c == '\t') && !quoted)); +} + +/* Do a single C-h SPC C-h sequence */ +static inline void +write_erase_sequence () +{ + poutput ('\b'); + poutput (' '); + poutput ('\b'); +} + +/* Echo a single character to the output */ +/* If this is an echo of a character which is being hard-erased, + set hderase. If this is a newline or tab which should not receive + their normal special processing, set quoted. */ +static void +echo_char (char c, int hderase, int quoted) +{ + echo_qsize++; + + if (echo_p (c, quoted)) + { + if (!hderase && (termflags & INSIDE_HDERASE)) + { + poutput ('/'); + termflags &= ~INSIDE_HDERASE; + echo_qsize = 0; + echo_pstart = output_psize; + } + + if (hderase && !(termflags & INSIDE_HDERASE)) + { + poutput ('\\'); + termflags |= INSIDE_HDERASE; + } + + /* Control characters that should use caret-letter */ + if (echo_double (c, quoted)) + { + poutput ('^'); + poutput (c + ('A' - CHAR_SOH)); + } + else + poutput (c); + } +} + + +/* Re-echo the current rawq preceded by the VREPRINT char. */ +static inline void +reprint_line () +{ + short *cp; + + if (termstate.c_cc[VREPRINT] != _POSIX_VDISABLE) + echo_char (termstate.c_cc[VREPRINT], 0, 0); + else + echo_char (CHAR_DC3, 0, 0); + echo_char ('\n', 0, 0); + + echo_qsize = 0; + echo_pstart = output_psize; + + for (cp = rawq->cs; cp != rawq->ce; cp++) + echo_char (unquote_char (*cp), 0, char_quoted_p (*cp)); +} + +/* Erase a single character off the end of the rawq, and delete + it from the screen appropriately. Set ERASE_CHAR if this + is being done by the VERASE character, to that character. */ +static void +erase_1 (char erase_char) +{ + int quoted; + char c; + quoted_char cq; + + if (qsize (rawq) == 0) + return; + + cq = queue_erase (rawq); + c = unquote_char (cq); + quoted = char_quoted_p (cq); + + if (!echo_p (c, quoted)) + return; + + /* The code for WERASE knows that we echo erase_char iff + !ECHOPRT && !ECHO. */ + + if (echo_qsize--) + { + if (termstate.c_lflag & ECHOPRT) + echo_char (c, 1, quoted); + else if (!(termstate.c_lflag & ECHOE) && erase_char) + echo_char (erase_char, 0, 0); + else + { + int nerase; + + if (echo_double (c, quoted)) + nerase = 2; + else if (c == '\t') + { + quoted_char *cp; + int loc = echo_pstart; + + for (cp = rawq->ce - echo_qsize; cp != rawq->ce; cp++) + loc += (echo_double (unquote_char (*cp), char_quoted_p (*cp)) + ? 2 + : output_width (*cp, loc)); + nerase = output_psize - loc; + } + else + nerase = output_width (c, output_psize); + + while (nerase--) + write_erase_sequence (); + } + if (echo_qsize == 0) + assert (echo_pstart == output_psize); + } + else + reprint_line (); +} + + +/* Place newly input character C on the input queue. If all remaining + pending input characters should be dropped, return 1; else return + 0. */ +int +input_character (int c) +{ + int lflag = termstate.c_lflag; + int iflag = termstate.c_iflag; + int cflag = termstate.c_cflag; + cc_t *cc = termstate.c_cc; + struct queue **qp = (lflag & ICANON) ? &rawq : &inputq; + int flush = 0; + + /* Handle parity errors */ + if ((iflag & INPCK) + && ((cflag & PARODD) ? checkoddpar (c) : checkevenpar (c))) + { + if (iflag & IGNPAR) + goto alldone; + else if (iflag & PARMRK) + { + enqueue_quote (qp, CHAR_USER_QUOTE); + enqueue_quote (qp, '\0'); + enqueue_quote (qp, c); + goto alldone; + } + else + c = 0; + } + + /* Check to see if we should send IXOFF */ + if ((iflag & IXOFF) + && !qavail (*qp) + && (cc[VSTOP] != _POSIX_VDISABLE)) + { + poutput (cc[VSTOP]); + termflags |= SENT_VSTOP; + } + + /* Character mutations */ + if (!(iflag & ISTRIP) && (iflag & PARMRK) && (c == CHAR_USER_QUOTE)) + enqueue_quote (qp, CHAR_USER_QUOTE); /* cause doubling */ + + if (iflag & ISTRIP) + c &= 0x7f; + + /* Handle LNEXT right away */ + if (termflags & LAST_LNEXT) + { + enqueue_quote (qp, c); + echo_char (c, 0, 1); + termflags &= ~LAST_LNEXT; + } + + /* Mutate ILCASE */ + if ((iflag & ILCASE) && isalpha(c)) + { + if (termflags & LAST_SLASH) + erase_1 (0); /* remove the slash from input */ + else + c = isupper(c) ? tolower (c) : c; + } + + /* IEXTEN control chars */ + if (lflag & IEXTEN) + { + if (CCEQ (cc[VLNEXT], c)) + { + if (lflag & ECHO) + { + poutput ('^'); + poutput ('\b'); + } + termflags |= LAST_LNEXT; + goto alldone; + } + + if (CCEQ (cc[VDISCARD], c)) + { + if (termflags & FLUSH_OUTPUT) + termflags &= ~FLUSH_OUTPUT; + else + { + drop_output (); + termflags |= FLUSH_OUTPUT; + } + } + } + + /* Signals */ + if (lflag & ISIG) + { + if (CCEQ (cc[VINTR], c) || CCEQ (cc[VQUIT], c)) + { + if (!(lflag & NOFLSH)) + { + drop_output (); + flush = 1; + } + echo_char (c, 0, 0); + send_signal (CCEQ (cc[VINTR], c) ? SIGINT : SIGQUIT); + goto alldone; + } + + if (CCEQ (cc[VSUSP], c)) + { + if (!(lflag & NOFLSH)) + flush = 1; + echo_char (c, 0, 0); + send_signal (SIGTSTP); + goto alldone; + } + } + + /* IXON */ + if (iflag & IXON) + { + if (CCEQ (cc[VSTOP], c)) + { + termflags |= USER_OUTPUT_SUSP; + + if (!(CCEQ(cc[VSTART], c))) /* toggle if VSTART == VSTOP */ + /* Alldone code always turns off USER_OUTPUT_SUSP. */ + goto alldone; + + return flush; + } + if (CCEQ (cc[VSTART], c)) + goto alldone; + } + + /* Newline and carriage-return frobbing */ + if (c == '\r') + { + if (iflag & ICRNL) + c = '\n'; + else if (iflag & IGNCR) + goto alldone; + } + else if ((c == '\n') && (iflag & INLCR)) + c = '\r'; + + /* Canonical mode processing */ + if (lflag & ICANON) + { + if (CCEQ (cc[VERASE], c)) + { + if (qsize(rawq)) + erase_1 (c); + if (!(termflags & LAST_SLASH) + || !(lflag & IEXTEN)) + goto alldone; + } + + if (CCEQ (cc[VKILL], c)) + { + if (!(termflags & LAST_SLASH) + || !(lflag & IEXTEN)) + { + if ((lflag & ECHOKE) && !(lflag & ECHOPRT) + && (echo_qsize == qsize (rawq))) + { + while (output_psize > echo_pstart) + write_erase_sequence (); + } + else + { + echo_char (c, 0, 0); + if ((lflag & ECHOK) || (lflag & ECHOKE)) + echo_char ('\n', 0, 0); + } + clear_queue (rawq); + echo_qsize = 0; + echo_pstart = output_psize; + termflags &= ~(LAST_SLASH|LAST_LNEXT|INSIDE_HDERASE); + goto alldone; + } + else + erase_1 (0); /* remove \ */ + } + + if (CCEQ (cc[VWERASE], c)) + { + /* If we are not going to echo the erase, then + echo a WERASE character right now. (If we + passed it to erase_1; it would echo it multiple + times.) */ + if (!(lflag & (ECHOPRT|ECHOE))) + echo_char (cc[VWERASE], 0, 1); + + /* Erase whitespace */ + while (qsize (rawq) && isblank (unquote_char (rawq->ce[-1]))) + erase_1 (0); + + /* Erase word. */ + if (lflag & ALTWERASE) + /* For ALTWERASE, we erase back to the first blank */ + while (qsize (rawq) && !isblank (unquote_char (rawq->ce[-1]))) + erase_1 (0); + else + /* For regular WERASE, we erase back to the first nonalpha/_ */ + while (qsize (rawq) && !isblank (unquote_char (rawq->ce[-1])) + && (isalnum (unquote_char (rawq->ce[-1])) + || (unquote_char (rawq->ce[-1]) != '_'))) + erase_1 (0); + + goto alldone; + } + + if (CCEQ (cc[VREPRINT], c) && (lflag & IEXTEN)) + { + reprint_line (); + goto alldone; + } + + if (CCEQ (cc[VSTATUS], c) && (lflag & ISIG) && (lflag & IEXTEN)) + { + send_signal (SIGINFO); + goto alldone; + } + } + + /* Now we have a character intended as input. See if it will fit. */ + if (!qavail (*qp)) + { + if (iflag & IMAXBEL) + poutput ('\a'); + else + { + /* Drop everything */ + drop_output (); + flush = 1; + } + goto alldone; + } + + /* Echo */ + echo_char (c, 0, 0); + if (CCEQ (cc[VEOF], c) && (lflag & ECHO)) + { + /* Special bizzare echo processing for VEOF character. */ + int n; + n = echo_double (c, 0) ? 2 : output_width (c, output_psize); + while (n--) + poutput ('\b'); + } + + /* Put character on input queue */ + enqueue (qp, c); + + /* Check for break characters in canonical input processing */ + if (lflag & ICANON) + { + if (CCEQ (cc[VEOL], c) + || CCEQ (cc[VEOL2], c) + || CCEQ (cc[VEOF], c) + || c == '\n') + /* Make input available */ + while (qsize (rawq)) + enqueue (&inputq, dequeue (rawq)); + } + +alldone: + /* Restart output */ + if ((iflag & IXANY) || (CCEQ (cc[VSTART], c))) + termflags &= ~USER_OUTPUT_SUSP; + start_output (); + + return flush; +} + + +/* This is called by the lower half when a break is received. */ +void +input_break () +{ + struct queue **qp = termstate.c_lflag & ICANON ? &rawq : &inputq; + + /* Don't do anything if IGNBRK is set. */ + if (termstate.c_iflag & IGNBRK) + return; + + /* If BRKINT is set, then flush queues and send SIGINT. */ + if (termstate.c_iflag & BRKINT) + { + drop_output (); + /* XXX drop pending input How?? */ + send_signal (SIGINT); + return; + } + + /* A break is then read as a null byte; marked specially if PARMRK is set. */ + if (termstate.c_iflag & PARMRK) + { + enqueue_quote (qp, CHAR_USER_QUOTE); + enqueue_quote (qp, '\0'); + } + enqueue_quote (qp, '\0'); +} + +/* Called when a character is recived with a framing error. */ +void +input_framing_error (int c) +{ + struct queue **qp = termstate.c_lflag & ICANON ? &rawq : &inputq; + + /* Ignore it if IGNPAR is set. */ + if (termstate.c_iflag & IGNPAR) + return; + + /* If PARMRK is set, pass it specially marked. */ + if (termstate.c_iflag & PARMRK) + { + enqueue_quote (qp, CHAR_USER_QUOTE); + enqueue_quote (qp, '\0'); + enqueue_quote (qp, c); + } + else + /* Otherwise, it looks like a null. */ + enqueue_quote (qp, '\0'); +} + +/* Copy the characters in RAWQ to the end of INPUTQ and clear RAWQ. */ +void +copy_rawq () +{ + while (qsize (rawq)) + enqueue (&inputq, dequeue (rawq)); +} + +/* Process all the characters in INPUTQ as if they had just been read. */ +void +rescan_inputq () +{ + short *buf; + int i, n; + + n = qsize (inputq); + buf = alloca (n * sizeof (quoted_char)); + bcopy (inputq->cs, buf, n * sizeof (quoted_char)); + clear_queue (inputq); + + for (i = 0; i < n; i++) + input_character (unquote_char (buf[i])); +} + +void +drop_output () +{ + clear_queue (outputq); + abandon_physical_output (); +} + +error_t +drain_output () +{ + int cancel = 0; + + while ((qsize (outputq) || pending_output_size ()) + && (!(termflags & NO_CARRIER) || (termstate.c_cflag & CLOCAL)) + && !cancel) + cancel = hurd_condition_wait (outputq->wait, &global_lock); + + return cancel ? EINTR : 0; +} + +/* Create and return a new queue. */ +struct queue * +create_queue (int size, int lowat, int hiwat) +{ + struct queue *q; + + q = malloc (sizeof (struct queue) + size * sizeof (quoted_char)); + q->susp = 0; + q->lowat = lowat; + q->hiwat = hiwat; + q->cs = q->ce = q->array; + q->arraylen = size; + q->wait = malloc (sizeof (struct condition)); + condition_init (q->wait); + return q; +} + +/* Make Q able to have more characters added to it. */ +struct queue * +reallocate_queue (struct queue *q) +{ + int len; + struct queue *newq; + + len = qsize (q); + + if (len < q->arraylen / 2) + { + /* Shift the characters to the front of + the queue. */ + memmove (q->array, q->cs, len * sizeof (quoted_char)); + q->cs = q->array; + q->ce = q->cs + len; + } + else + { + /* Make the queue twice as large. */ + newq = malloc (sizeof (struct queue) + + q->arraylen * 2 * sizeof (quoted_char)); + newq->susp = q->susp; + newq->lowat = q->lowat; + newq->hiwat = q->hiwat; + newq->cs = newq->array; + newq->ce = newq->array + len; + newq->arraylen = q->arraylen * 2; + newq->wait = q->wait; + memmove (newq->array, q->cs, len * sizeof (quoted_char)); + free (q); + q = newq; + } + return q; +} diff --git a/term/term.h b/term/term.h new file mode 100644 index 00000000..a55f7027 --- /dev/null +++ b/term/term.h @@ -0,0 +1,264 @@ +/* + 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 <cthreads.h> +#include <assert.h> + +#undef MDMBUF +#undef ECHO +#undef TOSTOP +#undef FLUSHO +#undef PENDIN +#undef NOFLSH +#include <termios.h> + +#define CHAR_SOH '\001' /* C-a */ +#define CHAR_EOT '\004' /* C-d */ +#define CHAR_DC3 '\022' /* C-r */ +#define CHAR_USER_QUOTE '\377' /* break quoting, etc. */ + +/* XXX These belong in <termios.h> */ +#define ILCASE (1 << 14) +#define OLCASE (1 << 9) +#define OTILDE (1 << 10) + +/* used in mdmctl device call */ +#define MDMCTL_BIS 0 +#define MDMCTL_BIC 1 +#define MDMCTL_SET 2 + +/* Directly user-visible state */ +struct termios termstate; + +/* Other state with the following bits: */ +long termflags; + +#define USER_OUTPUT_SUSP 0x00000001 /* user has suspended output */ +#define TTY_OPEN 0x00000002 /* someone has us open */ +#define LAST_SLASH 0x00000004 /* last input char was \ */ +#define LAST_LNEXT 0x00000008 /* last input char was VLNEXT */ +#define INSIDE_HDERASE 0x00000010 /* inside \.../ hardcopy erase pair */ +#define SENT_VSTOP 0x00000020 /* we've sent VSTOP to IXOFF peer */ +#define FLUSH_OUTPUT 0x00000040 /* user wants output flushed */ +#define NO_CARRIER 0x00000080 /* carrier is absent */ +#define EXCL_USE 0x00000100 /* user accessible exclusive use */ +#define NO_OWNER 0x00000200 /* there is no foreground_id */ +#define ICKY_ASYNC 0x00000400 /* some user has set O_ASYNC */ + +/* PHYSICAL position of the terminal cursor */ +int output_psize; + +/* Global lock */ +struct mutex global_lock; + +/* Wakeup when NO_CARRIER turns off */ +struct condition carrier_alert; + +/* Wakeup for select */ +struct condition select_alert; + +/* Bucket for all our ports. */ +struct port_bucket *term_bucket; + +/* Port class for tty control ports */ +struct port_class *tty_cntl_class; + +/* Port class for tty I/O ports */ +struct port_class *tty_class; + +/* Port class for ctty ID ports */ +struct port_class *cttyid_class; + +/* Trivfs control structure for the tty */ +struct trivfs_control *termctl; + +/* Filename for this terminal */ +char *nodename; + +/* Mach device name for this terminal */ +char *pterm_name; + +/* The queues we use */ +struct queue *inputq, *rawq, *outputq; + + +/* Character queues */ +#define QUEUE_QUOTE_MARK 0xf000 +typedef short quoted_char; +struct queue +{ + int susp; + int lowat; + int hiwat; + short *cs, *ce; + int arraylen; + struct condition *wait; + quoted_char array[0]; +}; + +struct queue *create_queue (int size, int lowat, int hiwat); + +/* Return the number of characters in Q. */ +extern inline int +qsize (struct queue *q) +{ + return q->ce - q->cs; +} + +/* Return nonzero if characters can be added to Q. */ +extern inline int +qavail (struct queue *q) +{ + return !q->susp; +} + +/* Flush all the characters from Q. */ +extern inline int +clear_queue (struct queue *q) +{ + q->susp = 0; + q->cs = q->ce = q->array; + condition_broadcast (q->wait); +} + + +/* Return the next character off Q; leave the quoting bit on. */ +extern inline quoted_char +dequeue_quote (struct queue *q) +{ + int beep = 0; + + assert (qsize (q)); + if (q->susp && (qsize (q) < q->lowat)) + { + q->susp = 0; + beep = 1; + } + if (qsize (q) == 1) + beep = 1; + if (beep) + condition_broadcast (q->wait); + return *q->cs++; +} + +/* Return the next character off Q. */ +extern inline char +dequeue (struct queue *q) +{ + return dequeue_quote (q) & ~QUEUE_QUOTE_MARK; +} + +struct queue *reallocate_queue (struct queue *); + +/* Add C to *QP. */ +extern inline void +enqueue_internal (struct queue **qp, quoted_char c) +{ + struct queue *q = *qp; + + if (q->ce - q->array == q->arraylen) + q = *qp = reallocate_queue (q); + + *q->ce++ = c; + + if (qsize (q) == 1) + condition_broadcast (q->wait); + + if (!q->susp && (qsize (q) > q->hiwat)) + q->susp = 1; +} + +/* Add C to *QP. */ +extern inline void +enqueue (struct queue **qp, char c) +{ + enqueue_internal (qp, c); +} + +/* Add C to *QP, marking it with a quote. */ +extern inline void +enqueue_quote (struct queue **qp, char c) +{ + enqueue_internal (qp, c | QUEUE_QUOTE_MARK); +} + +/* Return the unquoted version of a quoted_char. */ +extern inline char +unquote_char (quoted_char c) +{ + return c & ~QUEUE_QUOTE_MARK; +} + +/* Tell if a quoted_char is actually quoted. */ +extern inline int +char_quoted_p (quoted_char c) +{ + return c & QUEUE_QUOTE_MARK; +} + +/* Remove the most recently enqueue character from Q; leaving + the quote mark on. */ +extern inline short +queue_erase (struct queue *q) +{ + short answer; + int beep = 0; + + assert (qsize (q)); + answer = *--q->ce; + if (q->susp && (qsize (q) < q->lowat)) + { + q->susp = 0; + beep = 1; + } + if (qsize (q) == 0) + beep = 1; + if (beep) + condition_broadcast (q->wait); + return answer; +} + + +/* Functions devio is supposed to call */ +int input_character (int); +void report_carrier_on (void); +void report_carrier_off (void); + + +/* Other decls */ +void drop_output (void); +void send_signal (int); +error_t drain_output (); +void output_character (int); +void copy_rawq (void); +void rescan_inputq (void); + + +/* exported by the devio interface */ +void start_output (void); +void set_break (void); +void clear_break (void); +void abandon_physical_output (void); +int pending_output_size (void); +error_t assert_dtr (void); +void desert_dtr (void); +void set_bits (void); +void mdmctl (int, int); +int mdmstate (void); 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; +} |