1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
| | #!perl -w
# Copyright (C) unicorn hackers <unicorn-public@yhbt.net>
# License: GPL-3.0+ <https://www.gnu.org/licenses/gpl-3.0.txt>
use v5.14; BEGIN { require './t/lib.perl' };
use IO::Socket::UNIX;
use autodie;
no autodie 'kill';
my %to_kill;
END { kill('TERM', values(%to_kill)) if keys %to_kill }
my $u1 = "$tmpdir/u1.sock";
my $u2 = "$tmpdir/u2.sock";
my $unix_req = sub {
my $s = IO::Socket::UNIX->new(Peer => shift, Type => SOCK_STREAM);
print $s @_, "\r\n\r\n";
$s;
};
{
open my $fh, '>', "$tmpdir/u1.conf.rb";
print $fh <<EOM;
pid "$tmpdir/u.pid"
listen "$u1"
stderr_path "$tmpdir/err1.log"
EOM
close $fh;
open $fh, '>', "$tmpdir/u2.conf.rb";
print $fh <<EOM;
pid "$tmpdir/u.pid"
listen "$u2"
stderr_path "$tmpdir/err2.log"
EOM
close $fh;
open $fh, '>', "$tmpdir/u3.conf.rb";
print $fh <<EOM;
pid "$tmpdir/u3.pid"
listen "$u1"
stderr_path "$tmpdir/err3.log"
EOM
close $fh;
}
my @uarg = qw(-D -E none t/integration.ru);
# this pipe will be used to notify us when all daemons die:
pipe(my $p0, my $p1);
fcntl($p1, POSIX::F_SETFD, 0);
# start the first instance
unicorn('-c', "$tmpdir/u1.conf.rb", @uarg)->join;
is($?, 0, 'daemonized 1st process');
chomp($to_kill{u1} = slurp("$tmpdir/u.pid"));
like($to_kill{u1}, qr/\A\d+\z/s, 'read pid file');
chomp(my $worker_pid = readline($unix_req->($u1, 'GET /pid')));
like($worker_pid, qr/\A\d+\z/s, 'captured worker pid');
ok(kill(0, $worker_pid), 'worker is kill-able');
# 2nd process conflicts on PID
unicorn('-c', "$tmpdir/u2.conf.rb", @uarg)->join;
isnt($?, 0, 'conflicting PID file fails to start');
chomp(my $pidf = slurp("$tmpdir/u.pid"));
is($pidf, $to_kill{u1}, 'pid file contents unchanged after start failure');
chomp(my $pid2 = readline($unix_req->($u1, 'GET /pid')));
is($worker_pid, $pid2, 'worker PID unchanged');
# 3rd process conflicts on socket
unicorn('-c', "$tmpdir/u3.conf.rb", @uarg)->join;
isnt($?, 0, 'conflicting UNIX socket fails to start');
chomp($pid2 = readline($unix_req->($u1, 'GET /pid')));
is($worker_pid, $pid2, 'worker PID still unchanged');
chomp($pidf = slurp("$tmpdir/u.pid"));
is($pidf, $to_kill{u1}, 'pid file contents unchanged after 2nd start failure');
{ # teardown initial process via SIGKILL
ok(kill('KILL', delete $to_kill{u1}), 'SIGKILL initial daemon');
close $p1;
vec(my $rvec = '', fileno($p0), 1) = 1;
is(select($rvec, undef, undef, 5), 1, 'timeout for pipe HUP');
is(my $undef = <$p0>, undef, 'process closed pipe writer at exit');
ok(-f "$tmpdir/u.pid", 'pid file stayed after SIGKILL');
ok(-S $u1, 'socket stayed after SIGKILL');
is(IO::Socket::UNIX->new(Peer => $u1, Type => SOCK_STREAM), undef,
'fail to connect to u1');
ok(!kill(0, $worker_pid), 'worker gone after parent dies');
}
# restart the first instance
{
pipe($p0, $p1);
fcntl($p1, POSIX::F_SETFD, 0);
unicorn('-c', "$tmpdir/u1.conf.rb", @uarg)->join;
is($?, 0, 'daemonized 1st process');
chomp($to_kill{u1} = slurp("$tmpdir/u.pid"));
like($to_kill{u1}, qr/\A\d+\z/s, 'read pid file');
chomp($pid2 = readline($unix_req->($u1, 'GET /pid')));
like($pid2, qr/\A\d+\z/, 'worker running');
ok(kill('TERM', delete $to_kill{u1}), 'SIGTERM restarted daemon');
close $p1;
vec(my $rvec = '', fileno($p0), 1) = 1;
is(select($rvec, undef, undef, 5), 1, 'timeout for pipe HUP');
is(my $undef = <$p0>, undef, 'process closed pipe writer at exit');
ok(!-f "$tmpdir/u.pid", 'pid file gone after SIGTERM');
ok(-S $u1, 'socket stays after SIGTERM');
}
my @log = slurp("$tmpdir/err.log");
diag("@log") if $ENV{V};
done_testing;
|