use
5.20.0;
our
$VERSION
=
'3.20210514'
;
use
Socket
qw(AF_INET AF_UNIX SOCK_DGRAM SOCK_STREAM)
;
sub
run_child {
my
$self
=
shift
;
my
$config
=
$self
->{config} || get_config();
return
$self
->SUPER
unless
$config
->{
'patch_net_server'
};
my
$prop
=
$self
->{
'server'
};
$SIG
{
'INT'
} =
$SIG
{
'TERM'
} =
$SIG
{
'QUIT'
} =
sub
{
$self
->child_finish_hook;
exit
;
};
$SIG
{
'PIPE'
} =
'IGNORE'
;
$SIG
{
'CHLD'
} =
'DEFAULT'
;
$SIG
{
'HUP'
} =
sub
{
if
(!
$prop
->{
'connected'
}) {
$self
->child_finish_hook;
exit
;
}
$prop
->{
'SigHUPed'
} = 1;
};
if
(
$prop
->{
'serialize'
} eq
'flock'
) {
open
$prop
->{
'lock_fh'
},
">"
,
$prop
->{
'lock_file'
}
or
$self
->fatal(
"Couldn't open lock file \"$prop->{'lock_file'}\"[$!]"
);
pipe
(
$prop
->{
'SigHUPReadPipe'
},
$prop
->{
'SigHUPWritePipe'
});
$prop
->{
'select'
}->add(
$prop
->{
'SigHUPReadPipe'
});
$SIG
{
'HUP'
} =
sub
{
$prop
->{
'SigHUPed'
} = 1;
syswrite
$prop
->{
'SigHUPWritePipe'
},
"1"
if
!
$prop
->{SigHUPWriten}++; };
}
$self
->
log
(4,
"Child Preforked ($$)"
);
delete
@{
$prop
}{
qw(children tally last_start last_process)
};
$self
->child_init_hook;
my
$write
=
$prop
->{
'_WRITE'
};
while
(
$self
->
accept
()) {
$prop
->{
'connected'
} = 1;
print
$write
"$$ processing\n"
;
my
$ok
=
eval
{
$self
->run_client_connection; 1 };
if
(!
$ok
) {
print
$write
"$$ exiting\n"
;
die
$@;
}
last
if
$self
->done;
$prop
->{
'connected'
} = 0;
print
$write
"$$ waiting\n"
;
}
$self
->child_finish_hook;
print
$write
"$$ exiting\n"
;
exit
;
}
sub
accept
{
my
$self
=
shift
;
my
$config
=
$self
->{config} || get_config();
return
$self
->SUPER
unless
$config
->{
'patch_net_server'
};
my
$prop
=
$self
->{
'server'
};
if
(
$prop
->{
'serialize'
} eq
'flock'
) {
while
(!
flock
$prop
->{
'lock_fh'
}, Fcntl::LOCK_EX()) {
return
undef
if
$prop
->{
'SigHUPed'
};
next
if
$! == EINTR;
$self
->fatal(
"Couldn't get lock on file \"$prop->{'lock_file'}\" [$!]"
);
}
my
$v
=
$self
->super_accept();
flock
$prop
->{
'lock_fh'
}, Fcntl::LOCK_UN();
return
$v
;
}
elsif
(
$prop
->{
'serialize'
} eq
'semaphore'
) {
$prop
->{
'sem'
}->op(0, -1, IPC::SysV::SEM_UNDO()) or
$self
->fatal(
"Semaphore Error [$!]"
);
my
$v
=
$self
->super_accept();
$prop
->{
'sem'
}->op(0, 1, IPC::SysV::SEM_UNDO()) or
$self
->fatal(
"Semaphore Error [$!]"
);
return
$v
;
}
elsif
(
$prop
->{
'serialize'
} eq
'pipe'
) {
my
$waiting
=
$prop
->{
'_WAITING'
};
scalar
<
$waiting
>;
my
$v
=
$self
->super_accept();
print
{
$prop
->{
'_READY'
} }
"Next!\n"
;
return
$v
;
}
else
{
my
$v
=
$self
->super_accept();
return
$v
;
}
}
sub
super_accept {
my
$self
=
shift
;
my
$prop
=
$self
->{
'server'
};
my
$sock
=
undef
;
my
$retries
= 30;
while
(
$retries
--) {
if
(
$prop
->{
'multi_port'
}) {
return
0
if
$prop
->{
'_HUP'
};
(
$sock
,
my
$hup
) =
$self
->accept_multi_port;
return
0
if
$hup
||
$prop
->{
'_HUP'
};
if
(
$self
->can_read_hook(
$sock
)) {
$retries
++;
next
;
}
}
else
{
$sock
=
$prop
->{
'sock'
}->[0];
}
$self
->fatal(
"Received a bad sock!"
)
if
!
defined
$sock
;
if
(SOCK_DGRAM ==
$sock
->
getsockopt
(Socket::SOL_SOCKET(), Socket::SO_TYPE())) {
$prop
->{
'client'
} =
$sock
;
$prop
->{
'udp_true'
} = 1;
$prop
->{
'udp_peer'
} =
$sock
->
recv
(
$prop
->{
'udp_data'
},
$sock
->NS_recv_len,
$sock
->NS_recv_flags);
}
else
{
delete
$prop
->{
'udp_true'
};
$prop
->{
'client'
} =
$sock
->
accept
();
}
return
0
if
$prop
->{
'_HUP'
};
return
1
if
$prop
->{
'client'
};
$self
->
log
(2,
"Accept failed with $retries tries left: $!"
);
sleep
(1);
}
$self
->
log
(1,
"Ran out of accept retries!"
);
return
undef
;
}
sub
accept_multi_port {
my
$self
=
shift
;
my
$config
=
$self
->{config} || get_config();
return
$self
->SUPER
unless
$config
->{
'patch_net_server'
};
my
$prop
=
$self
->{
'server'
};
while
(1) {
my
@waiting
=
$prop
->{
'select'
}->can_read();
next
if
!
@waiting
&& $! == EINTR;
return
(
undef
, 1)
if
grep
{
fileno
(
$_
) ==
fileno
(
$prop
->{
'SigHUPReadPipe'
}) }
@waiting
;
return
(
undef
, 0)
if
!
@waiting
;
return
(
$waiting
[
rand
@waiting
], 0);
}
}
1;