]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/basic/util.c
Merge pull request #7933 from keszybz/fuzz-regression
[thirdparty/systemd.git] / src / basic / util.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
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
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.
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
15 Lesser General Public License for more details.
16
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/>.
19 ***/
20
21 #include <alloca.h>
22 #include <errno.h>
23 #include <fcntl.h>
24 #include <sched.h>
25 #include <signal.h>
26 #include <stdarg.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <sys/mman.h>
31 #include <sys/prctl.h>
32 #include <sys/statfs.h>
33 #include <sys/sysmacros.h>
34 #include <sys/types.h>
35 #include <unistd.h>
36
37 #include "alloc-util.h"
38 #include "btrfs-util.h"
39 #include "build.h"
40 #include "cgroup-util.h"
41 #include "def.h"
42 #include "device-nodes.h"
43 #include "dirent-util.h"
44 #include "fd-util.h"
45 #include "fileio.h"
46 #include "format-util.h"
47 #include "hashmap.h"
48 #include "hostname-util.h"
49 #include "log.h"
50 #include "macro.h"
51 #include "missing.h"
52 #include "parse-util.h"
53 #include "path-util.h"
54 #include "process-util.h"
55 #include "procfs-util.h"
56 #include "set.h"
57 #include "signal-util.h"
58 #include "stat-util.h"
59 #include "string-util.h"
60 #include "strv.h"
61 #include "time-util.h"
62 #include "umask-util.h"
63 #include "user-util.h"
64 #include "util.h"
65 #include "virt.h"
66
67 int saved_argc = 0;
68 char **saved_argv = NULL;
69 static int saved_in_initrd = -1;
70
71 size_t page_size(void) {
72 static thread_local size_t pgsz = 0;
73 long r;
74
75 if (_likely_(pgsz > 0))
76 return pgsz;
77
78 r = sysconf(_SC_PAGESIZE);
79 assert(r > 0);
80
81 pgsz = (size_t) r;
82 return pgsz;
83 }
84
85 bool plymouth_running(void) {
86 return access("/run/plymouth/pid", F_OK) >= 0;
87 }
88
89 bool display_is_local(const char *display) {
90 assert(display);
91
92 return
93 display[0] == ':' &&
94 display[1] >= '0' &&
95 display[1] <= '9';
96 }
97
98 int socket_from_display(const char *display, char **path) {
99 size_t k;
100 char *f, *c;
101
102 assert(display);
103 assert(path);
104
105 if (!display_is_local(display))
106 return -EINVAL;
107
108 k = strspn(display+1, "0123456789");
109
110 f = new(char, STRLEN("/tmp/.X11-unix/X") + k + 1);
111 if (!f)
112 return -ENOMEM;
113
114 c = stpcpy(f, "/tmp/.X11-unix/X");
115 memcpy(c, display+1, k);
116 c[k] = 0;
117
118 *path = f;
119
120 return 0;
121 }
122
123 bool kexec_loaded(void) {
124 _cleanup_free_ char *s = NULL;
125
126 if (read_one_line_file("/sys/kernel/kexec_loaded", &s) < 0)
127 return false;
128
129 return s[0] == '1';
130 }
131
132 int prot_from_flags(int flags) {
133
134 switch (flags & O_ACCMODE) {
135
136 case O_RDONLY:
137 return PROT_READ;
138
139 case O_WRONLY:
140 return PROT_WRITE;
141
142 case O_RDWR:
143 return PROT_READ|PROT_WRITE;
144
145 default:
146 return -EINVAL;
147 }
148 }
149
150 bool in_initrd(void) {
151 struct statfs s;
152
153 if (saved_in_initrd >= 0)
154 return saved_in_initrd;
155
156 /* We make two checks here:
157 *
158 * 1. the flag file /etc/initrd-release must exist
159 * 2. the root file system must be a memory file system
160 *
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.
164 */
165
166 saved_in_initrd = access("/etc/initrd-release", F_OK) >= 0 &&
167 statfs("/", &s) >= 0 &&
168 is_temporary_fs(&s);
169
170 return saved_in_initrd;
171 }
172
173 void in_initrd_force(bool value) {
174 saved_in_initrd = value;
175 }
176
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) {
180 size_t l, u, idx;
181 const void *p;
182 int comparison;
183
184 l = 0;
185 u = nmemb;
186 while (l < u) {
187 idx = (l + u) / 2;
188 p = (const char *) base + idx * size;
189 comparison = compar(key, p, arg);
190 if (comparison < 0)
191 u = idx;
192 else if (comparison > 0)
193 l = idx + 1;
194 else
195 return (void *)p;
196 }
197 return NULL;
198 }
199
200 int on_ac_power(void) {
201 bool found_offline = false, found_online = false;
202 _cleanup_closedir_ DIR *d = NULL;
203 struct dirent *de;
204
205 d = opendir("/sys/class/power_supply");
206 if (!d)
207 return errno == ENOENT ? true : -errno;
208
209 FOREACH_DIRENT(de, d, return -errno) {
210 _cleanup_close_ int fd = -1, device = -1;
211 char contents[6];
212 ssize_t n;
213
214 device = openat(dirfd(d), de->d_name, O_DIRECTORY|O_RDONLY|O_CLOEXEC|O_NOCTTY);
215 if (device < 0) {
216 if (IN_SET(errno, ENOENT, ENOTDIR))
217 continue;
218
219 return -errno;
220 }
221
222 fd = openat(device, "type", O_RDONLY|O_CLOEXEC|O_NOCTTY);
223 if (fd < 0) {
224 if (errno == ENOENT)
225 continue;
226
227 return -errno;
228 }
229
230 n = read(fd, contents, sizeof(contents));
231 if (n < 0)
232 return -errno;
233
234 if (n != 6 || memcmp(contents, "Mains\n", 6))
235 continue;
236
237 safe_close(fd);
238 fd = openat(device, "online", O_RDONLY|O_CLOEXEC|O_NOCTTY);
239 if (fd < 0) {
240 if (errno == ENOENT)
241 continue;
242
243 return -errno;
244 }
245
246 n = read(fd, contents, sizeof(contents));
247 if (n < 0)
248 return -errno;
249
250 if (n != 2 || contents[1] != '\n')
251 return -EIO;
252
253 if (contents[0] == '1') {
254 found_online = true;
255 break;
256 } else if (contents[0] == '0')
257 found_offline = true;
258 else
259 return -EIO;
260 }
261
262 return found_online || !found_offline;
263 }
264
265 int container_get_leader(const char *machine, pid_t *pid) {
266 _cleanup_free_ char *s = NULL, *class = NULL;
267 const char *p;
268 pid_t leader;
269 int r;
270
271 assert(machine);
272 assert(pid);
273
274 if (!machine_name_is_valid(machine))
275 return -EINVAL;
276
277 p = strjoina("/run/systemd/machines/", machine);
278 r = parse_env_file(p, NEWLINE, "LEADER", &s, "CLASS", &class, NULL);
279 if (r == -ENOENT)
280 return -EHOSTDOWN;
281 if (r < 0)
282 return r;
283 if (!s)
284 return -EIO;
285
286 if (!streq_ptr(class, "container"))
287 return -EIO;
288
289 r = parse_pid(s, &leader);
290 if (r < 0)
291 return r;
292 if (leader <= 1)
293 return -EIO;
294
295 *pid = leader;
296 return 0;
297 }
298
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;
301 int rfd = -1;
302
303 assert(pid >= 0);
304
305 if (mntns_fd) {
306 const char *mntns;
307
308 mntns = procfs_file_alloca(pid, "ns/mnt");
309 mntnsfd = open(mntns, O_RDONLY|O_NOCTTY|O_CLOEXEC);
310 if (mntnsfd < 0)
311 return -errno;
312 }
313
314 if (pidns_fd) {
315 const char *pidns;
316
317 pidns = procfs_file_alloca(pid, "ns/pid");
318 pidnsfd = open(pidns, O_RDONLY|O_NOCTTY|O_CLOEXEC);
319 if (pidnsfd < 0)
320 return -errno;
321 }
322
323 if (netns_fd) {
324 const char *netns;
325
326 netns = procfs_file_alloca(pid, "ns/net");
327 netnsfd = open(netns, O_RDONLY|O_NOCTTY|O_CLOEXEC);
328 if (netnsfd < 0)
329 return -errno;
330 }
331
332 if (userns_fd) {
333 const char *userns;
334
335 userns = procfs_file_alloca(pid, "ns/user");
336 usernsfd = open(userns, O_RDONLY|O_NOCTTY|O_CLOEXEC);
337 if (usernsfd < 0 && errno != ENOENT)
338 return -errno;
339 }
340
341 if (root_fd) {
342 const char *root;
343
344 root = procfs_file_alloca(pid, "root");
345 rfd = open(root, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
346 if (rfd < 0)
347 return -errno;
348 }
349
350 if (pidns_fd)
351 *pidns_fd = pidnsfd;
352
353 if (mntns_fd)
354 *mntns_fd = mntnsfd;
355
356 if (netns_fd)
357 *netns_fd = netnsfd;
358
359 if (userns_fd)
360 *userns_fd = usernsfd;
361
362 if (root_fd)
363 *root_fd = rfd;
364
365 pidnsfd = mntnsfd = netnsfd = usernsfd = -1;
366
367 return 0;
368 }
369
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;
376 int r;
377 if (asprintf(&userns_fd_path, "/proc/self/fd/%d", userns_fd) < 0)
378 return -ENOMEM;
379
380 r = files_same(userns_fd_path, "/proc/self/ns/user", 0);
381 if (r < 0)
382 return r;
383 if (r)
384 userns_fd = -1;
385 }
386
387 if (pidns_fd >= 0)
388 if (setns(pidns_fd, CLONE_NEWPID) < 0)
389 return -errno;
390
391 if (mntns_fd >= 0)
392 if (setns(mntns_fd, CLONE_NEWNS) < 0)
393 return -errno;
394
395 if (netns_fd >= 0)
396 if (setns(netns_fd, CLONE_NEWNET) < 0)
397 return -errno;
398
399 if (userns_fd >= 0)
400 if (setns(userns_fd, CLONE_NEWUSER) < 0)
401 return -errno;
402
403 if (root_fd >= 0) {
404 if (fchdir(root_fd) < 0)
405 return -errno;
406
407 if (chroot(".") < 0)
408 return -errno;
409 }
410
411 return reset_uid_gid();
412 }
413
414 uint64_t physical_memory(void) {
415 _cleanup_free_ char *root = NULL, *value = NULL;
416 uint64_t mem, lim;
417 size_t ps;
418 long sc;
419
420 /* We return this as uint64_t in case we are running as 32bit process on a 64bit kernel with huge amounts of
421 * memory.
422 *
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. */
425
426 sc = sysconf(_SC_PHYS_PAGES);
427 assert(sc > 0);
428
429 ps = page_size();
430 mem = (uint64_t) sc * (uint64_t) ps;
431
432 if (cg_get_root_path(&root) < 0)
433 return mem;
434
435 if (cg_get_attribute("memory", root, "memory.limit_in_bytes", &value))
436 return mem;
437
438 if (safe_atou64(value, &lim) < 0)
439 return mem;
440
441 /* Make sure the limit is a multiple of our own page size */
442 lim /= ps;
443 lim *= ps;
444
445 return MIN(mem, lim);
446 }
447
448 uint64_t physical_memory_scale(uint64_t v, uint64_t max) {
449 uint64_t p, m, ps, r;
450
451 assert(max > 0);
452
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). */
455
456 ps = page_size();
457 assert(ps > 0);
458
459 p = physical_memory() / ps;
460 assert(p > 0);
461
462 m = p * v;
463 if (m / p != v)
464 return UINT64_MAX;
465
466 m /= max;
467
468 r = m * ps;
469 if (r / ps != m)
470 return UINT64_MAX;
471
472 return r;
473 }
474
475 uint64_t system_tasks_max(void) {
476
477 uint64_t a = TASKS_MAX, b = TASKS_MAX;
478 _cleanup_free_ char *root = NULL;
479
480 /* Determine the maximum number of tasks that may run on this system. We check three sources to determine this
481 * limit:
482 *
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
486 *
487 * And then pick the smallest of the three */
488
489 (void) procfs_tasks_get_limit(&a);
490
491 if (cg_get_root_path(&root) >= 0) {
492 _cleanup_free_ char *value = NULL;
493
494 if (cg_get_attribute("pids", root, "pids.max", &value) >= 0)
495 (void) safe_atou64(value, &b);
496 }
497
498 return MIN3(TASKS_MAX,
499 a <= 0 ? TASKS_MAX : a,
500 b <= 0 ? TASKS_MAX : b);
501 }
502
503 uint64_t system_tasks_max_scale(uint64_t v, uint64_t max) {
504 uint64_t t, m;
505
506 assert(max > 0);
507
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. */
510
511 t = system_tasks_max();
512 assert(t > 0);
513
514 m = t * v;
515 if (m / t != v) /* overflow? */
516 return UINT64_MAX;
517
518 return m / max;
519 }
520
521 int update_reboot_parameter_and_warn(const char *param) {
522 int r;
523
524 if (isempty(param)) {
525 if (unlink("/run/systemd/reboot-param") < 0) {
526 if (errno == ENOENT)
527 return 0;
528
529 return log_warning_errno(errno, "Failed to unlink reboot parameter file: %m");
530 }
531
532 return 0;
533 }
534
535 RUN_WITH_UMASK(0022) {
536 r = write_string_file("/run/systemd/reboot-param", param, WRITE_STRING_FILE_CREATE);
537 if (r < 0)
538 return log_warning_errno(r, "Failed to write reboot parameter file: %m");
539 }
540
541 return 0;
542 }
543
544 int version(void) {
545 puts(PACKAGE_STRING "\n"
546 SYSTEMD_FEATURES);
547 return 0;
548 }
549
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';
553 }
554
555 static int c_order(int c) {
556 if (c == 0 || is_digit(c))
557 return 0;
558
559 if ((c >= 'a') && (c <= 'z'))
560 return c;
561
562 return c + 0x10000;
563 }
564
565 int str_verscmp(const char *s1, const char *s2) {
566 const char *os1, *os2;
567
568 assert(s1);
569 assert(s2);
570
571 os1 = s1;
572 os2 = s2;
573
574 while (*s1 || *s2) {
575 int first;
576
577 while ((*s1 && !is_digit(*s1)) || (*s2 && !is_digit(*s2))) {
578 int order;
579
580 order = c_order(*s1) - c_order(*s2);
581 if (order != 0)
582 return order;
583 s1++;
584 s2++;
585 }
586
587 while (*s1 == '0')
588 s1++;
589 while (*s2 == '0')
590 s2++;
591
592 first = 0;
593 while (is_digit(*s1) && is_digit(*s2)) {
594 if (first == 0)
595 first = *s1 - *s2;
596 s1++;
597 s2++;
598 }
599
600 if (is_digit(*s1))
601 return 1;
602 if (is_digit(*s2))
603 return -1;
604
605 if (first != 0)
606 return first;
607 }
608
609 return strcmp(os1, os2);
610 }
611
612 /* Turn off core dumps but only if we're running outside of a container. */
613 void disable_coredumps(void) {
614 int r;
615
616 if (detect_container() > 0)
617 return;
618
619 r = write_string_file("/proc/sys/kernel/core_pattern", "|/bin/false", 0);
620 if (r < 0)
621 log_debug_errno(r, "Failed to turn off coredumps, ignoring: %m");
622 }