5 use POSIX
":sys_wait_h";
8 use Time
::HiRes qw
(gettimeofday
);
9 use PVE
::Tools
qw(dir_glob_foreach file_read_firstline);
13 use PVE
::Cluster
qw(cfs_read_file);
17 use PVE
::RPCEnvironment
;
18 use PVE
::API2
::Subscription
;
20 $SIG{'__WARN__'} = sub {
24 syslog
('warning', "WARNING: %s", $t);
30 $ENV{'PATH'} = '/sbin:/bin:/usr/sbin:/usr/bin';
32 die "please run as root\n" if $> != 0;
34 my $nodename = PVE
::INotify
::nodename
();
38 if (!GetOptions
('debug' => \
$opt_debug)) {
39 die "USAGE: $0 [--debug]\n";
42 my $opt_pidfile = "/var/run/pvestatd.pid";
46 my $lkfn = "$pidfile.lock";
48 if (!open (FLCK
, ">>$lkfn")) {
49 my $msg = "can't aquire lock on file '$lkfn' - $!";
54 if (!flock (FLCK
, LOCK_EX
|LOCK_NB
)) {
56 my $msg = "can't aquire lock '$lkfn' - $!";
65 if (!open (PIDFH
, ">$pidfile")) {
66 my $msg = "can't open pid file '$pidfile' - $!";
75 lockpidfile
($opt_pidfile);
80 my $restart = $ENV{RESTART_PVESTATD
};
83 open STDIN
, '</dev/null' || die "can't read /dev/null";
84 open STDOUT
, '>/dev/null' || die "can't write /dev/null";
87 if (!$restart && !$opt_debug) {
89 if (!defined ($spid)) {
90 my $msg = "can't put server into background - fork failed";
93 } elsif ($spid) { #parent
98 writepidfile
($opt_pidfile);
100 open STDERR
, '>&STDOUT' || die "can't close STDERR\n";
103 unlink "$opt_pidfile.lock";
104 unlink "$opt_pidfile";
107 $SIG{INT
} = $SIG{TERM
} = $SIG{QUIT
} = sub {
108 syslog
('info' , "server closing");
110 $SIG{INT
} = 'DEFAULT';
113 1 while (waitpid(-1, POSIX
::WNOHANG
()) > 0);
120 PVE
::INotify
::inotify_init
();
125 syslog
('info' , "restarting server");
127 syslog
('info' , "starting server");
134 sub update_node_status
{
136 my ($avg1, $avg5, $avg15) = PVE
::ProcFSTools
::read_loadavg
();
138 my $stat = PVE
::ProcFSTools
::read_proc_stat
();
140 my $netdev = PVE
::ProcFSTools
::read_proc_net_dev
();
142 my ($uptime) = PVE
::ProcFSTools
::read_proc_uptime
();
144 my $cpuinfo = PVE
::ProcFSTools
::read_cpuinfo
();
146 my $maxcpu = $cpuinfo->{cpus
};
148 my $subinfo = PVE
::INotify
::read_file
('subscription');
149 my $sublevel = $subinfo->{level
} || '';
151 # traffic from/to physical interface cards
154 foreach my $dev (keys %$netdev) {
155 next if $dev !~ m/^eth\d+$/;
156 $netin += $netdev->{$dev}->{receive
};
157 $netout += $netdev->{$dev}->{transmit
};
160 my $meminfo = PVE
::ProcFSTools
::read_meminfo
();
162 my $dinfo = df
('/', 1); # output is bytes
166 # everything not free is considered to be used
167 my $dused = $dinfo->{blocks
} - $dinfo->{bfree
};
169 my $data = "$uptime:$sublevel:$ctime:$avg1:$maxcpu:$stat->{cpu}:$stat->{wait}:" .
170 "$meminfo->{memtotal}:$meminfo->{memused}:" .
171 "$meminfo->{swaptotal}:$meminfo->{swapused}:" .
172 "$dinfo->{blocks}:$dused:$netin:$netout";
174 PVE
::Cluster
::broadcast_rrd
("pve2-node/$nodename", $data);
177 sub update_qemu_status
{
181 my $vmstatus = PVE
::QemuServer
::vmstatus
(undef, 1);
183 foreach my $vmid (keys %$vmstatus) {
184 my $d = $vmstatus->{$vmid};
186 if ($d->{pid
}) { # running
187 $data = "$d->{uptime}:$d->{name}:$ctime:$d->{cpus}:$d->{cpu}:" .
188 "$d->{maxmem}:$d->{mem}:" .
189 "$d->{maxdisk}:$d->{disk}:" .
190 "$d->{netin}:$d->{netout}:" .
191 "$d->{diskread}:$d->{diskwrite}";
193 $data = "0:$d->{name}:$ctime:$d->{cpus}::" .
195 "$d->{maxdisk}:$d->{disk}:" .
198 PVE
::Cluster
::broadcast_rrd
("pve2-vm/$vmid", $data);
202 sub find_vzctl_console_pids
{
206 dir_glob_foreach
('/proc', '\d+', sub {
209 my $cmdline = file_read_firstline
("/proc/$pid/cmdline");
212 my @args = split(/\0/, $cmdline);
214 # serach for vzctl console <vmid>
215 return if scalar(@args) != 3;
216 return if $args[1] ne 'console';
217 return if $args[2] !~ m/^\d+$/;
218 return if $args[0] !~ m
|^(/usr/sbin
/)?vzctl
$|;
222 push @{$res->{$vmid}}, $pid;
227 sub remove_stale_openvz_consoles
{
229 my $vmstatus = PVE
::OpenVZ
::vmstatus
();
230 my $pidhash = find_vzctl_console_pids
();
232 foreach my $vmid (keys %$pidhash) {
233 next if defined($vmstatus->{$vmid});
234 syslog
('info', "remove stale vzctl console for CT $vmid");
235 foreach my $pid (@{$pidhash->{$vmid}}) {
241 sub update_openvz_status
{
245 my $vmstatus = PVE
::OpenVZ
::vmstatus
();
247 foreach my $vmid (keys %$vmstatus) {
248 my $d = $vmstatus->{$vmid};
250 if ($d->{status
} eq 'running') { # running
251 $data = "$d->{uptime}:$d->{name}:$ctime:$d->{cpus}:$d->{cpu}:" .
252 "$d->{maxmem}:$d->{mem}:" .
253 "$d->{maxdisk}:$d->{disk}:" .
254 "$d->{netin}:$d->{netout}:" .
255 "$d->{diskread}:$d->{diskwrite}";
257 $data = "0:$d->{name}:$ctime:$d->{cpus}::" .
259 "$d->{maxdisk}:$d->{disk}:" .
262 PVE
::Cluster
::broadcast_rrd
("pve2-vm/$vmid", $data);
266 sub update_storage_status
{
268 my $cfg = cfs_read_file
("storage.cfg");
272 my $info = PVE
::Storage
::storage_info
($cfg);
274 foreach my $storeid (keys %$info) {
275 my $d = $info->{$storeid};
276 next if !$d->{active
};
278 # everything not free is considered to be used
279 my $realused = $d->{total
} - $d->{avail
};
281 my $data = "$ctime:$d->{total}:$realused";
283 my $key = "pve2-storage/${nodename}/$storeid";
284 PVE
::Cluster
::broadcast_rrd
($key, $data);
290 # update worker list. This is not really required and
291 # we just call this to make sure that we have a correct
292 # list in case of an unexpected crash.
294 my $tlist = PVE
::RPCEnvironment
::active_workers
();
295 PVE
::Cluster
::broadcast_tasklist
($tlist);
298 syslog
('err', $err) if $err;
301 update_node_status
();
304 syslog
('err', "node status update error: $err") if $err;
307 update_qemu_status
();
310 syslog
('err', "qemu status update error: $err") if $err;
313 update_openvz_status
();
316 syslog
('err', "openvz status update error: $err") if $err;
319 update_storage_status
();
322 syslog
('err', "storage status update error: $err") if $err;
325 remove_stale_openvz_consoles
();
328 syslog
('err', "openvz console cleanup error: $err") if $err;
333 # do not update directly after startup, because install scripts
334 # have a problem with that
338 my $commandline = [$0, @ARGV];
343 my $waittime = shift;
345 syslog
('info', "server shutdown (restart)");
347 $ENV{RESTART_PVESTATD
} = 1;
349 sleep($waittime) if $waittime; # avoid high server load due to restarts
351 exec (@$commandline);
352 exit (-1); # never reached?
355 my $initial_memory_usage;
360 $next_update = time() + $updatetime;
363 my ($ccsec, $cusec) = gettimeofday
();
366 # syslog('info', "start status update");
367 PVE
::Cluster
::cfs_update
();
373 syslog
('err', "status update error: $err");
376 my ($ccsec_end, $cusec_end) = gettimeofday
();
377 my $cptime = ($ccsec_end-$ccsec) + ($cusec_end - $cusec)/1000000;
379 syslog
('info', sprintf("status update time (%.3f seconds)", $cptime))
385 my $mem = PVE
::ProcFSTools
::read_memory_usage
();
387 if (!defined($initial_memory_usage)) {
388 $initial_memory_usage = $mem->{resident
};
390 my $diff = $mem->{resident
} - $initial_memory_usage;
391 if ($diff > 5*1024*1024) {
392 syslog
('info', "restarting server after $cycle cycles to " .
393 "reduce memory usage (free $mem->{resident} ($diff) bytes)");
399 while ((time() < $next_update) &&
400 ($wcount < $updatetime) && # protect against time wrap
401 !$reload_config) { $wcount++; sleep (1); };
407 syslog
('err', "ERROR: $err");
419 pvestatd - PVE Status Daemon
427 Documentation is available at www.proxmox.com