2013-01-16 7 views
9

chcę mieć demona Perl wysłuchać dla i zaakceptować przychodzące połączenie od klienta, a następnie widelec & Exec inny program Perl, aby kontynuować rozmowa z klientem.Perl - przekazując otwarte gniazdo całej widelcem/exec

Mogę to zrobić dobrze, gdy po prostu rozwidlenie - gdzie kod demona zawiera również kod podrzędny. Ale nie widzę sposobu, w jaki otwarte gniazdo może zostać "przekazane" przez exec() do innego programu Perla.

Jakoś odniosłem wrażenie, że było to łatwe w Uniksie (co jest moim środowiskiem), a zatem również w Perlu. Czy to rzeczywiście możliwe?

Odpowiedz

14

Można to zrobić w około trzy etapy:

  1. Opróżnij close-on-exec flagą na deskryptorze pliku.
  2. Powiedz programowi exec'd, który deskryptor pliku ma użyć.
  3. Przywróć deskryptor pliku do uchwytu.

1. Perl (domyślnie) ustawia flagę close-on-exec deskryptorów otwiera. Oznacza to, że deskryptory plików nie będą zachowane w postaci exec. Trzeba usunąć tę flagę pierwszy:

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. Teraz, gdy deskryptor pliku pozostanie otwarte w poprzek exec, trzeba powiedzieć, że program, który jest deskryptor:

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

3 . Odzyskaj uchwytu pliku z drugiej strony:

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 

teraz masz Per Uchwyt na poziomie L ponownie w $fh.

Uzupełnienie: Jak Ikegami wspomniano w komentarzu, można również upewnić się, że gniazdo jest za pomocą jednego z trzech "standardowych" deskryptorów (0 (stdin), 1 (stdout), 2 (stderr)), który są 1. domyślnie otwarte dla wszystkich execów, 2. mają znane liczby, więc nie trzeba niczego przekazywać, a 3. perl automatycznie tworzy odpowiednie dla nich uchwyty.

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

that_program Teraz można po prostu użyć STDIN. Działa to nawet dla wyjścia; nie ma nieodłącznych ograniczeń dla deskryptorów plików 0, 1, 2, że są one przeznaczone tylko do wejścia lub wyjścia. To tylko konwencja, którą podążają wszystkie programy unix.

+0

Mam zamiar wypróbować twoją procedurę, ale mam pytanie dotyczące używania STDIN: czy może być związany z dwukierunkowym połączeniem, takim jak gniazdo? Zwykle uważam, że fds 0-2 jest pół-magiczny (wyspecjalizowany w jakiś sposób w terminalowym we/wy). Czy są one naprawdę wyjątkowe, poza tymi, o których wspomniałeś w swoim addendum? – Chap

+0

Problem, który mam z twoim rozwiązaniem, ma związek z otwarciem FD dla wejścia i wyjścia. 'otwórz moje $ fh, <& = ', $ fd;' po prostu otwiera je dla danych wejściowych.Próbowałem to zrobić z 'open $ fh,>> & = ', $ fd;', ale pojawia się błąd: Zły deskryptor pliku. (Poprzedni otwiera dla wejścia działa.) Nie mogę znaleźć żadnych informacji o tym, jak otworzyć dla wejścia i wyjścia. – Chap

+1

Kolejna obserwacja: otwarta (dla dwukierunkowego wejścia/wyjścia) powinna mieć postać 'otwórz $ fh, '+> & =', $ fd;'. Musiałem również wydać '$ fh-> autoflush (1)'. – Chap