1
1
mirror of https://github.com/systemd/systemd-stable.git synced 2025-01-24 02:03:54 +03:00

service: add the ability for units to join other unit's PrivateNetwork= and PrivateTmp= namespaces

This commit is contained in:
Lennart Poettering 2013-11-27 20:23:18 +01:00
parent df41776d66
commit 613b411c94
24 changed files with 791 additions and 340 deletions

View File

@ -846,9 +846,9 @@
system namespace for the executed
processes and mounts private
<filename>/tmp</filename> and
<filename>/var/tmp</filename> directories
inside it, that are not shared by
processes outside of the
<filename>/var/tmp</filename>
directories inside it, that are not
shared by processes outside of the
namespace. This is useful to secure
access to temporary files of the
process, but makes sharing between
@ -856,9 +856,17 @@
<filename>/tmp</filename> or
<filename>/var/tmp</filename>
impossible. All temporary data created
by service will be removed after service
is stopped. Defaults to
false.</para></listitem>
by service will be removed after
service is stopped. Defaults to
false. Note that it is possible to run
two or more units within the same
private <filename>/tmp</filename> and
<filename>/var/tmp</filename>
namespace by using the
<varname>JoinsNamespaceOf=</varname>
directive, see
<citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>
for details.</para></listitem>
</varlistentry>
<varlistentry>
@ -874,8 +882,14 @@
available to the executed process.
This is useful to securely turn off
network access by the executed
process. Defaults to
false.</para></listitem>
process. Defaults to false. Note that
it is possible to run two or more
units within the same private network
namespace by using the
<varname>JoinsNamespaceOf=</varname>
directive, see
<citerefentry><refentrytitle>systemd.unit</refentrytitle><manvolnum>5</manvolnum></citerefentry>
for details.</para></listitem>
</varlistentry>
<varlistentry>

View File

@ -656,6 +656,37 @@
settings.</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>JoinsNamespaceOf=</varname></term>
<listitem><para>For units that start
processes (such as service units),
lists one or more other units whose
network and/or temporary file
namespace to join. This only applies
to unit types which support the
<varname>PrivateNetwork=</varname> and
<varname>PrivateTmp=</varname>
directives (see
<citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry>
for details). If a unit that has this
setting set is started its processes
will see the same
<filename>/tmp</filename>,
<filename>/tmp/var</filename> and
network namespace as one listed unit
that is started. If multiple listed
units are already started it is not
defined which namespace is
joined. Note that this setting only
has an effect if
<varname>PrivateNetwork=</varname>
and/or <varname>PrivateTmp=</varname>
is enabled for both the unit that
joins the namespace and the unit whose
namespace is joined.</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>RequiresMountsFor=</varname></term>

View File

@ -522,6 +522,7 @@ const sd_bus_vtable bus_unit_vtable[] = {
SD_BUS_PROPERTY("TriggeredBy", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_TRIGGERED_BY]), 0),
SD_BUS_PROPERTY("PropagatesReloadTo", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_PROPAGATES_RELOAD_TO]), 0),
SD_BUS_PROPERTY("ReloadPropagatedFrom", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_RELOAD_PROPAGATED_FROM]), 0),
SD_BUS_PROPERTY("JoinsNamespaceOf", "as", property_get_dependencies, offsetof(Unit, dependencies[UNIT_JOINS_NAMESPACE_OF]), 0),
SD_BUS_PROPERTY("RequiresMountsFor", "as", NULL, offsetof(Unit, requires_mounts_for), 0),
SD_BUS_PROPERTY("Documentation", "as", NULL, offsetof(Unit, documentation), 0),
SD_BUS_PROPERTY("Description", "s", property_get_description, 0, 0),

View File

@ -61,7 +61,6 @@
#include "missing.h"
#include "utmp-wtmp.h"
#include "def.h"
#include "loopback-setup.h"
#include "path-util.h"
#include "syscall-list.h"
#include "env-util.h"
@ -176,24 +175,13 @@ static bool is_terminal_output(ExecOutput o) {
o == EXEC_OUTPUT_JOURNAL_AND_CONSOLE;
}
void exec_context_serialize(const ExecContext *context, Unit *u, FILE *f) {
assert(context);
assert(u);
assert(f);
if (context->tmp_dir)
unit_serialize_item(u, f, "tmp-dir", context->tmp_dir);
if (context->var_tmp_dir)
unit_serialize_item(u, f, "var-tmp-dir", context->var_tmp_dir);
}
static int open_null_as(int flags, int nfd) {
int fd, r;
assert(nfd >= 0);
if ((fd = open("/dev/null", flags|O_NOCTTY)) < 0)
fd = open("/dev/null", flags|O_NOCTTY);
if (fd < 0)
return -errno;
if (fd != nfd) {
@ -1037,6 +1025,7 @@ int exec_spawn(ExecCommand *command,
const char *cgroup_path,
const char *unit_id,
int idle_pipe[4],
ExecRuntime *runtime,
pid_t *ret) {
_cleanup_strv_free_ char **files_env = NULL;
@ -1088,25 +1077,19 @@ int exec_spawn(ExecCommand *command,
NULL);
free(line);
if (context->private_tmp && !context->tmp_dir && !context->var_tmp_dir) {
r = setup_tmpdirs(unit_id, &context->tmp_dir, &context->var_tmp_dir);
if (r < 0)
return r;
}
pid = fork();
if (pid < 0)
return -errno;
if (pid == 0) {
int i, err;
sigset_t ss;
_cleanup_strv_free_ char **our_env = NULL, **pam_env = NULL, **final_env = NULL, **final_argv = NULL;
const char *username = NULL, *home = NULL, *shell = NULL;
unsigned n_dont_close = 0, n_env = 0;
int dont_close[n_fds + 3];
uid_t uid = (uid_t) -1;
gid_t gid = (gid_t) -1;
_cleanup_strv_free_ char **our_env = NULL, **pam_env = NULL,
**final_env = NULL, **final_argv = NULL;
unsigned n_env = 0;
sigset_t ss;
int i, err;
/* child */
@ -1137,8 +1120,21 @@ int exec_spawn(ExecCommand *command,
* block init reexecution because it cannot bind its
* sockets */
log_forget_fds();
err = close_all_fds(socket_fd >= 0 ? &socket_fd : fds,
socket_fd >= 0 ? 1 : n_fds);
if (socket_fd >= 0)
dont_close[n_dont_close++] = socket_fd;
if (n_fds > 0) {
memcpy(dont_close + n_dont_close, fds, sizeof(int) * n_fds);
n_dont_close += n_fds;
}
if (runtime) {
if (runtime->netns_storage_socket[0] >= 0)
dont_close[n_dont_close++] = runtime->netns_storage_socket[0];
if (runtime->netns_storage_socket[1] >= 0)
dont_close[n_dont_close++] = runtime->netns_storage_socket[1];
}
err = close_all_fds(dont_close, n_dont_close);
if (err < 0) {
r = EXIT_FDS;
goto fail_child;
@ -1335,28 +1331,43 @@ int exec_spawn(ExecCommand *command,
}
}
#endif
if (context->private_network) {
if (unshare(CLONE_NEWNET) < 0) {
err = -errno;
if (context->private_network && runtime && runtime->netns_storage_socket[0] >= 0) {
err = setup_netns(runtime->netns_storage_socket);
if (err < 0) {
r = EXIT_NETWORK;
goto fail_child;
}
loopback_setup();
}
if (strv_length(context->read_write_dirs) > 0 ||
strv_length(context->read_only_dirs) > 0 ||
strv_length(context->inaccessible_dirs) > 0 ||
if (!strv_isempty(context->read_write_dirs) ||
!strv_isempty(context->read_only_dirs) ||
!strv_isempty(context->inaccessible_dirs) ||
context->mount_flags != 0 ||
context->private_tmp) {
err = setup_namespace(context->read_write_dirs,
context->read_only_dirs,
context->inaccessible_dirs,
context->tmp_dir,
context->var_tmp_dir,
context->private_tmp,
context->mount_flags);
(context->private_tmp && runtime && (runtime->tmp_dir || runtime->var_tmp_dir))) {
char *tmp = NULL, *var = NULL;
/* The runtime struct only contains the parent
* of the private /tmp, which is
* non-accessible to world users. Inside of it
* there's a /tmp that is sticky, and that's
* the one we want to use here. */
if (context->private_tmp && runtime) {
if (runtime->tmp_dir)
tmp = strappenda(runtime->tmp_dir, "/tmp");
if (runtime->var_tmp_dir)
var = strappenda(runtime->var_tmp_dir, "/tmp");
}
err = setup_namespace(
context->read_write_dirs,
context->read_only_dirs,
context->inaccessible_dirs,
tmp,
var,
context->mount_flags);
if (err < 0) {
r = EXIT_NAMESPACE;
goto fail_child;
@ -1580,43 +1591,7 @@ void exec_context_init(ExecContext *c) {
c->timer_slack_nsec = (nsec_t) -1;
}
static void *remove_tmpdir_thread(void *p) {
int r;
_cleanup_free_ char *dirp = p;
char *dir;
assert(dirp);
r = rm_rf_dangerous(dirp, false, true, false);
dir = dirname(dirp);
if (r < 0)
log_warning("Failed to remove content of temporary directory %s: %s",
dir, strerror(-r));
else {
r = rmdir(dir);
if (r < 0)
log_warning("Failed to remove temporary directory %s: %s",
dir, strerror(-r));
}
return NULL;
}
void exec_context_tmp_dirs_done(ExecContext *c) {
char* dirs[] = {c->tmp_dir ? c->tmp_dir : c->var_tmp_dir,
c->tmp_dir ? c->var_tmp_dir : NULL,
NULL};
char **dirp;
for(dirp = dirs; *dirp; dirp++) {
log_debug("Spawning thread to nuke %s", *dirp);
asynchronous_job(remove_tmpdir_thread, *dirp);
}
c->tmp_dir = c->var_tmp_dir = NULL;
}
void exec_context_done(ExecContext *c, bool reloading_or_reexecuting) {
void exec_context_done(ExecContext *c) {
unsigned l;
assert(c);
@ -1680,9 +1655,6 @@ void exec_context_done(ExecContext *c, bool reloading_or_reexecuting) {
free(c->syscall_filter);
c->syscall_filter = NULL;
if (!reloading_or_reexecuting)
exec_context_tmp_dirs_done(c);
}
void exec_command_done(ExecCommand *c) {
@ -2229,6 +2201,216 @@ int exec_command_set(ExecCommand *c, const char *path, ...) {
return 0;
}
static int exec_runtime_allocate(ExecRuntime **rt) {
if (*rt)
return 0;
*rt = new0(ExecRuntime, 1);
if (!rt)
return -ENOMEM;
(*rt)->n_ref = 1;
(*rt)->netns_storage_socket[0] = (*rt)->netns_storage_socket[1] = -1;
return 0;
}
int exec_runtime_make(ExecRuntime **rt, ExecContext *c, const char *id) {
int r;
assert(rt);
assert(c);
assert(id);
if (*rt)
return 1;
if (!c->private_network && !c->private_tmp)
return 0;
r = exec_runtime_allocate(rt);
if (r < 0)
return r;
if (c->private_network && (*rt)->netns_storage_socket[0] < 0) {
if (socketpair(AF_UNIX, SOCK_DGRAM, 0, (*rt)->netns_storage_socket) < 0)
return -errno;
}
if (c->private_tmp && !(*rt)->tmp_dir) {
r = setup_tmp_dirs(id, &(*rt)->tmp_dir, &(*rt)->var_tmp_dir);
if (r < 0)
return r;
}
return 1;
}
ExecRuntime *exec_runtime_ref(ExecRuntime *r) {
assert(r);
assert(r->n_ref > 0);
r->n_ref++;
return r;
}
ExecRuntime *exec_runtime_unref(ExecRuntime *r) {
if (!r)
return NULL;
assert(r->n_ref > 0);
r->n_ref--;
if (r->n_ref <= 0) {
free(r->tmp_dir);
free(r->var_tmp_dir);
close_pipe(r->netns_storage_socket);
free(r);
}
return NULL;
}
int exec_runtime_serialize(ExecRuntime *rt, Unit *u, FILE *f, FDSet *fds) {
assert(u);
assert(f);
assert(fds);
if (!rt)
return 0;
if (rt->tmp_dir)
unit_serialize_item(u, f, "tmp-dir", rt->tmp_dir);
if (rt->var_tmp_dir)
unit_serialize_item(u, f, "var-tmp-dir", rt->var_tmp_dir);
if (rt->netns_storage_socket[0] >= 0) {
int copy;
copy = fdset_put_dup(fds, rt->netns_storage_socket[0]);
if (copy < 0)
return copy;
unit_serialize_item_format(u, f, "netns-socket-0", "%i", copy);
}
if (rt->netns_storage_socket[1] >= 0) {
int copy;
copy = fdset_put_dup(fds, rt->netns_storage_socket[1]);
if (copy < 0)
return copy;
unit_serialize_item_format(u, f, "netns-socket-1", "%i", copy);
}
return 0;
}
int exec_runtime_deserialize_item(ExecRuntime **rt, Unit *u, const char *key, const char *value, FDSet *fds) {
int r;
assert(rt);
assert(key);
assert(value);
if (streq(key, "tmp-dir")) {
char *copy;
r = exec_runtime_allocate(rt);
if (r < 0)
return r;
copy = strdup(value);
if (!copy)
return log_oom();
free((*rt)->tmp_dir);
(*rt)->tmp_dir = copy;
} else if (streq(key, "var-tmp-dir")) {
char *copy;
r = exec_runtime_allocate(rt);
if (r < 0)
return r;
copy = strdup(value);
if (!copy)
return log_oom();
free((*rt)->var_tmp_dir);
(*rt)->var_tmp_dir = copy;
} else if (streq(key, "netns-socket-0")) {
int fd;
r = exec_runtime_allocate(rt);
if (r < 0)
return r;
if (safe_atoi(value, &fd) < 0 || !fdset_contains(fds, fd))
log_debug_unit(u->id, "Failed to parse netns socket value %s", value);
else {
if ((*rt)->netns_storage_socket[0] >= 0)
close_nointr_nofail((*rt)->netns_storage_socket[0]);
(*rt)->netns_storage_socket[0] = fdset_remove(fds, fd);
}
} else if (streq(key, "netns-socket-1")) {
int fd;
r = exec_runtime_allocate(rt);
if (r < 0)
return r;
if (safe_atoi(value, &fd) < 0 || !fdset_contains(fds, fd))
log_debug_unit(u->id, "Failed to parse netns socket value %s", value);
else {
if ((*rt)->netns_storage_socket[1] >= 0)
close_nointr_nofail((*rt)->netns_storage_socket[1]);
(*rt)->netns_storage_socket[1] = fdset_remove(fds, fd);
}
} else
return 0;
return 1;
}
static void *remove_tmpdir_thread(void *p) {
_cleanup_free_ char *path = p;
rm_rf_dangerous(path, false, true, false);
return NULL;
}
void exec_runtime_destroy(ExecRuntime *rt) {
if (!rt)
return;
/* If there are multiple users of this, let's leave the stuff around */
if (rt->n_ref > 1)
return;
if (rt->tmp_dir) {
log_debug("Spawning thread to nuke %s", rt->tmp_dir);
asynchronous_job(remove_tmpdir_thread, rt->tmp_dir);
rt->tmp_dir = NULL;
}
if (rt->var_tmp_dir) {
log_debug("Spawning thread to nuke %s", rt->var_tmp_dir);
asynchronous_job(remove_tmpdir_thread, rt->var_tmp_dir);
rt->var_tmp_dir = NULL;
}
close_pipe(rt->netns_storage_socket);
}
static const char* const exec_input_table[_EXEC_INPUT_MAX] = {
[EXEC_INPUT_NULL] = "null",
[EXEC_INPUT_TTY] = "tty",

View File

@ -24,6 +24,7 @@
typedef struct ExecStatus ExecStatus;
typedef struct ExecCommand ExecCommand;
typedef struct ExecContext ExecContext;
typedef struct ExecRuntime ExecRuntime;
#include <linux/types.h>
#include <sys/time.h>
@ -35,6 +36,7 @@ typedef struct ExecContext ExecContext;
#include "list.h"
#include "util.h"
#include "fdset.h"
typedef struct Unit Unit;
@ -79,6 +81,15 @@ struct ExecCommand {
bool ignore;
};
struct ExecRuntime {
int n_ref;
char *tmp_dir;
char *var_tmp_dir;
int netns_storage_socket[2];
};
struct ExecContext {
char **environment;
char **environment_files;
@ -140,8 +151,6 @@ struct ExecContext {
bool non_blocking;
bool private_tmp;
bool private_network;
char *tmp_dir;
char *var_tmp_dir;
bool no_new_privileges;
@ -175,6 +184,7 @@ int exec_spawn(ExecCommand *command,
const char *cgroup_path,
const char *unit_id,
int pipe_fd[2],
ExecRuntime *runtime,
pid_t *ret);
void exec_command_done(ExecCommand *c);
@ -191,19 +201,26 @@ void exec_command_append_list(ExecCommand **l, ExecCommand *e);
int exec_command_set(ExecCommand *c, const char *path, ...);
void exec_context_init(ExecContext *c);
void exec_context_done(ExecContext *c, bool reloading_or_reexecuting);
void exec_context_tmp_dirs_done(ExecContext *c);
void exec_context_done(ExecContext *c);
void exec_context_dump(ExecContext *c, FILE* f, const char *prefix);
int exec_context_load_environment(const ExecContext *c, char ***l);
bool exec_context_may_touch_console(ExecContext *c);
void exec_context_serialize(const ExecContext *c, Unit *u, FILE *f);
void exec_status_start(ExecStatus *s, pid_t pid);
void exec_status_exit(ExecStatus *s, ExecContext *context, pid_t pid, int code, int status);
void exec_status_dump(ExecStatus *s, FILE *f, const char *prefix);
int exec_runtime_make(ExecRuntime **rt, ExecContext *c, const char *id);
ExecRuntime *exec_runtime_ref(ExecRuntime *r);
ExecRuntime *exec_runtime_unref(ExecRuntime *r);
int exec_runtime_serialize(ExecRuntime *rt, Unit *u, FILE *f, FDSet *fds);
int exec_runtime_deserialize_item(ExecRuntime **rt, Unit *u, const char *key, const char *value, FDSet *fds);
void exec_runtime_destroy(ExecRuntime *rt);
const char* exec_output_to_string(ExecOutput i) _const_;
ExecOutput exec_output_from_string(const char *s) _pure_;

View File

@ -116,6 +116,7 @@ Unit.PropagateReloadTo, config_parse_unit_deps, UNIT_PROPAG
Unit.ReloadPropagatedFrom, config_parse_unit_deps, UNIT_RELOAD_PROPAGATED_FROM, 0
Unit.PropagateReloadFrom, config_parse_unit_deps, UNIT_RELOAD_PROPAGATED_FROM, 0
Unit.PartOf, config_parse_unit_deps, UNIT_PART_OF, 0
Unit.JoinsNamespaceOf, config_parse_unit_deps, UNIT_JOINS_NAMESPACE_OF, 0
Unit.RequiresMountsFor, config_parse_unit_requires_mounts_for, 0, 0
Unit.StopWhenUnneeded, config_parse_bool, 0, offsetof(Unit, stop_when_unneeded)
Unit.RefuseManualStart, config_parse_bool, 0, offsetof(Unit, refuse_manual_start)

View File

@ -1350,15 +1350,17 @@ int config_parse_socket_service(const char *unit,
assert(data);
r = unit_name_printf(UNIT(s), rvalue, &p);
if (r < 0)
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, -r, "Failed to resolve specifiers, ignoring: %s", rvalue);
return 0;
}
if (!endswith(p ?: rvalue, ".service")) {
if (!endswith(p, ".service")) {
log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Unit must be of type service, ignoring: %s", rvalue);
return 0;
}
r = manager_load_unit(UNIT(s)->manager, p ?: rvalue, NULL, &error, &x);
r = manager_load_unit(UNIT(s)->manager, p, NULL, &error, &x);
if (r < 0) {
log_syntax(unit, LOG_ERR, filename, line, r, "Failed to load unit %s, ignoring: %s", rvalue, bus_error_message(&error, r));
return 0;

View File

@ -217,7 +217,8 @@ static void mount_done(Unit *u) {
mount_parameters_done(&m->parameters_fragment);
cgroup_context_done(&m->cgroup_context);
exec_context_done(&m->exec_context, manager_is_reloading_or_reexecuting(u->manager));
exec_context_done(&m->exec_context);
m->exec_runtime = exec_runtime_unref(m->exec_runtime);
exec_command_done_array(m->exec_command, _MOUNT_EXEC_COMMAND_MAX);
m->control_command = NULL;
@ -772,6 +773,10 @@ static int mount_spawn(Mount *m, ExecCommand *c, pid_t *_pid) {
unit_realize_cgroup(UNIT(m));
r = unit_setup_exec_runtime(UNIT(m));
if (r < 0)
goto fail;
r = mount_arm_timer(m);
if (r < 0)
goto fail;
@ -789,6 +794,7 @@ static int mount_spawn(Mount *m, ExecCommand *c, pid_t *_pid) {
UNIT(m)->cgroup_path,
UNIT(m)->id,
NULL,
m->exec_runtime,
&pid);
if (r < 0)
goto fail;
@ -814,7 +820,9 @@ static void mount_enter_dead(Mount *m, MountResult f) {
if (f != MOUNT_SUCCESS)
m->result = f;
exec_context_tmp_dirs_done(&m->exec_context);
exec_runtime_destroy(m->exec_runtime);
m->exec_runtime = exec_runtime_unref(m->exec_runtime);
mount_set_state(m, m->result != MOUNT_SUCCESS ? MOUNT_FAILED : MOUNT_DEAD);
}
@ -1095,8 +1103,6 @@ static int mount_serialize(Unit *u, FILE *f, FDSet *fds) {
if (m->control_command_id >= 0)
unit_serialize_item(u, f, "control-command", mount_exec_command_to_string(m->control_command_id));
exec_context_serialize(&m->exec_context, UNIT(m), f);
return 0;
}
@ -1153,22 +1159,6 @@ static int mount_deserialize_item(Unit *u, const char *key, const char *value, F
m->control_command_id = id;
m->control_command = m->exec_command + id;
}
} else if (streq(key, "tmp-dir")) {
char *t;
t = strdup(value);
if (!t)
return log_oom();
m->exec_context.tmp_dir = t;
} else if (streq(key, "var-tmp-dir")) {
char *t;
t = strdup(value);
if (!t)
return log_oom();
m->exec_context.var_tmp_dir = t;
} else
log_debug_unit(UNIT(m)->id,
"Unknown serialization key '%s'", key);
@ -1770,6 +1760,7 @@ const UnitVTable mount_vtable = {
.exec_context_offset = offsetof(Mount, exec_context),
.cgroup_context_offset = offsetof(Mount, cgroup_context),
.kill_context_offset = offsetof(Mount, kill_context),
.exec_runtime_offset = offsetof(Mount, exec_runtime),
.sections =
"Unit\0"

View File

@ -101,6 +101,8 @@ struct Mount {
KillContext kill_context;
CGroupContext cgroup_context;
ExecRuntime *exec_runtime;
MountState state, deserialized_state;
ExecCommand* control_command;

View File

@ -30,6 +30,7 @@
#include <sys/syscall.h>
#include <limits.h>
#include <linux/fs.h>
#include <sys/file.h>
#include "strv.h"
#include "util.h"
@ -37,6 +38,7 @@
#include "namespace.h"
#include "missing.h"
#include "execute.h"
#include "loopback-setup.h"
typedef enum MountMode {
/* This is ordered by priority! */
@ -57,6 +59,8 @@ typedef struct BindMount {
static int append_mounts(BindMount **p, char **strv, MountMode mode) {
char **i;
assert(p);
STRV_FOREACH(i, strv) {
(*p)->ignore = false;
@ -184,68 +188,50 @@ static int make_read_only(BindMount *m) {
return 0;
}
int setup_tmpdirs(const char *unit_id,
char **tmp_dir,
char **var_tmp_dir) {
int r = 0;
_cleanup_free_ char *tmp = NULL, *var = NULL;
int setup_namespace(
char** read_write_dirs,
char** read_only_dirs,
char** inaccessible_dirs,
char* tmp_dir,
char* var_tmp_dir,
unsigned mount_flags) {
assert(tmp_dir);
assert(var_tmp_dir);
tmp = strjoin("/tmp/systemd-", unit_id, "-XXXXXXX", NULL);
var = strjoin("/var/tmp/systemd-", unit_id, "-XXXXXXX", NULL);
r = create_tmp_dir(tmp, tmp_dir);
if (r < 0)
return r;
r = create_tmp_dir(var, var_tmp_dir);
if (r == 0)
return 0;
/* failure */
rmdir(*tmp_dir);
rmdir(tmp);
free(*tmp_dir);
*tmp_dir = NULL;
return r;
}
int setup_namespace(char** read_write_dirs,
char** read_only_dirs,
char** inaccessible_dirs,
char* tmp_dir,
char* var_tmp_dir,
bool private_tmp,
unsigned mount_flags) {
unsigned n = strv_length(read_write_dirs) +
strv_length(read_only_dirs) +
strv_length(inaccessible_dirs) +
(private_tmp ? 2 : 0);
BindMount *m, *mounts = NULL;
unsigned n;
int r = 0;
if (!mount_flags)
if (mount_flags == 0)
mount_flags = MS_SHARED;
if (unshare(CLONE_NEWNS) < 0)
return -errno;
if (n) {
n = !!tmp_dir + !!var_tmp_dir +
strv_length(read_write_dirs) +
strv_length(read_only_dirs) +
strv_length(inaccessible_dirs);
if (n > 0) {
m = mounts = (BindMount *) alloca(n * sizeof(BindMount));
if ((r = append_mounts(&m, read_write_dirs, READWRITE)) < 0 ||
(r = append_mounts(&m, read_only_dirs, READONLY)) < 0 ||
(r = append_mounts(&m, inaccessible_dirs, INACCESSIBLE)) < 0)
r = append_mounts(&m, read_write_dirs, READWRITE);
if (r < 0)
return r;
if (private_tmp) {
r = append_mounts(&m, read_only_dirs, READONLY);
if (r < 0)
return r;
r = append_mounts(&m, inaccessible_dirs, INACCESSIBLE);
if (r < 0)
return r;
if (tmp_dir) {
m->path = "/tmp";
m->mode = PRIVATE_TMP;
m++;
}
if (var_tmp_dir) {
m->path = "/var/tmp";
m->mode = PRIVATE_VAR_TMP;
m++;
@ -265,28 +251,172 @@ int setup_namespace(char** read_write_dirs,
for (m = mounts; m < mounts + n; ++m) {
r = apply_mount(m, tmp_dir, var_tmp_dir);
if (r < 0)
goto undo_mounts;
goto fail;
}
for (m = mounts; m < mounts + n; ++m) {
r = make_read_only(m);
if (r < 0)
goto undo_mounts;
goto fail;
}
/* Remount / as the desired mode */
if (mount(NULL, "/", NULL, mount_flags | MS_REC, NULL) < 0) {
r = -errno;
goto undo_mounts;
goto fail;
}
return 0;
undo_mounts:
for (m = mounts; m < mounts + n; ++m) {
fail:
for (m = mounts; m < mounts + n; ++m)
if (m->done)
umount2(m->path, MNT_DETACH);
}
return r;
}
static int setup_one_tmp_dir(const char *id, const char *prefix, char **path) {
_cleanup_free_ char *x = NULL;
assert(id);
assert(prefix);
assert(path);
x = strjoin(prefix, "/systemd-", id, "-XXXXXX", NULL);
if (!x)
return -ENOMEM;
RUN_WITH_UMASK(0077)
if (!mkdtemp(x))
return -errno;
RUN_WITH_UMASK(0000) {
char *y;
y = strappenda(x, "/tmp");
if (mkdir(y, 0777 | S_ISVTX) < 0)
return -errno;
}
*path = x;
x = NULL;
return 0;
}
int setup_tmp_dirs(const char *id, char **tmp_dir, char **var_tmp_dir) {
char *a, *b;
int r;
assert(id);
assert(tmp_dir);
assert(var_tmp_dir);
r = setup_one_tmp_dir(id, "/tmp", &a);
if (r < 0)
return r;
r = setup_one_tmp_dir(id, "/var/tmp", &b);
if (r < 0) {
char *t;
t = strappenda(a, "/tmp");
rmdir(t);
rmdir(a);
free(a);
return r;
}
*tmp_dir = a;
*var_tmp_dir = b;
return 0;
}
int setup_netns(int netns_storage_socket[2]) {
_cleanup_close_ int netns = -1;
union {
struct cmsghdr cmsghdr;
uint8_t buf[CMSG_SPACE(sizeof(int))];
} control = {};
struct msghdr mh = {
.msg_control = &control,
.msg_controllen = sizeof(control),
};
struct cmsghdr *cmsg;
int r;
assert(netns_storage_socket);
assert(netns_storage_socket[0] >= 0);
assert(netns_storage_socket[1] >= 0);
/* We use the passed socketpair as a storage buffer for our
* namespace socket. Whatever process runs this first shall
* create a new namespace, all others should just join it. To
* serialize that we use a file lock on the socket pair.
*
* It's a bit crazy, but hey, works great! */
if (lockf(netns_storage_socket[0], F_LOCK, 0) < 0)
return -errno;
if (recvmsg(netns_storage_socket[0], &mh, MSG_DONTWAIT|MSG_CMSG_CLOEXEC) < 0) {
if (errno != EAGAIN) {
r = -errno;
goto fail;
}
/* Nothing stored yet, so let's create a new namespace */
if (unshare(CLONE_NEWNET) < 0) {
r = -errno;
goto fail;
}
loopback_setup();
netns = open("/proc/self/ns/net", O_RDONLY|O_CLOEXEC|O_NOCTTY);
if (netns < 0) {
r = -errno;
goto fail;
}
r = 1;
} else {
/* Yay, found something, so let's join the namespace */
for (cmsg = CMSG_FIRSTHDR(&mh); cmsg; cmsg = CMSG_NXTHDR(&mh, cmsg)) {
if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
assert(cmsg->cmsg_len == CMSG_LEN(sizeof(int)));
netns = *(int*) CMSG_DATA(cmsg);
}
}
if (setns(netns, CLONE_NEWNET) < 0) {
r = -errno;
goto fail;
}
r = 0;
}
cmsg = CMSG_FIRSTHDR(&mh);
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
cmsg->cmsg_len = CMSG_LEN(sizeof(int));
memcpy(CMSG_DATA(cmsg), &netns, sizeof(int));
mh.msg_controllen = cmsg->cmsg_len;
if (sendmsg(netns_storage_socket[1], &mh, MSG_DONTWAIT|MSG_NOSIGNAL) < 0) {
r = -errno;
goto fail;
}
fail:
lockf(netns_storage_socket[0], F_ULOCK, 0);
return r;
}

View File

@ -23,14 +23,15 @@
#include <stdbool.h>
int setup_tmpdirs(const char *unit_id,
char **tmp_dir,
char **var_tmp_dir);
int setup_namespace(char **read_write_dirs,
char **read_only_dirs,
char **inaccessible_dirs,
char *tmp_dir,
char *var_tmp_dir,
bool private_tmp,
unsigned mount_flags);
int setup_tmp_dirs(const char *id,
char **tmp_dir,
char **var_tmp_dir);
int setup_netns(int netns_storage_socket[2]);

View File

@ -304,7 +304,8 @@ static void service_done(Unit *u) {
s->status_text = NULL;
cgroup_context_done(&s->cgroup_context);
exec_context_done(&s->exec_context, manager_is_reloading_or_reexecuting(u->manager));
exec_context_done(&s->exec_context);
s->exec_runtime = exec_runtime_unref(s->exec_runtime);
exec_command_free_array(s->exec_command, _SERVICE_EXEC_COMMAND_MAX);
s->control_command = NULL;
s->main_command = NULL;
@ -1751,6 +1752,10 @@ static int service_spawn(
unit_realize_cgroup(UNIT(s));
r = unit_setup_exec_runtime(UNIT(s));
if (r < 0)
goto fail;
if (pass_fds ||
s->exec_context.std_input == EXEC_INPUT_SOCKET ||
s->exec_context.std_output == EXEC_OUTPUT_SOCKET ||
@ -1834,6 +1839,7 @@ static int service_spawn(
path,
UNIT(s)->id,
s->type == SERVICE_IDLE ? UNIT(s)->manager->idle_pipe : NULL,
s->exec_runtime,
&pid);
if (r < 0)
goto fail;
@ -1932,7 +1938,8 @@ static void service_enter_dead(Service *s, ServiceResult f, bool allow_restart)
s->forbid_restart = false;
/* we want fresh tmpdirs in case service is started again immediately */
exec_context_tmp_dirs_done(&s->exec_context);
exec_runtime_destroy(s->exec_runtime);
s->exec_runtime = exec_runtime_unref(s->exec_runtime);
/* Try to delete the pid file. At this point it will be
* out-of-date, and some software might be confused by it, so
@ -2642,12 +2649,6 @@ static int service_serialize(Unit *u, FILE *f, FDSet *fds) {
dual_timestamp_serialize(f, "watchdog-timestamp",
&s->watchdog_timestamp);
if (s->exec_context.tmp_dir)
unit_serialize_item(u, f, "tmp-dir", s->exec_context.tmp_dir);
if (s->exec_context.var_tmp_dir)
unit_serialize_item(u, f, "var-tmp-dir", s->exec_context.var_tmp_dir);
if (s->forbid_restart)
unit_serialize_item(u, f, "forbid-restart", yes_no(s->forbid_restart));
@ -2771,23 +2772,7 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value,
dual_timestamp_deserialize(value, &s->main_exec_status.exit_timestamp);
else if (streq(key, "watchdog-timestamp"))
dual_timestamp_deserialize(value, &s->watchdog_timestamp);
else if (streq(key, "tmp-dir")) {
char *t;
t = strdup(value);
if (!t)
return log_oom();
s->exec_context.tmp_dir = t;
} else if (streq(key, "var-tmp-dir")) {
char *t;
t = strdup(value);
if (!t)
return log_oom();
s->exec_context.var_tmp_dir = t;
} else if (streq(key, "forbid-restart")) {
else if (streq(key, "forbid-restart")) {
int b;
b = parse_boolean(value);
@ -3835,6 +3820,7 @@ const UnitVTable service_vtable = {
.exec_context_offset = offsetof(Service, exec_context),
.cgroup_context_offset = offsetof(Service, cgroup_context),
.kill_context_offset = offsetof(Service, kill_context),
.exec_runtime_offset = offsetof(Service, exec_runtime),
.sections =
"Unit\0"

View File

@ -154,6 +154,9 @@ struct Service {
/* The ID of the control command currently being executed */
ServiceExecCommand control_command_id;
/* Runtime data of the execution context */
ExecRuntime *exec_runtime;
pid_t main_pid, control_pid;
int socket_fd;

View File

@ -134,9 +134,9 @@ static void socket_done(Unit *u) {
socket_free_ports(s);
exec_context_done(&s->exec_context, manager_is_reloading_or_reexecuting(u->manager));
cgroup_context_init(&s->cgroup_context);
cgroup_context_done(&s->cgroup_context);
exec_context_done(&s->exec_context);
s->exec_runtime = exec_runtime_unref(s->exec_runtime);
exec_command_free_array(s->exec_command, _SOCKET_EXEC_COMMAND_MAX);
s->control_command = NULL;
@ -1232,6 +1232,10 @@ static int socket_spawn(Socket *s, ExecCommand *c, pid_t *_pid) {
unit_realize_cgroup(UNIT(s));
r = unit_setup_exec_runtime(UNIT(s));
if (r < 0)
goto fail;
r = socket_arm_timer(s);
if (r < 0)
goto fail;
@ -1253,6 +1257,7 @@ static int socket_spawn(Socket *s, ExecCommand *c, pid_t *_pid) {
UNIT(s)->cgroup_path,
UNIT(s)->id,
NULL,
s->exec_runtime,
&pid);
strv_free(argv);
@ -1280,7 +1285,9 @@ static void socket_enter_dead(Socket *s, SocketResult f) {
if (f != SOCKET_SUCCESS)
s->result = f;
exec_context_tmp_dirs_done(&s->exec_context);
exec_runtime_destroy(s->exec_runtime);
s->exec_runtime = exec_runtime_unref(s->exec_runtime);
socket_set_state(s, s->result != SOCKET_SUCCESS ? SOCKET_FAILED : SOCKET_DEAD);
}
@ -1736,11 +1743,12 @@ static int socket_serialize(Unit *u, FILE *f, FDSet *fds) {
if (p->fd < 0)
continue;
if ((copy = fdset_put_dup(fds, p->fd)) < 0)
copy = fdset_put_dup(fds, p->fd);
if (copy < 0)
return copy;
if (p->type == SOCKET_SOCKET) {
char *t;
_cleanup_free_ char *t = NULL;
r = socket_address_print(&p->address, &t);
if (r < 0)
@ -1750,7 +1758,7 @@ static int socket_serialize(Unit *u, FILE *f, FDSet *fds) {
unit_serialize_item_format(u, f, "netlink", "%i %s", copy, t);
else
unit_serialize_item_format(u, f, "socket", "%i %i %s", copy, p->address.type, t);
free(t);
} else if (p->type == SOCKET_SPECIAL)
unit_serialize_item_format(u, f, "special", "%i %s", copy, p->path);
else if (p->type == SOCKET_MQUEUE)
@ -1761,8 +1769,6 @@ static int socket_serialize(Unit *u, FILE *f, FDSet *fds) {
}
}
exec_context_serialize(&s->exec_context, UNIT(s), f);
return 0;
}
@ -1922,22 +1928,6 @@ static int socket_deserialize_item(Unit *u, const char *key, const char *value,
p->fd = fdset_remove(fds, fd);
}
}
} else if (streq(key, "tmp-dir")) {
char *t;
t = strdup(value);
if (!t)
return log_oom();
s->exec_context.tmp_dir = t;
} else if (streq(key, "var-tmp-dir")) {
char *t;
t = strdup(value);
if (!t)
return log_oom();
s->exec_context.var_tmp_dir = t;
} else
log_debug_unit(UNIT(s)->id,
"Unknown serialization key '%s'", key);
@ -2428,6 +2418,7 @@ const UnitVTable socket_vtable = {
.exec_context_offset = offsetof(Socket, exec_context),
.cgroup_context_offset = offsetof(Socket, cgroup_context),
.kill_context_offset = offsetof(Socket, kill_context),
.exec_runtime_offset = offsetof(Socket, exec_runtime),
.sections =
"Unit\0"

View File

@ -105,6 +105,7 @@ struct Socket {
ExecContext exec_context;
KillContext kill_context;
CGroupContext cgroup_context;
ExecRuntime *exec_runtime;
/* For Accept=no sockets refers to the one service we'll
activate. For Accept=yes sockets is either NULL, or filled

View File

@ -155,12 +155,12 @@ static void swap_done(Unit *u) {
free(s->parameters_fragment.what);
s->parameters_fragment.what = NULL;
exec_context_done(&s->exec_context, manager_is_reloading_or_reexecuting(u->manager));
cgroup_context_done(&s->cgroup_context);
exec_context_done(&s->exec_context);
s->exec_runtime = exec_runtime_unref(s->exec_runtime);
exec_command_done_array(s->exec_command, _SWAP_EXEC_COMMAND_MAX);
s->control_command = NULL;
cgroup_context_done(&s->cgroup_context);
swap_unwatch_control_pid(s);
s->timer_event_source = sd_event_source_unref(s->timer_event_source);
@ -625,6 +625,10 @@ static int swap_spawn(Swap *s, ExecCommand *c, pid_t *_pid) {
unit_realize_cgroup(UNIT(s));
r = unit_setup_exec_runtime(UNIT(s));
if (r < 0)
goto fail;
r = swap_arm_timer(s);
if (r < 0)
goto fail;
@ -642,6 +646,7 @@ static int swap_spawn(Swap *s, ExecCommand *c, pid_t *_pid) {
UNIT(s)->cgroup_path,
UNIT(s)->id,
NULL,
s->exec_runtime,
&pid);
if (r < 0)
goto fail;
@ -667,7 +672,9 @@ static void swap_enter_dead(Swap *s, SwapResult f) {
if (f != SWAP_SUCCESS)
s->result = f;
exec_context_tmp_dirs_done(&s->exec_context);
exec_runtime_destroy(s->exec_runtime);
s->exec_runtime = exec_runtime_unref(s->exec_runtime);
swap_set_state(s, s->result != SWAP_SUCCESS ? SWAP_FAILED : SWAP_DEAD);
}
@ -867,8 +874,6 @@ static int swap_serialize(Unit *u, FILE *f, FDSet *fds) {
if (s->control_command_id >= 0)
unit_serialize_item(u, f, "control-command", swap_exec_command_to_string(s->control_command_id));
exec_context_serialize(&s->exec_context, UNIT(s), f);
return 0;
}
@ -912,22 +917,6 @@ static int swap_deserialize_item(Unit *u, const char *key, const char *value, FD
s->control_command_id = id;
s->control_command = s->exec_command + id;
}
} else if (streq(key, "tmp-dir")) {
char *t;
t = strdup(value);
if (!t)
return log_oom();
s->exec_context.tmp_dir = t;
} else if (streq(key, "var-tmp-dir")) {
char *t;
t = strdup(value);
if (!t)
return log_oom();
s->exec_context.var_tmp_dir = t;
} else
log_debug_unit(u->id, "Unknown serialization key '%s'", key);
@ -1420,6 +1409,7 @@ const UnitVTable swap_vtable = {
.exec_context_offset = offsetof(Swap, exec_context),
.cgroup_context_offset = offsetof(Swap, cgroup_context),
.kill_context_offset = offsetof(Swap, kill_context),
.exec_runtime_offset = offsetof(Swap, exec_runtime),
.sections =
"Unit\0"

View File

@ -98,6 +98,8 @@ struct Swap {
KillContext kill_context;
CGroupContext cgroup_context;
ExecRuntime *exec_runtime;
SwapState state, deserialized_state;
ExecCommand* control_command;

View File

@ -49,6 +49,7 @@
#include "fileio-label.h"
#include "bus-errors.h"
#include "dbus.h"
#include "execute.h"
const UnitVTable * const unit_vtable[_UNIT_TYPE_MAX] = {
[UNIT_SERVICE] = &service_vtable,
@ -1745,6 +1746,7 @@ int unit_add_dependency(Unit *u, UnitDependency d, Unit *other, bool add_referen
[UNIT_TRIGGERED_BY] = UNIT_TRIGGERS,
[UNIT_PROPAGATES_RELOAD_TO] = UNIT_RELOAD_PROPAGATED_FROM,
[UNIT_RELOAD_PROPAGATED_FROM] = UNIT_PROPAGATES_RELOAD_TO,
[UNIT_JOINS_NAMESPACE_OF] = UNIT_JOINS_NAMESPACE_OF,
};
int r, q = 0, v = 0, w = 0;
@ -1760,34 +1762,47 @@ int unit_add_dependency(Unit *u, UnitDependency d, Unit *other, bool add_referen
if (u == other)
return 0;
if ((r = set_ensure_allocated(&u->dependencies[d], trivial_hash_func, trivial_compare_func)) < 0)
r = set_ensure_allocated(&u->dependencies[d], trivial_hash_func, trivial_compare_func);
if (r < 0)
return r;
if (inverse_table[d] != _UNIT_DEPENDENCY_INVALID)
if ((r = set_ensure_allocated(&other->dependencies[inverse_table[d]], trivial_hash_func, trivial_compare_func)) < 0)
if (inverse_table[d] != _UNIT_DEPENDENCY_INVALID) {
r = set_ensure_allocated(&other->dependencies[inverse_table[d]], trivial_hash_func, trivial_compare_func);
if (r < 0)
return r;
}
if (add_reference) {
r = set_ensure_allocated(&u->dependencies[UNIT_REFERENCES], trivial_hash_func, trivial_compare_func);
if (r < 0)
return r;
if (add_reference)
if ((r = set_ensure_allocated(&u->dependencies[UNIT_REFERENCES], trivial_hash_func, trivial_compare_func)) < 0 ||
(r = set_ensure_allocated(&other->dependencies[UNIT_REFERENCED_BY], trivial_hash_func, trivial_compare_func)) < 0)
r = set_ensure_allocated(&other->dependencies[UNIT_REFERENCED_BY], trivial_hash_func, trivial_compare_func);
if (r < 0)
return r;
}
if ((q = set_put(u->dependencies[d], other)) < 0)
q = set_put(u->dependencies[d], other);
if (q < 0)
return q;
if (inverse_table[d] != _UNIT_DEPENDENCY_INVALID)
if ((v = set_put(other->dependencies[inverse_table[d]], u)) < 0) {
if (inverse_table[d] != _UNIT_DEPENDENCY_INVALID && inverse_table[d] != d) {
v = set_put(other->dependencies[inverse_table[d]], u);
if (v < 0) {
r = v;
goto fail;
}
}
if (add_reference) {
if ((w = set_put(u->dependencies[UNIT_REFERENCES], other)) < 0) {
w = set_put(u->dependencies[UNIT_REFERENCES], other);
if (w < 0) {
r = w;
goto fail;
}
if ((r = set_put(other->dependencies[UNIT_REFERENCED_BY], u)) < 0)
r = set_put(other->dependencies[UNIT_REFERENCED_BY], u);
if (r < 0)
goto fail;
}
@ -2082,6 +2097,7 @@ bool unit_can_serialize(Unit *u) {
}
int unit_serialize(Unit *u, FILE *f, FDSet *fds, bool serialize_jobs) {
ExecRuntime *rt;
int r;
assert(u);
@ -2095,17 +2111,11 @@ int unit_serialize(Unit *u, FILE *f, FDSet *fds, bool serialize_jobs) {
if (r < 0)
return r;
if (serialize_jobs) {
if (u->job) {
fprintf(f, "job\n");
job_serialize(u->job, f, fds);
}
if (u->nop_job) {
fprintf(f, "job\n");
job_serialize(u->nop_job, f, fds);
}
rt = unit_get_exec_runtime(u);
if (rt) {
r = exec_runtime_serialize(rt, u, f, fds);
if (r < 0)
return r;
}
dual_timestamp_serialize(f, "inactive-exit-timestamp", &u->inactive_exit_timestamp);
@ -2122,6 +2132,18 @@ int unit_serialize(Unit *u, FILE *f, FDSet *fds, bool serialize_jobs) {
if (u->cgroup_path)
unit_serialize_item(u, f, "cgroup", u->cgroup_path);
if (serialize_jobs) {
if (u->job) {
fprintf(f, "job\n");
job_serialize(u->job, f, fds);
}
if (u->nop_job) {
fprintf(f, "job\n");
job_serialize(u->nop_job, f, fds);
}
}
/* End marker */
fputc('\n', f);
return 0;
@ -2155,6 +2177,8 @@ void unit_serialize_item(Unit *u, FILE *f, const char *key, const char *value) {
}
int unit_deserialize(Unit *u, FILE *f, FDSet *fds) {
size_t offset;
ExecRuntime **rt;
int r;
assert(u);
@ -2164,6 +2188,10 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) {
if (!unit_can_serialize(u))
return 0;
offset = UNIT_VTABLE(u)->exec_runtime_offset;
if (offset > 0)
rt = (ExecRuntime**) ((uint8_t*) u + offset);
for (;;) {
char line[LINE_MAX], *l, *v;
size_t k;
@ -2276,6 +2304,14 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) {
continue;
}
if (rt) {
r = exec_runtime_deserialize_item(rt, u, l, v, fds);
if (r < 0)
return r;
if (r > 0)
continue;
}
r = UNIT_VTABLE(u)->deserialize_item(u, l, v, fds);
if (r < 0)
return r;
@ -2660,6 +2696,16 @@ CGroupContext *unit_get_cgroup_context(Unit *u) {
return (CGroupContext*) ((uint8_t*) u + offset);
}
ExecRuntime *unit_get_exec_runtime(Unit *u) {
size_t offset;
offset = UNIT_VTABLE(u)->exec_runtime_offset;
if (offset <= 0)
return NULL;
return *(ExecRuntime**) ((uint8_t*) u + offset);
}
static int drop_in_file(Unit *u, UnitSetPropertiesMode mode, const char *name, char **_p, char **_q) {
_cleanup_free_ char *b = NULL;
char *p, *q;
@ -3010,6 +3056,33 @@ int unit_require_mounts_for(Unit *u, const char *path) {
return 0;
}
int unit_setup_exec_runtime(Unit *u) {
ExecRuntime **rt;
size_t offset;
Iterator i;
Unit *other;
offset = UNIT_VTABLE(u)->exec_runtime_offset;
assert(offset > 0);
/* Check if ther already is an ExecRuntime for this unit? */
rt = (ExecRuntime**) ((uint8_t*) u + offset);
if (*rt)
return 0;
/* Try to get it from somebody else */
SET_FOREACH(other, u->dependencies[UNIT_JOINS_NAMESPACE_OF], i) {
*rt = unit_get_exec_runtime(other);
if (*rt) {
exec_runtime_ref(*rt);
return 0;
}
}
return exec_runtime_make(rt, unit_get_exec_context(u), u->id);
}
static const char* const unit_active_state_table[_UNIT_ACTIVE_STATE_MAX] = {
[UNIT_ACTIVE] = "active",
[UNIT_RELOADING] = "reloading",
@ -3045,6 +3118,7 @@ static const char* const unit_dependency_table[_UNIT_DEPENDENCY_MAX] = {
[UNIT_RELOAD_PROPAGATED_FROM] = "ReloadPropagatedFrom",
[UNIT_REFERENCES] = "References",
[UNIT_REFERENCED_BY] = "ReferencedBy",
[UNIT_JOINS_NAMESPACE_OF] = "JoinsNamespaceOf",
};
DEFINE_STRING_TABLE_LOOKUP(unit_dependency, UnitDependency);

View File

@ -106,6 +106,9 @@ enum UnitDependency {
UNIT_PROPAGATES_RELOAD_TO,
UNIT_RELOAD_PROPAGATED_FROM,
/* Joins namespace of */
UNIT_JOINS_NAMESPACE_OF,
/* Reference information for GC logic */
UNIT_REFERENCES, /* Inverse of 'references' is 'referenced_by' */
UNIT_REFERENCED_BY,
@ -301,6 +304,11 @@ struct UnitVTable {
* KillContext is found, if the unit type has that */
size_t kill_context_offset;
/* If greater than 0, the offset into the object where the
* pointer to ExecRuntime is found, if the unit type has
* that */
size_t exec_runtime_offset;
/* The name of the configuration file section with the private settings of this unit*/
const char *private_section;
@ -586,6 +594,9 @@ int unit_exec_context_defaults(Unit *u, ExecContext *c);
ExecContext *unit_get_exec_context(Unit *u) _pure_;
KillContext *unit_get_kill_context(Unit *u) _pure_;
CGroupContext *unit_get_cgroup_context(Unit *u) _pure_;
ExecRuntime *unit_get_exec_runtime(Unit *u) _pure_;
int unit_setup_exec_runtime(Unit *u);
int unit_write_drop_in(Unit *u, UnitSetPropertiesMode mode, const char *name, const char *data);
int unit_write_drop_in_format(Unit *u, UnitSetPropertiesMode mode, const char *name, const char *format, ...) _printf_(4,5);

View File

@ -399,7 +399,6 @@ static int terminate_machine(sd_bus *bus, char **args, unsigned n) {
static int openpt_in_namespace(pid_t pid, int flags) {
_cleanup_close_ int nsfd = -1, rootfd = -1;
_cleanup_free_ char *ns = NULL, *root = NULL;
_cleanup_close_pipe_ int sock[2] = { -1, -1 };
union {
struct cmsghdr cmsghdr;
@ -411,20 +410,17 @@ static int openpt_in_namespace(pid_t pid, int flags) {
};
struct cmsghdr *cmsg;
int master = -1, r;
char *ns, *root;
pid_t child;
siginfo_t si;
r = asprintf(&ns, "/proc/%lu/ns/mnt", (unsigned long) pid);
if (r < 0)
return -ENOMEM;
ns = procfs_file_alloca(pid, "ns/mnt");
nsfd = open(ns, O_RDONLY|O_NOCTTY|O_CLOEXEC);
if (nsfd < 0)
return -errno;
r = asprintf(&root, "/proc/%lu/root", (unsigned long) pid);
if (r < 0)
return -ENOMEM;
root = procfs_file_alloca(pid, "root");
rootfd = open(root, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY);
if (rootfd < 0)

View File

@ -5705,56 +5705,6 @@ int search_and_fopen_nulstr(const char *path, const char *mode, const char *sear
return search_and_fopen_internal(path, mode, s, _f);
}
int create_tmp_dir(char template[], char** dir_name) {
int r = 0;
char *d = NULL, *dt;
assert(dir_name);
RUN_WITH_UMASK(0077) {
d = mkdtemp(template);
}
if (!d) {
log_error("Can't create directory %s: %m", template);
return -errno;
}
dt = strjoin(d, "/tmp", NULL);
if (!dt) {
r = log_oom();
goto fail3;
}
RUN_WITH_UMASK(0000) {
r = mkdir(dt, 0777);
}
if (r < 0) {
log_error("Can't create directory %s: %m", dt);
r = -errno;
goto fail2;
}
log_debug("Created temporary directory %s", dt);
r = chmod(dt, 0777 | S_ISVTX);
if (r < 0) {
log_error("Failed to chmod %s: %m", dt);
r = -errno;
goto fail1;
}
log_debug("Set sticky bit on %s", dt);
*dir_name = dt;
return 0;
fail1:
rmdir(dt);
fail2:
free(dt);
fail3:
rmdir(template);
return r;
}
char *strextend(char **x, ...) {
va_list ap;
size_t f, l;

View File

@ -636,7 +636,6 @@ int on_ac_power(void);
int search_and_fopen(const char *path, const char *mode, const char **search, FILE **_f);
int search_and_fopen_nulstr(const char *path, const char *mode, const char *search, FILE **_f);
int create_tmp_dir(char template[], char** dir_name);
#define FOREACH_LINE(line, f, on_error) \
for (;;) \

View File

@ -20,27 +20,102 @@
***/
#include <libgen.h>
#include <sys/socket.h>
#include "namespace.h"
#include "util.h"
static void test_tmpdir(const char *id, const char *A, const char *B) {
_cleanup_free_ char *a, *b;
struct stat x, y;
char *c, *d;
assert_se(setup_tmpdirs(id, &a, &b) == 0);
assert(startswith(a, A));
assert(startswith(b, B));
assert(access(a, F_OK) == 0);
assert(access(b, F_OK) == 0);
assert_se(setup_tmp_dirs(id, &a, &b) == 0);
assert_se(startswith(a, A));
assert_se(startswith(b, B));
assert_se(rmdir(a) == 0);
assert_se(rmdir(b) == 0);
assert_se(stat(a, &x) >= 0);
assert_se(stat(b, &y) >= 0);
assert(endswith(a, "/tmp"));
assert(endswith(b, "/tmp"));
assert_se(S_ISDIR(x.st_mode));
assert_se(S_ISDIR(y.st_mode));
assert_se(rmdir(dirname(a)) == 0);
assert_se(rmdir(dirname(b)) == 0);
assert_se((x.st_mode & 01777) == 0700);
assert_se((y.st_mode & 01777) == 0700);
c = strappenda(a, "/tmp");
d = strappenda(b, "/tmp");
assert_se(stat(c, &x) >= 0);
assert_se(stat(d, &y) >= 0);
assert_se(S_ISDIR(x.st_mode));
assert_se(S_ISDIR(y.st_mode));
assert_se((x.st_mode & 01777) == 01777);
assert_se((y.st_mode & 01777) == 01777);
assert_se(rmdir(c) >= 0);
assert_se(rmdir(d) >= 0);
assert_se(rmdir(a) >= 0);
assert_se(rmdir(b) >= 0);
}
static void test_netns(void) {
_cleanup_close_pipe_ int s[2] = { -1, -1 };
pid_t pid1, pid2, pid3;
int r, n = 0;
siginfo_t si;
if (geteuid() > 0)
return;
assert_se(socketpair(AF_UNIX, SOCK_DGRAM, 0, s) >= 0);
pid1 = fork();
assert_se(pid1 >= 0);
if (pid1 == 0) {
r = setup_netns(s);
assert_se(r >= 0);
_exit(r);
}
pid2 = fork();
assert_se(pid2 >= 0);
if (pid2 == 0) {
r = setup_netns(s);
assert_se(r >= 0);
exit(r);
}
pid3 = fork();
assert_se(pid3 >= 0);
if (pid3 == 0) {
r = setup_netns(s);
assert_se(r >= 0);
exit(r);
}
r = wait_for_terminate(pid1, &si);
assert_se(r >= 0);
assert_se(si.si_code == CLD_EXITED);
n += si.si_status;
r = wait_for_terminate(pid2, &si);
assert_se(r >= 0);
assert_se(si.si_code == CLD_EXITED);
n += si.si_status;
r = wait_for_terminate(pid3, &si);
assert_se(r >= 0);
assert_se(si.si_code == CLD_EXITED);
n += si.si_status;
assert_se(n == 1);
}
int main(int argc, char *argv[]) {
@ -52,5 +127,7 @@ int main(int argc, char *argv[]) {
"/tmp/systemd-sys-devices-pci0000:00-0000:00:1a.0-usb3-3\\x2d1-3\\x2d1:1.0-bluetooth-hci0.device-",
"/var/tmp/systemd-sys-devices-pci0000:00-0000:00:1a.0-usb3-3\\x2d1-3\\x2d1:1.0-bluetooth-hci0.device-");
test_netns();
return 0;
}

View File

@ -59,7 +59,6 @@ int main(int argc, char *argv[]) {
(char **) inaccessible,
tmp_dir,
var_tmp_dir,
true,
0);
if (r < 0) {
log_error("Failed to setup namespace: %s", strerror(-r));