newlib/winsup/cygwin/tty.cc
Corinna Vinschen cc01c77f7e * autoload.cc (GetNamedPipeClientProcessId): Define.
* fhandler.h (fhandler_tty_slave::fch_open_handles): Declare private.
	(fhandler_tty_slave::fch_close_handles): Ditto.
	(fhandler_tty_slave::cygserver_attach_tty): Drop declaration.
	(fhandler_tty_slave::fstat): Declare public.
	(fhandler_tty_slave::fchmod): Declare public.
	(fhandler_tty_slave::fchown): Declare public.
	(class fhandler_pty_master): Add master_ctl handle.
	(fhandler_pty_master::pty_master_thread): Declare public.
	* fhandler_termios.cc (fhandler_termios::tcinit): If the process
	is started from a non-Cygwin process, make it tty process group
	leader.
	* fhandler_tty.cc: Throughout accommodate additional security related
	arguments in calls to functions creating or opening objects.
	(close_maybe): Move to start of file to reuse it
	in other methods.
	(struct pipe_request): Define.
	(struct pipe_reply): Define.
	(fhandler_tty_slave::open): Throughout, try to open synchronization
	objects with MAXIMUM_ALLOWED permissions.  Drop call to cygserver.
	Try to duplicate pipe handles via master_ctl pipe if duplicating
	directly doesn't work.
	(fhandler_tty_slave::cygserver_attach_tty): Remove.
	(fhandler_tty_slave::init): Close unused incoming pipe handle.
	(fhandler_pty_master::close): Send exit message to master control
	thread and close master_ctl handle.
	(fhandler_pty_master::pty_master_thread): New method, implementing the
	master control thread.
	(pty_master_thread): Static helper to start master control thread.
	(fhandler_pty_master::setup): Simplify creating pipe inheritance.
	Make sure we're the one creating the input_available_event.  Add
	comment to explain why.  Create master_ctl pipe and start master
	control thread.  Close master_ctl handle in case of error.
	* security.cc (alloc_sd): Add code to handle tty objects.  Add comments
	to explain what exactly is required.
	(get_object_sd): New function.
	(get_object_attribute): New function.
	(create_object_sd_from_attribute): New function.
	(set_object_sd): New function.
	(set_object_attribute): New function.
	(set_file_attribute): Change attribute type to mode_t.
	* security.h (set_file_attribute): Change attribute type to mode_t.
	(get_object_sd): Declare.
	(get_object_attribute): Declare.
	(create_object_sd_from_attribute): Declare.
	(set_object_sd): Declare.
	(set_object_attribute): Declare.
	* tty.cc (tty::slave_alive): Implement directly instead of via alive.
	(tty::exists): Open mutex handle with READ_CONTROL access.
	(tty::alive): Remove.
	(tty::open_output_mutex): Convert to inline method.
	(tty::open_input_mutex): Ditto.
	(tty::open_mutex): Take additional ACCESS_MASK parameter for the
	mutex open access mask.
	(tty::open_inuse): New method.
	(tty::create_inuse): Take PSECURITY_ATTRIBUTES parameter.  Drop fmt
	name parameter.  Always create TTY_SLAVE_ALIVE event.
	(tty::get_event): Take additional PSECURITY_ATTRIBUTES parameter for
	CreateEvent.
	* tty.h (class tty): Change declarations according to aforementioned
	changes.
	(tty::open_output_mutex): Implement as inline method.
	(tty::open_input_mutex): Ditto.
2010-04-19 19:52:43 +00:00

397 lines
8.4 KiB
C++

/* tty.cc
Copyright 1997, 1998, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2008, 2009,
2010 Red Hat, Inc.
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"
extern fhandler_tty_master *tty_master;
extern "C" int
posix_openpt (int oflags)
{
return open ("/dev/ptmx", oflags);
}
extern "C" int
grantpt (int fd)
{
return 0;
}
extern "C" int
unlockpt (int fd)
{
return 0;
}
extern "C" int
revoke (char *ttyname)
{
set_errno (ENOSYS);
return -1;
}
extern "C" int
ttyslot (void)
{
if (NOTSTATE (myself, PID_USETTY))
return -1;
return myself->ctty;
}
HANDLE NO_COPY tty_list::mutex = NULL;
void __stdcall
tty_list::init_session ()
{
char mutex_name[MAX_PATH];
/* tty_list::mutex is used while searching for a tty slot. It's necessary
while finding console window handle */
char *name = shared_name (mutex_name, "tty_list::mutex", 0);
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 ();
if (NOTSTATE (myself, PID_USETTY))
return;
if (myself->ctty != -1)
/* nothing to do */;
else if (NOTSTATE (myself, PID_CYGPARENT))
myself->ctty = cygwin_shared->tty.attach (myself->ctty);
else
return;
if (myself->ctty == -1)
termios_printf ("Can't attach to tty");
}
/* Create session's master tty */
void __stdcall
tty::create_master (int ttynum)
{
device ttym = *ttym_dev;
ttym.setunit (ttynum); /* CGF FIXME device */
tty_master = (fhandler_tty_master *) build_fh_dev (ttym);
if (tty_master->init ())
api_fatal ("can't create master tty");
else
{
/* Log utmp entry */
struct utmp our_utmp;
DWORD len = sizeof our_utmp.ut_host;
bzero ((char *) &our_utmp, sizeof (utmp));
time (&our_utmp.ut_time);
strncpy (our_utmp.ut_name, getlogin (), sizeof (our_utmp.ut_name));
GetComputerName (our_utmp.ut_host, &len);
__small_sprintf (our_utmp.ut_line, "tty%d", ttynum);
if ((len = strlen (our_utmp.ut_line)) >= UT_IDLEN)
len -= UT_IDLEN;
else
len = 0;
strncpy (our_utmp.ut_id, our_utmp.ut_line + len, UT_IDLEN);
our_utmp.ut_type = USER_PROCESS;
our_utmp.ut_pid = myself->pid;
myself->ctty = ttynum;
login (&our_utmp);
}
}
int __stdcall
tty_list::attach (int num)
{
if (num != -1)
{
return connect (num);
}
if (NOTSTATE (myself, PID_USETTY))
return -1;
return allocate (true);
}
void
tty_list::terminate ()
{
if (NOTSTATE (myself, PID_USETTY))
return;
int ttynum = myself->ctty;
/* Keep master running till there are connected clients */
if (ttynum != -1 && tty_master && ttys[ttynum].master_pid == myself->pid)
{
tty *t = ttys + ttynum;
/* Wait for children which rely on tty handling in this process to
go away */
for (int i = 0; ; i++)
{
if (!t->slave_alive ())
break;
if (i >= 100)
{
small_printf ("waiting for children using tty%d to terminate\n",
ttynum);
i = 0;
}
Sleep (200);
}
lock_ttys here ();
CloseHandle (tty_master->from_master);
CloseHandle (tty_master->to_master);
termios_printf ("tty %d master about to finish", ttynum);
CloseHandle (tty_master->get_io_handle ());
CloseHandle (tty_master->get_output_handle ());
t->init ();
char buf[20];
__small_sprintf (buf, "tty%d", ttynum);
logout (buf);
}
}
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 ("tty %d was not allocated", ttynum);
return -1;
}
return ttynum;
}
void
tty_list::init ()
{
for (int i = 0; i < NTTYS; i++)
{
ttys[i].init ();
ttys[i].setntty (i);
}
}
/* Search for tty class for our console. Allocate new tty if our process is
the only cygwin process in the current console.
Return tty number or -1 if error.
If with_console == 0, just find a free tty.
*/
int
tty_list::allocate (bool with_console)
{
HWND console;
int freetty = -1;
HANDLE hmaster = NULL;
lock_ttys here;
if (!with_console)
console = NULL;
else if (!(console = GetConsoleWindow ()))
{
termios_printf ("Can't find console window");
goto out;
}
/* Is a tty allocated for console? */
for (int i = 0; i < NTTYS; i++)
{
if (!ttys[i].exists ())
{
if (freetty < 0) /* Scanning? */
freetty = i; /* Yes. */
if (!with_console) /* Do we want to attach this to a console? */
break; /* No. We've got one. */
}
/* FIXME: Is this right? We can potentially query a "nonexistent"
tty slot after falling through from the above? */
if (with_console && ttys[i].gethwnd () == console)
{
termios_printf ("console %x already associated with tty%d",
console, i);
/* Is the master alive? */
hmaster = OpenProcess (PROCESS_DUP_HANDLE, FALSE, ttys[i].master_pid);
if (hmaster)
{
CloseHandle (hmaster);
freetty = i;
goto out;
}
/* Master is dead */
freetty = i;
break;
}
}
/* There is no tty allocated to console; allocate the first free found */
if (freetty == -1)
goto out;
tty *t;
t = ttys + freetty;
t->init ();
t->setsid (-1);
t->sethwnd (console);
out:
if (freetty < 0)
system_printf ("No tty allocated");
else if (!with_console)
{
termios_printf ("tty%d allocated", freetty);
here.dont_release (); /* exit with mutex still held -- caller has more work to do */
}
else
{
termios_printf ("console %p associated with tty%d", console, freetty);
if (!hmaster)
tty::create_master (freetty);
}
return freetty;
}
bool
tty::exists ()
{
/* Attempt to open the from-master side of the tty. If it is accessible
then it exists although it may have been privileges to actually use it. */
char pipename[sizeof("ttyNNNN-from-master")];
__small_sprintf (pipename, "tty%d-from-master", ntty);
HANDLE r, w;
int res = fhandler_pipe::create_selectable (&sec_none_nih, r, w, 0, pipename);
if (res)
return true;
CloseHandle (r);
CloseHandle (w);
HANDLE h = open_output_mutex (READ_CONTROL);
if (h)
{
CloseHandle (h);
return true;
}
return slave_alive ();
}
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, ntty);
return OpenMutex (access, TRUE, buf);
}
HANDLE
tty::open_inuse (ACCESS_MASK access)
{
char buf[MAX_PATH];
shared_name (buf, TTY_SLAVE_ALIVE, ntty);
return OpenEvent (access, FALSE, buf);
}
HANDLE
tty::create_inuse (PSECURITY_ATTRIBUTES sa)
{
HANDLE h;
char buf[MAX_PATH];
shared_name (buf, TTY_SLAVE_ALIVE, ntty);
h = CreateEvent (sa, TRUE, FALSE, buf);
termios_printf ("%s %p", buf, h);
if (!h)
termios_printf ("couldn't open inuse event, %E", buf);
return h;
}
void
tty::init ()
{
output_stopped = 0;
setsid (0);
pgid = 0;
hwnd = NULL;
was_opened = 0;
master_pid = 0;
}
HANDLE
tty::get_event (const char *fmt, PSECURITY_ATTRIBUTES sa, BOOL manual_reset)
{
HANDLE hev;
char buf[MAX_PATH];
shared_name (buf, fmt, ntty);
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);
}