]>
git.proxmox.com Git - pve-common.git/blob - data/PVE/Daemon.pm
279a78c1f36fce16394c9b06b3b9bba6963061c2
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 # * restart by stop/start, exec, or signal HUP
10 # * daemon restart on error (option 'restart_on_error')
18 use POSIX
":sys_wait_h";
21 use Time
::HiRes qw
(gettimeofday
);
23 use base
qw(PVE::CLIHandler);
25 $SIG{'__WARN__'} = sub {
30 syslog
('warning', "WARNING: %s", $t);
34 $ENV{'PATH'} = '/sbin:/bin:/usr/sbin:/usr/bin';
36 my $daemon_initialized = 0; # we only allow one instance
38 my $lockpidfile = sub {
41 my $lkfn = $self->{pidfile
} . ".lock";
43 if (!open (FLCK
, ">>$lkfn")) {
44 my $msg = "can't aquire lock on file '$lkfn' - $!";
49 if (!flock (FLCK
, LOCK_EX
|LOCK_NB
)) {
51 my $msg = "can't aquire lock '$lkfn' - $!";
57 my $writepidfile = sub {
60 my $pidfile = $self->{pidfile
};
62 if (!open (PIDFH
, ">$pidfile")) {
63 my $msg = "can't open pid file '$pidfile' - $!";
71 my $server_cleanup = sub {
74 unlink $self->{pidfile
} . ".lock";
75 unlink $self->{pidfile
};
78 my $server_run = sub {
79 my ($self, $debug) = @_;
86 my $restart = $ENV{RESTART_PVE_DAEMON
};
88 delete $ENV{RESTART_PVE_DAEMON
};
90 $self->{debug
} = 1 if $debug;
95 open STDIN
, '</dev/null' || die "can't read /dev/null";
96 open STDOUT
, '>/dev/null' || die "can't write /dev/null";
99 if (!$restart && !$debug) {
100 PVE
::INotify
::inotify_close
();
102 if (!defined ($spid)) {
103 my $msg = "can't put server into background - fork failed";
106 } elsif ($spid) { # parent
109 PVE
::INotify
::inotify_init
();
112 &$writepidfile($self);
117 syslog
('info' , "restarting server");
119 syslog
('info' , "starting server");
122 open STDERR
, '>&STDOUT' || die "can't close STDERR\n";
124 $SIG{INT
} = $SIG{TERM
} = $SIG{QUIT
} = sub {
125 $SIG{INT
} = 'DEFAULT';
127 eval { $self->shutdown(); };
130 &$server_cleanup($self);
133 if ($self->can('hup')) {
135 eval { $self->hup() };
140 eval { $self->run() };
144 syslog
('err', "ERROR: $err");
145 if (my $wait_time = $self->{restart_on_error
}) {
146 $self->restart_daemon($wait_time);
148 $self->exit_daemon(-1);
152 $self->exit_daemon(0);
156 my ($this, $name, $cmdline, %params) = @_;
158 die "please run as root\n" if $> != 0;
160 die "missing name" if !$name;
162 die "can't create more that one PVE::Daemon" if $daemon_initialized;
163 $daemon_initialized = 1;
165 PVE
::INotify
::inotify_init
();
169 my $class = ref($this) || $this;
173 run_dir
=> '/var/run',
176 foreach my $opt (keys %params) {
177 my $value = $params{$opt};
178 if ($opt eq 'restart_on_error') {
179 $self->{$opt} = $value;
180 } elsif ($opt eq 'stop_wait_time') {
181 $self->{$opt} = $value;
182 } elsif ($opt eq 'run_dir') {
183 $self->{$opt} = $value;
185 die "unknown option '$opt'";
189 $self->{pidfile
} = "$self->{run_dir}/${name}.pid";
191 $self->{nodename
} = PVE
::INotify
::nodename
();
193 $self->{cmdline
} = $cmdline;
202 my ($self, $status) = @_;
204 syslog
("info", "server stopped");
206 &$server_cleanup($self);
212 my ($self, $waittime) = @_;
214 syslog
('info', "server shutdown (restart)");
216 $ENV{RESTART_PVE_DAEMON
} = 1;
218 sleep($waittime) if $waittime; # avoid high server load due to restarts
220 PVE
::INotify
::inotify_close
();
222 exec (@{$self->{cmdline
}});
224 exit (-1); # never reached?
227 # please overwrite in subclass
228 # this is called at startup - before forking
234 # please overwrite in subclass
238 syslog
('info' , "server closing");
241 1 while (waitpid(-1, POSIX
::WNOHANG
()) > 0);
244 # please define in subclass
248 # syslog('info' , "received signal HUP (restart)");
251 # please overwrite in subclass
256 syslog
('info' , "server is running");
262 my ($self, $debug) = @_;
264 &$server_run($self, $debug);
270 my $pid_str = PVE
::Tools
::file_read_firstline
($self->{pidfile
});
272 return 0 if !$pid_str;
274 return 0 if $pid_str !~ m/^(\d+)$/; # untaint
284 my $pid = &$read_pid($self);
287 my $res = PVE
::ProcFSTools
::check_process_running
($pid) ?
1 : 0;
288 return wantarray ?
($res, $pid) : $res;
291 return wantarray ?
(0, 0) : 0;
297 my $pid = &$read_pid($self);
301 if (PVE
::ProcFSTools
::check_process_running
($pid)) {
302 kill(15, $pid); # send TERM signal
304 my $wait_time = $self->{stop_wait_time
} || 5;
306 for (my $i = 0; $i < $wait_time; $i++) {
307 $running = PVE
::ProcFSTools
::check_process_running
($pid);
312 syslog
('err', "server still running - send KILL") if $running;
319 if (-f
$self->{pidfile
}) {
320 # try to get the lock
321 &$lockpidfile($self);
322 &$server_cleanup($self);
326 sub register_start_command
{
327 my ($self, $class, $description) = @_;
329 $class->register_method({
333 description
=> $description || "Start the daemon.",
335 additionalProperties
=> 0,
338 description
=> "Debug mode - stay in foreground",
345 returns
=> { type
=> 'null' },
350 $self->start($param->{debug
});
356 my $reload_daemon = sub {
357 my ($self, $use_hup) = @_;
359 if (my $restart = $ENV{RESTART_PVE_DAEMON
}) {
362 my ($running, $pid) = $self->running();
376 sub register_restart_command
{
377 my ($self, $class, $use_hup, $description) = @_;
379 $class->register_method({
383 description
=> $description || "Restart the daemon (or start if not running).",
385 additionalProperties
=> 0,
388 returns
=> { type
=> 'null' },
393 &$reload_daemon($self, $use_hup);
399 sub register_reload_command
{
400 my ($self, $class, $description) = @_;
402 $class->register_method({
406 description
=> $description || "Reload daemon configuration (or start if not running).",
408 additionalProperties
=> 0,
411 returns
=> { type
=> 'null' },
416 &$reload_daemon($self, 1);
422 sub register_stop_command
{
423 my ($self, $class, $description) = @_;
425 $class->register_method({
429 description
=> $description || "Stop the daemon.",
431 additionalProperties
=> 0,
434 returns
=> { type
=> 'null' },
445 sub register_status_command
{
446 my ($self, $class, $description) = @_;
448 $class->register_method({
452 description
=> "Get daemon status.",
454 additionalProperties
=> 0,
459 enum
=> ['stopped', 'running'],
464 return $self->running() ?
'running' : 'stopped';