]> git.proxmox.com Git - pve-common.git/blame - data/PVE/Daemon.pm
Daemon: correctly set got_hup_signal flag (before we call terminate)
[pve-common.git] / data / PVE / Daemon.pm
CommitLineData
390802ab
DM
1package PVE::Daemon;
2
3# Abstract class to implement Daemons
4#
5# Features:
6# * lock and write PID file /var/run/$name.pid to make sure onyl
7# one instance is running.
8# * correctly daemonize (redirect STDIN/STDOUT)
4fc69176
DM
9# * restart by stop/start, exec, or signal HUP
10# * daemon restart on error (option 'restart_on_error')
b2132054 11# * handle worker processes (option 'max_workers')
390802ab
DM
12
13use strict;
14use warnings;
15use PVE::SafeSyslog;
16use PVE::INotify;
17
18use POSIX ":sys_wait_h";
19use Fcntl ':flock';
20use Getopt::Long;
21use Time::HiRes qw (gettimeofday);
22
23use base qw(PVE::CLIHandler);
24
390802ab
DM
25$ENV{'PATH'} = '/sbin:/bin:/usr/sbin:/usr/bin';
26
27my $daemon_initialized = 0; # we only allow one instance
28
3206cdf6
DM
29my $close_daemon_lock = sub {
30 my ($self) = @_;
31
eead1ad8
DM
32 return if !$self->{daemon_lock_fh};
33
3206cdf6
DM
34 close $self->{daemon_lock_fh};
35 delete $self->{daemon_lock_fh};
36};
37
c5611195
DM
38my $log_err = sub {
39 my ($msg) = @_;
40 chomp $msg;
41 print STDERR "$msg\n";
42 syslog('err', "%s", $msg);
43};
44
eead1ad8
DM
45# call this if you fork() from child
46# Note: we already call this for workers, so it is only required
47# if you fork inside a simple daemon (max_workers == 0).
48sub after_fork_cleanup {
49 my ($self) = @_;
50
51 &$close_daemon_lock($self);
52
53 PVE::INotify::inotify_close();
54
55 for my $sig (qw(CHLD HUP INT TERM QUIT)) {
56 $SIG{$sig} = 'DEFAULT'; # restore default handler
57 # AnyEvent signals only works if $SIG{XX} is
58 # undefined (perl event loop)
59 delete $SIG{$sig}; # so that we can handle events with AnyEvent
60 }
61}
62
390802ab
DM
63my $lockpidfile = sub {
64 my ($self) = @_;
65
66 my $lkfn = $self->{pidfile} . ".lock";
67
48876459
DM
68 my $waittime = 0;
69
1a6bc2f3
DM
70 if (my $fd = $self->{env_pve_lock_fd}) {
71
72 $self->{daemon_lock_fh} = IO::Handle->new_from_fd($fd, "a");
48876459 73
1a6bc2f3
DM
74 } else {
75
48876459 76 $waittime = 5;
1a6bc2f3
DM
77 $self->{daemon_lock_fh} = IO::File->new(">>$lkfn");
78 }
79
3206cdf6 80 if (!$self->{daemon_lock_fh}) {
1a6bc2f3 81 die "can't open lock '$lkfn' - $!\n";
390802ab
DM
82 }
83
48876459 84 for (my $i = 0; $i < $waittime; $i ++) {
3206cdf6
DM
85 return if flock ($self->{daemon_lock_fh}, LOCK_EX|LOCK_NB);
86 sleep(1);
87 }
88
89 if (!flock ($self->{daemon_lock_fh}, LOCK_EX|LOCK_NB)) {
90 &$close_daemon_lock($self);
c5611195
DM
91 my $err = $!;
92
93 my ($running, $pid) = $self->running();
94 if ($running) {
95 die "can't aquire lock '$lkfn' - daemon already started (pid = $pid)\n";
96 } else {
97 die "can't aquire lock '$lkfn' - $err\n";
98 }
390802ab
DM
99 }
100};
101
102my $writepidfile = sub {
103 my ($self) = @_;
104
105 my $pidfile = $self->{pidfile};
106
c5611195
DM
107 die "can't open pid file '$pidfile' - $!\n" if !open (PIDFH, ">$pidfile");
108
390802ab
DM
109 print PIDFH "$$\n";
110 close (PIDFH);
111};
112
113my $server_cleanup = sub {
114 my ($self) = @_;
115
116 unlink $self->{pidfile} . ".lock";
117 unlink $self->{pidfile};
118};
119
b2132054
DM
120my $finish_workers = sub {
121 my ($self) = @_;
122
123 foreach my $cpid (keys %{$self->{workers}}) {
124 my $waitpid = waitpid($cpid, WNOHANG);
125 if (defined($waitpid) && ($waitpid == $cpid)) {
126 delete ($self->{workers}->{$cpid});
127 syslog('info', "worker $cpid finished");
128 }
129 }
130};
131
132my $start_workers = sub {
133 my ($self) = @_;
134
135 return if $self->{terminate};
136
137 my $count = 0;
138 foreach my $cpid (keys %{$self->{workers}}) {
139 $count++;
140 }
141
142 my $need = $self->{max_workers} - $count;
143
144 return if $need <= 0;
145
146 syslog('info', "starting $need worker(s)");
147
148 while ($need > 0) {
149 my $pid = fork;
150
151 if (!defined ($pid)) {
152 syslog('err', "can't fork worker");
153 sleep (1);
154 } elsif ($pid) { # parent
155 $self->{workers}->{$pid} = 1;
156 syslog('info', "worker $pid started");
157 $need--;
158 } else {
159 $0 = "$self->{name} worker";
160
eead1ad8 161 $self->after_fork_cleanup();
b2132054
DM
162
163 eval { $self->run(); };
164 if (my $err = $@) {
165 syslog('err', $err);
166 sleep(5); # avoid fast restarts
167 }
168
169 syslog('info', "worker exit");
170 exit (0);
171 }
172 }
173};
174
175my $terminate_server = sub {
176 my ($self) = @_;
177
178 $self->{terminate} = 1; # set flag to avoid worker restart
179
180 if (!$self->{max_workers}) {
181 eval { $self->shutdown(); };
182 warn $@ if $@;
183 return;
184 }
185
186 eval { $self->shutdown(); };
187 warn $@ if $@;
188
189 # we have workers - terminate them
190
191 foreach my $cpid (keys %{$self->{workers}}) {
192 kill(15, $cpid); # TERM childs
193 }
194
195 # nicely shutdown childs (give them max 10 seconds to shut down)
196 my $previous_alarm = alarm(10);
197 eval {
198 local $SIG{ALRM} = sub { die "timeout\n" };
199
200 while ((my $pid = waitpid (-1, 0)) > 0) {
201 if (defined($self->{workers}->{$pid})) {
202 delete($self->{workers}->{$pid});
203 syslog('info', "worker $pid finished");
204 }
205 }
206 alarm(0); # avoid race condition
207 };
208 my $err = $@;
209
210 alarm ($previous_alarm);
211
212 if ($err) {
213 syslog('err', "error stopping workers (will kill them now) - $err");
214 foreach my $cpid (keys %{$self->{workers}}) {
215 # KILL childs still alive!
216 if (kill (0, $cpid)) {
217 delete($self->{workers}->{$cpid});
218 syslog("err", "kill worker $cpid");
219 kill(9, $cpid);
220 # fixme: waitpid?
221 }
222 }
223 }
224};
225
390802ab
DM
226my $server_run = sub {
227 my ($self, $debug) = @_;
228
1a6bc2f3 229 # fixme: handle restart lockfd
390802ab
DM
230 &$lockpidfile($self);
231
1a6bc2f3
DM
232 # remove FD_CLOEXEC bit to reuse on exec
233 $self->{daemon_lock_fh}->fcntl(Fcntl::F_SETFD(), 0);
234
235 $ENV{PVE_DAEMON_LOCK_FD} = $self->{daemon_lock_fh}->fileno;
236
390802ab
DM
237 # run in background
238 my $spid;
239
390802ab
DM
240 $self->{debug} = 1 if $debug;
241
242 $self->init();
243
244 if (!$debug) {
245 open STDIN, '</dev/null' || die "can't read /dev/null";
246 open STDOUT, '>/dev/null' || die "can't write /dev/null";
247 }
248
a8ba2293 249 if (!$self->{env_restart_pve_daemon} && !$debug) {
390802ab
DM
250 PVE::INotify::inotify_close();
251 $spid = fork();
252 if (!defined ($spid)) {
c5611195 253 die "can't put server into background - fork failed";
390802ab
DM
254 } elsif ($spid) { # parent
255 exit (0);
256 }
257 PVE::INotify::inotify_init();
258 }
259
a8ba2293 260 if ($self->{env_restart_pve_daemon}) {
390802ab
DM
261 syslog('info' , "restarting server");
262 } else {
c5611195 263 &$writepidfile($self);
390802ab
DM
264 syslog('info' , "starting server");
265 }
266
c5611195
DM
267 POSIX::setsid();
268
390802ab
DM
269 open STDERR, '>&STDOUT' || die "can't close STDERR\n";
270
b2132054
DM
271 my $old_sig_term = $SIG{TERM};
272 local $SIG{TERM} = sub {
273 local ($@, $!, $?); # do not overwrite error vars
274 syslog('info', "received signal TERM");
275 &$terminate_server($self);
276 &$server_cleanup($self);
277 &$old_sig_term(@_) if $old_sig_term;
278 };
390802ab 279
b2132054
DM
280 my $old_sig_quit = $SIG{QUIT};
281 local $SIG{QUIT} = sub {
282 local ($@, $!, $?); # do not overwrite error vars
283 syslog('info', "received signal QUIT");
284 &$terminate_server($self);
285 &$server_cleanup($self);
286 &$old_sig_quit(@_) if $old_sig_quit;
287 };
390802ab 288
b2132054
DM
289 my $old_sig_int = $SIG{INT};
290 local $SIG{INT} = sub {
291 local ($@, $!, $?); # do not overwrite error vars
292 syslog('info', "received signal INT");
293 $SIG{INT} = 'DEFAULT'; # allow to terminate now
294 &$terminate_server($self);
390802ab 295 &$server_cleanup($self);
b2132054 296 &$old_sig_int(@_) if $old_sig_int;
390802ab
DM
297 };
298
b2132054
DM
299 $SIG{HUP} = sub {
300 local ($@, $!, $?); # do not overwrite error vars
301 syslog('info', "received signal HUP");
93710700 302 $self->{got_hup_signal} = 1;
b2132054
DM
303 if ($self->{max_workers}) {
304 &$terminate_server($self);
b2132054 305 } elsif ($self->can('hup')) {
bdb5acce
DM
306 eval { $self->hup() };
307 warn $@ if $@;
b2132054
DM
308 }
309 };
310
311 eval {
312 if ($self->{max_workers}) {
313 my $old_sig_chld = $SIG{CHLD};
314 local $SIG{CHLD} = sub {
315 local ($@, $!, $?); # do not overwrite error vars
316 &$finish_workers($self);
317 &$old_sig_chld(@_) if $old_sig_chld;
318 };
319
320 for (;;) { # forever
321 &$start_workers($self);
322 sleep(5);
323 &$finish_workers($self);
324 last if $self->{terminate};
325 }
bdb5acce 326
b2132054
DM
327 } else {
328 $self->run();
329 }
330 };
390802ab
DM
331 my $err = $@;
332
333 if ($err) {
334 syslog ('err', "ERROR: $err");
b2132054
DM
335
336 # fixme: kill all workers
337
390802ab
DM
338 if (my $wait_time = $self->{restart_on_error}) {
339 $self->restart_daemon($wait_time);
340 } else {
341 $self->exit_daemon(-1);
342 }
343 }
344
b2132054
DM
345 if ($self->{got_hup_signal}) {
346 $self->restart_daemon();
347 } else {
348 $self->exit_daemon(0);
349 }
390802ab
DM
350};
351
352sub new {
353 my ($this, $name, $cmdline, %params) = @_;
354
1ea9e6df 355 $name = 'daemon' if !$name; # should not happen
1a6bc2f3
DM
356
357 initlog($name);
a8ba2293 358
1ea9e6df 359 my $self;
a8ba2293 360
1ea9e6df 361 eval {
390802ab 362
1ea9e6df
DM
363 my $restart = $ENV{RESTART_PVE_DAEMON};
364 delete $ENV{RESTART_PVE_DAEMON};
390802ab 365
1ea9e6df
DM
366 my $lockfd = $ENV{PVE_DAEMON_LOCK_FD};
367 delete $ENV{PVE_DAEMON_LOCK_FD};
390802ab 368
7db2b940 369 if (defined($lockfd)) {
6105a115
DM
370 die "unable to parse lock fd '$lockfd'\n"
371 if $lockfd !~ m/^(\d+)$/;
7db2b940
DM
372 $lockfd = $1; # untaint
373 }
374
1ea9e6df 375 die "please run as root\n" if !$restart && ($> != 0);
390802ab 376
1ea9e6df
DM
377 die "can't create more that one PVE::Daemon" if $daemon_initialized;
378 $daemon_initialized = 1;
390802ab 379
1ea9e6df 380 PVE::INotify::inotify_init();
390802ab 381
1ea9e6df
DM
382 my $class = ref($this) || $this;
383
384 $self = bless {
385 name => $name,
386 run_dir => '/var/run',
387 env_restart_pve_daemon => $restart,
388 env_pve_lock_fd => $lockfd,
389 workers => {},
390 }, $class;
391
392 foreach my $opt (keys %params) {
393 my $value = $params{$opt};
394 if ($opt eq 'restart_on_error') {
395 $self->{$opt} = $value;
396 } elsif ($opt eq 'stop_wait_time') {
397 $self->{$opt} = $value;
398 } elsif ($opt eq 'run_dir') {
399 $self->{$opt} = $value;
400 } elsif ($opt eq 'max_workers') {
401 $self->{$opt} = $value;
402 } else {
403 die "unknown daemon option '$opt'\n";
404 }
390802ab 405 }
390802ab 406
1ea9e6df 407 $self->{pidfile} = "$self->{run_dir}/${name}.pid";
bdb5acce 408
1ea9e6df 409 $self->{nodename} = PVE::INotify::nodename();
bdb5acce 410
1ea9e6df 411 $self->{cmdline} = [];
bdb5acce 412
1ea9e6df
DM
413 foreach my $el (@$cmdline) {
414 $el =~ m/^(.*)$/; # untaint
415 push @{$self->{cmdline}}, $1;
416 }
bdb5acce 417
1ea9e6df
DM
418 $0 = $name;
419 };
420 if (my $err = $@) {
421 &$log_err($err);
422 exit(-1);
423 }
bdb5acce 424
390802ab
DM
425 return $self;
426}
427
428sub exit_daemon {
429 my ($self, $status) = @_;
430
431 syslog("info", "server stopped");
432
433 &$server_cleanup($self);
434
435 exit($status);
436}
437
438sub restart_daemon {
439 my ($self, $waittime) = @_;
440
441 syslog('info', "server shutdown (restart)");
442
443 $ENV{RESTART_PVE_DAEMON} = 1;
444
445 sleep($waittime) if $waittime; # avoid high server load due to restarts
446
447 PVE::INotify::inotify_close();
448
449 exec (@{$self->{cmdline}});
450
451 exit (-1); # never reached?
452}
453
454# please overwrite in subclass
455# this is called at startup - before forking
456sub init {
457 my ($self) = @_;
458
459}
460
461# please overwrite in subclass
462sub shutdown {
463 my ($self) = @_;
464
465 syslog('info' , "server closing");
466
b2132054
DM
467 if (!$self->{max_workers}) {
468 # wait for children
469 1 while (waitpid(-1, POSIX::WNOHANG()) > 0);
470 }
390802ab
DM
471}
472
bdb5acce
DM
473# please define in subclass
474#sub hup {
475# my ($self) = @_;
476#
477# syslog('info' , "received signal HUP (restart)");
478#}
390802ab
DM
479
480# please overwrite in subclass
481sub run {
482 my ($self) = @_;
483
484 for (;;) { # forever
485 syslog('info' , "server is running");
486 sleep(5);
487 }
488}
489
490sub start {
491 my ($self, $debug) = @_;
492
1a6bc2f3
DM
493 eval { &$server_run($self, $debug); };
494 if (my $err = $@) {
c5611195
DM
495 &$log_err("start failed - $err");
496 exit(-1);
1a6bc2f3 497 }
390802ab
DM
498}
499
bdb5acce
DM
500my $read_pid = sub {
501 my ($self) = @_;
502
503 my $pid_str = PVE::Tools::file_read_firstline($self->{pidfile});
504
505 return 0 if !$pid_str;
506
507 return 0 if $pid_str !~ m/^(\d+)$/; # untaint
508
509 my $pid = int($1);
510
511 return $pid;
512};
513
390802ab
DM
514sub running {
515 my ($self) = @_;
516
bdb5acce 517 my $pid = &$read_pid($self);
390802ab
DM
518
519 if ($pid) {
520 my $res = PVE::ProcFSTools::check_process_running($pid) ? 1 : 0;
521 return wantarray ? ($res, $pid) : $res;
522 }
523
524 return wantarray ? (0, 0) : 0;
525}
526
527sub stop {
528 my ($self) = @_;
529
bdb5acce
DM
530 my $pid = &$read_pid($self);
531
390802ab
DM
532 return if !$pid;
533
534 if (PVE::ProcFSTools::check_process_running($pid)) {
535 kill(15, $pid); # send TERM signal
536 # give some time
537 my $wait_time = $self->{stop_wait_time} || 5;
538 my $running = 1;
539 for (my $i = 0; $i < $wait_time; $i++) {
540 $running = PVE::ProcFSTools::check_process_running($pid);
541 last if !$running;
542 sleep (1);
543 }
544
545 syslog('err', "server still running - send KILL") if $running;
546
547 # to be sure
548 kill(9, $pid);
549 waitpid($pid, 0);
550 }
551
552 if (-f $self->{pidfile}) {
1a6bc2f3
DM
553 eval {
554 # try to get the lock
555 &$lockpidfile($self);
556 &$server_cleanup($self);
557 };
558 if (my $err = $@) {
c5611195 559 &$log_err("cleanup failed - $err");
1a6bc2f3 560 }
390802ab
DM
561 }
562}
563
564sub register_start_command {
da1a7f58
DM
565 my ($self, $description) = @_;
566
567 my $class = ref($self);
390802ab
DM
568
569 $class->register_method({
570 name => 'start',
571 path => 'start',
572 method => 'POST',
573 description => $description || "Start the daemon.",
574 parameters => {
575 additionalProperties => 0,
576 properties => {
577 debug => {
578 description => "Debug mode - stay in foreground",
579 type => "boolean",
580 optional => 1,
581 default => 0,
582 },
583 },
584 },
585 returns => { type => 'null' },
586
587 code => sub {
588 my ($param) = @_;
589
590 $self->start($param->{debug});
591
592 return undef;
593 }});
594}
595
bdb5acce
DM
596my $reload_daemon = sub {
597 my ($self, $use_hup) = @_;
598
a8ba2293 599 if ($self->{env_restart_pve_daemon}) {
bdb5acce
DM
600 $self->start();
601 } else {
602 my ($running, $pid) = $self->running();
603 if (!$running) {
604 $self->start();
605 } else {
606 if ($use_hup) {
b2132054
DM
607 syslog('info', "send HUP to $pid");
608 kill 1, $pid;
bdb5acce
DM
609 } else {
610 $self->stop();
611 $self->start();
612 }
613 }
614 }
615};
616
390802ab 617sub register_restart_command {
da1a7f58
DM
618 my ($self, $use_hup, $description) = @_;
619
620 my $class = ref($self);
390802ab
DM
621
622 $class->register_method({
623 name => 'restart',
624 path => 'restart',
625 method => 'POST',
626 description => $description || "Restart the daemon (or start if not running).",
627 parameters => {
628 additionalProperties => 0,
629 properties => {},
630 },
631 returns => { type => 'null' },
632
633 code => sub {
634 my ($param) = @_;
635
bdb5acce
DM
636 &$reload_daemon($self, $use_hup);
637
638 return undef;
639 }});
640}
641
642sub register_reload_command {
da1a7f58
DM
643 my ($self, $description) = @_;
644
645 my $class = ref($self);
bdb5acce
DM
646
647 $class->register_method({
648 name => 'reload',
649 path => 'reload',
650 method => 'POST',
651 description => $description || "Reload daemon configuration (or start if not running).",
652 parameters => {
653 additionalProperties => 0,
654 properties => {},
655 },
656 returns => { type => 'null' },
657
658 code => sub {
659 my ($param) = @_;
660
661 &$reload_daemon($self, 1);
390802ab
DM
662
663 return undef;
664 }});
665}
666
667sub register_stop_command {
da1a7f58
DM
668 my ($self, $description) = @_;
669
670 my $class = ref($self);
390802ab
DM
671
672 $class->register_method({
673 name => 'stop',
674 path => 'stop',
675 method => 'POST',
676 description => $description || "Stop the daemon.",
677 parameters => {
678 additionalProperties => 0,
679 properties => {},
680 },
681 returns => { type => 'null' },
682
683 code => sub {
684 my ($param) = @_;
685
686 $self->stop();
687
688 return undef;
689 }});
690}
691
692sub register_status_command {
da1a7f58
DM
693 my ($self, $description) = @_;
694
695 my $class = ref($self);
390802ab
DM
696
697 $class->register_method({
698 name => 'status',
699 path => 'status',
700 method => 'GET',
701 description => "Get daemon status.",
702 parameters => {
703 additionalProperties => 0,
704 properties => {},
705 },
706 returns => {
707 type => 'string',
708 enum => ['stopped', 'running'],
709 },
710 code => sub {
711 my ($param) = @_;
712
713 return $self->running() ? 'running' : 'stopped';
714 }});
715}
716
7171;
718