]>
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"
56 #include "signal-util.h"
57 #include "stat-util.h"
58 #include "string-util.h"
60 #include "time-util.h"
61 #include "umask-util.h"
62 #include "user-util.h"
66 char **saved_argv
= NULL
;
67 static int saved_in_initrd
= -1;
69 size_t page_size(void) {
70 static thread_local
size_t pgsz
= 0;
73 if (_likely_(pgsz
> 0))
76 r
= sysconf(_SC_PAGESIZE
);
83 bool plymouth_running(void) {
84 return access("/run/plymouth/pid", F_OK
) >= 0;
87 bool display_is_local(const char *display
) {
96 int socket_from_display(const char *display
, char **path
) {
103 if (!display_is_local(display
))
106 k
= strspn(display
+1, "0123456789");
108 f
= new(char, STRLEN("/tmp/.X11-unix/X") + k
+ 1);
112 c
= stpcpy(f
, "/tmp/.X11-unix/X");
113 memcpy(c
, display
+1, k
);
121 bool kexec_loaded(void) {
122 _cleanup_free_
char *s
= NULL
;
124 if (read_one_line_file("/sys/kernel/kexec_loaded", &s
) < 0)
130 int prot_from_flags(int flags
) {
132 switch (flags
& O_ACCMODE
) {
141 return PROT_READ
|PROT_WRITE
;
148 bool in_initrd(void) {
151 if (saved_in_initrd
>= 0)
152 return saved_in_initrd
;
154 /* We make two checks here:
156 * 1. the flag file /etc/initrd-release must exist
157 * 2. the root file system must be a memory file system
159 * The second check is extra paranoia, since misdetecting an
160 * initrd can have bad consequences due the initrd
161 * emptying when transititioning to the main systemd.
164 saved_in_initrd
= access("/etc/initrd-release", F_OK
) >= 0 &&
165 statfs("/", &s
) >= 0 &&
168 return saved_in_initrd
;
171 void in_initrd_force(bool value
) {
172 saved_in_initrd
= value
;
175 /* hey glibc, APIs with callbacks without a user pointer are so useless */
176 void *xbsearch_r(const void *key
, const void *base
, size_t nmemb
, size_t size
,
177 int (*compar
) (const void *, const void *, void *), void *arg
) {
186 p
= (const char *) base
+ idx
* size
;
187 comparison
= compar(key
, p
, arg
);
190 else if (comparison
> 0)
198 int on_ac_power(void) {
199 bool found_offline
= false, found_online
= false;
200 _cleanup_closedir_
DIR *d
= NULL
;
203 d
= opendir("/sys/class/power_supply");
205 return errno
== ENOENT
? true : -errno
;
207 FOREACH_DIRENT(de
, d
, return -errno
) {
208 _cleanup_close_
int fd
= -1, device
= -1;
212 device
= openat(dirfd(d
), de
->d_name
, O_DIRECTORY
|O_RDONLY
|O_CLOEXEC
|O_NOCTTY
);
214 if (IN_SET(errno
, ENOENT
, ENOTDIR
))
220 fd
= openat(device
, "type", O_RDONLY
|O_CLOEXEC
|O_NOCTTY
);
228 n
= read(fd
, contents
, sizeof(contents
));
232 if (n
!= 6 || memcmp(contents
, "Mains\n", 6))
236 fd
= openat(device
, "online", O_RDONLY
|O_CLOEXEC
|O_NOCTTY
);
244 n
= read(fd
, contents
, sizeof(contents
));
248 if (n
!= 2 || contents
[1] != '\n')
251 if (contents
[0] == '1') {
254 } else if (contents
[0] == '0')
255 found_offline
= true;
260 return found_online
|| !found_offline
;
263 int container_get_leader(const char *machine
, pid_t
*pid
) {
264 _cleanup_free_
char *s
= NULL
, *class = NULL
;
272 if (!machine_name_is_valid(machine
))
275 p
= strjoina("/run/systemd/machines/", machine
);
276 r
= parse_env_file(p
, NEWLINE
, "LEADER", &s
, "CLASS", &class, NULL
);
284 if (!streq_ptr(class, "container"))
287 r
= parse_pid(s
, &leader
);
297 int namespace_open(pid_t pid
, int *pidns_fd
, int *mntns_fd
, int *netns_fd
, int *userns_fd
, int *root_fd
) {
298 _cleanup_close_
int pidnsfd
= -1, mntnsfd
= -1, netnsfd
= -1, usernsfd
= -1;
306 mntns
= procfs_file_alloca(pid
, "ns/mnt");
307 mntnsfd
= open(mntns
, O_RDONLY
|O_NOCTTY
|O_CLOEXEC
);
315 pidns
= procfs_file_alloca(pid
, "ns/pid");
316 pidnsfd
= open(pidns
, O_RDONLY
|O_NOCTTY
|O_CLOEXEC
);
324 netns
= procfs_file_alloca(pid
, "ns/net");
325 netnsfd
= open(netns
, O_RDONLY
|O_NOCTTY
|O_CLOEXEC
);
333 userns
= procfs_file_alloca(pid
, "ns/user");
334 usernsfd
= open(userns
, O_RDONLY
|O_NOCTTY
|O_CLOEXEC
);
335 if (usernsfd
< 0 && errno
!= ENOENT
)
342 root
= procfs_file_alloca(pid
, "root");
343 rfd
= open(root
, O_RDONLY
|O_NOCTTY
|O_CLOEXEC
|O_DIRECTORY
);
358 *userns_fd
= usernsfd
;
363 pidnsfd
= mntnsfd
= netnsfd
= usernsfd
= -1;
368 int namespace_enter(int pidns_fd
, int mntns_fd
, int netns_fd
, int userns_fd
, int root_fd
) {
369 if (userns_fd
>= 0) {
370 /* Can't setns to your own userns, since then you could
371 * escalate from non-root to root in your own namespace, so
372 * check if namespaces equal before attempting to enter. */
373 _cleanup_free_
char *userns_fd_path
= NULL
;
375 if (asprintf(&userns_fd_path
, "/proc/self/fd/%d", userns_fd
) < 0)
378 r
= files_same(userns_fd_path
, "/proc/self/ns/user", 0);
386 if (setns(pidns_fd
, CLONE_NEWPID
) < 0)
390 if (setns(mntns_fd
, CLONE_NEWNS
) < 0)
394 if (setns(netns_fd
, CLONE_NEWNET
) < 0)
398 if (setns(userns_fd
, CLONE_NEWUSER
) < 0)
402 if (fchdir(root_fd
) < 0)
409 return reset_uid_gid();
412 uint64_t physical_memory(void) {
413 _cleanup_free_
char *root
= NULL
, *value
= NULL
;
418 /* We return this as uint64_t in case we are running as 32bit process on a 64bit kernel with huge amounts of
421 * In order to support containers nicely that have a configured memory limit we'll take the minimum of the
422 * physically reported amount of memory and the limit configured for the root cgroup, if there is any. */
424 sc
= sysconf(_SC_PHYS_PAGES
);
428 mem
= (uint64_t) sc
* (uint64_t) ps
;
430 if (cg_get_root_path(&root
) < 0)
433 if (cg_get_attribute("memory", root
, "memory.limit_in_bytes", &value
))
436 if (safe_atou64(value
, &lim
) < 0)
439 /* Make sure the limit is a multiple of our own page size */
443 return MIN(mem
, lim
);
446 uint64_t physical_memory_scale(uint64_t v
, uint64_t max
) {
447 uint64_t p
, m
, ps
, r
;
451 /* Returns the physical memory size, multiplied by v divided by max. Returns UINT64_MAX on overflow. On success
452 * the result is a multiple of the page size (rounds down). */
457 p
= physical_memory() / ps
;
473 uint64_t system_tasks_max(void) {
475 #if SIZEOF_PID_T == 4
476 #define TASKS_MAX ((uint64_t) (INT32_MAX-1))
477 #elif SIZEOF_PID_T == 2
478 #define TASKS_MAX ((uint64_t) (INT16_MAX-1))
480 #error "Unknown pid_t size"
483 _cleanup_free_
char *value
= NULL
, *root
= NULL
;
484 uint64_t a
= TASKS_MAX
, b
= TASKS_MAX
;
486 /* Determine the maximum number of tasks that may run on this system. We check three sources to determine this
489 * a) the maximum value for the pid_t type
490 * b) the cgroups pids_max attribute for the system
491 * c) the kernel's configure maximum PID value
493 * And then pick the smallest of the three */
495 if (read_one_line_file("/proc/sys/kernel/pid_max", &value
) >= 0)
496 (void) safe_atou64(value
, &a
);
498 if (cg_get_root_path(&root
) >= 0) {
499 value
= mfree(value
);
501 if (cg_get_attribute("pids", root
, "pids.max", &value
) >= 0)
502 (void) safe_atou64(value
, &b
);
505 return MIN3(TASKS_MAX
,
506 a
<= 0 ? TASKS_MAX
: a
,
507 b
<= 0 ? TASKS_MAX
: b
);
510 uint64_t system_tasks_max_scale(uint64_t v
, uint64_t max
) {
515 /* Multiply the system's task value by the fraction v/max. Hence, if max==100 this calculates percentages
516 * relative to the system's maximum number of tasks. Returns UINT64_MAX on overflow. */
518 t
= system_tasks_max();
522 if (m
/ t
!= v
) /* overflow? */
528 int update_reboot_parameter_and_warn(const char *param
) {
531 if (isempty(param
)) {
532 if (unlink("/run/systemd/reboot-param") < 0) {
536 return log_warning_errno(errno
, "Failed to unlink reboot parameter file: %m");
542 RUN_WITH_UMASK(0022) {
543 r
= write_string_file("/run/systemd/reboot-param", param
, WRITE_STRING_FILE_CREATE
);
545 return log_warning_errno(r
, "Failed to write reboot parameter file: %m");
552 puts(PACKAGE_STRING
"\n"
557 /* This is a direct translation of str_verscmp from boot.c */
558 static bool is_digit(int c
) {
559 return c
>= '0' && c
<= '9';
562 static int c_order(int c
) {
563 if (c
== 0 || is_digit(c
))
566 if ((c
>= 'a') && (c
<= 'z'))
572 int str_verscmp(const char *s1
, const char *s2
) {
573 const char *os1
, *os2
;
584 while ((*s1
&& !is_digit(*s1
)) || (*s2
&& !is_digit(*s2
))) {
587 order
= c_order(*s1
) - c_order(*s2
);
600 while (is_digit(*s1
) && is_digit(*s2
)) {
616 return strcmp(os1
, os2
);