538ecaf3532dd94ebad70d26b4f372872766ba41
[pve-common.git] / data / PVE / Daemon.pm
1 package 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)
9 # * daemon restart (option 'restart_on_error')
10
11 use strict;
12 use warnings;
13 use PVE::SafeSyslog;
14 use PVE::INotify;
15
16 use POSIX ":sys_wait_h";
17 use Fcntl ':flock';
18 use Getopt::Long;
19 use Time::HiRes qw (gettimeofday);
20
21 use base qw(PVE::CLIHandler);
22
23 $SIG{'__WARN__'} = sub {
24     my $err = $@;
25     my $t = $_[0];
26     chomp $t;
27     print "$t\n";
28     syslog('warning', "WARNING: %s", $t);
29     $@ = $err;
30 };
31
32 $ENV{'PATH'} = '/sbin:/bin:/usr/sbin:/usr/bin';
33
34 my $daemon_initialized = 0; # we only allow one instance
35
36 my $lockpidfile = sub {
37     my ($self) = @_;
38
39     my $lkfn = $self->{pidfile} . ".lock";
40
41     if (!open (FLCK, ">>$lkfn")) {
42         my $msg = "can't aquire lock on file '$lkfn' - $!";
43         syslog ('err', $msg);
44         die "ERROR: $msg\n";
45     }
46
47     if (!flock (FLCK, LOCK_EX|LOCK_NB)) {
48         close (FLCK);
49         my $msg = "can't aquire lock '$lkfn' - $!";
50         syslog ('err', $msg);
51         die "ERROR: $msg\n";
52     }
53 };
54
55 my $writepidfile = sub {
56     my ($self) = @_;
57
58     my $pidfile = $self->{pidfile};
59
60     if (!open (PIDFH, ">$pidfile")) {
61         my $msg = "can't open pid file '$pidfile' - $!";
62         syslog ('err', $msg);
63         die "ERROR: $msg\n";
64     }
65     print PIDFH "$$\n";
66     close (PIDFH);
67 };
68
69 my $server_cleanup = sub {
70     my ($self) = @_;
71
72     unlink $self->{pidfile} . ".lock";
73     unlink $self->{pidfile};
74 };
75
76 my $server_run = sub {
77     my ($self, $debug) = @_;
78
79     &$lockpidfile($self);
80
81     # run in background
82     my $spid;
83
84     my $restart = $ENV{RESTART_PVE_DAEMON};
85
86     delete $ENV{RESTART_PVE_DAEMON};
87
88     $self->{debug} = 1 if $debug;
89
90     $self->init();
91
92     if (!$debug) {
93         open STDIN,  '</dev/null' || die "can't read /dev/null";
94         open STDOUT, '>/dev/null' || die "can't write /dev/null";
95     }
96
97     if (!$restart && !$debug) {
98         PVE::INotify::inotify_close();
99         $spid = fork();
100         if (!defined ($spid)) {
101             my $msg =  "can't put server into background - fork failed";
102             syslog('err', $msg);
103             die "ERROR: $msg\n";
104         } elsif ($spid) { # parent
105             exit (0);
106         }
107         PVE::INotify::inotify_init();
108     }
109
110     &$writepidfile($self);
111
112     POSIX::setsid(); 
113
114     if ($restart) {
115         syslog('info' , "restarting server");
116     } else {
117         syslog('info' , "starting server");
118     }
119
120     open STDERR, '>&STDOUT' || die "can't close STDERR\n";
121
122     $SIG{INT} = $SIG{TERM} = $SIG{QUIT} = sub {
123         $SIG{INT} = 'DEFAULT';
124
125         eval { $self->shutdown(); };
126         warn $@ if $@;
127
128         &$server_cleanup($self);
129     };
130
131     if ($self->can('hup')) {
132         $SIG{HUP} = sub {
133             eval { $self->hup() };
134             warn $@ if $@;
135         };
136     }
137
138     eval { $self->run() };
139     my $err = $@;
140
141     if ($err) {
142         syslog ('err', "ERROR: $err");
143         if (my $wait_time = $self->{restart_on_error}) {
144             $self->restart_daemon($wait_time);
145         } else {
146             $self->exit_daemon(-1);
147         }
148     }
149
150     $self->exit_daemon(0);
151 };
152
153 sub new {
154     my ($this, $name, $cmdline, %params) = @_;
155
156     die "please run as root\n" if $> != 0;
157
158     die "missing name" if !$name;
159
160     die "can't create more that one PVE::Daemon" if $daemon_initialized;
161     $daemon_initialized = 1;
162
163     PVE::INotify::inotify_init();
164
165     initlog($name);
166
167     my $class = ref($this) || $this;
168
169     my $self = bless { 
170         name => $name,
171         run_dir => '/var/run',
172     }, $class;
173
174     foreach my $opt (keys %params) {
175         my $value = $params{$opt};
176         if ($opt eq 'restart_on_error') {
177             $self->{$opt} = $value;
178         } elsif ($opt eq 'stop_wait_time') {
179             $self->{$opt} = $value;
180         } elsif ($opt eq 'run_dir') {
181             $self->{$opt} = $value;
182         } else {
183             die "unknown option '$opt'";
184         }
185     }
186
187     $self->{pidfile} = "$self->{run_dir}/${name}.pid";
188
189     $self->{nodename} = PVE::INotify::nodename();
190
191     $self->{cmdline} = $cmdline;
192
193     $0 = $name;
194
195
196     return $self;
197 }
198
199 sub exit_daemon {
200     my ($self, $status) = @_;
201
202     syslog("info", "server stopped");
203
204     &$server_cleanup($self);
205
206     exit($status);
207 }
208
209 sub restart_daemon {
210     my ($self, $waittime) = @_;
211
212     syslog('info', "server shutdown (restart)");
213
214     $ENV{RESTART_PVE_DAEMON} = 1;
215
216     sleep($waittime) if $waittime; # avoid high server load due to restarts
217
218     PVE::INotify::inotify_close();
219
220     exec (@{$self->{cmdline}});
221
222     exit (-1); # never reached?
223 }
224
225 # please overwrite in subclass
226 # this is called at startup - before forking
227 sub init {
228     my ($self) = @_;
229
230 }
231
232 # please overwrite in subclass
233 sub shutdown {
234     my ($self) = @_;
235
236     syslog('info' , "server closing");
237
238     # wait for children
239     1 while (waitpid(-1, POSIX::WNOHANG()) > 0);
240 }
241
242 # please define in subclass
243 #sub hup {
244 #    my ($self) = @_;
245 #
246 #    syslog('info' , "received signal HUP (restart)");
247 #}
248
249 # please overwrite in subclass
250 sub run {
251     my ($self) = @_;
252
253     for (;;) { # forever
254         syslog('info' , "server is running");
255         sleep(5);
256     }
257 }
258
259 sub start {
260     my ($self, $debug) = @_;
261
262     &$server_run($self, $debug);
263 }
264
265 my $read_pid = sub {
266     my ($self) = @_;
267
268     my $pid_str = PVE::Tools::file_read_firstline($self->{pidfile});
269
270     return 0 if !$pid_str;
271
272     return 0 if $pid_str !~ m/^(\d+)$/; # untaint
273  
274     my $pid = int($1);
275
276     return $pid;
277 };
278
279 sub running {
280     my ($self) = @_;
281
282     my $pid = &$read_pid($self);
283
284     if ($pid) {
285         my $res = PVE::ProcFSTools::check_process_running($pid) ? 1 : 0;
286         return wantarray ? ($res, $pid) : $res;
287     }
288
289     return wantarray ? (0, 0) : 0;
290 }
291
292 sub stop {
293     my ($self) = @_;
294
295     my $pid = &$read_pid($self);
296
297     return if !$pid;
298
299     if (PVE::ProcFSTools::check_process_running($pid)) {
300         kill(15, $pid); # send TERM signal
301         # give some time
302         my $wait_time = $self->{stop_wait_time} || 5;
303         my $running = 1;
304         for (my $i = 0; $i < $wait_time; $i++) {
305             $running = PVE::ProcFSTools::check_process_running($pid);
306             last if !$running;
307             sleep (1);
308         }
309
310         syslog('err', "server still running - send KILL") if $running;
311
312         # to be sure
313         kill(9, $pid);
314         waitpid($pid, 0);
315     }
316
317     if (-f $self->{pidfile}) {
318         # try to get the lock
319         &$lockpidfile($self);
320         &$server_cleanup($self);
321     }
322 }
323
324 sub register_start_command {
325     my ($self, $class, $description) = @_;
326
327     $class->register_method({
328         name => 'start',
329         path => 'start',
330         method => 'POST',
331         description => $description || "Start the daemon.",
332         parameters => {
333             additionalProperties => 0,
334             properties => {
335                 debug => {
336                     description => "Debug mode - stay in foreground",
337                     type => "boolean",
338                     optional => 1,
339                     default => 0,
340                 },
341             },
342         },
343         returns => { type => 'null' },
344
345         code => sub {
346             my ($param) = @_;
347
348             $self->start($param->{debug});
349
350             return undef;
351         }});  
352 }
353
354 my $reload_daemon = sub {
355     my ($self, $use_hup) = @_;
356
357     if (my $restart = $ENV{RESTART_PVE_DAEMON}) {
358         $self->start();
359     } else {
360         my ($running, $pid) = $self->running(); 
361         if (!$running) {
362             $self->start();
363         } else {
364             if ($use_hup) {
365                 kill(1, $pid);
366             } else {
367                 $self->stop();
368                 $self->start();
369             }
370         }
371     }
372 };
373
374 sub register_restart_command {
375     my ($self, $class, $use_hup, $description) = @_;
376
377     $class->register_method({
378         name => 'restart',
379         path => 'restart',
380         method => 'POST',
381         description => $description || "Restart the daemon (or start if not running).",
382         parameters => {
383             additionalProperties => 0,
384             properties => {},
385         },
386         returns => { type => 'null' },
387
388         code => sub {
389             my ($param) = @_;
390
391             &$reload_daemon($self, $use_hup);
392
393             return undef;
394         }});               
395 }
396
397 sub register_reload_command {
398     my ($self, $class, $description) = @_;
399
400     $class->register_method({
401         name => 'reload',
402         path => 'reload',
403         method => 'POST',
404         description => $description || "Reload daemon configuration (or start if not running).",
405         parameters => {
406             additionalProperties => 0,
407             properties => {},
408         },
409         returns => { type => 'null' },
410
411         code => sub {
412             my ($param) = @_;
413
414             &$reload_daemon($self, 1);
415
416             return undef;
417         }});               
418 }
419
420 sub register_stop_command {
421     my ($self, $class, $description) = @_;
422
423     $class->register_method({
424         name => 'stop',
425         path => 'stop',
426         method => 'POST',
427         description => $description || "Stop the daemon.",
428         parameters => {
429             additionalProperties => 0,
430             properties => {},
431         },
432         returns => { type => 'null' },
433
434         code => sub {
435             my ($param) = @_;
436             
437             $self->stop();
438
439             return undef;
440         }});               
441 }
442
443 sub register_status_command {
444     my ($self, $class, $description) = @_;
445
446     $class->register_method({
447         name => 'status',
448         path => 'status',
449         method => 'GET',
450         description => "Get daemon status.",
451         parameters => {
452             additionalProperties => 0,
453             properties => {},
454         },
455         returns => { 
456             type => 'string',
457             enum => ['stopped', 'running'],
458         },
459         code => sub {
460             my ($param) = @_;
461
462             return $self->running() ? 'running' : 'stopped';
463         }});
464 }
465
466 1;
467