This is the mail archive of the cygwin mailing list for the Cygwin project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: AW: 1.5.25-7 piping directed output to /dev/stdout will not work


-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

According to Corinna Vinschen on 2/16/2008 2:52 AM:
| /dev/stdout is nothing special for Cygwin.  /dev/stdout is just a
| symlink pointing to /proc/self/fd/1.  /proc/self is just a symlink to
| /proc/<pid>.

This part's just fine (same as on Linux).

|  /proc/<pid>/fd/1 is just a symlink to the file opened
| in this process using fd 1.

I think that's where the difference lies.  On Linux,
open("/proc/<pid>/fd/1",O_WRONLY) behaves like dup(1) - now both fd 1 and
fd 3 share the same handle.  At which point, calling dup2(3,1) closes one
of the copies, but fd 3 is not invalidated, so the handle remains alive.
But on cygwin, the open creates a new handle via a symbolic name (or fails
outright on some inherited handles); if the underlying handle was a
regular file, this is virtually the same, but if the underlying handle was
special, such as a pipe, it is acting up.  Consider running the attached
file, which uses dup(1) if there were no arguments, and
open("/dev/stdout",O_WRONLY) if there are:

$ gcc -o foo foo.c
$ ./foo
hi
$ ./foo 1
hi
$ # ok, so the current pty is correctly duplicated
$ rm -f f && ./foo > f && cat f
hi
$ rm -f f && ./foo 1 > f && cat f
hi
$ # ok, so a disk file is correctly duplicated
$ ./foo | cat
hi
$ ./foo 1 | cat
failed to write to new stdout, 9
$ # hmm, duplicating the pipe had problems
$ rm -f f && strace -o f ./foo
hi
$ rm -f f && strace -o f ./foo 1
failed to write to new stdout, 9
$ # hmm, strace was unable to duplicate the pty, which kind
$ # of makes sense, since strace is a pure windows program

Snippets of that strace include:

~   42   27150 [main] foo 3448 fhandler_process::exists: exists
(/proc/3448/fd/pipe:[1504])
...
~   51   27276 [main] foo 3448 fhandler_base::dup: in fhandler_base dup
...
~   30   27402 [main] foo 3448 open: 3 = open (/dev/stdout, 0x1)
~   31   27433 [main] foo 3448 dtable::dup2: dup2 (3, 1)
...
~   29   27642 [main] foo 3448 fhandler_pipe::dup: res 0
~   30   27672 [main] foo 3448 dtable::dup_worker: duped
'/proc/3448/fd/pipe:[1504]' old 0x6F4, new 0x6F0
...
~   34   27770 [main] foo 3448 fhandler_base::close: closing '' handle 0x5E0
~   33   27803 [main] foo 3448 close: 0 = close (1)
~   30   27833 [main] foo 3448 dtable::dup2: 1 = dup2 (3, 1)
...
~   56   28343 [main] foo 3448 fhandler_base::write: binary write
~   58   28401 [main] foo 3448 sig_send: sendsig 0x708, pid 3448, signal
- -34, its_me 1
~  613   29014 [main] foo 3448 sig_send: wakeup 0x5E0

The symlink was correctly resolved, and discovered the pipe, but fell back
to fhandler_base::dup to do the duplication.  At any rate, the open
succeeded, but fd 3 is now tied to a symbolic name
(/proc/3448/fd/pipe:[1504] stands for handle 0x5e0).  Then, in the dup2,
handle 0x5e0 from fd 1 is closed before overlaying fd 3 onto fd 1.
Finally, the write tries to access the pipe via fd 1, and sends a wakeup
to the original handle 0x5e0, based on the symbolic name associated with
fd 3 (now fd 1) - oops; that was the handle that dup already closed.


Also interesting is:


$ rm -f f f1 && strace -o f1 ./foo > f && cat f
hi
$ rm -f f f1 && strace -o f1 ./foo 1 > f && cat f
failed to open /dev/stdout, 2
failed to write to new stdout, 9
hi

Here, the strace inherits an open handle to f, but does not know which
file it came from, so it gives up with ENOENT rather than successfully
creating fd 3.  But since strace is obviously able to dup open handles to
unknown files, it would seem that open(/proc/self/fd/1) to an unknown file
handle should work as well.

| It's not even clear anymore, that this file is an open file in *this*
| very process.
|
| How is Cygwin supposed to know that it is a symlink with a special
| meaning?  Do you think that Cygwin should test every file on every open
| to be "/dev/stdout"?

Not /dev/stdout, per se, but /proc/<pid>/fd/n.  But since /proc is already
a special device (via fhandler_proc), it seems like such special handling
should already be happening.  Maybe it's just a matter of writing
fhandler_proc::dup, or making fhandler_proc::open go through the dup
machinery.

- --
Don't work too hard, make some time for fun as well!

Eric Blake             ebb9@byu.net
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.5 (Cygwin)
Comment: Public key at home.comcast.net/~ericblake/eblake.gpg
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org

iD8DBQFHtuKz84KuGfSFAYARAuiBAJ9OvbQkE8eTElLBMqFVbZ1VCijgxwCfWLOi
rlUWQX5B+WxYXogMj2JqnD4=
=nbpE
-----END PGP SIGNATURE-----
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>

int
main (int argc, char* argv[])
{
  int f;
  if (argc > 1)
    {
      f = open ("/dev/stdout", O_WRONLY);
      if (f < 0)
	fprintf (stderr, "failed to open /dev/stdout, %d\n", errno);
    }
  else
    {
      f = dup (1);
      if (f < 0)
	fprintf (stderr, "failed to dup stdout, %d\n", errno);
    }
  f = dup2 (f, 1);
  if (f < 0)
    fprintf (stderr, "failed to dup2 back to stdout, %d\n", errno);
  f = write (1, "hi\n", 3);
  if (f < 3)
    fprintf (stderr, "failed to write to new stdout, %d\n", errno);
  return 0;
}

--
Unsubscribe info:      http://cygwin.com/ml/#unsubscribe-simple
Problem reports:       http://cygwin.com/problems.html
Documentation:         http://cygwin.com/docs.html
FAQ:                   http://cygwin.com/faq/

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]