]> git.ipfire.org Git - people/ms/pakfire.git/blob - src/libpakfire/os.c
2fa63e3604081b55c5694a94e453b199c3a46cd5
[people/ms/pakfire.git] / src / libpakfire / os.c
1 /*#############################################################################
2 # #
3 # Pakfire - The IPFire package management system #
4 # Copyright (C) 2023 Pakfire development team #
5 # #
6 # This program is free software: you can redistribute it and/or modify #
7 # it under the terms of the GNU General Public License as published by #
8 # the Free Software Foundation, either version 3 of the License, or #
9 # (at your option) any later version. #
10 # #
11 # This program is distributed in the hope that it will be useful, #
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of #
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
14 # GNU General Public License for more details. #
15 # #
16 # You should have received a copy of the GNU General Public License #
17 # along with this program. If not, see <http://www.gnu.org/licenses/>. #
18 # #
19 #############################################################################*/
20
21 #include <ctype.h>
22 #include <errno.h>
23 #include <limits.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <unistd.h>
28
29 #include <pakfire/os.h>
30 #include <pakfire/string.h>
31
32 typedef int (*pakfire_parse_line)(char* line, size_t length, void* data);
33
34 static int pakfire_parse_file(const char* path, pakfire_parse_line parse, void* data) {
35 FILE* f = NULL;
36 char* line = NULL;
37 size_t length = 0;
38 int r = 0;
39
40 ssize_t bytes_read = 0;
41
42 // Open the file
43 f = fopen(path, "r");
44 if (!f)
45 return -errno;
46
47 // Walk through the file line by line
48 for (;;) {
49 bytes_read = getline(&line, &length, f);
50 if (bytes_read < 0)
51 break;
52
53 r = parse(line, length, data);
54 if (r)
55 goto ERROR;
56 }
57
58 ERROR:
59 if (line)
60 free(line);
61 if (f)
62 fclose(f);
63
64 return r;
65 }
66
67 static int pakfire_split_line(char* line, size_t length, char** key, char** value, char delim) {
68 char* k = line;
69 char* v = NULL;
70 char* p = NULL;
71
72 // Strip any trailing whitespace
73 pakfire_string_strip(line);
74
75 if (!*line)
76 return 0;
77
78 // Find the delimiter
79 p = strchr(line, delim);
80 if (!p)
81 return -EINVAL;
82
83 // The value starts somewhere after the delimiter
84 v = p + 1;
85
86 // Replace the delimiter by NULL
87 *p = '\0';
88
89 // Strip the key
90 pakfire_string_strip(k);
91
92 // The key is empty
93 if (!*k)
94 return -EINVAL;
95
96 // Strip the value
97 pakfire_string_strip(v);
98
99 // Return the pointers
100 *key = k;
101 *value = v;
102
103 return 0;
104 }
105
106 // CPU Info
107
108 static int pakfire_parse_cpuinfo(char* line, size_t length, void* data) {
109 struct pakfire_cpuinfo* cpuinfo = data;
110 int r;
111
112 // Key & Value
113 char* k = NULL;
114 char* v = NULL;
115
116 // Split the line
117 r = pakfire_split_line(line, length, &k, &v, ':');
118 if (r)
119 return r;
120
121 // If we didn't get a result we skip this line
122 if (!k || !v)
123 return 0;
124
125 // Vendor
126 if (strcmp(k, "vendor_id") == 0) {
127 r = pakfire_string_set(cpuinfo->vendor, v);
128 if (r)
129 return r;
130
131 // Model Name
132 } else if (strcmp(k, "model name") == 0) {
133 r = pakfire_string_set(cpuinfo->model, v);
134 if (r)
135 return r;
136 }
137
138 return 0;
139 }
140
141 int pakfire_cpuinfo(struct pakfire_cpuinfo* cpuinfo) {
142 int r;
143
144 // Parse /proc/cpuinfo
145 r = pakfire_parse_file("/proc/cpuinfo", pakfire_parse_cpuinfo, cpuinfo);
146 if (r)
147 return r;
148
149 // Fetch the number of processors
150 cpuinfo->count = sysconf(_SC_NPROCESSORS_CONF);
151
152 return 0;
153 }
154
155 // CPU Stats
156
157 static int pakfire_parse_cpustat(char* line, size_t length, void* data) {
158 struct pakfire_cpustat* cpustat = data;
159 struct cpustat {
160 unsigned long int user;
161 unsigned long int nice;
162 unsigned long int system;
163 unsigned long int idle;
164 unsigned long int iowait;
165 unsigned long int irq;
166 unsigned long int softirq;
167 unsigned long int steal;
168 unsigned long int guest;
169 unsigned long int guest_nice;
170 } stat;
171 int r;
172
173 // Only care about the line of interest
174 if (pakfire_string_startswith(line, "cpu ")) {
175 r = sscanf(line, "cpu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu",
176 &stat.user, &stat.nice, &stat.system, &stat.idle, &stat.iowait,
177 &stat.irq, &stat.softirq, &stat.steal, &stat.guest, &stat.guest_nice);
178 if (r < 10)
179 return -EINVAL;
180
181 // Fetch how many ticks a second
182 unsigned long int ticks = stat.user + stat.nice + stat.system + stat.idle
183 + stat.iowait + stat.irq + stat.softirq + stat.steal + stat.guest + stat.guest_nice;
184
185 // Convert to relative terms
186 cpustat->user = (double)stat.user / ticks;
187 cpustat->nice = (double)stat.nice / ticks;
188 cpustat->system = (double)stat.system / ticks;
189 cpustat->idle = (double)stat.idle / ticks;
190 cpustat->iowait = (double)stat.iowait / ticks;
191 cpustat->irq = (double)stat.irq / ticks;
192 cpustat->softirq = (double)stat.softirq / ticks;
193 cpustat->steal = (double)stat.steal / ticks;
194 cpustat->guest = (double)stat.guest / ticks;
195 cpustat->guest_nice = (double)stat.guest_nice / ticks;
196 }
197
198 return 0;
199 }
200
201 int pakfire_cpustat(struct pakfire_cpustat* cpustat) {
202 return pakfire_parse_file("/proc/stat", pakfire_parse_cpustat, cpustat);
203 }
204
205 // Load Average
206
207 static int pakfire_parse_loadavg(char* line, size_t length, void* data) {
208 struct pakfire_loadavg* loadavg = data;
209 int r;
210
211 // Parse the first three values
212 r = sscanf(line, "%lf %lf %lf", &loadavg->load1, &loadavg->load5, &loadavg->load15);
213 if (r < 3)
214 return -EINVAL;
215
216 return 0;
217 }
218
219 int pakfire_loadavg(struct pakfire_loadavg* loadavg) {
220 return pakfire_parse_file("/proc/loadavg", pakfire_parse_loadavg, loadavg);
221 }
222
223 // Meminfo
224
225 static int pakfire_parse_meminfo_value(uint64_t* mem, const char* s) {
226 char* remainder = NULL;
227 uint64_t m = 0;
228
229 // Parse the numeric value
230 m = strtoul(s, &remainder, 10);
231 if (m == ULONG_MAX)
232 return -errno;
233
234 // Convert into bytes
235 if (strcmp(remainder, " kB") == 0) {
236 // Check if the multiplication won't overflow
237 if (m > UINT64_MAX / 1024)
238 return -EOVERFLOW;
239
240 m *= 1024;
241
242 // Fail on anything else
243 } else if (*remainder)
244 return -EINVAL;
245
246 // Store the value
247 *mem = m;
248
249 return 0;
250 }
251
252 static int pakfire_parse_meminfo(char* line, size_t length, void* data) {
253 struct pakfire_meminfo* meminfo = data;
254 int r;
255
256 // Key & Value
257 char* k = NULL;
258 char* v = NULL;
259
260 // Split the line
261 r = pakfire_split_line(line, length, &k, &v, ':');
262 if (r)
263 return r;
264
265 // If we didn't get a result we skip this line
266 if (!k || !v)
267 return 0;
268
269 // Total
270 if (strcmp(k, "MemTotal") == 0)
271 return pakfire_parse_meminfo_value(&meminfo->total, v);
272
273 // Free
274 else if (strcmp(k, "MemFree") == 0)
275 return pakfire_parse_meminfo_value(&meminfo->free, v);
276
277 // Available
278 else if (strcmp(k, "MemAvailable") == 0)
279 return pakfire_parse_meminfo_value(&meminfo->available, v);
280
281 // Buffers
282 else if (strcmp(k, "Buffers") == 0)
283 return pakfire_parse_meminfo_value(&meminfo->buffers, v);
284
285 // Cached
286 else if (strcmp(k, "Cached") == 0)
287 return pakfire_parse_meminfo_value(&meminfo->cached, v);
288
289 // Shared
290 else if (strcmp(k, "Shmem") == 0)
291 return pakfire_parse_meminfo_value(&meminfo->shared, v);
292
293 // Active
294 else if (strcmp(k, "Active") == 0)
295 return pakfire_parse_meminfo_value(&meminfo->active, v);
296
297 // Inactive
298 else if (strcmp(k, "Inactive") == 0)
299 return pakfire_parse_meminfo_value(&meminfo->inactive, v);
300
301 // Swap Total
302 else if (strcmp(k, "SwapTotal") == 0)
303 return pakfire_parse_meminfo_value(&meminfo->swap_total, v);
304
305 // Swap Free
306 else if (strcmp(k, "SwapFree") == 0)
307 return pakfire_parse_meminfo_value(&meminfo->swap_free, v);
308
309 return 0;
310 }
311
312 int pakfire_meminfo(struct pakfire_meminfo* meminfo) {
313 int r;
314
315 // Parse /proc/meminfo
316 r = pakfire_parse_file("/proc/meminfo", pakfire_parse_meminfo, meminfo);
317 if (r)
318 return r;
319
320 // Set used memory
321 meminfo->used = meminfo->total - meminfo->free;
322
323 // Set used swap
324 meminfo->swap_used = meminfo->swap_total - meminfo->swap_free;
325
326 return 0;
327 }
328
329 // Distro
330
331 static int pakfire_parse_distro(char* line, size_t length, void* data) {
332 struct pakfire_distro* distro = data;
333 int r;
334
335 // Key & Value
336 char* k = NULL;
337 char* v = NULL;
338
339 // Split the line
340 r = pakfire_split_line(line, length, &k, &v, '=');
341 if (r)
342 return r;
343
344 // If we didn't get a result we skip this line
345 if (!k || !v)
346 return 0;
347
348 // Unquote the strings
349 pakfire_string_unquote(v);
350
351 // PRETTY_NAME
352 if (strcmp(k, "PRETTY_NAME") == 0)
353 return pakfire_string_set(distro->pretty_name, v);
354
355 // NAME
356 else if (strcmp(k, "NAME") == 0)
357 return pakfire_string_set(distro->name, v);
358
359 // ID
360 else if (strcmp(k, "ID") == 0)
361 return pakfire_string_set(distro->id, v);
362
363 // VERSION
364 else if (strcmp(k, "VERSION") == 0)
365 return pakfire_string_set(distro->version, v);
366
367 // VERSION_CODENAME
368 else if (strcmp(k, "VERSION_CODENAME") == 0)
369 return pakfire_string_set(distro->version_codename, v);
370
371 // VERSION_ID
372 else if (strcmp(k, "VERSION_ID") == 0)
373 return pakfire_string_set(distro->version_id, v);
374
375 return 0;
376 }
377
378 int pakfire_distro(struct pakfire_distro* distro, const char* path) {
379 if (!path)
380 path = "/etc/os-release";
381
382 return pakfire_parse_file(path, pakfire_parse_distro, distro);
383 }
384
385 // PIDFD
386
387 static int pidfd_parse_pid(char* line, size_t length, void* data) {
388 pid_t* pid = data;
389 int r;
390
391 // Key & Value
392 char* k = NULL;
393 char* v = NULL;
394
395 // Split the line
396 r = pakfire_split_line(line, length, &k, &v, ':');
397 if (r)
398 return r;
399
400 // If we didn't get a result we skip this line
401 if (!k || !v)
402 return 0;
403
404 if (strcmp(k, "Pid") == 0)
405 *pid = strtoul(v, NULL, 10);
406
407 return 0;
408 }
409
410 int pidfd_get_pid(int pidfd, pid_t* pid) {
411 char path[PATH_MAX];
412 int r;
413
414 if (!pid)
415 return -EINVAL;
416
417 // Compose path
418 r = pakfire_string_format(path, "/proc/self/fdinfo/%d", pidfd);
419 if (r)
420 return r;
421
422 return pakfire_parse_file(path, pidfd_parse_pid, pid);
423 }