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