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