]>
git.ipfire.org Git - thirdparty/systemd.git/blob - src/basic/util.c
d1580895384e10756d7b93cf7a4eac2dc57cd2d4
1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2010 Lennart Poettering
18 #include <sys/prctl.h>
19 #include <sys/statfs.h>
20 #include <sys/sysmacros.h>
21 #include <sys/types.h>
24 #include "alloc-util.h"
25 #include "btrfs-util.h"
27 #include "cgroup-util.h"
29 #include "device-nodes.h"
30 #include "dirent-util.h"
33 #include "format-util.h"
35 #include "hostname-util.h"
39 #include "parse-util.h"
40 #include "path-util.h"
41 #include "process-util.h"
42 #include "procfs-util.h"
44 #include "signal-util.h"
45 #include "stat-util.h"
46 #include "string-util.h"
48 #include "time-util.h"
49 #include "umask-util.h"
50 #include "user-util.h"
55 char **saved_argv
= NULL
;
56 static int saved_in_initrd
= -1;
58 size_t page_size(void) {
59 static thread_local
size_t pgsz
= 0;
62 if (_likely_(pgsz
> 0))
65 r
= sysconf(_SC_PAGESIZE
);
72 bool plymouth_running(void) {
73 return access("/run/plymouth/pid", F_OK
) >= 0;
76 bool display_is_local(const char *display
) {
85 int socket_from_display(const char *display
, char **path
) {
92 if (!display_is_local(display
))
95 k
= strspn(display
+1, "0123456789");
97 f
= new(char, STRLEN("/tmp/.X11-unix/X") + k
+ 1);
101 c
= stpcpy(f
, "/tmp/.X11-unix/X");
102 memcpy(c
, display
+1, k
);
110 bool kexec_loaded(void) {
111 _cleanup_free_
char *s
= NULL
;
113 if (read_one_line_file("/sys/kernel/kexec_loaded", &s
) < 0)
119 int prot_from_flags(int flags
) {
121 switch (flags
& O_ACCMODE
) {
130 return PROT_READ
|PROT_WRITE
;
137 bool in_initrd(void) {
140 if (saved_in_initrd
>= 0)
141 return saved_in_initrd
;
143 /* We make two checks here:
145 * 1. the flag file /etc/initrd-release must exist
146 * 2. the root file system must be a memory file system
148 * The second check is extra paranoia, since misdetecting an
149 * initrd can have bad consequences due the initrd
150 * emptying when transititioning to the main systemd.
153 saved_in_initrd
= access("/etc/initrd-release", F_OK
) >= 0 &&
154 statfs("/", &s
) >= 0 &&
157 return saved_in_initrd
;
160 void in_initrd_force(bool value
) {
161 saved_in_initrd
= value
;
164 /* hey glibc, APIs with callbacks without a user pointer are so useless */
165 void *xbsearch_r(const void *key
, const void *base
, size_t nmemb
, size_t size
,
166 int (*compar
) (const void *, const void *, void *), void *arg
) {
171 assert(!size_multiply_overflow(nmemb
, size
));
177 p
= (const uint8_t*) base
+ idx
* size
;
178 comparison
= compar(key
, p
, arg
);
181 else if (comparison
> 0)
189 int on_ac_power(void) {
190 bool found_offline
= false, found_online
= false;
191 _cleanup_closedir_
DIR *d
= NULL
;
194 d
= opendir("/sys/class/power_supply");
196 return errno
== ENOENT
? true : -errno
;
198 FOREACH_DIRENT(de
, d
, return -errno
) {
199 _cleanup_close_
int fd
= -1, device
= -1;
203 device
= openat(dirfd(d
), de
->d_name
, O_DIRECTORY
|O_RDONLY
|O_CLOEXEC
|O_NOCTTY
);
205 if (IN_SET(errno
, ENOENT
, ENOTDIR
))
211 fd
= openat(device
, "type", O_RDONLY
|O_CLOEXEC
|O_NOCTTY
);
219 n
= read(fd
, contents
, sizeof(contents
));
223 if (n
!= 6 || memcmp(contents
, "Mains\n", 6))
227 fd
= openat(device
, "online", O_RDONLY
|O_CLOEXEC
|O_NOCTTY
);
235 n
= read(fd
, contents
, sizeof(contents
));
239 if (n
!= 2 || contents
[1] != '\n')
242 if (contents
[0] == '1') {
245 } else if (contents
[0] == '0')
246 found_offline
= true;
251 return found_online
|| !found_offline
;
254 int container_get_leader(const char *machine
, pid_t
*pid
) {
255 _cleanup_free_
char *s
= NULL
, *class = NULL
;
263 if (!machine_name_is_valid(machine
))
266 p
= strjoina("/run/systemd/machines/", machine
);
267 r
= parse_env_file(NULL
, p
, NEWLINE
, "LEADER", &s
, "CLASS", &class, NULL
);
275 if (!streq_ptr(class, "container"))
278 r
= parse_pid(s
, &leader
);
288 int namespace_open(pid_t pid
, int *pidns_fd
, int *mntns_fd
, int *netns_fd
, int *userns_fd
, int *root_fd
) {
289 _cleanup_close_
int pidnsfd
= -1, mntnsfd
= -1, netnsfd
= -1, usernsfd
= -1;
297 mntns
= procfs_file_alloca(pid
, "ns/mnt");
298 mntnsfd
= open(mntns
, O_RDONLY
|O_NOCTTY
|O_CLOEXEC
);
306 pidns
= procfs_file_alloca(pid
, "ns/pid");
307 pidnsfd
= open(pidns
, O_RDONLY
|O_NOCTTY
|O_CLOEXEC
);
315 netns
= procfs_file_alloca(pid
, "ns/net");
316 netnsfd
= open(netns
, O_RDONLY
|O_NOCTTY
|O_CLOEXEC
);
324 userns
= procfs_file_alloca(pid
, "ns/user");
325 usernsfd
= open(userns
, O_RDONLY
|O_NOCTTY
|O_CLOEXEC
);
326 if (usernsfd
< 0 && errno
!= ENOENT
)
333 root
= procfs_file_alloca(pid
, "root");
334 rfd
= open(root
, O_RDONLY
|O_NOCTTY
|O_CLOEXEC
|O_DIRECTORY
);
349 *userns_fd
= usernsfd
;
354 pidnsfd
= mntnsfd
= netnsfd
= usernsfd
= -1;
359 int namespace_enter(int pidns_fd
, int mntns_fd
, int netns_fd
, int userns_fd
, int root_fd
) {
360 if (userns_fd
>= 0) {
361 /* Can't setns to your own userns, since then you could
362 * escalate from non-root to root in your own namespace, so
363 * check if namespaces equal before attempting to enter. */
364 _cleanup_free_
char *userns_fd_path
= NULL
;
366 if (asprintf(&userns_fd_path
, "/proc/self/fd/%d", userns_fd
) < 0)
369 r
= files_same(userns_fd_path
, "/proc/self/ns/user", 0);
377 if (setns(pidns_fd
, CLONE_NEWPID
) < 0)
381 if (setns(mntns_fd
, CLONE_NEWNS
) < 0)
385 if (setns(netns_fd
, CLONE_NEWNET
) < 0)
389 if (setns(userns_fd
, CLONE_NEWUSER
) < 0)
393 if (fchdir(root_fd
) < 0)
400 return reset_uid_gid();
403 uint64_t physical_memory(void) {
404 _cleanup_free_
char *root
= NULL
, *value
= NULL
;
410 /* We return this as uint64_t in case we are running as 32bit process on a 64bit kernel with huge amounts of
413 * In order to support containers nicely that have a configured memory limit we'll take the minimum of the
414 * physically reported amount of memory and the limit configured for the root cgroup, if there is any. */
416 sc
= sysconf(_SC_PHYS_PAGES
);
420 mem
= (uint64_t) sc
* (uint64_t) ps
;
422 r
= cg_get_root_path(&root
);
424 log_debug_errno(r
, "Failed to determine root cgroup, ignoring cgroup memory limit: %m");
428 r
= cg_all_unified();
430 log_debug_errno(r
, "Failed to determine root unified mode, ignoring cgroup memory limit: %m");
434 r
= cg_get_attribute("memory", root
, "memory.max", &value
);
436 log_debug_errno(r
, "Failed to read memory.max cgroup attribute, ignoring cgroup memory limit: %m");
440 if (streq(value
, "max"))
443 r
= cg_get_attribute("memory", root
, "memory.limit_in_bytes", &value
);
445 log_debug_errno(r
, "Failed to read memory.limit_in_bytes cgroup attribute, ignoring cgroup memory limit: %m");
450 r
= safe_atou64(value
, &lim
);
452 log_debug_errno(r
, "Failed to parse cgroup memory limit '%s', ignoring: %m", value
);
455 if (lim
== UINT64_MAX
)
458 /* Make sure the limit is a multiple of our own page size */
462 return MIN(mem
, lim
);
465 uint64_t physical_memory_scale(uint64_t v
, uint64_t max
) {
466 uint64_t p
, m
, ps
, r
;
470 /* Returns the physical memory size, multiplied by v divided by max. Returns UINT64_MAX on overflow. On success
471 * the result is a multiple of the page size (rounds down). */
476 p
= physical_memory() / ps
;
492 uint64_t system_tasks_max(void) {
494 uint64_t a
= TASKS_MAX
, b
= TASKS_MAX
;
495 _cleanup_free_
char *root
= NULL
;
498 /* Determine the maximum number of tasks that may run on this system. We check three sources to determine this
501 * a) the maximum tasks value the kernel allows on this architecture
502 * b) the cgroups pids_max attribute for the system
503 * c) the kernel's configured maximum PID value
505 * And then pick the smallest of the three */
507 r
= procfs_tasks_get_limit(&a
);
509 log_debug_errno(r
, "Failed to read maximum number of tasks from /proc, ignoring: %m");
511 r
= cg_get_root_path(&root
);
513 log_debug_errno(r
, "Failed to determine cgroup root path, ignoring: %m");
515 _cleanup_free_
char *value
= NULL
;
517 r
= cg_get_attribute("pids", root
, "pids.max", &value
);
519 log_debug_errno(r
, "Failed to read pids.max attribute of cgroup root, ignoring: %m");
520 else if (!streq(value
, "max")) {
521 r
= safe_atou64(value
, &b
);
523 log_debug_errno(r
, "Failed to parse pids.max attribute of cgroup root, ignoring: %m");
527 return MIN3(TASKS_MAX
,
528 a
<= 0 ? TASKS_MAX
: a
,
529 b
<= 0 ? TASKS_MAX
: b
);
532 uint64_t system_tasks_max_scale(uint64_t v
, uint64_t max
) {
537 /* Multiply the system's task value by the fraction v/max. Hence, if max==100 this calculates percentages
538 * relative to the system's maximum number of tasks. Returns UINT64_MAX on overflow. */
540 t
= system_tasks_max();
544 if (m
/ t
!= v
) /* overflow? */
551 puts(PACKAGE_STRING
"\n"
556 /* This is a direct translation of str_verscmp from boot.c */
557 static bool is_digit(int c
) {
558 return c
>= '0' && c
<= '9';
561 static int c_order(int c
) {
562 if (c
== 0 || is_digit(c
))
565 if ((c
>= 'a') && (c
<= 'z'))
571 int str_verscmp(const char *s1
, const char *s2
) {
572 const char *os1
, *os2
;
583 while ((*s1
&& !is_digit(*s1
)) || (*s2
&& !is_digit(*s2
))) {
586 order
= c_order(*s1
) - c_order(*s2
);
599 while (is_digit(*s1
) && is_digit(*s2
)) {
615 return strcmp(os1
, os2
);
618 /* Turn off core dumps but only if we're running outside of a container. */
619 void disable_coredumps(void) {
622 if (detect_container() > 0)
625 r
= write_string_file("/proc/sys/kernel/core_pattern", "|/bin/false", 0);
627 log_debug_errno(r
, "Failed to turn off coredumps, ignoring: %m");