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