mirror of
git://sourceware.org/git/newlib-cygwin.git
synced 2025-03-04 22:15:52 +08:00
- When Ctrl-C terminates a non-cygwin process on a pseudo console, pty master attaches to the pseudo console first, and send CTRL_C_EVENT. If the non-cygwin process closes the pseudo console before the pty master calls FreeConsole(), the pty master process will crash. With this patch, pty master process takes over the ownership of the pseudo console, and closes it by myself.
405 lines
8.8 KiB
C++
405 lines
8.8 KiB
C++
/* tty.cc
|
|
|
|
This file is part of Cygwin.
|
|
|
|
This software is a copyrighted work licensed under the terms of the
|
|
Cygwin license. Please consult the file "CYGWIN_LICENSE" for
|
|
details. */
|
|
|
|
#include "winsup.h"
|
|
#include "miscfuncs.h"
|
|
#include <unistd.h>
|
|
#include <utmp.h>
|
|
#include <sys/cygwin.h>
|
|
#include "cygerrno.h"
|
|
#include "security.h"
|
|
#include "path.h"
|
|
#include "fhandler.h"
|
|
#include "dtable.h"
|
|
#include "cygheap.h"
|
|
#include "pinfo.h"
|
|
#include "shared_info.h"
|
|
|
|
HANDLE NO_COPY tty_list::mutex = NULL;
|
|
|
|
extern "C" int
|
|
getpt (void)
|
|
{
|
|
return open ("/dev/ptmx", O_RDWR | O_NOCTTY);
|
|
}
|
|
|
|
extern "C" int
|
|
posix_openpt (int oflags)
|
|
{
|
|
return open ("/dev/ptmx", oflags);
|
|
}
|
|
|
|
extern "C" int
|
|
grantpt (int fd)
|
|
{
|
|
cygheap_fdget cfd (fd);
|
|
return cfd < 0 ? -1 : 0;
|
|
}
|
|
|
|
extern "C" int
|
|
unlockpt (int fd)
|
|
{
|
|
cygheap_fdget cfd (fd);
|
|
return cfd < 0 ? -1 : 0;
|
|
}
|
|
|
|
extern "C" int
|
|
revoke (char *ttyname)
|
|
{
|
|
set_errno (ENOSYS);
|
|
return -1;
|
|
}
|
|
|
|
extern "C" int
|
|
ttyslot (void)
|
|
{
|
|
if (myself->ctty <= 0 || iscons_dev (myself->ctty))
|
|
return -1;
|
|
return device::minor (myself->ctty);
|
|
}
|
|
|
|
void __stdcall
|
|
tty_list::init_session ()
|
|
{
|
|
char mutex_name[MAX_PATH];
|
|
char *name = shared_name (mutex_name, "tty_list::mutex", 0);
|
|
|
|
/* tty_list::mutex is used while searching for a tty slot */
|
|
if (!(mutex = CreateMutex (&sec_all_nih, FALSE, name)))
|
|
api_fatal ("can't create tty_list::mutex '%s', %E", name);
|
|
ProtectHandle (mutex);
|
|
}
|
|
|
|
void __stdcall
|
|
tty::init_session ()
|
|
{
|
|
if (!myself->cygstarted && NOTSTATE (myself, PID_CYGPARENT))
|
|
cygheap->fdtab.get_debugger_info ();
|
|
}
|
|
|
|
int __reg2
|
|
tty_list::attach (int n)
|
|
{
|
|
int res;
|
|
if (iscons_dev (n))
|
|
res = -1;
|
|
else if (n != -1)
|
|
res = connect (device::minor (n));
|
|
else
|
|
res = -1;
|
|
return res;
|
|
}
|
|
|
|
int
|
|
tty_list::connect (int ttynum)
|
|
{
|
|
if (ttynum < 0 || ttynum >= NTTYS)
|
|
{
|
|
termios_printf ("ttynum (%d) out of range", ttynum);
|
|
return -1;
|
|
}
|
|
if (!ttys[ttynum].exists ())
|
|
{
|
|
termios_printf ("pty %d was not allocated", ttynum);
|
|
set_errno (ENXIO);
|
|
return -1;
|
|
}
|
|
|
|
return ttynum;
|
|
}
|
|
|
|
void
|
|
tty_list::init ()
|
|
{
|
|
for (int i = 0; i < NTTYS; i++)
|
|
{
|
|
ttys[i].init ();
|
|
ttys[i].setntty (DEV_PTYS_MAJOR, i);
|
|
}
|
|
}
|
|
|
|
/* Search for a free tty and allocate it.
|
|
Return tty number or -1 if error.
|
|
*/
|
|
int
|
|
tty_list::allocate (HANDLE& r, HANDLE& w)
|
|
{
|
|
lock_ttys here;
|
|
int freetty = -1;
|
|
|
|
tty *t = NULL;
|
|
for (int i = 0; i < NTTYS; i++)
|
|
if (ttys[i].not_allocated (r, w))
|
|
{
|
|
t = ttys + i;
|
|
t->init ();
|
|
t->setsid (0);
|
|
freetty = i;
|
|
break;
|
|
}
|
|
|
|
if (freetty >= 0)
|
|
termios_printf ("pty%d allocated", freetty);
|
|
else
|
|
{
|
|
system_printf ("No pty allocated");
|
|
r = w = NULL;
|
|
}
|
|
|
|
return freetty;
|
|
}
|
|
|
|
bool
|
|
tty::not_allocated (HANDLE& r, HANDLE& w)
|
|
{
|
|
/* Attempt to open the from-master side of the tty. If it is accessible
|
|
then it exists although we may not have privileges to actually use it. */
|
|
char pipename[sizeof("ptyNNNN-from-master")];
|
|
__small_sprintf (pipename, "pty%d-from-master", get_minor ());
|
|
/* fhandler_pipe::create returns 0 when creation succeeds */
|
|
return fhandler_pipe::create (&sec_none, &r, &w,
|
|
fhandler_pty_common::pipesize, pipename,
|
|
0) == 0;
|
|
}
|
|
|
|
bool
|
|
tty::exists ()
|
|
{
|
|
HANDLE r, w;
|
|
bool res;
|
|
if (!not_allocated (r, w))
|
|
res = true;
|
|
|
|
else
|
|
{
|
|
/* Handles are left open when not_allocated finds a non-open "tty" */
|
|
CloseHandle (r);
|
|
CloseHandle (w);
|
|
res = false;
|
|
}
|
|
debug_printf ("exists %d", res);
|
|
return res;
|
|
}
|
|
|
|
bool
|
|
tty::slave_alive ()
|
|
{
|
|
HANDLE ev;
|
|
if ((ev = open_inuse (READ_CONTROL)))
|
|
CloseHandle (ev);
|
|
return ev != NULL;
|
|
}
|
|
|
|
HANDLE
|
|
tty::open_mutex (const char *mutex, ACCESS_MASK access)
|
|
{
|
|
char buf[MAX_PATH];
|
|
shared_name (buf, mutex, get_minor ());
|
|
return OpenMutex (access, TRUE, buf);
|
|
}
|
|
|
|
HANDLE
|
|
tty::open_inuse (ACCESS_MASK access)
|
|
{
|
|
char buf[MAX_PATH];
|
|
shared_name (buf, TTY_SLAVE_ALIVE, get_minor ());
|
|
return OpenEvent (access, FALSE, buf);
|
|
}
|
|
|
|
HANDLE
|
|
tty::create_inuse (PSECURITY_ATTRIBUTES sa)
|
|
{
|
|
HANDLE h;
|
|
char buf[MAX_PATH];
|
|
|
|
shared_name (buf, TTY_SLAVE_ALIVE, get_minor ());
|
|
h = CreateEvent (sa, TRUE, FALSE, buf);
|
|
termios_printf ("%s %p", buf, h);
|
|
if (!h)
|
|
termios_printf ("couldn't open inuse event %s, %E", buf);
|
|
return h;
|
|
}
|
|
|
|
void
|
|
tty::init ()
|
|
{
|
|
output_stopped = 0;
|
|
setsid (0);
|
|
pgid = 0;
|
|
was_opened = false;
|
|
master_pid = 0;
|
|
is_console = false;
|
|
column = 0;
|
|
pcon_activated = false;
|
|
switch_to_pcon_in = false;
|
|
pcon_pid = 0;
|
|
term_code_page = 0;
|
|
pcon_last_time = 0;
|
|
pcon_fwd_not_empty = false;
|
|
pcon_start = false;
|
|
pcon_start_pid = 0;
|
|
pcon_cap_checked = false;
|
|
has_csi6n = false;
|
|
need_invisible_console = false;
|
|
invisible_console_pid = 0;
|
|
previous_code_page = 0;
|
|
previous_output_code_page = 0;
|
|
master_is_running_as_service = false;
|
|
req_xfer_input = false;
|
|
pcon_input_state = to_cyg;
|
|
last_sig = 0;
|
|
mask_flusho = false;
|
|
discard_input = false;
|
|
stop_fwd_thread = false;
|
|
}
|
|
|
|
HANDLE
|
|
tty::get_event (const char *fmt, PSECURITY_ATTRIBUTES sa, BOOL manual_reset)
|
|
{
|
|
HANDLE hev;
|
|
char buf[MAX_PATH];
|
|
|
|
shared_name (buf, fmt, get_minor ());
|
|
if (!sa)
|
|
sa = &sec_all;
|
|
if (!(hev = CreateEvent (sa, manual_reset, FALSE, buf)))
|
|
{
|
|
termios_printf ("couldn't create %s", buf);
|
|
set_errno (ENOENT); /* FIXME this can't be the right errno */
|
|
return NULL;
|
|
}
|
|
|
|
termios_printf ("created event %s", buf);
|
|
return hev;
|
|
}
|
|
|
|
lock_ttys::lock_ttys (DWORD howlong): release_me (true)
|
|
{
|
|
if (WaitForSingleObject (tty_list::mutex, howlong) == WAIT_FAILED)
|
|
{
|
|
termios_printf ("WFSO for mutex %p failed, %E", tty_list::mutex);
|
|
release_me = false;
|
|
}
|
|
}
|
|
|
|
void
|
|
lock_ttys::release ()
|
|
{
|
|
ReleaseMutex (tty_list::mutex);
|
|
}
|
|
|
|
const char *
|
|
tty_min::ttyname ()
|
|
{
|
|
device d;
|
|
d.parse (ntty);
|
|
return d.name ();
|
|
}
|
|
|
|
extern DWORD mutex_timeout; /* defined in fhandler_termios.cc */
|
|
|
|
void
|
|
tty_min::setpgid (int pid)
|
|
{
|
|
fhandler_pty_slave *ptys = NULL;
|
|
cygheap_fdenum cfd (false);
|
|
while (cfd.next () >= 0 && ptys == NULL)
|
|
if (cfd->get_device () == getntty ()
|
|
&& cfd->get_major () == DEV_PTYS_MAJOR)
|
|
ptys = (fhandler_pty_slave *) (fhandler_base *) cfd;
|
|
|
|
if (ptys)
|
|
{
|
|
tty *ttyp = (tty *) ptys->tc ();
|
|
WaitForSingleObject (ptys->pcon_mutex, INFINITE);
|
|
bool was_pcon_fg = ttyp->pcon_fg (pgid);
|
|
bool pcon_fg = ttyp->pcon_fg (pid);
|
|
if (!was_pcon_fg && pcon_fg && ttyp->switch_to_pcon_in
|
|
&& ttyp->pcon_input_state_eq (tty::to_cyg))
|
|
{
|
|
WaitForSingleObject (ptys->input_mutex, mutex_timeout);
|
|
fhandler_pty_slave::transfer_input (tty::to_nat,
|
|
ptys->get_handle (), ttyp,
|
|
ptys->get_input_available_event ());
|
|
ReleaseMutex (ptys->input_mutex);
|
|
}
|
|
else if (was_pcon_fg && !pcon_fg && ttyp->switch_to_pcon_in
|
|
&& ttyp->pcon_input_state_eq (tty::to_nat))
|
|
{
|
|
bool attach_restore = false;
|
|
HANDLE from = ptys->get_handle_nat ();
|
|
if (ttyp->pcon_activated && ttyp->pcon_pid
|
|
&& !ptys->get_console_process_id (ttyp->pcon_pid, true))
|
|
{
|
|
HANDLE pcon_owner =
|
|
OpenProcess (PROCESS_DUP_HANDLE, FALSE, ttyp->pcon_pid);
|
|
DuplicateHandle (pcon_owner, ttyp->h_pcon_in,
|
|
GetCurrentProcess (), &from,
|
|
0, TRUE, DUPLICATE_SAME_ACCESS);
|
|
CloseHandle (pcon_owner);
|
|
FreeConsole ();
|
|
AttachConsole (ttyp->pcon_pid);
|
|
init_console_handler (false);
|
|
attach_restore = true;
|
|
}
|
|
WaitForSingleObject (ptys->input_mutex, mutex_timeout);
|
|
fhandler_pty_slave::transfer_input (tty::to_cyg, from, ttyp,
|
|
ptys->get_input_available_event ());
|
|
ReleaseMutex (ptys->input_mutex);
|
|
if (attach_restore)
|
|
{
|
|
FreeConsole ();
|
|
pinfo p (myself->ppid);
|
|
if (!p || !AttachConsole (p->dwProcessId))
|
|
AttachConsole (ATTACH_PARENT_PROCESS);
|
|
init_console_handler (false);
|
|
}
|
|
}
|
|
ReleaseMutex (ptys->pcon_mutex);
|
|
}
|
|
pgid = pid;
|
|
}
|
|
|
|
void
|
|
tty::wait_pcon_fwd ()
|
|
{
|
|
/* The forwarding in pseudo console sometimes stops for
|
|
16-32 msec even if it already has data to transfer.
|
|
If the time without transfer exceeds 32 msec, the
|
|
forwarding is supposed to be finished. pcon_last_time
|
|
is reset to GetTickCount() in pty master forwarding
|
|
thread when the last data is transfered. */
|
|
const int sleep_in_pcon = 16;
|
|
const int time_to_wait = sleep_in_pcon * 2 + 1/* margine */;
|
|
int elapsed;
|
|
while (pcon_fwd_not_empty
|
|
|| (elapsed = GetTickCount () - pcon_last_time) < time_to_wait)
|
|
{
|
|
int tw = pcon_fwd_not_empty ? 10 : (time_to_wait - elapsed);
|
|
cygwait (tw);
|
|
}
|
|
}
|
|
|
|
bool
|
|
tty::pcon_fg (pid_t pgid)
|
|
{
|
|
/* Check if the terminal pgid matches with the pgid of the
|
|
non-cygwin process. */
|
|
winpids pids ((DWORD) 0);
|
|
for (unsigned i = 0; i < pids.npids; i++)
|
|
{
|
|
_pinfo *p = pids[i];
|
|
if (p->ctty == ntty && p->pgid == pgid
|
|
&& (p->process_state & (PID_NOTCYGWIN | PID_NEW_PG)))
|
|
return true;
|
|
}
|
|
if (pgid > MAX_PID)
|
|
return true;
|
|
return false;
|
|
}
|