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