]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/basic/util.c
tree-wide: use typesafe_qsort_r()
[thirdparty/systemd.git] / src / basic / util.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
a7334b09 2
11c3a366 3#include <alloca.h>
60918275 4#include <errno.h>
f6c2284a 5#include <fcntl.h>
f6c2284a
LP
6#include <sched.h>
7#include <signal.h>
8#include <stdarg.h>
9#include <stdio.h>
10#include <stdlib.h>
11#include <string.h>
87d2c1ff 12#include <sys/mman.h>
f6c2284a 13#include <sys/prctl.h>
11c3a366
TA
14#include <sys/statfs.h>
15#include <sys/sysmacros.h>
f6c2284a 16#include <sys/types.h>
f6c2284a 17#include <unistd.h>
eef46c37 18
b5efdb8a 19#include "alloc-util.h"
c43b2b9c 20#include "btrfs-util.h"
3f6fd1ba 21#include "build.h"
d9ab2bcf 22#include "cgroup-util.h"
f6c2284a 23#include "def.h"
553e15f2 24#include "device-nodes.h"
cf0fbc49 25#include "dirent-util.h"
3ffd4af2 26#include "fd-util.h"
f6c2284a 27#include "fileio.h"
f97b34a6 28#include "format-util.h"
f6c2284a
LP
29#include "hashmap.h"
30#include "hostname-util.h"
a9f5d454 31#include "log.h"
f6c2284a
LP
32#include "macro.h"
33#include "missing.h"
6bedfcbb 34#include "parse-util.h"
9eb977db 35#include "path-util.h"
0b452006 36#include "process-util.h"
1e7da35b 37#include "procfs-util.h"
11c3a366 38#include "set.h"
93cc7779 39#include "signal-util.h"
cf0fbc49 40#include "stat-util.h"
07630cea 41#include "string-util.h"
f6c2284a 42#include "strv.h"
93cc7779 43#include "time-util.h"
8612da97 44#include "umask-util.h"
b1d4f8e1 45#include "user-util.h"
4f5dd394 46#include "util.h"
9ce17593 47#include "virt.h"
56cf987f 48
9a0e6896
LP
49int saved_argc = 0;
50char **saved_argv = NULL;
dcd61450 51static int saved_in_initrd = -1;
9086e840 52
37f85e66 53size_t page_size(void) {
ec202eae 54 static thread_local size_t pgsz = 0;
37f85e66 55 long r;
56
87d2c1ff 57 if (_likely_(pgsz > 0))
37f85e66 58 return pgsz;
59
e67f47e5
LP
60 r = sysconf(_SC_PAGESIZE);
61 assert(r > 0);
37f85e66 62
63 pgsz = (size_t) r;
37f85e66 64 return pgsz;
65}
66
a88c8750
TG
67bool plymouth_running(void) {
68 return access("/run/plymouth/pid", F_OK) >= 0;
69}
70
4d6d6518
LP
71bool display_is_local(const char *display) {
72 assert(display);
73
74 return
75 display[0] == ':' &&
76 display[1] >= '0' &&
77 display[1] <= '9';
78}
79
65457142 80bool kexec_loaded(void) {
c47f86e6
ZJS
81 _cleanup_free_ char *s = NULL;
82
83 if (read_one_line_file("/sys/kernel/kexec_loaded", &s) < 0)
84 return false;
85
86 return s[0] == '1';
65457142 87}
fb9de93d 88
87d2c1ff
LP
89int prot_from_flags(int flags) {
90
91 switch (flags & O_ACCMODE) {
92
93 case O_RDONLY:
94 return PROT_READ;
95
96 case O_WRONLY:
97 return PROT_WRITE;
98
99 case O_RDWR:
100 return PROT_READ|PROT_WRITE;
101
102 default:
103 return -EINVAL;
104 }
7c99e0c1 105}
689b9a22 106
9be346c9 107bool in_initrd(void) {
825c6fe5 108 struct statfs s;
8f33b5b8 109
dcd61450
IS
110 if (saved_in_initrd >= 0)
111 return saved_in_initrd;
825c6fe5
LP
112
113 /* We make two checks here:
114 *
115 * 1. the flag file /etc/initrd-release must exist
116 * 2. the root file system must be a memory file system
117 *
118 * The second check is extra paranoia, since misdetecting an
629ff674 119 * initrd can have bad consequences due the initrd
825c6fe5
LP
120 * emptying when transititioning to the main systemd.
121 */
122
dcd61450
IS
123 saved_in_initrd = access("/etc/initrd-release", F_OK) >= 0 &&
124 statfs("/", &s) >= 0 &&
125 is_temporary_fs(&s);
9be346c9 126
dcd61450
IS
127 return saved_in_initrd;
128}
129
130void in_initrd_force(bool value) {
131 saved_in_initrd = value;
9be346c9 132}
069cfc85 133
a9e12476
KS
134/* hey glibc, APIs with callbacks without a user pointer are so useless */
135void *xbsearch_r(const void *key, const void *base, size_t nmemb, size_t size,
1c574591 136 int (*compar) (const void *, const void *, void *), void *arg) {
a9e12476
KS
137 size_t l, u, idx;
138 const void *p;
139 int comparison;
140
2901f4b3
LP
141 assert(!size_multiply_overflow(nmemb, size));
142
a9e12476
KS
143 l = 0;
144 u = nmemb;
145 while (l < u) {
146 idx = (l + u) / 2;
2901f4b3 147 p = (const uint8_t*) base + idx * size;
a9e12476
KS
148 comparison = compar(key, p, arg);
149 if (comparison < 0)
150 u = idx;
151 else if (comparison > 0)
152 l = idx + 1;
153 else
154 return (void *)p;
155 }
156 return NULL;
157}
09017585 158
240dbaa4
LP
159int on_ac_power(void) {
160 bool found_offline = false, found_online = false;
161 _cleanup_closedir_ DIR *d = NULL;
8fb3f009 162 struct dirent *de;
240dbaa4
LP
163
164 d = opendir("/sys/class/power_supply");
165 if (!d)
6d890034 166 return errno == ENOENT ? true : -errno;
240dbaa4 167
8fb3f009 168 FOREACH_DIRENT(de, d, return -errno) {
240dbaa4
LP
169 _cleanup_close_ int fd = -1, device = -1;
170 char contents[6];
171 ssize_t n;
240dbaa4 172
240dbaa4
LP
173 device = openat(dirfd(d), de->d_name, O_DIRECTORY|O_RDONLY|O_CLOEXEC|O_NOCTTY);
174 if (device < 0) {
3742095b 175 if (IN_SET(errno, ENOENT, ENOTDIR))
240dbaa4
LP
176 continue;
177
178 return -errno;
179 }
180
181 fd = openat(device, "type", O_RDONLY|O_CLOEXEC|O_NOCTTY);
182 if (fd < 0) {
183 if (errno == ENOENT)
184 continue;
185
186 return -errno;
187 }
188
189 n = read(fd, contents, sizeof(contents));
190 if (n < 0)
191 return -errno;
192
193 if (n != 6 || memcmp(contents, "Mains\n", 6))
194 continue;
195
03e334a1 196 safe_close(fd);
240dbaa4
LP
197 fd = openat(device, "online", O_RDONLY|O_CLOEXEC|O_NOCTTY);
198 if (fd < 0) {
199 if (errno == ENOENT)
200 continue;
201
202 return -errno;
203 }
204
205 n = read(fd, contents, sizeof(contents));
206 if (n < 0)
207 return -errno;
208
209 if (n != 2 || contents[1] != '\n')
210 return -EIO;
211
212 if (contents[0] == '1') {
213 found_online = true;
214 break;
215 } else if (contents[0] == '0')
216 found_offline = true;
217 else
218 return -EIO;
219 }
220
221 return found_online || !found_offline;
222}
fabe5c0e 223
bc9fd78c
LP
224int container_get_leader(const char *machine, pid_t *pid) {
225 _cleanup_free_ char *s = NULL, *class = NULL;
226 const char *p;
227 pid_t leader;
228 int r;
229
230 assert(machine);
231 assert(pid);
232
1e5057b9
LP
233 if (streq(machine, ".host")) {
234 *pid = 1;
235 return 0;
236 }
237
b9a8d250
LP
238 if (!machine_name_is_valid(machine))
239 return -EINVAL;
240
63c372cb 241 p = strjoina("/run/systemd/machines/", machine);
1a5a177e 242 r = parse_env_file(NULL, p, NEWLINE, "LEADER", &s, "CLASS", &class, NULL);
bc9fd78c
LP
243 if (r == -ENOENT)
244 return -EHOSTDOWN;
245 if (r < 0)
246 return r;
247 if (!s)
248 return -EIO;
249
250 if (!streq_ptr(class, "container"))
251 return -EIO;
252
253 r = parse_pid(s, &leader);
254 if (r < 0)
255 return r;
256 if (leader <= 1)
257 return -EIO;
258
259 *pid = leader;
260 return 0;
261}
262
671c3419
RM
263int namespace_open(pid_t pid, int *pidns_fd, int *mntns_fd, int *netns_fd, int *userns_fd, int *root_fd) {
264 _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, netnsfd = -1, usernsfd = -1;
359a06aa 265 int rfd = -1;
bc9fd78c
LP
266
267 assert(pid >= 0);
bc9fd78c 268
878cd7e9
LP
269 if (mntns_fd) {
270 const char *mntns;
a4475f57 271
878cd7e9
LP
272 mntns = procfs_file_alloca(pid, "ns/mnt");
273 mntnsfd = open(mntns, O_RDONLY|O_NOCTTY|O_CLOEXEC);
274 if (mntnsfd < 0)
275 return -errno;
276 }
bc9fd78c 277
878cd7e9
LP
278 if (pidns_fd) {
279 const char *pidns;
280
281 pidns = procfs_file_alloca(pid, "ns/pid");
282 pidnsfd = open(pidns, O_RDONLY|O_NOCTTY|O_CLOEXEC);
283 if (pidnsfd < 0)
284 return -errno;
285 }
286
287 if (netns_fd) {
288 const char *netns;
289
290 netns = procfs_file_alloca(pid, "ns/net");
291 netnsfd = open(netns, O_RDONLY|O_NOCTTY|O_CLOEXEC);
292 if (netnsfd < 0)
293 return -errno;
294 }
295
671c3419
RM
296 if (userns_fd) {
297 const char *userns;
298
299 userns = procfs_file_alloca(pid, "ns/user");
300 usernsfd = open(userns, O_RDONLY|O_NOCTTY|O_CLOEXEC);
301 if (usernsfd < 0 && errno != ENOENT)
302 return -errno;
303 }
304
878cd7e9
LP
305 if (root_fd) {
306 const char *root;
307
308 root = procfs_file_alloca(pid, "root");
309 rfd = open(root, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
310 if (rfd < 0)
311 return -errno;
312 }
313
314 if (pidns_fd)
315 *pidns_fd = pidnsfd;
bc9fd78c 316
878cd7e9
LP
317 if (mntns_fd)
318 *mntns_fd = mntnsfd;
319
320 if (netns_fd)
321 *netns_fd = netnsfd;
322
671c3419
RM
323 if (userns_fd)
324 *userns_fd = usernsfd;
325
878cd7e9
LP
326 if (root_fd)
327 *root_fd = rfd;
328
671c3419 329 pidnsfd = mntnsfd = netnsfd = usernsfd = -1;
bc9fd78c
LP
330
331 return 0;
332}
333
671c3419
RM
334int namespace_enter(int pidns_fd, int mntns_fd, int netns_fd, int userns_fd, int root_fd) {
335 if (userns_fd >= 0) {
336 /* Can't setns to your own userns, since then you could
337 * escalate from non-root to root in your own namespace, so
338 * check if namespaces equal before attempting to enter. */
339 _cleanup_free_ char *userns_fd_path = NULL;
340 int r;
341 if (asprintf(&userns_fd_path, "/proc/self/fd/%d", userns_fd) < 0)
342 return -ENOMEM;
343
e3f791a2 344 r = files_same(userns_fd_path, "/proc/self/ns/user", 0);
671c3419
RM
345 if (r < 0)
346 return r;
347 if (r)
348 userns_fd = -1;
349 }
bc9fd78c 350
878cd7e9
LP
351 if (pidns_fd >= 0)
352 if (setns(pidns_fd, CLONE_NEWPID) < 0)
353 return -errno;
a4475f57 354
878cd7e9
LP
355 if (mntns_fd >= 0)
356 if (setns(mntns_fd, CLONE_NEWNS) < 0)
357 return -errno;
bc9fd78c 358
878cd7e9
LP
359 if (netns_fd >= 0)
360 if (setns(netns_fd, CLONE_NEWNET) < 0)
361 return -errno;
bc9fd78c 362
671c3419
RM
363 if (userns_fd >= 0)
364 if (setns(userns_fd, CLONE_NEWUSER) < 0)
365 return -errno;
366
878cd7e9
LP
367 if (root_fd >= 0) {
368 if (fchdir(root_fd) < 0)
369 return -errno;
370
371 if (chroot(".") < 0)
372 return -errno;
373 }
bc9fd78c 374
b4da6d6b 375 return reset_uid_gid();
bc9fd78c 376}
bf108e55 377
1c231f56 378uint64_t physical_memory(void) {
d9ab2bcf
LP
379 _cleanup_free_ char *root = NULL, *value = NULL;
380 uint64_t mem, lim;
381 size_t ps;
382 long sc;
bd969ee6 383 int r;
1c231f56 384
d9ab2bcf
LP
385 /* We return this as uint64_t in case we are running as 32bit process on a 64bit kernel with huge amounts of
386 * memory.
387 *
388 * In order to support containers nicely that have a configured memory limit we'll take the minimum of the
389 * physically reported amount of memory and the limit configured for the root cgroup, if there is any. */
390
391 sc = sysconf(_SC_PHYS_PAGES);
392 assert(sc > 0);
393
394 ps = page_size();
395 mem = (uint64_t) sc * (uint64_t) ps;
396
bd969ee6
LP
397 r = cg_get_root_path(&root);
398 if (r < 0) {
399 log_debug_errno(r, "Failed to determine root cgroup, ignoring cgroup memory limit: %m");
d9ab2bcf 400 return mem;
bd969ee6 401 }
d9ab2bcf 402
bd969ee6
LP
403 r = cg_all_unified();
404 if (r < 0) {
405 log_debug_errno(r, "Failed to determine root unified mode, ignoring cgroup memory limit: %m");
d9ab2bcf 406 return mem;
bd969ee6
LP
407 }
408 if (r > 0) {
409 r = cg_get_attribute("memory", root, "memory.max", &value);
410 if (r < 0) {
411 log_debug_errno(r, "Failed to read memory.max cgroup attribute, ignoring cgroup memory limit: %m");
412 return mem;
413 }
d9ab2bcf 414
bd969ee6
LP
415 if (streq(value, "max"))
416 return mem;
417 } else {
418 r = cg_get_attribute("memory", root, "memory.limit_in_bytes", &value);
419 if (r < 0) {
420 log_debug_errno(r, "Failed to read memory.limit_in_bytes cgroup attribute, ignoring cgroup memory limit: %m");
421 return mem;
422 }
423 }
424
425 r = safe_atou64(value, &lim);
426 if (r < 0) {
427 log_debug_errno(r, "Failed to parse cgroup memory limit '%s', ignoring: %m", value);
428 return mem;
429 }
430 if (lim == UINT64_MAX)
d9ab2bcf 431 return mem;
1c231f56 432
d9ab2bcf
LP
433 /* Make sure the limit is a multiple of our own page size */
434 lim /= ps;
435 lim *= ps;
1c231f56 436
d9ab2bcf 437 return MIN(mem, lim);
1c231f56 438}
6db615c1 439
d8cf2ac7
LP
440uint64_t physical_memory_scale(uint64_t v, uint64_t max) {
441 uint64_t p, m, ps, r;
442
443 assert(max > 0);
444
445 /* Returns the physical memory size, multiplied by v divided by max. Returns UINT64_MAX on overflow. On success
446 * the result is a multiple of the page size (rounds down). */
447
448 ps = page_size();
449 assert(ps > 0);
450
451 p = physical_memory() / ps;
452 assert(p > 0);
453
454 m = p * v;
455 if (m / p != v)
456 return UINT64_MAX;
457
458 m /= max;
459
460 r = m * ps;
461 if (r / ps != m)
462 return UINT64_MAX;
463
464 return r;
465}
466
83f8e808
LP
467uint64_t system_tasks_max(void) {
468
83f8e808 469 uint64_t a = TASKS_MAX, b = TASKS_MAX;
1e7da35b 470 _cleanup_free_ char *root = NULL;
0f578ea2 471 int r;
83f8e808
LP
472
473 /* Determine the maximum number of tasks that may run on this system. We check three sources to determine this
474 * limit:
475 *
f3a367d6 476 * a) the maximum tasks value the kernel allows on this architecture
83f8e808 477 * b) the cgroups pids_max attribute for the system
f3a367d6 478 * c) the kernel's configured maximum PID value
83f8e808
LP
479 *
480 * And then pick the smallest of the three */
481
0f578ea2
LP
482 r = procfs_tasks_get_limit(&a);
483 if (r < 0)
484 log_debug_errno(r, "Failed to read maximum number of tasks from /proc, ignoring: %m");
83f8e808 485
0f578ea2
LP
486 r = cg_get_root_path(&root);
487 if (r < 0)
488 log_debug_errno(r, "Failed to determine cgroup root path, ignoring: %m");
489 else {
1e7da35b 490 _cleanup_free_ char *value = NULL;
83f8e808 491
0f578ea2
LP
492 r = cg_get_attribute("pids", root, "pids.max", &value);
493 if (r < 0)
494 log_debug_errno(r, "Failed to read pids.max attribute of cgroup root, ignoring: %m");
495 else if (!streq(value, "max")) {
496 r = safe_atou64(value, &b);
497 if (r < 0)
498 log_debug_errno(r, "Failed to parse pids.max attribute of cgroup root, ignoring: %m");
499 }
83f8e808
LP
500 }
501
502 return MIN3(TASKS_MAX,
503 a <= 0 ? TASKS_MAX : a,
504 b <= 0 ? TASKS_MAX : b);
505}
506
507uint64_t system_tasks_max_scale(uint64_t v, uint64_t max) {
508 uint64_t t, m;
509
510 assert(max > 0);
511
512 /* Multiply the system's task value by the fraction v/max. Hence, if max==100 this calculates percentages
513 * relative to the system's maximum number of tasks. Returns UINT64_MAX on overflow. */
514
515 t = system_tasks_max();
516 assert(t > 0);
517
518 m = t * v;
519 if (m / t != v) /* overflow? */
520 return UINT64_MAX;
521
522 return m / max;
523}
524
3f6fd1ba
LP
525int version(void) {
526 puts(PACKAGE_STRING "\n"
527 SYSTEMD_FEATURES);
528 return 0;
529}
68c58c67
LP
530
531/* This is a direct translation of str_verscmp from boot.c */
532static bool is_digit(int c) {
533 return c >= '0' && c <= '9';
534}
535
536static int c_order(int c) {
537 if (c == 0 || is_digit(c))
538 return 0;
539
540 if ((c >= 'a') && (c <= 'z'))
541 return c;
542
543 return c + 0x10000;
544}
545
546int str_verscmp(const char *s1, const char *s2) {
547 const char *os1, *os2;
548
549 assert(s1);
550 assert(s2);
551
552 os1 = s1;
553 os2 = s2;
554
555 while (*s1 || *s2) {
556 int first;
557
558 while ((*s1 && !is_digit(*s1)) || (*s2 && !is_digit(*s2))) {
559 int order;
560
561 order = c_order(*s1) - c_order(*s2);
562 if (order != 0)
563 return order;
564 s1++;
565 s2++;
566 }
567
568 while (*s1 == '0')
569 s1++;
570 while (*s2 == '0')
571 s2++;
572
573 first = 0;
574 while (is_digit(*s1) && is_digit(*s2)) {
575 if (first == 0)
576 first = *s1 - *s2;
577 s1++;
578 s2++;
579 }
580
581 if (is_digit(*s1))
582 return 1;
583 if (is_digit(*s2))
584 return -1;
585
586 if (first != 0)
587 return first;
588 }
589
590 return strcmp(os1, os2);
591}
9ce17593
JK
592
593/* Turn off core dumps but only if we're running outside of a container. */
e557b1a6
LP
594void disable_coredumps(void) {
595 int r;
596
597 if (detect_container() > 0)
598 return;
599
600 r = write_string_file("/proc/sys/kernel/core_pattern", "|/bin/false", 0);
601 if (r < 0)
602 log_debug_errno(r, "Failed to turn off coredumps, ignoring: %m");
9ce17593 603}