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