11

I want to have a Perl daemon listen for and accept an incoming connection from a client, and then fork & exec another Perl program to continue the conversation with the client.

I can do this fine when simply forking - where the daemon code also contains the child's code. But I don't see how the open socket can be "passed" across an exec() to another Perl program.

Somehow I got the impression that this was easy in Unix (which is my environment) and therefore in Perl, too. Can it actually be done?

0

1 Answer 1

16

This can be done in approximately three steps:

  1. Clear the close-on-exec flag on the file descriptor.
  2. Tell the exec'd program which file descriptor to use.
  3. Restore the file descriptor into a handle.

1. Perl (by default) sets the close-on-exec flag on file descriptors it opens. This means file descriptors won't be preserved across an exec. You have to clear this flag first:

use Fcntl;

my $flags = fcntl $fh, F_GETFD, 0 or die "fcntl F_GETFD: $!";
fcntl $fh, F_SETFD, $flags & ~FD_CLOEXEC or die "fcntl F_SETFD: $!";

2. Now that the file descriptor will stay open across exec, you need to tell the program which descriptor it is:

my $fd = fileno $fh;
exec 'that_program', $fd;  # pass it on the command line
# (you could also pass it via %ENV or whatever)

3. Recover the filehandle on the other side:

my $fd = $ARGV[0];  # or however you passed it
open my $fh, '+<&=', $fd;  # fdopen
$fh->autoflush(1);  # because "normal" sockets have that enabled by default

Now you have a Perl-level handle in $fh again.

Addendum: As ikegami mentioned in a comment, you can also make sure the socket is using one of the three "standard" file descriptors (0 (stdin), 1 (stdout), 2 (stderr)) which are 1. left open by default across execs, 2. have known numbers so no need to pass anything, and 3. perl will create corresponding handles for them automatically.

open STDIN, '+<&', $fh;  # now STDIN refers to the socket
exec 'that_program';

Now that_program can simply use STDIN. This works even for output; there is no inherent restriction on file descriptors 0, 1, 2 that they be for input or output only. It's just a convention that all unix programs follow.

4
  • I'm about to try out your procedure, but I have a question about using STDIN: can it be bound to a bi-directional connection such as a socket? I tend to think of fds 0-2 as semi-magical (specialized in some way for terminal I/O). Are they really special other than in the ways you mentioned in your addendum?
    – Chap
    Commented Jan 16, 2013 at 15:03
  • The problem I'm having with your solution has to do with opening the FD for both input and output. open my $fh, '<&=', $fd; just opens it for input. I tried following this with open $fh, '>&=', $fd; but it get the error: Bad file descriptor. (The preceding open for input works.) I can't find any info about how to open for both input and output.
    – Chap
    Commented Jan 16, 2013 at 21:21
  • 1
    Another followup: the open (for bidirectional i/o) should have been open $fh, '+>&=', $fd;. Also, I needed to issue $fh->autoflush(1).
    – Chap
    Commented Jan 16, 2013 at 22:37
  • Correction to 'open' flags: open $fh, '+<&=', $fd;
    – Chap
    Commented Jan 16, 2013 at 23:35

Not the answer you're looking for? Browse other questions tagged or ask your own question.