📄 fhandler_tty.cc
字号:
/* fhandler_tty.cc Copyright 1997, 1998, 2000, 2001, 2002 Red Hat, Inc.This file is part of Cygwin.This software is a copyrighted work licensed under the terms of theCygwin license. Please consult the file "CYGWIN_LICENSE" fordetails. */#include "winsup.h"#include <stdio.h>#include <unistd.h>#include <stdlib.h>#include <errno.h>#include <ctype.h>#include <limits.h>#include "cygerrno.h"#include "security.h"#include "fhandler.h"#include "path.h"#include "dtable.h"#include "sigproc.h"#include "pinfo.h"#include "cygheap.h"#include "shared_info.h"#include "cygwin/cygserver.h"#include "cygthread.h"/* Tty master stuff */fhandler_tty_master NO_COPY *tty_master;static DWORD WINAPI process_input (void *); // Input queue threadstatic DWORD WINAPI process_output (void *); // Output queue threadstatic DWORD WINAPI process_ioctl (void *); // Ioctl requests threadfhandler_tty_master::fhandler_tty_master (int unit) : fhandler_pty_master (FH_TTYM, unit), console (NULL){}intfhandler_tty_master::init (int ntty){ termios_printf ("Creating master for tty%d", ntty); if (init_console ()) { termios_printf ("can't create fhandler"); return -1; } termios ti; memset (&ti, 0, sizeof (ti)); console->tcsetattr (0, &ti); ttynum = ntty; cygwin_shared->tty[ttynum]->common_init (this); inuse = get_ttyp ()->create_inuse (TTY_MASTER_ALIVE); cygthread *h; h = new cygthread (process_input, cygself, "ttyin"); h->SetThreadPriority (THREAD_PRIORITY_HIGHEST); h->zap_h (); h = new cygthread (process_ioctl, cygself, "ttyioctl"); h->SetThreadPriority (THREAD_PRIORITY_HIGHEST); h->zap_h (); h = new cygthread (process_output, cygself, "ttyout"); h->SetThreadPriority (THREAD_PRIORITY_HIGHEST); h->zap_h (); return 0;}#ifdef DEBUGGINGstatic class mutex_stack{public: const char *fn; int ln; const char *tname;} ostack[100];static int osi;#endif /*DEBUGGING*/DWORDfhandler_tty_common::__acquire_output_mutex (const char *fn, int ln, DWORD ms){ if (strace.active) strace.prntf (_STRACE_TERMIOS, fn, "(%d): tty output_mutex: waiting %d ms", ln, ms); DWORD res = WaitForSingleObject (output_mutex, ms); if (res == WAIT_OBJECT_0) {#ifndef DEBUGGING if (strace.active) strace.prntf (_STRACE_TERMIOS, fn, "(%d): tty output_mutex: acquired", ln, res);#else ostack[osi].fn = fn; ostack[osi].ln = ln; ostack[osi].tname = cygthread::name (); termios_printf ("acquired for %s:%d, osi %d", fn, ln, osi); osi++;#endif } return res;}voidfhandler_tty_common::__release_output_mutex (const char *fn, int ln){ if (ReleaseMutex (output_mutex)) {#ifndef DEBUGGING if (strace.active) strace.prntf (_STRACE_TERMIOS, fn, "(%d): tty output_mutex released", ln);#else if (osi > 0) osi--; termios_printf ("released at %s:%d, osi %d", fn, ln, osi); termios_printf (" for %s:%d (%s)", ostack[osi].fn, ostack[osi].ln, ostack[osi].tname); ostack[osi].ln = -ln;#endif }}/* Process tty input. */voidfhandler_pty_master::doecho (const void *str, DWORD len){ acquire_output_mutex (INFINITE); if (!WriteFile (get_ttyp ()->to_master, str, len, &len, NULL)) termios_printf ("Write to %p failed, %E", get_ttyp ()->to_master);// WaitForSingleObject (output_done_event, INFINITE); release_output_mutex ();}intfhandler_pty_master::accept_input (){ DWORD bytes_left, written; DWORD n; DWORD rc; char* p; rc = WaitForSingleObject (input_mutex, INFINITE); bytes_left = n = eat_readahead (-1); get_ttyp ()->read_retval = 0; p = rabuf; if (n != 0) { while (bytes_left > 0) { termios_printf ("about to write %d chars to slave", bytes_left); rc = WriteFile (get_output_handle (), p, bytes_left, &written, NULL); if (!rc) { debug_printf ("error writing to pipe %E"); break; } get_ttyp ()->read_retval += written; p += written; bytes_left -= written; if (bytes_left > 0) { debug_printf ("to_slave pipe is full"); SetEvent (input_available_event); ReleaseMutex (input_mutex); Sleep (10); rc = WaitForSingleObject (input_mutex, INFINITE); } } } else termios_printf ("sending EOF to slave"); SetEvent (input_available_event); ReleaseMutex (input_mutex); return get_ttyp ()->read_retval;}static DWORD WINAPIprocess_input (void *){ char rawbuf[INP_BUFFER_SIZE]; while (1) { int nraw = tty_master->console->read ((void *) rawbuf, (size_t) INP_BUFFER_SIZE); (void) tty_master->line_edit (rawbuf, nraw); }}boolfhandler_pty_master::hit_eof (){ if (get_ttyp ()->was_opened && !get_ttyp ()->slave_alive ()) { /* We have the only remaining open handle to this pty, and the slave pty has been opened at least once. We treat this as EOF. */ termios_printf ("all other handles closed"); return 1; } return 0;}/* Process tty output requests */intfhandler_pty_master::process_slave_output (char *buf, size_t len, int pktmode_on){ size_t rlen; char outbuf[OUT_BUFFER_SIZE + 1]; DWORD n; int column = 0; int rc = 0; if (len == 0) goto out; if (need_nl) { /* We need to return a left over \n character, resulting from \r\n conversion. Note that we already checked for FLUSHO and output_stopped at the time that we read the character, so we don't check again here. */ buf[0] = '\n'; need_nl = 0; rc = 1; goto out; } for (;;) { /* Set RLEN to the number of bytes to read from the pipe. */ rlen = len; if (get_ttyp ()->ti.c_oflag & OPOST && get_ttyp ()->ti.c_oflag & ONLCR) { /* We are going to expand \n to \r\n, so don't read more than half of the number of bytes requested. */ rlen /= 2; if (rlen == 0) rlen = 1; } if (rlen > sizeof outbuf) rlen = sizeof outbuf; HANDLE handle = get_io_handle (); n = 0; // get_readahead_into_buffer (outbuf, len); if (!n) { /* Doing a busy wait like this is quite inefficient, but nothing else seems to work completely. Windows should provide some sort of overlapped I/O for pipes, or something, but it doesn't. */ while (1) { if (!PeekNamedPipe (handle, NULL, 0, NULL, &n, NULL)) goto err; if (n > 0) break; if (hit_eof ()) goto out; if (n == 0 && is_nonblocking ()) { set_errno (EAGAIN); rc = -1; break; } Sleep (10); } if (ReadFile (handle, outbuf, rlen, &n, NULL) == FALSE) goto err; } termios_printf ("bytes read %u", n); get_ttyp ()->write_error = 0; if (output_done_event != NULL) SetEvent (output_done_event); if (get_ttyp ()->ti.c_lflag & FLUSHO) continue; char *optr; optr = buf; if (pktmode_on) *optr++ = TIOCPKT_DATA; if (!(get_ttyp ()->ti.c_oflag & OPOST)) // post-process output { memcpy (optr, outbuf, n); optr += n; } else // raw output mode { char *iptr = outbuf; while (n--) { switch (*iptr) { case '\r': if ((get_ttyp ()->ti.c_oflag & ONOCR) && column == 0) { iptr++; continue; } if (get_ttyp ()->ti.c_oflag & OCRNL) *iptr = '\n'; else column = 0; break; case '\n': if (get_ttyp ()->ti.c_oflag & ONLCR) { *optr++ = '\r'; column = 0; } if (get_ttyp ()->ti.c_oflag & ONLRET) column = 0; break; default: column++; break; } /* Don't store data past the end of the user's buffer. This can happen if the user requests a read of 1 byte when doing \r\n expansion. */ if (optr - buf >= (int) len) { if (*iptr != '\n' || n != 0) system_printf ("internal error: %d unexpected characters", n); need_nl = 1; break; } *optr++ = *iptr++; } } rc = optr - buf; break; err: if (GetLastError () == ERROR_BROKEN_PIPE) rc = 0; else { __seterrno (); rc = -1; } break; }out: termios_printf ("returning %d", rc); return rc;}static DWORD WINAPIprocess_output (void *){ char buf[OUT_BUFFER_SIZE * 2]; for (;;) { int n = tty_master->process_slave_output (buf, OUT_BUFFER_SIZE, 0); if (n <= 0) { if (n < 0) termios_printf ("ReadFile %E"); ExitThread (0); } n = tty_master->console->write ((void *) buf, (size_t) n); tty_master->get_ttyp ()->write_error = n == -1 ? get_errno () : 0; }}/* Process tty ioctl requests */static DWORD WINAPIprocess_ioctl (void *){ while (1) { WaitForSingleObject (tty_master->ioctl_request_event, INFINITE); termios_printf ("ioctl() request"); tty_master->get_ttyp ()->ioctl_retval = tty_master->console->ioctl (tty_master->get_ttyp ()->cmd, (void *) &tty_master->get_ttyp ()->arg); SetEvent (tty_master->ioctl_done_event); }}/**********************************************************************//* Tty slave stuff */fhandler_tty_slave::fhandler_tty_slave (int num) : fhandler_tty_common (FH_TTYS, num){ set_r_no_interrupt (1);}fhandler_tty_slave::fhandler_tty_slave () : fhandler_tty_common (FH_TTYS, 0){ set_r_no_interrupt (1);}/* FIXME: This function needs to close handles when it has a failing condition. */intfhandler_tty_slave::open (path_conv *, int flags, mode_t){ tcinit (cygwin_shared->tty[ttynum]); attach_tty (ttynum); tc->set_ctty (ttynum, flags); set_flags ((flags & ~O_TEXT) | O_BINARY); /* Create synchronisation events */ char buf[40]; /* output_done_event may or may not exist. It will exist if the tty was opened by fhandler_tty_master::init, normally called at startup if use_tty is non-zero. It will not exist if this is a pty opened by fhandler_pty_master::open. In the former case, tty output is handled by a separate thread which controls output. */ __small_sprintf (buf, OUTPUT_DONE_EVENT, ttynum); output_done_event = OpenEvent (EVENT_ALL_ACCESS, TRUE, buf); if (!(output_mutex = get_ttyp ()->open_output_mutex ())) { termios_printf ("open output mutex failed, %E"); __seterrno (); return 0; } if (!(input_mutex = get_ttyp ()->open_input_mutex ())) { termios_printf ("open input mutex failed, %E"); __seterrno (); return 0; } __small_sprintf (buf, INPUT_AVAILABLE_EVENT, ttynum); if (!(input_available_event = OpenEvent (EVENT_ALL_ACCESS, TRUE, buf))) { termios_printf ("open input event failed, %E"); __seterrno (); return 0; } /* The ioctl events may or may not exist. See output_done_event, above. */ __small_sprintf (buf, IOCTL_REQUEST_EVENT, ttynum); ioctl_request_event = OpenEvent (EVENT_ALL_ACCESS, TRUE, buf); __small_sprintf (buf, IOCTL_DONE_EVENT, ttynum); ioctl_done_event = OpenEvent (EVENT_ALL_ACCESS, TRUE, buf); /* FIXME: Needs a method to eliminate tty races */ { acquire_output_mutex (500); inuse = get_ttyp ()->create_inuse (TTY_SLAVE_ALIVE); get_ttyp ()->was_opened = TRUE; release_output_mutex (); } /* Duplicate tty handles. */ if (!get_ttyp ()->from_slave || !get_ttyp ()->to_slave) { termios_printf ("tty handles have been closed"); set_errno (EACCES); return 0; } HANDLE from_master_local, to_master_local; if (!wincap.has_security () || cygserver_running == CYGSERVER_UNAVAIL || !cygserver_attach_tty (&from_master_local, &to_master_local)) { termios_printf ("cannot dup handles via server. using old method."); HANDLE tty_owner = OpenProcess (PROCESS_DUP_HANDLE, FALSE, get_ttyp ()->master_pid); termios_printf ("tty own handle %p",tty_owner); if (tty_owner == NULL) { termios_printf ("can't open tty (%d) handle process %d", ttynum, get_ttyp ()->master_pid); __seterrno (); return 0; } if (!DuplicateHandle (tty_owner, get_ttyp ()->from_master, hMainProc, &from_master_local, 0, TRUE, DUPLICATE_SAME_ACCESS)) { termios_printf ("can't duplicate input, %E"); __seterrno (); return 0; } if (!DuplicateHandle (tty_owner, get_ttyp ()->to_master, hMainProc, &to_master_local, 0, TRUE, DUPLICATE_SAME_ACCESS)) { termios_printf ("can't duplicate output, %E"); __seterrno (); return 0; } CloseHandle (tty_owner); } termios_printf ("duplicated from_master %p->%p from tty_owner", get_ttyp ()->from_master, from_master_local); termios_printf ("duplicated to_master %p->%p from tty_owner", get_ttyp ()->to_master, to_master_local); set_io_handle (from_master_local); set_output_handle (to_master_local); set_open_status (); termios_printf ("tty%d opened", ttynum); return 1;}intfhandler_tty_slave::cygserver_attach_tty (LPHANDLE from_master_ptr, LPHANDLE to_master_ptr){ if (!from_master_ptr || !to_master_ptr) return 0; client_request_attach_tty req ((DWORD) get_ttyp ()->master_pid, (HANDLE) get_ttyp ()->from_master, (HANDLE) get_ttyp ()->to_master); if (req.make_request () == -1 || req.error_code ()) return 0; *from_master_ptr = req.from_master (); *to_master_ptr = req.to_master (); return 1;}voidfhandler_tty_slave::init (HANDLE, DWORD a, mode_t){ int mode = 0; a &= GENERIC_READ | GENERIC_WRITE; if (a == GENERIC_READ) mode = O_RDONLY; if (a == GENERIC_WRITE) mode = O_WRONLY; if (a == (GENERIC_READ | GENERIC_WRITE)) mode = O_RDWR; open (0, mode);}intfhandler_tty_slave::write (const void *ptr, size_t len){ DWORD n, towrite = len; termios_printf ("tty%d, write(%x, %d)", ttynum, ptr, len); acquire_output_mutex (INFINITE); while (len) { n = min (OUT_BUFFER_SIZE, len); char *buf = (char *)ptr; ptr = (char *) ptr + n; len -= n; /* Previous write may have set write_error to != 0. Check it here. This is less than optimal, but the alternative slows down tty writes enormously. */ if (get_ttyp ()->write_error) { set_errno (get_ttyp ()->write_error); towrite = (DWORD) -1; break; } if (WriteFile (get_output_handle (), buf, n, &n, NULL) == FALSE) { DWORD err = GetLastError (); termios_printf ("WriteFile failed, %E"); switch (err) { case ERROR_NO_DATA: err = ERROR_IO_DEVICE; default: __seterrno_from_win_error (err); }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -