From 5f932eb9af7a5e4723855bcd776c2acaa2a31932 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 2 Feb 2016 01:52:01 +0100 Subject: nspawn: add new --chdir= switch Fixes: #2192 --- man/systemd-nspawn.xml | 7 +++++++ man/systemd.nspawn.xml | 8 ++++++++ src/nspawn/nspawn-gperf.gperf | 1 + src/nspawn/nspawn-settings.c | 1 + src/nspawn/nspawn-settings.h | 4 +++- src/nspawn/nspawn.c | 33 ++++++++++++++++++++++++++++++++- 6 files changed, 52 insertions(+), 2 deletions(-) diff --git a/man/systemd-nspawn.xml b/man/systemd-nspawn.xml index 28b91dee24..1cfef93df1 100644 --- a/man/systemd-nspawn.xml +++ b/man/systemd-nspawn.xml @@ -260,6 +260,13 @@ + + + + Change to the specified working directory before invoking the process in the container. Expects + an absolute path in the container's file system namespace. + + diff --git a/man/systemd.nspawn.xml b/man/systemd.nspawn.xml index f39e1ad42c..7e604246f6 100644 --- a/man/systemd.nspawn.xml +++ b/man/systemd.nspawn.xml @@ -186,6 +186,14 @@ switch. + + WorkingDirectory= + + Selects the working directory for the process invoked in the container. Expects an absolute + path in the container's file system namespace. This corresponds to the command line + switch. + + Capability= DropCapability= diff --git a/src/nspawn/nspawn-gperf.gperf b/src/nspawn/nspawn-gperf.gperf index 58f9f4c635..08020d510c 100644 --- a/src/nspawn/nspawn-gperf.gperf +++ b/src/nspawn/nspawn-gperf.gperf @@ -24,6 +24,7 @@ Exec.DropCapability, config_parse_capability, 0, offsetof(Settings, Exec.KillSignal, config_parse_signal, 0, offsetof(Settings, kill_signal) Exec.Personality, config_parse_personality, 0, offsetof(Settings, personality) Exec.MachineID, config_parse_id128, 0, offsetof(Settings, machine_id) +Exec.WorkingDirectory, config_parse_path, 0, offsetof(Settings, working_directory) Files.ReadOnly, config_parse_tristate, 0, offsetof(Settings, read_only) Files.Volatile, config_parse_volatile_mode, 0, offsetof(Settings, volatile_mode) Files.Bind, config_parse_bind, 0, 0 diff --git a/src/nspawn/nspawn-settings.c b/src/nspawn/nspawn-settings.c index d6b64d8d5a..3c552e0734 100644 --- a/src/nspawn/nspawn-settings.c +++ b/src/nspawn/nspawn-settings.c @@ -74,6 +74,7 @@ Settings* settings_free(Settings *s) { strv_free(s->parameters); strv_free(s->environment); free(s->user); + free(s->working_directory); strv_free(s->network_interfaces); strv_free(s->network_macvlan); diff --git a/src/nspawn/nspawn-settings.h b/src/nspawn/nspawn-settings.h index 10230a5b83..236a1baa4f 100644 --- a/src/nspawn/nspawn-settings.h +++ b/src/nspawn/nspawn-settings.h @@ -40,7 +40,8 @@ typedef enum SettingsMask { SETTING_READ_ONLY = 1 << 9, SETTING_VOLATILE_MODE = 1 << 10, SETTING_CUSTOM_MOUNTS = 1 << 11, - _SETTINGS_MASK_ALL = (1 << 12) -1 + SETTING_WORKING_DIRECTORY = 1 << 12, + _SETTINGS_MASK_ALL = (1 << 13) -1 } SettingsMask; typedef struct Settings { @@ -54,6 +55,7 @@ typedef struct Settings { int kill_signal; unsigned long personality; sd_id128_t machine_id; + char *working_directory; /* [Image] */ int read_only; diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index 9dd4c051b2..0dae9984c9 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -114,6 +114,7 @@ typedef enum LinkJournal { static char *arg_directory = NULL; static char *arg_template = NULL; +static char *arg_chdir = NULL; static char *arg_user = NULL; static sd_id128_t arg_uuid = {}; static char *arg_machine = NULL; @@ -193,6 +194,7 @@ static void help(void) { " remove it after exit\n" " -i --image=PATH File system device or disk image for the container\n" " -b --boot Boot up full system (i.e. invoke init)\n" + " --chdir=PATH Set working directory in the container\n" " -u --user=USER Run the command under specified user or uid\n" " -M --machine=NAME Set the machine name for the container\n" " --uuid=UUID Set a specific machine UUID for the container\n" @@ -345,6 +347,7 @@ static int parse_argv(int argc, char *argv[]) { ARG_PRIVATE_USERS, ARG_KILL_SIGNAL, ARG_SETTINGS, + ARG_CHDIR, }; static const struct option options[] = { @@ -389,6 +392,7 @@ static int parse_argv(int argc, char *argv[]) { { "private-users", optional_argument, NULL, ARG_PRIVATE_USERS }, { "kill-signal", required_argument, NULL, ARG_KILL_SIGNAL }, { "settings", required_argument, NULL, ARG_SETTINGS }, + { "chdir", required_argument, NULL, ARG_CHDIR }, {} }; @@ -849,6 +853,19 @@ static int parse_argv(int argc, char *argv[]) { break; + case ARG_CHDIR: + if (!path_is_absolute(optarg)) { + log_error("Working directory %s is not an absolute path.", optarg); + return -EINVAL; + } + + r = free_and_strdup(&arg_chdir, optarg); + if (r < 0) + return log_oom(); + + arg_settings_mask |= SETTING_WORKING_DIRECTORY; + break; + case '?': return -EINVAL; @@ -2563,6 +2580,10 @@ static int inner_child( return -ESRCH; } + if (arg_chdir) + if (chdir(arg_chdir) < 0) + return log_error_errno(errno, "Failed to change to specified working directory %s: %m", arg_chdir); + /* Now, explicitly close the log, so that we * then can close all remaining fds. Closing * the log explicitly first has the benefit @@ -2598,7 +2619,9 @@ static int inner_child( } else if (!strv_isempty(arg_parameters)) execvpe(arg_parameters[0], arg_parameters, env_use); else { - chdir(home ?: "/root"); + if (!arg_chdir) + chdir(home ?: "/root"); + execle("/bin/bash", "-bash", NULL, env_use); execle("/bin/sh", "-sh", NULL, env_use); } @@ -2903,6 +2926,13 @@ static int load_settings(void) { settings->parameters = NULL; } + if ((arg_settings_mask & SETTING_WORKING_DIRECTORY) == 0 && + settings->working_directory) { + free(arg_chdir); + arg_chdir = settings->working_directory; + settings->working_directory = NULL; + } + if ((arg_settings_mask & SETTING_ENVIRONMENT) == 0 && settings->environment) { strv_free(arg_setenv); @@ -3629,6 +3659,7 @@ finish: free(arg_image); free(arg_machine); free(arg_user); + free(arg_chdir); strv_free(arg_setenv); free(arg_network_bridge); strv_free(arg_network_interfaces); -- cgit v1.2.3-54-g00ecf From 021dd87bc055a5bfb2dcef83fc868fe24648b959 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 3 Feb 2016 18:28:40 +0100 Subject: resolved: apply epoch to system time from PID 1 For use in timesyncd we already defined a compile-time "epoch" value, which is based on the mtime of the NEWS file, and specifies a point in time we know lies in the past at runtime. timesyncd uses this to filter out nonsensical timestamp file data, and bump the system clock to a time that is after the build time of systemd. This patch adds similar bumping code to earliest PID 1 initialization, so that the system never continues operation with a clock that is in the 1970ies or even 1930s. --- src/basic/clock-util.c | 14 ++++++++++++++ src/basic/clock-util.h | 1 + src/core/main.c | 8 +++++++- 3 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/basic/clock-util.c b/src/basic/clock-util.c index 05788a360e..c64b2e5e8c 100644 --- a/src/basic/clock-util.c +++ b/src/basic/clock-util.c @@ -146,3 +146,17 @@ int clock_reset_timewarp(void) { return 0; } + +#define TIME_EPOCH_USEC ((usec_t) TIME_EPOCH * USEC_PER_SEC) + +int clock_apply_epoch(void) { + struct timespec ts; + + if (now(CLOCK_REALTIME) >= TIME_EPOCH_USEC) + return 0; + + if (clock_settime(CLOCK_REALTIME, timespec_store(&ts, TIME_EPOCH_USEC)) < 0) + return -errno; + + return 1; +} diff --git a/src/basic/clock-util.h b/src/basic/clock-util.h index fef2d471a6..09d46758f4 100644 --- a/src/basic/clock-util.h +++ b/src/basic/clock-util.h @@ -28,3 +28,4 @@ int clock_set_timezone(int *min); int clock_reset_timewarp(void); int clock_get_hwclock(struct tm *tm); int clock_set_hwclock(const struct tm *tm); +int clock_apply_epoch(void); diff --git a/src/core/main.c b/src/core/main.c index 99ef723fcb..fb27e897e4 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -1424,8 +1424,14 @@ int main(int argc, char *argv[]) { * saving time change. All kernel local time concepts will be treated * as UTC that way. */ - clock_reset_timewarp(); + (void) clock_reset_timewarp(); } + + r = clock_apply_epoch(); + if (r < 0) + log_error_errno(r, "Current system time is before build time, but cannot correct: %m"); + else if (r > 0) + log_info("System time before build time, advancing clock."); } /* Set the default for later on, but don't actually -- cgit v1.2.3-54-g00ecf From 7732f92bad5f24a4bd03bb357af46da56b0ac94d Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 3 Feb 2016 20:32:06 +0100 Subject: nspawn: optionally run a stub init process as PID 1 This adds a new switch --as-pid2, which allows running commands as PID 2, while a stub init process is run as PID 1. This is useful in order to run arbitrary commands in a container, as PID1's semantics are different from all other processes regarding reaping of unknown children or signal handling. --- Makefile.am | 2 + man/systemd-nspawn.xml | 65 ++++++++++++++-- man/systemd.nspawn.xml | 24 +++--- src/nspawn/nspawn-gperf.gperf | 3 +- src/nspawn/nspawn-settings.c | 93 ++++++++++++++++++++++- src/nspawn/nspawn-settings.h | 38 ++++++---- src/nspawn/nspawn-stub-pid1.c | 170 ++++++++++++++++++++++++++++++++++++++++++ src/nspawn/nspawn-stub-pid1.h | 22 ++++++ src/nspawn/nspawn.c | 52 ++++++++++--- 9 files changed, 426 insertions(+), 43 deletions(-) create mode 100644 src/nspawn/nspawn-stub-pid1.c create mode 100644 src/nspawn/nspawn-stub-pid1.h diff --git a/Makefile.am b/Makefile.am index a291c3c56f..f1f8315f30 100644 --- a/Makefile.am +++ b/Makefile.am @@ -2944,6 +2944,8 @@ systemd_nspawn_SOURCES = \ src/nspawn/nspawn-register.h \ src/nspawn/nspawn-setuid.c \ src/nspawn/nspawn-setuid.h \ + src/nspawn/nspawn-stub-pid1.c \ + src/nspawn/nspawn-stub-pid1.h \ src/core/mount-setup.c \ src/core/mount-setup.h \ src/core/loopback-setup.c \ diff --git a/man/systemd-nspawn.xml b/man/systemd-nspawn.xml index 1cfef93df1..86cdb4e124 100644 --- a/man/systemd-nspawn.xml +++ b/man/systemd-nspawn.xml @@ -248,16 +248,69 @@ . + + + + + Invoke the shell or specified program as process ID (PID) 2 instead of PID 1 (init). By + default, if neither this option nor is used, the selected binary is run as process with + PID 1, a mode only suitable for programs that are aware of the special semantics that the process with PID 1 + has on UNIX. For example, it needs to reap all processes reparented to it, and should implement + sysvinit compatible signal handling (specifically: it needs to reboot on SIGINT, reexecute + on SIGTERM, reload configuration on SIGHUP, and so on). With a minimal stub init + process is run as PID 1 and the selected binary is executed as PID 2 (and hence does not need to implement any + special semantics). The stub init process will reap processes as necessary and react appropriately to + signals. It is recommended to use this mode to invoke arbitrary commands in containers, unless they have been + modified to run correctly as PID 1. Or in other words: this switch should be used for pretty much all commands, + except when the command refers to an init or shell implementation, as these are generally capable of running + correctly as PID 1). This option may not be combined with or + . + + + - Automatically search for an init binary and - invoke it instead of a shell or a user supplied program. If - this option is used, arguments specified on the command line - are used as arguments for the init binary. This option may not - be combined with . - + Automatically search for an init binary and invoke it as PID 1, instead of a shell or a user + supplied program. If this option is used, arguments specified on the command line are used as arguments for the + init binary. This option may not be combined with or + . + + The following table explains the different modes of invocation and relationship to + (see above): + + + Invocation Mode + + + + + + Switch + Explanation + + + + + Neither nor specified + The passed parameters are interpreted as command line, which is executed as PID 1 in the container. + + + + specified + The passed parameters are interpreted as command line, which are executed as PID 2 in the container. A stub init process is run as PID 1. + + + + specified + An init binary as automatically searched and run as PID 1 in the container. The passed parameters are used as invocation parameters for this process. + + + + +
+
diff --git a/man/systemd.nspawn.xml b/man/systemd.nspawn.xml index 7e604246f6..c07a4b0243 100644 --- a/man/systemd.nspawn.xml +++ b/man/systemd.nspawn.xml @@ -141,15 +141,21 @@ Boot= - Takes a boolean argument, which defaults to off. If - enabled, systemd-nspawn will automatically - search for an init executable and invoke - it. In this case, the specified parameters using - Parameters= are passed as additional - arguments to the init process. This - setting corresponds to the switch on - the systemd-nspawn command - line. + Takes a boolean argument, which defaults to off. If enabled, systemd-nspawn + will automatically search for an init executable and invoke it. In this case, the + specified parameters using Parameters= are passed as additional arguments to the + init process. This setting corresponds to the switch on the + systemd-nspawn command line. This option may not be combined with + ProcessTwo=yes. + + + + ProcessTwo= + + Takes a boolean argument, which defaults to off. If enabled, the specified program is run as + PID 2. A stub init process is run as PID 1. This setting corresponds to the switch + on the systemd-nspawn command line. This option may not be combined with + Boot=yes. diff --git a/src/nspawn/nspawn-gperf.gperf b/src/nspawn/nspawn-gperf.gperf index 08020d510c..116655cdd2 100644 --- a/src/nspawn/nspawn-gperf.gperf +++ b/src/nspawn/nspawn-gperf.gperf @@ -15,7 +15,8 @@ struct ConfigPerfItem; %struct-type %includes %% -Exec.Boot, config_parse_tristate, 0, offsetof(Settings, boot) +Exec.Boot, config_parse_boot, 0, 0 +Exec.ProcessTwo, config_parse_pid2, 0, 0, Exec.Parameters, config_parse_strv, 0, offsetof(Settings, parameters) Exec.Environment, config_parse_strv, 0, offsetof(Settings, environment) Exec.User, config_parse_string, 0, offsetof(Settings, user) diff --git a/src/nspawn/nspawn-settings.c b/src/nspawn/nspawn-settings.c index 3c552e0734..12524d3b89 100644 --- a/src/nspawn/nspawn-settings.c +++ b/src/nspawn/nspawn-settings.c @@ -24,6 +24,7 @@ #include "conf-parser.h" #include "nspawn-network.h" #include "nspawn-settings.h" +#include "parse-util.h" #include "process-util.h" #include "strv.h" #include "util.h" @@ -39,7 +40,7 @@ int settings_load(FILE *f, const char *path, Settings **ret) { if (!s) return -ENOMEM; - s->boot = -1; + s->start_mode = _START_MODE_INVALID; s->personality = PERSONALITY_INVALID; s->read_only = -1; @@ -303,3 +304,93 @@ int config_parse_veth_extra( return 0; } + +int config_parse_boot( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + Settings *settings = data; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + + r = parse_boolean(rvalue); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse Boot= parameter %s, ignoring: %m", rvalue); + return 0; + } + + if (r > 0) { + if (settings->start_mode == START_PID2) + goto conflict; + + settings->start_mode = START_BOOT; + } else { + if (settings->start_mode == START_BOOT) + goto conflict; + + if (settings->start_mode < 0) + settings->start_mode = START_PID1; + } + + return 0; + +conflict: + log_syntax(unit, LOG_ERR, filename, line, r, "Conflicting Boot= or ProcessTwo= setting found. Ignoring."); + return 0; +} + +int config_parse_pid2( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + Settings *settings = data; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + + r = parse_boolean(rvalue); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse ProcessTwo= parameter %s, ignoring: %m", rvalue); + return 0; + } + + if (r > 0) { + if (settings->start_mode == START_BOOT) + goto conflict; + + settings->start_mode = START_PID2; + } else { + if (settings->start_mode == START_PID2) + goto conflict; + + if (settings->start_mode < 0) + settings->start_mode = START_PID1; + } + + return 0; + +conflict: + log_syntax(unit, LOG_ERR, filename, line, r, "Conflicting Boot= or ProcessTwo= setting found. Ignoring."); + return 0; +} diff --git a/src/nspawn/nspawn-settings.h b/src/nspawn/nspawn-settings.h index 236a1baa4f..fdb07486da 100644 --- a/src/nspawn/nspawn-settings.h +++ b/src/nspawn/nspawn-settings.h @@ -27,26 +27,34 @@ #include "nspawn-expose-ports.h" #include "nspawn-mount.h" +typedef enum StartMode { + START_PID1, /* Run parameters as command line as process 1 */ + START_PID2, /* Use stub init process as PID 1, run parameters as command line as process 2 */ + START_BOOT, /* Search for init system, pass arguments as parameters */ + _START_MODE_MAX, + _START_MODE_INVALID = -1 +} StartMode; + typedef enum SettingsMask { - SETTING_BOOT = 1 << 0, - SETTING_ENVIRONMENT = 1 << 1, - SETTING_USER = 1 << 2, - SETTING_CAPABILITY = 1 << 3, - SETTING_KILL_SIGNAL = 1 << 4, - SETTING_PERSONALITY = 1 << 5, - SETTING_MACHINE_ID = 1 << 6, - SETTING_NETWORK = 1 << 7, - SETTING_EXPOSE_PORTS = 1 << 8, - SETTING_READ_ONLY = 1 << 9, - SETTING_VOLATILE_MODE = 1 << 10, - SETTING_CUSTOM_MOUNTS = 1 << 11, + SETTING_START_MODE = 1 << 0, + SETTING_ENVIRONMENT = 1 << 1, + SETTING_USER = 1 << 2, + SETTING_CAPABILITY = 1 << 3, + SETTING_KILL_SIGNAL = 1 << 4, + SETTING_PERSONALITY = 1 << 5, + SETTING_MACHINE_ID = 1 << 6, + SETTING_NETWORK = 1 << 7, + SETTING_EXPOSE_PORTS = 1 << 8, + SETTING_READ_ONLY = 1 << 9, + SETTING_VOLATILE_MODE = 1 << 10, + SETTING_CUSTOM_MOUNTS = 1 << 11, SETTING_WORKING_DIRECTORY = 1 << 12, - _SETTINGS_MASK_ALL = (1 << 13) -1 + _SETTINGS_MASK_ALL = (1 << 13) -1 } SettingsMask; typedef struct Settings { /* [Run] */ - int boot; + StartMode start_mode; char **parameters; char **environment; char *user; @@ -91,3 +99,5 @@ int config_parse_volatile_mode(const char *unit, const char *filename, unsigned int config_parse_bind(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_tmpfs(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_veth_extra(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_boot(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_pid2(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); diff --git a/src/nspawn/nspawn-stub-pid1.c b/src/nspawn/nspawn-stub-pid1.c new file mode 100644 index 0000000000..2de87e3c63 --- /dev/null +++ b/src/nspawn/nspawn-stub-pid1.c @@ -0,0 +1,170 @@ +/*** + This file is part of systemd. + + Copyright 2016 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 . +***/ + +#include +#include +#include + +#include "fd-util.h" +#include "log.h" +#include "nspawn-stub-pid1.h" +#include "process-util.h" +#include "signal-util.h" +#include "time-util.h" +#include "def.h" + +int stub_pid1(void) { + enum { + STATE_RUNNING, + STATE_REBOOT, + STATE_POWEROFF, + } state = STATE_RUNNING; + + sigset_t fullmask, oldmask, waitmask; + usec_t quit_usec = USEC_INFINITY; + pid_t pid; + int r; + + /* Implements a stub PID 1, that reaps all processes and processes a couple of standard signals. This is useful + * for allowing arbitrary processes run in a container, and still have all zombies reaped. */ + + assert_se(sigfillset(&fullmask) >= 0); + assert_se(sigprocmask(SIG_BLOCK, &fullmask, &oldmask) >= 0); + + pid = fork(); + if (pid < 0) + return log_error_errno(errno, "Failed to fork child pid: %m"); + + if (pid == 0) { + /* Return in the child */ + assert_se(sigprocmask(SIG_SETMASK, &oldmask, NULL) >= 0); + setsid(); + return 0; + } + + reset_all_signal_handlers(); + + log_close(); + close_all_fds(NULL, 0); + log_open(); + + rename_process("STUBINIT"); + + assert_se(sigemptyset(&waitmask) >= 0); + assert_se(sigset_add_many(&waitmask, + SIGCHLD, /* posix: process died */ + SIGINT, /* sysv: ctrl-alt-del */ + SIGRTMIN+3, /* systemd: halt */ + SIGRTMIN+4, /* systemd: poweroff */ + SIGRTMIN+5, /* systemd: reboot */ + SIGRTMIN+6, /* systemd: kexec */ + SIGRTMIN+13, /* systemd: halt */ + SIGRTMIN+14, /* systemd: poweroff */ + SIGRTMIN+15, /* systemd: reboot */ + SIGRTMIN+16, /* systemd: kexec */ + -1) >= 0); + + /* Note that we ignore SIGTERM (sysv's reexec), SIGHUP (reload), and all other signals here, since we don't + * support reexec/reloading in this stub process. */ + + for (;;) { + siginfo_t si; + usec_t current_usec; + + si.si_pid = 0; + r = waitid(P_ALL, 0, &si, WEXITED|WNOHANG); + if (r < 0) { + r = log_error_errno(errno, "Failed to reap children: %m"); + goto finish; + } + + current_usec = now(CLOCK_MONOTONIC); + + if (si.si_pid == pid || current_usec >= quit_usec) { + + /* The child we started ourselves died or we reached a timeout. */ + + if (state == STATE_REBOOT) { /* dispatch a queued reboot */ + (void) reboot(RB_AUTOBOOT); + r = log_error_errno(errno, "Failed to reboot: %m"); + goto finish; + + } else if (state == STATE_POWEROFF) + (void) reboot(RB_POWER_OFF); /* if this fails, fall back to normal exit. */ + + if (si.si_pid == pid && si.si_code == CLD_EXITED) + r = si.si_status; /* pass on exit code */ + else + r = 255; /* signal, coredump, timeout, … */ + + goto finish; + } + if (si.si_pid != 0) + /* We reaped something. Retry until there's nothing more to reap. */ + continue; + + if (quit_usec == USEC_INFINITY) + r = sigwaitinfo(&waitmask, &si); + else { + struct timespec ts; + r = sigtimedwait(&waitmask, &si, timespec_store(&ts, quit_usec - current_usec)); + } + if (r < 0) { + if (errno == EINTR) /* strace -p attach can result in EINTR, let's handle this nicely. */ + continue; + if (errno == EAGAIN) /* timeout reached */ + continue; + + r = log_error_errno(errno, "Failed to wait for signal: %m"); + goto finish; + } + + if (si.si_signo == SIGCHLD) + continue; /* Let's reap this */ + + if (state != STATE_RUNNING) + continue; + + /* Would love to use a switch() statement here, but SIGRTMIN is actually a function call, not a + * constant… */ + + if (si.si_signo == SIGRTMIN+3 || + si.si_signo == SIGRTMIN+4 || + si.si_signo == SIGRTMIN+13 || + si.si_signo == SIGRTMIN+14) + + state = STATE_POWEROFF; + + else if (si.si_signo == SIGINT || + si.si_signo == SIGRTMIN+5 || + si.si_signo == SIGRTMIN+6 || + si.si_signo == SIGRTMIN+15 || + si.si_signo == SIGRTMIN+16) + + state = STATE_REBOOT; + else + assert_not_reached("Got unexpected signal"); + + /* (void) kill_and_sigcont(pid, SIGTERM); */ + quit_usec = now(CLOCK_MONOTONIC) + DEFAULT_TIMEOUT_USEC; + } + +finish: + _exit(r < 0 ? EXIT_FAILURE : r); +} diff --git a/src/nspawn/nspawn-stub-pid1.h b/src/nspawn/nspawn-stub-pid1.h new file mode 100644 index 0000000000..36c1aaf5dd --- /dev/null +++ b/src/nspawn/nspawn-stub-pid1.h @@ -0,0 +1,22 @@ +#pragma once + +/*** + This file is part of systemd. + + Copyright 2016 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 . +***/ + +int stub_pid1(void); diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index 0dae9984c9..e7314ac29c 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -79,6 +79,7 @@ #include "nspawn-register.h" #include "nspawn-settings.h" #include "nspawn-setuid.h" +#include "nspawn-stub-pid1.h" #include "parse-util.h" #include "path-util.h" #include "process-util.h" @@ -123,7 +124,7 @@ static const char *arg_selinux_apifs_context = NULL; static const char *arg_slice = NULL; static bool arg_private_network = false; static bool arg_read_only = false; -static bool arg_boot = false; +static StartMode arg_start_mode = START_PID1; static bool arg_ephemeral = false; static LinkJournal arg_link_journal = LINK_AUTO; static bool arg_link_journal_try = false; @@ -193,6 +194,7 @@ static void help(void) { " -x --ephemeral Run container with snapshot of root directory, and\n" " remove it after exit\n" " -i --image=PATH File system device or disk image for the container\n" + " -a --as-pid2 Maintain a stub init as PID1, invoke binary as PID2\n" " -b --boot Boot up full system (i.e. invoke init)\n" " --chdir=PATH Set working directory in the container\n" " -u --user=USER Run the command under specified user or uid\n" @@ -358,6 +360,7 @@ static int parse_argv(int argc, char *argv[]) { { "ephemeral", no_argument, NULL, 'x' }, { "user", required_argument, NULL, 'u' }, { "private-network", no_argument, NULL, ARG_PRIVATE_NETWORK }, + { "as-pid2", no_argument, NULL, 'a' }, { "boot", no_argument, NULL, 'b' }, { "uuid", required_argument, NULL, ARG_UUID }, { "read-only", no_argument, NULL, ARG_READ_ONLY }, @@ -404,7 +407,7 @@ static int parse_argv(int argc, char *argv[]) { assert(argc >= 0); assert(argv); - while ((c = getopt_long(argc, argv, "+hD:u:bL:M:jS:Z:qi:xp:n", options, NULL)) >= 0) + while ((c = getopt_long(argc, argv, "+hD:u:abL:M:jS:Z:qi:xp:n", options, NULL)) >= 0) switch (c) { @@ -495,8 +498,23 @@ static int parse_argv(int argc, char *argv[]) { break; case 'b': - arg_boot = true; - arg_settings_mask |= SETTING_BOOT; + if (arg_start_mode == START_PID2) { + log_error("--boot and --as-pid2 may not be combined."); + return -EINVAL; + } + + arg_start_mode = START_BOOT; + arg_settings_mask |= SETTING_START_MODE; + break; + + case 'a': + if (arg_start_mode == START_BOOT) { + log_error("--boot and --as-pid2 may not be combined."); + return -EINVAL; + } + + arg_start_mode = START_PID2; + arg_settings_mask |= SETTING_START_MODE; break; case ARG_UUID: @@ -876,7 +894,7 @@ static int parse_argv(int argc, char *argv[]) { if (arg_share_system) arg_register = false; - if (arg_boot && arg_share_system) { + if (arg_start_mode != START_PID1 && arg_share_system) { log_error("--boot and --share-system may not be combined."); return -EINVAL; } @@ -924,7 +942,7 @@ static int parse_argv(int argc, char *argv[]) { if (!arg_parameters) return log_oom(); - arg_settings_mask |= SETTING_BOOT; + arg_settings_mask |= SETTING_START_MODE; } /* Load all settings from .nspawn files */ @@ -960,7 +978,7 @@ static int verify_arguments(void) { return -EINVAL; } - if (arg_boot && arg_kill_signal <= 0) + if (arg_start_mode == START_BOOT && arg_kill_signal <= 0) arg_kill_signal = SIGRTMIN+3; return 0; @@ -2584,6 +2602,12 @@ static int inner_child( if (chdir(arg_chdir) < 0) return log_error_errno(errno, "Failed to change to specified working directory %s: %m", arg_chdir); + if (arg_start_mode == START_PID2) { + r = stub_pid1(); + if (r < 0) + return r; + } + /* Now, explicitly close the log, so that we * then can close all remaining fds. Closing * the log explicitly first has the benefit @@ -2595,7 +2619,7 @@ static int inner_child( log_close(); (void) fdset_close_others(fds); - if (arg_boot) { + if (arg_start_mode == START_BOOT) { char **a; size_t m; @@ -2917,9 +2941,9 @@ static int load_settings(void) { /* Copy over bits from the settings, unless they have been * explicitly masked by command line switches. */ - if ((arg_settings_mask & SETTING_BOOT) == 0 && - settings->boot >= 0) { - arg_boot = settings->boot; + if ((arg_settings_mask & SETTING_START_MODE) == 0 && + settings->start_mode >= 0) { + arg_start_mode = settings->start_mode; strv_free(arg_parameters); arg_parameters = settings->parameters; @@ -3074,6 +3098,10 @@ int main(int argc, char *argv[]) { log_parse_environment(); log_open(); + /* Make sure rename_process() in the stub init process can work */ + saved_argv = argv; + saved_argc = argc; + r = parse_argv(argc, argv); if (r <= 0) goto finish; @@ -3180,7 +3208,7 @@ int main(int argc, char *argv[]) { } } - if (arg_boot) { + if (arg_start_mode == START_BOOT) { if (path_is_os_tree(arg_directory) <= 0) { log_error("Directory %s doesn't look like an OS root directory (os-release file is missing). Refusing.", arg_directory); r = -EINVAL; -- cgit v1.2.3-54-g00ecf From 2b26a72816aac6d28f44460ffe28882f006489c8 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 3 Feb 2016 20:33:38 +0100 Subject: nspawn: make sure --help fits it 79ch --- src/nspawn/nspawn.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index e7314ac29c..370161e9bf 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -235,8 +235,8 @@ static void help(void) { " capability\n" " --drop-capability=CAP Drop the specified capability from the default set\n" " --kill-signal=SIGNAL Select signal to use for shutting down PID 1\n" - " --link-journal=MODE Link up guest journal, one of no, auto, guest, host,\n" - " try-guest, try-host\n" + " --link-journal=MODE Link up guest journal, one of no, auto, guest, \n" + " host, try-guest, try-host\n" " -j Equivalent to --link-journal=try-guest\n" " --read-only Mount the root directory read-only\n" " --bind=PATH[:PATH[:OPTIONS]]\n" -- cgit v1.2.3-54-g00ecf From 59e73c5b102402ea18aec1f4d69b4daad574e82b Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 3 Feb 2016 20:34:04 +0100 Subject: gpt-auto: handle errors from blkid more correctly Let's make sure we don't choke if blkid_probe_lookup_value() returns a NULL string. Also, make sur we propagate the correct error when blkid_probe_lookup_value() fails. --- src/gpt-auto-generator/gpt-auto-generator.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/gpt-auto-generator/gpt-auto-generator.c b/src/gpt-auto-generator/gpt-auto-generator.c index a1bad9fcbe..9086ca5dd7 100644 --- a/src/gpt-auto-generator/gpt-auto-generator.c +++ b/src/gpt-auto-generator/gpt-auto-generator.c @@ -515,14 +515,15 @@ static int add_boot(const char *what) { return log_error_errno(errno ?: EIO, "Failed to probe %s: %m", what); (void) blkid_probe_lookup_value(b, "TYPE", &fstype, NULL); - if (!streq(fstype, "vfat")) { + if (!streq_ptr(fstype, "vfat")) { log_debug("Partition for /boot is not a FAT filesystem, ignoring."); return 0; } + errno = 0; r = blkid_probe_lookup_value(b, "PART_ENTRY_UUID", &uuid, NULL); if (r != 0) { - log_debug_errno(r, "Partition for /boot does not have a UUID, ignoring. %m"); + log_debug_errno(errno, "Partition for /boot does not have a UUID, ignoring."); return 0; } -- cgit v1.2.3-54-g00ecf From 2d60169dd60d503712d5c2e81a01158e82e636e6 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 3 Feb 2016 21:05:59 +0100 Subject: util: add check that makes sure time_t and TIME_T_MAX work the way we assume they do --- src/basic/time-util.h | 2 +- src/test/test-time.c | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/basic/time-util.h b/src/basic/time-util.h index b37d5ad5dc..9c7758a959 100644 --- a/src/basic/time-util.h +++ b/src/basic/time-util.h @@ -69,7 +69,7 @@ typedef struct dual_timestamp { #define FORMAT_TIMESTAMP_RELATIVE_MAX 256 #define FORMAT_TIMESPAN_MAX 64 -#define TIME_T_MAX (time_t)((1UL << ((sizeof(time_t) << 3) - 1)) - 1) +#define TIME_T_MAX (time_t)((UINTMAX_C(1) << ((sizeof(time_t) << 3) - 1)) - 1) #define DUAL_TIMESTAMP_NULL ((struct dual_timestamp) { 0ULL, 0ULL }) diff --git a/src/test/test-time.c b/src/test/test-time.c index ca44f81f9c..254a8d0e52 100644 --- a/src/test/test-time.c +++ b/src/test/test-time.c @@ -192,6 +192,8 @@ static void test_usec_add(void) { } int main(int argc, char *argv[]) { + uintmax_t x; + test_parse_sec(); test_parse_time(); test_parse_nsec(); @@ -202,5 +204,13 @@ int main(int argc, char *argv[]) { test_get_timezones(); test_usec_add(); + /* Ensure time_t is signed */ + assert_cc((time_t) -1 < (time_t) 1); + + /* Ensure TIME_T_MAX works correctly */ + x = (uintmax_t) TIME_T_MAX; + x ++; + assert((time_t) x < 0); + return 0; } -- cgit v1.2.3-54-g00ecf From ce359e98f87c7fbe5aa1d275e43cf088ce46c2b0 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 3 Feb 2016 23:38:50 +0100 Subject: core: when a service's ExecStartPre= times out, skip ExecStop= This makes sure we never run two control processes at the same time, we cannot keep track off. This introduces a slight change of behaviour but cleans up the definition of ExecStop= and ExecStopPost=. The former is now invoked only if the service managed to start-up correctly. The latter is called even if start-up failed half-way. Thus, ExecStopPost= may be used as clean-up step for both successful and failed start-up attempts, but ExecStop='s purpose is clearly defined as being responsible for shutting down the service and nothing else. The precise behaviour of this was not documented yet. This commit adds the necessary docs. Fixes: #1254 --- man/systemd.service.xml | 42 +++++++++++++++++++++++++++++++----------- src/core/service.c | 4 ++-- 2 files changed, 33 insertions(+), 13 deletions(-) diff --git a/man/systemd.service.xml b/man/systemd.service.xml index 14f6cd6adc..4cd36ac70e 100644 --- a/man/systemd.service.xml +++ b/man/systemd.service.xml @@ -383,6 +383,11 @@ used to start long-running processes. All processes forked off by processes invoked via ExecStartPre= will be killed before the next service process is run. + + Note that if any of the commands specified in ExecStartPre=, + ExecStart=, or ExecStartPost= fail (and are not prefixed with + -, see above) or time out before the service is fully up, execution continues with commands + specified in ExecStopPost=, the commands in ExecStop= are skipped. @@ -438,21 +443,36 @@ SIGKILL immediately after the command exited, this would not result in a clean stop. The specified command should hence be a synchronous operation, not an - asynchronous one. + asynchronous one. + + Note that the commands specified in ExecStop= are only executed when the service + started successfuly first. They are not invoked if the service was never started at all, or in case its + start-up failed, for example because any of the commands specified in ExecStart=, + ExecStartPre= or ExecStartPost= failed (and weren't prefixed with + -, see above) or timed out. Use ExecStopPost= to invoke commands when a + service failed to start up correctly and is shut down again. + + It is recommended to use this setting for commands that communicate with the service requesting clean + termination. When the commands specified with this option are executed it should be assumed that the service is + still fully up and is able to react correctly to all commands. For post-mortem clean-up steps use + ExecStopPost= instead. ExecStopPost= - Additional commands that are executed after - the service was stopped. This includes cases where the - commands configured in ExecStop= were used, - where the service does not have any - ExecStop= defined, or where the service - exited unexpectedly. This argument takes multiple command - lines, following the same scheme as described for - ExecStart=. Use of these settings is - optional. Specifier and environment variable substitution is - supported. + Additional commands that are executed after the service is stopped. This includes cases where + the commands configured in ExecStop= were used, where the service does not have any + ExecStop= defined, or where the service exited unexpectedly. This argument takes multiple + command lines, following the same scheme as described for ExecStart=. Use of these settings + is optional. Specifier and environment variable substitution is supported. Note that – unlike + ExecStop= – commands specified with this setting are invoked when a service failed to start + up correctly and is shut down again. + + It is recommended to use this setting for clean-up operations that shall be executed even when the + service failed to start up correctly. Commands configured with this setting need to be able to operate even if + the service failed starting up half-way and left incompletely initialized data around. As the service's + processes have been terminated already when the commands specified with this setting are executed they should + not attempt to communicate with them. diff --git a/src/core/service.c b/src/core/service.c index a9345e38b9..4942fc425d 100644 --- a/src/core/service.c +++ b/src/core/service.c @@ -2788,7 +2788,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { case SERVICE_START_POST: if (f != SERVICE_SUCCESS) { - service_enter_stop(s, f); + service_enter_signal(s, SERVICE_STOP_SIGTERM, f); break; } @@ -2878,7 +2878,7 @@ static int service_dispatch_timer(sd_event_source *source, usec_t usec, void *us case SERVICE_START_POST: log_unit_warning(UNIT(s), "Start-post operation timed out. Stopping."); - service_enter_stop(s, SERVICE_FAILURE_TIMEOUT); + service_enter_signal(s, SERVICE_STOP_SIGTERM, SERVICE_FAILURE_TIMEOUT); break; case SERVICE_RUNNING: -- cgit v1.2.3-54-g00ecf From 1411b09467c90ae358656d14165311090a2e175e Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 3 Feb 2016 23:53:08 +0100 Subject: core: log about path_is_mount_point() errors We really shouldn't fail silently, but print a log message about these errors. Also make sure to attach error codes to all log messages where that makes sense. (While we are at it, add a couple of (void) casts to functions where we knowingly ignore return values.) --- src/core/mount-setup.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/core/mount-setup.c b/src/core/mount-setup.c index 7a06c24016..321a52c30e 100644 --- a/src/core/mount-setup.c +++ b/src/core/mount-setup.c @@ -158,11 +158,13 @@ static int mount_one(const MountPoint *p, bool relabel) { /* Relabel first, just in case */ if (relabel) - label_fix(p->where, true, true); + (void) label_fix(p->where, true, true); r = path_is_mount_point(p->where, AT_SYMLINK_FOLLOW); - if (r < 0 && r != -ENOENT) - return r; + if (r < 0 && r != -ENOENT) { + log_full_errno((p->mode & MNT_FATAL) ? LOG_ERR : LOG_DEBUG, r, "Failed to determine whether %s is a mount point: %m", p->where); + return (p->mode & MNT_FATAL) ? r : 0; + } if (r > 0) return 0; @@ -173,9 +175,9 @@ static int mount_one(const MountPoint *p, bool relabel) { /* The access mode here doesn't really matter too much, since * the mounted file system will take precedence anyway. */ if (relabel) - mkdir_p_label(p->where, 0755); + (void) mkdir_p_label(p->where, 0755); else - mkdir_p(p->where, 0755); + (void) mkdir_p(p->where, 0755); log_debug("Mounting %s to %s of type %s with options %s.", p->what, @@ -188,13 +190,13 @@ static int mount_one(const MountPoint *p, bool relabel) { p->type, p->flags, p->options) < 0) { - log_full((p->mode & MNT_FATAL) ? LOG_ERR : LOG_DEBUG, "Failed to mount %s at %s: %m", p->type, p->where); + log_full_errno((p->mode & MNT_FATAL) ? LOG_ERR : LOG_DEBUG, errno, "Failed to mount %s at %s: %m", p->type, p->where); return (p->mode & MNT_FATAL) ? -errno : 0; } /* Relabel again, since we now mounted something fresh here */ if (relabel) - label_fix(p->where, false, false); + (void) label_fix(p->where, false, false); return 1; } -- cgit v1.2.3-54-g00ecf From 739731cdace09ff179fdd75ae0714da0d81e384d Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 3 Feb 2016 23:54:47 +0100 Subject: journal: fix boolean handling in MMapCache Let's use bitfields for our booleans, and don't try to apply binary OR or addition on them, because that's weird and we should instead use logical OR only. --- src/journal/mmap-cache.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/journal/mmap-cache.c b/src/journal/mmap-cache.c index eb4b092e80..a69672ce21 100644 --- a/src/journal/mmap-cache.c +++ b/src/journal/mmap-cache.c @@ -40,9 +40,9 @@ typedef struct FileDescriptor FileDescriptor; struct Window { MMapCache *cache; - bool invalidated; - bool keep_always; - bool in_unused; + bool invalidated:1; + bool keep_always:1; + bool in_unused:1; int prot; void *ptr; @@ -78,7 +78,6 @@ struct MMapCache { unsigned n_hit, n_missed; - Hashmap *fds; Context *contexts[MMAP_CACHE_MAX_CONTEXTS]; @@ -408,7 +407,7 @@ static int try_context( if (c->window->fd->sigbus) return -EIO; - c->window->keep_always |= keep_always; + c->window->keep_always = c->window->keep_always || keep_always; *ret = (uint8_t*) c->window->ptr + (offset - c->window->offset); return 1; @@ -454,7 +453,7 @@ static int find_mmap( return -ENOMEM; context_attach_window(c, w); - w->keep_always += keep_always; + w->keep_always = w->keep_always || keep_always; *ret = (uint8_t*) w->ptr + (offset - w->offset); return 1; -- cgit v1.2.3-54-g00ecf From 089b64d5f8418ffe257d07ce80077d8c6c68da15 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 3 Feb 2016 23:55:53 +0100 Subject: core: move service_unwatch_control_pid() call into service_enter_running() When we enter the running state we should forget about any control processes, in all cases, and not just when hit a reload timeout... --- src/core/service.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/core/service.c b/src/core/service.c index 4942fc425d..8bfb9fed4c 100644 --- a/src/core/service.c +++ b/src/core/service.c @@ -1635,6 +1635,8 @@ static void service_enter_running(Service *s, ServiceResult f) { if (f != SERVICE_SUCCESS) s->result = f; + service_unwatch_control_pid(s); + if (service_good(s)) { /* If there are any queued up sd_notify() @@ -2887,8 +2889,7 @@ static int service_dispatch_timer(sd_event_source *source, usec_t usec, void *us break; case SERVICE_RELOAD: - log_unit_warning(UNIT(s), "Reload operation timed out. Stopping."); - service_unwatch_control_pid(s); + log_unit_warning(UNIT(s), "Reload operation timed out. Killing reload process."); service_kill_control_processes(s); s->reload_result = SERVICE_FAILURE_TIMEOUT; service_enter_running(s, SERVICE_SUCCESS); -- cgit v1.2.3-54-g00ecf From 8e5de09f442874bed2a8889aa28739d2a516b094 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 4 Feb 2016 00:14:25 +0100 Subject: resolved: don't follow CNAMEs originating from DNS on LLMNR Fixes: #2514 --- src/resolve/resolved-dns-query.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/resolve/resolved-dns-query.c b/src/resolve/resolved-dns-query.c index 06d30d7863..b8bdff9dfa 100644 --- a/src/resolve/resolved-dns-query.c +++ b/src/resolve/resolved-dns-query.c @@ -967,6 +967,17 @@ static int dns_query_cname_redirect(DnsQuery *q, const DnsResourceRecord *cname) if (r == 0 && k == 0) /* No actual cname happened? */ return -ELOOP; + if (q->answer_protocol == DNS_PROTOCOL_DNS) { + /* Don't permit CNAME redirects from unicast DNS to LLMNR or MulticastDNS, so that global resources + * cannot invade the local namespace. The opposite way we permit: local names may redirect to global + * ones. */ + + q->flags &= ~(SD_RESOLVED_LLMNR|SD_RESOLVED_MDNS); /* mask away the local protocols */ + } + + /* Turn off searching for the new name */ + q->flags |= SD_RESOLVED_NO_SEARCH; + dns_question_unref(q->question_idna); q->question_idna = nq_idna; nq_idna = NULL; @@ -977,10 +988,8 @@ static int dns_query_cname_redirect(DnsQuery *q, const DnsResourceRecord *cname) dns_query_free_candidates(q); dns_query_reset_answer(q); - q->state = DNS_TRANSACTION_NULL; - /* Turn off searching for the new name */ - q->flags |= SD_RESOLVED_NO_SEARCH; + q->state = DNS_TRANSACTION_NULL; return 0; } -- cgit v1.2.3-54-g00ecf From 7a7821c878a6ddfb2e79268bb1cd8f7662a9b8f8 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 4 Feb 2016 00:35:43 +0100 Subject: core: rework job_get_timeout() to use usec_t and handle USEC_INFINITY time events correctly --- src/core/busname.c | 8 ++++++-- src/core/job.c | 16 +++++++--------- src/core/job.h | 4 ++-- src/core/mount.c | 8 ++++++-- src/core/scope.c | 8 ++++++-- src/core/service.c | 8 ++++++-- src/core/socket.c | 8 ++++++-- src/core/swap.c | 8 ++++++-- src/core/unit.h | 3 ++- 9 files changed, 47 insertions(+), 24 deletions(-) diff --git a/src/core/busname.c b/src/core/busname.c index ed083a8412..4b0cad8db4 100644 --- a/src/core/busname.c +++ b/src/core/busname.c @@ -972,17 +972,21 @@ static int busname_kill(Unit *u, KillWho who, int signo, sd_bus_error *error) { return unit_kill_common(u, who, signo, -1, BUSNAME(u)->control_pid, error); } -static int busname_get_timeout(Unit *u, uint64_t *timeout) { +static int busname_get_timeout(Unit *u, usec_t *timeout) { BusName *n = BUSNAME(u); + usec_t t; int r; if (!n->timer_event_source) return 0; - r = sd_event_source_get_time(n->timer_event_source, timeout); + r = sd_event_source_get_time(n->timer_event_source, &t); if (r < 0) return r; + if (t == USEC_INFINITY) + return 0; + *timeout = t; return 1; } diff --git a/src/core/job.c b/src/core/job.c index 1dcb872019..b1737c8110 100644 --- a/src/core/job.c +++ b/src/core/job.c @@ -1165,10 +1165,10 @@ void job_shutdown_magic(Job *j) { asynchronous_sync(); } -int job_get_timeout(Job *j, uint64_t *timeout) { +int job_get_timeout(Job *j, usec_t *timeout) { + usec_t x = USEC_INFINITY, y = USEC_INFINITY; Unit *u = j->unit; - uint64_t x = -1, y = -1; - int r = 0, q = 0; + int r; assert(u); @@ -1176,20 +1176,18 @@ int job_get_timeout(Job *j, uint64_t *timeout) { r = sd_event_source_get_time(j->timer_event_source, &x); if (r < 0) return r; - r = 1; } if (UNIT_VTABLE(u)->get_timeout) { - q = UNIT_VTABLE(u)->get_timeout(u, &y); - if (q < 0) - return q; + r = UNIT_VTABLE(u)->get_timeout(u, &y); + if (r < 0) + return r; } - if (r == 0 && q == 0) + if (x == USEC_INFINITY && y == USEC_INFINITY) return 0; *timeout = MIN(x, y); - return 1; } diff --git a/src/core/job.h b/src/core/job.h index bbf5471e8b..4c19ad0c6a 100644 --- a/src/core/job.h +++ b/src/core/job.h @@ -227,6 +227,8 @@ char *job_dbus_path(Job *j); void job_shutdown_magic(Job *j); +int job_get_timeout(Job *j, usec_t *timeout) _pure_; + const char* job_type_to_string(JobType t) _const_; JobType job_type_from_string(const char *s) _pure_; @@ -239,6 +241,4 @@ JobMode job_mode_from_string(const char *s) _pure_; const char* job_result_to_string(JobResult t) _const_; JobResult job_result_from_string(const char *s) _pure_; -int job_get_timeout(Job *j, uint64_t *timeout) _pure_; - const char* job_type_to_access_method(JobType t); diff --git a/src/core/mount.c b/src/core/mount.c index 7e3a6d578f..e0bd09bbbb 100644 --- a/src/core/mount.c +++ b/src/core/mount.c @@ -1556,17 +1556,21 @@ static void mount_shutdown(Manager *m) { m->mount_monitor = NULL; } -static int mount_get_timeout(Unit *u, uint64_t *timeout) { +static int mount_get_timeout(Unit *u, usec_t *timeout) { Mount *m = MOUNT(u); + usec_t t; int r; if (!m->timer_event_source) return 0; - r = sd_event_source_get_time(m->timer_event_source, timeout); + r = sd_event_source_get_time(m->timer_event_source, &t); if (r < 0) return r; + if (t == USEC_INFINITY) + return 0; + *timeout = t; return 1; } diff --git a/src/core/scope.c b/src/core/scope.c index 7cddee23b8..7dd437d204 100644 --- a/src/core/scope.c +++ b/src/core/scope.c @@ -345,17 +345,21 @@ static int scope_kill(Unit *u, KillWho who, int signo, sd_bus_error *error) { return unit_kill_common(u, who, signo, -1, -1, error); } -static int scope_get_timeout(Unit *u, uint64_t *timeout) { +static int scope_get_timeout(Unit *u, usec_t *timeout) { Scope *s = SCOPE(u); + usec_t t; int r; if (!s->timer_event_source) return 0; - r = sd_event_source_get_time(s->timer_event_source, timeout); + r = sd_event_source_get_time(s->timer_event_source, &t); if (r < 0) return r; + if (t == USEC_INFINITY) + return 0; + *timeout = t; return 1; } diff --git a/src/core/service.c b/src/core/service.c index 8bfb9fed4c..02ce1a566a 100644 --- a/src/core/service.c +++ b/src/core/service.c @@ -3111,17 +3111,21 @@ static void service_notify_message(Unit *u, pid_t pid, char **tags, FDSet *fds) unit_add_to_dbus_queue(u); } -static int service_get_timeout(Unit *u, uint64_t *timeout) { +static int service_get_timeout(Unit *u, usec_t *timeout) { Service *s = SERVICE(u); + uint64_t t; int r; if (!s->timer_event_source) return 0; - r = sd_event_source_get_time(s->timer_event_source, timeout); + r = sd_event_source_get_time(s->timer_event_source, &t); if (r < 0) return r; + if (t == USEC_INFINITY) + return 0; + *timeout = t; return 1; } diff --git a/src/core/socket.c b/src/core/socket.c index 740b748d65..f0d65577e3 100644 --- a/src/core/socket.c +++ b/src/core/socket.c @@ -2770,17 +2770,21 @@ static int socket_kill(Unit *u, KillWho who, int signo, sd_bus_error *error) { return unit_kill_common(u, who, signo, -1, SOCKET(u)->control_pid, error); } -static int socket_get_timeout(Unit *u, uint64_t *timeout) { +static int socket_get_timeout(Unit *u, usec_t *timeout) { Socket *s = SOCKET(u); + usec_t t; int r; if (!s->timer_event_source) return 0; - r = sd_event_source_get_time(s->timer_event_source, timeout); + r = sd_event_source_get_time(s->timer_event_source, &t); if (r < 0) return r; + if (t == USEC_INFINITY) + return 0; + *timeout = t; return 1; } diff --git a/src/core/swap.c b/src/core/swap.c index d895e3ced1..b663a029eb 100644 --- a/src/core/swap.c +++ b/src/core/swap.c @@ -1396,17 +1396,21 @@ static int swap_kill(Unit *u, KillWho who, int signo, sd_bus_error *error) { return unit_kill_common(u, who, signo, -1, SWAP(u)->control_pid, error); } -static int swap_get_timeout(Unit *u, uint64_t *timeout) { +static int swap_get_timeout(Unit *u, usec_t *timeout) { Swap *s = SWAP(u); + usec_t t; int r; if (!s->timer_event_source) return 0; - r = sd_event_source_get_time(s->timer_event_source, timeout); + r = sd_event_source_get_time(s->timer_event_source, &t); if (r < 0) return r; + if (t == USEC_INFINITY) + return 0; + *timeout = t; return 1; } diff --git a/src/core/unit.h b/src/core/unit.h index f86a0f687b..8712e03133 100644 --- a/src/core/unit.h +++ b/src/core/unit.h @@ -379,7 +379,8 @@ struct UnitVTable { /* Called whenever CLOCK_REALTIME made a jump */ void (*time_change)(Unit *u); - int (*get_timeout)(Unit *u, uint64_t *timeout); + /* Returns the next timeout of a unit */ + int (*get_timeout)(Unit *u, usec_t *timeout); /* This is called for each unit type and should be used to * enumerate existing devices and load them. However, -- cgit v1.2.3-54-g00ecf From 658f7f026e85734ebbaf72dbf83df61d62b87460 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 4 Feb 2016 01:10:12 +0100 Subject: resolved: properly turn off DNSSEC for LLMNR/mDNS scopes --- src/resolve/resolved-dns-scope.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/resolve/resolved-dns-scope.c b/src/resolve/resolved-dns-scope.c index 03239794ee..8f3be4b991 100644 --- a/src/resolve/resolved-dns-scope.c +++ b/src/resolve/resolved-dns-scope.c @@ -57,8 +57,6 @@ int dns_scope_new(Manager *m, DnsScope **ret, Link *l, DnsProtocol protocol, int s->family = family; s->resend_timeout = MULTICAST_RESEND_TIMEOUT_MIN_USEC; - s->dnssec_mode = _DNSSEC_MODE_INVALID; - if (protocol == DNS_PROTOCOL_DNS) { /* Copy DNSSEC mode from the link if it is set there, * otherwise take the manager's DNSSEC mode. Note that @@ -70,7 +68,8 @@ int dns_scope_new(Manager *m, DnsScope **ret, Link *l, DnsProtocol protocol, int s->dnssec_mode = link_get_dnssec_mode(l); else s->dnssec_mode = manager_get_dnssec_mode(m); - } + } else + s->dnssec_mode = DNSSEC_NO; LIST_PREPEND(scopes, m->dns_scopes, s); -- cgit v1.2.3-54-g00ecf From 06d127543513a9d4881c4e915053901b77ec4fe0 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 4 Feb 2016 01:10:36 +0100 Subject: resolved: correctly store interface index of RRs in cache Fixes: #2361 --- src/resolve/resolved-dns-cache.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/resolve/resolved-dns-cache.c b/src/resolve/resolved-dns-cache.c index 9267b67f79..1ac8a223d8 100644 --- a/src/resolve/resolved-dns-cache.c +++ b/src/resolve/resolved-dns-cache.c @@ -51,6 +51,7 @@ struct DnsCacheItem { bool authenticated:1; bool shared_owner:1; + int ifindex; int owner_family; union in_addr_union owner_address; @@ -329,6 +330,7 @@ static void dns_cache_item_update_positive( bool authenticated, bool shared_owner, usec_t timestamp, + int ifindex, int owner_family, const union in_addr_union *owner_address) { @@ -356,6 +358,8 @@ static void dns_cache_item_update_positive( i->authenticated = authenticated; i->shared_owner = shared_owner; + i->ifindex = ifindex; + i->owner_family = owner_family; i->owner_address = *owner_address; @@ -368,6 +372,7 @@ static int dns_cache_put_positive( bool authenticated, bool shared_owner, usec_t timestamp, + int ifindex, int owner_family, const union in_addr_union *owner_address) { @@ -414,6 +419,7 @@ static int dns_cache_put_positive( authenticated, shared_owner, timestamp, + ifindex, owner_family, owner_address); return 0; @@ -436,6 +442,7 @@ static int dns_cache_put_positive( i->until = calculate_until(rr, (uint32_t) -1, timestamp, false); i->authenticated = authenticated; i->shared_owner = shared_owner; + i->ifindex = ifindex; i->owner_family = owner_family; i->owner_address = *owner_address; i->prioq_idx = PRIOQ_IDX_NULL; @@ -615,7 +622,7 @@ int dns_cache_put( DnsResourceRecord *soa = NULL, *rr; DnsAnswerFlags flags; unsigned cache_keys; - int r; + int r, ifindex; assert(c); assert(owner_address); @@ -653,7 +660,7 @@ int dns_cache_put( timestamp = now(clock_boottime_or_monotonic()); /* Second, add in positive entries for all contained RRs */ - DNS_ANSWER_FOREACH_FLAGS(rr, flags, answer) { + DNS_ANSWER_FOREACH_FULL(rr, ifindex, flags, answer) { if ((flags & DNS_ANSWER_CACHEABLE) == 0) continue; @@ -669,6 +676,7 @@ int dns_cache_put( flags & DNS_ANSWER_AUTHENTICATED, flags & DNS_ANSWER_SHARED_OWNER, timestamp, + ifindex, owner_family, owner_address); if (r < 0) goto fail; @@ -922,7 +930,7 @@ int dns_cache_lookup(DnsCache *c, DnsResourceKey *key, int *rcode, DnsAnswer **r if (!j->rr) continue; - r = dns_answer_add(answer, j->rr, 0, j->authenticated ? DNS_ANSWER_AUTHENTICATED : 0); + r = dns_answer_add(answer, j->rr, j->ifindex, j->authenticated ? DNS_ANSWER_AUTHENTICATED : 0); if (r < 0) return r; } -- cgit v1.2.3-54-g00ecf