]>
git.ipfire.org Git - thirdparty/systemd.git/blob - src/basic/util.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
13 #include <sys/prctl.h>
14 #include <sys/statfs.h>
15 #include <sys/sysmacros.h>
16 #include <sys/types.h>
19 #include "alloc-util.h"
20 #include "btrfs-util.h"
22 #include "cgroup-util.h"
24 #include "device-nodes.h"
25 #include "dirent-util.h"
28 #include "format-util.h"
30 #include "hostname-util.h"
34 #include "parse-util.h"
35 #include "path-util.h"
36 #include "process-util.h"
37 #include "procfs-util.h"
39 #include "signal-util.h"
40 #include "stat-util.h"
41 #include "string-util.h"
43 #include "time-util.h"
44 #include "umask-util.h"
45 #include "user-util.h"
50 char **saved_argv
= NULL
;
51 static int saved_in_initrd
= -1;
53 size_t page_size(void) {
54 static thread_local
size_t pgsz
= 0;
57 if (_likely_(pgsz
> 0))
60 r
= sysconf(_SC_PAGESIZE
);
67 bool plymouth_running(void) {
68 return access("/run/plymouth/pid", F_OK
) >= 0;
71 bool display_is_local(const char *display
) {
80 int socket_from_display(const char *display
, char **path
) {
87 if (!display_is_local(display
))
90 k
= strspn(display
+1, "0123456789");
92 f
= new(char, STRLEN("/tmp/.X11-unix/X") + k
+ 1);
96 c
= stpcpy(f
, "/tmp/.X11-unix/X");
97 memcpy(c
, display
+1, k
);
105 bool kexec_loaded(void) {
106 _cleanup_free_
char *s
= NULL
;
108 if (read_one_line_file("/sys/kernel/kexec_loaded", &s
) < 0)
114 int prot_from_flags(int flags
) {
116 switch (flags
& O_ACCMODE
) {
125 return PROT_READ
|PROT_WRITE
;
132 bool in_initrd(void) {
135 if (saved_in_initrd
>= 0)
136 return saved_in_initrd
;
138 /* We make two checks here:
140 * 1. the flag file /etc/initrd-release must exist
141 * 2. the root file system must be a memory file system
143 * The second check is extra paranoia, since misdetecting an
144 * initrd can have bad consequences due the initrd
145 * emptying when transititioning to the main systemd.
148 saved_in_initrd
= access("/etc/initrd-release", F_OK
) >= 0 &&
149 statfs("/", &s
) >= 0 &&
152 return saved_in_initrd
;
155 void in_initrd_force(bool value
) {
156 saved_in_initrd
= value
;
159 /* hey glibc, APIs with callbacks without a user pointer are so useless */
160 void *xbsearch_r(const void *key
, const void *base
, size_t nmemb
, size_t size
,
161 int (*compar
) (const void *, const void *, void *), void *arg
) {
166 assert(!size_multiply_overflow(nmemb
, size
));
172 p
= (const uint8_t*) base
+ idx
* size
;
173 comparison
= compar(key
, p
, arg
);
176 else if (comparison
> 0)
184 int on_ac_power(void) {
185 bool found_offline
= false, found_online
= false;
186 _cleanup_closedir_
DIR *d
= NULL
;
189 d
= opendir("/sys/class/power_supply");
191 return errno
== ENOENT
? true : -errno
;
193 FOREACH_DIRENT(de
, d
, return -errno
) {
194 _cleanup_close_
int fd
= -1, device
= -1;
198 device
= openat(dirfd(d
), de
->d_name
, O_DIRECTORY
|O_RDONLY
|O_CLOEXEC
|O_NOCTTY
);
200 if (IN_SET(errno
, ENOENT
, ENOTDIR
))
206 fd
= openat(device
, "type", O_RDONLY
|O_CLOEXEC
|O_NOCTTY
);
214 n
= read(fd
, contents
, sizeof(contents
));
218 if (n
!= 6 || memcmp(contents
, "Mains\n", 6))
222 fd
= openat(device
, "online", O_RDONLY
|O_CLOEXEC
|O_NOCTTY
);
230 n
= read(fd
, contents
, sizeof(contents
));
234 if (n
!= 2 || contents
[1] != '\n')
237 if (contents
[0] == '1') {
240 } else if (contents
[0] == '0')
241 found_offline
= true;
246 return found_online
|| !found_offline
;
249 int container_get_leader(const char *machine
, pid_t
*pid
) {
250 _cleanup_free_
char *s
= NULL
, *class = NULL
;
258 if (!machine_name_is_valid(machine
))
261 p
= strjoina("/run/systemd/machines/", machine
);
262 r
= parse_env_file(NULL
, p
, NEWLINE
, "LEADER", &s
, "CLASS", &class, NULL
);
270 if (!streq_ptr(class, "container"))
273 r
= parse_pid(s
, &leader
);
283 int namespace_open(pid_t pid
, int *pidns_fd
, int *mntns_fd
, int *netns_fd
, int *userns_fd
, int *root_fd
) {
284 _cleanup_close_
int pidnsfd
= -1, mntnsfd
= -1, netnsfd
= -1, usernsfd
= -1;
292 mntns
= procfs_file_alloca(pid
, "ns/mnt");
293 mntnsfd
= open(mntns
, O_RDONLY
|O_NOCTTY
|O_CLOEXEC
);
301 pidns
= procfs_file_alloca(pid
, "ns/pid");
302 pidnsfd
= open(pidns
, O_RDONLY
|O_NOCTTY
|O_CLOEXEC
);
310 netns
= procfs_file_alloca(pid
, "ns/net");
311 netnsfd
= open(netns
, O_RDONLY
|O_NOCTTY
|O_CLOEXEC
);
319 userns
= procfs_file_alloca(pid
, "ns/user");
320 usernsfd
= open(userns
, O_RDONLY
|O_NOCTTY
|O_CLOEXEC
);
321 if (usernsfd
< 0 && errno
!= ENOENT
)
328 root
= procfs_file_alloca(pid
, "root");
329 rfd
= open(root
, O_RDONLY
|O_NOCTTY
|O_CLOEXEC
|O_DIRECTORY
);
344 *userns_fd
= usernsfd
;
349 pidnsfd
= mntnsfd
= netnsfd
= usernsfd
= -1;
354 int namespace_enter(int pidns_fd
, int mntns_fd
, int netns_fd
, int userns_fd
, int root_fd
) {
355 if (userns_fd
>= 0) {
356 /* Can't setns to your own userns, since then you could
357 * escalate from non-root to root in your own namespace, so
358 * check if namespaces equal before attempting to enter. */
359 _cleanup_free_
char *userns_fd_path
= NULL
;
361 if (asprintf(&userns_fd_path
, "/proc/self/fd/%d", userns_fd
) < 0)
364 r
= files_same(userns_fd_path
, "/proc/self/ns/user", 0);
372 if (setns(pidns_fd
, CLONE_NEWPID
) < 0)
376 if (setns(mntns_fd
, CLONE_NEWNS
) < 0)
380 if (setns(netns_fd
, CLONE_NEWNET
) < 0)
384 if (setns(userns_fd
, CLONE_NEWUSER
) < 0)
388 if (fchdir(root_fd
) < 0)
395 return reset_uid_gid();
398 uint64_t physical_memory(void) {
399 _cleanup_free_
char *root
= NULL
, *value
= NULL
;
405 /* We return this as uint64_t in case we are running as 32bit process on a 64bit kernel with huge amounts of
408 * In order to support containers nicely that have a configured memory limit we'll take the minimum of the
409 * physically reported amount of memory and the limit configured for the root cgroup, if there is any. */
411 sc
= sysconf(_SC_PHYS_PAGES
);
415 mem
= (uint64_t) sc
* (uint64_t) ps
;
417 r
= cg_get_root_path(&root
);
419 log_debug_errno(r
, "Failed to determine root cgroup, ignoring cgroup memory limit: %m");
423 r
= cg_all_unified();
425 log_debug_errno(r
, "Failed to determine root unified mode, ignoring cgroup memory limit: %m");
429 r
= cg_get_attribute("memory", root
, "memory.max", &value
);
431 log_debug_errno(r
, "Failed to read memory.max cgroup attribute, ignoring cgroup memory limit: %m");
435 if (streq(value
, "max"))
438 r
= cg_get_attribute("memory", root
, "memory.limit_in_bytes", &value
);
440 log_debug_errno(r
, "Failed to read memory.limit_in_bytes cgroup attribute, ignoring cgroup memory limit: %m");
445 r
= safe_atou64(value
, &lim
);
447 log_debug_errno(r
, "Failed to parse cgroup memory limit '%s', ignoring: %m", value
);
450 if (lim
== UINT64_MAX
)
453 /* Make sure the limit is a multiple of our own page size */
457 return MIN(mem
, lim
);
460 uint64_t physical_memory_scale(uint64_t v
, uint64_t max
) {
461 uint64_t p
, m
, ps
, r
;
465 /* Returns the physical memory size, multiplied by v divided by max. Returns UINT64_MAX on overflow. On success
466 * the result is a multiple of the page size (rounds down). */
471 p
= physical_memory() / ps
;
487 uint64_t system_tasks_max(void) {
489 uint64_t a
= TASKS_MAX
, b
= TASKS_MAX
;
490 _cleanup_free_
char *root
= NULL
;
493 /* Determine the maximum number of tasks that may run on this system. We check three sources to determine this
496 * a) the maximum tasks value the kernel allows on this architecture
497 * b) the cgroups pids_max attribute for the system
498 * c) the kernel's configured maximum PID value
500 * And then pick the smallest of the three */
502 r
= procfs_tasks_get_limit(&a
);
504 log_debug_errno(r
, "Failed to read maximum number of tasks from /proc, ignoring: %m");
506 r
= cg_get_root_path(&root
);
508 log_debug_errno(r
, "Failed to determine cgroup root path, ignoring: %m");
510 _cleanup_free_
char *value
= NULL
;
512 r
= cg_get_attribute("pids", root
, "pids.max", &value
);
514 log_debug_errno(r
, "Failed to read pids.max attribute of cgroup root, ignoring: %m");
515 else if (!streq(value
, "max")) {
516 r
= safe_atou64(value
, &b
);
518 log_debug_errno(r
, "Failed to parse pids.max attribute of cgroup root, ignoring: %m");
522 return MIN3(TASKS_MAX
,
523 a
<= 0 ? TASKS_MAX
: a
,
524 b
<= 0 ? TASKS_MAX
: b
);
527 uint64_t system_tasks_max_scale(uint64_t v
, uint64_t max
) {
532 /* Multiply the system's task value by the fraction v/max. Hence, if max==100 this calculates percentages
533 * relative to the system's maximum number of tasks. Returns UINT64_MAX on overflow. */
535 t
= system_tasks_max();
539 if (m
/ t
!= v
) /* overflow? */
546 puts(PACKAGE_STRING
"\n"
551 /* This is a direct translation of str_verscmp from boot.c */
552 static bool is_digit(int c
) {
553 return c
>= '0' && c
<= '9';
556 static int c_order(int c
) {
557 if (c
== 0 || is_digit(c
))
560 if ((c
>= 'a') && (c
<= 'z'))
566 int str_verscmp(const char *s1
, const char *s2
) {
567 const char *os1
, *os2
;
578 while ((*s1
&& !is_digit(*s1
)) || (*s2
&& !is_digit(*s2
))) {
581 order
= c_order(*s1
) - c_order(*s2
);
594 while (is_digit(*s1
) && is_digit(*s2
)) {
610 return strcmp(os1
, os2
);
613 /* Turn off core dumps but only if we're running outside of a container. */
614 void disable_coredumps(void) {
617 if (detect_container() > 0)
620 r
= write_string_file("/proc/sys/kernel/core_pattern", "|/bin/false", 0);
622 log_debug_errno(r
, "Failed to turn off coredumps, ignoring: %m");