6 use Fcntl qw(F_GETFL F_SETFL O_NONBLOCK);
10 our $env_name = 'PREFORK_INTERP';
17 syslog(LOG_INFO, "$0 prefork [$$]: $m, quitting");
21 sub become_monitor () {
25 sub initialisation_complete {
28 # if env var not set, we're not running under prefork-interp
29 my @env_params = split /,/, ($ENV{$env_name} // return), 5;
30 croak "$env_name has too few entries" unless @env_params == 5;
32 $num_servers = $opts{max_servers} // 4;
34 #---- setup (pm) [1] ----
36 $socket_path = pop @env_params;
37 foreach (@env_params) {
38 $_ eq ($_+0) or croak "$env_name contains $_, not a number";
40 open LISTEN, "<>&=$env_params[0]" or croak "listen fd: $!";
41 open CALL, "<>&=$env_params[1]" or croak "listen fd: $!";
42 @call_fds = ($env_params[2], $env_params[3], 2);
44 if (!$opts{no_openlog}) {
45 openlog("prefork-interp $0", 'ndelay,nofatal,pid',
46 $opts{log_facility} // 'log_user');
49 open NULL, "<>/dev/null" or croak "open /dev/null: $!";
51 #---- fork for server ----
53 my $child = fork // croak "first fork failed: $!";
55 #---- setup (pm) [2], exits ----
59 #---- server(pm) [1] ----
61 $child = fork // croak "second fork failed: $!";
63 # we are the child, i.e. the one fa-monitor
68 $children{$child} = 1;
70 # --- server(pm) [2] ----
72 setsid() > 0 or fail("setsid: $!");
73 open STDIN, "<&NULL" or fail("dup null onto stdin: $!");
74 open STDOUT, ">&NULL" or fail("dup null onto stdout: $!");
75 open STDERR, ">&NULL" or fail("dup null onto stderr: $!");
78 POSIX::close($_) foreach @call_fds;
81 my $flags = fcntl(LISTEN, F_GETFL, 0) or fail("F_GETFL listen socket: $!");
83 fcntl(LISTEN, F_SETFL, $flags) or fail("F_SETFL listen socket: $!");
90 my $full = %children >= $num_servers;
91 my $got = waitpid -1, ($full ? 0 : WNOHANG);
94 "$0 prefork [$$]: monitor process [$got] failed with wait status $?");
96 if (!exists $children{$got}) {
98 "$0 prefork [$$]: monitor process [$got] wasn't one of ours?!");
100 delete $children{$got};
104 # select for accepting or housekeeping timeout
106 vec($rbits, fileno(LISTEN), 1) = 1;
108 my $nfound = select($rbits, '', $ebits, ($opts{check_interval} // 300));
111 if (accept(CALL, LISTEN)) {
112 $child = fork // fail("fork for accepted call failed: $!");
118 $children{$child} = 1;
119 } elsif ($! == EINTR || $! == EAGAIN || $! == EWOULDBLOCK) {
121 syslog(LOG_WARNING, "$0 prefork [$$]: accept failed: $!");
122 if ($errcount > ($opts{max_errors} // 100)) {
124 "$0 prefork [$$]: too many accept failures, quitting");
132 my @st_listen = stat(LISTEN) // fail("fstat listening socket: $!");
133 my @st_socket = stat($socket_path) // do {
134 if ($! == ENOENT) { server_quit("socket $socket is ENOENT"); }
135 fail("stat socket $socket: $!");
137 if ("@st_listen[0..2]" ne "@st_socket[0..2]") {
138 server_quit("socket $socket is no longer ours");