]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/basic/util.c
Merge pull request #7629 from poettering/condition-kernel-version
[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 "set.h"
56 #include "signal-util.h"
57 #include "stat-util.h"
58 #include "string-util.h"
59 #include "strv.h"
60 #include "time-util.h"
61 #include "umask-util.h"
62 #include "user-util.h"
63 #include "util.h"
64
65 int saved_argc = 0;
66 char **saved_argv = NULL;
67 static int saved_in_initrd = -1;
68
69 size_t page_size(void) {
70 static thread_local size_t pgsz = 0;
71 long r;
72
73 if (_likely_(pgsz > 0))
74 return pgsz;
75
76 r = sysconf(_SC_PAGESIZE);
77 assert(r > 0);
78
79 pgsz = (size_t) r;
80 return pgsz;
81 }
82
83 bool plymouth_running(void) {
84 return access("/run/plymouth/pid", F_OK) >= 0;
85 }
86
87 bool 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
96 int 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
108 f = new(char, STRLEN("/tmp/.X11-unix/X") + k + 1);
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
121 bool kexec_loaded(void) {
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';
128 }
129
130 int 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 }
146 }
147
148 bool in_initrd(void) {
149 struct statfs s;
150
151 if (saved_in_initrd >= 0)
152 return saved_in_initrd;
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
160 * initrd can have bad consequences due the initrd
161 * emptying when transititioning to the main systemd.
162 */
163
164 saved_in_initrd = access("/etc/initrd-release", F_OK) >= 0 &&
165 statfs("/", &s) >= 0 &&
166 is_temporary_fs(&s);
167
168 return saved_in_initrd;
169 }
170
171 void in_initrd_force(bool value) {
172 saved_in_initrd = value;
173 }
174
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) {
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;
186 p = (const char *) base + idx * size;
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 }
197
198 int on_ac_power(void) {
199 bool found_offline = false, found_online = false;
200 _cleanup_closedir_ DIR *d = NULL;
201 struct dirent *de;
202
203 d = opendir("/sys/class/power_supply");
204 if (!d)
205 return errno == ENOENT ? true : -errno;
206
207 FOREACH_DIRENT(de, d, return -errno) {
208 _cleanup_close_ int fd = -1, device = -1;
209 char contents[6];
210 ssize_t n;
211
212 device = openat(dirfd(d), de->d_name, O_DIRECTORY|O_RDONLY|O_CLOEXEC|O_NOCTTY);
213 if (device < 0) {
214 if (IN_SET(errno, ENOENT, ENOTDIR))
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
235 safe_close(fd);
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 }
262
263 int 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
272 if (!machine_name_is_valid(machine))
273 return -EINVAL;
274
275 p = strjoina("/run/systemd/machines/", machine);
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
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;
299 int rfd = -1;
300
301 assert(pid >= 0);
302
303 if (mntns_fd) {
304 const char *mntns;
305
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 }
311
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
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
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;
350
351 if (mntns_fd)
352 *mntns_fd = mntnsfd;
353
354 if (netns_fd)
355 *netns_fd = netnsfd;
356
357 if (userns_fd)
358 *userns_fd = usernsfd;
359
360 if (root_fd)
361 *root_fd = rfd;
362
363 pidnsfd = mntnsfd = netnsfd = usernsfd = -1;
364
365 return 0;
366 }
367
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;
374 int r;
375 if (asprintf(&userns_fd_path, "/proc/self/fd/%d", userns_fd) < 0)
376 return -ENOMEM;
377
378 r = files_same(userns_fd_path, "/proc/self/ns/user", 0);
379 if (r < 0)
380 return r;
381 if (r)
382 userns_fd = -1;
383 }
384
385 if (pidns_fd >= 0)
386 if (setns(pidns_fd, CLONE_NEWPID) < 0)
387 return -errno;
388
389 if (mntns_fd >= 0)
390 if (setns(mntns_fd, CLONE_NEWNS) < 0)
391 return -errno;
392
393 if (netns_fd >= 0)
394 if (setns(netns_fd, CLONE_NEWNET) < 0)
395 return -errno;
396
397 if (userns_fd >= 0)
398 if (setns(userns_fd, CLONE_NEWUSER) < 0)
399 return -errno;
400
401 if (root_fd >= 0) {
402 if (fchdir(root_fd) < 0)
403 return -errno;
404
405 if (chroot(".") < 0)
406 return -errno;
407 }
408
409 return reset_uid_gid();
410 }
411
412 uint64_t physical_memory(void) {
413 _cleanup_free_ char *root = NULL, *value = NULL;
414 uint64_t mem, lim;
415 size_t ps;
416 long sc;
417
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;
438
439 /* Make sure the limit is a multiple of our own page size */
440 lim /= ps;
441 lim *= ps;
442
443 return MIN(mem, lim);
444 }
445
446 uint64_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
473 uint64_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
510 uint64_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
528 int update_reboot_parameter_and_warn(const char *param) {
529 int r;
530
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
542 RUN_WITH_UMASK(0022) {
543 r = write_string_file("/run/systemd/reboot-param", param, WRITE_STRING_FILE_CREATE);
544 if (r < 0)
545 return log_warning_errno(r, "Failed to write reboot parameter file: %m");
546 }
547
548 return 0;
549 }
550
551 int version(void) {
552 puts(PACKAGE_STRING "\n"
553 SYSTEMD_FEATURES);
554 return 0;
555 }
556
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';
560 }
561
562 static 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
572 int 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 }