diff options
author | Christopher Faylor <cgf@redhat.com> | 2003-09-19 03:55:54 +0000 |
---|---|---|
committer | Christopher Faylor <cgf@redhat.com> | 2003-09-19 03:55:54 +0000 |
commit | 8baf9504efc1777cfc7642114bcbc927e80aee1d (patch) | |
tree | 082b1c75322f0b607742ce6d65a1f2e0c99b80e4 /winsup | |
parent | 6fd775d900181e93204b6c58065fcb9be07cd437 (diff) | |
download | gdb-8baf9504efc1777cfc7642114bcbc927e80aee1d.tar.gz |
* fhandler_tty.cc (fhandler_pty_master::process_slave_output): Handle buf ==
NULL as flushing the buffer.
(fhandler_tty_slave::read): Handle ptr == NULL as flushing the buffer.
(fhandler_tty_slave::tcflush): Implement input queue flushing by calling read
with NULL buffer.
(fhandler_pty_master::tcflush): Ditto, calling process_slave_output.
* termios.cc (tcflush): Check for legal `queue' value. Return EINVAL
otherwise.
* syscalls.cc (gethostid): Add lpFreeBytesAvailable argument to
GetDiskFreeSpaceEx call since NT4 requires it.
Diffstat (limited to 'winsup')
-rw-r--r-- | winsup/cygwin/ChangeLog | 20 | ||||
-rw-r--r-- | winsup/cygwin/fhandler_tty.cc | 1361 | ||||
-rw-r--r-- | winsup/cygwin/syscalls.cc | 2985 | ||||
-rw-r--r-- | winsup/cygwin/termios.cc | 307 |
4 files changed, 4671 insertions, 2 deletions
diff --git a/winsup/cygwin/ChangeLog b/winsup/cygwin/ChangeLog index 490a1883378..021a445453e 100644 --- a/winsup/cygwin/ChangeLog +++ b/winsup/cygwin/ChangeLog @@ -1,3 +1,19 @@ +2003-09-18 Corinna Vinschen <corinna@vinschen.de> + + * fhandler_tty.cc (fhandler_pty_master::process_slave_output): Handle + buf == NULL as flushing the buffer. + (fhandler_tty_slave::read): Handle ptr == NULL as flushing the buffer. + (fhandler_tty_slave::tcflush): Implement input queue flushing by + calling read with NULL buffer. + (fhandler_pty_master::tcflush): Ditto, calling process_slave_output. + * termios.cc (tcflush): Check for legal `queue' value. Return EINVAL + otherwise. + +2003-09-18 Brian Ford <ford@vss.fsi.com> + + * syscalls.cc (gethostid): Add lpFreeBytesAvailable argument to + GetDiskFreeSpaceEx call since NT4 requires it. + 2003-09-18 Christopher Faylor <cgf@redhat.com> * fhandler_disk_file.cc (path_conv::ndisk_links): Fix potential @@ -169,11 +185,11 @@ * security.h: Add _SECURITY_H guard. (sec_user): Use sec_none in the no ntsec case. * spawn.cc (spawn_guts): Remove call to load_registry_hive. - * syscalls (seteuid32): If warranted, call load_registry_hive, + * syscalls (seteuid32): If warranted, call load_registry_hive, user_shared_initialize and RegCloseKey(HKEY_CURRENT_USER). * shared.cc (user_shared_initialize): New. (open_shared): Add and use psa argument. - (memory_init): Move mount table initialization to + (memory_init): Move mount table initialization to user_shared_initialize. Call it. 2003-09-09 Corinna Vinschen <corinna@vinschen.de> diff --git a/winsup/cygwin/fhandler_tty.cc b/winsup/cygwin/fhandler_tty.cc new file mode 100644 index 00000000000..1bb3d87a762 --- /dev/null +++ b/winsup/cygwin/fhandler_tty.cc @@ -0,0 +1,1361 @@ +/* fhandler_tty.cc + + Copyright 1997, 1998, 2000, 2001, 2002, 2003 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 <wingdi.h> +#include <winuser.h> +#include <stdio.h> +#include <unistd.h> +#include <stdlib.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 thread +static DWORD WINAPI process_output (void *); // Output queue thread +static DWORD WINAPI process_ioctl (void *); // Ioctl requests thread + +fhandler_tty_master::fhandler_tty_master (int unit) + : fhandler_pty_master (FH_TTYM, unit), console (NULL) +{ +} + +void +fhandler_tty_master::set_winsize (bool sendSIGWINCH) +{ + winsize w; + console->ioctl (TIOCGWINSZ, &w); + get_ttyp ()->winsize = w; + if (sendSIGWINCH) + tc->kill_pgrp (SIGWINCH); +} + +int +fhandler_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); + + set_winsize (false); + + 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 DEBUGGING +static class mutex_stack +{ +public: + const char *fn; + int ln; + const char *tname; +} ostack[100]; + +static int osi; +#endif /*DEBUGGING*/ + +DWORD +fhandler_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; +} + +void +fhandler_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. */ + +void +fhandler_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 (); +} + +int +fhandler_pty_master::accept_input () +{ + DWORD bytes_left; + int ret = 1; + + (void) WaitForSingleObject (input_mutex, INFINITE); + + bytes_left = eat_readahead (-1); + + if (!bytes_left) + { + termios_printf ("sending EOF to slave"); + get_ttyp ()->read_retval = 0; + } + else + { + char *p = rabuf; + DWORD rc; + DWORD written = 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"); + get_ttyp ()->read_retval = -1; + ret = -1; + } + else + { + get_ttyp ()->read_retval = 1; + p += written; + bytes_left -= written; + if (bytes_left > 0) + { + debug_printf ("to_slave pipe is full"); + puts_readahead (p, bytes_left); + ret = 0; + } + } + } + + SetEvent (input_available_event); + ReleaseMutex (input_mutex); + return ret; +} + +static DWORD WINAPI +process_input (void *) +{ + char rawbuf[INP_BUFFER_SIZE]; + + while (1) + { + size_t nraw = INP_BUFFER_SIZE; + tty_master->console->read ((void *) rawbuf, nraw); + if (tty_master->line_edit (rawbuf, nraw, tty_master->get_ttyp ()->ti) + == line_edit_signalled) + tty_master->console->eat_readahead (-1); + } +} + +bool +fhandler_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 */ + +int +fhandler_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. */ + if (buf) + 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; + /* DISCARD (FLUSHO) and tcflush can finish here. */ + if (n == 0 && (get_ttyp ()->ti.c_lflag & FLUSHO || !buf)) + 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 || !buf) + 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 WINAPI +process_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 WINAPI +process_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. */ +int +fhandler_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; + +#ifdef USE_SERVER + if (!wincap.has_security () + || cygserver_running == CYGSERVER_UNAVAIL + || !cygserver_attach_tty (&from_master_local, &to_master_local)) +#endif + { + 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 (); + if (fhandler_console::open_fhs++ == 0 && !GetConsoleCP () + && !output_done_event && wincap.pty_needs_alloc_console ()) + { + BOOL b; + HWINSTA h = CreateWindowStation (NULL, 0, GENERIC_READ | GENERIC_WRITE, &sec_none_nih); + termios_printf ("CreateWindowStation %p, %E", h); + if (h) + { + b = SetProcessWindowStation (h); + termios_printf ("SetProcessWindowStation %d, %E", b); + } + b = AllocConsole (); // will cause flashing if workstation + // stuff fails + termios_printf ("%d = AllocConsole (), %E", b); + if (b) + init_console_handler (); + } + termios_printf ("incremented open_fhs %d", fhandler_console::open_fhs); + termios_printf ("tty%d opened", ttynum); + + return 1; +} + +int +fhandler_tty_slave::close () +{ + if (!--fhandler_console::open_fhs && myself->ctty == -1) + FreeConsole (); + termios_printf ("decremented open_fhs %d", fhandler_console::open_fhs); + return fhandler_tty_common::close (); +} + +int +fhandler_tty_slave::cygserver_attach_tty (LPHANDLE from_master_ptr, + LPHANDLE to_master_ptr) +{ +#ifndef USE_SERVER + return 0; +#else + 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; +#endif +} + +void +fhandler_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); +} + +int +fhandler_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); + } + raise (SIGHUP); /* FIXME: Should this be SIGTTOU? */ + towrite = (DWORD) -1; + break; + } + + if (output_done_event != NULL) + { + DWORD rc; + DWORD x = n * 1000; + rc = WaitForSingleObject (output_done_event, x); + termios_printf ("waited %d ms for output_done_event, WFSO %d", x, rc); + } + } + release_output_mutex (); + return towrite; +} + +void __stdcall +fhandler_tty_slave::read (void *ptr, size_t& len) +{ + int totalread = 0; + int vmin = 0; + int vtime = 0; /* Initialized to prevent -Wuninitialized warning */ + size_t readlen; + DWORD bytes_in_pipe; + char buf[INP_BUFFER_SIZE]; + char peek_buf[INP_BUFFER_SIZE]; + DWORD time_to_wait; + DWORD rc; + HANDLE w4[2]; + + termios_printf ("read(%x, %d) handle %p", ptr, len, get_handle ()); + + if (!ptr) /* Indicating tcflush(). */ + time_to_wait = 0; + else if ((get_ttyp ()->ti.c_lflag & ICANON)) + time_to_wait = INFINITE; + else + { + vmin = get_ttyp ()->ti.c_cc[VMIN]; + if (vmin > INP_BUFFER_SIZE) + vmin = INP_BUFFER_SIZE; + vtime = get_ttyp ()->ti.c_cc[VTIME]; + if (vmin < 0) + vmin = 0; + if (vtime < 0) + vtime = 0; + if (!vmin && !vtime) + time_to_wait = 0; + else + time_to_wait = !vtime ? INFINITE : 100 * vtime; + } + + w4[0] = signal_arrived; + w4[1] = input_available_event; + + DWORD waiter = !ptr ? 0 : INFINITE; + while (len) + { + rc = WaitForMultipleObjects (2, w4, FALSE, waiter); + + if (rc == WAIT_TIMEOUT) + break; + + if (rc == WAIT_FAILED) + { + termios_printf ("wait for input event failed, %E"); + break; + } + + if (rc == WAIT_OBJECT_0) + { + /* if we've received signal after successfully reading some data, + just return all data successfully read */ + if (totalread > 0) + break; + set_sig_errno (EINTR); + len = (size_t) -1; + return; + } + + rc = WaitForSingleObject (input_mutex, 1000); + if (rc == WAIT_FAILED) + { + termios_printf ("wait for input mutex failed, %E"); + break; + } + else if (rc == WAIT_TIMEOUT) + { + termios_printf ("failed to acquire input mutex after input event arrived"); + break; + } + if (!PeekNamedPipe (get_handle (), peek_buf, sizeof (peek_buf), &bytes_in_pipe, NULL, NULL)) + { + termios_printf ("PeekNamedPipe failed, %E"); + raise (SIGHUP); + bytes_in_pipe = 0; + } + + /* On first peek determine no. of bytes to flush. */ + if (!ptr && len == UINT_MAX) + len = (size_t) bytes_in_pipe; + + if (ptr && !vmin && !time_to_wait) + { + ReleaseMutex (input_mutex); + len = (size_t) bytes_in_pipe; + return; + } + + readlen = min (bytes_in_pipe, min (len, sizeof (buf))); + + if (ptr && vmin && readlen > (unsigned) vmin) + readlen = vmin; + + DWORD n = 0; + if (readlen) + { + termios_printf ("reading %d bytes (vtime %d)", readlen, vtime); + if (ReadFile (get_handle (), buf, readlen, &n, NULL) == FALSE) + { + termios_printf ("read failed, %E"); + raise (SIGHUP); + } + /* MSDN states that 5th prameter can be used to determine total + number of bytes in pipe, but for some reason this number doesn't + change after successful read. So we have to peek into the pipe + again to see if input is still available */ + if (!PeekNamedPipe (get_handle (), peek_buf, 1, &bytes_in_pipe, NULL, NULL)) + { + termios_printf ("PeekNamedPipe failed, %E"); + raise (SIGHUP); + bytes_in_pipe = 0; + } + if (n) + { + len -= n; + totalread += n; + if (ptr) + { + memcpy (ptr, buf, n); + ptr = (char *) ptr + n; + } + } + } + + if (!bytes_in_pipe) + ResetEvent (input_available_event); + + ReleaseMutex (input_mutex); + + if (!ptr) + { + if (!bytes_in_pipe) + break; + continue; + } + + if (get_ttyp ()->read_retval < 0) // read error + { + set_errno (-get_ttyp ()->read_retval); + totalread = -1; + break; + } + if (get_ttyp ()->read_retval == 0) //EOF + { + termios_printf ("saw EOF"); + break; + } + if (get_ttyp ()->ti.c_lflag & ICANON || is_nonblocking ()) + break; + if (vmin && totalread >= vmin) + break; + + /* vmin == 0 && vtime == 0: + * we've already read all input, if any, so return immediately + * vmin == 0 && vtime > 0: + * we've waited for input 10*vtime ms in WFSO(input_available_event), + * no matter whether any input arrived, we shouldn't wait any longer, + * so return immediately + * vmin > 0 && vtime == 0: + * here, totalread < vmin, so continue waiting until more data + * arrive + * vmin > 0 && vtime > 0: + * similar to the previous here, totalread < vmin, and timer + * hadn't expired -- WFSO(input_available_event) != WAIT_TIMEOUT, + * so "restart timer" and wait until more data arrive + */ + + if (vmin == 0) + break; + + if (n) + waiter = time_to_wait; + } + termios_printf ("%d=read(%x, %d)", totalread, ptr, len); + len = (size_t) totalread; + return; +} + +int +fhandler_tty_slave::dup (fhandler_base *child) +{ + fhandler_console::open_fhs++; + termios_printf ("incremented open_fhs %d", fhandler_console::open_fhs); + return fhandler_tty_common::dup (child); +} + +int +fhandler_tty_common::dup (fhandler_base *child) +{ + fhandler_tty_slave *fts = (fhandler_tty_slave *) child; + int errind; + + fts->ttynum = ttynum; + fts->tcinit (get_ttyp ()); + + attach_tty (ttynum); + tc->set_ctty (ttynum, openflags); + + HANDLE nh; + + if (output_done_event == NULL) + fts->output_done_event = NULL; + else if (!DuplicateHandle (hMainProc, output_done_event, hMainProc, + &fts->output_done_event, 0, 1, + DUPLICATE_SAME_ACCESS)) + { + errind = 1; + goto err; + } + if (ioctl_request_event == NULL) + fts->ioctl_request_event = NULL; + else if (!DuplicateHandle (hMainProc, ioctl_request_event, hMainProc, + &fts->ioctl_request_event, 0, 1, + DUPLICATE_SAME_ACCESS)) + { + errind = 2; + goto err; + } + if (ioctl_done_event == NULL) + fts->ioctl_done_event = NULL; + else if (!DuplicateHandle (hMainProc, ioctl_done_event, hMainProc, + &fts->ioctl_done_event, 0, 1, + DUPLICATE_SAME_ACCESS)) + { + errind = 3; + goto err; + } + if (!DuplicateHandle (hMainProc, input_available_event, hMainProc, + &fts->input_available_event, 0, 1, + DUPLICATE_SAME_ACCESS)) + { + errind = 4; + goto err; + } + if (!DuplicateHandle (hMainProc, output_mutex, hMainProc, + &fts->output_mutex, 0, 1, + DUPLICATE_SAME_ACCESS)) + { + errind = 5; + goto err; + } + if (!DuplicateHandle (hMainProc, input_mutex, hMainProc, + &fts->input_mutex, 0, 1, + DUPLICATE_SAME_ACCESS)) + { + errind = 6; + goto err; + } + if (!DuplicateHandle (hMainProc, get_handle (), hMainProc, + &nh, 0, 1, + DUPLICATE_SAME_ACCESS)) + { + errind = 7; + goto err; + } + fts->set_io_handle (nh); + + if (!DuplicateHandle (hMainProc, get_output_handle (), hMainProc, + &nh, 0, 1, + DUPLICATE_SAME_ACCESS)) + { + errind = 8; + goto err; + } + fts->set_output_handle (nh); + + if (inuse == NULL) + fts->inuse = NULL; + else if (!DuplicateHandle (hMainProc, inuse, hMainProc, + &fts->inuse, 0, 1, + DUPLICATE_SAME_ACCESS)) + { + errind = 9; + goto err; + } + return 0; + +err: + __seterrno (); + termios_printf ("dup %d failed in DuplicateHandle, %E", errind); + return -1; +} + +int +fhandler_tty_slave::tcgetattr (struct termios *t) +{ + *t = get_ttyp ()->ti; + return 0; +} + +int +fhandler_tty_slave::tcsetattr (int, const struct termios *t) +{ + acquire_output_mutex (INFINITE); + get_ttyp ()->ti = *t; + release_output_mutex (); + return 0; +} + +int +fhandler_tty_slave::tcflush (int queue) +{ + int ret = 0; + + termios_printf ("tcflush(%d) handle %p", queue, get_handle ()); + + if (queue == TCIFLUSH || queue == TCIOFLUSH) + { + size_t len = UINT_MAX; + read (NULL, len); + ret = len >= 0; + } + if (queue == TCOFLUSH || queue == TCIOFLUSH) + { + /* do nothing for now. */ + } + + termios_printf ("%d=tcflush(%d)", ret, queue); + return ret; +} + +int +fhandler_tty_slave::ioctl (unsigned int cmd, void *arg) +{ + termios_printf ("ioctl (%x)", cmd); + + if (myself->pgid && get_ttyp ()->getpgid () != myself->pgid + && myself->ctty == ttynum && (get_ttyp ()->ti.c_lflag & TOSTOP)) + { + /* background process */ + termios_printf ("bg ioctl pgid %d, tpgid %d, ctty %d", + myself->pgid, get_ttyp ()->getpgid (), myself->ctty); + raise (SIGTTOU); + } + + switch (cmd) + { + case TIOCGWINSZ: + case TIOCSWINSZ: + break; + case FIONBIO: + set_nonblocking (*(int *) arg); + goto out; + default: + set_errno (EINVAL); + return -1; + } + + acquire_output_mutex (INFINITE); + + get_ttyp ()->cmd = cmd; + get_ttyp ()->ioctl_retval = 0; + switch (cmd) + { + case TIOCGWINSZ: + get_ttyp ()->arg.winsize = get_ttyp ()->winsize; + if (ioctl_request_event) + SetEvent (ioctl_request_event); + *(struct winsize *) arg = get_ttyp ()->arg.winsize; + if (ioctl_done_event) + WaitForSingleObject (ioctl_done_event, INFINITE); + get_ttyp ()->winsize = get_ttyp ()->arg.winsize; + break; + case TIOCSWINSZ: + if (get_ttyp ()->winsize.ws_row != ((struct winsize *) arg)->ws_row + || get_ttyp ()->winsize.ws_col != ((struct winsize *) arg)->ws_col) + { + get_ttyp ()->arg.winsize = *(struct winsize *) arg; + if (ioctl_request_event) + { + get_ttyp ()->ioctl_retval = -1; + SetEvent (ioctl_request_event); + } + else + { + get_ttyp ()->winsize = *(struct winsize *) arg; + kill (-get_ttyp ()->getpgid (), SIGWINCH); + } + if (ioctl_done_event) + WaitForSingleObject (ioctl_done_event, INFINITE); + } + break; + } + + release_output_mutex (); + +out: + termios_printf ("%d = ioctl (%x)", get_ttyp ()->ioctl_retval, cmd); + return get_ttyp ()->ioctl_retval; +} + +/******************************************************* + fhandler_pty_master +*/ +fhandler_pty_master::fhandler_pty_master (DWORD devtype, int unit) + : fhandler_tty_common (devtype, unit) +{ +} + +int +fhandler_pty_master::open (path_conv *, int flags, mode_t) +{ + ttynum = cygwin_shared->tty.allocate_tty (0); + if (ttynum < 0) + return 0; + + cygwin_shared->tty[ttynum]->common_init (this); + inuse = get_ttyp ()->create_inuse (TTY_MASTER_ALIVE); + set_flags ((flags & ~O_TEXT) | O_BINARY); + set_open_status (); + + termios_printf ("opened pty master tty%d<%p>", ttynum, this); + return 1; +} + +int +fhandler_tty_common::close () +{ + if (output_done_event && !CloseHandle (output_done_event)) + termios_printf ("CloseHandle (output_done_event), %E"); + if (ioctl_done_event && !CloseHandle (ioctl_done_event)) + termios_printf ("CloseHandle (ioctl_done_event), %E"); + if (ioctl_request_event && !CloseHandle (ioctl_request_event)) + termios_printf ("CloseHandle (ioctl_request_event), %E"); + if (inuse && !CloseHandle (inuse)) + termios_printf ("CloseHandle (inuse), %E"); + if (!ForceCloseHandle (input_mutex)) + termios_printf ("CloseHandle (input_mutex<%p>), %E", input_mutex); + if (!ForceCloseHandle (output_mutex)) + termios_printf ("CloseHandle (output_mutex<%p>), %E", output_mutex); + + /* Send EOF to slaves if master side is closed */ + if (!get_ttyp ()->master_alive ()) + { + termios_printf ("no more masters left. sending EOF" ); + SetEvent (input_available_event); + } + + if (!ForceCloseHandle (input_available_event)) + termios_printf ("CloseHandle (input_available_event<%p>), %E", input_available_event); + if (!ForceCloseHandle1 (get_handle (), from_pty)) + termios_printf ("CloseHandle (get_handle ()<%p>), %E", get_handle ()); + if (!ForceCloseHandle1 (get_output_handle (), to_pty)) + termios_printf ("CloseHandle (get_output_handle ()<%p>), %E", get_output_handle ()); + + inuse = NULL; + termios_printf ("tty%d <%p,%p> closed", ttynum, get_handle (), get_output_handle ()); + return 0; +} + +int +fhandler_pty_master::close () +{ +#if 0 + while (accept_input () > 0) + continue; +#endif + fhandler_tty_common::close (); + + if (!get_ttyp ()->master_alive ()) + { + termios_printf ("freeing tty%d (%d)", ttynum, get_ttyp ()->ntty); +#if 0 + if (get_ttyp ()->to_slave) + ForceCloseHandle1 (get_ttyp ()->to_slave, to_slave); + if (get_ttyp ()->from_slave) + ForceCloseHandle1 (get_ttyp ()->from_slave, from_slave); +#endif + if (get_ttyp ()->from_master) + CloseHandle (get_ttyp ()->from_master); + if (get_ttyp ()->to_master) + CloseHandle (get_ttyp ()->to_master); + get_ttyp ()->init (); + } + + return 0; +} + +int +fhandler_pty_master::write (const void *ptr, size_t len) +{ + int i; + char *p = (char *) ptr; + termios ti = tc->ti; + + for (i = 0; i < (int) len; i++) + { + line_edit_status status = line_edit (p++, 1, ti); + if (status > line_edit_signalled) + { + if (status != line_edit_pipe_full) + i = -1; + break; + } + } + return i; +} + +void __stdcall +fhandler_pty_master::read (void *ptr, size_t& len) +{ + len = (size_t) process_slave_output ((char *) ptr, len, pktmode); + return; +} + +int +fhandler_pty_master::tcgetattr (struct termios *t) +{ + *t = cygwin_shared->tty[ttynum]->ti; + return 0; +} + +int +fhandler_pty_master::tcsetattr (int, const struct termios *t) +{ + cygwin_shared->tty[ttynum]->ti = *t; + return 0; +} + +int +fhandler_pty_master::tcflush (int queue) +{ + int ret = 0; + + termios_printf ("tcflush(%d) handle %p", queue, get_handle ()); + + if (queue == TCIFLUSH || queue == TCIOFLUSH) + ret = process_slave_output (NULL, OUT_BUFFER_SIZE, 0); + else if (queue == TCIFLUSH || queue == TCIOFLUSH) + { + /* do nothing for now. */ + } + + termios_printf ("%d=tcflush(%d)", ret, queue); + return ret; +} + +int +fhandler_pty_master::ioctl (unsigned int cmd, void *arg) +{ + switch (cmd) + { + case TIOCPKT: + pktmode = * (int *) arg; + break; + case TIOCGWINSZ: + *(struct winsize *) arg = get_ttyp ()->winsize; + break; + case TIOCSWINSZ: + if (get_ttyp ()->winsize.ws_row != ((struct winsize *) arg)->ws_row + || get_ttyp ()->winsize.ws_col != ((struct winsize *) arg)->ws_col) + { + get_ttyp ()->winsize = * (struct winsize *) arg; + kill (-get_ttyp ()->getpgid (), SIGWINCH); + } + break; + case FIONBIO: + set_nonblocking (*(int *) arg); + break; + default: + set_errno (EINVAL); + return -1; + } + return 0; +} + +char * +fhandler_pty_master::ptsname (void) +{ + static char buf[32]; + + __small_sprintf (buf, "/dev/tty%d", ttynum); + return buf; +} + +void +fhandler_tty_common::set_close_on_exec (int val) +{ +#ifndef DEBUGGING + fhandler_base::set_close_on_exec (val); +#else + /* FIXME: This is a duplication from fhandler_base::set_close_on_exec. + It is here because we need to specify the "from_pty" stuff here or + we'll get warnings from ForceCloseHandle when debugging. */ + set_inheritance (get_io_handle (), val); + set_close_on_exec_flag (val); +#endif + if (output_done_event) + set_inheritance (output_done_event, val); + if (ioctl_request_event) + set_inheritance (ioctl_request_event, val); + if (ioctl_done_event) + set_inheritance (ioctl_done_event, val); + if (inuse) + set_inheritance (inuse, val); + set_inheritance (output_mutex, val); + set_inheritance (input_mutex, val); + set_inheritance (input_available_event, val); + set_inheritance (output_handle, val); +} + +void +fhandler_tty_slave::fixup_after_fork (HANDLE parent) +{ + fhandler_console::open_fhs++; + termios_printf ("incremented open_fhs %d", fhandler_console::open_fhs); + fhandler_tty_common::fixup_after_fork (parent); +} + +void +fhandler_tty_common::fixup_after_fork (HANDLE parent) +{ + fhandler_termios::fixup_after_fork (parent); + if (output_done_event) + fork_fixup (parent, output_done_event, "output_done_event"); + if (ioctl_request_event) + fork_fixup (parent, ioctl_request_event, "ioctl_request_event"); + if (ioctl_done_event) + fork_fixup (parent, ioctl_done_event, "ioctl_done_event"); + if (output_mutex) + fork_fixup (parent, output_mutex, "output_mutex"); + if (input_mutex) + fork_fixup (parent, input_mutex, "input_mutex"); + if (input_available_event) + fork_fixup (parent, input_available_event, "input_available_event"); + fork_fixup (parent, inuse, "inuse"); +} + +void +fhandler_pty_master::set_close_on_exec (int val) +{ + fhandler_tty_common::set_close_on_exec (val); + + /* FIXME: There is a console handle leak here. */ + if (get_ttyp ()->master_pid == GetCurrentProcessId ()) + { + get_ttyp ()->from_slave = get_handle (); + get_ttyp ()->to_slave = get_output_handle (); + termios_printf ("from_slave %p, to_slave %p", get_handle (), + get_output_handle ()); + } +} + +void +fhandler_tty_master::fixup_after_fork (HANDLE child) +{ + fhandler_pty_master::fixup_after_fork (child); + console->fixup_after_fork (child); +} + +void +fhandler_tty_master::fixup_after_exec (HANDLE) +{ + console->close (); + init_console (); +} + +int +fhandler_tty_master::init_console () +{ + console = (fhandler_console *) cygheap->fdtab.build_fhandler (-1, FH_CONSOLE, "/dev/ttym"); + if (console == NULL) + return -1; + + console->init (INVALID_HANDLE_VALUE, GENERIC_READ | GENERIC_WRITE, O_BINARY); + fhandler_console::open_fhs--; /* handled when individual fds are opened */ + console->set_r_no_interrupt (1); + return 0; +} diff --git a/winsup/cygwin/syscalls.cc b/winsup/cygwin/syscalls.cc new file mode 100644 index 00000000000..702c7a5e2b9 --- /dev/null +++ b/winsup/cygwin/syscalls.cc @@ -0,0 +1,2985 @@ +/* syscalls.cc: syscalls + + Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003 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. */ + +#define fstat __FOOfstat__ +#define lstat __FOOlstat__ +#define stat __FOOstat__ +#define _close __FOO_close__ +#define _lseek __FOO_lseek__ +#define _open __FOO_open__ +#define _read __FOO_read__ +#define _write __FOO_write__ +#define _open64 __FOO_open64__ +#define _lseek64 __FOO_lseek64__ +#define _fstat64 __FOO_fstat64__ + +#include "winsup.h" +#include <sys/stat.h> +#include <sys/vfs.h> /* needed for statfs */ +#include <pwd.h> +#include <grp.h> +#include <stdlib.h> +#include <stdio.h> +#include <process.h> +#include <utmp.h> +#include <sys/uio.h> +#include <errno.h> +#include <ctype.h> +#include <limits.h> +#include <unistd.h> +#include <setjmp.h> +#include <winnls.h> +#include <wininet.h> +#include <lmcons.h> /* for UNLEN */ +#include <rpc.h> + +#undef fstat +#undef lstat +#undef stat + +#include <cygwin/version.h> +#include <sys/cygwin.h> +#include "cygerrno.h" +#include "perprocess.h" +#include "security.h" +#include "fhandler.h" +#include "path.h" +#include "dtable.h" +#include "sigproc.h" +#include "pinfo.h" +#include "shared_info.h" +#include "cygheap.h" +#define NEED_VFORK +#include "perthread.h" +#include "pwdgrp.h" +#include "cpuid.h" +#include "registry.h" + +#undef _close +#undef _lseek +#undef _open +#undef _read +#undef _write +#undef _open64 +#undef _lseek64 +#undef _fstat64 + +SYSTEM_INFO system_info; + +/* Close all files and process any queued deletions. + Lots of unix style applications will open a tmp file, unlink it, + but never call close. This function is called by _exit to + ensure we don't leave any such files lying around. */ + +void __stdcall +close_all_files (void) +{ + SetResourceLock (LOCK_FD_LIST, WRITE_LOCK | READ_LOCK, "close_all_files"); + + fhandler_base *fh; + for (int i = 0; i < (int) cygheap->fdtab.size; i++) + if ((fh = cygheap->fdtab[i]) != NULL) + { + fh->close (); + cygheap->fdtab.release (i); + } + + ReleaseResourceLock (LOCK_FD_LIST, WRITE_LOCK | READ_LOCK, "close_all_files"); + cygwin_shared->delqueue.process_queue (); +} + +int +dup (int fd) +{ + return cygheap->fdtab.dup2 (fd, cygheap_fdnew ()); +} + +int +dup2 (int oldfd, int newfd) +{ + return cygheap->fdtab.dup2 (oldfd, newfd); +} + +extern "C" int +unlink (const char *ourname) +{ + int res = -1; + DWORD devn; + sigframe thisframe (mainthread); + + path_conv win32_name (ourname, PC_SYM_NOFOLLOW | PC_FULL); + + if (win32_name.error) + { + set_errno (win32_name.error); + goto done; + } + + if ((devn = win32_name.get_devn ()) == FH_PROC || devn == FH_REGISTRY + || devn == FH_PROCESS) + { + set_errno (EROFS); + goto done; + } + + syscall_printf ("_unlink (%s)", win32_name.get_win32 ()); + + if (!win32_name.exists ()) + { + syscall_printf ("unlinking a nonexistent file"); + set_errno (ENOENT); + goto done; + } + else if (win32_name.isdir ()) + { + syscall_printf ("unlinking a directory"); + set_errno (EPERM); + goto done; + } + + /* Windows won't check the directory mode, so we do that ourselves. */ + if (!writable_directory (win32_name)) + { + syscall_printf ("non-writable directory"); + set_errno (EPERM); + goto done; + } + + bool setattrs; + if (!((DWORD) win32_name & (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_SYSTEM))) + setattrs = false; + else + { + /* Allow us to delete even if read-only */ + setattrs = SetFileAttributes (win32_name, + (DWORD) win32_name + & ~(FILE_ATTRIBUTE_READONLY + | FILE_ATTRIBUTE_SYSTEM)); + } + /* Attempt to use "delete on close" semantics to handle removing + a file which may be open. */ + HANDLE h; + h = CreateFile (win32_name, 0, FILE_SHARE_READ, &sec_none_nih, + OPEN_EXISTING, FILE_FLAG_DELETE_ON_CLOSE, 0); + if (h != INVALID_HANDLE_VALUE) + { + if (wincap.has_hard_links () && setattrs) + (void) SetFileAttributes (win32_name, (DWORD) win32_name); + BOOL res = CloseHandle (h); + syscall_printf ("%d = CloseHandle (%p)", res, h); + if (GetFileAttributes (win32_name) == INVALID_FILE_ATTRIBUTES + || !win32_name.isremote ()) + { + syscall_printf ("CreateFile (FILE_FLAG_DELETE_ON_CLOSE) succeeded"); + goto ok; + } + else + { + syscall_printf ("CreateFile (FILE_FLAG_DELETE_ON_CLOSE) failed"); + if (setattrs) + SetFileAttributes (win32_name, (DWORD) win32_name & ~(FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_SYSTEM)); + } + } + + /* Try a delete with attributes reset */ + if (DeleteFile (win32_name)) + { + syscall_printf ("DeleteFile after CreateFile/CloseHandle succeeded"); + goto ok; + } + + DWORD lasterr; + lasterr = GetLastError (); + + (void) SetFileAttributes (win32_name, (DWORD) win32_name); + + /* Windows 9x seems to report ERROR_ACCESS_DENIED rather than sharing + violation. So, set lasterr to ERROR_SHARING_VIOLATION in this case + to simplify tests. */ + if (wincap.access_denied_on_delete () && lasterr == ERROR_ACCESS_DENIED + && !win32_name.isremote ()) + lasterr = ERROR_SHARING_VIOLATION; + + /* FILE_FLAGS_DELETE_ON_CLOSE was a bust. If this is a sharing + violation, then queue the file for deletion when the process + exits. Otherwise, punt. */ + if (lasterr != ERROR_SHARING_VIOLATION) + goto err; + + syscall_printf ("couldn't delete file, err %d", lasterr); + + /* Add file to the "to be deleted" queue. */ + cygwin_shared->delqueue.queue_file (win32_name); + + /* Success condition. */ + ok: + res = 0; + goto done; + + /* Error condition. */ + err: + __seterrno (); + res = -1; + + done: + syscall_printf ("%d = unlink (%s)", res, ourname); + return res; +} + +extern "C" int +_remove_r (struct _reent *, const char *ourname) +{ + path_conv win32_name (ourname, PC_SYM_NOFOLLOW | PC_FULL); + + if (win32_name.error) + { + set_errno (win32_name.error); + syscall_printf ("-1 = remove (%s)", ourname); + return -1; + } + + return win32_name.isdir () ? rmdir (ourname) : unlink (ourname); +} + +extern "C" int +remove (const char *ourname) +{ + path_conv win32_name (ourname, PC_SYM_NOFOLLOW | PC_FULL); + + if (win32_name.error) + { + set_errno (win32_name.error); + syscall_printf ("-1 = remove (%s)", ourname); + return -1; + } + + return win32_name.isdir () ? rmdir (ourname) : unlink (ourname); +} + +extern "C" pid_t +getpid () +{ + return myself->pid; +} + +extern "C" pid_t +_getpid_r (struct _reent *) +{ + return getpid (); +} + +/* getppid: POSIX 4.1.1.1 */ +extern "C" pid_t +getppid () +{ + return myself->ppid; +} + +/* setsid: POSIX 4.3.2.1 */ +extern "C" pid_t +setsid (void) +{ + vfork_save *vf = vfork_storage.val (); + /* This is a horrible, horrible kludge */ + if (vf && vf->pid < 0) + { + pid_t pid = fork (); + if (pid > 0) + { + syscall_printf ("longjmping due to vfork"); + vf->restore_pid (pid); + } + /* assuming that fork was successful */ + } + + if (myself->pgid != myself->pid) + { + if (myself->ctty >= 0 && fhandler_console::open_fhs <= 0) + { + syscall_printf ("freeing console"); + FreeConsole (); + } + myself->ctty = -1; + myself->sid = getpid (); + myself->pgid = getpid (); + syscall_printf ("sid %d, pgid %d, ctty %d, open_fhs %d", myself->sid, + myself->pgid, myself->ctty, fhandler_console::open_fhs); + return myself->sid; + } + + set_errno (EPERM); + return -1; +} + +extern "C" pid_t +getsid (pid_t pid) +{ + pid_t res; + if (!pid) + res = myself->sid; + else + { + pinfo p (pid); + if (p) + res = p->sid; + else + { + set_errno (ESRCH); + res = -1; + } + } + return res; +} + +extern "C" ssize_t +read (int fd, void *ptr, size_t len) +{ + const iovec iov = + { + iov_base: ptr, + iov_len: len + }; + + return readv (fd, &iov, 1); +} + +extern "C" ssize_t _read (int, void *, size_t) + __attribute__ ((alias ("read"))); + +extern "C" ssize_t +write (int fd, const void *ptr, size_t len) +{ + const struct iovec iov = + { + iov_base: (void *) ptr, // const_cast + iov_len: len + }; + + return writev (fd, &iov, 1); +} + +extern "C" ssize_t _write (int fd, const void *ptr, size_t len) + __attribute__ ((alias ("write"))); + +extern "C" ssize_t +readv (int fd, const struct iovec *const iov, const int iovcnt) +{ + extern int sigcatchers; + const int e = get_errno (); + + int res = -1; + + const ssize_t tot = check_iovec_for_read (iov, iovcnt); + + if (tot <= 0) + { + res = tot; + goto done; + } + + while (1) + { + sig_dispatch_pending (); + sigframe thisframe (mainthread); + + cygheap_fdget cfd (fd); + if (cfd < 0) + break; + + if ((cfd->get_flags () & O_ACCMODE) == O_WRONLY) + { + set_errno (EBADF); + break; + } + + DWORD wait = cfd->is_nonblocking () ? 0 : INFINITE; + + /* Could block, so let user know we at least got here. */ + syscall_printf ("readv (%d, %p, %d) %sblocking, sigcatchers %d", + fd, iov, iovcnt, wait ? "" : "non", sigcatchers); + + if (wait && (!cfd->is_slow () || cfd->get_r_no_interrupt ())) + debug_printf ("no need to call ready_for_read"); + else if (!cfd->ready_for_read (fd, wait)) + { + res = -1; + goto out; + } + + /* FIXME: This is not thread safe. We need some method to + ensure that an fd, closed in another thread, aborts I/O + operations. */ + if (!cfd.isopen ()) + break; + + /* Check to see if this is a background read from a "tty", + sending a SIGTTIN, if appropriate */ + res = cfd->bg_check (SIGTTIN); + + if (!cfd.isopen ()) + { + res = -1; + break; + } + + if (res > bg_eof) + { + myself->process_state |= PID_TTYIN; + if (!cfd.isopen ()) + { + res = -1; + break; + } + res = cfd->readv (iov, iovcnt, tot); + myself->process_state &= ~PID_TTYIN; + } + + out: + if (res >= 0 || get_errno () != EINTR || !thisframe.call_signal_handler ()) + break; + set_errno (e); + } + +done: + syscall_printf ("%d = readv (%d, %p, %d), errno %d", res, fd, iov, iovcnt, + get_errno ()); + MALLOC_CHECK; + return res; +} + +extern "C" ssize_t +writev (const int fd, const struct iovec *const iov, const int iovcnt) +{ + int res = -1; + sig_dispatch_pending (); + const ssize_t tot = check_iovec_for_write (iov, iovcnt); + + sigframe thisframe (mainthread); + cygheap_fdget cfd (fd); + if (cfd < 0) + goto done; + + if (tot <= 0) + { + res = tot; + goto done; + } + + if ((cfd->get_flags () & O_ACCMODE) == O_RDONLY) + { + set_errno (EBADF); + goto done; + } + + /* Could block, so let user know we at least got here. */ + if (fd == 1 || fd == 2) + paranoid_printf ("writev (%d, %p, %d)", fd, iov, iovcnt); + else + syscall_printf ("writev (%d, %p, %d)", fd, iov, iovcnt); + + res = cfd->bg_check (SIGTTOU); + + if (res > bg_eof) + { + myself->process_state |= PID_TTYOU; + res = cfd->writev (iov, iovcnt, tot); + myself->process_state &= ~PID_TTYOU; + } + +done: + if (fd == 1 || fd == 2) + paranoid_printf ("%d = write (%d, %p, %d), errno %d", + res, fd, iov, iovcnt, get_errno ()); + else + syscall_printf ("%d = write (%d, %p, %d), errno %d", + res, fd, iov, iovcnt, get_errno ()); + + MALLOC_CHECK; + return res; +} + +/* _open */ +/* newlib's fcntl.h defines _open as taking variable args so we must + correspond. The third arg if it exists is: mode_t mode. */ +extern "C" int +open (const char *unix_path, int flags, ...) +{ + int res = -1; + va_list ap; + mode_t mode = 0; + sig_dispatch_pending (); + sigframe thisframe (mainthread); + + syscall_printf ("open (%s, %p)", unix_path, flags); + if (!check_null_empty_str_errno (unix_path)) + { + /* check for optional mode argument */ + va_start (ap, flags); + mode = va_arg (ap, mode_t); + va_end (ap); + + fhandler_base *fh; + cygheap_fdnew fd; + + if (fd >= 0) + { + path_conv pc; + if (!(fh = cygheap->fdtab.build_fhandler_from_name (fd, unix_path, + NULL, pc))) + res = -1; // errno already set + else if (!fh->open (&pc, flags, (mode & 07777) & ~cygheap->umask)) + { + fd.release (); + res = -1; + } + else if ((res = fd) <= 2) + set_std_handle (res); + } + } + + syscall_printf ("%d = open (%s, %p)", res, unix_path, flags); + return res; +} + +extern "C" int _open (const char *, int flags, ...) + __attribute__ ((alias ("open"))); + +extern "C" int _open64 (const char *, int flags, ...) + __attribute__ ((alias ("open"))); + +extern "C" _off64_t +lseek64 (int fd, _off64_t pos, int dir) +{ + _off64_t res; + sigframe thisframe (mainthread); + + if (dir != SEEK_SET && dir != SEEK_CUR && dir != SEEK_END) + { + set_errno (EINVAL); + res = -1; + } + else + { + cygheap_fdget cfd (fd); + if (cfd >= 0) + res = cfd->lseek (pos, dir); + else + res = -1; + } + syscall_printf ("%d = lseek (%d, %D, %d)", res, fd, pos, dir); + + return res; +} + +extern "C" int _lseek64 (int fd, _off64_t pos, int dir) + __attribute__ ((alias ("lseek64"))); + +extern "C" _off_t +lseek (int fd, _off_t pos, int dir) +{ + return lseek64 (fd, (_off64_t) pos, dir); +} + +extern "C" _off_t _lseek (int, _off_t, int) + __attribute__ ((alias ("lseek"))); + +extern "C" int +close (int fd) +{ + int res; + sigframe thisframe (mainthread); + + syscall_printf ("close (%d)", fd); + + MALLOC_CHECK; + cygheap_fdget cfd (fd, true); + if (cfd < 0) + res = -1; + else + { + res = cfd->close (); + cfd.release (); + } + + syscall_printf ("%d = close (%d)", res, fd); + MALLOC_CHECK; + return res; +} + +extern "C" int _close (int) __attribute__ ((alias ("close"))); + +extern "C" int +isatty (int fd) +{ + int res; + sigframe thisframe (mainthread); + + cygheap_fdget cfd (fd); + if (cfd < 0) + res = 0; + else + res = cfd->is_tty (); + syscall_printf ("%d = isatty (%d)", res, fd); + return res; +} + +/* Under NT, try to make a hard link using backup API. If that + fails or we are Win 95, just copy the file. + FIXME: We should actually be checking partition type, not OS. + Under NTFS, we should support hard links. On FAT partitions, + we should just copy the file. +*/ + +extern "C" int +link (const char *a, const char *b) +{ + int res = -1; + sigframe thisframe (mainthread); + path_conv real_a (a, PC_SYM_NOFOLLOW | PC_FULL); + path_conv real_b (b, PC_SYM_NOFOLLOW | PC_FULL); + extern BOOL allow_winsymlinks; + + if (real_a.error) + { + set_errno (real_a.error); + goto done; + } + + if (real_b.error) + { + set_errno (real_b.case_clash ? ECASECLASH : real_b.error); + goto done; + } + + if (real_b.exists ()) + { + syscall_printf ("file '%s' exists?", (char *) real_b); + set_errno (EEXIST); + goto done; + } + + if (real_b[strlen (real_b) - 1] == '.') + { + syscall_printf ("trailing dot, bailing out"); + set_errno (EINVAL); + goto done; + } + + /* Shortcut hack. */ + char new_lnk_buf[MAX_PATH + 5]; + if (allow_winsymlinks && real_a.is_lnk_symlink () && !real_b.case_clash) + { + strcpy (new_lnk_buf, b); + strcat (new_lnk_buf, ".lnk"); + b = new_lnk_buf; + real_b.check (b, PC_SYM_NOFOLLOW | PC_FULL); + } + /* Try to make hard link first on Windows NT */ + if (wincap.has_hard_links ()) + { + if (CreateHardLinkA (real_b, real_a, NULL)) + goto success; + + HANDLE hFileSource; + + WIN32_STREAM_ID StreamId; + DWORD dwBytesWritten; + LPVOID lpContext; + DWORD cbPathLen; + DWORD StreamSize; + WCHAR wbuf[MAX_PATH]; + + BOOL bSuccess; + + hFileSource = CreateFile (real_a, FILE_WRITE_ATTRIBUTES, + FILE_SHARE_READ | FILE_SHARE_WRITE /*| FILE_SHARE_DELETE*/, + &sec_none_nih, // sa + OPEN_EXISTING, 0, NULL); + + if (hFileSource == INVALID_HANDLE_VALUE) + { + syscall_printf ("cannot open source, %E"); + goto docopy; + } + + cbPathLen = sys_mbstowcs (wbuf, real_b, MAX_PATH) * sizeof (WCHAR); + + StreamId.dwStreamId = BACKUP_LINK; + StreamId.dwStreamAttributes = 0; + StreamId.dwStreamNameSize = 0; + StreamId.Size.HighPart = 0; + StreamId.Size.LowPart = cbPathLen; + + StreamSize = sizeof (WIN32_STREAM_ID) - sizeof (WCHAR**) + + StreamId.dwStreamNameSize; + + lpContext = NULL; + /* Write the WIN32_STREAM_ID */ + bSuccess = BackupWrite ( + hFileSource, + (LPBYTE) &StreamId, // buffer to write + StreamSize, // number of bytes to write + &dwBytesWritten, + FALSE, // don't abort yet + FALSE, // don't process security + &lpContext); + + if (bSuccess) + { + /* write the buffer containing the path */ + /* FIXME: BackupWrite sometimes traps if linkname is invalid. + Need to handle. */ + bSuccess = BackupWrite ( + hFileSource, + (LPBYTE) wbuf, // buffer to write + cbPathLen, // number of bytes to write + &dwBytesWritten, + FALSE, // don't abort yet + FALSE, // don't process security + &lpContext + ); + + if (!bSuccess) + syscall_printf ("cannot write linkname, %E"); + + /* Free context */ + BackupWrite ( + hFileSource, + NULL, // buffer to write + 0, // number of bytes to write + &dwBytesWritten, + TRUE, // abort + FALSE, // don't process security + &lpContext); + } + else + syscall_printf ("cannot write streamId, %E"); + + CloseHandle (hFileSource); + + if (!bSuccess) + goto docopy; + + success: + res = 0; + if (!allow_winsymlinks && real_a.is_lnk_symlink ()) + SetFileAttributes (real_b, (DWORD) real_a + | FILE_ATTRIBUTE_SYSTEM + | FILE_ATTRIBUTE_READONLY); + + goto done; + } +docopy: + /* do this with a copy */ + if (CopyFileA (real_a, real_b, 1)) + res = 0; + else + __seterrno (); + +done: + syscall_printf ("%d = link (%s, %s)", res, a, b); + return res; +} + +/* chown: POSIX 5.6.5.1 */ +/* + * chown () is only implemented for Windows NT. Under other operating + * systems, it is only a stub that always returns zero. + */ +static int +chown_worker (const char *name, unsigned fmode, __uid32_t uid, __gid32_t gid) +{ + int res; + + if (check_null_empty_str_errno (name)) + return -1; + + if (!wincap.has_security ()) // real chown only works on NT + res = 0; // return zero (and do nothing) under Windows 9x + else + { + /* we need Win32 path names because of usage of Win32 API functions */ + path_conv win32_path (PC_NONULLEMPTY, name, fmode); + + if (win32_path.error) + { + set_errno (win32_path.error); + res = -1; + goto done; + } + + /* FIXME: This makes chown on a device succeed always. Someday we'll want + to actually allow chown to work properly on devices. */ + if (win32_path.is_device () && !win32_path.issocket ()) + { + res = 0; + goto done; + } + + mode_t attrib = 0; + if (win32_path.isdir ()) + attrib |= S_IFDIR; + res = get_file_attribute (win32_path.has_acls (), + win32_path.get_win32 (), + &attrib); + if (!res) + res = set_file_attribute (win32_path.has_acls (), win32_path, uid, + gid, attrib); + if (res != 0 && (!win32_path.has_acls () || !allow_ntsec)) + { + /* fake - if not supported, pretend we're like win95 + where it just works */ + res = 0; + } + } + +done: + syscall_printf ("%d = %schown (%s,...)", + res, (fmode & PC_SYM_NOFOLLOW) ? "l" : "", name); + return res; +} + +extern "C" int +chown32 (const char * name, __uid32_t uid, __gid32_t gid) +{ + sigframe thisframe (mainthread); + return chown_worker (name, PC_SYM_FOLLOW, uid, gid); +} + +extern "C" int +chown (const char * name, __uid16_t uid, __gid16_t gid) +{ + sigframe thisframe (mainthread); + return chown_worker (name, PC_SYM_FOLLOW, + uid16touid32 (uid), gid16togid32 (gid)); +} + +extern "C" int +lchown32 (const char * name, __uid32_t uid, __gid32_t gid) +{ + sigframe thisframe (mainthread); + return chown_worker (name, PC_SYM_NOFOLLOW, uid, gid); +} + +extern "C" int +lchown (const char * name, __uid16_t uid, __gid16_t gid) +{ + sigframe thisframe (mainthread); + return chown_worker (name, PC_SYM_NOFOLLOW, + uid16touid32 (uid), gid16togid32 (gid)); +} + +extern "C" int +fchown32 (int fd, __uid32_t uid, __gid32_t gid) +{ + sigframe thisframe (mainthread); + cygheap_fdget cfd (fd); + if (cfd < 0) + { + syscall_printf ("-1 = fchown (%d,...)", fd); + return -1; + } + + const char *path = cfd->get_name (); + + if (path == NULL) + { + syscall_printf ("-1 = fchown (%d,...) (no name)", fd); + set_errno (ENOSYS); + return -1; + } + + syscall_printf ("fchown (%d,...): calling chown_worker (%s,FOLLOW,...)", + fd, path); + return chown_worker (path, PC_SYM_FOLLOW, uid, gid); +} + +extern "C" int +fchown (int fd, __uid16_t uid, __gid16_t gid) +{ + return fchown32 (fd, uid16touid32 (uid), gid16togid32 (gid)); +} + +/* umask: POSIX 5.3.3.1 */ +extern "C" mode_t +umask (mode_t mask) +{ + mode_t oldmask; + + oldmask = cygheap->umask; + cygheap->umask = mask & 0777; + return oldmask; +} + +/* chmod: POSIX 5.6.4.1 */ +extern "C" int +chmod (const char *path, mode_t mode) +{ + int res = -1; + sigframe thisframe (mainthread); + + path_conv win32_path (path); + + if (win32_path.error) + { + set_errno (win32_path.error); + goto done; + } + + /* FIXME: This makes chmod on a device succeed always. Someday we'll want + to actually allow chmod to work properly on devices. */ + if (win32_path.is_device () && !win32_path.issocket ()) + { + res = 0; + goto done; + } + + if (!win32_path.exists ()) + __seterrno (); + else + { + /* temporary erase read only bit, to be able to set file security */ + SetFileAttributes (win32_path, (DWORD) win32_path & ~FILE_ATTRIBUTE_READONLY); + + if (win32_path.isdir ()) + mode |= S_IFDIR; + if (!set_file_attribute (win32_path.has_acls (), win32_path, + ILLEGAL_UID, ILLEGAL_GID, mode) + && allow_ntsec) + res = 0; + + /* if the mode we want has any write bits set, we can't + be read only. */ + if (mode & (S_IWUSR | S_IWGRP | S_IWOTH)) + (DWORD) win32_path &= ~FILE_ATTRIBUTE_READONLY; + else + (DWORD) win32_path |= FILE_ATTRIBUTE_READONLY; + + if (!win32_path.is_lnk_symlink () && S_ISLNK (mode) || S_ISSOCK (mode)) + (DWORD) win32_path |= FILE_ATTRIBUTE_SYSTEM; + + if (!SetFileAttributes (win32_path, win32_path)) + __seterrno (); + else if (!allow_ntsec) + /* Correct NTFS security attributes have higher priority */ + res = 0; + } + +done: + syscall_printf ("%d = chmod (%s, %p)", res, path, mode); + return res; +} + +/* fchmod: P96 5.6.4.1 */ + +extern "C" int +fchmod (int fd, mode_t mode) +{ + sigframe thisframe (mainthread); + cygheap_fdget cfd (fd); + if (cfd < 0) + { + syscall_printf ("-1 = fchmod (%d, 0%o)", fd, mode); + return -1; + } + + const char *path = cfd->get_name (); + + if (path == NULL) + { + syscall_printf ("-1 = fchmod (%d, 0%o) (no name)", fd, mode); + set_errno (ENOSYS); + return -1; + } + + syscall_printf ("fchmod (%d, 0%o): calling chmod (%s, 0%o)", + fd, mode, path, mode); + return chmod (path, mode); +} + +static void +stat64_to_stat32 (struct __stat64 *src, struct __stat32 *dst) +{ + dst->st_dev = ((src->st_dev >> 8) & 0xff00) | (src->st_dev & 0xff); + dst->st_ino = ((unsigned) (src->st_ino >> 32)) | (unsigned) src->st_ino; + dst->st_mode = src->st_mode; + dst->st_nlink = src->st_nlink; + dst->st_uid = src->st_uid; + dst->st_gid = src->st_gid; + dst->st_rdev = ((src->st_rdev >> 8) & 0xff00) | (src->st_rdev & 0xff); + dst->st_size = src->st_size; + dst->st_atim = src->st_atim; + dst->st_mtim = src->st_mtim; + dst->st_ctim = src->st_ctim; + dst->st_blksize = src->st_blksize; + dst->st_blocks = src->st_blocks; +} + +extern "C" int +fstat64 (int fd, struct __stat64 *buf) +{ + int res; + sigframe thisframe (mainthread); + + cygheap_fdget cfd (fd); + if (cfd < 0) + res = -1; + else + { + path_conv pc (cfd->get_name (), PC_SYM_NOFOLLOW); + memset (buf, 0, sizeof (struct __stat64)); + res = cfd->fstat (buf, &pc); + if (!res && cfd->get_device () != FH_SOCKET) + { + if (!buf->st_ino) + buf->st_ino = hash_path_name (0, cfd->get_win32_name ()); + if (!buf->st_dev) + buf->st_dev = (cfd->get_device () << 16) | cfd->get_unit (); + if (!buf->st_rdev) + buf->st_rdev = buf->st_dev; + } + } + + syscall_printf ("%d = fstat (%d, %p)", res, fd, buf); + return res; +} + +extern "C" int +_fstat64_r (struct _reent *ptr, int fd, struct __stat64 *buf) +{ + int ret; + + set_errno (0); + if ((ret = fstat64 (fd, buf)) == -1 && get_errno () != 0) + ptr->_errno = get_errno (); + return ret; +} + +extern "C" int +fstat (int fd, struct __stat32 *buf) +{ + struct __stat64 buf64; + int ret = fstat64 (fd, &buf64); + if (!ret) + stat64_to_stat32 (&buf64, buf); + return ret; +} + +extern "C" int +_fstat_r (struct _reent *ptr, int fd, struct __stat32 *buf) +{ + int ret; + + set_errno (0); + if ((ret = fstat (fd, buf)) == -1 && get_errno () != 0) + ptr->_errno = get_errno (); + return ret; +} + +/* fsync: P96 6.6.1.1 */ +extern "C" int +fsync (int fd) +{ + sigframe thisframe (mainthread); + cygheap_fdget cfd (fd); + if (cfd < 0) + { + syscall_printf ("-1 = fsync (%d)", fd); + return -1; + } + + if (FlushFileBuffers (cfd->get_handle ()) == 0) + { + __seterrno (); + return -1; + } + return 0; +} + +/* sync: standards? */ +extern "C" int +sync () +{ + return 0; +} + +suffix_info stat_suffixes[] = +{ + suffix_info ("", 1), + suffix_info (".exe", 1), + suffix_info (NULL) +}; + +/* Cygwin internal */ +int __stdcall +stat_worker (const char *name, struct __stat64 *buf, int nofollow, + path_conv *pc) +{ + int res = -1; + path_conv real_path; + fhandler_base *fh = NULL; + + if (check_null_invalid_struct_errno (buf)) + goto done; + + if (!pc) + pc = &real_path; + + fh = cygheap->fdtab.build_fhandler_from_name (-1, name, NULL, *pc, + (nofollow ? PC_SYM_NOFOLLOW + : PC_SYM_FOLLOW) + | PC_FULL, stat_suffixes); + + if (pc->error) + { + debug_printf ("got %d error from build_fhandler_from_name", pc->error); + set_errno (pc->error); + } + else + { + debug_printf ("(%s, %p, %d, %p), file_attributes %d", name, buf, nofollow, + pc, (DWORD) real_path); + memset (buf, 0, sizeof (*buf)); + res = fh->fstat (buf, pc); + if (!res && fh->get_device () != FH_SOCKET) + { + if (!buf->st_ino) + buf->st_ino = hash_path_name (0, fh->get_win32_name ()); + if (!buf->st_dev) + buf->st_dev = (fh->get_device () << 16) | fh->get_unit (); + if (!buf->st_rdev) + buf->st_rdev = buf->st_dev; + } + } + + done: + if (fh) + delete fh; + MALLOC_CHECK; + syscall_printf ("%d = (%s, %p)", res, name, buf); + return res; +} + +extern "C" int +stat64 (const char *name, struct __stat64 *buf) +{ + sigframe thisframe (mainthread); + syscall_printf ("entering"); + return stat_worker (name, buf, 0); +} + +extern "C" int +_stat64_r (struct _reent *ptr, const char *name, struct __stat64 *buf) +{ + int ret; + + set_errno (0); + if ((ret = stat64 (name, buf)) == -1 && get_errno () != 0) + ptr->_errno = get_errno (); + return ret; +} + +extern "C" int +stat (const char *name, struct __stat32 *buf) +{ + struct __stat64 buf64; + int ret = stat64 (name, &buf64); + if (!ret) + stat64_to_stat32 (&buf64, buf); + return ret; +} + +extern "C" int +_stat_r (struct _reent *ptr, const char *name, struct __stat32 *buf) +{ + int ret; + + set_errno (0); + if ((ret = stat (name, buf)) == -1 && get_errno () != 0) + ptr->_errno = get_errno (); + return ret; +} + +/* lstat: Provided by SVR4 and 4.3+BSD, POSIX? */ +extern "C" int +lstat64 (const char *name, struct __stat64 *buf) +{ + sigframe thisframe (mainthread); + syscall_printf ("entering"); + return stat_worker (name, buf, 1); +} + +/* lstat: Provided by SVR4 and 4.3+BSD, POSIX? */ +extern "C" int +lstat (const char *name, struct __stat32 *buf) +{ + struct __stat64 buf64; + int ret = lstat64 (name, &buf64); + if (!ret) + stat64_to_stat32 (&buf64, buf); + return ret; +} + +extern "C" int +access (const char *fn, int flags) +{ + sigframe thisframe (mainthread); + // flags were incorrectly specified + if (flags & ~(F_OK|R_OK|W_OK|X_OK)) + { + set_errno (EINVAL); + return -1; + } + + path_conv real_path (fn, PC_SYM_FOLLOW | PC_FULL, stat_suffixes); + if (real_path.error) + { + set_errno (real_path.error); + return -1; + } + + if (!real_path.exists ()) + { + set_errno (ENOENT); + return -1; + } + + if (!(flags & (R_OK | W_OK | X_OK))) + return 0; + + if (real_path.has_attribute (FILE_ATTRIBUTE_READONLY) && (flags & W_OK)) + { + set_errno (EACCES); + return -1; + } + + if (real_path.has_acls () && allow_ntsec) + return check_file_access (real_path, flags); + + struct __stat64 st; + int r = stat_worker (fn, &st, 0); + if (r) + return -1; + r = -1; + if (flags & R_OK) + { + if (st.st_uid == myself->uid) + { + if (!(st.st_mode & S_IRUSR)) + goto done; + } + else if (st.st_gid == myself->gid) + { + if (!(st.st_mode & S_IRGRP)) + goto done; + } + else if (!(st.st_mode & S_IROTH)) + goto done; + } + if (flags & W_OK) + { + if (st.st_uid == myself->uid) + { + if (!(st.st_mode & S_IWUSR)) + goto done; + } + else if (st.st_gid == myself->gid) + { + if (!(st.st_mode & S_IWGRP)) + goto done; + } + else if (!(st.st_mode & S_IWOTH)) + goto done; + } + if (flags & X_OK) + { + if (st.st_uid == myself->uid) + { + if (!(st.st_mode & S_IXUSR)) + goto done; + } + else if (st.st_gid == myself->gid) + { + if (!(st.st_mode & S_IXGRP)) + goto done; + } + else if (!(st.st_mode & S_IXOTH)) + goto done; + } + r = 0; +done: + if (r) + set_errno (EACCES); + return r; +} + +extern "C" int +rename (const char *oldpath, const char *newpath) +{ + sigframe thisframe (mainthread); + int res = 0; + char *lnk_suffix = NULL; + + path_conv real_old (oldpath, PC_SYM_NOFOLLOW); + + if (real_old.error) + { + syscall_printf ("-1 = rename (%s, %s)", oldpath, newpath); + set_errno (real_old.error); + return -1; + } + + path_conv real_new (newpath, PC_SYM_NOFOLLOW); + + /* Shortcut hack. */ + char new_lnk_buf[MAX_PATH + 5]; + if (real_old.is_lnk_symlink () && !real_new.error && !real_new.case_clash) + { + strcpy (new_lnk_buf, newpath); + strcat (new_lnk_buf, ".lnk"); + newpath = new_lnk_buf; + real_new.check (newpath, PC_SYM_NOFOLLOW); + } + + if (real_new.error || real_new.case_clash) + { + syscall_printf ("-1 = rename (%s, %s)", oldpath, newpath); + set_errno (real_new.case_clash ? ECASECLASH : real_new.error); + return -1; + } + + if (!writable_directory (real_old) || !writable_directory (real_new)) + { + syscall_printf ("-1 = rename (%s, %s)", oldpath, newpath); + set_errno (EACCES); + return -1; + } + + if (!real_old.exists ()) /* file to move doesn't exist */ + { + syscall_printf ("file to move doesn't exist"); + set_errno (ENOENT); + return (-1); + } + + /* Destination file exists and is read only, change that or else + the rename won't work. */ + if (real_new.has_attribute (FILE_ATTRIBUTE_READONLY)) + SetFileAttributes (real_new, (DWORD) real_new & ~FILE_ATTRIBUTE_READONLY); + + /* Shortcut hack No. 2, part 1 */ + if (!real_old.issymlink () && !real_new.error && real_new.is_lnk_symlink () + && (lnk_suffix = strrchr (real_new.get_win32 (), '.'))) + *lnk_suffix = '\0'; + + if (!MoveFile (real_old, real_new)) + res = -1; + + if (res == 0 || (GetLastError () != ERROR_ALREADY_EXISTS + && GetLastError () != ERROR_FILE_EXISTS)) + goto done; + + if (wincap.has_move_file_ex ()) + { + if (MoveFileEx (real_old.get_win32 (), real_new.get_win32 (), + MOVEFILE_REPLACE_EXISTING)) + res = 0; + } + else + { + syscall_printf ("try win95 hack"); + for (int i = 0; i < 2; i++) + { + if (!DeleteFileA (real_new.get_win32 ()) && + GetLastError () != ERROR_FILE_NOT_FOUND) + { + syscall_printf ("deleting %s to be paranoid", + real_new.get_win32 ()); + break; + } + else if (MoveFile (real_old.get_win32 (), real_new.get_win32 ())) + { + res = 0; + break; + } + } + } + +done: + if (res) + { + __seterrno (); + /* Reset R/O attributes if neccessary. */ + if (real_new.has_attribute (FILE_ATTRIBUTE_READONLY)) + SetFileAttributes (real_new, real_new); + } + else + { + /* make the new file have the permissions of the old one */ + DWORD attr = real_old; +#ifdef HIDDEN_DOT_FILES + char *c = strrchr (real_old.get_win32 (), '\\'); + if ((c && c[1] == '.') || *real_old.get_win32 () == '.') + attr &= ~FILE_ATTRIBUTE_HIDDEN; + c = strrchr (real_new.get_win32 (), '\\'); + if ((c && c[1] == '.') || *real_new.get_win32 () == '.') + attr |= FILE_ATTRIBUTE_HIDDEN; +#endif + SetFileAttributes (real_new, attr); + + /* Shortcut hack, No. 2, part 2 */ + /* if the new filename was an existing shortcut, remove it now if the + new filename is equal to the shortcut name without .lnk suffix. */ + if (lnk_suffix) + { + *lnk_suffix = '.'; + DeleteFile (real_new); + } + } + + syscall_printf ("%d = rename (%s, %s)", res, (char *) real_old, + (char *) real_new); + + return res; +} + +struct system_cleanup_args +{ + _sig_func_ptr oldint, oldquit; + sigset_t old_mask; +}; + +static void system_cleanup (void *args) +{ + struct system_cleanup_args *cleanup_args = (struct system_cleanup_args *) args; + + signal (SIGINT, cleanup_args->oldint); + signal (SIGQUIT, cleanup_args->oldquit); + (void) sigprocmask (SIG_SETMASK, &cleanup_args->old_mask, 0); +} + +extern "C" int +system (const char *cmdstring) +{ + pthread_testcancel (); + + if (check_null_empty_str_errno (cmdstring)) + return -1; + + sigframe thisframe (mainthread); + int res; + const char* command[4]; + struct system_cleanup_args cleanup_args; + sigset_t child_block; + + if (cmdstring == (const char *) NULL) + return 1; + + cleanup_args.oldint = signal (SIGINT, SIG_IGN); + cleanup_args.oldquit = signal (SIGQUIT, SIG_IGN); + sigemptyset (&child_block); + sigaddset (&child_block, SIGCHLD); + (void) sigprocmask (SIG_BLOCK, &child_block, &cleanup_args.old_mask); + + command[0] = "sh"; + command[1] = "-c"; + command[2] = cmdstring; + command[3] = (const char *) NULL; + + pthread_cleanup_push (system_cleanup, (void *) &cleanup_args); + + if ((res = spawnvp (_P_WAIT, "sh", command)) == -1) + { + // when exec fails, return value should be as if shell + // executed exit (127) + res = 127; + } + + pthread_cleanup_pop (1); + + return res; +} + +extern "C" int +setdtablesize (int size) +{ + if (size <= (int)cygheap->fdtab.size || cygheap->fdtab.extend (size - cygheap->fdtab.size)) + return 0; + + return -1; +} + +extern "C" int +getdtablesize () +{ + return cygheap->fdtab.size > OPEN_MAX ? cygheap->fdtab.size : OPEN_MAX; +} + +extern "C" size_t +getpagesize () +{ + if (!system_info.dwPageSize) + GetSystemInfo (&system_info); + return (int) system_info.dwPageSize; +} + +static int +check_posix_perm (const char *fname, int v) +{ + /* Windows 95/98/ME don't support file system security at all. */ + if (!wincap.has_security ()) + return 0; + + /* ntea is ok for supporting permission bits but it doesn't support + full POSIX security settings. */ + if (v == _PC_POSIX_PERMISSIONS && allow_ntea) + return 1; + + if (!allow_ntsec) + return 0; + + char *root = rootdir (strcpy ((char *)alloca (strlen (fname)), fname)); + + if (!allow_smbntsec + && ((root[0] == '\\' && root[1] == '\\') + || GetDriveType (root) == DRIVE_REMOTE)) + return 0; + + DWORD vsn, len, flags; + if (!GetVolumeInformation (root, NULL, 0, &vsn, &len, &flags, NULL, 16)) + { + __seterrno (); + return 0; + } + + return (flags & FS_PERSISTENT_ACLS) ? 1 : 0; +} + +/* FIXME: not all values are correct... */ +extern "C" long int +fpathconf (int fd, int v) +{ + cygheap_fdget cfd (fd); + if (cfd < 0) + return -1; + switch (v) + { + case _PC_LINK_MAX: + return _POSIX_LINK_MAX; + case _PC_MAX_CANON: + case _PC_MAX_INPUT: + if (isatty (fd)) + return _POSIX_MAX_CANON; + else + { + set_errno (EBADF); + return -1; + } + case _PC_NAME_MAX: + case _PC_PATH_MAX: + return PATH_MAX; + case _PC_PIPE_BUF: + return PIPE_BUF; + case _PC_CHOWN_RESTRICTED: + case _PC_NO_TRUNC: + return -1; + case _PC_VDISABLE: + if (cfd->is_tty ()) + return -1; + else + { + set_errno (EBADF); + return -1; + } + case _PC_POSIX_PERMISSIONS: + case _PC_POSIX_SECURITY: + if (cfd->get_device () == FH_DISK) + return check_posix_perm (cfd->get_win32_name (), v); + set_errno (EINVAL); + return -1; + default: + set_errno (EINVAL); + return -1; + } +} + +extern "C" long int +pathconf (const char *file, int v) +{ + switch (v) + { + case _PC_PATH_MAX: + if (check_null_empty_str_errno (file)) + return -1; + return PATH_MAX - strlen (file); + case _PC_NAME_MAX: + return PATH_MAX; + case _PC_LINK_MAX: + return _POSIX_LINK_MAX; + case _PC_MAX_CANON: + case _PC_MAX_INPUT: + return _POSIX_MAX_CANON; + case _PC_PIPE_BUF: + return PIPE_BUF; + case _PC_CHOWN_RESTRICTED: + case _PC_NO_TRUNC: + return -1; + case _PC_VDISABLE: + return -1; + case _PC_POSIX_PERMISSIONS: + case _PC_POSIX_SECURITY: + { + path_conv full_path (file, PC_SYM_FOLLOW | PC_FULL); + if (full_path.error) + { + set_errno (full_path.error); + return -1; + } + if (full_path.is_device ()) + { + set_errno (EINVAL); + return -1; + } + return check_posix_perm (full_path, v); + } + default: + set_errno (EINVAL); + return -1; + } +} + +extern "C" char * +ttyname (int fd) +{ + cygheap_fdget cfd (fd); + if (cfd < 0 || !cfd->is_tty ()) + { + return 0; + } + return (char *) (cfd->ttyname ()); +} + +extern "C" char * +ctermid (char *str) +{ + static NO_COPY char buf[16]; + if (str == NULL) + str = buf; + if (!real_tty_attached (myself)) + strcpy (str, "/dev/conin"); + else + __small_sprintf (str, "/dev/tty%d", myself->ctty); + return str; +} + +/* Tells stdio if it should do the cr/lf conversion for this file */ +extern "C" int +_cygwin_istext_for_stdio (int fd) +{ + syscall_printf ("_cygwin_istext_for_stdio (%d)", fd); + if (CYGWIN_VERSION_OLD_STDIO_CRLF_HANDLING) + { + syscall_printf (" _cifs: old API"); + return 0; /* we do it for old apps, due to getc/putc macros */ + } + + cygheap_fdget cfd (fd, false, false); + if (cfd < 0) + { + syscall_printf (" _cifs: fd not open"); + return 0; + } + + if (cfd->get_device () != FH_DISK) + { + syscall_printf (" _cifs: fd not disk file"); + return 0; + } + + if (cfd->get_w_binary () || cfd->get_r_binary ()) + { + syscall_printf (" _cifs: get_*_binary"); + return 0; + } + + syscall_printf ("_cygwin_istext_for_stdio says yes"); + return 1; +} + +/* internal newlib function */ +extern "C" int _fwalk (struct _reent *ptr, int (*function) (FILE *)); + +static int setmode_mode; +static int setmode_file; + +static int +setmode_helper (FILE *f) +{ + if (fileno (f) != setmode_file) + return 0; + syscall_printf ("setmode: file was %s now %s", + f->_flags & __SCLE ? "text" : "raw", + setmode_mode & O_TEXT ? "text" : "raw"); + if (setmode_mode & O_TEXT) + f->_flags |= __SCLE; + else + f->_flags &= ~__SCLE; + return 0; +} + +extern "C" int +getmode (int fd) +{ + cygheap_fdget cfd (fd); + if (cfd < 0) + return -1; + + return cfd->get_flags () & (O_BINARY | O_TEXT); +} + +/* Set a file descriptor into text or binary mode, returning the + previous mode. */ + +extern "C" int +setmode (int fd, int mode) +{ + cygheap_fdget cfd (fd); + if (cfd < 0) + return -1; + if (mode != O_BINARY && mode != O_TEXT && mode != 0) + { + set_errno (EINVAL); + return -1; + } + + /* Note that we have no way to indicate the case that writes are + binary but not reads, or vice-versa. These cases can arise when + using the tty or console interface. People using those + interfaces should not use setmode. */ + + int res; + if (cfd->get_w_binary () && cfd->get_r_binary ()) + res = O_BINARY; + else if (cfd->get_w_binset () && cfd->get_r_binset ()) + res = O_TEXT; /* Specifically set O_TEXT */ + else + res = 0; + + if (!mode) + cfd->reset_to_open_binmode (); + else + cfd->set_flags ((cfd->get_flags () & ~(O_TEXT | O_BINARY)) | mode); + + if (_cygwin_istext_for_stdio (fd)) + setmode_mode = O_TEXT; + else + setmode_mode = O_BINARY; + setmode_file = fd; + _fwalk (_REENT, setmode_helper); + + syscall_printf ("setmode (%d<%s>, %p) returns %s", fd, cfd->get_name (), + mode, res & O_TEXT ? "text" : "binary"); + return res; +} + +extern "C" int +ftruncate64 (int fd, _off64_t length) +{ + sigframe thisframe (mainthread); + int res = -1; + + if (length < 0) + set_errno (EINVAL); + else + { + cygheap_fdget cfd (fd); + if (cfd >= 0) + { + HANDLE h = cygheap->fdtab[fd]->get_handle (); + + if (cfd->get_handle ()) + { + /* remember curr file pointer location */ + _off64_t prev_loc = cfd->lseek (0, SEEK_CUR); + + cfd->lseek (length, SEEK_SET); + if (!SetEndOfFile (h)) + __seterrno (); + else + res = 0; + + /* restore original file pointer location */ + cfd->lseek (prev_loc, SEEK_SET); + } + } + } + + syscall_printf ("%d = ftruncate (%d, %d)", res, fd, length); + return res; +} + +/* ftruncate: P96 5.6.7.1 */ +extern "C" int +ftruncate (int fd, _off_t length) +{ + return ftruncate64 (fd, (_off64_t)length); +} + +/* truncate: Provided by SVR4 and 4.3+BSD. Not part of POSIX.1 or XPG3 */ +extern "C" int +truncate64 (const char *pathname, _off64_t length) +{ + sigframe thisframe (mainthread); + int fd; + int res = -1; + + fd = open (pathname, O_RDWR); + + if (fd == -1) + set_errno (EBADF); + else + { + res = ftruncate64 (fd, length); + close (fd); + } + syscall_printf ("%d = truncate (%s, %d)", res, pathname, length); + + return res; +} + +/* truncate: Provided by SVR4 and 4.3+BSD. Not part of POSIX.1 or XPG3 */ +extern "C" int +truncate (const char *pathname, _off_t length) +{ + return truncate64 (pathname, (_off64_t)length); +} + +extern "C" long +get_osfhandle (int fd) +{ + long res; + + cygheap_fdget cfd (fd); + if (cfd >= 0) + res = (long) cfd->get_handle (); + else + res = -1; + + syscall_printf ("%d = get_osfhandle (%d)", res, fd); + return res; +} + +extern "C" int +statfs (const char *fname, struct statfs *sfs) +{ + sigframe thisframe (mainthread); + if (!sfs) + { + set_errno (EFAULT); + return -1; + } + + path_conv full_path (fname, PC_SYM_FOLLOW | PC_FULL); + + const char *root = full_path.root_dir(); + + syscall_printf ("statfs %s", root); + + /* GetDiskFreeSpaceEx must be called before GetDiskFreeSpace on + WinME, to avoid the MS KB 314417 bug */ + ULARGE_INTEGER availb, freeb, totalb; + BOOL status = GetDiskFreeSpaceEx (root, &availb, &totalb, &freeb); + + DWORD spc, bps, availc, freec, totalc; + + if (!GetDiskFreeSpace (root, &spc, &bps, &freec, &totalc)) + { + __seterrno (); + return -1; + } + + if (status) + { + availc = availb.QuadPart / (spc*bps); + totalc = totalb.QuadPart / (spc*bps); + freec = freeb.QuadPart / (spc*bps); + } + else + availc = freec; + + DWORD vsn, maxlen, flags; + + if (!GetVolumeInformation (root, NULL, 0, &vsn, &maxlen, &flags, NULL, 0)) + { + __seterrno (); + return -1; + } + sfs->f_type = flags; + sfs->f_bsize = spc*bps; + sfs->f_blocks = totalc; + sfs->f_bavail = availc; + sfs->f_bfree = freec; + sfs->f_files = -1; + sfs->f_ffree = -1; + sfs->f_fsid = vsn; + sfs->f_namelen = maxlen; + return 0; +} + +extern "C" int +fstatfs (int fd, struct statfs *sfs) +{ + sigframe thisframe (mainthread); + cygheap_fdget cfd (fd); + if (cfd < 0) + return -1; + return statfs (cfd->get_name (), sfs); +} + +/* setpgid: POSIX 4.3.3.1 */ +extern "C" int +setpgid (pid_t pid, pid_t pgid) +{ + sigframe thisframe (mainthread); + int res = -1; + if (pid == 0) + pid = getpid (); + if (pgid == 0) + pgid = pid; + + if (pgid < 0) + { + set_errno (EINVAL); + goto out; + } + else + { + pinfo p (pid); + if (!p) + { + set_errno (ESRCH); + goto out; + } + /* A process may only change the process group of itself and its children */ + if (p == myself || p->ppid == myself->pid) + { + p->pgid = pgid; + if (p->pid != p->pgid) + p->set_has_pgid_children (0); + res = 0; + } + else + { + set_errno (EPERM); + goto out; + } + } +out: + syscall_printf ("pid %d, pgid %d, res %d", pid, pgid, res); + return res; +} + +extern "C" pid_t +getpgid (pid_t pid) +{ + sigframe thisframe (mainthread); + if (pid == 0) + pid = getpid (); + + pinfo p (pid); + if (p == 0) + { + set_errno (ESRCH); + return -1; + } + return p->pgid; +} + +extern "C" int +setpgrp (void) +{ + sigframe thisframe (mainthread); + return setpgid (0, 0); +} + +extern "C" pid_t +getpgrp (void) +{ + sigframe thisframe (mainthread); + return getpgid (0); +} + +extern "C" char * +ptsname (int fd) +{ + sigframe thisframe (mainthread); + cygheap_fdget cfd (fd); + if (cfd < 0) + return 0; + return (char *) (cfd->ptsname ()); +} + +/* FIXME: what is this? */ +extern "C" int __declspec(dllexport) +regfree () +{ + return 0; +} + +/* mknod was the call to create directories before the introduction + of mkdir in 4.2BSD and SVR3. Use of mknod required superuser privs + so the mkdir command had to be setuid root. + Although mknod hasn't been implemented yet, some GNU tools (e.g. the + fileutils) assume its existence so we must provide a stub that always + fails. */ +extern "C" int +mknod32 (const char *_path, mode_t mode, __dev32_t dev) +{ + set_errno (ENOSYS); + return -1; +} + +extern "C" int +mknod (const char *_path, mode_t mode, __dev16_t dev) +{ + return mknod32 (_path, mode, (__dev32_t) dev); +} + +extern "C" int +mkfifo (const char *_path, mode_t mode) +{ + set_errno (ENOSYS); + return -1; +} + +/* seteuid: standards? */ +extern "C" int +seteuid32 (__uid32_t uid) +{ + debug_printf ("uid: %u myself->gid: %u", uid, myself->gid); + + if (uid == myself->uid && !cygheap->user.groups.ischanged) + { + debug_printf ("Nothing happens"); + return 0; + } + + sigframe thisframe (mainthread); + cygsid usersid; + user_groups &groups = cygheap->user.groups; + HANDLE ptok, new_token = INVALID_HANDLE_VALUE; + struct passwd * pw_new; + cygpsid origpsid, psid2 (NO_SID); + BOOL token_is_internal, issamesid; + + pw_new = internal_getpwuid (uid); + if (!wincap.has_security () && pw_new) + goto success_9x; + if (!usersid.getfrompw (pw_new)) + { + set_errno (EINVAL); + return -1; + } + + RevertToSelf (); + if (!OpenProcessToken (hMainProc, TOKEN_QUERY | TOKEN_ADJUST_DEFAULT, &ptok)) + { + __seterrno (); + goto failed_ptok;; + } + + /* Verify if the process token is suitable. */ + if (verify_token (ptok, usersid, groups)) + new_token = ptok; + /* Verify if the external token is suitable */ + else if (cygheap->user.external_token != INVALID_HANDLE_VALUE + && verify_token (cygheap->user.external_token, usersid, groups)) + new_token = cygheap->user.external_token; + /* Verify if the current token (internal or former external) is suitable */ + else if (cygheap->user.current_token != INVALID_HANDLE_VALUE + && cygheap->user.current_token != cygheap->user.external_token + && verify_token (cygheap->user.current_token, usersid, groups, + &token_is_internal)) + new_token = cygheap->user.current_token; + /* Verify if the internal token is suitable */ + else if (cygheap->user.internal_token != INVALID_HANDLE_VALUE + && cygheap->user.internal_token != cygheap->user.current_token + && verify_token (cygheap->user.internal_token, usersid, groups, + &token_is_internal)) + new_token = cygheap->user.internal_token; + + debug_printf ("Found token %d", new_token); + + /* Set process def dacl to allow access to impersonated token */ + if (cygheap->user.current_token != new_token) + { + char dacl_buf[MAX_DACL_LEN (5)]; + if (usersid != (origpsid = cygheap->user.orig_sid ())) + psid2 = usersid; + if (sec_acl ((PACL) dacl_buf, FALSE, origpsid, psid2)) + { + TOKEN_DEFAULT_DACL tdacl; + tdacl.DefaultDacl = (PACL) dacl_buf; + if (!SetTokenInformation (ptok, TokenDefaultDacl, + &tdacl, sizeof dacl_buf)) + debug_printf ("SetTokenInformation" + "(TokenDefaultDacl): %E"); + } + } + + /* If no impersonation token is available, try to + authenticate using NtCreateToken () or subauthentication. */ + if (new_token == INVALID_HANDLE_VALUE) + { + new_token = create_token (usersid, groups, pw_new); + if (new_token == INVALID_HANDLE_VALUE) + { + /* create_token failed. Try subauthentication. */ + debug_printf ("create token failed, try subauthentication."); + new_token = subauth (pw_new); + if (new_token == INVALID_HANDLE_VALUE) + goto failed; + } + /* Keep at most one internal token */ + if (cygheap->user.internal_token != INVALID_HANDLE_VALUE) + CloseHandle (cygheap->user.internal_token); + cygheap->user.internal_token = new_token; + } + else if (new_token != ptok) + { + /* Avoid having HKCU use default user */ + load_registry_hive (usersid); + + /* Try setting owner to same value as user. */ + if (!SetTokenInformation (new_token, TokenOwner, + &usersid, sizeof usersid)) + debug_printf ("SetTokenInformation(user.token, " + "TokenOwner): %E"); + /* Try setting primary group in token to current group */ + if (!SetTokenInformation (new_token, + TokenPrimaryGroup, + &groups.pgsid, sizeof (cygsid))) + debug_printf ("SetTokenInformation(user.token, " + "TokenPrimaryGroup): %E"); + } + + CloseHandle (ptok); + issamesid = (usersid == (psid2 = cygheap->user.sid ())); + cygheap->user.set_sid (usersid); + cygheap->user.current_token = new_token == ptok ? INVALID_HANDLE_VALUE + : new_token; + if (!issamesid) /* MS KB 199190 */ + RegCloseKey(HKEY_CURRENT_USER); + cygheap->user.reimpersonate (); + if (!issamesid) + user_shared_initialize (); + +success_9x: + cygheap->user.set_name (pw_new->pw_name); + myself->uid = uid; + groups.ischanged = FALSE; + return 0; + +failed: + CloseHandle (ptok); +failed_ptok: + cygheap->user.reimpersonate (); + return -1; +} + +extern "C" int +seteuid (__uid16_t uid) +{ + return seteuid32 (uid16touid32 (uid)); +} + +/* setuid: POSIX 4.2.2.1 */ +extern "C" int +setuid32 (__uid32_t uid) +{ + int ret = seteuid32 (uid); + if (!ret) + cygheap->user.real_uid = myself->uid; + debug_printf ("real: %d, effective: %d", cygheap->user.real_uid, myself->uid); + return ret; +} + +extern "C" int +setuid (__uid16_t uid) +{ + return setuid32 (uid16touid32 (uid)); +} + +extern "C" int +setreuid32 (__uid32_t ruid, __uid32_t euid) +{ + int ret = 0; + bool tried = false; + __uid32_t old_euid = myself->uid; + + if (ruid != ILLEGAL_UID && cygheap->user.real_uid != ruid && euid != ruid) + tried = !(ret = seteuid32 (ruid)); + if (!ret && euid != ILLEGAL_UID) + ret = seteuid32 (euid); + if (tried && (ret || euid == ILLEGAL_UID) && seteuid32 (old_euid)) + system_printf ("Cannot restore original euid %u", old_euid); + if (!ret && ruid != ILLEGAL_UID) + cygheap->user.real_uid = ruid; + debug_printf ("real: %u, effective: %u", cygheap->user.real_uid, myself->uid); + return ret; +} + +extern "C" int +setreuid (__uid16_t ruid, __uid16_t euid) +{ + return setreuid32 (uid16touid32 (ruid), uid16touid32 (euid)); +} + +/* setegid: from System V. */ +extern "C" int +setegid32 (__gid32_t gid) +{ + debug_printf ("new egid: %u current: %u", gid, myself->gid); + + if (gid == myself->gid || !wincap.has_security ()) + { + myself->gid = gid; + return 0; + } + + sigframe thisframe (mainthread); + user_groups * groups = &cygheap->user.groups; + cygsid gsid; + HANDLE ptok; + struct __group32 * gr = internal_getgrgid (gid); + + if (!gsid.getfromgr (gr)) + { + set_errno (EINVAL); + return -1; + } + myself->gid = gid; + + groups->update_pgrp (gsid); + /* If impersonated, update primary group and revert */ + if (cygheap->user.issetuid ()) + { + if (!SetTokenInformation (cygheap->user.token (), + TokenPrimaryGroup, + &gsid, sizeof gsid)) + debug_printf ("SetTokenInformation(thread, " + "TokenPrimaryGroup): %E"); + RevertToSelf (); + } + if (!OpenProcessToken (hMainProc, TOKEN_ADJUST_DEFAULT, &ptok)) + debug_printf ("OpenProcessToken(): %E"); + else + { + if (!SetTokenInformation (ptok, TokenPrimaryGroup, + &gsid, sizeof gsid)) + debug_printf ("SetTokenInformation(process, " + "TokenPrimaryGroup): %E"); + CloseHandle (ptok); + } + if (cygheap->user.issetuid () + && !ImpersonateLoggedOnUser (cygheap->user.token ())) + system_printf ("Impersonating in setegid failed: %E"); + return 0; +} + +extern "C" int +setegid (__gid16_t gid) +{ + return setegid32 (gid16togid32 (gid)); +} + +/* setgid: POSIX 4.2.2.1 */ +extern "C" int +setgid32 (__gid32_t gid) +{ + int ret = setegid32 (gid); + if (!ret) + cygheap->user.real_gid = myself->gid; + return ret; +} + +extern "C" int +setgid (__gid16_t gid) +{ + int ret = setegid32 (gid16togid32 (gid)); + if (!ret) + cygheap->user.real_gid = myself->gid; + return ret; +} + +extern "C" int +setregid32 (__gid32_t rgid, __gid32_t egid) +{ + int ret = 0; + bool tried = false; + __gid32_t old_egid = myself->gid; + + if (rgid != ILLEGAL_GID && cygheap->user.real_gid != rgid && egid != rgid) + tried = !(ret = setegid32 (rgid)); + if (!ret && egid != ILLEGAL_GID) + ret = setegid32 (egid); + if (tried && (ret || egid == ILLEGAL_GID) && setegid32 (old_egid)) + system_printf ("Cannot restore original egid %u", old_egid); + if (!ret && rgid != ILLEGAL_GID) + cygheap->user.real_gid = rgid; + debug_printf ("real: %u, effective: %u", cygheap->user.real_gid, myself->gid); + return ret; +} + +extern "C" int +setregid (__gid16_t rgid, __gid16_t egid) +{ + return setregid32 (gid16togid32 (rgid), gid16togid32 (egid)); +} + +/* chroot: privileged Unix system call. */ +/* FIXME: Not privileged here. How should this be done? */ +extern "C" int +chroot (const char *newroot) +{ + sigframe thisframe (mainthread); + path_conv path (newroot, PC_SYM_FOLLOW | PC_FULL | PC_POSIX); + + int ret; + if (path.error) + ret = -1; + else if (!path.exists ()) + { + set_errno (ENOENT); + ret = -1; + } + else if (!path.isdir ()) + { + set_errno (ENOTDIR); + ret = -1; + } + else + { + cygheap->root.set (path.normalized_path, path); + ret = 0; + } + + syscall_printf ("%d = chroot (%s)", ret ? get_errno () : 0, + newroot ? newroot : "NULL"); + if (path.normalized_path) + cfree (path.normalized_path); + return ret; +} + +extern "C" int +creat (const char *path, mode_t mode) +{ + sigframe thisframe (mainthread); + return open (path, O_WRONLY | O_CREAT | O_TRUNC, mode); +} + +extern "C" void +__assertfail () +{ + exit (99); +} + +extern "C" int +getw (FILE *fp) +{ + sigframe thisframe (mainthread); + int w, ret; + ret = fread (&w, sizeof (int), 1, fp); + return ret != 1 ? EOF : w; +} + +extern "C" int +putw (int w, FILE *fp) +{ + sigframe thisframe (mainthread); + int ret; + ret = fwrite (&w, sizeof (int), 1, fp); + if (feof (fp) || ferror (fp)) + return -1; + return 0; +} + +extern "C" int +wcscmp (const wchar_t *s1, const wchar_t *s2) +{ + while (*s1 && *s1 == *s2) + { + s1++; + s2++; + } + + return (* (unsigned short *) s1) - (* (unsigned short *) s2); +} + +extern "C" size_t +wcslen (const wchar_t *s1) +{ + int l = 0; + while (s1[l]) + l++; + return l; +} + +/* FIXME: to do this right, maybe work out the usoft va_list machine + and use wsvprintfW instead? +*/ +extern "C" int +wprintf (const char *fmt, ...) +{ + va_list ap; + int ret; + + va_start (ap, fmt); + ret = vprintf (fmt, ap); + va_end (ap); + return ret; +} + +extern "C" int +vhangup () +{ + set_errno (ENOSYS); + return -1; +} + +extern "C" _PTR +memccpy (_PTR out, const _PTR in, int c, size_t len) +{ + const char *inc = (char *) in; + char *outc = (char *) out; + + while (len) + { + char x = *inc++; + *outc++ = x; + if (x == c) + return outc; + len --; + } + return 0; +} + +extern "C" int +nice (int incr) +{ + sigframe thisframe (mainthread); + DWORD priority[] = + { + IDLE_PRIORITY_CLASS, + IDLE_PRIORITY_CLASS, + NORMAL_PRIORITY_CLASS, + HIGH_PRIORITY_CLASS, + REALTIME_PRIORITY_CLASS, + REALTIME_PRIORITY_CLASS + }; + int curr = 2; + + switch (GetPriorityClass (hMainProc)) + { + case IDLE_PRIORITY_CLASS: + curr = 1; + break; + case NORMAL_PRIORITY_CLASS: + curr = 2; + break; + case HIGH_PRIORITY_CLASS: + curr = 3; + break; + case REALTIME_PRIORITY_CLASS: + curr = 4; + break; + } + if (incr > 0) + incr = -1; + else if (incr < 0) + incr = 1; + + if (SetPriorityClass (hMainProc, priority[curr + incr]) == FALSE) + { + __seterrno (); + return -1; + } + + return 0; +} + +/* + * Find the first bit set in I. + */ + +extern "C" int +ffs (int i) +{ + static const unsigned char table[] = + { + 0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8 + }; + unsigned long int a; + unsigned long int x = i & -i; + + a = x <= 0xffff ? (x <= 0xff ? 0 : 8) : (x <= 0xffffff ? 16 : 24); + + return table[x >> a] + a; +} + +extern "C" void +updwtmp (const char *wtmp_file, const struct utmp *ut) +{ + /* Writing to wtmp must be atomic to prevent mixed up data. */ + char mutex_name[MAX_PATH]; + HANDLE mutex; + int fd; + + mutex = CreateMutex (NULL, FALSE, shared_name (mutex_name, "wtmp_mutex", 0)); + if (mutex) + while (WaitForSingleObject (mutex, INFINITE) == WAIT_ABANDONED) + ; + if ((fd = open (wtmp_file, O_WRONLY | O_APPEND | O_BINARY, 0)) >= 0) + { + write (fd, ut, sizeof *ut); + close (fd); + } + if (mutex) + { + ReleaseMutex (mutex); + CloseHandle (mutex); + } +} + +extern "C" void +logwtmp (const char *line, const char *user, const char *host) +{ + sigframe thisframe (mainthread); + struct utmp ut; + memset (&ut, 0, sizeof ut); + ut.ut_type = USER_PROCESS; + ut.ut_pid = getpid (); + if (line) + strncpy (ut.ut_line, line, sizeof ut.ut_line); + time (&ut.ut_time); + if (user) + strncpy (ut.ut_user, user, sizeof ut.ut_user); + if (host) + strncpy (ut.ut_host, host, sizeof ut.ut_host); + updwtmp (_PATH_WTMP, &ut); +} + +extern "C" void +login (struct utmp *ut) +{ + sigframe thisframe (mainthread); + pututline (ut); + endutent (); + updwtmp (_PATH_WTMP, ut); +} + +extern "C" int +logout (char *line) +{ + sigframe thisframe (mainthread); + struct utmp ut_buf, *ut; + + memset (&ut_buf, 0, sizeof ut_buf); + strncpy (ut_buf.ut_line, line, sizeof ut_buf.ut_line); + setutent (); + ut = getutline (&ut_buf); + + if (ut) + { + ut->ut_type = DEAD_PROCESS; + memset (ut->ut_user, 0, sizeof ut->ut_user); + time (&ut->ut_time); + updwtmp (_PATH_WTMP, &ut_buf); + debug_printf ("set logout time for %s", line); + memset (ut->ut_line, 0, sizeof ut_buf.ut_line); + ut->ut_time = 0; + pututline (ut); + endutent (); + } + return 1; +} + +static int utmp_fd = -1; +static bool utmp_readonly = false; +static char *utmp_file = (char *) _PATH_UTMP; + +static void +internal_setutent (bool force_readwrite) +{ + sigframe thisframe (mainthread); + if (force_readwrite && utmp_readonly) + endutent (); + if (utmp_fd < 0) + { + utmp_fd = open (utmp_file, O_RDWR | O_BINARY); + /* If open fails, we assume an unprivileged process (who?). In this + case we try again for reading only unless the process calls + pututline() (==force_readwrite) in which case opening just fails. */ + if (utmp_fd < 0 && !force_readwrite) + { + utmp_fd = open (utmp_file, O_RDONLY | O_BINARY); + if (utmp_fd >= 0) + utmp_readonly = true; + } + } + else + lseek (utmp_fd, 0, SEEK_SET); +} + +extern "C" void +setutent () +{ + internal_setutent (false); +} + +extern "C" void +endutent () +{ + sigframe thisframe (mainthread); + if (utmp_fd >= 0) + { + close (utmp_fd); + utmp_fd = -1; + utmp_readonly = false; + } +} + +extern "C" void +utmpname (_CONST char *file) +{ + sigframe thisframe (mainthread); + if (check_null_empty_str (file)) + { + debug_printf ("Invalid file"); + return; + } + endutent (); + utmp_file = strdup (file); + debug_printf ("New UTMP file: %s", utmp_file); +} + +/* Note: do not make NO_COPY */ +static struct utmp utmp_data_buf[16]; +static unsigned utix = 0; +#define nutdbuf (sizeof (utmp_data_buf) / sizeof (utmp_data_buf[0])) +#define utmp_data ({ \ + if (utix > nutdbuf) \ + utix = 0; \ + utmp_data_buf + utix++; \ +}) + +extern "C" struct utmp * +getutent () +{ + sigframe thisframe (mainthread); + if (utmp_fd < 0) + { + internal_setutent (false); + if (utmp_fd < 0) + return NULL; + } + + utmp *ut = utmp_data; + if (read (utmp_fd, ut, sizeof *ut) != sizeof *ut) + return NULL; + return ut; +} + +extern "C" struct utmp * +getutid (struct utmp *id) +{ + sigframe thisframe (mainthread); + if (check_null_invalid_struct_errno (id)) + return NULL; + if (utmp_fd < 0) + { + internal_setutent (false); + if (utmp_fd < 0) + return NULL; + } + + utmp *ut = utmp_data; + while (read (utmp_fd, ut, sizeof *ut) == sizeof *ut) + { + switch (id->ut_type) + { + case RUN_LVL: + case BOOT_TIME: + case OLD_TIME: + case NEW_TIME: + if (id->ut_type == ut->ut_type) + return ut; + break; + case INIT_PROCESS: + case LOGIN_PROCESS: + case USER_PROCESS: + case DEAD_PROCESS: + if (strncmp (id->ut_id, ut->ut_id, UT_IDLEN) == 0) + return ut; + break; + default: + return NULL; + } + } + return NULL; +} + +extern "C" struct utmp * +getutline (struct utmp *line) +{ + sigframe thisframe (mainthread); + if (check_null_invalid_struct_errno (line)) + return NULL; + if (utmp_fd < 0) + { + internal_setutent (false); + if (utmp_fd < 0) + return NULL; + } + + utmp *ut = utmp_data; + while (read (utmp_fd, ut, sizeof *ut) == sizeof *ut) + if ((ut->ut_type == LOGIN_PROCESS || + ut->ut_type == USER_PROCESS) && + !strncmp (ut->ut_line, line->ut_line, sizeof (ut->ut_line))) + return ut; + + return NULL; +} + +extern "C" void +pututline (struct utmp *ut) +{ + sigframe thisframe (mainthread); + if (check_null_invalid_struct (ut)) + return; + internal_setutent (true); + if (utmp_fd < 0) + { + debug_printf ("error: utmp_fd %d", utmp_fd); + return; + } + debug_printf ("ut->ut_type %d, ut->ut_pid %d, ut->ut_line '%s', ut->ut_id '%s'\n", + ut->ut_type, ut->ut_pid, ut->ut_line, ut->ut_id); + debug_printf ("ut->ut_user '%s', ut->ut_host '%s'\n", + ut->ut_user, ut->ut_host); + /* Read/write to utmp must be atomic to prevent overriding data + by concurrent processes. */ + char mutex_name[MAX_PATH]; + HANDLE mutex = CreateMutex (NULL, FALSE, + shared_name (mutex_name, "utmp_mutex", 0)); + if (mutex) + while (WaitForSingleObject (mutex, INFINITE) == WAIT_ABANDONED) + ; + struct utmp *u; + if ((u = getutid (ut))) + lseek (utmp_fd, -sizeof *ut, SEEK_CUR); + else + lseek (utmp_fd, 0, SEEK_END); + write (utmp_fd, ut, sizeof *ut); + if (mutex) + { + ReleaseMutex (mutex); + CloseHandle (mutex); + } +} + +extern "C" +long gethostid(void) +{ + unsigned data[13] = {0x92895012, + 0x10293412, + 0x29602018, + 0x81928167, + 0x34601329, + 0x75630198, + 0x89860395, + 0x62897564, + 0x00194362, + 0x20548593, + 0x96839102, + 0x12219854, + 0x00290012}; + + bool has_cpuid = false; + sigframe thisframe (mainthread); + + DWORD opmask = SetThreadAffinityMask (GetCurrentThread (), 1); + if (!opmask) + debug_printf ("SetThreadAffinityMask to 1 failed, %E"); + + if (!can_set_flag (0x00040000)) + debug_printf ("386 processor - no cpuid"); + else + { + debug_printf ("486 processor"); + if (can_set_flag (0x00200000)) + { + debug_printf ("processor supports CPUID instruction"); + has_cpuid = true; + } + else + debug_printf ("processor does not support CPUID instruction"); + } + if (has_cpuid) + { + unsigned maxf, unused[3]; + cpuid (&maxf, &unused[0], &unused[1], &unused[2], 0); + maxf &= 0xffff; + if (maxf >= 1) + { + unsigned features; + cpuid (&data[0], &unused[0], &unused[1], &features, 1); + if (features & (1 << 18)) + { + debug_printf ("processor has psn"); + if (maxf >= 3) + { + cpuid (&unused[0], &unused[1], &data[1], &data[2], 3); + debug_printf ("Processor PSN: %04x-%04x-%04x-%04x-%04x-%04x", + data[0] >> 16, data[0] & 0xffff, data[2] >> 16, data[2] & 0xffff, data[1] >> 16, data[1] & 0xffff); + } + } + else + debug_printf ("processor does not have psn"); + } + } + + UUID Uuid; + RPC_STATUS status = UuidCreateSequential (&Uuid); + if (GetLastError () == ERROR_PROC_NOT_FOUND) + status = UuidCreate (&Uuid); + if (status == RPC_S_OK) + { + data[4] = *(unsigned *)&Uuid.Data4[2]; + data[5] = *(unsigned short *)&Uuid.Data4[6]; + // Unfortunately Windows will sometimes pick a virtual Ethernet card + // e.g. VMWare Virtual Ethernet Adaptor + debug_printf ("MAC address of first Ethernet card: %02x:%02x:%02x:%02x:%02x:%02x", + Uuid.Data4[2], Uuid.Data4[3], Uuid.Data4[4], + Uuid.Data4[5], Uuid.Data4[6], Uuid.Data4[7]); + } + else + { + debug_printf ("no Ethernet card installed"); + } + + reg_key key (HKEY_LOCAL_MACHINE, KEY_READ, "SOFTWARE", "Microsoft", "Windows", "CurrentVersion", NULL); + key.get_string ("ProductId", (char *)&data[6], 24, "00000-000-0000000-00000"); + debug_printf ("Windows Product ID: %s", (char *)&data[6]); + + /* Contrary to MSDN, NT4 requires the second argument + or a STATUS_ACCESS_VIOLATION is generated */ + ULARGE_INTEGER availb; + GetDiskFreeSpaceEx ("C:\\", &availb, (PULARGE_INTEGER) &data[11], NULL); + if (GetLastError () == ERROR_PROC_NOT_FOUND) + GetDiskFreeSpace ("C:\\", NULL, NULL, NULL, (DWORD *)&data[11]); + + debug_printf ("hostid entropy: %08x %08x %08x %08x " + "%08x %08x %08x %08x " + "%08x %08x %08x %08x " + "%08x", + data[0], data[1], + data[2], data[3], + data[4], data[5], + data[6], data[7], + data[8], data[9], + data[10], data[11], + data[12]); + + long hostid = 0x40291372; + // a random hashing algorithm + // dependancy on md5 is probably too costly + for (int i=0;i<13;i++) + hostid ^= ((data[i] << (i << 2)) | (data[i] >> (32 - (i << 2)))); + + if (opmask && !SetThreadAffinityMask (GetCurrentThread (), opmask)) + debug_printf ("SetThreadAffinityMask to %p failed, %E", opmask); + + debug_printf ("hostid: %08x", hostid); + + return hostid; +} + +#define ETC_SHELLS "/etc/shells" +static int shell_index; +static struct __sFILE64 *shell_fp; + +extern "C" char * +getusershell () +{ + /* List of default shells if no /etc/shells exists, defined as on Linux. + FIXME: SunOS has a far longer list, containing all shells which + might be shipped with the OS. Should we do the same for the Cygwin + distro, adding bash, tcsh, ksh, pdksh and zsh? */ + static NO_COPY const char *def_shells[] = { + "/bin/sh", + "/bin/csh", + "/usr/bin/sh", + "/usr/bin/csh", + NULL + }; + static char buf[MAX_PATH]; + int ch, buf_idx; + + if (!shell_fp && !(shell_fp = fopen64 (ETC_SHELLS, "rt"))) + { + if (def_shells[shell_index]) + return strcpy (buf, def_shells[shell_index++]); + return NULL; + } + /* Skip white space characters. */ + while ((ch = getc (shell_fp)) != EOF && isspace (ch)) + ; + /* Get each non-whitespace character as part of the shell path as long as + it fits in buf. */ + for (buf_idx = 0; + ch != EOF && !isspace (ch) && buf_idx < MAX_PATH; + buf_idx++, ch = getc (shell_fp)) + buf[buf_idx] = ch; + /* Skip any trailing non-whitespace character not fitting in buf. If the + path is longer than MAX_PATH, it's invalid anyway. */ + while (ch != EOF && !isspace (ch)) + ch = getc (shell_fp); + if (buf_idx) + { + buf[buf_idx] = '\0'; + return buf; + } + return NULL; +} + +extern "C" void +setusershell () +{ + if (shell_fp) + fseek (shell_fp, 0L, SEEK_SET); + shell_index = 0; +} + +extern "C" void +endusershell () +{ + if (shell_fp) + fclose (shell_fp); + shell_index = 0; +} diff --git a/winsup/cygwin/termios.cc b/winsup/cygwin/termios.cc new file mode 100644 index 00000000000..c82f771b7dd --- /dev/null +++ b/winsup/cygwin/termios.cc @@ -0,0 +1,307 @@ +/* termios.cc: termios for WIN32. + + Copyright 1996, 1997, 1998, 2000, 2001, 2002 Red Hat, Inc. + + Written by Doug Evans and Steve Chamberlain of Cygnus Support + dje@cygnus.com, sac@cygnus.com + +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 <signal.h> +#include <stdlib.h> +#include "cygerrno.h" +#include "security.h" +#include "fhandler.h" +#include "path.h" +#include "dtable.h" +#include "cygheap.h" +#include "cygwin/version.h" +#include "perprocess.h" +#include "sigproc.h" +#include <sys/termios.h> + +/* tcsendbreak: POSIX 7.2.2.1 */ +extern "C" int +tcsendbreak (int fd, int duration) +{ + int res = -1; + + cygheap_fdget cfd (fd); + if (cfd < 0) + goto out; + + if (!cfd->is_tty ()) + set_errno (ENOTTY); + else if ((res = cfd->bg_check (-SIGTTOU)) > bg_eof) + res = cfd->tcsendbreak (duration); + +out: + syscall_printf ("%d = tcsendbreak (%d, %d)", res, fd, duration); + return res; +} + +/* tcdrain: POSIX 7.2.2.1 */ +extern "C" int +tcdrain (int fd) +{ + int res = -1; + + termios_printf ("tcdrain"); + + cygheap_fdget cfd (fd); + if (cfd < 0) + goto out; + + if (!cfd->is_tty ()) + set_errno (ENOTTY); + else if ((res = cfd->bg_check (-SIGTTOU)) > bg_eof) + res = cfd->tcdrain (); + +out: + syscall_printf ("%d = tcdrain (%d)", res, fd); + return res; +} + +/* tcflush: POSIX 7.2.2.1 */ +extern "C" int +tcflush (int fd, int queue) +{ + int res = -1; + + cygheap_fdget cfd (fd); + if (cfd < 0) + goto out; + + if (!cfd->is_tty ()) + set_errno (ENOTTY); + else if (queue != TCIFLUSH && queue != TCOFLUSH && queue != TCIOFLUSH) + set_errno (EINVAL); + else if ((res = cfd->bg_check (-SIGTTOU)) > bg_eof) + res = cfd->tcflush (queue); + +out: + termios_printf ("%d = tcflush (%d, %d)", res, fd, queue); + return res; +} + +/* tcflow: POSIX 7.2.2.1 */ +extern "C" int +tcflow (int fd, int action) +{ + int res = -1; + + cygheap_fdget cfd (fd); + if (cfd < 0) + goto out; + + if (!cfd->is_tty ()) + set_errno (ENOTTY); + else if ((res = cfd->bg_check (-SIGTTOU)) > bg_eof) + res = cfd->tcflow (action); + +out: + syscall_printf ("%d = tcflow (%d, %d)", res, fd, action); + return res; +} + +/* tcsetattr: POSIX96 7.2.1.1 */ +extern "C" int +tcsetattr (int fd, int a, const struct termios *t) +{ + int res; + t = __tonew_termios (t); + int e = get_errno (); + + while (1) + { + sigframe thisframe (mainthread); + + res = -1; + cygheap_fdget cfd (fd); + if (cfd < 0) + { + e = get_errno (); + break; + } + + if (!cfd->is_tty ()) + { + e = ENOTTY; + break; + } + + res = cfd->bg_check (-SIGTTOU); + + switch (res) + { + case bg_eof: + e = get_errno (); + break; + case bg_ok: + if (cfd.isopen ()) + res = cfd->tcsetattr (a, t); + e = get_errno (); + break; + case bg_signalled: + if (thisframe.call_signal_handler ()) + continue; + res = -1; + /* fall through intentionally */ + default: + e = get_errno (); + break; + } + break; + } + + set_errno (e); + termios_printf ("iflag %p, oflag %p, cflag %p, lflag %p, VMIN %d, VTIME %d", + t->c_iflag, t->c_oflag, t->c_cflag, t->c_lflag, t->c_cc[VMIN], + t->c_cc[VTIME]); + termios_printf ("%d = tcsetattr (%d, %d, %x)", res, fd, a, t); + return res; +} + +/* tcgetattr: POSIX 7.2.1.1 */ +extern "C" int +tcgetattr (int fd, struct termios *in_t) +{ + int res = -1; + struct termios *t = __makenew_termios (in_t); + + cygheap_fdget cfd (fd); + if (cfd < 0) + /* saw an error */; + else if (!cfd->is_tty ()) + set_errno (ENOTTY); + else if ((res = cfd->tcgetattr (t)) == 0) + (void) __toapp_termios (in_t, t); + + if (res) + termios_printf ("%d = tcgetattr (%d, %p)", res, fd, in_t); + else + termios_printf ("iflag %x, oflag %x, cflag %x, lflag %x, VMIN %d, VTIME %d", + t->c_iflag, t->c_oflag, t->c_cflag, t->c_lflag, t->c_cc[VMIN], + t->c_cc[VTIME]); + + return res; +} + +/* tcgetpgrp: POSIX 7.2.3.1 */ +extern "C" int +tcgetpgrp (int fd) +{ + int res = -1; + + cygheap_fdget cfd (fd); + if (cfd < 0) + /* saw an error */; + else if (!cfd->is_tty ()) + set_errno (ENOTTY); + else + res = cfd->tcgetpgrp (); + + termios_printf ("%d = tcgetpgrp (%d)", res, fd); + return res; +} + +/* tcsetpgrp: POSIX 7.2.4.1 */ +extern "C" int +tcsetpgrp (int fd, pid_t pgid) +{ + int res = -1; + + cygheap_fdget cfd (fd); + if (cfd < 0) + /* saw an error */; + else if (!cfd->is_tty ()) + set_errno (ENOTTY); + else + res = cfd->tcsetpgrp (pgid); + + termios_printf ("%d = tcsetpgrp (%d, %x)", res, fd, pgid); + return res; +} + +/* NIST PCTS requires not macro-only implementation */ +#undef cfgetospeed +#undef cfgetispeed +#undef cfsetospeed +#undef cfsetispeed + +/* cfgetospeed: POSIX96 7.1.3.1 */ +extern "C" speed_t +cfgetospeed (struct termios *tp) +{ + return __tonew_termios (tp)->c_ospeed; +} + +/* cfgetispeed: POSIX96 7.1.3.1 */ +extern "C" speed_t +cfgetispeed (struct termios *tp) +{ + return __tonew_termios (tp)->c_ispeed; +} + +static inline int +setspeed (speed_t &set_speed, speed_t from_speed) +{ + int res; + switch (from_speed) + { + case B0: + case B50: + case B75: + case B110: + case B134: + case B150: + case B200: + case B300: + case B600: + case B1200: + case B1800: + case B2400: + case B4800: + case B9600: + case B19200: + case B38400: + case B57600: + case B115200: + case B128000: + case B230400: + case B256000: + set_speed = from_speed; + res = 0; + break; + default: + set_errno (EINVAL); + res = -1; + break; + } + return res; +} + +/* cfsetospeed: POSIX96 7.1.3.1 */ +extern "C" int +cfsetospeed (struct termios *in_tp, speed_t speed) +{ + struct termios *tp = __tonew_termios (in_tp); + int res = setspeed (tp->c_ospeed, speed); + (void) __toapp_termios (in_tp, tp); + return res; +} + +/* cfsetispeed: POSIX96 7.1.3.1 */ +extern "C" int +cfsetispeed (struct termios *in_tp, speed_t speed) +{ + struct termios *tp = __tonew_termios (in_tp); + int res = setspeed (tp->c_ispeed, speed); + (void) __toapp_termios (in_tp, tp); + return res; +} |