]> git.proxmox.com Git - pmg-api.git/blame - src/PMG/Service/pmgtunnel.pm
replace /var/run with /run
[pmg-api.git] / src / PMG / Service / pmgtunnel.pm
CommitLineData
dbe94472
DM
1package PMG::Service::pmgtunnel;
2
3use strict;
4use warnings;
5use Data::Dumper;
6use Time::HiRes qw (gettimeofday);
7
8use PVE::SafeSyslog;
9use PVE::Tools qw(extract_param);
10use PVE::INotify;
11use PVE::Daemon;
12
13use PMG::RESTEnvironment;
14use PMG::DBTools;
15use PMG::RuleDB;
16use PMG::Cluster;
17use PMG::ClusterConfig;
18use PMG::Statistic;
19
20use base qw(PVE::Daemon);
21
22my $cmdline = [$0, @ARGV];
23
24my %daemon_options = (restart_on_error => 5, stop_wait_time => 5);
25
26my $daemon = __PACKAGE__->new('pmgtunnel', $cmdline, %daemon_options);
27
28my $restart_request = 0;
29my $next_update = 0;
30
31my $cycle = 0;
32my $updatetime = 10;
33
34my $workers = {};
35my $delayed_exec = {};
36my $startcount = {};
37
83e9f427 38my $socketdir = "/run/pmgtunnel";
155b0da9
DM
39
40my $socketfile = sub {
41 my ($cid) = @_;
42 return "$socketdir/.s.PGSQL.$cid";
43};
44
dbe94472
DM
45sub 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
58sub 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
113sub 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
137sub init {
138 # syslog('INIT');
139}
140
141sub 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
160sub hup {
161 my ($self) = @_;
162
163 $restart_request = 1;
164}
165
166
167
168sub 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
228our $cmddef = {
229 start => [ __PACKAGE__, 'start', []],
230 restart => [ __PACKAGE__, 'restart', []],
231 stop => [ __PACKAGE__, 'stop', []],
232 status => [ __PACKAGE__, 'status', []]
233};
25d268f2
DM
234
2351;