chiark / gitweb /
basic: split out update_reboot_parameter_and_warn() into its own .c/.h files
[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 void in_initrd_force(bool value) {
178         saved_in_initrd = value;
179 }
180
181 #if 0 /// UNNEEDED by elogind
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
205 int on_ac_power(void) {
206         bool found_offline = false, found_online = false;
207         _cleanup_closedir_ DIR *d = NULL;
208         struct dirent *de;
209
210         d = opendir("/sys/class/power_supply");
211         if (!d)
212                 return errno == ENOENT ? true : -errno;
213
214         FOREACH_DIRENT(de, d, return -errno) {
215                 _cleanup_close_ int fd = -1, device = -1;
216                 char contents[6];
217                 ssize_t n;
218
219                 device = openat(dirfd(d), de->d_name, O_DIRECTORY|O_RDONLY|O_CLOEXEC|O_NOCTTY);
220                 if (device < 0) {
221                         if (IN_SET(errno, ENOENT, ENOTDIR))
222                                 continue;
223
224                         return -errno;
225                 }
226
227                 fd = openat(device, "type", O_RDONLY|O_CLOEXEC|O_NOCTTY);
228                 if (fd < 0) {
229                         if (errno == ENOENT)
230                                 continue;
231
232                         return -errno;
233                 }
234
235                 n = read(fd, contents, sizeof(contents));
236                 if (n < 0)
237                         return -errno;
238
239                 if (n != 6 || memcmp(contents, "Mains\n", 6))
240                         continue;
241
242                 safe_close(fd);
243                 fd = openat(device, "online", O_RDONLY|O_CLOEXEC|O_NOCTTY);
244                 if (fd < 0) {
245                         if (errno == ENOENT)
246                                 continue;
247
248                         return -errno;
249                 }
250
251                 n = read(fd, contents, sizeof(contents));
252                 if (n < 0)
253                         return -errno;
254
255                 if (n != 2 || contents[1] != '\n')
256                         return -EIO;
257
258                 if (contents[0] == '1') {
259                         found_online = true;
260                         break;
261                 } else if (contents[0] == '0')
262                         found_offline = true;
263                 else
264                         return -EIO;
265         }
266
267         return found_online || !found_offline;
268 }
269
270 #endif // 0
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 #else
484         uint64_t a = TASKS_MAX, b = TASKS_MAX;
485         _cleanup_free_ char *root = NULL;
486
487         /* Determine the maximum number of tasks that may run on this system. We check three sources to determine this
488          * limit:
489          *
490          * a) the maximum tasks value the kernel allows on this architecture
491          * b) the cgroups pids_max attribute for the system
492          * c) the kernel's configured maximum PID value
493          *
494          * And then pick the smallest of the three */
495
496         (void) procfs_tasks_get_limit(&a);
497
498         if (cg_get_root_path(&root) >= 0) {
499                 _cleanup_free_ char *value = NULL;
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 #if 0 /// UNNEEDED by elogind
529 #endif // 0
530 int version(void) {
531         puts(PACKAGE_STRING "\n"
532              SYSTEMD_FEATURES);
533         return 0;
534 }
535
536 #if 0 /// UNNEEDED by elogind
537 #endif // 0
538 /* This is a direct translation of str_verscmp from boot.c */
539 static bool is_digit(int c) {
540         return c >= '0' && c <= '9';
541 }
542
543 static int c_order(int c) {
544         if (c == 0 || is_digit(c))
545                 return 0;
546
547         if ((c >= 'a') && (c <= 'z'))
548                 return c;
549
550         return c + 0x10000;
551 }
552
553 int str_verscmp(const char *s1, const char *s2) {
554         const char *os1, *os2;
555
556         assert(s1);
557         assert(s2);
558
559         os1 = s1;
560         os2 = s2;
561
562         while (*s1 || *s2) {
563                 int first;
564
565                 while ((*s1 && !is_digit(*s1)) || (*s2 && !is_digit(*s2))) {
566                         int order;
567
568                         order = c_order(*s1) - c_order(*s2);
569                         if (order != 0)
570                                 return order;
571                         s1++;
572                         s2++;
573                 }
574
575                 while (*s1 == '0')
576                         s1++;
577                 while (*s2 == '0')
578                         s2++;
579
580                 first = 0;
581                 while (is_digit(*s1) && is_digit(*s2)) {
582                         if (first == 0)
583                                 first = *s1 - *s2;
584                         s1++;
585                         s2++;
586                 }
587
588                 if (is_digit(*s1))
589                         return 1;
590                 if (is_digit(*s2))
591                         return -1;
592
593                 if (first != 0)
594                         return first;
595         }
596
597         return strcmp(os1, os2);
598 }
599
600 /* Turn off core dumps but only if we're running outside of a container. */
601 void disable_coredumps(void) {
602         int r;
603
604         if (detect_container() > 0)
605                 return;
606
607         r = write_string_file("/proc/sys/kernel/core_pattern", "|/bin/false", 0);
608         if (r < 0)
609                 log_debug_errno(r, "Failed to turn off coredumps, ignoring: %m");
610 }