274 lines
7 KiB
C
274 lines
7 KiB
C
/* SPDX-License-Identifier: LGPL-2.1+ */
|
|
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <sys/mman.h>
|
|
|
|
#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) {
|
|
puts("systemd " STRINGIFY(PROJECT_VERSION) " (" GIT_VERSION ")\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");
|
|
}
|