assert(cgroup_context);
assert(ret);
-#define N_ENV_VARS 21
+#define N_ENV_VARS 22
our_env = new0(char*, N_ENV_VARS + _EXEC_DIRECTORY_TYPE_MAX);
if (!our_env)
return -ENOMEM;
our_env[n_env++] = x;
}
+ assert(c->private_var_tmp >= 0 && c->private_var_tmp < _PRIVATE_TMP_MAX);
+ if (c->private_tmp != c->private_var_tmp) {
+ assert(c->private_tmp == PRIVATE_TMP_DISCONNECTED);
+ assert(c->private_var_tmp == PRIVATE_TMP_NO);
+
+ /* When private tmpfs is enabled only on /tmp/, then explicitly set $TMPDIR to suggest the
+ * service to use /tmp/. */
+
+ x = strdup("TMPDIR=/tmp");
+ if (!x)
+ return -ENOMEM;
+
+ our_env[n_env++] = x;
+ }
+
assert(n_env < N_ENV_VARS + _EXEC_DIRECTORY_TYPE_MAX);
#undef N_ENV_VARS
.private_ipc = needs_sandboxing && exec_needs_ipc_namespace(context),
.private_pids = needs_sandboxing && exec_needs_pid_namespace(context) ? context->private_pids : PRIVATE_PIDS_NO,
.private_tmp = needs_sandboxing ? context->private_tmp : PRIVATE_TMP_NO,
+ .private_var_tmp = needs_sandboxing ? context->private_var_tmp : PRIVATE_TMP_NO,
.mount_apivfs = needs_sandboxing && exec_context_get_effective_mount_apivfs(context),
.bind_log_sockets = needs_sandboxing && exec_context_get_effective_bind_log_sockets(context),
if (r < 0)
return r;
+ /* This must be set in unit_patch_contexts() before executing a command. */
+ assert(c->private_var_tmp >= 0 && c->private_var_tmp < _PRIVATE_TMP_MAX);
+ r = serialize_item(f, "exec-context-private-var-tmp", private_tmp_to_string(c->private_var_tmp));
+ if (r < 0)
+ return r;
+
r = serialize_bool_elide(f, "exec-context-private-devices", c->private_devices);
if (r < 0)
return r;
c->private_tmp = private_tmp_from_string(val);
if (c->private_tmp < 0)
return c->private_tmp;
+ } else if ((val = startswith(l, "exec-context-private-var-tmp="))) {
+ c->private_var_tmp = private_tmp_from_string(val);
+ if (c->private_var_tmp < 0)
+ return c->private_var_tmp;
} else if ((val = startswith(l, "exec-context-private-devices="))) {
r = parse_boolean(val);
if (r < 0)
.mount_apivfs = -1,
.bind_log_sockets = -1,
.memory_ksm = -1,
+ .private_var_tmp = _PRIVATE_TMP_INVALID,
.set_login_environment = -1,
};
int bind_log_sockets;
int memory_ksm;
PrivateTmp private_tmp;
+ PrivateTmp private_var_tmp; /* This is not an independent parameter, but calculated from other
+ * parameters in unit_patch_contexts(). */
bool private_network;
bool private_devices;
PrivateUsers private_users;
};
exec_context_init(&exec_context);
+ exec_context.private_var_tmp = PRIVATE_TMP_DISCONNECTED;
cgroup_context_init(&cgroup_context);
(void) exec_deserialize_invocation(f, fdset, &exec_context, &command, ¶ms, &runtime, &cgroup_context);
assert(ml);
assert(p);
+ assert(p->private_tmp == p->private_var_tmp ||
+ (p->private_tmp == PRIVATE_TMP_DISCONNECTED && p->private_var_tmp == PRIVATE_TMP_NO));
if (p->tmp_dir) {
assert(p->private_tmp == PRIVATE_TMP_CONNECTED);
}
if (p->var_tmp_dir) {
- assert(p->private_tmp == PRIVATE_TMP_CONNECTED);
+ assert(p->private_var_tmp == PRIVATE_TMP_CONNECTED);
me = mount_list_extend(ml);
if (!me)
if (p->private_tmp != PRIVATE_TMP_DISCONNECTED)
return 0;
+ if (p->private_var_tmp == PRIVATE_TMP_NO) {
+ me = mount_list_extend(ml);
+ if (!me)
+ return log_oom_debug();
+ *me = (MountEntry) {
+ .path_const = "/tmp/",
+ .mode = MOUNT_PRIVATE_TMPFS,
+ .options_const = "mode=0700" NESTED_TMPFS_LIMITS,
+ .flags = MS_NODEV|MS_STRICTATIME,
+ };
+
+ return 0;
+ }
+
_cleanup_free_ char *tmpfs_dir = NULL, *tmp_dir = NULL, *var_tmp_dir = NULL;
tmpfs_dir = path_join(p->private_namespace_dir, "unit-private-tmp");
tmp_dir = path_join(tmpfs_dir, "tmp");
ProtectProc protect_proc;
ProcSubset proc_subset;
PrivateTmp private_tmp;
+ PrivateTmp private_var_tmp;
PrivatePIDs private_pids;
};
return r;
}
+ /* This must be already set in unit_patch_contexts(). */
+ assert(c->private_var_tmp >= 0 && c->private_var_tmp < _PRIVATE_TMP_MAX);
+
if (c->private_tmp == PRIVATE_TMP_CONNECTED) {
+ assert(c->private_var_tmp == PRIVATE_TMP_CONNECTED);
+
r = unit_add_mounts_for(u, "/tmp/", UNIT_DEPENDENCY_FILE, UNIT_MOUNT_WANTS);
if (r < 0)
return r;
r = unit_add_dependency_by_name(u, UNIT_AFTER, SPECIAL_TMPFILES_SETUP_SERVICE, true, UNIT_DEPENDENCY_FILE);
if (r < 0)
return r;
+
+ } else if (c->private_var_tmp == PRIVATE_TMP_DISCONNECTED && !exec_context_with_rootfs(c)) {
+ /* Even if PrivateTmp=disconnected, we still require /var/tmp/ mountpoint to be present,
+ * i.e. /var/ needs to be mounted. See comments in unit_patch_contexts(). */
+ r = unit_add_mounts_for(u, "/var/", UNIT_DEPENDENCY_FILE, UNIT_MOUNT_WANTS);
+ if (r < 0)
+ return r;
}
if (c->root_image) {
return 0;
}
+static PrivateTmp unit_get_private_var_tmp(const Unit *u, const ExecContext *c) {
+ assert(u);
+ assert(c);
+ assert(c->private_tmp >= 0 && c->private_tmp < _PRIVATE_TMP_MAX);
+
+ /* Disable disconnected private tmpfs on /var/tmp/ when DefaultDependencies=no and
+ * RootImage=/RootDirectory= are not set, as /var/ may be a separated partition.
+ * See issue #37258. */
+
+ /* PrivateTmp=yes/no also enables/disables private tmpfs on /var/tmp/. */
+ if (c->private_tmp != PRIVATE_TMP_DISCONNECTED)
+ return c->private_tmp;
+
+ /* When DefaultDependencies=yes, disconnected tmpfs is also enabled on /var/tmp/, and an explicit
+ * dependency to the mount on /var/ will be added in unit_add_exec_dependencies(). */
+ if (u->default_dependencies)
+ return PRIVATE_TMP_DISCONNECTED;
+
+ /* When RootImage=/RootDirectory= is enabled, /var/ should be prepared by the image or directory,
+ * hence we can mount a disconnected tmpfs on /var/tmp/. */
+ if (exec_context_with_rootfs(c))
+ return PRIVATE_TMP_DISCONNECTED;
+
+ /* Even if DefaultDependencies=no, enable disconnected tmpfs when
+ * RequiresMountsFor=/WantsMountsFor=/var/ is explicitly set. */
+ for (UnitMountDependencyType t = 0; t < _UNIT_MOUNT_DEPENDENCY_TYPE_MAX; t++)
+ if (hashmap_contains(u->mounts_for[t], "/var/"))
+ return PRIVATE_TMP_DISCONNECTED;
+
+ /* Check the same but for After= with Requires=/Requisite=/Wants= or friends. */
+ Unit *m = manager_get_unit(u->manager, "var.mount");
+ if (!m)
+ return PRIVATE_TMP_NO;
+
+ if (!unit_has_dependency(u, UNIT_ATOM_AFTER, m))
+ return PRIVATE_TMP_NO;
+
+ if (unit_has_dependency(u, UNIT_ATOM_PULL_IN_START, m) ||
+ unit_has_dependency(u, UNIT_ATOM_PULL_IN_VERIFY, m) ||
+ unit_has_dependency(u, UNIT_ATOM_PULL_IN_START_IGNORED, m))
+ return PRIVATE_TMP_DISCONNECTED;
+
+ return PRIVATE_TMP_NO;
+}
+
int unit_patch_contexts(Unit *u) {
CGroupContext *cc;
ExecContext *ec;
ec->restrict_suid_sgid = true;
}
+ ec->private_var_tmp = unit_get_private_var_tmp(u, ec);
+
FOREACH_ARRAY(d, ec->directories, _EXEC_DIRECTORY_TYPE_MAX)
exec_directory_sort(d);
}
ASSERT_OK(r);
+ ASSERT_OK(unit_patch_contexts(u));
ASSERT_OK(unit_start(u, NULL));
while (!IN_SET(SERVICE(u)->state, SERVICE_DEAD, SERVICE_FAILED))
SERVICE(u)->type = SERVICE_ONESHOT;
u->load_state = UNIT_LOADED;
+ ASSERT_OK(unit_patch_contexts(u));
ASSERT_OK(unit_start(u, NULL));
while (!IN_SET(SERVICE(u)->state, SERVICE_DEAD, SERVICE_FAILED))
SERVICE(u)->type = SERVICE_ONESHOT;
u->load_state = UNIT_LOADED;
+ ASSERT_OK(unit_patch_contexts(u));
r = unit_start(u, NULL);
if (r < 0)
return log_error_errno(r, "Unit start failed %m");
SERVICE(u)->type = SERVICE_ONESHOT;
u->load_state = UNIT_LOADED;
+ ASSERT_OK(unit_patch_contexts(u));
r = unit_start(u, NULL);
if (r < 0)
return log_error_errno(r, "Unit start failed %m");
SERVICE(u)->type = SERVICE_ONESHOT;
u->load_state = UNIT_LOADED;
+ ASSERT_OK(unit_patch_contexts(u));
r = unit_start(u, NULL);
if (r < 0)
return log_error_errno(r, "Unit start failed %m");