]>
git.proxmox.com Git - pve-common.git/blob - data/PVE/Daemon.pm
3 # Abstract class to implement Daemons
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')
16 use POSIX
":sys_wait_h";
19 use Time
::HiRes qw
(gettimeofday
);
21 use base
qw(PVE::CLIHandler);
23 $SIG{'__WARN__'} = sub {
28 syslog
('warning', "WARNING: %s", $t);
32 $ENV{'PATH'} = '/sbin:/bin:/usr/sbin:/usr/bin';
34 my $daemon_initialized = 0; # we only allow one instance
36 my $lockpidfile = sub {
39 my $lkfn = $self->{pidfile
} . ".lock";
41 if (!open (FLCK
, ">>$lkfn")) {
42 my $msg = "can't aquire lock on file '$lkfn' - $!";
47 if (!flock (FLCK
, LOCK_EX
|LOCK_NB
)) {
49 my $msg = "can't aquire lock '$lkfn' - $!";
55 my $writepidfile = sub {
58 my $pidfile = $self->{pidfile
};
60 if (!open (PIDFH
, ">$pidfile")) {
61 my $msg = "can't open pid file '$pidfile' - $!";
69 my $server_cleanup = sub {
72 unlink $self->{pidfile
} . ".lock";
73 unlink $self->{pidfile
};
76 my $server_run = sub {
77 my ($self, $debug) = @_;
84 my $restart = $ENV{RESTART_PVE_DAEMON
};
86 delete $ENV{RESTART_PVE_DAEMON
};
88 $self->{debug
} = 1 if $debug;
93 open STDIN
, '</dev/null' || die "can't read /dev/null";
94 open STDOUT
, '>/dev/null' || die "can't write /dev/null";
97 if (!$restart && !$debug) {
98 PVE
::INotify
::inotify_close
();
100 if (!defined ($spid)) {
101 my $msg = "can't put server into background - fork failed";
104 } elsif ($spid) { # parent
107 PVE
::INotify
::inotify_init
();
110 &$writepidfile($self);
113 syslog
('info' , "restarting server");
115 syslog
('info' , "starting server");
118 open STDERR
, '>&STDOUT' || die "can't close STDERR\n";
120 $SIG{INT
} = $SIG{TERM
} = $SIG{QUIT
} = sub {
121 $SIG{INT
} = 'DEFAULT';
123 eval { $self->shutdown(); };
126 &$server_cleanup($self);
130 eval { $self->hup(); };
134 eval { $self->run() };
138 syslog
('err', "ERROR: $err");
139 if (my $wait_time = $self->{restart_on_error
}) {
140 $self->restart_daemon($wait_time);
142 $self->exit_daemon(-1);
146 $self->exit_daemon(0);
150 my ($this, $name, $cmdline, %params) = @_;
152 die "please run as root\n" if $> != 0;
154 die "missing name" if !$name;
156 die "can't create more that one PVE::Daemon" if $daemon_initialized;
157 $daemon_initialized = 1;
159 PVE
::INotify
::inotify_init
();
163 my $class = ref($this) || $this;
165 my $self = bless { name
=> $name }, $class;
167 $self->{pidfile
} = "/var/run/${name}.pid";
169 $self->{nodename
} = PVE
::INotify
::nodename
();
171 $self->{cmdline
} = $cmdline;
175 foreach my $opt (keys %params) {
176 my $value = $params{$opt};
177 if ($opt eq 'restart_on_error') {
178 $self->{$opt} = $value;
179 } elsif ($opt eq 'stop_wait_time') {
180 $self->{$opt} = $value;
182 die "unknown option '$opt'";
190 my ($self, $status) = @_;
192 syslog
("info", "server stopped");
194 &$server_cleanup($self);
200 my ($self, $waittime) = @_;
202 syslog
('info', "server shutdown (restart)");
204 $ENV{RESTART_PVE_DAEMON
} = 1;
206 sleep($waittime) if $waittime; # avoid high server load due to restarts
208 PVE
::INotify
::inotify_close
();
210 exec (@{$self->{cmdline
}});
212 exit (-1); # never reached?
215 # please overwrite in subclass
216 # this is called at startup - before forking
222 # please overwrite in subclass
226 syslog
('info' , "server closing");
229 1 while (waitpid(-1, POSIX
::WNOHANG
()) > 0);
232 # please overwrite in subclass
236 syslog
('info' , "received signal HUP (restart)");
239 # please overwrite in subclass
244 syslog
('info' , "server is running");
250 my ($self, $debug) = @_;
252 &$server_run($self, $debug);
258 my $pid = int(PVE
::Tools
::file_read_firstline
($self->{pidfile
}) || 0);
261 my $res = PVE
::ProcFSTools
::check_process_running
($pid) ?
1 : 0;
262 return wantarray ?
($res, $pid) : $res;
265 return wantarray ?
(0, 0) : 0;
271 my $pid = int(PVE
::Tools
::file_read_firstline
($self->{pidfile
}) || 0);
274 if (PVE
::ProcFSTools
::check_process_running
($pid)) {
275 kill(15, $pid); # send TERM signal
277 my $wait_time = $self->{stop_wait_time
} || 5;
279 for (my $i = 0; $i < $wait_time; $i++) {
280 $running = PVE
::ProcFSTools
::check_process_running
($pid);
285 syslog
('err', "server still running - send KILL") if $running;
292 if (-f
$self->{pidfile
}) {
293 # try to get the lock
294 &$lockpidfile($self);
295 &$server_cleanup($self);
299 sub register_start_command
{
300 my ($self, $class, $description) = @_;
302 $class->register_method({
306 description
=> $description || "Start the daemon.",
308 additionalProperties
=> 0,
311 description
=> "Debug mode - stay in foreground",
318 returns
=> { type
=> 'null' },
323 $self->start($param->{debug
});
329 sub register_restart_command
{
330 my ($self, $class, $description) = @_;
332 $class->register_method({
336 description
=> $description || "Restart the daemon (or start if not running).",
338 additionalProperties
=> 0,
341 returns
=> { type
=> 'null' },
346 if (my $restart = $ENV{RESTART_PVE_DAEMON
}) {
349 my ($running, $pid) = $self->running();
361 sub register_stop_command
{
362 my ($self, $class, $description) = @_;
364 $class->register_method({
368 description
=> $description || "Stop the daemon.",
370 additionalProperties
=> 0,
373 returns
=> { type
=> 'null' },
384 sub register_status_command
{
385 my ($self, $class, $description) = @_;
387 $class->register_method({
391 description
=> "Get daemon status.",
393 additionalProperties
=> 0,
398 enum
=> ['stopped', 'running'],
403 return $self->running() ?
'running' : 'stopped';