]> git.proxmox.com Git - pve-common.git/blame - data/PVE/ProcFSTools.pm
allow to pass single '-' as argument.
[pve-common.git] / data / PVE / ProcFSTools.pm
CommitLineData
e143e9d8
DM
1package PVE::ProcFSTools;
2
3use strict;
4use POSIX;
5use Time::HiRes qw (gettimeofday);
6use IO::File;
7use PVE::Tools;
8
9my $clock_ticks = POSIX::sysconf(&POSIX::_SC_CLK_TCK);
10
11my $cpuinfo;
12
e143e9d8
DM
13sub read_cpuinfo {
14 my $fn = '/proc/cpuinfo';
15
16 return $cpuinfo if $cpuinfo;
17
18 my $res = {
8104dfe3 19 user_hz => $clock_ticks,
e143e9d8
DM
20 model => 'unknown',
21 mhz => 0,
22 cpus => 1,
e143e9d8
DM
23 };
24
25 my $fh = IO::File->new ($fn, "r");
26 return $res if !$fh;
27
28 my $count = 0;
29 while (defined(my $line = <$fh>)) {
30 if ($line =~ m/^processor\s*:\s*\d+\s*$/i) {
31 $count++;
32 } elsif ($line =~ m/^model\s+name\s*:\s*(.*)\s*$/i) {
33 $res->{model} = $1 if $res->{model} eq 'unknown';
34 } elsif ($line =~ m/^cpu\s+MHz\s*:\s*(\d+\.\d+)\s*$/i) {
35 $res->{mhz} = $1 if !$res->{mhz};
e143e9d8
DM
36 } elsif ($line =~ m/^flags\s*:.*(vmx|svm)/) {
37 $res->{hvm} = 1; # Hardware Virtual Machine (Intel VT / AMD-V)
38 }
39 }
40
41 $res->{cpus} = $count;
42
43 $fh->close;
44
45 $cpuinfo = $res;
46
47 return $res;
48}
49
50sub read_proc_uptime {
51 my $ticks = shift;
52
53 my $line = PVE::Tools::file_read_firstline("/proc/uptime");
54 if ($line && $line =~ m|^(\d+\.\d+)\s+(\d+\.\d+)\s*$|) {
55 if ($ticks) {
5157a3a2 56 return (int($1*$clock_ticks), int($2*$clock_ticks));
e143e9d8
DM
57 } else {
58 return (int($1), int($2));
59 }
60 }
61
62 return (0, 0);
63}
64
65sub read_loadavg {
66
67 my $line = PVE::Tools::file_read_firstline('/proc/loadavg');
68
69 if ($line =~ m|^(\d+\.\d+)\s+(\d+\.\d+)\s+(\d+\.\d+)\s+\d+/\d+\s+\d+\s*$|) {
70 return wantarray ? ($1, $2, $3) : $1;
71 }
72
73 return wantarray ? (0, 0, 0) : 0;
74}
75
76my $last_proc_stat;
77
78sub read_proc_stat {
79 my $res = { user => 0, nice => 0, system => 0, idle => 0 , sum => 0};
80
81 my $cpucount = 0;
82
83 if (my $fh = IO::File->new ("/proc/stat", "r")) {
84 while (defined (my $line = <$fh>)) {
85 if ($line =~ m|^cpu\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s|) {
86 $res->{user} = $1;
87 $res->{nice} = $2;
88 $res->{system} = $3;
89 $res->{idle} = $4;
90 $res->{used} = $1+$2+$3;
91 $res->{iowait} = $5;
92 } elsif ($line =~ m|^cpu\d+\s|) {
93 $cpucount++;
94 }
95 }
96 $fh->close;
97 }
98
99 $cpucount = 1 if !$cpucount;
100
101 my $ctime = gettimeofday; # floating point time in seconds
102
103 $res->{ctime} = $ctime;
104 $res->{cpu} = 0;
105 $res->{wait} = 0;
106
107 $last_proc_stat = $res if !$last_proc_stat;
108
109 my $diff = ($ctime - $last_proc_stat->{ctime}) * $clock_ticks * $cpucount;
110
111 if ($diff > 1000) { # don't update too often
112 my $useddiff = $res->{used} - $last_proc_stat->{used};
113 $useddiff = $diff if $useddiff > $diff;
114 $res->{cpu} = $useddiff/$diff;
115 my $waitdiff = $res->{iowait} - $last_proc_stat->{iowait};
116 $waitdiff = $diff if $waitdiff > $diff;
117 $res->{wait} = $waitdiff/$diff;
118 $last_proc_stat = $res;
119 } else {
120 $res->{cpu} = $last_proc_stat->{cpu};
121 $res->{wait} = $last_proc_stat->{wait};
122 }
123
124 return $res;
125}
126
5157a3a2 127sub read_proc_pid_stat {
e143e9d8
DM
128 my $pid = shift;
129
130 my $statstr = PVE::Tools::file_read_firstline("/proc/$pid/stat");
131
5157a3a2
DM
132 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+/) {
133 return {
134 status => $1,
135 utime => $3,
136 stime => $4,
137 starttime => $7,
138 vsize => $8,
139 rss => $9 * 4096,
140 };
e143e9d8
DM
141 }
142
5157a3a2
DM
143 return undef;
144}
145
146sub check_process_running {
147 my ($pid, $pstart) = @_;
148
149 # note: waitpid only work for child processes, but not
150 # for processes spanned by other processes.
151 # kill(0, pid) return succes for zombies.
152 # So we read the status form /proc/$pid/stat instead
153
154 my $info = read_proc_pid_stat($pid);
155
5399d422 156 return $info && (!$pstart || ($info->{starttime} eq $pstart)) && ($info->{status} ne 'Z') ? $info : undef;
5157a3a2
DM
157}
158
159sub read_proc_starttime {
160 my $pid = shift;
161
162 my $info = read_proc_pid_stat($pid);
163 return $info ? $info->{starttime} : 0;
e143e9d8
DM
164}
165
166sub read_meminfo {
167
168 my $res = {
169 memtotal => 0,
170 memfree => 0,
171 memused => 0,
172 swaptotal => 0,
173 swapfree => 0,
174 swapused => 0,
175 };
176
177 my $fh = IO::File->new ("/proc/meminfo", "r");
178 return $res if !$fh;
179
180 my $d = {};
181 while (my $line = <$fh>) {
182 if ($line =~ m/^(\S+):\s+(\d+)\s*kB/i) {
183 $d->{lc ($1)} = $2 * 1024;
184 }
185 }
186 close($fh);
187
188 $res->{memtotal} = $d->{memtotal};
189 $res->{memfree} = $d->{memfree} + $d->{buffers} + $d->{cached};
190 $res->{memused} = $res->{memtotal} - $res->{memfree};
191
192 $res->{swaptotal} = $d->{swaptotal};
193 $res->{swapfree} = $d->{swapfree};
194 $res->{swapused} = $res->{swaptotal} - $res->{swapfree};
195
196 return $res;
197}
198
199# memory usage of current process
200sub read_memory_usage {
201
202 my $res = { size => 0, resident => 0, shared => 0 };
203
204 my $ps = 4096;
205
206 my $line = PVE::Tools::file_read_firstline("/proc/$$/statm");
207
208 if ($line =~ m/^(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+/) {
209 $res->{size} = $1*$ps;
210 $res->{resident} = $2*$ps;
211 $res->{shared} = $3*$ps;
212 }
213
214 return $res;
215}
216
217sub read_proc_net_dev {
218
219 my $res = {};
220
221 my $fh = IO::File->new ("/proc/net/dev", "r");
222 return $res if !$fh;
223
224 while (defined (my $line = <$fh>)) {
225 if ($line =~ m/^\s*(.*):\s*(\d+)\s+\d+\s+\d+\s+\d+\s+\d+\s+\d+\s+\d+\s+\d+\s+(\d+)\s+/) {
226 $res->{$1} = {
227 receive => $2,
228 transmit => $3,
229 };
230 }
231 }
232
233 close($fh);
234
235 return $res;
236}
237
2381;