/* SPDX-License-Identifier: LGPL-2.1-or-later */ #include #include #include #include "alloc-util.h" #include "build.h" #include "dirent-util.h" #include "env-file.h" #include "env-util.h" #include "fd-util.h" #include "fileio.h" #include "hostname-util.h" #include "log.h" #include "macro.h" #include "parse-util.h" #include "stat-util.h" #include "string-util.h" #include "util.h" #include "virt.h" int saved_argc = 0; char **saved_argv = NULL; static int saved_in_initrd = -1; bool kexec_loaded(void) { _cleanup_free_ char *s = NULL; if (read_one_line_file("/sys/kernel/kexec_loaded", &s) < 0) return false; return s[0] == '1'; } int prot_from_flags(int flags) { switch (flags & O_ACCMODE) { case O_RDONLY: return PROT_READ; case O_WRONLY: return PROT_WRITE; case O_RDWR: return PROT_READ|PROT_WRITE; default: return -EINVAL; } } bool in_initrd(void) { struct statfs s; int r; if (saved_in_initrd >= 0) return saved_in_initrd; /* We make two checks here: * * 1. the flag file /etc/initrd-release must exist * 2. the root file system must be a memory file system * * The second check is extra paranoia, since misdetecting an * initrd can have bad consequences due the initrd * emptying when transititioning to the main systemd. */ r = getenv_bool_secure("SYSTEMD_IN_INITRD"); if (r < 0 && r != -ENXIO) log_debug_errno(r, "Failed to parse $SYSTEMD_IN_INITRD, ignoring: %m"); if (r >= 0) saved_in_initrd = r > 0; else saved_in_initrd = access("/etc/initrd-release", F_OK) >= 0 && statfs("/", &s) >= 0 && is_temporary_fs(&s); return saved_in_initrd; } void in_initrd_force(bool value) { saved_in_initrd = value; } int on_ac_power(void) { bool found_offline = false, found_online = false; _cleanup_closedir_ DIR *d = NULL; struct dirent *de; d = opendir("/sys/class/power_supply"); if (!d) return errno == ENOENT ? true : -errno; FOREACH_DIRENT(de, d, return -errno) { _cleanup_close_ int fd = -1, device = -1; char contents[6]; ssize_t n; device = openat(dirfd(d), de->d_name, O_DIRECTORY|O_RDONLY|O_CLOEXEC|O_NOCTTY); if (device < 0) { if (IN_SET(errno, ENOENT, ENOTDIR)) continue; return -errno; } fd = openat(device, "type", O_RDONLY|O_CLOEXEC|O_NOCTTY); if (fd < 0) { if (errno == ENOENT) continue; return -errno; } n = read(fd, contents, sizeof(contents)); if (n < 0) return -errno; if (n != 6 || memcmp(contents, "Mains\n", 6)) continue; safe_close(fd); fd = openat(device, "online", O_RDONLY|O_CLOEXEC|O_NOCTTY); if (fd < 0) { if (errno == ENOENT) continue; return -errno; } n = read(fd, contents, sizeof(contents)); if (n < 0) return -errno; if (n != 2 || contents[1] != '\n') return -EIO; if (contents[0] == '1') { found_online = true; break; } else if (contents[0] == '0') found_offline = true; else return -EIO; } return found_online || !found_offline; } int container_get_leader(const char *machine, pid_t *pid) { _cleanup_free_ char *s = NULL, *class = NULL; const char *p; pid_t leader; int r; assert(machine); assert(pid); if (streq(machine, ".host")) { *pid = 1; return 0; } if (!machine_name_is_valid(machine)) return -EINVAL; p = strjoina("/run/systemd/machines/", machine); r = parse_env_file(NULL, p, "LEADER", &s, "CLASS", &class); if (r == -ENOENT) return -EHOSTDOWN; if (r < 0) return r; if (!s) return -EIO; if (!streq_ptr(class, "container")) return -EIO; r = parse_pid(s, &leader); if (r < 0) return r; if (leader <= 1) return -EIO; *pid = leader; return 0; } int version(void) { printf("systemd " STRINGIFY(PROJECT_VERSION) " (" GIT_VERSION ")\n%s\n", systemd_features); return 0; } /* This is a direct translation of str_verscmp from boot.c */ static bool is_digit(int c) { return c >= '0' && c <= '9'; } static int c_order(int c) { if (c == 0 || is_digit(c)) return 0; if ((c >= 'a') && (c <= 'z')) return c; return c + 0x10000; } int str_verscmp(const char *s1, const char *s2) { const char *os1, *os2; assert(s1); assert(s2); os1 = s1; os2 = s2; while (*s1 || *s2) { int first; while ((*s1 && !is_digit(*s1)) || (*s2 && !is_digit(*s2))) { int order; order = c_order(*s1) - c_order(*s2); if (order != 0) return order; s1++; s2++; } while (*s1 == '0') s1++; while (*s2 == '0') s2++; first = 0; while (is_digit(*s1) && is_digit(*s2)) { if (first == 0) first = *s1 - *s2; s1++; s2++; } if (is_digit(*s1)) return 1; if (is_digit(*s2)) return -1; if (first != 0) return first; } return strcmp(os1, os2); } /* Turn off core dumps but only if we're running outside of a container. */ void disable_coredumps(void) { int r; if (detect_container() > 0) return; r = write_string_file("/proc/sys/kernel/core_pattern", "|/bin/false", WRITE_STRING_FILE_DISABLE_BUFFER); if (r < 0) log_debug_errno(r, "Failed to turn off coredumps, ignoring: %m"); }