]>
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);
115 syslog
('info' , "restarting server");
117 syslog
('info' , "starting server");
120 open STDERR
, '>&STDOUT' || die "can't close STDERR\n";
122 $SIG{INT
} = $SIG{TERM
} = $SIG{QUIT
} = sub {
123 $SIG{INT
} = 'DEFAULT';
125 eval { $self->shutdown(); };
128 &$server_cleanup($self);
132 eval { $self->hup(); };
136 eval { $self->run() };
140 syslog
('err', "ERROR: $err");
141 if (my $wait_time = $self->{restart_on_error
}) {
142 $self->restart_daemon($wait_time);
144 $self->exit_daemon(-1);
148 $self->exit_daemon(0);
152 my ($this, $name, $cmdline, %params) = @_;
154 die "please run as root\n" if $> != 0;
156 die "missing name" if !$name;
158 die "can't create more that one PVE::Daemon" if $daemon_initialized;
159 $daemon_initialized = 1;
161 PVE
::INotify
::inotify_init
();
165 my $class = ref($this) || $this;
167 my $self = bless { name
=> $name }, $class;
169 $self->{pidfile
} = "/var/run/${name}.pid";
171 $self->{nodename
} = PVE
::INotify
::nodename
();
173 $self->{cmdline
} = $cmdline;
177 foreach my $opt (keys %params) {
178 my $value = $params{$opt};
179 if ($opt eq 'restart_on_error') {
180 $self->{$opt} = $value;
181 } elsif ($opt eq 'stop_wait_time') {
182 $self->{$opt} = $value;
184 die "unknown option '$opt'";
192 my ($self, $status) = @_;
194 syslog
("info", "server stopped");
196 &$server_cleanup($self);
202 my ($self, $waittime) = @_;
204 syslog
('info', "server shutdown (restart)");
206 $ENV{RESTART_PVE_DAEMON
} = 1;
208 sleep($waittime) if $waittime; # avoid high server load due to restarts
210 PVE
::INotify
::inotify_close
();
212 exec (@{$self->{cmdline
}});
214 exit (-1); # never reached?
217 # please overwrite in subclass
218 # this is called at startup - before forking
224 # please overwrite in subclass
228 syslog
('info' , "server closing");
231 1 while (waitpid(-1, POSIX
::WNOHANG
()) > 0);
234 # please overwrite in subclass
238 syslog
('info' , "received signal HUP (restart)");
241 # please overwrite in subclass
246 syslog
('info' , "server is running");
252 my ($self, $debug) = @_;
254 &$server_run($self, $debug);
260 my $pid = int(PVE
::Tools
::file_read_firstline
($self->{pidfile
}) || 0);
263 my $res = PVE
::ProcFSTools
::check_process_running
($pid) ?
1 : 0;
264 return wantarray ?
($res, $pid) : $res;
267 return wantarray ?
(0, 0) : 0;
273 my $pid = int(PVE
::Tools
::file_read_firstline
($self->{pidfile
}) || 0);
276 if (PVE
::ProcFSTools
::check_process_running
($pid)) {
277 kill(15, $pid); # send TERM signal
279 my $wait_time = $self->{stop_wait_time
} || 5;
281 for (my $i = 0; $i < $wait_time; $i++) {
282 $running = PVE
::ProcFSTools
::check_process_running
($pid);
287 syslog
('err', "server still running - send KILL") if $running;
294 if (-f
$self->{pidfile
}) {
295 # try to get the lock
296 &$lockpidfile($self);
297 &$server_cleanup($self);
301 sub register_start_command
{
302 my ($self, $class, $description) = @_;
304 $class->register_method({
308 description
=> $description || "Start the daemon.",
310 additionalProperties
=> 0,
313 description
=> "Debug mode - stay in foreground",
320 returns
=> { type
=> 'null' },
325 $self->start($param->{debug
});
331 sub register_restart_command
{
332 my ($self, $class, $description) = @_;
334 $class->register_method({
338 description
=> $description || "Restart the daemon (or start if not running).",
340 additionalProperties
=> 0,
343 returns
=> { type
=> 'null' },
348 if (my $restart = $ENV{RESTART_PVE_DAEMON
}) {
351 my ($running, $pid) = $self->running();
363 sub register_stop_command
{
364 my ($self, $class, $description) = @_;
366 $class->register_method({
370 description
=> $description || "Stop the daemon.",
372 additionalProperties
=> 0,
375 returns
=> { type
=> 'null' },
386 sub register_status_command
{
387 my ($self, $class, $description) = @_;
389 $class->register_method({
393 description
=> "Get daemon status.",
395 additionalProperties
=> 0,
400 enum
=> ['stopped', 'running'],
405 return $self->running() ?
'running' : 'stopped';