]> git.ipfire.org Git - thirdparty/systemd.git/blame_incremental - src/basic/util.c
util: check for overflows in xbsearch_r()
[thirdparty/systemd.git] / src / basic / util.c
... / ...
CommitLineData
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
67int saved_argc = 0;
68char **saved_argv = NULL;
69static int saved_in_initrd = -1;
70
71size_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
85bool plymouth_running(void) {
86 return access("/run/plymouth/pid", F_OK) >= 0;
87}
88
89bool 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
98int 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
123bool 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
132int 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
150bool 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
173void 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 */
178void *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 assert(!size_multiply_overflow(nmemb, size));
185
186 l = 0;
187 u = nmemb;
188 while (l < u) {
189 idx = (l + u) / 2;
190 p = (const uint8_t*) base + idx * size;
191 comparison = compar(key, p, arg);
192 if (comparison < 0)
193 u = idx;
194 else if (comparison > 0)
195 l = idx + 1;
196 else
197 return (void *)p;
198 }
199 return NULL;
200}
201
202int on_ac_power(void) {
203 bool found_offline = false, found_online = false;
204 _cleanup_closedir_ DIR *d = NULL;
205 struct dirent *de;
206
207 d = opendir("/sys/class/power_supply");
208 if (!d)
209 return errno == ENOENT ? true : -errno;
210
211 FOREACH_DIRENT(de, d, return -errno) {
212 _cleanup_close_ int fd = -1, device = -1;
213 char contents[6];
214 ssize_t n;
215
216 device = openat(dirfd(d), de->d_name, O_DIRECTORY|O_RDONLY|O_CLOEXEC|O_NOCTTY);
217 if (device < 0) {
218 if (IN_SET(errno, ENOENT, ENOTDIR))
219 continue;
220
221 return -errno;
222 }
223
224 fd = openat(device, "type", O_RDONLY|O_CLOEXEC|O_NOCTTY);
225 if (fd < 0) {
226 if (errno == ENOENT)
227 continue;
228
229 return -errno;
230 }
231
232 n = read(fd, contents, sizeof(contents));
233 if (n < 0)
234 return -errno;
235
236 if (n != 6 || memcmp(contents, "Mains\n", 6))
237 continue;
238
239 safe_close(fd);
240 fd = openat(device, "online", O_RDONLY|O_CLOEXEC|O_NOCTTY);
241 if (fd < 0) {
242 if (errno == ENOENT)
243 continue;
244
245 return -errno;
246 }
247
248 n = read(fd, contents, sizeof(contents));
249 if (n < 0)
250 return -errno;
251
252 if (n != 2 || contents[1] != '\n')
253 return -EIO;
254
255 if (contents[0] == '1') {
256 found_online = true;
257 break;
258 } else if (contents[0] == '0')
259 found_offline = true;
260 else
261 return -EIO;
262 }
263
264 return found_online || !found_offline;
265}
266
267int container_get_leader(const char *machine, pid_t *pid) {
268 _cleanup_free_ char *s = NULL, *class = NULL;
269 const char *p;
270 pid_t leader;
271 int r;
272
273 assert(machine);
274 assert(pid);
275
276 if (!machine_name_is_valid(machine))
277 return -EINVAL;
278
279 p = strjoina("/run/systemd/machines/", machine);
280 r = parse_env_file(p, NEWLINE, "LEADER", &s, "CLASS", &class, NULL);
281 if (r == -ENOENT)
282 return -EHOSTDOWN;
283 if (r < 0)
284 return r;
285 if (!s)
286 return -EIO;
287
288 if (!streq_ptr(class, "container"))
289 return -EIO;
290
291 r = parse_pid(s, &leader);
292 if (r < 0)
293 return r;
294 if (leader <= 1)
295 return -EIO;
296
297 *pid = leader;
298 return 0;
299}
300
301int namespace_open(pid_t pid, int *pidns_fd, int *mntns_fd, int *netns_fd, int *userns_fd, int *root_fd) {
302 _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, netnsfd = -1, usernsfd = -1;
303 int rfd = -1;
304
305 assert(pid >= 0);
306
307 if (mntns_fd) {
308 const char *mntns;
309
310 mntns = procfs_file_alloca(pid, "ns/mnt");
311 mntnsfd = open(mntns, O_RDONLY|O_NOCTTY|O_CLOEXEC);
312 if (mntnsfd < 0)
313 return -errno;
314 }
315
316 if (pidns_fd) {
317 const char *pidns;
318
319 pidns = procfs_file_alloca(pid, "ns/pid");
320 pidnsfd = open(pidns, O_RDONLY|O_NOCTTY|O_CLOEXEC);
321 if (pidnsfd < 0)
322 return -errno;
323 }
324
325 if (netns_fd) {
326 const char *netns;
327
328 netns = procfs_file_alloca(pid, "ns/net");
329 netnsfd = open(netns, O_RDONLY|O_NOCTTY|O_CLOEXEC);
330 if (netnsfd < 0)
331 return -errno;
332 }
333
334 if (userns_fd) {
335 const char *userns;
336
337 userns = procfs_file_alloca(pid, "ns/user");
338 usernsfd = open(userns, O_RDONLY|O_NOCTTY|O_CLOEXEC);
339 if (usernsfd < 0 && errno != ENOENT)
340 return -errno;
341 }
342
343 if (root_fd) {
344 const char *root;
345
346 root = procfs_file_alloca(pid, "root");
347 rfd = open(root, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
348 if (rfd < 0)
349 return -errno;
350 }
351
352 if (pidns_fd)
353 *pidns_fd = pidnsfd;
354
355 if (mntns_fd)
356 *mntns_fd = mntnsfd;
357
358 if (netns_fd)
359 *netns_fd = netnsfd;
360
361 if (userns_fd)
362 *userns_fd = usernsfd;
363
364 if (root_fd)
365 *root_fd = rfd;
366
367 pidnsfd = mntnsfd = netnsfd = usernsfd = -1;
368
369 return 0;
370}
371
372int namespace_enter(int pidns_fd, int mntns_fd, int netns_fd, int userns_fd, int root_fd) {
373 if (userns_fd >= 0) {
374 /* Can't setns to your own userns, since then you could
375 * escalate from non-root to root in your own namespace, so
376 * check if namespaces equal before attempting to enter. */
377 _cleanup_free_ char *userns_fd_path = NULL;
378 int r;
379 if (asprintf(&userns_fd_path, "/proc/self/fd/%d", userns_fd) < 0)
380 return -ENOMEM;
381
382 r = files_same(userns_fd_path, "/proc/self/ns/user", 0);
383 if (r < 0)
384 return r;
385 if (r)
386 userns_fd = -1;
387 }
388
389 if (pidns_fd >= 0)
390 if (setns(pidns_fd, CLONE_NEWPID) < 0)
391 return -errno;
392
393 if (mntns_fd >= 0)
394 if (setns(mntns_fd, CLONE_NEWNS) < 0)
395 return -errno;
396
397 if (netns_fd >= 0)
398 if (setns(netns_fd, CLONE_NEWNET) < 0)
399 return -errno;
400
401 if (userns_fd >= 0)
402 if (setns(userns_fd, CLONE_NEWUSER) < 0)
403 return -errno;
404
405 if (root_fd >= 0) {
406 if (fchdir(root_fd) < 0)
407 return -errno;
408
409 if (chroot(".") < 0)
410 return -errno;
411 }
412
413 return reset_uid_gid();
414}
415
416uint64_t physical_memory(void) {
417 _cleanup_free_ char *root = NULL, *value = NULL;
418 uint64_t mem, lim;
419 size_t ps;
420 long sc;
421
422 /* We return this as uint64_t in case we are running as 32bit process on a 64bit kernel with huge amounts of
423 * memory.
424 *
425 * In order to support containers nicely that have a configured memory limit we'll take the minimum of the
426 * physically reported amount of memory and the limit configured for the root cgroup, if there is any. */
427
428 sc = sysconf(_SC_PHYS_PAGES);
429 assert(sc > 0);
430
431 ps = page_size();
432 mem = (uint64_t) sc * (uint64_t) ps;
433
434 if (cg_get_root_path(&root) < 0)
435 return mem;
436
437 if (cg_get_attribute("memory", root, "memory.limit_in_bytes", &value))
438 return mem;
439
440 if (safe_atou64(value, &lim) < 0)
441 return mem;
442
443 /* Make sure the limit is a multiple of our own page size */
444 lim /= ps;
445 lim *= ps;
446
447 return MIN(mem, lim);
448}
449
450uint64_t physical_memory_scale(uint64_t v, uint64_t max) {
451 uint64_t p, m, ps, r;
452
453 assert(max > 0);
454
455 /* Returns the physical memory size, multiplied by v divided by max. Returns UINT64_MAX on overflow. On success
456 * the result is a multiple of the page size (rounds down). */
457
458 ps = page_size();
459 assert(ps > 0);
460
461 p = physical_memory() / ps;
462 assert(p > 0);
463
464 m = p * v;
465 if (m / p != v)
466 return UINT64_MAX;
467
468 m /= max;
469
470 r = m * ps;
471 if (r / ps != m)
472 return UINT64_MAX;
473
474 return r;
475}
476
477uint64_t system_tasks_max(void) {
478
479 uint64_t a = TASKS_MAX, b = TASKS_MAX;
480 _cleanup_free_ char *root = NULL;
481
482 /* Determine the maximum number of tasks that may run on this system. We check three sources to determine this
483 * limit:
484 *
485 * a) the maximum tasks value the kernel allows on this architecture
486 * b) the cgroups pids_max attribute for the system
487 * c) the kernel's configured maximum PID value
488 *
489 * And then pick the smallest of the three */
490
491 (void) procfs_tasks_get_limit(&a);
492
493 if (cg_get_root_path(&root) >= 0) {
494 _cleanup_free_ char *value = NULL;
495
496 if (cg_get_attribute("pids", root, "pids.max", &value) >= 0)
497 (void) safe_atou64(value, &b);
498 }
499
500 return MIN3(TASKS_MAX,
501 a <= 0 ? TASKS_MAX : a,
502 b <= 0 ? TASKS_MAX : b);
503}
504
505uint64_t system_tasks_max_scale(uint64_t v, uint64_t max) {
506 uint64_t t, m;
507
508 assert(max > 0);
509
510 /* Multiply the system's task value by the fraction v/max. Hence, if max==100 this calculates percentages
511 * relative to the system's maximum number of tasks. Returns UINT64_MAX on overflow. */
512
513 t = system_tasks_max();
514 assert(t > 0);
515
516 m = t * v;
517 if (m / t != v) /* overflow? */
518 return UINT64_MAX;
519
520 return m / max;
521}
522
523int version(void) {
524 puts(PACKAGE_STRING "\n"
525 SYSTEMD_FEATURES);
526 return 0;
527}
528
529/* This is a direct translation of str_verscmp from boot.c */
530static bool is_digit(int c) {
531 return c >= '0' && c <= '9';
532}
533
534static int c_order(int c) {
535 if (c == 0 || is_digit(c))
536 return 0;
537
538 if ((c >= 'a') && (c <= 'z'))
539 return c;
540
541 return c + 0x10000;
542}
543
544int str_verscmp(const char *s1, const char *s2) {
545 const char *os1, *os2;
546
547 assert(s1);
548 assert(s2);
549
550 os1 = s1;
551 os2 = s2;
552
553 while (*s1 || *s2) {
554 int first;
555
556 while ((*s1 && !is_digit(*s1)) || (*s2 && !is_digit(*s2))) {
557 int order;
558
559 order = c_order(*s1) - c_order(*s2);
560 if (order != 0)
561 return order;
562 s1++;
563 s2++;
564 }
565
566 while (*s1 == '0')
567 s1++;
568 while (*s2 == '0')
569 s2++;
570
571 first = 0;
572 while (is_digit(*s1) && is_digit(*s2)) {
573 if (first == 0)
574 first = *s1 - *s2;
575 s1++;
576 s2++;
577 }
578
579 if (is_digit(*s1))
580 return 1;
581 if (is_digit(*s2))
582 return -1;
583
584 if (first != 0)
585 return first;
586 }
587
588 return strcmp(os1, os2);
589}
590
591/* Turn off core dumps but only if we're running outside of a container. */
592void disable_coredumps(void) {
593 int r;
594
595 if (detect_container() > 0)
596 return;
597
598 r = write_string_file("/proc/sys/kernel/core_pattern", "|/bin/false", 0);
599 if (r < 0)
600 log_debug_errno(r, "Failed to turn off coredumps, ignoring: %m");
601}