]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/basic/util.c
util: check for overflows in xbsearch_r()
[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"
1e7da35b 55#include "procfs-util.h"
11c3a366 56#include "set.h"
93cc7779 57#include "signal-util.h"
cf0fbc49 58#include "stat-util.h"
07630cea 59#include "string-util.h"
f6c2284a 60#include "strv.h"
93cc7779 61#include "time-util.h"
8612da97 62#include "umask-util.h"
b1d4f8e1 63#include "user-util.h"
4f5dd394 64#include "util.h"
9ce17593 65#include "virt.h"
56cf987f 66
9a0e6896
LP
67int saved_argc = 0;
68char **saved_argv = NULL;
dcd61450 69static int saved_in_initrd = -1;
9086e840 70
37f85e66 71size_t page_size(void) {
ec202eae 72 static thread_local size_t pgsz = 0;
37f85e66 73 long r;
74
87d2c1ff 75 if (_likely_(pgsz > 0))
37f85e66 76 return pgsz;
77
e67f47e5
LP
78 r = sysconf(_SC_PAGESIZE);
79 assert(r > 0);
37f85e66 80
81 pgsz = (size_t) r;
37f85e66 82 return pgsz;
83}
84
a88c8750
TG
85bool plymouth_running(void) {
86 return access("/run/plymouth/pid", F_OK) >= 0;
87}
88
4d6d6518
LP
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
fbd0b64f 110 f = new(char, STRLEN("/tmp/.X11-unix/X") + k + 1);
4d6d6518
LP
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
65457142 123bool kexec_loaded(void) {
c47f86e6
ZJS
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';
65457142 130}
fb9de93d 131
87d2c1ff
LP
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 }
7c99e0c1 148}
689b9a22 149
9be346c9 150bool in_initrd(void) {
825c6fe5 151 struct statfs s;
8f33b5b8 152
dcd61450
IS
153 if (saved_in_initrd >= 0)
154 return saved_in_initrd;
825c6fe5
LP
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
629ff674 162 * initrd can have bad consequences due the initrd
825c6fe5
LP
163 * emptying when transititioning to the main systemd.
164 */
165
dcd61450
IS
166 saved_in_initrd = access("/etc/initrd-release", F_OK) >= 0 &&
167 statfs("/", &s) >= 0 &&
168 is_temporary_fs(&s);
9be346c9 169
dcd61450
IS
170 return saved_in_initrd;
171}
172
173void in_initrd_force(bool value) {
174 saved_in_initrd = value;
9be346c9 175}
069cfc85 176
a9e12476
KS
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,
1c574591 179 int (*compar) (const void *, const void *, void *), void *arg) {
a9e12476
KS
180 size_t l, u, idx;
181 const void *p;
182 int comparison;
183
2901f4b3
LP
184 assert(!size_multiply_overflow(nmemb, size));
185
a9e12476
KS
186 l = 0;
187 u = nmemb;
188 while (l < u) {
189 idx = (l + u) / 2;
2901f4b3 190 p = (const uint8_t*) base + idx * size;
a9e12476
KS
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}
09017585 201
240dbaa4
LP
202int on_ac_power(void) {
203 bool found_offline = false, found_online = false;
204 _cleanup_closedir_ DIR *d = NULL;
8fb3f009 205 struct dirent *de;
240dbaa4
LP
206
207 d = opendir("/sys/class/power_supply");
208 if (!d)
6d890034 209 return errno == ENOENT ? true : -errno;
240dbaa4 210
8fb3f009 211 FOREACH_DIRENT(de, d, return -errno) {
240dbaa4
LP
212 _cleanup_close_ int fd = -1, device = -1;
213 char contents[6];
214 ssize_t n;
240dbaa4 215
240dbaa4
LP
216 device = openat(dirfd(d), de->d_name, O_DIRECTORY|O_RDONLY|O_CLOEXEC|O_NOCTTY);
217 if (device < 0) {
3742095b 218 if (IN_SET(errno, ENOENT, ENOTDIR))
240dbaa4
LP
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
03e334a1 239 safe_close(fd);
240dbaa4
LP
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}
fabe5c0e 266
bc9fd78c
LP
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
b9a8d250
LP
276 if (!machine_name_is_valid(machine))
277 return -EINVAL;
278
63c372cb 279 p = strjoina("/run/systemd/machines/", machine);
bc9fd78c
LP
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
671c3419
RM
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;
359a06aa 303 int rfd = -1;
bc9fd78c
LP
304
305 assert(pid >= 0);
bc9fd78c 306
878cd7e9
LP
307 if (mntns_fd) {
308 const char *mntns;
a4475f57 309
878cd7e9
LP
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 }
bc9fd78c 315
878cd7e9
LP
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
671c3419
RM
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
878cd7e9
LP
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;
bc9fd78c 354
878cd7e9
LP
355 if (mntns_fd)
356 *mntns_fd = mntnsfd;
357
358 if (netns_fd)
359 *netns_fd = netnsfd;
360
671c3419
RM
361 if (userns_fd)
362 *userns_fd = usernsfd;
363
878cd7e9
LP
364 if (root_fd)
365 *root_fd = rfd;
366
671c3419 367 pidnsfd = mntnsfd = netnsfd = usernsfd = -1;
bc9fd78c
LP
368
369 return 0;
370}
371
671c3419
RM
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
e3f791a2 382 r = files_same(userns_fd_path, "/proc/self/ns/user", 0);
671c3419
RM
383 if (r < 0)
384 return r;
385 if (r)
386 userns_fd = -1;
387 }
bc9fd78c 388
878cd7e9
LP
389 if (pidns_fd >= 0)
390 if (setns(pidns_fd, CLONE_NEWPID) < 0)
391 return -errno;
a4475f57 392
878cd7e9
LP
393 if (mntns_fd >= 0)
394 if (setns(mntns_fd, CLONE_NEWNS) < 0)
395 return -errno;
bc9fd78c 396
878cd7e9
LP
397 if (netns_fd >= 0)
398 if (setns(netns_fd, CLONE_NEWNET) < 0)
399 return -errno;
bc9fd78c 400
671c3419
RM
401 if (userns_fd >= 0)
402 if (setns(userns_fd, CLONE_NEWUSER) < 0)
403 return -errno;
404
878cd7e9
LP
405 if (root_fd >= 0) {
406 if (fchdir(root_fd) < 0)
407 return -errno;
408
409 if (chroot(".") < 0)
410 return -errno;
411 }
bc9fd78c 412
b4da6d6b 413 return reset_uid_gid();
bc9fd78c 414}
bf108e55 415
1c231f56 416uint64_t physical_memory(void) {
d9ab2bcf
LP
417 _cleanup_free_ char *root = NULL, *value = NULL;
418 uint64_t mem, lim;
419 size_t ps;
420 long sc;
1c231f56 421
d9ab2bcf
LP
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;
1c231f56 442
d9ab2bcf
LP
443 /* Make sure the limit is a multiple of our own page size */
444 lim /= ps;
445 lim *= ps;
1c231f56 446
d9ab2bcf 447 return MIN(mem, lim);
1c231f56 448}
6db615c1 449
d8cf2ac7
LP
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
83f8e808
LP
477uint64_t system_tasks_max(void) {
478
83f8e808 479 uint64_t a = TASKS_MAX, b = TASKS_MAX;
1e7da35b 480 _cleanup_free_ char *root = NULL;
83f8e808
LP
481
482 /* Determine the maximum number of tasks that may run on this system. We check three sources to determine this
483 * limit:
484 *
f3a367d6 485 * a) the maximum tasks value the kernel allows on this architecture
83f8e808 486 * b) the cgroups pids_max attribute for the system
f3a367d6 487 * c) the kernel's configured maximum PID value
83f8e808
LP
488 *
489 * And then pick the smallest of the three */
490
1e7da35b 491 (void) procfs_tasks_get_limit(&a);
83f8e808
LP
492
493 if (cg_get_root_path(&root) >= 0) {
1e7da35b 494 _cleanup_free_ char *value = NULL;
83f8e808
LP
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
3f6fd1ba
LP
523int version(void) {
524 puts(PACKAGE_STRING "\n"
525 SYSTEMD_FEATURES);
526 return 0;
527}
68c58c67
LP
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}
9ce17593
JK
590
591/* Turn off core dumps but only if we're running outside of a container. */
e557b1a6
LP
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");
9ce17593 601}