Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Perl - passing an open socket across fork/exec

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?

like image 556
Chap Avatar asked Jan 16 '13 04:01

Chap


1 Answers

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.

like image 178
melpomene Avatar answered Sep 23 '22 20:09

melpomene