main.c revision b8f8323268ae974288e49a7cc6c1c47531e436c9
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland/***
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland This file is part of systemd.
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland Copyright 2010 Lennart Poettering
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland systemd is free software; you can redistribute it and/or modify it
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland under the terms of the GNU Lesser General Public License as published by
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland the Free Software Foundation; either version 2.1 of the License, or
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland (at your option) any later version.
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland systemd is distributed in the hope that it will be useful, but
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland WITHOUT ANY WARRANTY; without even the implied warranty of
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland Lesser General Public License for more details.
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland You should have received a copy of the GNU Lesser General Public License
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland along with systemd; If not, see <http://www.gnu.org/licenses/>.
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland***/
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland#include <dbus/dbus.h>
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland#include <stdio.h>
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland#include <errno.h>
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland#include <string.h>
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland#include <unistd.h>
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland#include <sys/types.h>
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland#include <sys/stat.h>
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland#include <getopt.h>
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland#include <signal.h>
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland#include <sys/wait.h>
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland#include <fcntl.h>
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland#include <sys/prctl.h>
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland#include <sys/mount.h>
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland#include "manager.h"
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland#include "log.h"
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland#include "load-fragment.h"
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland#include "fdset.h"
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland#include "special.h"
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland#include "conf-parser.h"
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland#include "bus-errors.h"
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland#include "missing.h"
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland#include "label.h"
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland#include "build.h"
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland#include "strv.h"
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland#include "def.h"
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland#include "virt.h"
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland#include "watchdog.h"
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland#include "path-util.h"
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland#include "mount-setup.h"
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland#include "loopback-setup.h"
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland#include "kmod-setup.h"
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland#include "hostname-setup.h"
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland#include "machine-id-setup.h"
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland#include "locale-setup.h"
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland#include "hwclock.h"
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland#include "selinux-setup.h"
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland#include "ima-setup.h"
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterlandstatic enum {
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland ACTION_RUN,
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland ACTION_HELP,
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland ACTION_TEST,
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland ACTION_DUMP_CONFIGURATION_ITEMS,
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland ACTION_DONE
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland} arg_action = ACTION_RUN;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterlandstatic char *arg_default_unit = NULL;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterlandstatic ManagerRunningAs arg_running_as = _MANAGER_RUNNING_AS_INVALID;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterlandstatic bool arg_dump_core = true;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterlandstatic bool arg_crash_shell = false;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterlandstatic int arg_crash_chvt = -1;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterlandstatic bool arg_confirm_spawn = false;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterlandstatic bool arg_show_status = true;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland#ifdef HAVE_SYSV_COMPAT
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterlandstatic bool arg_sysv_console = true;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland#endif
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterlandstatic char **arg_default_controllers = NULL;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterlandstatic char ***arg_join_controllers = NULL;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterlandstatic ExecOutput arg_default_std_output = EXEC_OUTPUT_JOURNAL;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterlandstatic ExecOutput arg_default_std_error = EXEC_OUTPUT_INHERIT;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterlandstatic usec_t arg_runtime_watchdog = 0;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterlandstatic usec_t arg_shutdown_watchdog = 10 * USEC_PER_MINUTE;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterlandstatic FILE* serialization = NULL;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterlandstatic void nop_handler(int sig) {
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland}
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland_noreturn_ static void crash(int sig) {
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland if (!arg_dump_core)
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland log_error("Caught <%s>, not dumping core.", signal_to_string(sig));
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland else {
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland struct sigaction sa;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland pid_t pid;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland /* We want to wait for the core process, hence let's enable SIGCHLD */
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland zero(sa);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland sa.sa_handler = nop_handler;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland sa.sa_flags = SA_NOCLDSTOP|SA_RESTART;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland assert_se(sigaction(SIGCHLD, &sa, NULL) == 0);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland if ((pid = fork()) < 0)
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland log_error("Caught <%s>, cannot fork for core dump: %s", signal_to_string(sig), strerror(errno));
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland else if (pid == 0) {
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland struct rlimit rl;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland /* Enable default signal handler for core dump */
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland zero(sa);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland sa.sa_handler = SIG_DFL;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland assert_se(sigaction(sig, &sa, NULL) == 0);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland /* Don't limit the core dump size */
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland zero(rl);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland rl.rlim_cur = RLIM_INFINITY;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland rl.rlim_max = RLIM_INFINITY;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland setrlimit(RLIMIT_CORE, &rl);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland /* Just to be sure... */
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland assert_se(chdir("/") == 0);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland /* Raise the signal again */
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland raise(sig);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland assert_not_reached("We shouldn't be here...");
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland _exit(1);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland } else {
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland siginfo_t status;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland int r;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland /* Order things nicely. */
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland if ((r = wait_for_terminate(pid, &status)) < 0)
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland log_error("Caught <%s>, waitpid() failed: %s", signal_to_string(sig), strerror(-r));
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland else if (status.si_code != CLD_DUMPED)
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland log_error("Caught <%s>, core dump failed.", signal_to_string(sig));
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland else
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland log_error("Caught <%s>, dumped core as pid %lu.", signal_to_string(sig), (unsigned long) pid);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland }
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland }
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland if (arg_crash_chvt)
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland chvt(arg_crash_chvt);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland if (arg_crash_shell) {
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland struct sigaction sa;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland pid_t pid;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland log_info("Executing crash shell in 10s...");
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland sleep(10);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland /* Let the kernel reap children for us */
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland zero(sa);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland sa.sa_handler = SIG_IGN;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland sa.sa_flags = SA_NOCLDSTOP|SA_NOCLDWAIT|SA_RESTART;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland assert_se(sigaction(SIGCHLD, &sa, NULL) == 0);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland if ((pid = fork()) < 0)
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland log_error("Failed to fork off crash shell: %s", strerror(errno));
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland else if (pid == 0) {
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland int fd, r;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland if ((fd = acquire_terminal("/dev/console", false, true, true)) < 0)
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland log_error("Failed to acquire terminal: %s", strerror(-fd));
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland else if ((r = make_stdio(fd)) < 0)
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland log_error("Failed to duplicate terminal fd: %s", strerror(-r));
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland execl("/bin/sh", "/bin/sh", NULL);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland log_error("execl() failed: %s", strerror(errno));
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland _exit(1);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland }
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland log_info("Successfully spawned crash shell as pid %lu.", (unsigned long) pid);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland }
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland log_info("Freezing execution.");
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland freeze();
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland}
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterlandstatic void install_crash_handler(void) {
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland struct sigaction sa;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland zero(sa);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland sa.sa_handler = crash;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland sa.sa_flags = SA_NODEFER;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland sigaction_many(&sa, SIGNALS_CRASH_HANDLER, -1);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland}
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterlandstatic int console_setup(bool do_reset) {
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland int tty_fd, r;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland /* If we are init, we connect stdin/stdout/stderr to /dev/null
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland * and make sure we don't have a controlling tty. */
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland release_terminal();
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland if (!do_reset)
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland return 0;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland tty_fd = open_terminal("/dev/console", O_WRONLY|O_NOCTTY|O_CLOEXEC);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland if (tty_fd < 0) {
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland log_error("Failed to open /dev/console: %s", strerror(-tty_fd));
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland return -tty_fd;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland }
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland /* We don't want to force text mode.
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland * plymouth may be showing pictures already from initrd. */
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland r = reset_terminal_fd(tty_fd, false);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland if (r < 0)
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland log_error("Failed to reset /dev/console: %s", strerror(-r));
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland close_nointr_nofail(tty_fd);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland return r;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland}
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterlandstatic int set_default_unit(const char *u) {
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland char *c;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland assert(u);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland if (!(c = strdup(u)))
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland return -ENOMEM;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland free(arg_default_unit);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland arg_default_unit = c;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland return 0;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland}
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterlandstatic int parse_proc_cmdline_word(const char *word) {
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland static const char * const rlmap[] = {
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland "emergency", SPECIAL_EMERGENCY_TARGET,
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland "-b", SPECIAL_EMERGENCY_TARGET,
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland "single", SPECIAL_RESCUE_TARGET,
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland "-s", SPECIAL_RESCUE_TARGET,
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland "s", SPECIAL_RESCUE_TARGET,
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland "S", SPECIAL_RESCUE_TARGET,
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland "1", SPECIAL_RESCUE_TARGET,
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland "2", SPECIAL_RUNLEVEL2_TARGET,
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland "3", SPECIAL_RUNLEVEL3_TARGET,
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland "4", SPECIAL_RUNLEVEL4_TARGET,
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland "5", SPECIAL_RUNLEVEL5_TARGET,
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland };
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland assert(word);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland if (startswith(word, "systemd.unit="))
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland return set_default_unit(word + 13);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland else if (startswith(word, "systemd.log_target=")) {
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland if (log_set_target_from_string(word + 19) < 0)
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland log_warning("Failed to parse log target %s. Ignoring.", word + 19);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland } else if (startswith(word, "systemd.log_level=")) {
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland if (log_set_max_level_from_string(word + 18) < 0)
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland log_warning("Failed to parse log level %s. Ignoring.", word + 18);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland } else if (startswith(word, "systemd.log_color=")) {
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland if (log_show_color_from_string(word + 18) < 0)
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland log_warning("Failed to parse log color setting %s. Ignoring.", word + 18);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland } else if (startswith(word, "systemd.log_location=")) {
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland if (log_show_location_from_string(word + 21) < 0)
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland log_warning("Failed to parse log location setting %s. Ignoring.", word + 21);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland } else if (startswith(word, "systemd.dump_core=")) {
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland int r;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland if ((r = parse_boolean(word + 18)) < 0)
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland log_warning("Failed to parse dump core switch %s. Ignoring.", word + 18);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland else
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland arg_dump_core = r;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland } else if (startswith(word, "systemd.crash_shell=")) {
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland int r;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland if ((r = parse_boolean(word + 20)) < 0)
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland log_warning("Failed to parse crash shell switch %s. Ignoring.", word + 20);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland else
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland arg_crash_shell = r;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland } else if (startswith(word, "systemd.confirm_spawn=")) {
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland int r;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland if ((r = parse_boolean(word + 22)) < 0)
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland log_warning("Failed to parse confirm spawn switch %s. Ignoring.", word + 22);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland else
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland arg_confirm_spawn = r;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland } else if (startswith(word, "systemd.crash_chvt=")) {
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland int k;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland if (safe_atoi(word + 19, &k) < 0)
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland log_warning("Failed to parse crash chvt switch %s. Ignoring.", word + 19);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland else
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland arg_crash_chvt = k;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland } else if (startswith(word, "systemd.show_status=")) {
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland int r;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland if ((r = parse_boolean(word + 20)) < 0)
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland log_warning("Failed to parse show status switch %s. Ignoring.", word + 20);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland else
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland arg_show_status = r;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland } else if (startswith(word, "systemd.default_standard_output=")) {
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland int r;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland if ((r = exec_output_from_string(word + 32)) < 0)
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland log_warning("Failed to parse default standard output switch %s. Ignoring.", word + 32);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland else
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland arg_default_std_output = r;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland } else if (startswith(word, "systemd.default_standard_error=")) {
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland int r;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland if ((r = exec_output_from_string(word + 31)) < 0)
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland log_warning("Failed to parse default standard error switch %s. Ignoring.", word + 31);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland else
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland arg_default_std_error = r;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland } else if (startswith(word, "systemd.setenv=")) {
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland char *cenv, *eq;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland int r;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland cenv = strdup(word + 15);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland if (!cenv)
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland return -ENOMEM;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland eq = strchr(cenv, '=');
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland if (!eq) {
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland r = unsetenv(cenv);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland if (r < 0)
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland log_warning("unsetenv failed %s. Ignoring.", strerror(errno));
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland } else {
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland *eq = 0;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland r = setenv(cenv, eq + 1, 1);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland if (r < 0)
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland log_warning("setenv failed %s. Ignoring.", strerror(errno));
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland }
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland free(cenv);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland#ifdef HAVE_SYSV_COMPAT
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland } else if (startswith(word, "systemd.sysv_console=")) {
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland int r;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland if ((r = parse_boolean(word + 21)) < 0)
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland log_warning("Failed to parse SysV console switch %s. Ignoring.", word + 20);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland else
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland arg_sysv_console = r;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland#endif
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland } else if (startswith(word, "systemd.")) {
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland log_warning("Unknown kernel switch %s. Ignoring.", word);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland log_info("Supported kernel switches:\n"
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland "systemd.unit=UNIT Default unit to start\n"
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland "systemd.dump_core=0|1 Dump core on crash\n"
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland "systemd.crash_shell=0|1 Run shell on crash\n"
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland "systemd.crash_chvt=N Change to VT #N on crash\n"
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland "systemd.confirm_spawn=0|1 Confirm every process spawn\n"
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland "systemd.show_status=0|1 Show status updates on the console during bootup\n"
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland#ifdef HAVE_SYSV_COMPAT
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland "systemd.sysv_console=0|1 Connect output of SysV scripts to console\n"
62224350e5355e6834f7deb9d8a7d062a50cb7c2Casper H.S. Dik#endif
62224350e5355e6834f7deb9d8a7d062a50cb7c2Casper H.S. Dik "systemd.log_target=console|kmsg|journal|journal-or-kmsg|syslog|syslog-or-kmsg|null\n"
62224350e5355e6834f7deb9d8a7d062a50cb7c2Casper H.S. Dik " Log target\n"
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland "systemd.log_level=LEVEL Log level\n"
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland "systemd.log_color=0|1 Highlight important log messages\n"
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland "systemd.log_location=0|1 Include code location in log messages\n"
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland "systemd.default_standard_output=null|tty|syslog|syslog+console|kmsg|kmsg+console|journal|journal+console\n"
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland " Set default log output for services\n"
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland "systemd.default_standard_error=null|tty|syslog|syslog+console|kmsg|kmsg+console|journal|journal+console\n"
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland " Set default log error output for services\n");
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland } else if (streq(word, "quiet")) {
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland arg_show_status = false;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland#ifdef HAVE_SYSV_COMPAT
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland arg_sysv_console = false;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland#endif
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland } else {
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland unsigned i;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland /* SysV compatibility */
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland for (i = 0; i < ELEMENTSOF(rlmap); i += 2)
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland if (streq(word, rlmap[i]))
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland return set_default_unit(rlmap[i+1]);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland }
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland return 0;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland}
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterlandstatic int config_parse_level2(
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland const char *filename,
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland unsigned line,
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland const char *section,
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland const char *lvalue,
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland int ltype,
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland const char *rvalue,
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland void *data,
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland void *userdata) {
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland assert(filename);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland assert(lvalue);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland assert(rvalue);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland log_set_max_level_from_string(rvalue);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland return 0;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland}
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterlandstatic int config_parse_target(
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland const char *filename,
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland unsigned line,
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland const char *section,
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland const char *lvalue,
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland int ltype,
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland const char *rvalue,
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland void *data,
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland void *userdata) {
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland assert(filename);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland assert(lvalue);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland assert(rvalue);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland log_set_target_from_string(rvalue);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland return 0;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland}
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterlandstatic int config_parse_color(
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland const char *filename,
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland unsigned line,
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland const char *section,
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland const char *lvalue,
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland int ltype,
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland const char *rvalue,
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland void *data,
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland void *userdata) {
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland assert(filename);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland assert(lvalue);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland assert(rvalue);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland log_show_color_from_string(rvalue);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland return 0;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland}
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterlandstatic int config_parse_location(
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland const char *filename,
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland unsigned line,
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland const char *section,
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland const char *lvalue,
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland int ltype,
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland const char *rvalue,
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland void *data,
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland void *userdata) {
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland assert(filename);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland assert(lvalue);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland assert(rvalue);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland log_show_location_from_string(rvalue);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland return 0;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland}
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterlandstatic int config_parse_cpu_affinity2(
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland const char *filename,
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland unsigned line,
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland const char *section,
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland const char *lvalue,
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland int ltype,
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland const char *rvalue,
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland void *data,
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland void *userdata) {
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland char *w;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland size_t l;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland char *state;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland cpu_set_t *c = NULL;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland unsigned ncpus = 0;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland assert(filename);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland assert(lvalue);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland assert(rvalue);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland FOREACH_WORD_QUOTED(w, l, rvalue, state) {
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland char *t;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland int r;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland unsigned cpu;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland if (!(t = strndup(w, l)))
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland return -ENOMEM;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland r = safe_atou(t, &cpu);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland free(t);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland if (!c)
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland if (!(c = cpu_set_malloc(&ncpus)))
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland return -ENOMEM;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland if (r < 0 || cpu >= ncpus) {
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland log_error("[%s:%u] Failed to parse CPU affinity: %s", filename, line, rvalue);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland CPU_FREE(c);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland return -EBADMSG;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland }
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland CPU_SET_S(cpu, CPU_ALLOC_SIZE(ncpus), c);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland }
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland if (c) {
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland if (sched_setaffinity(0, CPU_ALLOC_SIZE(ncpus), c) < 0)
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland log_warning("Failed to set CPU affinity: %m");
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland CPU_FREE(c);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland }
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland return 0;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland}
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterlandstatic void strv_free_free(char ***l) {
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland char ***i;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland if (!l)
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland return;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland for (i = l; *i; i++)
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland strv_free(*i);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland free(l);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland}
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterlandstatic void free_join_controllers(void) {
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland if (!arg_join_controllers)
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland return;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland strv_free_free(arg_join_controllers);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland arg_join_controllers = NULL;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland}
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterlandstatic int config_parse_join_controllers(
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland const char *filename,
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland unsigned line,
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland const char *section,
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland const char *lvalue,
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland int ltype,
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland const char *rvalue,
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland void *data,
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland void *userdata) {
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland unsigned n = 0;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland char *state, *w;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland size_t length;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland assert(filename);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland assert(lvalue);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland assert(rvalue);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland free_join_controllers();
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland FOREACH_WORD_QUOTED(w, length, rvalue, state) {
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland char *s, **l;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland s = strndup(w, length);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland if (!s)
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland return -ENOMEM;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland l = strv_split(s, ",");
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland free(s);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland strv_uniq(l);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland if (strv_length(l) <= 1) {
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland strv_free(l);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland continue;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland }
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland if (!arg_join_controllers) {
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland arg_join_controllers = new(char**, 2);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland if (!arg_join_controllers) {
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland strv_free(l);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland return -ENOMEM;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland }
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland arg_join_controllers[0] = l;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland arg_join_controllers[1] = NULL;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland n = 1;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland } else {
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland char ***a;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland char ***t;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland t = new0(char**, n+2);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland if (!t) {
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland strv_free(l);
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland return -ENOMEM;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland }
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland n = 0;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland for (a = arg_join_controllers; *a; a++) {
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland if (strv_overlap(*a, l)) {
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland char **c;
5c51f1241dbbdf2656d0e10011981411ed0c9673Moriah Waterland
c = strv_merge(*a, l);
if (!c) {
strv_free(l);
strv_free_free(t);
return -ENOMEM;
}
strv_free(l);
l = c;
} else {
char **c;
c = strv_copy(*a);
if (!c) {
strv_free(l);
strv_free_free(t);
return -ENOMEM;
}
t[n++] = c;
}
}
t[n++] = strv_uniq(l);
strv_free_free(arg_join_controllers);
arg_join_controllers = t;
}
}
return 0;
}
static int parse_config_file(void) {
const ConfigTableItem items[] = {
{ "Manager", "LogLevel", config_parse_level2, 0, NULL },
{ "Manager", "LogTarget", config_parse_target, 0, NULL },
{ "Manager", "LogColor", config_parse_color, 0, NULL },
{ "Manager", "LogLocation", config_parse_location, 0, NULL },
{ "Manager", "DumpCore", config_parse_bool, 0, &arg_dump_core },
{ "Manager", "CrashShell", config_parse_bool, 0, &arg_crash_shell },
{ "Manager", "ShowStatus", config_parse_bool, 0, &arg_show_status },
#ifdef HAVE_SYSV_COMPAT
{ "Manager", "SysVConsole", config_parse_bool, 0, &arg_sysv_console },
#endif
{ "Manager", "CrashChVT", config_parse_int, 0, &arg_crash_chvt },
{ "Manager", "CPUAffinity", config_parse_cpu_affinity2, 0, NULL },
{ "Manager", "DefaultControllers", config_parse_strv, 0, &arg_default_controllers },
{ "Manager", "DefaultStandardOutput", config_parse_output, 0, &arg_default_std_output },
{ "Manager", "DefaultStandardError", config_parse_output, 0, &arg_default_std_error },
{ "Manager", "JoinControllers", config_parse_join_controllers, 0, &arg_join_controllers },
{ "Manager", "RuntimeWatchdogSec", config_parse_usec, 0, &arg_runtime_watchdog },
{ "Manager", "ShutdownWatchdogSec", config_parse_usec, 0, &arg_shutdown_watchdog },
{ NULL, NULL, NULL, 0, NULL }
};
FILE *f;
const char *fn;
int r;
fn = arg_running_as == MANAGER_SYSTEM ? SYSTEM_CONFIG_FILE : USER_CONFIG_FILE;
f = fopen(fn, "re");
if (!f) {
if (errno == ENOENT)
return 0;
log_warning("Failed to open configuration file '%s': %m", fn);
return 0;
}
r = config_parse(fn, f, "Manager\0", config_item_table_lookup, (void*) items, false, NULL);
if (r < 0)
log_warning("Failed to parse configuration file: %s", strerror(-r));
fclose(f);
return 0;
}
static int parse_proc_cmdline(void) {
char *line, *w, *state;
int r;
size_t l;
/* Don't read /proc/cmdline if we are in a container, since
* that is only relevant for the host system */
if (detect_container(NULL) > 0)
return 0;
if ((r = read_one_line_file("/proc/cmdline", &line)) < 0) {
log_warning("Failed to read /proc/cmdline, ignoring: %s", strerror(-r));
return 0;
}
FOREACH_WORD_QUOTED(w, l, line, state) {
char *word;
if (!(word = strndup(w, l))) {
r = -ENOMEM;
goto finish;
}
r = parse_proc_cmdline_word(word);
free(word);
if (r < 0)
goto finish;
}
r = 0;
finish:
free(line);
return r;
}
static int parse_argv(int argc, char *argv[]) {
enum {
ARG_LOG_LEVEL = 0x100,
ARG_LOG_TARGET,
ARG_LOG_COLOR,
ARG_LOG_LOCATION,
ARG_UNIT,
ARG_SYSTEM,
ARG_USER,
ARG_TEST,
ARG_DUMP_CONFIGURATION_ITEMS,
ARG_DUMP_CORE,
ARG_CRASH_SHELL,
ARG_CONFIRM_SPAWN,
ARG_SHOW_STATUS,
ARG_SYSV_CONSOLE,
ARG_DESERIALIZE,
ARG_INTROSPECT,
ARG_DEFAULT_STD_OUTPUT,
ARG_DEFAULT_STD_ERROR
};
static const struct option options[] = {
{ "log-level", required_argument, NULL, ARG_LOG_LEVEL },
{ "log-target", required_argument, NULL, ARG_LOG_TARGET },
{ "log-color", optional_argument, NULL, ARG_LOG_COLOR },
{ "log-location", optional_argument, NULL, ARG_LOG_LOCATION },
{ "unit", required_argument, NULL, ARG_UNIT },
{ "system", no_argument, NULL, ARG_SYSTEM },
{ "user", no_argument, NULL, ARG_USER },
{ "test", no_argument, NULL, ARG_TEST },
{ "help", no_argument, NULL, 'h' },
{ "dump-configuration-items", no_argument, NULL, ARG_DUMP_CONFIGURATION_ITEMS },
{ "dump-core", optional_argument, NULL, ARG_DUMP_CORE },
{ "crash-shell", optional_argument, NULL, ARG_CRASH_SHELL },
{ "confirm-spawn", optional_argument, NULL, ARG_CONFIRM_SPAWN },
{ "show-status", optional_argument, NULL, ARG_SHOW_STATUS },
#ifdef HAVE_SYSV_COMPAT
{ "sysv-console", optional_argument, NULL, ARG_SYSV_CONSOLE },
#endif
{ "deserialize", required_argument, NULL, ARG_DESERIALIZE },
{ "introspect", optional_argument, NULL, ARG_INTROSPECT },
{ "default-standard-output", required_argument, NULL, ARG_DEFAULT_STD_OUTPUT, },
{ "default-standard-error", required_argument, NULL, ARG_DEFAULT_STD_ERROR, },
{ NULL, 0, NULL, 0 }
};
int c, r;
assert(argc >= 1);
assert(argv);
if (getpid() == 1)
opterr = 0;
while ((c = getopt_long(argc, argv, "hDbsz:", options, NULL)) >= 0)
switch (c) {
case ARG_LOG_LEVEL:
if ((r = log_set_max_level_from_string(optarg)) < 0) {
log_error("Failed to parse log level %s.", optarg);
return r;
}
break;
case ARG_LOG_TARGET:
if ((r = log_set_target_from_string(optarg)) < 0) {
log_error("Failed to parse log target %s.", optarg);
return r;
}
break;
case ARG_LOG_COLOR:
if (optarg) {
if ((r = log_show_color_from_string(optarg)) < 0) {
log_error("Failed to parse log color setting %s.", optarg);
return r;
}
} else
log_show_color(true);
break;
case ARG_LOG_LOCATION:
if (optarg) {
if ((r = log_show_location_from_string(optarg)) < 0) {
log_error("Failed to parse log location setting %s.", optarg);
return r;
}
} else
log_show_location(true);
break;
case ARG_DEFAULT_STD_OUTPUT:
if ((r = exec_output_from_string(optarg)) < 0) {
log_error("Failed to parse default standard output setting %s.", optarg);
return r;
} else
arg_default_std_output = r;
break;
case ARG_DEFAULT_STD_ERROR:
if ((r = exec_output_from_string(optarg)) < 0) {
log_error("Failed to parse default standard error output setting %s.", optarg);
return r;
} else
arg_default_std_error = r;
break;
case ARG_UNIT:
if ((r = set_default_unit(optarg)) < 0) {
log_error("Failed to set default unit %s: %s", optarg, strerror(-r));
return r;
}
break;
case ARG_SYSTEM:
arg_running_as = MANAGER_SYSTEM;
break;
case ARG_USER:
arg_running_as = MANAGER_USER;
break;
case ARG_TEST:
arg_action = ACTION_TEST;
break;
case ARG_DUMP_CONFIGURATION_ITEMS:
arg_action = ACTION_DUMP_CONFIGURATION_ITEMS;
break;
case ARG_DUMP_CORE:
r = optarg ? parse_boolean(optarg) : 1;
if (r < 0) {
log_error("Failed to parse dump core boolean %s.", optarg);
return r;
}
arg_dump_core = r;
break;
case ARG_CRASH_SHELL:
r = optarg ? parse_boolean(optarg) : 1;
if (r < 0) {
log_error("Failed to parse crash shell boolean %s.", optarg);
return r;
}
arg_crash_shell = r;
break;
case ARG_CONFIRM_SPAWN:
r = optarg ? parse_boolean(optarg) : 1;
if (r < 0) {
log_error("Failed to parse confirm spawn boolean %s.", optarg);
return r;
}
arg_confirm_spawn = r;
break;
case ARG_SHOW_STATUS:
r = optarg ? parse_boolean(optarg) : 1;
if (r < 0) {
log_error("Failed to parse show status boolean %s.", optarg);
return r;
}
arg_show_status = r;
break;
#ifdef HAVE_SYSV_COMPAT
case ARG_SYSV_CONSOLE:
r = optarg ? parse_boolean(optarg) : 1;
if (r < 0) {
log_error("Failed to parse SysV console boolean %s.", optarg);
return r;
}
arg_sysv_console = r;
break;
#endif
case ARG_DESERIALIZE: {
int fd;
FILE *f;
if ((r = safe_atoi(optarg, &fd)) < 0 || fd < 0) {
log_error("Failed to parse deserialize option %s.", optarg);
return r;
}
if (!(f = fdopen(fd, "r"))) {
log_error("Failed to open serialization fd: %m");
return r;
}
if (serialization)
fclose(serialization);
serialization = f;
break;
}
case ARG_INTROSPECT: {
const char * const * i = NULL;
for (i = bus_interface_table; *i; i += 2)
if (!optarg || streq(i[0], optarg)) {
fputs(DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE
"<node>\n", stdout);
fputs(i[1], stdout);
fputs("</node>\n", stdout);
if (optarg)
break;
}
if (!i[0] && optarg)
log_error("Unknown interface %s.", optarg);
arg_action = ACTION_DONE;
break;
}
case 'h':
arg_action = ACTION_HELP;
break;
case 'D':
log_set_max_level(LOG_DEBUG);
break;
case 'b':
case 's':
case 'z':
/* Just to eat away the sysvinit kernel
* cmdline args without getopt() error
* messages that we'll parse in
* parse_proc_cmdline_word() or ignore. */
case '?':
default:
if (getpid() != 1) {
log_error("Unknown option code %c", c);
return -EINVAL;
}
break;
}
if (optind < argc && getpid() != 1) {
/* Hmm, when we aren't run as init system
* let's complain about excess arguments */
log_error("Excess arguments.");
return -EINVAL;
}
if (detect_container(NULL) > 0) {
char **a;
/* All /proc/cmdline arguments the kernel didn't
* understand it passed to us. We're not really
* interested in that usually since /proc/cmdline is
* more interesting and complete. With one exception:
* if we are run in a container /proc/cmdline is not
* relevant for the container, hence we rely on argv[]
* instead. */
for (a = argv; a < argv + argc; a++)
if ((r = parse_proc_cmdline_word(*a)) < 0)
return r;
}
return 0;
}
static int help(void) {
printf("%s [OPTIONS...]\n\n"
"Starts up and maintains the system or user services.\n\n"
" -h --help Show this help\n"
" --test Determine startup sequence, dump it and exit\n"
" --dump-configuration-items Dump understood unit configuration items\n"
" --introspect[=INTERFACE] Extract D-Bus interface data\n"
" --unit=UNIT Set default unit\n"
" --system Run a system instance, even if PID != 1\n"
" --user Run a user instance\n"
" --dump-core[=0|1] Dump core on crash\n"
" --crash-shell[=0|1] Run shell on crash\n"
" --confirm-spawn[=0|1] Ask for confirmation when spawning processes\n"
" --show-status[=0|1] Show status updates on the console during bootup\n"
#ifdef HAVE_SYSV_COMPAT
" --sysv-console[=0|1] Connect output of SysV scripts to console\n"
#endif
" --log-target=TARGET Set log target (console, journal, syslog, kmsg, journal-or-kmsg, syslog-or-kmsg, null)\n"
" --log-level=LEVEL Set log level (debug, info, notice, warning, err, crit, alert, emerg)\n"
" --log-color[=0|1] Highlight important log messages\n"
" --log-location[=0|1] Include code location in log messages\n"
" --default-standard-output= Set default standard output for services\n"
" --default-standard-error= Set default standard error output for services\n",
program_invocation_short_name);
return 0;
}
static int prepare_reexecute(Manager *m, FILE **_f, FDSet **_fds) {
FILE *f = NULL;
FDSet *fds = NULL;
int r;
assert(m);
assert(_f);
assert(_fds);
/* Make sure nothing is really destructed when we shut down */
m->n_reloading ++;
if ((r = manager_open_serialization(m, &f)) < 0) {
log_error("Failed to create serialization file: %s", strerror(-r));
goto fail;
}
if (!(fds = fdset_new())) {
r = -ENOMEM;
log_error("Failed to allocate fd set: %s", strerror(-r));
goto fail;
}
if ((r = manager_serialize(m, f, fds)) < 0) {
log_error("Failed to serialize state: %s", strerror(-r));
goto fail;
}
if (fseeko(f, 0, SEEK_SET) < 0) {
log_error("Failed to rewind serialization fd: %m");
goto fail;
}
if ((r = fd_cloexec(fileno(f), false)) < 0) {
log_error("Failed to disable O_CLOEXEC for serialization: %s", strerror(-r));
goto fail;
}
if ((r = fdset_cloexec(fds, false)) < 0) {
log_error("Failed to disable O_CLOEXEC for serialization fds: %s", strerror(-r));
goto fail;
}
*_f = f;
*_fds = fds;
return 0;
fail:
fdset_free(fds);
if (f)
fclose(f);
return r;
}
static struct dual_timestamp* parse_initrd_timestamp(struct dual_timestamp *t) {
const char *e;
unsigned long long a, b;
assert(t);
e = getenv("RD_TIMESTAMP");
if (!e)
return NULL;
if (sscanf(e, "%llu %llu", &a, &b) != 2)
return NULL;
t->realtime = (usec_t) a;
t->monotonic = (usec_t) b;
return t;
}
static void test_mtab(void) {
char *p;
/* Check that /etc/mtab is a symlink */
if (readlink_malloc("/etc/mtab", &p) >= 0) {
bool b;
b = streq(p, "/proc/self/mounts") || streq(p, "/proc/mounts");
free(p);
if (b)
return;
}
log_warning("/etc/mtab is not a symlink or not pointing to /proc/self/mounts. "
"This is not supported anymore. "
"Please make sure to replace this file by a symlink to avoid incorrect or misleading mount(8) output.");
}
static void test_usr(void) {
/* Check that /usr is not a separate fs */
if (dir_is_empty("/usr") <= 0)
return;
log_warning("/usr appears to be on its own filesytem and is not already mounted. This is not a supported setup. "
"Some things will probably break (sometimes even silently) in mysterious ways. "
"Consult http://freedesktop.org/wiki/Software/systemd/separate-usr-is-broken for more information.");
}
static void test_cgroups(void) {
if (access("/proc/cgroups", F_OK) >= 0)
return;
log_warning("CONFIG_CGROUPS was not set when your kernel was compiled. "
"Systems without control groups are not supported. "
"We will now sleep for 10s, and then continue boot-up. "
"Expect breakage and please do not file bugs. "
"Instead fix your kernel and enable CONFIG_CGROUPS. "
"Consult http://0pointer.de/blog/projects/cgroups-vs-cgroups.html for more information.");
sleep(10);
}
static int do_switch_root(const char *switch_root) {
int r;
if (path_equal(switch_root, "/"))
return 0;
if (chdir(switch_root) < 0) {
r = -errno;
goto fail;
}
if (mount(switch_root, "/", NULL, MS_MOVE, NULL) < 0) {
r = -errno;
chdir("/");
goto fail;
}
if (chroot(".") < 0)
log_warning("Failed to change root, ignoring: %m");
/* FIXME: remove old root */
return 0;
fail:
log_error("Failed to switch root, ignoring: %s", strerror(-r));
return r;
}
int main(int argc, char *argv[]) {
Manager *m = NULL;
int r, retval = EXIT_FAILURE;
usec_t before_startup, after_startup;
char timespan[FORMAT_TIMESPAN_MAX];
FDSet *fds = NULL;
bool reexecute = false;
const char *shutdown_verb = NULL;
dual_timestamp initrd_timestamp = { 0ULL, 0ULL };
static char systemd[] = "systemd";
bool is_reexec = false;
int j;
bool loaded_policy = false;
bool arm_reboot_watchdog = false;
char *switch_root = NULL, *switch_root_init = NULL;
#ifdef HAVE_SYSV_COMPAT
if (getpid() != 1 && strstr(program_invocation_short_name, "init")) {
/* This is compatibility support for SysV, where
* calling init as a user is identical to telinit. */
errno = -ENOENT;
execv(SYSTEMCTL_BINARY_PATH, argv);
log_error("Failed to exec " SYSTEMCTL_BINARY_PATH ": %m");
return 1;
}
#endif
/* Determine if this is a reexecution or normal bootup. We do
* the full command line parsing much later, so let's just
* have a quick peek here. */
for (j = 1; j < argc; j++)
if (streq(argv[j], "--deserialize")) {
is_reexec = true;
break;
}
/* If we get started via the /sbin/init symlink then we are
called 'init'. After a subsequent reexecution we are then
called 'systemd'. That is confusing, hence let's call us
systemd right-away. */
program_invocation_short_name = systemd;
prctl(PR_SET_NAME, systemd);
saved_argv = argv;
saved_argc = argc;
log_show_color(isatty(STDERR_FILENO) > 0);
log_show_location(false);
log_set_max_level(LOG_INFO);
if (getpid() == 1) {
arg_running_as = MANAGER_SYSTEM;
log_set_target(detect_container(NULL) > 0 ? LOG_TARGET_JOURNAL : LOG_TARGET_JOURNAL_OR_KMSG);
if (!is_reexec) {
if (selinux_setup(&loaded_policy) < 0)
goto finish;
if (ima_setup() < 0)
goto finish;
}
log_open();
if (label_init(NULL) < 0)
goto finish;
if (!is_reexec)
if (hwclock_is_localtime() > 0) {
int min;
r = hwclock_apply_localtime_delta(&min);
if (r < 0)
log_error("Failed to apply local time delta, ignoring: %s", strerror(-r));
else
log_info("RTC configured in localtime, applying delta of %i minutes to system time.", min);
}
} else {
arg_running_as = MANAGER_USER;
log_set_target(LOG_TARGET_AUTO);
log_open();
}
/* Initialize default unit */
if (set_default_unit(SPECIAL_DEFAULT_TARGET) < 0)
goto finish;
/* By default, mount "cpu" and "cpuacct" together */
arg_join_controllers = new(char**, 2);
if (!arg_join_controllers)
goto finish;
arg_join_controllers[0] = strv_new("cpu", "cpuacct", NULL);
arg_join_controllers[1] = NULL;
if (!arg_join_controllers[0])
goto finish;
/* Mount /proc, /sys and friends, so that /proc/cmdline and
* /proc/$PID/fd is available. */
if (geteuid() == 0 && !getenv("SYSTEMD_SKIP_API_MOUNTS")) {
r = mount_setup(loaded_policy);
if (r < 0)
goto finish;
}
/* Reset all signal handlers. */
assert_se(reset_all_signal_handlers() == 0);
/* If we are init, we can block sigkill. Yay. */
ignore_signals(SIGNALS_IGNORE, -1);
if (parse_config_file() < 0)
goto finish;
if (arg_running_as == MANAGER_SYSTEM)
if (parse_proc_cmdline() < 0)
goto finish;
log_parse_environment();
if (parse_argv(argc, argv) < 0)
goto finish;
if (arg_action == ACTION_TEST && geteuid() == 0) {
log_error("Don't run test mode as root.");
goto finish;
}
if (arg_running_as == MANAGER_SYSTEM &&
arg_action == ACTION_RUN &&
running_in_chroot() > 0) {
log_error("Cannot be run in a chroot() environment.");
goto finish;
}
if (arg_action == ACTION_HELP) {
retval = help();
goto finish;
} else if (arg_action == ACTION_DUMP_CONFIGURATION_ITEMS) {
unit_dump_config_items(stdout);
retval = EXIT_SUCCESS;
goto finish;
} else if (arg_action == ACTION_DONE) {
retval = EXIT_SUCCESS;
goto finish;
}
assert_se(arg_action == ACTION_RUN || arg_action == ACTION_TEST);
/* Close logging fds, in order not to confuse fdset below */
log_close();
/* Remember open file descriptors for later deserialization */
if (serialization) {
r = fdset_new_fill(&fds);
if (r < 0) {
log_error("Failed to allocate fd set: %s", strerror(-r));
goto finish;
}
assert_se(fdset_remove(fds, fileno(serialization)) >= 0);
} else
close_all_fds(NULL, 0);
/* Set up PATH unless it is already set */
setenv("PATH",
#ifdef HAVE_SPLIT_USR
"/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
#else
"/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin",
#endif
arg_running_as == MANAGER_SYSTEM);
if (arg_running_as == MANAGER_SYSTEM) {
/* Parse the data passed to us. We leave this
* variables set, but the manager later on will not
* pass them on to our children. */
parse_initrd_timestamp(&initrd_timestamp);
/* Unset some environment variables passed in from the
* kernel that don't really make sense for us. */
unsetenv("HOME");
unsetenv("TERM");
/* When we are invoked by a shell, these might be set,
* but make little sense to pass on */
unsetenv("PWD");
unsetenv("SHLVL");
unsetenv("_");
/* When we are invoked by a tool chroot-like such as
* nspawn, these might be set, but make little sense
* to pass on */
unsetenv("USER");
unsetenv("LOGNAME");
/* All other variables are left as is, so that clients
* can still read them via /proc/1/environ */
}
/* Move out of the way, so that we won't block unmounts */
assert_se(chdir("/") == 0);
if (arg_running_as == MANAGER_SYSTEM) {
/* Become a session leader if we aren't one yet. */
setsid();
/* Disable the umask logic */
umask(0);
}
/* Make sure D-Bus doesn't fiddle with the SIGPIPE handlers */
dbus_connection_set_change_sigpipe(FALSE);
/* Reset the console, but only if this is really init and we
* are freshly booted */
if (arg_running_as == MANAGER_SYSTEM && arg_action == ACTION_RUN) {
console_setup(getpid() == 1 && !is_reexec);
make_null_stdio();
}
/* Open the logging devices, if possible and necessary */
log_open();
/* Make sure we leave a core dump without panicing the
* kernel. */
if (getpid() == 1)
install_crash_handler();
if (geteuid() == 0 && !getenv("SYSTEMD_SKIP_API_MOUNTS")) {
r = mount_cgroup_controllers(arg_join_controllers);
if (r < 0)
goto finish;
}
log_full(arg_running_as == MANAGER_SYSTEM ? LOG_INFO : LOG_DEBUG,
PACKAGE_STRING " running in %s mode. (" SYSTEMD_FEATURES "; " DISTRIBUTION ")", manager_running_as_to_string(arg_running_as));
if (arg_running_as == MANAGER_SYSTEM && !is_reexec) {
locale_setup();
if (arg_show_status || plymouth_running())
status_welcome();
kmod_setup();
hostname_setup();
machine_id_setup();
loopback_setup();
test_mtab();
test_usr();
test_cgroups();
}
if (arg_running_as == MANAGER_SYSTEM && arg_runtime_watchdog > 0)
watchdog_set_timeout(&arg_runtime_watchdog);
r = manager_new(arg_running_as, &m);
if (r < 0) {
log_error("Failed to allocate manager object: %s", strerror(-r));
goto finish;
}
m->confirm_spawn = arg_confirm_spawn;
#ifdef HAVE_SYSV_COMPAT
m->sysv_console = arg_sysv_console;
#endif
m->default_std_output = arg_default_std_output;
m->default_std_error = arg_default_std_error;
m->runtime_watchdog = arg_runtime_watchdog;
m->shutdown_watchdog = arg_shutdown_watchdog;
if (dual_timestamp_is_set(&initrd_timestamp))
m->initrd_timestamp = initrd_timestamp;
if (arg_default_controllers)
manager_set_default_controllers(m, arg_default_controllers);
manager_set_show_status(m, arg_show_status);
before_startup = now(CLOCK_MONOTONIC);
r = manager_startup(m, serialization, fds);
if (r < 0)
log_error("Failed to fully start up daemon: %s", strerror(-r));
if (fds) {
/* This will close all file descriptors that were opened, but
* not claimed by any unit. */
fdset_free(fds);
fds = NULL;
}
if (serialization) {
fclose(serialization);
serialization = NULL;
} else {
DBusError error;
Unit *target = NULL;
Job *default_unit_job;
dbus_error_init(&error);
log_debug("Activating default unit: %s", arg_default_unit);
r = manager_load_unit(m, arg_default_unit, NULL, &error, &target);
if (r < 0) {
log_error("Failed to load default target: %s", bus_error(&error, r));
dbus_error_free(&error);
} else if (target->load_state == UNIT_ERROR)
log_error("Failed to load default target: %s", strerror(-target->load_error));
else if (target->load_state == UNIT_MASKED)
log_error("Default target masked.");
if (!target || target->load_state != UNIT_LOADED) {
log_info("Trying to load rescue target...");
r = manager_load_unit(m, SPECIAL_RESCUE_TARGET, NULL, &error, &target);
if (r < 0) {
log_error("Failed to load rescue target: %s", bus_error(&error, r));
dbus_error_free(&error);
goto finish;
} else if (target->load_state == UNIT_ERROR) {
log_error("Failed to load rescue target: %s", strerror(-target->load_error));
goto finish;
} else if (target->load_state == UNIT_MASKED) {
log_error("Rescue target masked.");
goto finish;
}
}
assert(target->load_state == UNIT_LOADED);
if (arg_action == ACTION_TEST) {
printf("-> By units:\n");
manager_dump_units(m, stdout, "\t");
}
r = manager_add_job(m, JOB_START, target, JOB_REPLACE, false, &error, &default_unit_job);
if (r < 0) {
log_error("Failed to start default target: %s", bus_error(&error, r));
dbus_error_free(&error);
goto finish;
}
m->default_unit_job_id = default_unit_job->id;
after_startup = now(CLOCK_MONOTONIC);
log_full(arg_action == ACTION_TEST ? LOG_INFO : LOG_DEBUG,
"Loaded units and determined initial transaction in %s.",
format_timespan(timespan, sizeof(timespan), after_startup - before_startup));
if (arg_action == ACTION_TEST) {
printf("-> By jobs:\n");
manager_dump_jobs(m, stdout, "\t");
retval = EXIT_SUCCESS;
goto finish;
}
}
for (;;) {
r = manager_loop(m);
if (r < 0) {
log_error("Failed to run mainloop: %s", strerror(-r));
goto finish;
}
switch (m->exit_code) {
case MANAGER_EXIT:
retval = EXIT_SUCCESS;
log_debug("Exit.");
goto finish;
case MANAGER_RELOAD:
log_info("Reloading.");
r = manager_reload(m);
if (r < 0)
log_error("Failed to reload: %s", strerror(-r));
break;
case MANAGER_REEXECUTE:
if (prepare_reexecute(m, &serialization, &fds) < 0)
goto finish;
reexecute = true;
log_notice("Reexecuting.");
goto finish;
case MANAGER_SWITCH_ROOT:
/* Steal the switch root parameters */
switch_root = m->switch_root;
switch_root_init = m->switch_root_init;
m->switch_root = m->switch_root_init = NULL;
if (!switch_root_init)
if (prepare_reexecute(m, &serialization, &fds) < 0)
goto finish;
reexecute = true;
log_notice("Switching root.");
goto finish;
case MANAGER_REBOOT:
case MANAGER_POWEROFF:
case MANAGER_HALT:
case MANAGER_KEXEC: {
static const char * const table[_MANAGER_EXIT_CODE_MAX] = {
[MANAGER_REBOOT] = "reboot",
[MANAGER_POWEROFF] = "poweroff",
[MANAGER_HALT] = "halt",
[MANAGER_KEXEC] = "kexec"
};
assert_se(shutdown_verb = table[m->exit_code]);
arm_reboot_watchdog = m->exit_code == MANAGER_REBOOT;
log_notice("Shutting down.");
goto finish;
}
default:
assert_not_reached("Unknown exit code.");
}
}
finish:
if (m)
manager_free(m);
free(arg_default_unit);
strv_free(arg_default_controllers);
free_join_controllers();
dbus_shutdown();
label_finish();
if (reexecute) {
const char **args;
unsigned i, args_size;
/* Close and disarm the watchdog, so that the new
* instance can reinitialize it, but doesn't get
* rebooted while we do that */
watchdog_close(true);
if (switch_root)
do_switch_root(switch_root);
args_size = MAX(5, argc+1);
args = newa(const char*, args_size);
if (!switch_root_init) {
char sfd[16];
/* First try to spawn ourselves with the right
* path, and with full serialization. We do
* this only if the user didn't specify an
* explicit init to spawn. */
assert(serialization);
assert(fds);
snprintf(sfd, sizeof(sfd), "%i", fileno(serialization));
char_array_0(sfd);
i = 0;
args[i++] = SYSTEMD_BINARY_PATH;
args[i++] = arg_running_as == MANAGER_SYSTEM ? "--system" : "--user";
args[i++] = "--deserialize";
args[i++] = sfd;
args[i++] = NULL;
assert(i <= args_size);
execv(args[0], (char* const*) args);
}
/* Try the fallback, if there is any, without any
* serialization. We pass the original argv[] and
* envp[]. (Well, modulo the ordering changes due to
* getopt() in argv[], and some cleanups in envp[],
* but let's hope that doesn't matter.) */
if (serialization) {
fclose(serialization);
serialization = NULL;
}
if (fds) {
fdset_free(fds);
fds = NULL;
}
for (j = 1, i = 1; j < argc; j++)
args[i++] = argv[j];
args[i++] = NULL;
assert(i <= args_size);
if (switch_root_init) {
args[0] = switch_root_init;
execv(args[0], (char* const*) args);
log_warning("Failed to execute configured init, trying fallback: %m");
}
args[0] = "/sbin/init";
execv(args[0], (char* const*) args);
log_warning("Failed to execute /sbin/init, trying fallback: %m");
args[0] = "/bin/sh";
args[1] = NULL;
execv(args[0], (char* const*) args);
log_error("Failed to execute /bin/sh, giving up: %m");
}
if (serialization)
fclose(serialization);
if (fds)
fdset_free(fds);
if (shutdown_verb) {
const char * command_line[] = {
SYSTEMD_SHUTDOWN_BINARY_PATH,
shutdown_verb,
NULL
};
char **env_block;
if (arm_reboot_watchdog && arg_shutdown_watchdog > 0) {
char e[32];
/* If we reboot let's set the shutdown
* watchdog and tell the shutdown binary to
* repeatedly ping it */
watchdog_set_timeout(&arg_shutdown_watchdog);
watchdog_close(false);
/* Tell the binary how often to ping */
snprintf(e, sizeof(e), "WATCHDOG_USEC=%llu", (unsigned long long) arg_shutdown_watchdog);
char_array_0(e);
env_block = strv_append(environ, e);
} else {
env_block = strv_copy(environ);
watchdog_close(true);
}
execve(SYSTEMD_SHUTDOWN_BINARY_PATH, (char **) command_line, env_block);
free(env_block);
log_error("Failed to execute shutdown binary, freezing: %m");
}
if (getpid() == 1)
freeze();
return retval;
}