/* pipe.cc: pipe for Cygwin.
- Copyright 1996, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
- 2005 Red Hat, Inc.
+ Copyright 1996, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007,
+ 2008 Hat, Inc.
This file is part of Cygwin.
#include "cygthread.h"
#include "ntdll.h"
-static unsigned pipecount;
-static const NO_COPY char pipeid_fmt[] = "stupid_pipe.%u.%u";
-
fhandler_pipe::fhandler_pipe ()
- : fhandler_base (), guard (NULL), broken_pipe (false), writepipe_exists(0),
- orig_pid (0), id (0)
+ : fhandler_base (), popen_pid (0)
+{
+ get_overlapped ()->hEvent = NULL;
+ need_fork_fixup (true);
+}
+
+struct pipesync
+{
+ bool reader;
+ HANDLE ev, non_cygwin_h, ret_handle;
+ pipesync(HANDLE, DWORD);
+ int operator == (int x) const {return !!ev;}
+ static DWORD WINAPI handler (LPVOID *);
+};
+
+inline bool
+getov_result (HANDLE h, DWORD& nbytes, LPOVERLAPPED ov)
+{
+ if (ov && (GetLastError () != ERROR_IO_PENDING
+ || !GetOverlappedResult (h, ov, &nbytes, true)))
+ {
+ __seterrno ();
+ return false;
+ }
+ return true;
+}
+
+static DWORD WINAPI
+pipe_handler (LPVOID in_ps)
+{
+ pipesync ps = *(pipesync *) in_ps;
+ HANDLE in, out;
+ DWORD err = fhandler_pipe::create_selectable (&sec_none_nih, in, out, 0);
+ if (err)
+ {
+ SetLastError (err);
+ system_printf ("couldn't create a shadow pipe for non-cygwin pipe I/O, %E");
+ return 0;
+ }
+ ((pipesync *) in_ps)->ret_handle = ps.reader ? in : out;
+ SetEvent (ps.ev);
+
+ char buf[4096];
+ DWORD read_bytes, write_bytes;
+ HANDLE hread, hwrite, hclose;
+ OVERLAPPED ov, *rov, *wov;
+ memset (&ov, 0, sizeof (ov));
+ ov.hEvent = CreateEvent (&sec_none_nih, true, false, NULL);
+ if (ps.reader)
+ {
+ hread = ps.non_cygwin_h;
+ hclose = hwrite = out;
+ wov = &ov;
+ rov = NULL;
+ }
+ else
+ {
+ hclose = hread = in;
+ hwrite = ps.non_cygwin_h;
+ rov = &ov;
+ wov = NULL;
+ }
+
+ while (1)
+ {
+ ResetEvent (ov.hEvent);
+ BOOL res = ReadFile (hread, buf, 4096, &read_bytes, rov);
+ if (!res && !getov_result (hread, read_bytes, rov))
+ break;
+ if (!read_bytes)
+ break;
+
+ res = WriteFile (hwrite, buf, read_bytes, &write_bytes, wov);
+ if (!res && !getov_result (hwrite, write_bytes, wov))
+ break;
+ if (write_bytes != read_bytes)
+ break;
+ }
+
+ err = GetLastError ();
+ CloseHandle (ov.hEvent);
+ CloseHandle (hclose);
+ CloseHandle (ps.non_cygwin_h);
+ SetLastError (err);
+ return 0;
+}
+
+pipesync::pipesync (HANDLE f, DWORD is_reader):
+ reader (false), ret_handle (NULL)
+{
+ ev = CreateEvent (&sec_none_nih, true, false, NULL);
+ if (!ev)
+ {
+ system_printf ("couldn't create synchronization event for non-cygwin pipe, %E");
+ goto out;
+ }
+ non_cygwin_h = f;
+ reader = !!is_reader;
+ ret_handle = NULL;
+
+ DWORD tid;
+ HANDLE ht = CreateThread (&sec_none_nih, 0, pipe_handler, this, 0, &tid);
+
+ if (!ht)
+ goto out;
+ CloseHandle (ht);
+
+ switch (WaitForSingleObject (ev, INFINITE))
+ {
+ case WAIT_OBJECT_0:
+ break;
+ default:
+ system_printf ("WFSO failed waiting for synchronization event for non-cygwin pipe, %E");
+ break;
+ }
+
+out:
+ if (ev)
+ {
+ CloseHandle (ev);
+ ev = NULL;
+ }
+ return;
+}
+
+#define WINPIPE "\\\\.\\pipe\\"
+void
+fhandler_pipe::init (HANDLE f, DWORD a, mode_t mode)
{
+ // FIXME: Have to clean this up someday
+ if (!*get_win32_name () && get_name ())
+ {
+ char *hold_normalized_name = (char *) alloca (strlen (get_name ()) + 1);
+ strcpy (hold_normalized_name, get_name ());
+ char *s, *d;
+ for (s = hold_normalized_name, d = (char *) get_win32_name (); *s; s++, d++)
+ if (*s == '/')
+ *d = '\\';
+ else
+ *d = *s;
+ *d = '\0';
+ set_name (hold_normalized_name);
+ }
+
+ bool opened_properly = a & FILE_CREATE_PIPE_INSTANCE;
+ a &= ~FILE_CREATE_PIPE_INSTANCE;
+ if (!opened_properly)
+ {
+ pipesync ps (f, a & GENERIC_READ);
+ f = ps.ret_handle;
+ }
+
+ fhandler_base::init (f, a, mode);
+ if (mode & O_NOINHERIT)
+ close_on_exec (true);
+ setup_overlapped ();
}
+extern "C" int sscanf (const char *, const char *, ...);
+
int
fhandler_pipe::open (int flags, mode_t mode)
{
- const char *path = get_name ();
- debug_printf ("path: %s", path);
- if (!strncmp (get_name (), "/proc/", 6))
+ HANDLE proc, pipe_hdl, nio_hdl = NULL;
+ fhandler_pipe *fh = NULL;
+ size_t size;
+ int pid, rwflags = (flags & O_ACCMODE);
+ bool inh;
+
+ sscanf (get_name (), "/proc/%d/fd/pipe:[%d]", &pid, (int *) &pipe_hdl);
+ if (pid == myself->pid)
{
- char *c;
- HANDLE hdl;
- int pid = strtol (path += 6, &c, 10);
- if (!pid || !c || *c != '/')
- goto out;
- path = c;
- if (strncmp (path, "/fd/pipe:[", 10))
- goto out;
- path += 10;
- hdl = (HANDLE) atoi (path);
- if (pid == myself->pid)
- {
- cygheap_fdenum cfd;
- while (cfd.next () >= 0)
+ cygheap_fdenum cfd (true);
+ while (cfd.next () >= 0)
+ {
+ if (cfd->get_handle () != pipe_hdl)
+ continue;
+ if ((rwflags == O_RDONLY && !(cfd->get_access () & GENERIC_READ))
+ || (rwflags == O_WRONLY && !(cfd->get_access () & GENERIC_WRITE)))
{
- if (cfd->get_handle () == hdl)
- {
- if (!cfd->dup (this))
- return 1;
- return 0;
- }
+ set_errno (EACCES);
+ return 0;
}
+ if (!cfd->dup (this))
+ return 1;
+ return 0;
}
- else
- {
- /* TODO: Open pipes of different process. Is that possible? */
- }
+ set_errno (ENOENT);
+ return 0;
+ }
+
+ pinfo p (pid);
+ if (!p)
+ {
+ set_errno (ESRCH);
+ return 0;
+ }
+ if (!(proc = OpenProcess (PROCESS_DUP_HANDLE, false, p->dwProcessId)))
+ {
+ __seterrno ();
+ return 0;
}
+ if (!(fh = p->pipe_fhandler (pipe_hdl, size)) || !size)
+ {
+ set_errno (ENOENT);
+ goto out;
+ }
+ /* Too bad, but Windows only allows the same access mode when dup'ing
+ the pipe. */
+ if ((rwflags == O_RDONLY && !(fh->get_access () & GENERIC_READ))
+ || (rwflags == O_WRONLY && !(fh->get_access () & GENERIC_WRITE)))
+ {
+ set_errno (EACCES);
+ goto out;
+ }
+ inh = !(flags & O_NOINHERIT);
+ if (!DuplicateHandle (proc, pipe_hdl, hMainProc, &nio_hdl,
+ 0, inh, DUPLICATE_SAME_ACCESS))
+ {
+ __seterrno ();
+ goto out;
+ }
+ init (nio_hdl, fh->get_access (), mode & O_TEXT ?: O_BINARY);
+ if (flags & O_NOINHERIT)
+ close_on_exec (true);
+ uninterruptible_io (fh->uninterruptible_io ());
+ cfree (fh);
+ CloseHandle (proc);
+ return 1;
out:
- set_errno (ENXIO);
+ if (nio_hdl)
+ CloseHandle (nio_hdl);
+ if (fh)
+ free (fh);
+ if (proc)
+ CloseHandle (proc);
return 0;
}
return -1;
}
-void
-fhandler_pipe::set_close_on_exec (bool val)
+int
+fhandler_pipe::fadvise (_off64_t offset, _off64_t length, int advice)
{
- fhandler_base::set_close_on_exec (val);
- if (guard)
- set_no_inheritance (guard, val);
- if (writepipe_exists)
- set_no_inheritance (writepipe_exists, val);
+ set_errno (ESPIPE);
+ return -1;
}
-char *fhandler_pipe::get_proc_fd_name (char *buf)
+int
+fhandler_pipe::ftruncate (_off64_t length, bool allow_truncate)
{
- __small_sprintf (buf, "pipe:[%d]", get_handle ());
- return buf;
+ set_errno (allow_truncate ? EINVAL : ESPIPE);
+ return -1;
}
-struct pipeargs
-{
- fhandler_base *fh;
- void *ptr;
- size_t *len;
-};
-
-static DWORD WINAPI
-read_pipe (void *arg)
+char *
+fhandler_pipe::get_proc_fd_name (char *buf)
{
- pipeargs *pi = (pipeargs *) arg;
- pi->fh->fhandler_base::read (pi->ptr, *pi->len);
- return 0;
+ __small_sprintf (buf, "pipe:[%d]", get_handle ());
+ return buf;
}
void __stdcall
fhandler_pipe::read (void *in_ptr, size_t& in_len)
{
- if (broken_pipe)
- in_len = 0;
- else
- {
- pipeargs pi = {dynamic_cast<fhandler_base *>(this), in_ptr, &in_len};
- ResetEvent (read_state);
- cygthread *th = new cygthread (read_pipe, &pi, "read_pipe");
- if (th->detach (read_state) && !in_len)
- in_len = (size_t) -1; /* received a signal */
- }
- (void) ReleaseMutex (guard);
- return;
+ return read_overlapped (in_ptr, in_len);
}
int
-fhandler_pipe::close ()
+fhandler_pipe::write (const void *ptr, size_t len)
{
- if (guard)
- CloseHandle (guard);
- if (writepipe_exists)
- CloseHandle (writepipe_exists);
-#ifndef NEWVFORK
- if (read_state)
-#else
- // FIXME is this vfork_cleanup test right? Is it responsible for some of
- // the strange pipe behavior that has been reported in the cygwin mailing
- // list?
- if (read_state && !cygheap->fdtab.in_vfork_cleanup ())
-#endif
- ForceCloseHandle (read_state);
- if (get_handle ())
- {
- CloseHandle (get_handle ());
- set_io_handle (NULL);
- }
- return 0;
-}
-
-bool
-fhandler_pipe::hit_eof ()
-{
- char buf[80];
- HANDLE ev;
- if (broken_pipe)
- return 1;
- if (!orig_pid)
- return false;
- __small_sprintf (buf, pipeid_fmt, orig_pid, id);
- if ((ev = OpenEvent (EVENT_ALL_ACCESS, FALSE, buf)))
- CloseHandle (ev);
- debug_printf ("%s %p", buf, ev);
- return ev == NULL;
-}
-
-void
-fhandler_pipe::fixup_after_exec ()
-{
- if (read_state)
- {
- read_state = CreateEvent (&sec_none_nih, FALSE, FALSE, NULL);
- ProtectHandle (read_state);
- }
-}
-
-void
-fhandler_pipe::fixup_after_fork (HANDLE parent)
-{
- fhandler_base::fixup_after_fork (parent);
- if (guard)
- fork_fixup (parent, guard, "guard");
- if (writepipe_exists)
- fork_fixup (parent, writepipe_exists, "guard");
- fixup_after_exec ();
+ return write_overlapped (ptr, len);
}
int
fhandler_pipe::dup (fhandler_base *child)
{
- int res = -1;
fhandler_pipe *ftp = (fhandler_pipe *) child;
- ftp->guard = ftp->writepipe_exists = ftp->read_state = NULL;
+ ftp->set_popen_pid (0);
- if (get_handle ())
- {
- res = fhandler_base::dup (child);
- if (res)
- goto err;
- }
-
- /* FIXME: This leaks handles in the failing condition */
- if (guard == NULL)
- ftp->guard = NULL;
- else if (!DuplicateHandle (hMainProc, guard, hMainProc, &ftp->guard, 0, 1,
- DUPLICATE_SAME_ACCESS))
- {
- debug_printf ("couldn't duplicate guard %p, %E", guard);
- goto err;
- }
-
- if (writepipe_exists == NULL)
- ftp->writepipe_exists = NULL;
- else if (!DuplicateHandle (hMainProc, writepipe_exists, hMainProc,
- &ftp->writepipe_exists, 0, 1,
- DUPLICATE_SAME_ACCESS))
- {
- debug_printf ("couldn't duplicate writepipe_exists %p, %E", writepipe_exists);
- goto err;
- }
-
- if (read_state == NULL)
- ftp->read_state = NULL;
- else if (!DuplicateHandle (hMainProc, read_state, hMainProc,
- &ftp->read_state, 0, 0,
- DUPLICATE_SAME_ACCESS))
- {
- debug_printf ("couldn't duplicate read_state %p, %E", read_state);
- goto err;
- }
-
- res = 0;
- goto out;
-
-err:
- if (ftp->guard)
- CloseHandle (ftp->guard);
- if (ftp->writepipe_exists)
- CloseHandle (ftp->writepipe_exists);
- if (ftp->read_state)
- CloseHandle (ftp->read_state);
- goto leave;
-
-out:
- ftp->id = id;
- ftp->orig_pid = orig_pid;
- VerifyHandle (ftp->guard);
- VerifyHandle (ftp->writepipe_exists);
- VerifyHandle (ftp->read_state);
+ int res;
+ if (get_handle () && fhandler_base::dup (child))
+ res = -1;
+ else
+ res = 0;
-leave:
debug_printf ("res %d", res);
return res;
}
FILE_READ_ATTRIBUTES access, on later versions of win32 where
this is supported. This access is needed by NtQueryInformationFile,
which is used to implement select and nonblocking writes.
- Note that the return value is either NO_ERROR or GetLastError,
+ Note that the return value is either 0 or GetLastError,
unlike CreatePipe, which returns a bool for success or failure. */
-static int
-create_selectable_pipe (PHANDLE read_pipe_ptr,
- PHANDLE write_pipe_ptr,
- LPSECURITY_ATTRIBUTES sa_ptr,
- DWORD psize)
+int
+fhandler_pipe::create_selectable (LPSECURITY_ATTRIBUTES sa_ptr, HANDLE& r,
+ HANDLE& w, DWORD psize)
{
/* Default to error. */
- *read_pipe_ptr = *write_pipe_ptr = INVALID_HANDLE_VALUE;
-
- HANDLE read_pipe = INVALID_HANDLE_VALUE, write_pipe = INVALID_HANDLE_VALUE;
+ r = w = INVALID_HANDLE_VALUE;
/* Ensure that there is enough pipe buffer space for atomic writes. */
if (psize < PIPE_BUF)
to be as robust as possible. */
while (1)
{
- static volatile LONG pipe_unique_id;
+ static volatile ULONG pipe_unique_id;
- __small_sprintf (pipename, "\\\\.\\pipe\\cygwin-%d-%ld", myself->pid,
+ __small_sprintf (pipename, "\\\\.\\pipe\\cygwin-%p-%p", myself->pid,
InterlockedIncrement ((LONG *) &pipe_unique_id));
debug_printf ("CreateNamedPipe: name %s, size %lu", pipename, psize);
the pipe was not created earlier by some other process, even if
the pid has been reused. We avoid FILE_FLAG_FIRST_PIPE_INSTANCE
because that is only available for Win2k SP2 and WinXP. */
- SetLastError (0);
- read_pipe = CreateNamedPipe (pipename,
- PIPE_ACCESS_INBOUND,
- PIPE_TYPE_BYTE | PIPE_READMODE_BYTE,
- 1, /* max instances */
- psize, /* output buffer size */
- psize, /* input buffer size */
- NMPWAIT_USE_DEFAULT_WAIT,
- sa_ptr);
+ r = CreateNamedPipe (pipename, PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED,
+ PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 1, psize,
+ psize, NMPWAIT_USE_DEFAULT_WAIT, sa_ptr);
- DWORD err = GetLastError ();
/* Win 95 seems to return NULL instead of INVALID_HANDLE_VALUE */
- if ((read_pipe || !err) && read_pipe != INVALID_HANDLE_VALUE)
+ if (r && r != INVALID_HANDLE_VALUE)
{
- debug_printf ("pipe read handle %p", read_pipe);
+ debug_printf ("pipe read handle %p", r);
break;
}
+ DWORD err = GetLastError ();
switch (err)
{
case ERROR_PIPE_BUSY:
/* The pipe is already open with compatible parameters.
Pick a new name and retry. */
debug_printf ("pipe busy, retrying");
- continue;
+ break;
case ERROR_ACCESS_DENIED:
/* The pipe is already open with incompatible parameters.
Pick a new name and retry. */
debug_printf ("pipe access denied, retrying");
- continue;
- case ERROR_CALL_NOT_IMPLEMENTED:
- /* We are on an older Win9x platform without named pipes.
- Return an anonymous pipe as the best approximation. */
- debug_printf ("CreateNamedPipe not implemented, resorting to "
- "CreatePipe size %lu", psize);
- if (CreatePipe (read_pipe_ptr, write_pipe_ptr, sa_ptr, psize))
- {
- debug_printf ("pipe read handle %p", *read_pipe_ptr);
- debug_printf ("pipe write handle %p", *write_pipe_ptr);
- return NO_ERROR;
- }
- err = GetLastError ();
- debug_printf ("CreatePipe failed, %E");
- return err;
+ break;
default:
- debug_printf ("CreateNamedPipe failed, %E");
- return err;
+ {
+ err = GetLastError ();
+ debug_printf ("CreatePipe failed, %E");
+ return err;
+ }
}
- /* NOTREACHED */
}
debug_printf ("CreateFile: name %s", pipename);
/* Open the named pipe for writing.
Be sure to permit FILE_READ_ATTRIBUTES access. */
- write_pipe = CreateFile (pipename,
- GENERIC_WRITE | FILE_READ_ATTRIBUTES,
- 0, /* share mode */
- sa_ptr,
- OPEN_EXISTING,
- 0, /* flags and attributes */
- 0); /* handle to template file */
-
- if (write_pipe == INVALID_HANDLE_VALUE)
+ w = CreateFile (pipename, GENERIC_WRITE | FILE_READ_ATTRIBUTES, 0, sa_ptr,
+ OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0);
+
+ if (!w || w == INVALID_HANDLE_VALUE)
{
/* Failure. */
DWORD err = GetLastError ();
debug_printf ("CreateFile failed, %E");
- CloseHandle (read_pipe);
+ CloseHandle (r);
return err;
}
- debug_printf ("pipe write handle %p", write_pipe);
+ debug_printf ("pipe write handle %p", w);
/* Success. */
- *read_pipe_ptr = read_pipe;
- *write_pipe_ptr = write_pipe;
- return NO_ERROR;
+ return 0;
}
int
-fhandler_pipe::create (fhandler_pipe *fhs[2], unsigned psize, int mode, bool fifo)
+fhandler_pipe::create (fhandler_pipe *fhs[2], unsigned psize, int mode)
{
HANDLE r, w;
SECURITY_ATTRIBUTES *sa = (mode & O_NOINHERIT) ? &sec_none_nih : &sec_none;
- int res = -1;
- int ret;
+ int res;
- if ((ret = create_selectable_pipe (&r, &w, sa, psize)) != NO_ERROR)
- __seterrno_from_win_error (ret);
+ int ret = create_selectable (sa, r, w, psize);
+ if (ret)
+ {
+ __seterrno_from_win_error (ret);
+ res = -1;
+ }
else
{
fhs[0] = (fhandler_pipe *) build_fh_dev (*piper_dev);
fhs[1] = (fhandler_pipe *) build_fh_dev (*pipew_dev);
- int binmode = mode & O_TEXT ?: O_BINARY;
- fhs[0]->init (r, GENERIC_READ, binmode);
- fhs[1]->init (w, GENERIC_WRITE, binmode);
- if (mode & O_NOINHERIT)
- {
- fhs[0]->close_on_exec (true);
- fhs[1]->close_on_exec (true);
- }
-
- fhs[0]->read_state = CreateEvent (&sec_none_nih, FALSE, FALSE, NULL);
- fhs[0]->need_fork_fixup (true);
- ProtectHandle1 (fhs[0]->read_state, read_state);
-
+ mode |= mode & O_TEXT ?: O_BINARY;
+ fhs[0]->init (r, FILE_CREATE_PIPE_INSTANCE | GENERIC_READ, mode);
+ fhs[1]->init (w, FILE_CREATE_PIPE_INSTANCE | GENERIC_WRITE, mode);
res = 0;
- fhs[0]->create_guard (sa);
- if (wincap.has_unreliable_pipes ())
- {
- char buf[80];
- int count = pipecount++; /* FIXME: Should this be InterlockedIncrement? */
- __small_sprintf (buf, pipeid_fmt, myself->pid, count);
- fhs[1]->writepipe_exists = CreateEvent (sa, TRUE, FALSE, buf);
- fhs[0]->orig_pid = myself->pid;
- fhs[0]->id = count;
- }
}
syscall_printf ("%d = pipe ([%p, %p], %d, %p)", res, fhs[0], fhs[1], psize, mode);
return 0;
}
-#define DEFAULT_PIPEBUFSIZE (4 * PIPE_BUF)
+int __stdcall
+fhandler_pipe::fstatvfs (struct statvfs *sfs)
+{
+ set_errno (EBADF);
+ return -1;
+}
+
+#define DEFAULT_PIPEBUFSIZE (16 * PIPE_BUF)
extern "C" int
pipe (int filedes[2])