mirror of
git://sourceware.org/git/newlib-cygwin.git
synced 2025-01-19 04:49:25 +08:00
4e08fe42c9
- Pseudo console internally sends escape sequence CSI6n (query cursor position) on startup of non-cygwin apps. If the terminal does not support CSI6n, CreateProcess() hangs waiting for response. To prevent hang, this patch disables pseudo console if the terminal does not have CSI6n. This is checked on the first execution of non-cygwin app using the following steps. 1) Check if the terminal support ANSI escape sequences by looking into terminfo database. If terminfo has cursor_home (ESC [H), the terminal is supposed to support ANSI escape sequences. 2) If the terminal supports ANSI escape sequneces, send CSI6n for a test and wait for a responce for 40ms. 3) If there is a responce within 40ms, CSI6n is supposed to be supported. Also set-title capability is checked, and removes escape sequence for setting window title if the terminal does not have the set- title capability.
311 lines
6.0 KiB
C++
311 lines
6.0 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;
|
|
h_pseudo_console = NULL;
|
|
switch_to_pcon_in = false;
|
|
mask_switch_to_pcon_in = false;
|
|
pcon_pid = 0;
|
|
term_code_page = 0;
|
|
pcon_last_time = 0;
|
|
pcon_start = false;
|
|
pcon_cap_checked = false;
|
|
has_csi6n = false;
|
|
has_set_title = 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 ();
|
|
}
|
|
|
|
void
|
|
tty::wait_pcon_fwd (void)
|
|
{
|
|
/* 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 */;
|
|
pcon_last_time = GetTickCount ();
|
|
while (GetTickCount () - pcon_last_time < time_to_wait)
|
|
{
|
|
int tw = time_to_wait - (GetTickCount () - pcon_last_time);
|
|
cygwait (tw);
|
|
}
|
|
}
|