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