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