]>
git.ipfire.org Git - thirdparty/systemd.git/blob - src/basic/util.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2010 Lennart Poettering
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
31 #include <sys/prctl.h>
32 #include <sys/statfs.h>
33 #include <sys/sysmacros.h>
34 #include <sys/types.h>
37 #include "alloc-util.h"
38 #include "btrfs-util.h"
40 #include "cgroup-util.h"
42 #include "device-nodes.h"
43 #include "dirent-util.h"
46 #include "format-util.h"
48 #include "hostname-util.h"
52 #include "parse-util.h"
53 #include "path-util.h"
54 #include "process-util.h"
55 #include "procfs-util.h"
57 #include "signal-util.h"
58 #include "stat-util.h"
59 #include "string-util.h"
61 #include "time-util.h"
62 #include "umask-util.h"
63 #include "user-util.h"
68 char **saved_argv
= NULL
;
69 static int saved_in_initrd
= -1;
71 size_t page_size(void) {
72 static thread_local
size_t pgsz
= 0;
75 if (_likely_(pgsz
> 0))
78 r
= sysconf(_SC_PAGESIZE
);
85 bool plymouth_running(void) {
86 return access("/run/plymouth/pid", F_OK
) >= 0;
89 bool display_is_local(const char *display
) {
98 int socket_from_display(const char *display
, char **path
) {
105 if (!display_is_local(display
))
108 k
= strspn(display
+1, "0123456789");
110 f
= new(char, STRLEN("/tmp/.X11-unix/X") + k
+ 1);
114 c
= stpcpy(f
, "/tmp/.X11-unix/X");
115 memcpy(c
, display
+1, k
);
123 bool kexec_loaded(void) {
124 _cleanup_free_
char *s
= NULL
;
126 if (read_one_line_file("/sys/kernel/kexec_loaded", &s
) < 0)
132 int prot_from_flags(int flags
) {
134 switch (flags
& O_ACCMODE
) {
143 return PROT_READ
|PROT_WRITE
;
150 bool in_initrd(void) {
153 if (saved_in_initrd
>= 0)
154 return saved_in_initrd
;
156 /* We make two checks here:
158 * 1. the flag file /etc/initrd-release must exist
159 * 2. the root file system must be a memory file system
161 * The second check is extra paranoia, since misdetecting an
162 * initrd can have bad consequences due the initrd
163 * emptying when transititioning to the main systemd.
166 saved_in_initrd
= access("/etc/initrd-release", F_OK
) >= 0 &&
167 statfs("/", &s
) >= 0 &&
170 return saved_in_initrd
;
173 void in_initrd_force(bool value
) {
174 saved_in_initrd
= value
;
177 /* hey glibc, APIs with callbacks without a user pointer are so useless */
178 void *xbsearch_r(const void *key
, const void *base
, size_t nmemb
, size_t size
,
179 int (*compar
) (const void *, const void *, void *), void *arg
) {
188 p
= (const char *) base
+ idx
* size
;
189 comparison
= compar(key
, p
, arg
);
192 else if (comparison
> 0)
200 int on_ac_power(void) {
201 bool found_offline
= false, found_online
= false;
202 _cleanup_closedir_
DIR *d
= NULL
;
205 d
= opendir("/sys/class/power_supply");
207 return errno
== ENOENT
? true : -errno
;
209 FOREACH_DIRENT(de
, d
, return -errno
) {
210 _cleanup_close_
int fd
= -1, device
= -1;
214 device
= openat(dirfd(d
), de
->d_name
, O_DIRECTORY
|O_RDONLY
|O_CLOEXEC
|O_NOCTTY
);
216 if (IN_SET(errno
, ENOENT
, ENOTDIR
))
222 fd
= openat(device
, "type", O_RDONLY
|O_CLOEXEC
|O_NOCTTY
);
230 n
= read(fd
, contents
, sizeof(contents
));
234 if (n
!= 6 || memcmp(contents
, "Mains\n", 6))
238 fd
= openat(device
, "online", O_RDONLY
|O_CLOEXEC
|O_NOCTTY
);
246 n
= read(fd
, contents
, sizeof(contents
));
250 if (n
!= 2 || contents
[1] != '\n')
253 if (contents
[0] == '1') {
256 } else if (contents
[0] == '0')
257 found_offline
= true;
262 return found_online
|| !found_offline
;
265 int container_get_leader(const char *machine
, pid_t
*pid
) {
266 _cleanup_free_
char *s
= NULL
, *class = NULL
;
274 if (!machine_name_is_valid(machine
))
277 p
= strjoina("/run/systemd/machines/", machine
);
278 r
= parse_env_file(p
, NEWLINE
, "LEADER", &s
, "CLASS", &class, NULL
);
286 if (!streq_ptr(class, "container"))
289 r
= parse_pid(s
, &leader
);
299 int namespace_open(pid_t pid
, int *pidns_fd
, int *mntns_fd
, int *netns_fd
, int *userns_fd
, int *root_fd
) {
300 _cleanup_close_
int pidnsfd
= -1, mntnsfd
= -1, netnsfd
= -1, usernsfd
= -1;
308 mntns
= procfs_file_alloca(pid
, "ns/mnt");
309 mntnsfd
= open(mntns
, O_RDONLY
|O_NOCTTY
|O_CLOEXEC
);
317 pidns
= procfs_file_alloca(pid
, "ns/pid");
318 pidnsfd
= open(pidns
, O_RDONLY
|O_NOCTTY
|O_CLOEXEC
);
326 netns
= procfs_file_alloca(pid
, "ns/net");
327 netnsfd
= open(netns
, O_RDONLY
|O_NOCTTY
|O_CLOEXEC
);
335 userns
= procfs_file_alloca(pid
, "ns/user");
336 usernsfd
= open(userns
, O_RDONLY
|O_NOCTTY
|O_CLOEXEC
);
337 if (usernsfd
< 0 && errno
!= ENOENT
)
344 root
= procfs_file_alloca(pid
, "root");
345 rfd
= open(root
, O_RDONLY
|O_NOCTTY
|O_CLOEXEC
|O_DIRECTORY
);
360 *userns_fd
= usernsfd
;
365 pidnsfd
= mntnsfd
= netnsfd
= usernsfd
= -1;
370 int namespace_enter(int pidns_fd
, int mntns_fd
, int netns_fd
, int userns_fd
, int root_fd
) {
371 if (userns_fd
>= 0) {
372 /* Can't setns to your own userns, since then you could
373 * escalate from non-root to root in your own namespace, so
374 * check if namespaces equal before attempting to enter. */
375 _cleanup_free_
char *userns_fd_path
= NULL
;
377 if (asprintf(&userns_fd_path
, "/proc/self/fd/%d", userns_fd
) < 0)
380 r
= files_same(userns_fd_path
, "/proc/self/ns/user", 0);
388 if (setns(pidns_fd
, CLONE_NEWPID
) < 0)
392 if (setns(mntns_fd
, CLONE_NEWNS
) < 0)
396 if (setns(netns_fd
, CLONE_NEWNET
) < 0)
400 if (setns(userns_fd
, CLONE_NEWUSER
) < 0)
404 if (fchdir(root_fd
) < 0)
411 return reset_uid_gid();
414 uint64_t physical_memory(void) {
415 _cleanup_free_
char *root
= NULL
, *value
= NULL
;
420 /* We return this as uint64_t in case we are running as 32bit process on a 64bit kernel with huge amounts of
423 * In order to support containers nicely that have a configured memory limit we'll take the minimum of the
424 * physically reported amount of memory and the limit configured for the root cgroup, if there is any. */
426 sc
= sysconf(_SC_PHYS_PAGES
);
430 mem
= (uint64_t) sc
* (uint64_t) ps
;
432 if (cg_get_root_path(&root
) < 0)
435 if (cg_get_attribute("memory", root
, "memory.limit_in_bytes", &value
))
438 if (safe_atou64(value
, &lim
) < 0)
441 /* Make sure the limit is a multiple of our own page size */
445 return MIN(mem
, lim
);
448 uint64_t physical_memory_scale(uint64_t v
, uint64_t max
) {
449 uint64_t p
, m
, ps
, r
;
453 /* Returns the physical memory size, multiplied by v divided by max. Returns UINT64_MAX on overflow. On success
454 * the result is a multiple of the page size (rounds down). */
459 p
= physical_memory() / ps
;
475 uint64_t system_tasks_max(void) {
477 uint64_t a
= TASKS_MAX
, b
= TASKS_MAX
;
478 _cleanup_free_
char *root
= NULL
;
480 /* Determine the maximum number of tasks that may run on this system. We check three sources to determine this
483 * a) the maximum tasks value the kernel allows on this architecture
484 * b) the cgroups pids_max attribute for the system
485 * c) the kernel's configured maximum PID value
487 * And then pick the smallest of the three */
489 (void) procfs_tasks_get_limit(&a
);
491 if (cg_get_root_path(&root
) >= 0) {
492 _cleanup_free_
char *value
= NULL
;
494 if (cg_get_attribute("pids", root
, "pids.max", &value
) >= 0)
495 (void) safe_atou64(value
, &b
);
498 return MIN3(TASKS_MAX
,
499 a
<= 0 ? TASKS_MAX
: a
,
500 b
<= 0 ? TASKS_MAX
: b
);
503 uint64_t system_tasks_max_scale(uint64_t v
, uint64_t max
) {
508 /* Multiply the system's task value by the fraction v/max. Hence, if max==100 this calculates percentages
509 * relative to the system's maximum number of tasks. Returns UINT64_MAX on overflow. */
511 t
= system_tasks_max();
515 if (m
/ t
!= v
) /* overflow? */
521 int update_reboot_parameter_and_warn(const char *param
) {
524 if (isempty(param
)) {
525 if (unlink("/run/systemd/reboot-param") < 0) {
529 return log_warning_errno(errno
, "Failed to unlink reboot parameter file: %m");
535 RUN_WITH_UMASK(0022) {
536 r
= write_string_file("/run/systemd/reboot-param", param
, WRITE_STRING_FILE_CREATE
);
538 return log_warning_errno(r
, "Failed to write reboot parameter file: %m");
545 puts(PACKAGE_STRING
"\n"
550 /* This is a direct translation of str_verscmp from boot.c */
551 static bool is_digit(int c
) {
552 return c
>= '0' && c
<= '9';
555 static int c_order(int c
) {
556 if (c
== 0 || is_digit(c
))
559 if ((c
>= 'a') && (c
<= 'z'))
565 int str_verscmp(const char *s1
, const char *s2
) {
566 const char *os1
, *os2
;
577 while ((*s1
&& !is_digit(*s1
)) || (*s2
&& !is_digit(*s2
))) {
580 order
= c_order(*s1
) - c_order(*s2
);
593 while (is_digit(*s1
) && is_digit(*s2
)) {
609 return strcmp(os1
, os2
);
612 /* Turn off core dumps but only if we're running outside of a container. */
613 void disable_coredumps(void) {
616 if (detect_container() > 0)
619 r
= write_string_file("/proc/sys/kernel/core_pattern", "|/bin/false", 0);
621 log_debug_errno(r
, "Failed to turn off coredumps, ignoring: %m");