1
0
mirror of https://github.com/systemd/systemd.git synced 2025-03-31 14:50:15 +03:00

Merge pull request #2524 from poettering/bag-of-stuff

Bag of stuff
This commit is contained in:
Tom Gundersen 2016-02-04 18:05:32 +01:00
commit 6448e16d21
30 changed files with 637 additions and 111 deletions

View File

@ -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 \

View File

@ -248,16 +248,76 @@
<option>--ephemeral</option>.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>-a</option></term>
<term><option>--as-pid2</option></term>
<listitem><para>Invoke the shell or specified program as process ID (PID) 2 instead of PID 1 (init). By
default, if neither this option nor <option>--boot</option> 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
<command>sysvinit</command> compatible signal handling (specifically: it needs to reboot on SIGINT, reexecute
on SIGTERM, reload configuration on SIGHUP, and so on). With <option>--as-pid2</option> 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 <option>--boot</option> or
<option>--share-system</option>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>-b</option></term>
<term><option>--boot</option></term>
<listitem><para>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 <option>--share-system</option>.
</para></listitem>
<listitem><para>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 <option>--as-pid2</option> or
<option>--share-system</option>.</para>
<para>The following table explains the different modes of invocation and relationship to
<option>--as-pid2</option> (see above):</para>
<table>
<title>Invocation Mode</title>
<tgroup cols='2' align='left' colsep='1' rowsep='1'>
<colspec colname="switch" />
<colspec colname="explanation" />
<thead>
<row>
<entry>Switch</entry>
<entry>Explanation</entry>
</row>
</thead>
<tbody>
<row>
<entry>Neither <option>--as-pid2</option> nor <option>--boot</option> specified</entry>
<entry>The passed parameters are interpreted as command line, which is executed as PID 1 in the container.</entry>
</row>
<row>
<entry><option>--as-pid2</option> specified</entry>
<entry>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.</entry>
</row>
<row>
<entry><option>--boot</option> specified</entry>
<entry>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.</entry>
</row>
</tbody>
</tgroup>
</table>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--chdir=</option></term>
<listitem><para>Change to the specified working directory before invoking the process in the container. Expects
an absolute path in the container's file system namespace.</para></listitem>
</varlistentry>
<varlistentry>

View File

@ -141,15 +141,21 @@
<varlistentry>
<term><varname>Boot=</varname></term>
<listitem><para>Takes a boolean argument, which defaults to off. If
enabled, <command>systemd-nspawn</command> will automatically
search for an <filename>init</filename> executable and invoke
it. In this case, the specified parameters using
<varname>Parameters=</varname> are passed as additional
arguments to the <filename>init</filename> process. This
setting corresponds to the <option>--boot</option> switch on
the <command>systemd-nspawn</command> command
line. </para></listitem>
<listitem><para>Takes a boolean argument, which defaults to off. If enabled, <command>systemd-nspawn</command>
will automatically search for an <filename>init</filename> executable and invoke it. In this case, the
specified parameters using <varname>Parameters=</varname> are passed as additional arguments to the
<filename>init</filename> process. This setting corresponds to the <option>--boot</option> switch on the
<command>systemd-nspawn</command> command line. This option may not be combined with
<varname>ProcessTwo=yes</varname>.</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>ProcessTwo=</varname></term>
<listitem><para>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 <option>--as-pid2</option> switch
on the <command>systemd-nspawn</command> command line. This option may not be combined with
<varname>Boot=yes</varname>.</para></listitem>
</varlistentry>
<varlistentry>
@ -186,6 +192,14 @@
switch.</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>WorkingDirectory=</varname></term>
<listitem><para>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 <option>--chdir=</option> command line
switch.</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>Capability=</varname></term>
<term><varname>DropCapability=</varname></term>

View File

@ -383,6 +383,11 @@
used to start long-running processes. All processes forked
off by processes invoked via <varname>ExecStartPre=</varname> will
be killed before the next service process is run.</para>
<para>Note that if any of the commands specified in <varname>ExecStartPre=</varname>,
<varname>ExecStart=</varname>, or <varname>ExecStartPost=</varname> fail (and are not prefixed with
<literal>-</literal>, see above) or time out before the service is fully up, execution continues with commands
specified in <varname>ExecStopPost=</varname>, the commands in <varname>ExecStop=</varname> are skipped.</para>
</listitem>
</varlistentry>
@ -438,21 +443,36 @@
<constant>SIGKILL</constant> 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.</para></listitem>
asynchronous one.</para>
<para>Note that the commands specified in <varname>ExecStop=</varname> 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 <varname>ExecStart=</varname>,
<varname>ExecStartPre=</varname> or <varname>ExecStartPost=</varname> failed (and weren't prefixed with
<literal>-</literal>, see above) or timed out. Use <varname>ExecStopPost=</varname> to invoke commands when a
service failed to start up correctly and is shut down again.</para>
<para>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
<varname>ExecStopPost=</varname> instead.</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>ExecStopPost=</varname></term>
<listitem><para>Additional commands that are executed after
the service was stopped. This includes cases where the
commands configured in <varname>ExecStop=</varname> were used,
where the service does not have any
<varname>ExecStop=</varname> defined, or where the service
exited unexpectedly. This argument takes multiple command
lines, following the same scheme as described for
<varname>ExecStart=</varname>. Use of these settings is
optional. Specifier and environment variable substitution is
supported.</para></listitem>
<listitem><para>Additional commands that are executed after the service is stopped. This includes cases where
the commands configured in <varname>ExecStop=</varname> were used, where the service does not have any
<varname>ExecStop=</varname> defined, or where the service exited unexpectedly. This argument takes multiple
command lines, following the same scheme as described for <varname>ExecStart=</varname>. Use of these settings
is optional. Specifier and environment variable substitution is supported. Note that unlike
<varname>ExecStop=</varname> commands specified with this setting are invoked when a service failed to start
up correctly and is shut down again.</para>
<para>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.</para></listitem>
</varlistentry>
<varlistentry>

View File

@ -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;
}

View File

@ -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);

View File

@ -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 })

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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);

View File

@ -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

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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()
@ -2788,7 +2790,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 +2880,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:
@ -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);
@ -3110,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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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,

View File

@ -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;
}

View File

@ -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;

View File

@ -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)
@ -24,6 +25,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

View File

@ -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;
@ -74,6 +75,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);
@ -302,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;
}

View File

@ -27,25 +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,
_SETTINGS_MASK_ALL = (1 << 12) -1
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
} SettingsMask;
typedef struct Settings {
/* [Run] */
int boot;
StartMode start_mode;
char **parameters;
char **environment;
char *user;
@ -54,6 +63,7 @@ typedef struct Settings {
int kill_signal;
unsigned long personality;
sd_id128_t machine_id;
char *working_directory;
/* [Image] */
int read_only;
@ -89,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);

View File

@ -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 <http://www.gnu.org/licenses/>.
***/
#include <sys/reboot.h>
#include <sys/unistd.h>
#include <sys/wait.h>
#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);
}

View File

@ -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 <http://www.gnu.org/licenses/>.
***/
int stub_pid1(void);

View File

@ -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"
@ -114,6 +115,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;
@ -122,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;
@ -192,7 +194,9 @@ 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"
" -M --machine=NAME Set the machine name for the container\n"
" --uuid=UUID Set a specific machine UUID for the container\n"
@ -231,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"
@ -345,6 +349,7 @@ static int parse_argv(int argc, char *argv[]) {
ARG_PRIVATE_USERS,
ARG_KILL_SIGNAL,
ARG_SETTINGS,
ARG_CHDIR,
};
static const struct option options[] = {
@ -355,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 },
@ -389,6 +395,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 },
{}
};
@ -400,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) {
@ -491,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:
@ -849,6 +871,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;
@ -859,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;
}
@ -907,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 */
@ -943,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;
@ -2563,6 +2598,16 @@ 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);
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
@ -2574,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;
@ -2598,7 +2643,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);
}
@ -2894,15 +2941,22 @@ 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;
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);
@ -3044,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;
@ -3150,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;
@ -3629,6 +3687,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);

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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);

View File

@ -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;
}