Beefy Boxes and Bandwidth Generously Provided by pair Networks
Do you know where your variables are?

Re^2: Win32API::File not setting HANDLE_FLAG_PROTECT_FROM_CLOSE (sources)

by Yary (Pilgrim)
on Dec 01, 2012 at 01:23 UTC ( #1006550=note: print w/replies, xml ) Need Help??

in reply to Re: Win32API::File not setting HANDLE_FLAG_PROTECT_FROM_CLOSE (sources)
in thread Win32API::File not setting HANDLE_FLAG_PROTECT_FROM_CLOSE

I'm even curious to know why a flag like _PROTECT_FROM_CLOSE was created in the first place. :)
Sometimes two processes need to communicate via file descriptors and std* is not enough- Unix example I know of is UCSPI (ooks-pie) UNIX Client-Server Program Interface which qmail uses. In a case like that, you need to keep non-STD* filehandles from closing when you exec a new process, otherwise the child tries using FD 3 (or FDs 6 and 7, etc) and finds it is closed.

Unix has no problem with this, it uses fcntl($fh, F_SETFD, ~FD_CLOEXEC) and all is well. But those POSIX macros are not implemented in Win32-land, at least not in ActiveState's Perl. Hence my desire to try _PROTECT_FROM_CLOSE on FD 3.

I was all over the web looking for insight, tried other tricks like setting $^F=3 (never even glanced at that perlvar before) but no luck. The best I found was a terse comment that said "Can't be done. OS limitation." which gave me the courage to give up!

Thanks for looking into it, and confirming that I'm not alone.

Here is my original goal, which seems to be elusive to many on the web: have a program write to stdout, stderr, and some 3rd stream. In Unix it would be invoked like "MyProg >stdout.log 2>stderr.log 3>extra_data.log". Windows cmd.exe shell allows FD's 3-9 in redirects but it doesn't pass them to any programs it spawns, they seem to be just for dup'ing 0,1,2 on the command line.

My idea was to write a wrapper in perl that would allow communicating with a sub-program using those higher-number, user-defined file descriptors in Win32.

  • Comment on Re^2: Win32API::File not setting HANDLE_FLAG_PROTECT_FROM_CLOSE (sources)

Replies are listed 'Best First'.
Re^3: Win32API::File not setting HANDLE_FLAG_PROTECT_FROM_CLOSE (sources)
by tye (Sage) on Dec 01, 2012 at 01:35 UTC

    Ah, thanks. _PROTECT_FROM_CLOSE does a lot more than that ("If this flag is set, calling the CloseHandle function will not close the object handle"). But if 'exec' doesn't have its own flag for "don't close upon exec", I could see implementing it this way (and can see some problems that such a choice presents).

    I was conflating the "don't close when creating a subprocess" with the "don't close when exec()ing cases". But then, Windows doesn't have fork() nor exec() so there really is only the "don't close when creating a subprocess", which is what _INHERIT is for. (Perl's exec on (non-Cygwin) Windows actually creates a subprocess and then has the parent process exit.)

    Perhaps you should be setting _INHERIT instead?

    Also, your code doesn't appear useful for the use-case you proposed. You don't want some "Console" handle for talking over some protocol, surely. So write some code that creates a pipe or uses a socket and then try to give that file descriptor to the child (probably using _INHERIT not _PROTECT_FROM_CLOSE, I would bet) and see how that works.

    Indeed, the point of including SetHandleInformation() in Win32API::File was exactly for the purpose of letting other programs use handles that your Perl script opened. I think how I've done that successfully in the past was via _INHERIT, but, to be honest, I'm not completely sure at the moment.

    Please let us know how it works out.

    - tye        

      Hi & thanks for persisting. I'm going to post the working UNIX version of the entire program chain, with its debugging statements, for reference.
      #!/usr/bin/env perl use warnings; use strict; use Fcntl qw(F_GETFD F_SETFD FD_CLOEXEC); open my $fh,">&2"; unless (@ARGV && $ARGV[0] eq 'noset') { fcntl($fh, F_SETFD, ~FD_CLOEXEC) or die "Can't set flags: $!\n"; print "Set no close on exec\n"; } $ENV{myfd}=fileno($fh); system ''; print $fh "This is to fd $ENV{myfd} from the parent\n";

      #!/usr/bin/env perl use warnings; use strict; open OUT,">&=$ENV{myfd}" or die "cannot dup $ENV{myfd}: $!\n"; print "Child opened FD $ENV{myfd}\n"; my $p=print OUT "***\nHere is data going to fd $ENV{myfd}\n***\n"; warn "return value of print=$p ($!)\n";
      I'm copying the child's 3rd FD to STDERR- in real life I will want to move the child's 3rd FH to STDIN, and the child's stdin & stderr to filehandles the parent can read from, but I don't want to get bogged down in more details here.

      The above is what got me wanting to set _PROTECT_FROM_CLOSE. If you run ./ noset the pair of programs fail, you have to protect FD 3 from going away when the child exec's into a new process.

      Windows doesn't really fork, it emulates it with a thread running a copy of the interpreter, so I can believe it won't need _PROTECT_FROM_CLOSE. On the other hand, _INHERIT is already set, hence the flags was=1 debug output, so that isn't the issue.

      Here's the pair of programs modified for Windows. The outer program is most of the code already posted, with even more debug info.

      #!perl -w use strict; use Fcntl qw(F_GETFD F_SETFD FD_CLOEXEC O_TEXT); use Win32API::File qw(:Func :HANDLE_FLAG_); use Win32::Process; open my $fh,">&2"; $^F = 3; my $os_handle=GetOsFHandle($fh) or die "Can't GetOSFHandle: $^E,\nstop +ped"; #$ENV{myfd}=my $ivFD = OsFHandleOpenFd($os_handle,O_TEXT); GetHandleInformation($os_handle, my $flags); print "HANDLE_FLAG_INHERIT=",HANDLE_FLAG_INHERIT,"\n"; print "HANDLE_FLAG_PROTECT_FROM_CLOSE=",HANDLE_FLAG_PROTECT_FROM_CLOSE +,"\n"; print "flags was=$flags\n"; $flags |= HANDLE_FLAG_INHERIT|HANDLE_FLAG_PROTECT_FROM_CLOSE; print "Trying for flags=$flags\n"; SetHandleInformation($os_handle, 0x2, 0x2) or die "Can't SetHandleInfo +rmation: $^E,\nstopped"; GetHandleInformation($os_handle, my $newflags); print "flags now=$newflags\n"; $ENV{myfd}=fileno($fh); my $proc; #Win32::Process::Create($proc, $ENV{COMSPEC},"/c",1,0, " +." ) # or die "Can't run: $^E\n"; system ''; print $fh "This is to fd $ENV{myfd} from the parent\n";

      open OUT,">&=$ENV{myfd}" or die "cannot dup $ENV{myfd}: $!\n"; print "Opened FD $ENV{myfd}\n"; $x=print OUT "***\nHere is data to FD $ENV{myfd}\n***\n"; warn "print=$x ($!)\n";

        The flags are on the OsFHandle and yet you are trying to share the Unixish 'fd'. Try $ENV{myosh} = $os_handle; instead and then open an fd and fh for that in the spawned process (via OsFHandleOpen).

        That's how I've done this successfully before.

        - tye        

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: note [id://1006550]
[Corion]: A good morning and weekstart to you too, Discipulus!

How do I use this? | Other CB clients
Other Users?
Others romping around the Monastery: (5)
As of 2018-06-25 07:16 GMT
Find Nodes?
    Voting Booth?
    Should cpanminus be part of the standard Perl release?

    Results (126 votes). Check out past polls.