diff options
Diffstat (limited to 'term')
-rw-r--r-- | term/ChangeLog | 720 | ||||
-rw-r--r-- | term/Makefile | 34 | ||||
-rw-r--r-- | term/devio.c | 752 | ||||
-rw-r--r-- | term/main.c | 207 | ||||
-rw-r--r-- | term/munge.c | 764 | ||||
-rw-r--r-- | term/ourmsg.defs | 14 | ||||
-rw-r--r-- | term/ptyio.c | 625 | ||||
-rw-r--r-- | term/term.h | 338 | ||||
-rw-r--r-- | term/users.c | 2287 |
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; +} |