+/* SPDX-License-Identifier: LGPL-2.1+ */
/***
This file is part of systemd.
Copyright 2010 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 of the License, or
- (at your option) any later version.
-
- systemd is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <alloca.h>
#include <unistd.h>
#include "alloc-util.h"
+#include "btrfs-util.h"
#include "build.h"
#include "cgroup-util.h"
#include "def.h"
+#include "device-nodes.h"
#include "dirent-util.h"
#include "fd-util.h"
#include "fileio.h"
#include "parse-util.h"
#include "path-util.h"
#include "process-util.h"
+#include "procfs-util.h"
#include "set.h"
#include "signal-util.h"
#include "stat-util.h"
#include "umask-util.h"
#include "user-util.h"
#include "util.h"
-
-/* Put this test here for a lack of better place */
-assert_cc(EAGAIN == EWOULDBLOCK);
+#include "virt.h"
int saved_argc = 0;
char **saved_argv = NULL;
return pgsz;
}
-static int do_execute(char **directories, usec_t timeout, char *argv[]) {
- _cleanup_hashmap_free_free_ Hashmap *pids = NULL;
- _cleanup_set_free_free_ Set *seen = NULL;
- char **directory;
-
- /* We fork this all off from a child process so that we can
- * somewhat cleanly make use of SIGALRM to set a time limit */
-
- (void) reset_all_signal_handlers();
- (void) reset_signal_mask();
-
- assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
-
- pids = hashmap_new(NULL);
- if (!pids)
- return log_oom();
-
- seen = set_new(&string_hash_ops);
- if (!seen)
- return log_oom();
-
- STRV_FOREACH(directory, directories) {
- _cleanup_closedir_ DIR *d;
- struct dirent *de;
-
- d = opendir(*directory);
- if (!d) {
- if (errno == ENOENT)
- continue;
-
- return log_error_errno(errno, "Failed to open directory %s: %m", *directory);
- }
-
- FOREACH_DIRENT(de, d, break) {
- _cleanup_free_ char *path = NULL;
- pid_t pid;
- int r;
-
- if (!dirent_is_file(de))
- continue;
-
- if (set_contains(seen, de->d_name)) {
- log_debug("%1$s/%2$s skipped (%2$s was already seen).", *directory, de->d_name);
- continue;
- }
-
- r = set_put_strdup(seen, de->d_name);
- if (r < 0)
- return log_oom();
-
- path = strjoin(*directory, "/", de->d_name);
- if (!path)
- return log_oom();
-
- if (null_or_empty_path(path)) {
- log_debug("%s is empty (a mask).", path);
- continue;
- }
-
- pid = fork();
- if (pid < 0) {
- log_error_errno(errno, "Failed to fork: %m");
- continue;
- } else if (pid == 0) {
- char *_argv[2];
-
- assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
-
- if (!argv) {
- _argv[0] = path;
- _argv[1] = NULL;
- argv = _argv;
- } else
- argv[0] = path;
-
- execv(path, argv);
- return log_error_errno(errno, "Failed to execute %s: %m", path);
- }
-
- log_debug("Spawned %s as " PID_FMT ".", path, pid);
-
- r = hashmap_put(pids, PID_TO_PTR(pid), path);
- if (r < 0)
- return log_oom();
- path = NULL;
- }
- }
-
- /* Abort execution of this process after the timout. We simply
- * rely on SIGALRM as default action terminating the process,
- * and turn on alarm(). */
-
- if (timeout != USEC_INFINITY)
- alarm((timeout + USEC_PER_SEC - 1) / USEC_PER_SEC);
-
- while (!hashmap_isempty(pids)) {
- _cleanup_free_ char *path = NULL;
- pid_t pid;
-
- pid = PTR_TO_PID(hashmap_first_key(pids));
- assert(pid > 0);
-
- path = hashmap_remove(pids, PID_TO_PTR(pid));
- assert(path);
-
- wait_for_terminate_and_warn(path, pid, true);
- }
-
- return 0;
-}
-
-void execute_directories(const char* const* directories, usec_t timeout, char *argv[]) {
- pid_t executor_pid;
- int r;
- char *name;
- char **dirs = (char**) directories;
-
- assert(!strv_isempty(dirs));
-
- name = basename(dirs[0]);
- assert(!isempty(name));
-
- /* Executes all binaries in the directories in parallel and waits
- * for them to finish. Optionally a timeout is applied. If a file
- * with the same name exists in more than one directory, the
- * earliest one wins. */
-
- executor_pid = fork();
- if (executor_pid < 0) {
- log_error_errno(errno, "Failed to fork: %m");
- return;
-
- } else if (executor_pid == 0) {
- r = do_execute(dirs, timeout, argv);
- _exit(r < 0 ? EXIT_FAILURE : EXIT_SUCCESS);
- }
-
- wait_for_terminate_and_warn(name, executor_pid, true);
-}
-
bool plymouth_running(void) {
return access("/run/plymouth/pid", F_OK) >= 0;
}
k = strspn(display+1, "0123456789");
- f = new(char, strlen("/tmp/.X11-unix/X") + k + 1);
+ f = new(char, STRLEN("/tmp/.X11-unix/X") + k + 1);
if (!f)
return -ENOMEM;
return 0;
}
-int block_get_whole_disk(dev_t d, dev_t *ret) {
- char *p, *s;
- int r;
- unsigned n, m;
-
- assert(ret);
-
- /* If it has a queue this is good enough for us */
- if (asprintf(&p, "/sys/dev/block/%u:%u/queue", major(d), minor(d)) < 0)
- return -ENOMEM;
-
- r = access(p, F_OK);
- free(p);
-
- if (r >= 0) {
- *ret = d;
- return 0;
- }
-
- /* If it is a partition find the originating device */
- if (asprintf(&p, "/sys/dev/block/%u:%u/partition", major(d), minor(d)) < 0)
- return -ENOMEM;
-
- r = access(p, F_OK);
- free(p);
-
- if (r < 0)
- return -ENOENT;
-
- /* Get parent dev_t */
- if (asprintf(&p, "/sys/dev/block/%u:%u/../dev", major(d), minor(d)) < 0)
- return -ENOMEM;
-
- r = read_one_line_file(p, &s);
- free(p);
-
- if (r < 0)
- return r;
-
- r = sscanf(s, "%u:%u", &m, &n);
- free(s);
-
- if (r != 2)
- return -EINVAL;
-
- /* Only return this if it is really good enough for us. */
- if (asprintf(&p, "/sys/dev/block/%u:%u/queue", m, n) < 0)
- return -ENOMEM;
-
- r = access(p, F_OK);
- free(p);
+bool kexec_loaded(void) {
+ _cleanup_free_ char *s = NULL;
- if (r >= 0) {
- *ret = makedev(m, n);
- return 0;
- }
+ if (read_one_line_file("/sys/kernel/kexec_loaded", &s) < 0)
+ return false;
- return -ENOENT;
-}
-
-bool kexec_loaded(void) {
- bool loaded = false;
- char *s;
-
- if (read_one_line_file("/sys/kernel/kexec_loaded", &s) >= 0) {
- if (s[0] == '1')
- loaded = true;
- free(s);
- }
- return loaded;
+ return s[0] == '1';
}
int prot_from_flags(int flags) {
}
}
-int fork_agent(pid_t *pid, const int except[], unsigned n_except, const char *path, ...) {
- bool stdout_is_tty, stderr_is_tty;
- pid_t parent_pid, agent_pid;
- sigset_t ss, saved_ss;
- unsigned n, i;
- va_list ap;
- char **l;
-
- assert(pid);
- assert(path);
-
- /* Spawns a temporary TTY agent, making sure it goes away when
- * we go away */
-
- parent_pid = getpid();
-
- /* First we temporarily block all signals, so that the new
- * child has them blocked initially. This way, we can be sure
- * that SIGTERMs are not lost we might send to the agent. */
- assert_se(sigfillset(&ss) >= 0);
- assert_se(sigprocmask(SIG_SETMASK, &ss, &saved_ss) >= 0);
-
- agent_pid = fork();
- if (agent_pid < 0) {
- assert_se(sigprocmask(SIG_SETMASK, &saved_ss, NULL) >= 0);
- return -errno;
- }
-
- if (agent_pid != 0) {
- assert_se(sigprocmask(SIG_SETMASK, &saved_ss, NULL) >= 0);
- *pid = agent_pid;
- return 0;
- }
-
- /* In the child:
- *
- * Make sure the agent goes away when the parent dies */
- if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0)
- _exit(EXIT_FAILURE);
-
- /* Make sure we actually can kill the agent, if we need to, in
- * case somebody invoked us from a shell script that trapped
- * SIGTERM or so... */
- (void) reset_all_signal_handlers();
- (void) reset_signal_mask();
-
- /* Check whether our parent died before we were able
- * to set the death signal and unblock the signals */
- if (getppid() != parent_pid)
- _exit(EXIT_SUCCESS);
-
- /* Don't leak fds to the agent */
- close_all_fds(except, n_except);
-
- stdout_is_tty = isatty(STDOUT_FILENO);
- stderr_is_tty = isatty(STDERR_FILENO);
-
- if (!stdout_is_tty || !stderr_is_tty) {
- int fd;
-
- /* Detach from stdout/stderr. and reopen
- * /dev/tty for them. This is important to
- * ensure that when systemctl is started via
- * popen() or a similar call that expects to
- * read EOF we actually do generate EOF and
- * not delay this indefinitely by because we
- * keep an unused copy of stdin around. */
- fd = open("/dev/tty", O_WRONLY);
- if (fd < 0) {
- log_error_errno(errno, "Failed to open /dev/tty: %m");
- _exit(EXIT_FAILURE);
- }
-
- if (!stdout_is_tty && dup2(fd, STDOUT_FILENO) < 0) {
- log_error_errno(errno, "Failed to dup2 /dev/tty: %m");
- _exit(EXIT_FAILURE);
- }
-
- if (!stderr_is_tty && dup2(fd, STDERR_FILENO) < 0) {
- log_error_errno(errno, "Failed to dup2 /dev/tty: %m");
- _exit(EXIT_FAILURE);
- }
-
- if (fd > STDERR_FILENO)
- close(fd);
- }
-
- /* Count arguments */
- va_start(ap, path);
- for (n = 0; va_arg(ap, char*); n++)
- ;
- va_end(ap);
-
- /* Allocate strv */
- l = alloca(sizeof(char *) * (n + 1));
-
- /* Fill in arguments */
- va_start(ap, path);
- for (i = 0; i <= n; i++)
- l[i] = va_arg(ap, char*);
- va_end(ap);
-
- execv(path, l);
- _exit(EXIT_FAILURE);
-}
-
bool in_initrd(void) {
struct statfs s;
const void *p;
int comparison;
+ assert(!size_multiply_overflow(nmemb, size));
+
l = 0;
u = nmemb;
while (l < u) {
idx = (l + u) / 2;
- p = (void *)(((const char *) base) + (idx * size));
+ p = (const uint8_t*) base + idx * size;
comparison = compar(key, p, arg);
if (comparison < 0)
u = idx;
device = openat(dirfd(d), de->d_name, O_DIRECTORY|O_RDONLY|O_CLOEXEC|O_NOCTTY);
if (device < 0) {
- if (errno == ENOENT || errno == ENOTDIR)
+ if (IN_SET(errno, ENOENT, ENOTDIR))
continue;
return -errno;
if (asprintf(&userns_fd_path, "/proc/self/fd/%d", userns_fd) < 0)
return -ENOMEM;
- r = files_same(userns_fd_path, "/proc/self/ns/user");
+ r = files_same(userns_fd_path, "/proc/self/ns/user", 0);
if (r < 0)
return r;
if (r)
uint64_t system_tasks_max(void) {
-#if SIZEOF_PID_T == 4
-#define TASKS_MAX ((uint64_t) (INT32_MAX-1))
-#elif SIZEOF_PID_T == 2
-#define TASKS_MAX ((uint64_t) (INT16_MAX-1))
-#else
-#error "Unknown pid_t size"
-#endif
-
- _cleanup_free_ char *value = NULL, *root = NULL;
uint64_t a = TASKS_MAX, b = TASKS_MAX;
+ _cleanup_free_ char *root = NULL;
/* Determine the maximum number of tasks that may run on this system. We check three sources to determine this
* limit:
*
- * a) the maximum value for the pid_t type
+ * a) the maximum tasks value the kernel allows on this architecture
* b) the cgroups pids_max attribute for the system
- * c) the kernel's configure maximum PID value
+ * c) the kernel's configured maximum PID value
*
* And then pick the smallest of the three */
- if (read_one_line_file("/proc/sys/kernel/pid_max", &value) >= 0)
- (void) safe_atou64(value, &a);
+ (void) procfs_tasks_get_limit(&a);
if (cg_get_root_path(&root) >= 0) {
- value = mfree(value);
+ _cleanup_free_ char *value = NULL;
if (cg_get_attribute("pids", root, "pids.max", &value) >= 0)
(void) safe_atou64(value, &b);
return m / max;
}
-int update_reboot_parameter_and_warn(const char *param) {
- int r;
+int version(void) {
+ puts(PACKAGE_STRING "\n"
+ SYSTEMD_FEATURES);
+ return 0;
+}
- if (isempty(param)) {
- if (unlink("/run/systemd/reboot-param") < 0) {
- if (errno == ENOENT)
- 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);
- return log_warning_errno(errno, "Failed to unlink reboot parameter file: %m");
+ 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++;
}
- return 0;
- }
+ 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++;
+ }
- RUN_WITH_UMASK(0022) {
- r = write_string_file("/run/systemd/reboot-param", param, WRITE_STRING_FILE_CREATE);
- if (r < 0)
- return log_warning_errno(r, "Failed to write reboot parameter file: %m");
+ if (is_digit(*s1))
+ return 1;
+ if (is_digit(*s2))
+ return -1;
+
+ if (first != 0)
+ return first;
}
- return 0;
+ return strcmp(os1, os2);
}
-int version(void) {
- puts(PACKAGE_STRING "\n"
- SYSTEMD_FEATURES);
- return 0;
+/* 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", 0);
+ if (r < 0)
+ log_debug_errno(r, "Failed to turn off coredumps, ignoring: %m");
}