]>
Commit | Line | Data |
---|---|---|
dbe94472 DM |
1 | package PMG::Service::pmgtunnel; |
2 | ||
3 | use strict; | |
4 | use warnings; | |
5 | use Data::Dumper; | |
6 | use Time::HiRes qw (gettimeofday); | |
7 | ||
8 | use PVE::SafeSyslog; | |
9 | use PVE::Tools qw(extract_param); | |
10 | use PVE::INotify; | |
11 | use PVE::Daemon; | |
12 | ||
13 | use PMG::RESTEnvironment; | |
14 | use PMG::DBTools; | |
15 | use PMG::RuleDB; | |
16 | use PMG::Cluster; | |
17 | use PMG::ClusterConfig; | |
18 | use PMG::Statistic; | |
19 | ||
20 | use base qw(PVE::Daemon); | |
21 | ||
22 | my $cmdline = [$0, @ARGV]; | |
23 | ||
24 | my %daemon_options = (restart_on_error => 5, stop_wait_time => 5); | |
25 | ||
26 | my $daemon = __PACKAGE__->new('pmgtunnel', $cmdline, %daemon_options); | |
27 | ||
28 | my $restart_request = 0; | |
29 | my $next_update = 0; | |
30 | ||
31 | my $cycle = 0; | |
32 | my $updatetime = 10; | |
33 | ||
34 | my $workers = {}; | |
35 | my $delayed_exec = {}; | |
36 | my $startcount = {}; | |
37 | ||
83e9f427 | 38 | my $socketdir = "/run/pmgtunnel"; |
155b0da9 DM |
39 | |
40 | my $socketfile = sub { | |
41 | my ($cid) = @_; | |
42 | return "$socketdir/.s.PGSQL.$cid"; | |
43 | }; | |
44 | ||
dbe94472 DM |
45 | sub finish_children { |
46 | while ((my $cpid = waitpid(-1, POSIX::WNOHANG())) > 0) { | |
47 | if (defined($workers->{$cpid})) { | |
48 | my $ip = $workers->{$cpid}->{ip}; | |
49 | my $cid = $workers->{$cpid}->{cid}; | |
50 | syslog('err', "tunnel finished $cpid $ip"); | |
a9fa6ae0 | 51 | unlink $socketfile->($cid); |
dbe94472 DM |
52 | $delayed_exec->{$cid} = time + ($startcount->{$cid} > 5 ? 60 : 10); |
53 | delete $workers->{$cpid}; | |
54 | } | |
55 | } | |
56 | } | |
57 | ||
58 | sub start_tunnels { | |
59 | my ($self, $cinfo) = @_; | |
60 | ||
61 | my $role = $cinfo->{local}->{type} // '-'; | |
62 | return if $role eq '-'; | |
63 | ||
04439dbc | 64 | foreach my $cid (keys %{$cinfo->{ids}}) { |
dbe94472 | 65 | my $ni = $cinfo->{ids}->{$cid}; |
dbe94472 DM |
66 | next if $ni->{ip} eq $cinfo->{local}->{ip}; # just to be sure |
67 | ||
dbe94472 DM |
68 | my $running; |
69 | foreach my $cpid (keys %$workers) { | |
70 | $running = 1 if $workers->{$cpid}->{ip} eq $ni->{ip}; | |
71 | } | |
72 | next if $running; | |
73 | ||
74 | if ($delayed_exec->{$cid} && (time < $delayed_exec->{$cid})) { | |
75 | next; | |
76 | } | |
77 | $delayed_exec->{$cid} = 0; | |
78 | $startcount->{$cid}++; | |
79 | ||
80 | my $pid = fork; | |
81 | ||
82 | if (!defined ($pid)) { | |
83 | ||
84 | syslog('err', "can't fork tunnel"); | |
85 | ||
86 | } elsif($pid) { # parent | |
87 | ||
88 | $workers->{$pid}->{ip} = $ni->{ip}; | |
89 | $workers->{$pid}->{cid} = $cid; | |
dbe94472 DM |
90 | |
91 | if ($startcount->{$cid} > 1) { | |
92 | syslog('info', "restarting crashed tunnel $pid $ni->{ip}"); | |
93 | } else { | |
94 | syslog('info', "starting tunnel $pid $ni->{ip}"); | |
95 | } | |
96 | ||
97 | } else { # child | |
98 | ||
99 | $self->after_fork_cleanup(); | |
100 | ||
155b0da9 DM |
101 | mkdir $socketdir; |
102 | my $sock = $socketfile->($cid); | |
103 | unlink $sock; | |
dbe94472 | 104 | exec('/usr/bin/ssh', '-N', '-o', 'BatchMode=yes', |
2ee32bb7 | 105 | '-o', "HostKeyAlias=$ni->{name}", |
155b0da9 | 106 | '-L', "$sock:/var/run/postgresql/.s.PGSQL.5432", |
dbe94472 DM |
107 | $ni->{ip}); |
108 | exit (0); | |
109 | } | |
110 | } | |
111 | } | |
112 | ||
113 | sub purge_tunnels { | |
114 | my ($self, $cinfo) = @_; | |
115 | ||
116 | foreach my $cpid (keys %$workers) { | |
117 | my $ip = $workers->{$cpid}->{ip}; | |
dbe94472 DM |
118 | my $cid = $workers->{$cpid}->{cid}; |
119 | ||
120 | my $found; | |
121 | foreach my $ni (values %{$cinfo->{ids}}) { | |
3d1c8a6d | 122 | $found = 1 if ($ni->{ip} eq $ip) && ($ni->{cid} eq $cid); |
dbe94472 DM |
123 | } |
124 | ||
125 | my $role = $cinfo->{local}->{type} // '-'; | |
126 | $found = 0 if $role eq '-'; | |
127 | ||
128 | if (!$found) { | |
129 | syslog ('info', "trying to finish tunnel $cpid $ip"); | |
130 | kill(15, $cpid); | |
131 | $delayed_exec->{$cid} = time + ($startcount->{$cid} > 5 ? 60 : 10); | |
132 | delete $workers->{$cpid}; | |
133 | } | |
134 | } | |
135 | } | |
136 | ||
137 | sub init { | |
138 | # syslog('INIT'); | |
139 | } | |
140 | ||
141 | sub shutdown { | |
142 | my ($self) = @_; | |
143 | ||
144 | syslog('info' , "server closing"); | |
145 | ||
146 | foreach my $cpid (keys %$workers) { | |
147 | if (kill (15, $cpid) || ! kill(0, $cpid)) { | |
148 | my $ip = $workers->{$cpid}->{ip}; | |
149 | delete $workers->{$cpid}; | |
150 | syslog ('info', "successfully deleted tunnel $cpid $ip"); | |
151 | } | |
152 | } | |
153 | ||
154 | # wait for children | |
155 | 1 while (waitpid(-1, POSIX::WNOHANG()) > 0); | |
156 | ||
157 | # $self->exit_daemon(0); | |
158 | } | |
159 | ||
160 | sub hup { | |
161 | my ($self) = @_; | |
162 | ||
163 | $restart_request = 1; | |
164 | } | |
165 | ||
166 | ||
167 | ||
168 | sub run { | |
169 | my ($self) = @_; | |
170 | ||
171 | local $SIG{CHLD} = \&finish_children; | |
172 | ||
173 | for (;;) { # forever | |
174 | ||
175 | $next_update = time() + $updatetime; | |
176 | ||
177 | eval { | |
178 | my $cinfo = PMG::ClusterConfig->new(); # reload | |
179 | $self->purge_tunnels($cinfo); | |
180 | $self->start_tunnels($cinfo); | |
181 | }; | |
182 | ||
183 | if (my $err = $@) { | |
184 | ||
185 | syslog('err', "status update error: $err"); | |
186 | } | |
187 | ||
188 | my $wcount = 0; | |
189 | while ((time() < $next_update) && | |
190 | ($wcount < $updatetime) && # protect against time wrap | |
191 | !$restart_request && !$self->{terminate}) { | |
192 | ||
193 | finish_children(); | |
194 | ||
195 | $wcount++; sleep (1); | |
196 | }; | |
197 | ||
198 | last if $self->{terminate}; | |
199 | ||
200 | $self->restart_daemon() if $restart_request; | |
201 | } | |
202 | } | |
203 | ||
204 | __PACKAGE__->register_method ({ | |
205 | name => 'status', | |
206 | path => 'status', | |
207 | method => 'GET', | |
208 | description => "Print cluster tunnel status.", | |
209 | parameters => { | |
210 | additionalProperties => 0, | |
211 | properties => {}, | |
212 | }, | |
213 | returns => { type => 'null' }, | |
214 | code => sub { | |
215 | my ($param) = @_; | |
216 | ||
217 | my $status = $daemon->running() ? 'running' : 'stopped'; | |
218 | print "$status\n"; | |
219 | ||
220 | return undef; | |
221 | }}); | |
222 | ||
223 | ||
224 | $daemon->register_start_command("Start the Cluster Tunnel Daemon"); | |
225 | $daemon->register_stop_command("Stop the Cluster Tunnel Daemon"); | |
226 | $daemon->register_restart_command(1, "Restart the Cluster Tunnel Daemon"); | |
227 | ||
228 | our $cmddef = { | |
229 | start => [ __PACKAGE__, 'start', []], | |
230 | restart => [ __PACKAGE__, 'restart', []], | |
231 | stop => [ __PACKAGE__, 'stop', []], | |
232 | status => [ __PACKAGE__, 'status', []] | |
233 | }; | |
25d268f2 DM |
234 | |
235 | 1; |