aboutsummaryrefslogtreecommitdiff
path: root/term
diff options
context:
space:
mode:
Diffstat (limited to 'term')
-rw-r--r--term/ChangeLog720
-rw-r--r--term/Makefile34
-rw-r--r--term/devio.c752
-rw-r--r--term/main.c207
-rw-r--r--term/munge.c764
-rw-r--r--term/ourmsg.defs14
-rw-r--r--term/ptyio.c625
-rw-r--r--term/term.h338
-rw-r--r--term/users.c2287
9 files changed, 5741 insertions, 0 deletions
diff --git a/term/ChangeLog b/term/ChangeLog
new file mode 100644
index 00000000..be515205
--- /dev/null
+++ b/term/ChangeLog
@@ -0,0 +1,720 @@
+1999-10-04 Thomas Bushnell, BSG <tb@mit.edu>
+
+ * term.h, devio.c, users.c: Revert previous change. Do it this
+ way instead:
+ * users.c (report_carrier_error): New function.
+ (carrier_error): New static global variable.
+ (open_hook): Deal with errors from carrier open.
+ * devio.c (device_open_reply): Move the !RETURNCODE case out of
+ the "initial open" case and use report_carrier_error.
+ * term.h (report_carrier_error): Declare new function.
+
+1999-10-01 Roland McGrath <roland@baalperazim.frob.com>
+
+ * term.h (NO_DEVICE): New macro, bit for termflags.
+ (termflags): Change type to uint_fast32_t.
+ * devio.c (device_open_reply): For D_NO_SUCH_DEVICE error reply, set
+ NO_DEVICE flag in termflags.
+ * users.c (open_hook): If NO_DEVICE flag set, return ENXIO immediately.
+ If we put out an open request, check for that bit as well as
+ NO_CARRIER changing in termflags and diagnose with ENXIO.
+
+ * Makefile (device_replyServer-CPPFLAGS): New variable, turn off
+ TypeCheck for this stub. This is necessary for error replies to get
+ through to our server-side functions in devio.c.
+
+1999-09-13 Roland McGrath <roland@baalperazim.frob.com>
+
+ * users.c: Reverted changes related to io_map_segment.
+
+1999-09-07 Thomas Bushnell, BSG <tb@mit.edu>
+
+ * users.c (trivfs_S_io_map): Renamed to ...
+ (trivfs_S_io_map_segment): ... here.
+
+1999-07-23 Roland McGrath <roland@baalperazim.frob.com>
+
+ * term.h (ILCASE, OLCASE): Use IUCLC, OLCUC if defined.
+
+1999-07-11 Roland McGrath <roland@baalperazim.frob.com>
+
+ * term.h: Add #include <sys/mman.h>.
+
+1999-07-09 Thomas Bushnell, BSG <tb@mit.edu>
+
+ * ptyio.c (pty_io_read): Use mmap instead of vm_allocate.
+ * users.c (trivfs_S_io_read): Likewise.
+
+1999-05-24 Mark Kettenis <kettenis@gnu.org>
+
+ * devio.c: Include <assert.h>, <errno.h> and <error.h>. Do
+ not include <stdio.h> and <sys/types.h>.
+ Reorganize the order of inclusion of the header files a bit and
+ document the baud-rate hackery. Include <termios.h> explicitely.
+ (init_devio): Use new local variable ERR instead of ERRNO. Use
+ error instead of perror and exit to report failure.
+ (devio_desert_dtr): Declare BITS as `int' instead of
+ `dev_status_t'.
+
+1999-05-13 Roland McGrath <roland@baalperazim.frob.com>
+
+ * users.c (trivfs_S_file_chown): Either arg being -1 means don't
+ change that id.
+
+1999-03-22 Roland McGrath <roland@baalperazim.frob.com>
+
+ * devio.c (initial_open): Fix typos.
+
+Wed Mar 17 16:32:05 1999 Thomas Bushnell, BSG <tb@mit.edu>
+
+ * users.c (S_term_get_peername): Compare BOTTOM against the
+ correct value, and call ports_port_deref with the proper name.
+ Reported by Yamashita TAKAO (az207@epa.go.jp).
+
+ * devio.c (devio_desert_dtr): Cast &BITS correctly in call to
+ device_set_status.
+ (device_open_reply): Alwas clean open_pending no matter what. Use
+ ERR instead of ERRNO. On fake opens, close the device before
+ deallocating the port.
+ Reported by Mark Kettenis (kettenis@gnu.org).
+
+Tue Mar 16 01:04:06 1999 Thomas Bushnell, BSG <tb@mit.edu>
+
+ * devio.c (open_pending): Make an enum.
+ (devio_desert_dtr): Don't close the device, just set the TM_HUP
+ modem bit, which should cause a hangup to occur.
+ (devio_assert_dtr): Don't always open the device, instead use
+ initial_open the first time, and schedule a fake open the other
+ times.
+ (initial_open): New function, guts from old device_assert_dtr.
+ (device_open_reply): Distinguish replies from initial and fake
+ opens appropriately.
+ (ports_do_mach_notify_send_once): Test and set open_pending using
+ new enum values.
+
+Mon Mar 15 14:58:33 1999 Thomas Bushnell, BSG <tb@mit.edu>
+
+ * users.c (S_term_get_peername): Implement new RPC.
+
+ * users.c (S_term_get_nodename): Return ENOENT if the name is not
+ set.
+
+1999-03-13 Mark Kettenis <kettenis@gnu.org>
+
+ * users.c (po_destroy_hook): Only reset state and hardware if
+ TTY_OPEN bit is set in TERMFLAGS.
+
+Mon Mar 1 09:11:06 1999 Thomas Bushnell, BSG <tb@mit.edu>
+
+ * munge.c (output_width): If C is a tab, then the width is the
+ offset from LOC, not the total final position of the tab.
+ Reported by Kalle Olavi Niemitalo <tosi@ees2.oulu.fi).
+
+1999-02-28 Roland McGrath <roland@baalperazim.frob.com>
+
+ * users.c (trivfs_S_io_revoke): Use ports_class_iterate.
+
+Mon Feb 22 04:34:55 1999 Thomas Bushnell, BSG <tb@mit.edu>
+
+ * users.c (trivfs_S_io_revoke): Protect the revocation by blocking
+ all other rpcs.
+
+Sat Feb 20 04:59:15 1999 Thomas Bushnell, BSG <tb@mit.edu>
+
+ * users.c (trivfs_S_io_revoke): Release global_lock before
+ beginning the iteration.
+
+ * users.c (trivfs_S_io_revoke): Add reply, reply_type args.
+
+1999-02-06 Mark Kettenis <kettenis@gnu.org>
+
+ * main.c (main): Initialize status from underlying node.
+ * users.c (check_access_hook): New function. Correctly implement
+ access permission checking.
+ (trivfs_check_access_hook): Initialize with check_access_hook.
+ (trivfs_S_file_check_access): Removed.
+
+Thu Feb 18 00:57:30 1999 Thomas Bushnell, BSG <tb@mit.edu>
+
+ * devio.c (devio_assert_dtr): Bother to set open_pending.
+ Reported by OKUJI Yoshinori (okuji@kuicr.kyoto-u.ac.jp).
+
+1999-02-16 Roland McGrath <roland@baalperazim.frob.com>
+
+ * users.c (trivfs_S_io_revoke): Fix typos.
+
+Tue Feb 16 06:10:08 1999 Thomas Bushnell, BSG <tb@mit.edu>
+
+ * users.c (trivfs_S_io_revoke): New function.
+
+1999-02-05 Mark Kettenis <kettenis@gnu.org>
+
+ * users.c (trivfs_S_file_chmod): Clear S_ISVTX bit instead of
+ clearing all other bits.
+
+Sat Jan 30 00:27:14 1999 Thomas Bushnell, BSG <tb@mit.edu>
+
+ * munge.c (create_queue): Make sure that malloc succeeds.
+ Reported by OKUJI Yoshinori <okuji@kuicr.kyoto-u.ac.jp>.
+
+1998-10-24 Roland McGrath <roland@baalperazim.frob.com>
+
+ * users.c (open_hook): Assert DTR if NO_CARRIER, even for CLOCAL.
+ This is the only thing that tells it to open the device.
+
+Mon Oct 26 16:47:18 1998 Thomas Bushnell, BSG <tb@mit.edu>
+
+ * devio.c (char_size_mask_xxx): New variable.
+ (devio_set_bits): Don't munge c_cflag here. Instead,
+ set char_size_mask_xxx.
+ (device_read_reply_inband): Mask off high bits from the input to
+ simulate less than 8-bit channels.
+
+1998-09-04 Roland McGrath <roland@baalperazim.frob.com>
+
+ * devio.c: Add #undef's for B19200, B38400, B57600, B115200, in case
+ they are defined in <device/tty_status.h> too.
+
+1998-07-20 Roland McGrath <roland@baalperazim.frob.com>
+
+ * users.c: Include <hurd/fshelp.h> for fshelp_isowner decl.
+
+ * term.h (clear_queue): Change return type to void.
+
+Wed Aug 20 14:07:35 1997 Thomas Bushnell, n/BSG <thomas@gnu.ai.mit.edu>
+
+ * main.c (main): New args for
+ ports_manage_port_operations_multithread.
+
+Mon Jun 9 12:19:51 1997 Thomas Bushnell, n/BSG <thomas@gnu.ai.mit.edu>
+
+ * users.c (trivfs_S_file_chmod): Fix typo.
+
+Tue May 27 12:04:00 1997 Thomas Bushnell, n/BSG <thomas@gnu.ai.mit.edu>
+
+ * users.c (trivfs_S_file_chmod): Turn off S_ISPARE too.
+
+Fri Feb 21 13:17:48 1997 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * users.c (open_hook): Add OPEN_COUNT hack to try and detect lossage.
+
+Thu Feb 20 02:25:29 1997 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * main.c (main): Initialize TTY_CLASS & PTY_CLASS too instead of
+ doing TTY_CNTL_CLASS & PTY_CNTL_CLASS twice.
+
+Wed Feb 19 13:10:47 1997 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * main.c (trivfs_protid_portclasses, trivfs_protid_nportclasses,
+ trivfs_cntl_portclasses, trivfs_cntl_nportclasses): Variables removed.
+ (main): Don't set port class/bucket variables, use
+ trivfs_add_{protid,control}_port_class instead.
+
+ * Makefile (HURDLIBS): Add iohelp & shouldbeinlibc.
+
+Thu Dec 12 18:33:52 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * main.c (main): Add S_IROOT to TERM_MODE.
+ * users.c (trivfs_S_file_chmod): Turn off S_ITRANS bits, and turn
+ on S_IROOT in TERM_MODE.
+
+Sat Nov 23 16:28:36 1996 Thomas Bushnell, n/BSG <thomas@gnu.ai.mit.edu>
+
+ * users.c: Include <hurd/iohelp.h>.
+ (trivfs_S_file_chmod): Bother to fill in ST.
+
+Mon Nov 18 18:16:29 1996 Thomas Bushnell, n/BSG <thomas@gnu.ai.mit.edu>
+
+ * users.c (trivfs_modify_stat): Omit pointless assignment.
+
+Fri Nov 15 17:37:12 1996 Thomas Bushnell, n/BSG <thomas@gnu.ai.mit.edu>
+
+ * users.c (open_hook): New arg syntax.
+ (trivfs_check_open_hook): Likewise.
+ * ptyio.c (pty_open_hook): Likewise.
+ * term.h (pty_open_hook): Likewise.
+
+ * users.c (S_termctty_open_terminal): New syntax of trivfs_open.
+
+ * users.c (trivfs_S_file_chown): Rewrite using idvecs.
+ (trivfs_S_file_chmod): Likewise.
+
+Thu Oct 24 14:44:57 1996 Thomas Bushnell, n/BSG <thomas@gnu.ai.mit.edu>
+
+ * users.c (open_hook): Always assert DTR (even if O_NONBLOCK).
+ Don't return any errors if O_NONBLOCK and we don't have carrier.
+
+Tue Oct 8 22:46:09 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * users.c (open_hook): Zero WINDOW_SIZE during initialization.
+
+Fri Oct 4 12:30:22 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * users.c (S_tioctl_tiocswinsz): Correct test for a changed winsize.
+
+ * ptyio.c (pty_open_hook): Reinitialize pty variables.
+
+ * ptyio.c (pty_io_select): Return SELECT_READ if the slave isn't open.
+ (pty_io_read): If the slave isn't open, return EOF.
+
+ * users.c (set_state, open_hook): Call (*BOTTOM)->set_bits
+ unconditionally.
+ * devio.c (devio_set_bits): Only execute guts if CIGNORE isn't set.
+
+ * ptyio.c (pty_io_read, pty_io_write): Honor O_NONBLOCK.
+
+Wed Oct 2 10:49:18 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * ptyio.c (S_tioctl_tiocsig, S_tioctl_tiocucntl, S_tioctl_tiocpkt):
+ Hold GLOBAL_LOCK while frobbing (especially around send_signal).
+
+ * ptyio.c (ptyio_set_bits): We need only be in packet mode to send
+ TIOCPKT_NOSTOP & TIOCPKT_DOSTOP, regardless of the value of
+ EXTERNAL_PROCESSING.
+
+Thu Sep 26 14:24:16 1996 Thomas Bushnell, n/BSG <thomas@gnu.ai.mit.edu>
+
+ * ptyio.c: Include "tioctl_S.h".
+
+Thu Sep 12 16:34:37 1996 Thomas Bushnell, n/BSG <thomas@gnu.ai.mit.edu>
+
+ * Makefile (HURDLIBS): New variable.
+ (OBJS): Don't list libraries here.
+
+Thu Aug 29 17:26:37 1996 Thomas Bushnell, n/BSG <thomas@gnu.ai.mit.edu>
+
+ * devio.c (device_open_reply): After error from ports_create_port,
+ unlock global_lock before returning.
+ * users.c (trivfs_S_io_readable): Likewise before returning
+ EBADF.
+
+Thu Aug 15 16:07:07 1996 Thomas Bushnell, n/BSG <thomas@gnu.ai.mit.edu>
+
+ * users.c (trivfs_S_io_pathconf): Implement function.
+
+ * term.h (QUEUE_HIWAT, QUEUE_LOWAT): New macros.
+ * main.c (main): Use these new macros to create inputq, rawq, and
+ outputq.
+
+Thu Aug 15 15:32:47 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * users.c (trivfs_S_file_check_access): Renamed from
+ `trivfs_S_file_access'.
+
+Mon Aug 12 11:04:28 1996 Thomas Bushnell, n/BSG <thomas@gnu.ai.mit.edu>
+
+ * term.h (CTRL_BIT): Correct value is 0x40, not 0x20.
+
+ * munge.c (poutput): Compute tab width using the same loop
+ strategy as output_character and output_width.
+
+Thu Aug 8 17:16:06 1996 Thomas Bushnell, n/BSG <thomas@gnu.ai.mit.edu>
+
+ * munge.c (echo_char): Compute non-ctrl version of control
+ character correctly.
+ * term.h (CHAR_SOH): Delete macro.
+ (CTRL_BIT): New macro.
+
+Mon Jul 29 02:46:12 1996 Roland McGrath <roland@baalperazim.frob.com>
+
+ * munge.c (input_character): In LAST_LNEXT case, jump to `alldone'
+ after putting the char on the queue.
+
+Fri Jul 19 23:46:39 1996 Michael I. Bushnell, p/BSG <mib@gnu.ai.mit.edu>
+
+ * users.c (trivfs_S_file_access): Implement locally always
+ returning all access. Eventually, this needs to do the right
+ thing when trivfs wises up wrt modes.
+
+Tue Jul 16 20:49:29 1996 Michael I. Bushnell, p/BSG <mib@gnu.ai.mit.edu>
+
+ * users.c (trivfs_S_io_read): Bother to set atime when
+ appropriate.
+
+ * users.c (trivfs_modify_stat): Fill in st->st_mode, st->st_uid,
+ and st->st_gid ourselves.
+ (trivfs_S_file_chown): New routine, to override trivfs default.
+ (trivfs_S_file_chmod): Likewise.
+ * main.c (main): Initialize term_owner, term_group, and term_mode.
+ * term.h (term_owner, term_group, term_mode): New variables.
+
+Thu Jun 20 16:45:57 1996 Michael I. Bushnell, p/BSG <mib@gnu.ai.mit.edu>
+
+ * devio.c (devio_abandon_physical_output): Don't do anything if
+ carrier is off.
+
+Thu Jun 20 16:26:07 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * Makefile (OBJS): Add ../libfshelp/libfshelp.a.
+
+Sat May 11 01:18:41 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * iomux.c (parse_opt): Use ARGP_ERR_UNKNOWN instead of EINVAL.
+
+Fri May 10 09:27:46 1996 Michael I. Bushnell, p/BSG <mib@gnu.ai.mit.edu>
+
+ * users.c: Include <stdio.h>.
+
+ * users.c (init_users): Order args correctly in call to
+ ports_create_port.
+
+Thu May 9 19:32:50 1996 Michael I. Bushnell, p/BSG <mib@gnu.ai.mit.edu>
+
+ * users.c (call_asyncs): Provide sigcode arg in call to
+ nowait_msg_sig_post.
+
+ * users.c (init_users): Use new ports_create_port.
+ * devio.c (device_open_reply): Likewise.
+
+Thu Apr 25 16:04:17 1996 Michael I. Bushnell, p/BSG <mib@gnu.ai.mit.edu>
+
+ * ptyio.c (pty_open_hook): Don't do increment of nptyperopens
+ here.
+ (pty_po_create_hook): Increment nptyperopens here, but only if this is
+ for O_READ or O_WRITE.
+ (pty_po_destroy_hook): Only do decrement if this was for O_READ or
+ O_WRITE.
+ * users.c (open_hook): Don't circumvent pty_open_hook, not even
+ when FLAGS is clear.
+
+Wed Apr 24 09:24:29 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * users.c (call_asyncs): Add DIR argument; all callers changed.
+ * term.h: Include <fcntl.h>.
+
+ * users.c (call_asyncs): Remove FORCE argument; all callers changed.
+ * term.h (enqueue_internal): Go back to only using call_asyncs
+ when inputq becomes non-empty.
+ (SUPPRESS_ASYNC): Flag removed.
+
+Tue Apr 23 17:44:25 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * term.h (enqueue_internal): Use call_asyncs on every character.
+ (SUPPRESS_ASYNC): New flag.
+ * users.c (po_create_hook, trivfs_S_io_set_some_openmodes,
+ trivfs_S_io_set_all_openmodes): If setting ICKY_ASYNC, then use
+ call_asyncs.
+ (call_asyncs): New argument, FORCE, which use. All callers changed.
+ (init_users): Give our self send rights to the async id ports, since
+ hurd_sig_post uses COPY_SEND.
+ (trivfs_S_io_get_icky_async_id): Renamed from ..._get_async_icky.
+ (trivfs_S_file_set_size, trivfs_S_io_seek,
+ trivfs_S_io_get_icky_async_id, trivfs_S_io_async): Add reply port args.
+
+ * users.c (num_icky_async_peropens): New variable.
+ (po_create_hook, po_destroy_hook, trivfs_S_io_set_all_openmodes,
+ trivfs_S_io_set_some_openmodes, trivfs_S_io_clear_some_openmodes): Use
+ it to enable ICKY_ASYNC to be turned off.
+
+Tue Apr 23 14:26:22 1996 Michael I. Bushnell, p/BSG <mib@gnu.ai.mit.edu>
+
+ * users.c (call_asyncs): Delete local decl; no longer static.
+ * term.h (dequeue_quote): If this is the outputq, send SIGIO as
+ appropriate with call_asyncs.
+ (enqueue_internal): If this is the inputq, send SIGIO as appropriate
+ with call_asyncs.
+ (call_asyncs): Add decl.
+
+Mon Apr 22 14:55:20 1996 Michael I. Bushnell, p/BSG <mib@gnu.ai.mit.edu>
+
+ * devio.c (real_speed_to_bogus_speed): EXTB should be 38400, not
+ 24800.
+
+Tue Apr 2 16:18:09 1996 Michael I. Bushnell, p/BSG <mib@gnu.ai.mit.edu>
+
+ * ptyio.c (pty_po_create_hook): Don't do anything here.
+ (pty_open_hook): Increment nptyperopens here.
+
+Wed Mar 27 11:51:43 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * users.c (trivfs_S_io_read): Call call_asyncs *before* we release
+ GLOBAL_LOCK.
+ (pi_destroy_hook): Leak the hook for now, to try and catch a bug. XXX
+
+ * ptyio.c (pty_io_read): Block using hurd_condition_wait instead
+ of condition_wait.
+
+Sat Feb 24 13:56:46 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * ptyio.c (ptyio_init): This can't be a constructor because it
+ frobs INPUTQ, which is assigned in main.
+ * main.c (main): Call ptyio_init if appropriate.
+ * term.h: Declare ptyio_init.
+
+Wed Feb 14 14:02:54 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * users.c (trivfs_S_io_set_all_openmodes,
+ trivfs_S_io_set_some_openmodes): Set ICKY_ASYNC in TERMFLAGS if
+ O_ASYNC is set in BITS.
+
+Thu Jan 25 22:54:04 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * main.c (main): Use trivfs_startup & trivfs_create_port instead
+ of trivfs_handle_port.
+ * devio.c (devio_assert_dtr): Use ports_create_port instead of
+ ports_allocate_port.
+
+Thu Jan 18 11:50:29 1996 Michael I. Bushnell, p/BSG <mib@gnu.ai.mit.edu>
+
+ * users.c (report_carrier_off): Flush queues when carrier turns
+ off.
+
+Tue Dec 26 16:58:55 1995 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * users.c (trivfs_S_io_select): Ask for notification if our reply
+ port dies. Pass REPLY to pty_io_select().
+ * ptyio.c (pty_io_select): Add new reply port parameter, and ask
+ for notification if it dies.
+ * term.h (pty_io_select): Add new reply port parameter.
+
+Fri Dec 22 14:34:38 1995 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * main.c (main): Set PEERCNTL to &PTYCTL if we're a slave, not 0.
+
+Wed Dec 20 13:56:09 1995 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * users.c (S_term_get_nodename, S_term_set_nodename): Get the node
+ name from our cred->po->cntl->hook rather than NODENAME.
+ * main.c (main): Put the nodename on ourcntl->hook rather than
+ NODENAME, and also put our peer's nodname on peercntl->hook.
+ * term.h (nodename): Variable removed.
+
+ * ptyio.c (ptyopen, nptyperopens, pktnostop, output_stopped):
+ Initialize to 0.
+
+Tue Dec 19 19:57:53 1995 Michael I. Bushnell, p/BSG <mib@gnu.ai.mit.edu>
+
+ * users.c (trivfs_S_io_read): After a block on the input queue,
+ don't go back and check the input queue if there is a signal in
+ progress; wait for the signal to complete first.
+ (send_signal): Release global_lock around signal RPC. Call
+ report_sig_start and report_sig_end around signal RPC.
+ (call_asyncs): Likewise.
+ (report_sig_start, report_sig_end): New functions.
+ (sigs_in_progress, input_sig_wait, input_sig_wakeup): New variables.
+
+Thu Dec 14 12:48:08 1995 Michael I. Bushnell, p/BSG <mib@gnu.ai.mit.edu>
+
+ * ptyio.c (pty_io_read): When copying TIOCPKT_DATA; account for
+ size correctly.
+
+ * ptyio.c (pty_io_write): Always tell the user everything was
+ written.
+
+Wed Dec 13 19:32:52 1995 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * ptyio.c (pty_io_write): Keep track of how mount we wrote.
+
+Tue Dec 12 13:53:40 1995 Michael I. Bushnell, p/BSG <mib@gnu.ai.mit.edu>
+
+ * ptyio.c (ptyio_init): Make this a constructor function.
+
+ * ptyio.c (pty_open_hook, pty_po_create_hook,
+ pty_po_destroy_hook): New functions.
+ (ptyopen, nptyperopens): New variables.
+ * term.h (pty_open_hook, pty_po_create_hook, pty_po_destroy_hook):
+ New declarations.
+ * users.c (open_hook): If this is the pty, then call pty specific
+ function.
+ (po_create_hook): Likewise.
+ (po_destroy_hook): Likewise.
+ (pi_create_hook): Don't do anything for pty.
+ (pi_destroy_hook): Likewise.
+
+ * users.c (open_hook): Don't require carrier for opens that don't
+ want to read or write.
+
+ * users.c (S_tioctl_tiocgpgrp): Omit bogus extra attempt to lock
+ global_lock.
+
+ * users.c (S_term_get_bottom_type): Return TERM_ON_MASTERPTY when
+ appropriate.
+
+ * main.c (main): Set BOTTOM.
+
+Tue Dec 5 15:30:36 1995 Michael I. Bushnell, p/BSG <mib@gnu.ai.mit.edu>
+
+ * main.c (main): Totally rework arg parsing and translator
+ linkage. No longer support being started s a shell program. Now
+ support pty's, though no attempt is made to deal nicely with the
+ node collision problem.
+
+Mon Dec 4 20:09:21 1995 Michael I. Bushnell, p/BSG <mib@gnu.ai.mit.edu>
+
+ * term.h (pty_cntl_class, ptyctl): New variables.
+ * main.c (main): Initialize pty_class, pty_cntl_class,
+ trivfs_protid_portclasses[1], and trivfs_cntl_portclasses[1].
+ (trivfs_protid_portclasses): Increase size to 2.
+ (trivfs_cntl_portclasses): Likewise.
+ (trivfs_protid_nportclasses): Increase initialization to 2.
+ (trivfs_cntl_nportclasses): Likewise.
+
+ * ptyio.c: Include <unistd.h>.
+ (ptyio_set_bits): If the stop char state has changed, dinkle the
+ stop bits in the control_byte accordingly.
+
+ * term.h: Include <hurd/trivfs.h> and <sys/types.h>.
+ (pty_io_write, pty_io_read, pty_io_readable, pty_io_select): New
+ declarations.
+ * ptyio.c: Include <fcntl.h>.
+ (pty_io_read): Add CRED arg.
+ (pty_io_write): Likewise.
+ (pty_io_select): Likewise.
+ * users.c (trivfs_S_io_write): If this is a pty master, call pty
+ routine to do the work.
+ (trivfs_S_io_read): Likewise.
+ (trivfs_S_io_readable): Likewise.
+ (trivfs_S_io_select): Likewise.
+ (S_tioctl_tiocmodg): Accept both pty and tty ports.
+ (S_tioctl_tiocmods): Likewise.
+ (S_tioctl_tiocexcl): Likewise.
+ (S_tioctl_tiocnxcl): Likewise.
+ (S_tioctl_tiocflush): Likewise.
+ (S_tioctl_tiocgeta): Likewise.
+ (set_state): Likewise.
+ (S_tioctl_tiocgetd): Likewise.
+ (S_tioctl_tiocsetd): Likewise.
+ (S_tioctl_tiocdrain): Likewise.
+ (S_tioctl_tiocswinsz): Likewise.
+ (S_tioctl_tiocgwinsz): Likewise.
+ (S_tioctl_tiocmget): Likewise.
+ (S_tioctl_tiocmset): Likewise.
+ (S_tioctl_tiocmbic): Likewise.
+ (S_tioctl_tiocmbis): Likewise.
+ (S_tioctl_tiocstart): Likewise.
+ (S_tioctl_tiocstop): Likewise.
+ (S_tioctl_tiocsti): Likewise.
+ (S_tioctl_tiocoutq): Likewise.
+ (S_tioctl_tiocspgrp): Likewise.
+ (S_tioctl_tiocgpgrp): Likewise.
+ (S_tioctl_tioccdtr): Likewise.
+ (S_tioctl_tiocsdtr): Likewise.
+ (S_tioctl_tioccbrk): Likewise.
+ (S_tioctl_tiocsbrk): Likewise.
+ (set_state): If this op is being done on the pty master, then
+ flush output before beginning work.
+
+Fri Dec 1 14:08:44 1995 Michael I. Bushnell, p/BSG <mib@gnu.ai.mit.edu>
+
+ * users.c (trivfs_S_interrupt_operation): Delete function.
+
+ * users.c (S_tioctl_tiocdrain): Free reference on CRED before
+ returning.
+
+ * munge.c (input_character): Skip much processing if
+ external_processing is on.
+ (output_character): Don't do tab expansion of external_processing
+ is on.
+ (echo_p): Never echo if external_processing is on.
+ * users.c (set_state): Make EXTPROC bit read only. When
+ external_processing, call set_bits even if CIGNORE.
+ (S_tioctl_tiocext): Deleted function.
+ * term.h (external_processing): New variable.
+
+ * users.c (trivfs_S_io_readable): If remote_input_mode, then don't
+ include extra final character as input.
+ trivfs_S_io_read): If remote_input_mode, copy characters without
+ interpretation; treat last character left in queue as junk.
+ (S_tioctl_tiocremote): Deleted function.
+ * term.h (remote_input_mode): New variable.
+
+ * users.c (S_tioctl_tiocsig, S_tioctl_tiocpkt,
+ S_tioctl_tiocucntl): Deleted functions.
+
+ * term.h (ptyio_bottom, pty_class): New variables.
+ * Makefile (SRCS): Added ptyio.c.
+ * ptyio.c: New file.
+
+ * term.h (struct bottomhalf): New member `notice_input_flushed'.
+ * devio.c (devio_notice_input_flushed): New function.
+ (devio_bottom): Add devio_notice_input_flushed.
+ * users.c (po_destroy_hook): Call notice_input_flushed after
+ flushing input queues.
+ (S_tioctl_tiocflush): Likewise.
+ (set_state): Likewise.
+
+ * munge.c (input_character) [VSTOP]: Suspend physical output after
+ setting flag.
+ * term.h (struct bottomhalf): New member `suspend_physical_output'.
+ * users.c (S_tioctl_tiocstart): Start output after clearing
+ USER_OUTPUT_SUSP.
+ (S_tioctl_tiocstop): Suspend physical output after setting flag.
+ * devio.c (devio_start_output): Honor USER_OUTPUT_SUSP flag.
+ Restart output if USER_OUTPUT_SUSP flag off and output_stopped true.
+ (output_stopped): New variable.
+ (devio_suspend_physical_output): New function.
+ (devio_bottom): Add devio_suspend_physical_output.
+
+ * users.c (trivfs_S_io_select): Return EINTR if we are cancelled.
+
+ * munge.c (reprint_line): C-r is CHAR_DC2, not DC3.
+ * term.h (CHAR_DC3): Correct value is '\023'.
+ (CHAR_DC1, CHAR_DC2): New macros.
+
+Thu Nov 30 15:50:01 1995 Michael I. Bushnell, p/BSG <mib@gnu.ai.mit.edu>
+
+ * devio.c (start_output): Add devio_ prefix; declare static.
+ All callers changed.
+ (set_break): Likewise.
+ (clear_break): Likewise.
+ (abandon_physical_output): Likewise.
+ (pending_output_size): Likewise.
+ (assert_dtr): Likewise.
+ (desert_dtr): Likewise.
+ (set_bits): Likewise.
+ (mdmctl): Likewise.
+ (mdmstate): Likewise.
+ (devio_bottom): New variable.
+ * term.h (struct bottomhalf): New type.
+ (bottom, devio_bottom): New variables.
+ (start_output, set_break, clear_break, abandon_physical_output,
+ pending_output_size, assert_dtr, desert_dtr, set_bits, mdmctl,
+ mdmstate): Deleted declarations.
+
+ * devio.c (ports_do_mach_notify_send_once): New function.
+
+Sun Nov 5 02:07:56 1995 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * main.c (main): Add flags arg to fsys_startup call.
+
+Sat Sep 23 00:48:17 1995 Roland McGrath <roland@churchy.gnu.ai.mit.edu>
+
+ * term.h: Include errno.h.
+
+Mon Sep 18 14:51:40 1995 Miles Bader <miles@churchy.gnu.ai.mit.edu>
+
+ * users.c (trivfs_S_file_set_size): Renamed from
+ trivfs_S_file_truncate.
+
+Sat Sep 16 13:03:40 1995 Roland McGrath <roland@churchy.gnu.ai.mit.edu>
+
+ * Makefile (DIST_FILES): Added ourmsg.defs.
+ (ourmsg_U.h ourmsgUser.c, ourmsg.defs): Targets removed.
+
+Thu Sep 7 13:08:55 1995 Michael I. Bushnell, p/BSG <mib@duality.gnu.ai.mit.edu>
+
+ * users.c (trivfs_S_io_write): Start pending output before
+ blocking.
+
+Fri Sep 1 09:51:11 1995 Michael I. Bushnell, p/BSG <mib@duality.gnu.ai.mit.edu>
+
+ * munge.c (input_character): Clear input queues correctly for
+ VINTR/VQUIT, VSUSP, and input queue full.
+
+ * users.c (init_users): New function.
+ * main.c (main): Call init_users.
+ * term.h (init_users): New decl.
+
+ * users.c (open_hook): Turn on NO_OWNER for fresh opens.
+ * main.c (main): Assert NO_OWNER in initial state.
+
+ * term.h (output_psize): Delete decl.
+ (write_character): New decl.
+ * munge.c (output_character): Don't set echo_qsize or echo_pstart
+ here.
+ (write_character): New function.
+ (echo_char): Use write_character for closing '/' of hderase.
+ (output_psize): New decl.
+ * users.c (trivfs_S_io_write): Use write_character instead of
+ output_character.
+ * main.c (main): Don't initialize output_psize.
diff --git a/term/Makefile b/term/Makefile
new file mode 100644
index 00000000..8adbced3
--- /dev/null
+++ b/term/Makefile
@@ -0,0 +1,34 @@
+#
+# Copyright (C) 1995, 1996, 1997, 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.
+
+dir := term
+makemode := server
+
+target = term
+SRCS = devio.c munge.c users.c main.c ptyio.c
+LCLHDRS = term.h
+DIST_FILES = ourmsg.defs
+
+HURDLIBS=trivfs fshelp iohelp ports ihash shouldbeinlibc threads
+OBJS = $(subst .c,.o,$(SRCS)) termServer.o device_replyServer.o tioctlServer.o ourmsgUser.o
+
+include ../Makeconf
+
+device_replyServer-CPPFLAGS = -DTypeCheck=0 -Wno-unused # XXX
diff --git a/term/devio.c b/term/devio.c
new file mode 100644
index 00000000..2306fa3b
--- /dev/null
+++ b/term/devio.c
@@ -0,0 +1,752 @@
+/*
+ Copyright (C) 1995, 1996, 1998, 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. */
+
+/* Avoid defenition of the baud rates from <ternios.h> at a later time. */
+#include <termios.h>
+
+/* And undefine the baud rates to avoid warnings from
+ <device/tty_status.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 B19200
+#undef B38400
+#undef B57600
+#undef B115200
+#undef EXTA
+#undef EXTB
+
+#include <assert.h>
+#include <errno.h>
+#include <error.h>
+#include <string.h>
+
+#include <device/device.h>
+#include <device/device_request.h>
+#include <device/tty_status.h>
+#include <cthreads.h>
+
+#include <hurd.h>
+#include <hurd/ports.h>
+
+#include "term.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;
+
+/* Tell the status of any pending open */
+static enum
+{
+ NOTPENDING, /* no open is pending */
+ INITIAL, /* initial open of device pending */
+ FAKE, /* open pending to block on dtr */
+} 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 int output_stopped;
+
+/* XXX Mask that omits high bits we are currently not supposed to pass
+ through. */
+static int char_size_mask_xxx = 0xff;
+
+/* Forward */
+static void devio_desert_dtr ();
+
+static void init_devio (void) __attribute__ ((constructor));
+static void
+init_devio ()
+{
+ mach_port_t host_priv;
+ error_t err;
+
+ err = get_privileged_ports (&host_priv, &device_master);
+ if (err)
+ error (1, err, "Getting priviliged ports");
+ 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 38400:
+ *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. */
+static void
+devio_start_output ()
+{
+ char *cp;
+ int size;
+ error_t err;
+
+ size = qsize (outputq);
+
+ if (!size || output_pending || (termflags & USER_OUTPUT_SUSP))
+ return;
+
+ if (output_stopped)
+ {
+ device_set_status (phys_device, TTY_START, 0, 0);
+ output_stopped = 0;
+ }
+
+ /* 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)
+ devio_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);
+ }
+ devio_start_output ();
+ }
+ else if (return_code == D_WOULD_BLOCK)
+ /* Carrier has dropped. */
+ devio_desert_dtr ();
+ else
+ devio_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++)
+ {
+ int c = data[i];
+
+ /* XXX Mach only supports 8-bit channels; this munges things
+ to account for the reality. */
+ c &= char_size_mask_xxx;
+
+ flush = input_character (c);
+ if (flush)
+ break;
+ }
+ else if (error_code == D_WOULD_BLOCK)
+ {
+ devio_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)
+ devio_desert_dtr ();
+ else
+ input_pending = 1;
+
+ mutex_unlock (&global_lock);
+ return 0;
+}
+
+static void
+devio_set_break ()
+{
+ device_set_status (phys_device, TTY_SET_BREAK, 0, 0);
+}
+
+static void
+devio_clear_break ()
+{
+ device_set_status (phys_device, TTY_CLEAR_BREAK, 0, 0);
+}
+
+static void
+devio_abandon_physical_output ()
+{
+ int val = D_WRITE;
+
+ /* If this variable is clear, then carrier is gone, so we
+ have nothing to do. */
+ if (!phys_reply_writes_pi)
+ return;
+
+ 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;
+}
+
+static void
+devio_suspend_physical_output ()
+{
+ if (!output_stopped)
+ {
+ device_set_status (phys_device, TTY_STOP, 0, 0);
+ output_stopped = 1;
+ }
+}
+
+static void
+devio_notice_input_flushed ()
+{
+}
+
+static int
+devio_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;
+}
+
+/* Do this the first time the device is to be opened */
+static error_t
+initial_open ()
+{
+ error_t err;
+
+ assert (open_pending != FAKE);
+
+ /* Nothing to do */
+ if (open_pending == INITIAL)
+ return 0;
+
+ assert (phys_device == MACH_PORT_NULL);
+ assert (phys_reply == MACH_PORT_NULL);
+ assert (phys_reply_pi == 0);
+
+ err = ports_create_port (phys_reply_class, term_bucket,
+ sizeof (struct port_info), &phys_reply_pi);
+ if (err)
+ return err;
+
+ 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;
+ }
+ else
+ open_pending = INITIAL;
+
+ return err;
+}
+
+static void
+devio_desert_dtr ()
+{
+ int bits;
+
+ /* Turn off DTR. */
+ bits = TM_HUP;
+ device_set_status (phys_device, TTY_MODEM,
+ (dev_status_t) &bits, TTY_MODEM_COUNT);
+
+ report_carrier_off ();
+}
+
+static error_t
+devio_assert_dtr ()
+{
+ error_t err;
+
+ /* The first time is special. */
+ if (phys_device == MACH_PORT_NULL)
+ return initial_open ();
+
+ /* Schedule a fake open to wait for DTR, unless one is already
+ happening. */
+ assert (open_pending != INITIAL);
+ if (open_pending == FAKE)
+ return 0;
+
+ err = device_open_request (device_master, phys_reply,
+ D_READ|D_WRITE, pterm_name);
+
+ if (err)
+ return err;
+
+ open_pending = FAKE;
+ return 0;
+}
+
+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 = 0;
+
+ if (replyport != phys_reply)
+ return EOPNOTSUPP;
+
+ mutex_lock (&global_lock);
+
+ assert (open_pending != NOTPENDING);
+
+ if (returncode != 0)
+ {
+ report_carrier_error (returncode);
+
+ mach_port_deallocate (mach_task_self (), phys_reply);
+ phys_reply = MACH_PORT_NULL;
+ ports_port_deref (phys_reply_pi);
+ phys_reply_pi = 0;
+
+ open_pending = NOTPENDING;
+ mutex_unlock (&global_lock);
+ return 0;
+ }
+
+ if (open_pending == INITIAL)
+ {
+ /* Special handling for the first open */
+
+ assert (phys_device == MACH_PORT_NULL);
+ assert (phys_reply_writes == MACH_PORT_NULL);
+ assert (phys_reply_writes_pi == 0);
+ phys_device = device;
+ err = ports_create_port (phys_reply_class, term_bucket,
+ sizeof (struct port_info),
+ &phys_reply_writes_pi);
+ if (err)
+ {
+ open_pending = NOTPENDING;
+ mutex_unlock (&global_lock);
+ return err;
+ }
+ 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);
+
+ /* Schedule our first read */
+ err = device_read_request_inband (phys_device, phys_reply, D_NOWAIT,
+ 0, vm_page_size);
+
+ input_pending = 1;
+ }
+ else
+ {
+ /* This was a fake open, only for the sake of assert DTR. */
+ device_close (device);
+ mach_port_deallocate (mach_task_self (), device);
+ }
+
+ 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);
+
+ report_carrier_on ();
+ if (err)
+ devio_desert_dtr ();
+
+ open_pending = NOTPENDING;
+ 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. */
+static void
+devio_set_bits ()
+{
+ if (!(termstate.c_cflag & CIGNORE) && phys_device != MACH_PORT_NULL)
+ {
+ struct tty_status ttystat;
+ int cnt = TTY_STATUS_COUNT;
+
+ /* 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;
+
+ /* Figure out how to munge input, since we are unable to actually
+ affect what the hardware does. */
+ switch (termstate.c_cflag & CSIZE)
+ {
+ case CS5:
+ char_size_mask_xxx = 0x1f;
+ break;
+
+ case CS6:
+ char_size_mask_xxx = 0x3f;
+ break;
+
+ case CS7:
+ char_size_mask_xxx = 0x7f;
+ break;
+
+ case CS8:
+ default:
+ char_size_mask_xxx = 0xff;
+ break;
+ }
+ if (termstate.c_cflag & PARENB)
+ char_size_mask_xxx |= 0x80;
+ }
+}
+
+static void
+devio_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);
+}
+
+static int
+devio_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;
+}
+
+error_t
+ports_do_mach_notify_send_once (mach_port_t notify)
+{
+ error_t err;
+
+ mutex_lock (&global_lock);
+
+ if (notify == phys_reply_writes)
+ {
+ err = 0;
+ devio_start_output ();
+ }
+ else if (notify == phys_reply)
+ {
+ if (input_pending)
+ {
+ /* xxx */
+ char msg[] = "Term input check happened\r\n";
+ int foo;
+ device_write_inband (phys_device, 0, 0, msg, sizeof msg, &foo);
+ /* end xxx */
+
+ input_pending = 0;
+
+ err = device_read_request_inband (phys_device, phys_reply,
+ D_NOWAIT, 0, vm_page_size);
+ if (err)
+ devio_desert_dtr ();
+ else
+ input_pending = 1;
+ }
+ else if (open_pending != NOTPENDING)
+ {
+ open_pending = NOTPENDING;
+
+ 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;
+ }
+ err = 0;
+ }
+ else
+ err = EOPNOTSUPP;
+
+ mutex_unlock (&global_lock);
+ return err;
+}
+
+
+struct bottomhalf devio_bottom =
+{
+ devio_start_output,
+ devio_set_break,
+ devio_clear_break,
+ devio_abandon_physical_output,
+ devio_suspend_physical_output,
+ devio_pending_output_size,
+ devio_notice_input_flushed,
+ devio_assert_dtr,
+ devio_desert_dtr,
+ devio_set_bits,
+ devio_mdmctl,
+ devio_mdmstate,
+};
diff --git a/term/main.c b/term/main.c
new file mode 100644
index 00000000..db23ef5b
--- /dev/null
+++ b/term/main.c
@@ -0,0 +1,207 @@
+/*
+ Copyright (C) 1995, 1996, 1997 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;
+
+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)
+{
+ struct port_class *ourclass, *ourcntlclass;
+ struct port_class *peerclass, *peercntlclass;
+ struct trivfs_control **ourcntl, **peercntl;
+ mach_port_t bootstrap;
+ enum {T_DEVICE, T_PTYMASTER, T_PTYSLAVE} type;
+ struct stat st;
+
+ term_bucket = ports_create_bucket ();
+
+ trivfs_add_control_port_class (&tty_cntl_class);
+ trivfs_add_control_port_class (&pty_cntl_class);
+ trivfs_add_protid_port_class (&tty_class);
+ trivfs_add_protid_port_class (&pty_class);
+
+ cttyid_class = ports_create_class (0, 0);
+
+ init_users ();
+
+ task_get_bootstrap_port (mach_task_self (), &bootstrap);
+
+ if (argc != 4)
+ {
+ fprintf (stderr, "Usage: term ttyname type arg\n");
+ exit (1);
+ }
+
+ if (!strcmp (argv[2], "device"))
+ {
+ type = T_DEVICE;
+ bottom = &devio_bottom;
+ ourclass = tty_class;
+ ourcntlclass = tty_cntl_class;
+ ourcntl = &termctl;
+ peerclass = 0;
+ peercntlclass = 0;
+ peercntl = 0;
+ pterm_name = argv[3];
+ }
+ else if (!strcmp (argv[2], "pty-master"))
+ {
+ type = T_PTYMASTER;
+ bottom = &ptyio_bottom;
+ ourclass = pty_class;
+ ourcntlclass = pty_cntl_class;
+ ourcntl = &ptyctl;
+ peerclass = tty_class;
+ peercntlclass = tty_cntl_class;
+ peercntl = &termctl;
+ }
+ else if (!strcmp (argv[2], "pty-slave"))
+ {
+ type = T_PTYSLAVE;
+ bottom = &ptyio_bottom;
+ ourclass = tty_class;
+ ourcntlclass = tty_cntl_class;
+ ourcntl = &termctl;
+ peerclass = pty_class;
+ peercntlclass = pty_cntl_class;
+ peercntl = &ptyctl;
+ }
+ else
+ {
+ fprintf (stderr,
+ "Allowable types are device, pty-master, and pty-slave.\n");
+ exit (1);
+ }
+
+ if (bootstrap == MACH_PORT_NULL)
+ {
+ fprintf (stderr, "Must be started as a translator\n");
+ exit (1);
+ }
+
+ /* Set our node */
+ errno = trivfs_startup (bootstrap, 0,
+ ourcntlclass, term_bucket, ourclass, term_bucket,
+ ourcntl);
+ if (errno)
+ {
+ perror ("Starting translator");
+ exit (1);
+ }
+
+ /* For ptys, the nodename depends on which half is used. For now just use
+ the hook to store the nodename. */
+ (*ourcntl)->hook = argv[1];
+
+ /* Set peer */
+ if (peerclass)
+ {
+ char *peer_name = argv[3];
+ file_t file = file_name_lookup (peer_name, O_CREAT|O_NOTRANS, 0666);
+
+ if (file != MACH_PORT_NULL)
+ errno = 0;
+
+ if (! errno)
+ errno = trivfs_create_control (file, peercntlclass, term_bucket,
+ peerclass, term_bucket, peercntl);
+ if (! errno)
+ errno = file_set_translator (file, 0, FS_TRANS_EXCL | FS_TRANS_SET,
+ 0, 0, 0,
+ ports_get_right (*peercntl),
+ MACH_MSG_TYPE_MAKE_SEND);
+
+ if (errno)
+ {
+ perror (peer_name);
+ exit (1);
+ }
+
+ (*peercntl)->hook = peer_name;
+ ports_port_deref (*peercntl);
+ }
+
+ bzero (&termstate, sizeof (termstate));
+ termflags = NO_CARRIER | NO_OWNER;
+ mutex_init (&global_lock);
+
+ /* Initialize status from underlying node. */
+ errno = io_stat ((*ourcntl)->underlying, &st);
+ if (errno)
+ {
+ /* We cannot stat the underlying node. Fallback to the defaults. */
+ term_owner = term_group = 0;
+ term_mode = (bottom == &ptyio_bottom ? DEFFILEMODE : S_IRUSR | S_IWUSR);
+ errno = 0;
+ }
+ else
+ {
+ term_owner = st.st_uid;
+ term_group = st.st_gid;
+ term_mode = (st.st_mode & ACCESSPERMS);
+ }
+ term_mode |= S_IFCHR | S_IROOT;
+
+ inputq = create_queue (256, QUEUE_LOWAT, QUEUE_HIWAT);
+
+ rawq = create_queue (256, QUEUE_LOWAT, QUEUE_HIWAT);
+
+ outputq = create_queue (256, QUEUE_LOWAT, QUEUE_HIWAT);
+
+ if (bottom == &ptyio_bottom)
+ ptyio_init ();
+
+ 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);
+
+ return 0;
+}
diff --git a/term/munge.c b/term/munge.c
new file mode 100644
index 00000000..8b3ea40b
--- /dev/null
+++ b/term/munge.c
@@ -0,0 +1,764 @@
+/*
+ Copyright (C) 1995, 1996, 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 <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;
+
+/* PHYSICAL position of the terminal cursor */
+int output_psize;
+
+/* 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++;
+ while (output_psize % 8)
+ output_psize++;
+ }
+ else if (c == '\b')
+ output_psize--;
+
+ enqueue (&outputq, c);
+}
+
+/* Place C on output queue, doing normal output processing.
+ Only echo routines should directly call this function. Others
+ should call write_character below. */
+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 (!external_processing && (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);
+}
+
+/* Place C on output queue, doing normal processing. */
+void
+write_character (int c)
+{
+ output_character (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 - loc;
+ }
+ 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)
+{
+ if (external_processing)
+ return 0;
+ 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))
+ {
+ write_character ('/');
+ termflags &= ~INSIDE_HDERASE;
+ }
+
+ if (hderase && !(termflags & INSIDE_HDERASE))
+ {
+ output_character ('\\');
+ termflags |= INSIDE_HDERASE;
+ }
+
+ /* Control characters that should use caret-letter */
+ if (echo_double (c, quoted))
+ {
+ output_character ('^');
+ output_character (c ^ CTRL_BIT);
+ }
+ else
+ output_character (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_DC2, 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 (!external_processing && (termflags & LAST_LNEXT))
+ {
+ enqueue_quote (qp, c);
+ echo_char (c, 0, 1);
+ termflags &= ~LAST_LNEXT;
+ goto alldone;
+ }
+
+ /* Mutate ILCASE */
+ if (!external_processing && (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 (!external_processing && (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 (!external_processing && (lflag & ISIG))
+ {
+ if (CCEQ (cc[VINTR], c) || CCEQ (cc[VQUIT], c))
+ {
+ if (!(lflag & NOFLSH))
+ {
+ drop_output ();
+ clear_queue (inputq);
+ clear_queue (rawq);
+ flush = 1;
+ }
+ echo_char (c, 0, 0);
+ echo_qsize = 0;
+ echo_pstart = output_psize;
+ send_signal (CCEQ (cc[VINTR], c) ? SIGINT : SIGQUIT);
+ goto alldone;
+ }
+
+ if (CCEQ (cc[VSUSP], c))
+ {
+ if (!(lflag & NOFLSH))
+ {
+ flush = 1;
+ clear_queue (inputq);
+ clear_queue (rawq);
+ }
+ echo_char (c, 0, 0);
+ echo_qsize = 0;
+ echo_pstart = output_psize;
+ send_signal (SIGTSTP);
+ goto alldone;
+ }
+ }
+
+ /* IXON */
+ if (!external_processing && (iflag & IXON))
+ {
+ if (CCEQ (cc[VSTOP], c))
+ {
+ termflags |= USER_OUTPUT_SUSP;
+ (*bottom->suspend_physical_output) ();
+
+ 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;
+ }
+
+ if (!external_processing)
+ {
+ /* 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 (!external_processing && (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 ();
+ clear_queue (inputq);
+ clear_queue (rawq);
+ echo_pstart = 0;
+ echo_qsize = 0;
+ 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;
+ (*bottom->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);
+ (*bottom->abandon_physical_output) ();
+}
+
+error_t
+drain_output ()
+{
+ int cancel = 0;
+
+ while ((qsize (outputq) || (*bottom->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));
+ assert (q);
+
+ q->susp = 0;
+ q->lowat = lowat;
+ q->hiwat = hiwat;
+ q->cs = q->ce = q->array;
+ q->arraylen = size;
+ q->wait = malloc (sizeof (struct condition));
+ assert (q->wait);
+
+ 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/ourmsg.defs b/term/ourmsg.defs
new file mode 100644
index 00000000..ad55073b
--- /dev/null
+++ b/term/ourmsg.defs
@@ -0,0 +1,14 @@
+/* Private specialized presentation of msg.defs for term server. */
+
+#define routine simpleroutine
+
+/* The reason for this= 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. */
+#define out /* empty */
+
+#define USERPREFIX nowait_
+
+#define msg ourmsg /* Change subsystem name for headers et al. */
+
+#include <hurd/msg.defs>
diff --git a/term/ptyio.c b/term/ptyio.c
new file mode 100644
index 00000000..94aec468
--- /dev/null
+++ b/term/ptyio.c
@@ -0,0 +1,625 @@
+/*
+ Copyright (C) 1995, 1996, 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 <sys/ioctl.h>
+#include <hurd/hurd_types.h>
+#include <string.h>
+#include <hurd/ports.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include "term.h"
+#include "tioctl_S.h"
+
+/* Set if we need a wakeup when tty output has been done */
+static int pty_read_blocked = 0;
+
+/* Wake this up when tty output occurs and pty_read_blocked is set */
+static struct condition pty_read_wakeup = CONDITION_INITIALIZER;
+
+static struct condition pty_select_wakeup = CONDITION_INITIALIZER;
+
+/* Set if "dtr" is on. */
+static int dtr_on = 0;
+
+/* Set if packet mode is on. */
+static int packet_mode = 0;
+
+/* Set if user ioctl mode is on. */
+static int user_ioctl_mode = 0;
+
+/* Byte to send to user in packet mode or user ioctl mode. */
+static char control_byte = 0;
+
+static int output_stopped = 0;
+
+static int pktnostop = 0;
+
+static int ptyopen = 0;
+
+static int nptyperopens = 0;
+
+
+void
+ptyio_init ()
+{
+ condition_implies (inputq->wait, &pty_select_wakeup);
+ condition_implies (&pty_read_wakeup, &pty_select_wakeup);
+}
+
+error_t
+pty_open_hook (struct trivfs_control *cntl,
+ struct iouser *user,
+ int flags)
+{
+ if ((flags & (O_READ|O_WRITE)) == 0)
+ return 0;
+
+ mutex_lock (&global_lock);
+
+ if (ptyopen)
+ {
+ mutex_unlock (&global_lock);
+ return EBUSY;
+ }
+
+ ptyopen = 1;
+
+ /* Re-initialize pty state. */
+ external_processing = 0;
+ packet_mode = 0;
+ user_ioctl_mode = 0;
+ control_byte = 0;
+ pktnostop = 0;
+
+ mutex_unlock (&global_lock);
+
+ return 0;
+}
+
+error_t
+pty_po_create_hook (struct trivfs_peropen *po)
+{
+ mutex_lock (&global_lock);
+ if (po->openmodes & (O_READ | O_WRITE))
+ {
+ nptyperopens++;
+ report_carrier_on ();
+ }
+ mutex_unlock (&global_lock);
+ return 0;
+}
+
+error_t
+pty_po_destroy_hook (struct trivfs_peropen *po)
+{
+ mutex_lock (&global_lock);
+ if ((po->openmodes & (O_READ | O_WRITE)) == 0)
+ {
+ mutex_unlock (&global_lock);
+ return 0;
+ }
+ nptyperopens--;
+ if (!nptyperopens)
+ {
+ ptyopen = 0;
+ report_carrier_off ();
+ }
+ mutex_unlock (&global_lock);
+ return 0;
+}
+
+static inline void
+wake_reader ()
+{
+ if (pty_read_blocked)
+ {
+ pty_read_blocked = 0;
+ condition_broadcast (&pty_read_wakeup);
+ }
+}
+
+
+/* Lower half for tty node */
+
+static void
+ptyio_start_output ()
+{
+ if (packet_mode && output_stopped && (!(termflags & USER_OUTPUT_SUSP)))
+ {
+ control_byte &= ~TIOCPKT_STOP;
+ control_byte |= TIOCPKT_START;
+ output_stopped = 0;
+ }
+ wake_reader ();
+}
+
+static void
+ptyio_abandon_physical_output ()
+{
+ if (packet_mode)
+ {
+ control_byte |= TIOCPKT_FLUSHWRITE;
+ wake_reader ();
+ }
+}
+
+static void
+ptyio_suspend_physical_output ()
+{
+ if (packet_mode)
+ {
+ control_byte &= ~TIOCPKT_START;
+ control_byte |= TIOCPKT_STOP;
+ output_stopped = 1;
+ wake_reader ();
+ }
+}
+
+static int
+ptyio_pending_output_size ()
+{
+ /* We don't maintain any pending output buffer separate from the outputq. */
+ return 0;
+}
+
+static void
+ptyio_notice_input_flushed ()
+{
+ if (packet_mode)
+ {
+ control_byte |= TIOCPKT_FLUSHREAD;
+ wake_reader ();
+ }
+}
+
+static error_t
+ptyio_assert_dtr ()
+{
+ dtr_on = 1;
+ return 0;
+}
+
+static void
+ptyio_desert_dtr ()
+{
+ dtr_on = 0;
+ wake_reader ();
+}
+
+static void
+ptyio_set_bits ()
+{
+ if (packet_mode)
+ {
+ int wakeup = 0;
+ int stop = ((termstate.c_iflag & IXON)
+ && CCEQ (termstate.c_cc[VSTOP], CHAR_DC3)
+ && CCEQ (termstate.c_cc[VSTART], CHAR_DC1));
+
+ if (external_processing)
+ {
+ control_byte |= TIOCPKT_IOCTL;
+ wakeup = 1;
+ }
+
+ if (pktnostop && stop)
+ {
+ pktnostop = 0;
+ control_byte |= TIOCPKT_DOSTOP;
+ control_byte &= ~TIOCPKT_NOSTOP;
+ wakeup = 1;
+ }
+ else if (!pktnostop && !stop)
+ {
+ pktnostop = 1;
+ control_byte |= TIOCPKT_NOSTOP;
+ control_byte &= ~TIOCPKT_DOSTOP;
+ wakeup = 1;
+ }
+
+ if (wakeup)
+ wake_reader ();
+ }
+}
+
+/* These do nothing. In BSD the associated ioctls get errors, but
+ I'd rather just ignore them. */
+static void
+ptyio_set_break ()
+{
+}
+
+static void
+ptyio_clear_break ()
+{
+}
+
+static void
+ptyio_mdmctl (int a, int b)
+{
+}
+
+static int
+ptyio_mdmstate ()
+{
+ return 0;
+}
+
+struct bottomhalf ptyio_bottom =
+{
+ ptyio_start_output,
+ ptyio_set_break,
+ ptyio_clear_break,
+ ptyio_abandon_physical_output,
+ ptyio_suspend_physical_output,
+ ptyio_pending_output_size,
+ ptyio_notice_input_flushed,
+ ptyio_assert_dtr,
+ ptyio_desert_dtr,
+ ptyio_set_bits,
+ ptyio_mdmctl,
+ ptyio_mdmstate,
+};
+
+
+
+
+/* I/O interface for pty master nodes */
+
+/* Validation has already been done by trivfs_S_io_read. */
+error_t
+pty_io_read (struct trivfs_protid *cred,
+ char **data,
+ mach_msg_type_number_t *datalen,
+ mach_msg_type_number_t amount)
+{
+ int size;
+
+ mutex_lock (&global_lock);
+
+ if ((cred->po->openmodes & O_READ) == 0)
+ {
+ mutex_unlock (&global_lock);
+ return EBADF;
+ }
+
+ while (!control_byte
+ && (termflags & TTY_OPEN)
+ && (!qsize (outputq) || (termflags & USER_OUTPUT_SUSP)))
+ {
+ if (cred->po->openmodes & O_NONBLOCK)
+ {
+ mutex_unlock (&global_lock);
+ return EWOULDBLOCK;
+ }
+ pty_read_blocked = 1;
+ if (hurd_condition_wait (&pty_read_wakeup, &global_lock))
+ {
+ mutex_unlock (&global_lock);
+ return EINTR;
+ }
+ }
+
+ if (control_byte)
+ {
+ size = 1;
+ if (packet_mode && (control_byte & TIOCPKT_IOCTL))
+ size += sizeof (struct termios);
+ }
+ else
+ {
+ size = qsize (outputq);
+ if (packet_mode || user_ioctl_mode)
+ size++;
+ }
+
+ if (size > amount)
+ size = amount;
+ if (size > *datalen)
+ *data = mmap (0, size, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+ *datalen = size;
+
+ if (control_byte)
+ {
+ **data = control_byte;
+ if (packet_mode && (control_byte & TIOCPKT_IOCTL))
+ bcopy (&termstate, *data + 1, size - 1);
+ control_byte = 0;
+ }
+ else
+ {
+ char *cp = *data;
+
+ if (packet_mode || user_ioctl_mode)
+ {
+ *cp++ = TIOCPKT_DATA;
+ --size;
+ }
+ while (size--)
+ *cp++ = dequeue (outputq);
+ }
+
+ mutex_unlock (&global_lock);
+ return 0;
+}
+
+
+/* Validation has already been done by trivfs_S_io_write. */
+error_t
+pty_io_write (struct trivfs_protid *cred,
+ char *data,
+ mach_msg_type_number_t datalen,
+ mach_msg_type_number_t *amount)
+{
+ int i, flush;
+ int cancel = 0;
+
+ mutex_lock (&global_lock);
+
+ if ((cred->po->openmodes & O_WRITE) == 0)
+ {
+ mutex_unlock (&global_lock);
+ return EBADF;
+ }
+
+ if (remote_input_mode)
+ {
+ /* Wait for the queue to be empty */
+ while (qsize (inputq) && !cancel)
+ {
+ if (cred->po->openmodes & O_NONBLOCK)
+ {
+ mutex_unlock (&global_lock);
+ return EWOULDBLOCK;
+ }
+ cancel = hurd_condition_wait (inputq->wait, &global_lock);
+ }
+ if (cancel)
+ {
+ mutex_unlock (&global_lock);
+ return EINTR;
+ }
+
+ for (i = 0; i < datalen; i++)
+ enqueue (&inputq, data[i]);
+
+ /* Extra garbage charater */
+ enqueue (&inputq, 0);
+ }
+ else if (termstate.c_cflag & CREAD)
+ for (i = 0; i < datalen; i++)
+ {
+ flush = input_character (data[i]);
+
+ if (flush)
+ {
+ if (packet_mode)
+ {
+ control_byte |= TIOCPKT_FLUSHREAD;
+ wake_reader ();
+ }
+ break;
+ }
+ }
+
+ mutex_unlock (&global_lock);
+
+ *amount = datalen;
+ return 0;
+}
+
+/* Validation has already been done by trivfs_S_io_readable */
+error_t
+pty_io_readable (int *amt)
+{
+ mutex_lock (&global_lock);
+ if (control_byte)
+ {
+ *amt = 1;
+ if (packet_mode && (control_byte & TIOCPKT_IOCTL))
+ *amt += sizeof (struct termios);
+ }
+ else
+ *amt = qsize (outputq);
+ mutex_unlock (&global_lock);
+ return 0;
+}
+
+/* Validation has already been done by trivfs_S_io_select. */
+error_t
+pty_io_select (struct trivfs_protid *cred, mach_port_t reply,
+ int *type, int *idtag)
+{
+ int avail = 0;
+
+ if (*type == 0)
+ return 0;
+
+ mutex_lock (&global_lock);
+
+ while (1)
+ {
+ if ((*type & SELECT_READ)
+ && (control_byte || qsize (outputq) || !(termflags & TTY_OPEN)))
+ avail |= SELECT_READ;
+
+ if ((*type & SELECT_URG) && control_byte)
+ avail |= SELECT_URG;
+
+ if ((*type & SELECT_WRITE) && (!remote_input_mode || !qsize (inputq)))
+ avail |= SELECT_WRITE;
+
+ if (avail)
+ {
+ *type = avail;
+ mutex_unlock (&global_lock);
+ return 0;
+ }
+
+ ports_interrupt_self_on_port_death (cred, reply);
+
+ pty_read_blocked = 1;
+ if (hurd_condition_wait (&pty_select_wakeup, &global_lock))
+ {
+ *type = 0;
+ mutex_unlock (&global_lock);
+ return EINTR;
+ }
+ }
+}
+
+error_t
+S_tioctl_tiocsig (io_t port,
+ int sig)
+{
+ struct trivfs_protid *cred = ports_lookup_port (term_bucket,
+ port, pty_class);
+ if (!cred)
+ return EOPNOTSUPP;
+
+ mutex_lock (&global_lock);
+
+ drop_output ();
+ clear_queue (inputq);
+ clear_queue (rawq);
+ ptyio_notice_input_flushed ();
+ send_signal (sig);
+
+ mutex_unlock (&global_lock);
+ ports_port_deref (cred);
+
+ return 0;
+}
+
+error_t
+S_tioctl_tiocpkt (io_t port,
+ int mode)
+{
+ error_t err;
+
+ struct trivfs_protid *cred = ports_lookup_port (term_bucket,
+ port, pty_class);
+ if (!cred)
+ return EOPNOTSUPP;
+
+ mutex_lock (&global_lock);
+
+ if (!!mode == !!packet_mode)
+ err = 0;
+ else if (mode && user_ioctl_mode)
+ err = EINVAL;
+ else
+ {
+ packet_mode = mode;
+ control_byte = 0;
+ err = 0;
+ }
+
+ mutex_unlock (&global_lock);
+ ports_port_deref (cred);
+
+ return err;
+}
+
+error_t
+S_tioctl_tiocucntl (io_t port,
+ int mode)
+{
+ error_t err;
+
+ struct trivfs_protid *cred = ports_lookup_port (term_bucket,
+ port, pty_class);
+ if (!cred)
+ return EOPNOTSUPP;
+
+ mutex_lock (&global_lock);
+
+ if (!!mode == !!user_ioctl_mode)
+ err = 0;
+ else if (mode && packet_mode)
+ err = EINVAL;
+ else
+ {
+ user_ioctl_mode = mode;
+ control_byte = 0;
+ err = 0;
+ }
+
+ mutex_unlock (&global_lock);
+ ports_port_deref (cred);
+
+ return err;
+}
+
+error_t
+S_tioctl_tiocremote (io_t port,
+ int how)
+{
+ struct trivfs_protid *cred = ports_lookup_port (term_bucket,
+ port, pty_class);
+
+ if (!cred)
+ return EOPNOTSUPP;
+
+ mutex_lock (&global_lock);
+ remote_input_mode = how;
+ drop_output ();
+ clear_queue (inputq);
+ clear_queue (rawq);
+ ptyio_notice_input_flushed ();
+ mutex_unlock (&global_lock);
+ ports_port_deref (cred);
+ return 0;
+}
+
+error_t
+S_tioctl_tiocext (io_t port,
+ int mode)
+{
+ struct trivfs_protid *cred = ports_lookup_port (term_bucket,
+ port, pty_class);
+ if (!cred)
+ return EOPNOTSUPP;
+
+ mutex_lock (&global_lock);
+ if (mode && !external_processing)
+ {
+ if (packet_mode)
+ {
+ control_byte |= TIOCPKT_IOCTL;
+ wake_reader ();
+ }
+ external_processing = 1;
+ termstate.c_lflag |= EXTPROC;
+ }
+ else if (!mode && external_processing)
+ {
+ if (packet_mode)
+ {
+ control_byte |= TIOCPKT_IOCTL;
+ wake_reader ();
+ }
+ external_processing = 0;
+ termstate.c_lflag &= ~EXTPROC;
+ }
+ mutex_unlock (&global_lock);
+ ports_port_deref (cred);
+ return 0;
+}
diff --git a/term/term.h b/term/term.h
new file mode 100644
index 00000000..4fece7ee
--- /dev/null
+++ b/term/term.h
@@ -0,0 +1,338 @@
+/*
+ Copyright (C) 1995, 1996, 1998, 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 <cthreads.h>
+#include <assert.h>
+#include <errno.h>
+#include <hurd/trivfs.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+
+#undef MDMBUF
+#undef ECHO
+#undef TOSTOP
+#undef FLUSHO
+#undef PENDIN
+#undef NOFLSH
+#include <termios.h>
+
+#define CHAR_EOT '\004' /* C-d */
+#define CHAR_DC1 '\021' /* C-q */
+#define CHAR_DC2 '\022' /* C-r */
+#define CHAR_DC3 '\023' /* C-s */
+#define CHAR_USER_QUOTE '\377' /* break quoting, etc. */
+
+/* This bit specifies control */
+#define CTRL_BIT 0x40
+
+/* XXX These belong in <termios.h> */
+#ifdef IUCLC
+#define ILCASE IUCLC
+#else
+#define ILCASE (1 << 14)
+#endif
+#ifdef OLCUC
+#define OLCASE OLCUC
+#else
+#define OLCASE (1 << 9)
+#endif
+#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 */
+
+#define QUEUE_LOWAT 100
+#define QUEUE_HIWAT 300
+
+/* 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;
+
+/* Port class for pty master ports */
+struct port_class *pty_class;
+
+/* Port class for pty control ports */
+struct port_class *pty_cntl_class;
+
+/* Trivfs control structure for the tty */
+struct trivfs_control *termctl;
+
+/* Trivfs control structure for the pty */
+struct trivfs_control *ptyctl;
+
+/* Mach device name for this terminal */
+char *pterm_name;
+
+/* The queues we use */
+struct queue *inputq, *rawq, *outputq;
+
+/* Plain pass-through input */
+int remote_input_mode;
+
+/* External processing mode */
+int external_processing;
+
+/* Terminal owner */
+uid_t term_owner;
+
+/* Terminal group */
+uid_t term_group;
+
+/* Terminal mode */
+mode_t term_mode;
+
+
+/* Functions a bottom half defines */
+struct bottomhalf
+{
+ void (*start_output) (void);
+ void (*set_break) (void);
+ void (*clear_break) (void);
+ void (*abandon_physical_output) (void);
+ void (*suspend_physical_output) (void);
+ int (*pending_output_size) (void);
+ void (*notice_input_flushed) (void);
+ error_t (*assert_dtr) (void);
+ void (*desert_dtr) (void);
+ void (*set_bits) (void);
+ void (*mdmctl) (int, int);
+ int (*mdmstate) (void);
+};
+
+struct bottomhalf *bottom;
+extern struct bottomhalf devio_bottom, ptyio_bottom;
+
+
+/* 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 void
+clear_queue (struct queue *q)
+{
+ q->susp = 0;
+ q->cs = q->ce = q->array;
+ condition_broadcast (q->wait);
+}
+
+/* Should be below, but inlines need it. */
+void call_asyncs (int dir);
+
+/* 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);
+ if (q == outputq)
+ call_asyncs (O_WRITE);
+ }
+ 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 == inputq)
+ call_asyncs (O_READ);
+ }
+
+ 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);
+void report_carrier_error (error_t);
+
+
+/* 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);
+void write_character (int);
+void init_users (void);
+
+/* Call this before using ptyio_bottom. */
+void ptyio_init (void);
+
+/* kludge--these are pty versions of trivfs_S_io_* functions called by
+ the real functions in users.c to do work for ptys. */
+error_t pty_io_write (struct trivfs_protid *, char *,
+ mach_msg_type_number_t, mach_msg_type_number_t *);
+error_t pty_io_read (struct trivfs_protid *, char **,
+ mach_msg_type_number_t *, mach_msg_type_number_t);
+error_t pty_io_readable (int *);
+error_t pty_io_select (struct trivfs_protid *, mach_port_t, int *, int *);
+error_t pty_open_hook (struct trivfs_control *, struct iouser *, int);
+error_t pty_po_create_hook (struct trivfs_peropen *);
+error_t pty_po_destroy_hook (struct trivfs_peropen *);
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;
+}