1 package PVE::ProcFSTools;
6 use Time::HiRes qw (gettimeofday);
10 my $clock_ticks = POSIX::sysconf(&POSIX::_SC_CLK_TCK);
15 my $fn = '/proc/cpuinfo';
17 return $cpuinfo if $cpuinfo;
20 user_hz => $clock_ticks,
27 my $fh = IO::File->new ($fn, "r");
32 while (defined(my $line = <$fh>)) {
33 if ($line =~ m/^processor\s*:\s*\d+\s*$/i) {
35 } elsif ($line =~ m/^model\s+name\s*:\s*(.*)\s*$/i) {
36 $res->{model} = $1 if $res->{model} eq 'unknown';
37 } elsif ($line =~ m/^cpu\s+MHz\s*:\s*(\d+\.\d+)\s*$/i) {
38 $res->{mhz} = $1 if !$res->{mhz};
39 } elsif ($line =~ m/^flags\s*:.*(vmx|svm)/) {
40 $res->{hvm} = 1; # Hardware Virtual Machine (Intel VT / AMD-V)
41 } elsif ($line =~ m/^physical id\s*:\s*(\d+)\s*$/i) {
46 $res->{sockets} = scalar(keys %$idhash) || 1;
48 $res->{cpus} = $count;
57 sub read_proc_uptime {
60 my $line = PVE::Tools::file_read_firstline("/proc/uptime");
61 if ($line && $line =~ m|^(\d+\.\d+)\s+(\d+\.\d+)\s*$|) {
63 return (int($1*$clock_ticks), int($2*$clock_ticks));
65 return (int($1), int($2));
74 my $line = PVE::Tools::file_read_firstline('/proc/loadavg');
76 if ($line =~ m|^(\d+\.\d+)\s+(\d+\.\d+)\s+(\d+\.\d+)\s+\d+/\d+\s+\d+\s*$|) {
77 return wantarray ? ($1, $2, $3) : $1;
80 return wantarray ? (0, 0, 0) : 0;
86 my $res = { user => 0, nice => 0, system => 0, idle => 0 , sum => 0};
90 if (my $fh = IO::File->new ("/proc/stat", "r")) {
91 while (defined (my $line = <$fh>)) {
92 if ($line =~ m|^cpu\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s|) {
97 $res->{used} = $1+$2+$3;
99 } elsif ($line =~ m|^cpu\d+\s|) {
106 $cpucount = 1 if !$cpucount;
108 my $ctime = gettimeofday; # floating point time in seconds
110 $res->{ctime} = $ctime;
114 $last_proc_stat = $res if !$last_proc_stat;
116 my $diff = ($ctime - $last_proc_stat->{ctime}) * $clock_ticks * $cpucount;
118 if ($diff > 1000) { # don't update too often
119 my $useddiff = $res->{used} - $last_proc_stat->{used};
120 $useddiff = $diff if $useddiff > $diff;
121 $res->{cpu} = $useddiff/$diff;
122 my $waitdiff = $res->{iowait} - $last_proc_stat->{iowait};
123 $waitdiff = $diff if $waitdiff > $diff;
124 $res->{wait} = $waitdiff/$diff;
125 $last_proc_stat = $res;
127 $res->{cpu} = $last_proc_stat->{cpu};
128 $res->{wait} = $last_proc_stat->{wait};
134 sub read_proc_pid_stat {
137 my $statstr = PVE::Tools::file_read_firstline("/proc/$pid/stat");
139 if ($statstr && $statstr =~ m/^$pid \(.*\) (\S) (-?\d+) -?\d+ -?\d+ -?\d+ -?\d+ \d+ \d+ \d+ \d+ \d+ (\d+) (\d+) (-?\d+) (-?\d+) -?\d+ -?\d+ -?\d+ 0 (\d+) (\d+) (-?\d+) \d+ \d+ \d+ \d+ \d+ \d+ \d+ \d+ \d+ \d+ \d+ \d+ \d+ -?\d+ -?\d+ \d+ \d+ \d+/) {
153 sub check_process_running {
154 my ($pid, $pstart) = @_;
156 # note: waitpid only work for child processes, but not
157 # for processes spanned by other processes.
158 # kill(0, pid) return succes for zombies.
159 # So we read the status form /proc/$pid/stat instead
161 my $info = read_proc_pid_stat($pid);
163 return $info && (!$pstart || ($info->{starttime} eq $pstart)) && ($info->{status} ne 'Z') ? $info : undef;
166 sub read_proc_starttime {
169 my $info = read_proc_pid_stat($pid);
170 return $info ? $info->{starttime} : 0;
185 my $fh = IO::File->new ("/proc/meminfo", "r");
189 while (my $line = <$fh>) {
190 if ($line =~ m/^(\S+):\s+(\d+)\s*kB/i) {
191 $d->{lc ($1)} = $2 * 1024;
196 $res->{memtotal} = $d->{memtotal};
197 $res->{memfree} = $d->{memfree} + $d->{buffers} + $d->{cached};
198 $res->{memused} = $res->{memtotal} - $res->{memfree};
200 $res->{swaptotal} = $d->{swaptotal};
201 $res->{swapfree} = $d->{swapfree};
202 $res->{swapused} = $res->{swaptotal} - $res->{swapfree};
204 my $spages = PVE::Tools::file_read_firstline("/sys/kernel/mm/ksm/pages_sharing");
205 $res->{memshared} = int($spages) * 4096;
210 # memory usage of current process
211 sub read_memory_usage {
213 my $res = { size => 0, resident => 0, shared => 0 };
217 my $line = PVE::Tools::file_read_firstline("/proc/$$/statm");
219 if ($line =~ m/^(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s*/) {
220 $res->{size} = $1*$ps;
221 $res->{resident} = $2*$ps;
222 $res->{shared} = $3*$ps;
228 sub read_proc_net_dev {
232 my $fh = IO::File->new ("/proc/net/dev", "r");
235 while (defined (my $line = <$fh>)) {
236 if ($line =~ m/^\s*(.*):\s*(\d+)\s+\d+\s+\d+\s+\d+\s+\d+\s+\d+\s+\d+\s+\d+\s+(\d+)\s+/) {
249 sub write_proc_entry {
250 my ($filename, $data) = @_;#
252 my $fh = IO::File->new($filename, O_WRONLY);
253 die "unable to open file '$filename' - $!\n" if !$fh;
254 die "unable to write '$filename' - $!\n" unless print $fh $data;
255 die "closing file '$filename' failed - $!\n" unless close $fh;
259 sub read_proc_net_route {
260 my $filename = "/proc/net/route";
264 my $fh = IO::File->new ($filename, "r");
267 my $int_to_quad = sub {
268 return join '.' => map { ($_[0] >> 8*(3-$_)) % 256 } (3, 2, 1, 0);
271 while (defined(my $line = <$fh>)) {
272 next if $line =~/^Iface\s+Destination/; # skip head
273 my ($iface, $dest, $gateway, $metric, $mask, $mtu) = (split(/\s+/, $line))[0,1,2,6,7,8];
275 dest => &$int_to_quad(hex($dest)),
276 gateway => &$int_to_quad(hex($gateway)),
277 mask => &$int_to_quad(hex($mask)),
287 sub read_proc_mounts {
288 return PVE::Tools::file_get_contents("/proc/mounts");
291 sub read_proc_net_ipv6_route {
292 my $filename = "/proc/net/ipv6_route";
296 my $fh = IO::File->new ($filename, "r");
299 my $read_v6addr = sub { s/....(?!$)/$&:/g };
301 # ipv6_route has no header
302 while (defined(my $line = <$fh>)) {
303 my ($dest, $prefix, $nexthop, $metric, $iface) = (split(/\s+/, $line))[0,1,4,5,9];
305 dest => &$read_v6addr($dest),
307 gateway => &$read_v6addr($nexthop),