1
1
mirror of https://github.com/systemd/systemd-stable.git synced 2024-10-28 03:25:27 +03:00

Merge pull request #3818 from poettering/exit-status-env

beef up /var/tmp and /tmp handling; set $SERVICE_RESULT/$EXIT_CODE/$EXIT_STATUS on ExecStop= and make sure root/nobody are always resolvable
This commit is contained in:
Zbigniew Jędrzejewski-Szmek 2016-08-05 20:55:08 -04:00 committed by GitHub
commit 3bb81a80bd
24 changed files with 416 additions and 196 deletions

10
TODO
View File

@ -35,27 +35,17 @@ Features:
* RemoveIPC= in unit files for removing POSIX/SysV IPC objects
* Set SERVICE_RESULT= as env var while running ExecStop=
* Introduce ProtectSystem=strict for making the entire OS hierarchy read-only
except for a select few
* nspawn: start UID allocation loop from hash of container name
* in the DynamicUser=1 nss module, also map "nobody" and "root" statically
* pid1: log about all processes we kill with with SIGKILL or in abandoned scopes, as this should normally not happen
* nspawn: support that /proc, /sys/, /dev are pre-mounted
* nspawn: mount esp, so that bootctl can work
* define gpt header bits to select volatility mode
* nspawn: mount loopback filesystems with "discard"
* Make TasksMax= take percentages, taken relative to the pids_max sysctl and pids.max cgroup limit
* ProtectKernelLogs= (drops CAP_SYSLOG, add seccomp for syslog() syscall, and DeviceAllow to /dev/kmsg) in service files
* ProtectClock= (drops CAP_SYS_TIMES, adds seecomp filters for settimeofday, adjtimex), sets DeviceAllow o /dev/rtc

View File

@ -556,12 +556,30 @@ AC_SUBST(CERTIFICATEROOT)
AC_ARG_WITH([support-url],
AS_HELP_STRING([--with-support-url=URL],
[Specify the supoport URL to show in catalog entries included in systemd]),
[Specify the support URL to show in catalog entries included in systemd]),
[SUPPORT_URL="$withval"],
[SUPPORT_URL=http://lists.freedesktop.org/mailman/listinfo/systemd-devel])
AC_SUBST(SUPPORT_URL)
AC_ARG_WITH([nobody-user],
AS_HELP_STRING([--with-nobody-user=NAME],
[Specify the name of the nobody user (the one with UID 65534)]),
[NOBODY_USER_NAME="$withval"],
[NOBODY_USER_NAME=nobody])
AC_SUBST(NOBODY_USER_NAME)
AC_DEFINE_UNQUOTED(NOBODY_USER_NAME, ["$NOBODY_USER_NAME"], [The name of the nobody user (the one with UID 65534)])
AC_ARG_WITH([nobody-group],
AS_HELP_STRING([--with-nobody-group=NAME],
[Specify the name of the nobody group (the one with GID 65534)]),
[NOBODY_GROUP_NAME="$withval"],
[NOBODY_GROUP_NAME=nobody])
AC_SUBST(NOBODY_GROUP_NAME)
AC_DEFINE_UNQUOTED(NOBODY_GROUP_NAME, ["$NOBODY_GROUP_NAME"], [The name of the nobody group (the one with GID 65534)])
# ------------------------------------------------------------------------------
have_xz=no
AC_ARG_ENABLE(xz, AS_HELP_STRING([--disable-xz], [Disable optional XZ support]))
@ -1677,6 +1695,8 @@ AC_MSG_RESULT([
Maximum System GID: ${SYSTEM_GID_MAX}
Certificate root: ${CERTIFICATEROOT}
Support URL: ${SUPPORT_URL}
Nobody User Name: ${NOBODY_USER_NAME}
Nobody Group Name: ${NOBODY_GROUP_NAME}
CFLAGS: ${OUR_CFLAGS} ${CFLAGS}
CPPFLAGS: ${OUR_CPPFLAGS} ${CPPFLAGS}

View File

@ -61,6 +61,10 @@
<citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry> for details on
this option.</para>
<para>This module also ensures that the root and nobody users and groups (i.e. the users/groups with the UIDs/GIDs
0 and 65534) remain resolvable at all times, even if they aren't listed in <filename>/etc/passwd</filename> or
<filename>/etc/group</filename>, or if these files are missing.</para>
<para>To activate the NSS module, add <literal>systemd</literal> to the lines starting with
<literal>passwd:</literal> and <literal>group:</literal> in <filename>/etc/nsswitch.conf</filename>.</para>

View File

@ -1602,6 +1602,43 @@
functions) if their standard output or standard error output is connected to the journal anyway, thus enabling
delivery of structured metadata along with logged messages.</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>$SERVICE_RESULT</varname></term>
<listitem><para>Only defined for the service unit type, this environment variable is passed to all
<varname>ExecStop=</varname> and <varname>ExecStopPost=</varname> processes, and encodes the service
"result". Currently, the following values are defined: <literal>timeout</literal> (in case of an operation
timeout), <literal>exit-code</literal> (if a service process exited with a non-zero exit code; see
<varname>$EXIT_STATUS</varname> below for the actual exit status returned), <literal>signal</literal> (if a
service process was terminated abnormally by a signal; see <varname>$EXIT_STATUS</varname> below for the actual
signal used for the termination), <literal>core-dump</literal> (if a service process terminated abnormally and
dumped core), <literal>watchdog</literal> (if the watchdog keep-alive ping was enabled for the service but it
missed the deadline), or <literal>resources</literal> (a catch-all condition in case a system operation
failed).</para>
<para>This environment variable is useful to monitor failure or successful termination of a service. Even
though this variable is available in both <varname>ExecStop=</varname> and <varname>ExecStopPost=</varname>, it
is usually a better choice to place monitoring tools in the latter, as the former is only invoked for services
that managed to start up correctly, and the latter covers both services that failed during their start-up and
those which failed during their runtime.</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>$EXIT_CODE</varname></term>
<term><varname>$EXIT_STATUS</varname></term>
<listitem><para>Only defined for the service unit type, these environment variables are passed to all
<varname>ExecStop=</varname>, <varname>ExecStopPost=</varname> processes and contain exit status/code
information of the main process of the service. For the precise definition of the exit code and status, see
<citerefentry><refentrytitle>wait</refentrytitle><manvolnum>2</manvolnum></citerefentry>. <varname>$EXIT_CODE</varname>
is one of <literal>exited</literal>, <literal>killed</literal>,
<literal>dumped</literal>. <varname>$EXIT_STATUS</varname> contains the numeric exit code formatted as string
if <varname>$EXIT_CODE</varname> is <literal>exited</literal>, and the signal name in all other cases. Note
that these environment variables are only set if the service manager succeeded to start and identify the main
process of the service.</para></listitem>
</varlistentry>
</variablelist>
<para>Additional variables may be configured by the following

View File

@ -429,7 +429,13 @@
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>
not attempt to communicate with them.</para>
<para>Note that all commands that are configured with this setting are invoked with the result code of the
service, as well as the main process' exit code and status, set in the <varname>$SERVICE_RESULT</varname>,
<varname>$EXIT_CODE</varname> and <varname>$EXIT_STATUS</varname> environment variables, see
<citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry> for
details.</para></listitem>
</varlistentry>
<varlistentry>

View File

@ -1168,8 +1168,8 @@ int tempfn_random_child(const char *p, const char *extra, char **ret) {
char *t, *x;
uint64_t u;
unsigned i;
int r;
assert(p);
assert(ret);
/* Turns this:
@ -1178,6 +1178,12 @@ int tempfn_random_child(const char *p, const char *extra, char **ret) {
* /foo/bar/waldo/.#<extra>3c2b6219aa75d7d0
*/
if (!p) {
r = tmp_dir(&p);
if (r < 0)
return r;
}
if (!extra)
extra = "";
@ -1264,10 +1270,13 @@ int fputs_with_space(FILE *f, const char *s, const char *separator, bool *space)
int open_tmpfile_unlinkable(const char *directory, int flags) {
char *p;
int fd;
int fd, r;
if (!directory)
directory = "/tmp";
if (!directory) {
r = tmp_dir(&directory);
if (r < 0)
return r;
}
/* Returns an unlinked temporary file that cannot be linked into the file system anymore */

View File

@ -496,34 +496,94 @@ int get_files_in_directory(const char *path, char ***list) {
return n;
}
int var_tmp(char **ret) {
const char *tmp_dir = NULL;
const char *env_tmp_dir = NULL;
char *c = NULL;
int r;
static int getenv_tmp_dir(const char **ret_path) {
const char *n;
int r, ret = 0;
assert(ret);
assert(ret_path);
env_tmp_dir = getenv("TMPDIR");
if (env_tmp_dir != NULL) {
r = is_dir(env_tmp_dir, true);
if (r < 0 && r != -ENOENT)
return r;
if (r > 0)
tmp_dir = env_tmp_dir;
/* We use the same order of environment variables python uses in tempfile.gettempdir():
* https://docs.python.org/3/library/tempfile.html#tempfile.gettempdir */
FOREACH_STRING(n, "TMPDIR", "TEMP", "TMP") {
const char *e;
e = secure_getenv(n);
if (!e)
continue;
if (!path_is_absolute(e)) {
r = -ENOTDIR;
goto next;
}
if (!path_is_safe(e)) {
r = -EPERM;
goto next;
}
if (!tmp_dir)
tmp_dir = "/var/tmp";
r = is_dir(e, true);
if (r < 0)
goto next;
if (r == 0) {
r = -ENOTDIR;
goto next;
}
c = strdup(tmp_dir);
if (!c)
return -ENOMEM;
*ret = c;
*ret_path = e;
return 1;
next:
/* Remember first error, to make this more debuggable */
if (ret >= 0)
ret = r;
}
if (ret < 0)
return ret;
*ret_path = NULL;
return ret;
}
static int tmp_dir_internal(const char *def, const char **ret) {
const char *e;
int r, k;
assert(def);
assert(ret);
r = getenv_tmp_dir(&e);
if (r > 0) {
*ret = e;
return 0;
}
k = is_dir(def, true);
if (k == 0)
k = -ENOTDIR;
if (k < 0)
return r < 0 ? r : k;
*ret = def;
return 0;
}
int var_tmp_dir(const char **ret) {
/* Returns the location for "larger" temporary files, that is backed by physical storage if available, and thus
* even might survive a boot: /var/tmp. If $TMPDIR (or related environment variables) are set, its value is
* returned preferably however. Note that both this function and tmp_dir() below are affected by $TMPDIR,
* making it a variable that overrides all temporary file storage locations. */
return tmp_dir_internal("/var/tmp", ret);
}
int tmp_dir(const char **ret) {
/* Similar to var_tmp_dir() above, but returns the location for "smaller" temporary files, which is usually
* backed by an in-memory file system: /tmp. */
return tmp_dir_internal("/tmp", ret);
}
int inotify_add_watch_fd(int fd, int what, uint32_t mask) {
char path[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(int) + 1];
int r;

View File

@ -61,7 +61,8 @@ int mkfifo_atomic(const char *path, mode_t mode);
int get_files_in_directory(const char *path, char ***list);
int var_tmp(char **ret);
int tmp_dir(const char **ret);
int var_tmp_dir(const char **ret);
#define INOTIFY_EVENT_MAX (sizeof(struct inotify_event) + NAME_MAX + 1)

View File

@ -301,7 +301,7 @@ static void automount_dump(Unit *u, FILE *f, const char *prefix) {
static void automount_enter_dead(Automount *a, AutomountResult f) {
assert(a);
if (f != AUTOMOUNT_SUCCESS)
if (a->result == AUTOMOUNT_SUCCESS)
a->result = f;
automount_set_state(a, a->result != AUTOMOUNT_SUCCESS ? AUTOMOUNT_FAILED : AUTOMOUNT_DEAD);

View File

@ -442,7 +442,7 @@ fail:
static void busname_enter_dead(BusName *n, BusNameResult f) {
assert(n);
if (f != BUSNAME_SUCCESS)
if (n->result == BUSNAME_SUCCESS)
n->result = f;
busname_set_state(n, n->result != BUSNAME_SUCCESS ? BUSNAME_FAILED : BUSNAME_DEAD);
@ -454,7 +454,7 @@ static void busname_enter_signal(BusName *n, BusNameState state, BusNameResult f
assert(n);
if (f != BUSNAME_SUCCESS)
if (n->result == BUSNAME_SUCCESS)
n->result = f;
kill_context_init(&kill_context);
@ -882,7 +882,7 @@ static void busname_sigchld_event(Unit *u, pid_t pid, int code, int status) {
log_unit_full(u, f == BUSNAME_SUCCESS ? LOG_DEBUG : LOG_NOTICE, 0,
"Control process exited, code=%s status=%i", sigchld_code_to_string(code), status);
if (f != BUSNAME_SUCCESS)
if (n->result == BUSNAME_SUCCESS)
n->result = f;
switch (n->state) {

View File

@ -427,7 +427,7 @@ static int setup_input(
return STDIN_FILENO;
}
i = fixup_input(context->std_input, socket_fd, params->apply_tty_stdin);
i = fixup_input(context->std_input, socket_fd, params->flags & EXEC_APPLY_TTY_STDIN);
switch (i) {
@ -502,7 +502,7 @@ static int setup_output(
return STDERR_FILENO;
}
i = fixup_input(context->std_input, socket_fd, params->apply_tty_stdin);
i = fixup_input(context->std_input, socket_fd, params->flags & EXEC_APPLY_TTY_STDIN);
o = fixup_output(context->std_output, socket_fd);
if (fileno == STDERR_FILENO) {
@ -1425,7 +1425,7 @@ static int build_environment(
our_env[n_env++] = x;
}
if (p->watchdog_usec > 0) {
if ((p->flags & EXEC_SET_WATCHDOG) && p->watchdog_usec > 0) {
if (asprintf(&x, "WATCHDOG_PID="PID_FMT, getpid()) < 0)
return -ENOMEM;
our_env[n_env++] = x;
@ -1675,7 +1675,7 @@ static int exec_child(
exec_context_tty_reset(context, params);
if (params->confirm_spawn) {
if (params->flags & EXEC_CONFIRM_SPAWN) {
char response;
r = ask_for_confirmation(&response, argv);
@ -1940,7 +1940,7 @@ static int exec_child(
umask(context->umask);
if (params->apply_permissions && !command->privileged) {
if ((params->flags & EXEC_APPLY_PERMISSIONS) && !command->privileged) {
r = enforce_groups(context, username, gid);
if (r < 0) {
*exit_status = EXIT_GROUP;
@ -2010,7 +2010,7 @@ static int exec_child(
}
r = setup_namespace(
params->apply_chroot ? context->root_directory : NULL,
(params->flags & EXEC_APPLY_CHROOT) ? context->root_directory : NULL,
context->read_write_paths,
context->read_only_paths,
context->inaccessible_paths,
@ -2041,7 +2041,7 @@ static int exec_child(
else
wd = "/";
if (params->apply_chroot) {
if (params->flags & EXEC_APPLY_CHROOT) {
if (!needs_mount_namespace && context->root_directory)
if (chroot(context->root_directory) < 0) {
*exit_status = EXIT_CHROOT;
@ -2065,7 +2065,12 @@ static int exec_child(
}
#ifdef HAVE_SELINUX
if (params->apply_permissions && mac_selinux_use() && params->selinux_context_net && socket_fd >= 0 && !command->privileged) {
if ((params->flags & EXEC_APPLY_PERMISSIONS) &&
mac_selinux_use() &&
params->selinux_context_net &&
socket_fd >= 0 &&
!command->privileged) {
r = mac_selinux_get_child_mls_label(socket_fd, command->path, context->selinux_context, &mac_selinux_context_net);
if (r < 0) {
*exit_status = EXIT_SELINUX_CONTEXT;
@ -2090,7 +2095,7 @@ static int exec_child(
return r;
}
if (params->apply_permissions && !command->privileged) {
if ((params->flags & EXEC_APPLY_PERMISSIONS) && !command->privileged) {
bool use_address_families = context->address_families_whitelist ||
!set_isempty(context->address_families);
@ -2964,12 +2969,12 @@ void exec_status_dump(ExecStatus *s, FILE *f, const char *prefix) {
"%sPID: "PID_FMT"\n",
prefix, s->pid);
if (s->start_timestamp.realtime > 0)
if (dual_timestamp_is_set(&s->start_timestamp))
fprintf(f,
"%sStart Timestamp: %s\n",
prefix, format_timestamp(buf, sizeof(buf), s->start_timestamp.realtime));
if (s->exit_timestamp.realtime > 0)
if (dual_timestamp_is_set(&s->exit_timestamp))
fprintf(f,
"%sExit Timestamp: %s\n"
"%sExit Code: %s\n"

View File

@ -208,6 +208,19 @@ struct ExecContext {
bool no_new_privileges_set:1;
};
typedef enum ExecFlags {
EXEC_CONFIRM_SPAWN = 1U << 0,
EXEC_APPLY_PERMISSIONS = 1U << 1,
EXEC_APPLY_CHROOT = 1U << 2,
EXEC_APPLY_TTY_STDIN = 1U << 3,
/* The following are not used by execute.c, but by consumers internally */
EXEC_PASS_FDS = 1U << 4,
EXEC_IS_CONTROL = 1U << 5,
EXEC_SETENV_RESULT = 1U << 6,
EXEC_SET_WATCHDOG = 1U << 7,
} ExecFlags;
struct ExecParameters {
char **argv;
char **environment;
@ -216,11 +229,7 @@ struct ExecParameters {
char **fd_names;
unsigned n_fds;
bool apply_permissions:1;
bool apply_chroot:1;
bool apply_tty_stdin:1;
bool confirm_spawn:1;
ExecFlags flags;
bool selinux_context_net:1;
bool cgroup_delegate:1;

View File

@ -701,9 +701,7 @@ static int mount_spawn(Mount *m, ExecCommand *c, pid_t *_pid) {
pid_t pid;
int r;
ExecParameters exec_params = {
.apply_permissions = true,
.apply_chroot = true,
.apply_tty_stdin = true,
.flags = EXEC_APPLY_PERMISSIONS|EXEC_APPLY_CHROOT|EXEC_APPLY_TTY_STDIN,
.stdin_fd = -1,
.stdout_fd = -1,
.stderr_fd = -1,
@ -732,7 +730,7 @@ static int mount_spawn(Mount *m, ExecCommand *c, pid_t *_pid) {
return r;
exec_params.environment = UNIT(m)->manager->environment;
exec_params.confirm_spawn = UNIT(m)->manager->confirm_spawn;
exec_params.flags |= UNIT(m)->manager->confirm_spawn ? EXEC_CONFIRM_SPAWN : 0;
exec_params.cgroup_supported = UNIT(m)->manager->cgroup_supported;
exec_params.cgroup_path = UNIT(m)->cgroup_path;
exec_params.cgroup_delegate = m->cgroup_context.delegate;
@ -761,7 +759,7 @@ static int mount_spawn(Mount *m, ExecCommand *c, pid_t *_pid) {
static void mount_enter_dead(Mount *m, MountResult f) {
assert(m);
if (f != MOUNT_SUCCESS)
if (m->result == MOUNT_SUCCESS)
m->result = f;
mount_set_state(m, m->result != MOUNT_SUCCESS ? MOUNT_FAILED : MOUNT_DEAD);
@ -777,7 +775,7 @@ static void mount_enter_dead(Mount *m, MountResult f) {
static void mount_enter_mounted(Mount *m, MountResult f) {
assert(m);
if (f != MOUNT_SUCCESS)
if (m->result == MOUNT_SUCCESS)
m->result = f;
mount_set_state(m, MOUNT_MOUNTED);
@ -788,7 +786,7 @@ static void mount_enter_signal(Mount *m, MountState state, MountResult f) {
assert(m);
if (f != MOUNT_SUCCESS)
if (m->result == MOUNT_SUCCESS)
m->result = f;
r = unit_kill_context(
@ -1160,7 +1158,7 @@ static void mount_sigchld_event(Unit *u, pid_t pid, int code, int status) {
else
assert_not_reached("Unknown code");
if (f != MOUNT_SUCCESS)
if (m->result == MOUNT_SUCCESS)
m->result = f;
if (m->control_command) {

View File

@ -454,7 +454,7 @@ static int path_coldplug(Unit *u) {
static void path_enter_dead(Path *p, PathResult f) {
assert(p);
if (f != PATH_SUCCESS)
if (p->result == PATH_SUCCESS)
p->result = f;
path_set_state(p, p->result != PATH_SUCCESS ? PATH_FAILED : PATH_DEAD);

View File

@ -221,7 +221,7 @@ static void scope_dump(Unit *u, FILE *f, const char *prefix) {
static void scope_enter_dead(Scope *s, ScopeResult f) {
assert(s);
if (f != SCOPE_SUCCESS)
if (s->result == SCOPE_SUCCESS)
s->result = f;
scope_set_state(s, s->result != SCOPE_SUCCESS ? SCOPE_FAILED : SCOPE_DEAD);
@ -233,7 +233,7 @@ static void scope_enter_signal(Scope *s, ScopeState state, ScopeResult f) {
assert(s);
if (f != SCOPE_SUCCESS)
if (s->result == SCOPE_SUCCESS)
s->result = f;
unit_watch_all_pids(UNIT(s));

View File

@ -1171,11 +1171,7 @@ static int service_spawn(
Service *s,
ExecCommand *c,
usec_t timeout,
bool pass_fds,
bool apply_permissions,
bool apply_chroot,
bool apply_tty_stdin,
bool is_control,
ExecFlags flags,
pid_t *_pid) {
_cleanup_strv_free_ char **argv = NULL, **final_env = NULL, **our_env = NULL, **fd_names = NULL;
@ -1185,9 +1181,7 @@ static int service_spawn(
pid_t pid;
ExecParameters exec_params = {
.apply_permissions = apply_permissions,
.apply_chroot = apply_chroot,
.apply_tty_stdin = apply_tty_stdin,
.flags = flags,
.stdin_fd = -1,
.stdout_fd = -1,
.stderr_fd = -1,
@ -1199,6 +1193,14 @@ static int service_spawn(
assert(c);
assert(_pid);
if (flags & EXEC_IS_CONTROL) {
/* If this is a control process, mask the permissions/chroot application if this is requested. */
if (s->permissions_start_only)
exec_params.flags &= ~EXEC_APPLY_PERMISSIONS;
if (s->root_directory_start_only)
exec_params.flags &= ~EXEC_APPLY_CHROOT;
}
(void) unit_realize_cgroup(UNIT(s));
if (s->reset_cpu_usage) {
(void) unit_reset_cpu_usage(UNIT(s));
@ -1213,7 +1215,7 @@ static int service_spawn(
if (r < 0)
return r;
if (pass_fds ||
if ((flags & EXEC_PASS_FDS) ||
s->exec_context.std_input == EXEC_INPUT_SOCKET ||
s->exec_context.std_output == EXEC_OUTPUT_SOCKET ||
s->exec_context.std_error == EXEC_OUTPUT_SOCKET) {
@ -1233,11 +1235,11 @@ static int service_spawn(
if (r < 0)
return r;
our_env = new0(char*, 6);
our_env = new0(char*, 9);
if (!our_env)
return -ENOMEM;
if (is_control ? s->notify_access == NOTIFY_ALL : s->notify_access != NOTIFY_NONE)
if ((flags & EXEC_IS_CONTROL) ? s->notify_access == NOTIFY_ALL : s->notify_access != NOTIFY_NONE)
if (asprintf(our_env + n_env++, "NOTIFY_SOCKET=%s", UNIT(s)->manager->notify_socket) < 0)
return -ENOMEM;
@ -1245,7 +1247,7 @@ static int service_spawn(
if (asprintf(our_env + n_env++, "MAINPID="PID_FMT, s->main_pid) < 0)
return -ENOMEM;
if (!MANAGER_IS_SYSTEM(UNIT(s)->manager))
if (MANAGER_IS_USER(UNIT(s)->manager))
if (asprintf(our_env + n_env++, "MANAGERPID="PID_FMT, getpid()) < 0)
return -ENOMEM;
@ -1281,22 +1283,40 @@ static int service_spawn(
}
}
if (flags & EXEC_SETENV_RESULT) {
if (asprintf(our_env + n_env++, "SERVICE_RESULT=%s", service_result_to_string(s->result)) < 0)
return -ENOMEM;
if (s->main_exec_status.pid > 0 &&
dual_timestamp_is_set(&s->main_exec_status.exit_timestamp)) {
if (asprintf(our_env + n_env++, "EXIT_CODE=%s", sigchld_code_to_string(s->main_exec_status.code)) < 0)
return -ENOMEM;
if (s->main_exec_status.code == CLD_EXITED)
r = asprintf(our_env + n_env++, "EXIT_STATUS=%i", s->main_exec_status.status);
else
r = asprintf(our_env + n_env++, "EXIT_STATUS=%s", signal_to_string(s->main_exec_status.status));
if (r < 0)
return -ENOMEM;
}
}
final_env = strv_env_merge(2, UNIT(s)->manager->environment, our_env, NULL);
if (!final_env)
return -ENOMEM;
if (is_control && UNIT(s)->cgroup_path) {
if ((flags & EXEC_IS_CONTROL) && UNIT(s)->cgroup_path) {
path = strjoina(UNIT(s)->cgroup_path, "/control");
(void) cg_create(SYSTEMD_CGROUP_CONTROLLER, path);
} else
path = UNIT(s)->cgroup_path;
exec_params.argv = argv;
exec_params.environment = final_env;
exec_params.fds = fds;
exec_params.fd_names = fd_names;
exec_params.n_fds = n_fds;
exec_params.environment = final_env;
exec_params.confirm_spawn = UNIT(s)->manager->confirm_spawn;
exec_params.flags |= UNIT(s)->manager->confirm_spawn ? EXEC_CONFIRM_SPAWN : 0;
exec_params.cgroup_supported = UNIT(s)->manager->cgroup_supported;
exec_params.cgroup_path = path;
exec_params.cgroup_delegate = s->cgroup_context.delegate;
@ -1422,7 +1442,7 @@ static void service_enter_dead(Service *s, ServiceResult f, bool allow_restart)
int r;
assert(s);
if (f != SERVICE_SUCCESS)
if (s->result == SERVICE_SUCCESS)
s->result = f;
service_set_state(s, s->result != SERVICE_SUCCESS ? SERVICE_FAILED : SERVICE_DEAD);
@ -1471,7 +1491,7 @@ static void service_enter_stop_post(Service *s, ServiceResult f) {
int r;
assert(s);
if (f != SERVICE_SUCCESS)
if (s->result == SERVICE_SUCCESS)
s->result = f;
service_unwatch_control_pid(s);
@ -1484,11 +1504,7 @@ static void service_enter_stop_post(Service *s, ServiceResult f) {
r = service_spawn(s,
s->control_command,
s->timeout_stop_usec,
false,
!s->permissions_start_only,
!s->root_directory_start_only,
true,
true,
EXEC_APPLY_PERMISSIONS|EXEC_APPLY_CHROOT|EXEC_APPLY_TTY_STDIN|EXEC_IS_CONTROL|EXEC_SETENV_RESULT,
&s->control_pid);
if (r < 0)
goto fail;
@ -1528,7 +1544,7 @@ static void service_enter_signal(Service *s, ServiceState state, ServiceResult f
assert(s);
if (f != SERVICE_SUCCESS)
if (s->result == SERVICE_SUCCESS)
s->result = f;
unit_watch_all_pids(UNIT(s));
@ -1586,7 +1602,7 @@ static void service_enter_stop(Service *s, ServiceResult f) {
assert(s);
if (f != SERVICE_SUCCESS)
if (s->result == SERVICE_SUCCESS)
s->result = f;
service_unwatch_control_pid(s);
@ -1599,11 +1615,7 @@ static void service_enter_stop(Service *s, ServiceResult f) {
r = service_spawn(s,
s->control_command,
s->timeout_stop_usec,
false,
!s->permissions_start_only,
!s->root_directory_start_only,
false,
true,
EXEC_APPLY_PERMISSIONS|EXEC_APPLY_CHROOT|EXEC_IS_CONTROL|EXEC_SETENV_RESULT,
&s->control_pid);
if (r < 0)
goto fail;
@ -1642,7 +1654,7 @@ static bool service_good(Service *s) {
static void service_enter_running(Service *s, ServiceResult f) {
assert(s);
if (f != SERVICE_SUCCESS)
if (s->result == SERVICE_SUCCESS)
s->result = f;
service_unwatch_control_pid(s);
@ -1680,11 +1692,7 @@ static void service_enter_start_post(Service *s) {
r = service_spawn(s,
s->control_command,
s->timeout_start_usec,
false,
!s->permissions_start_only,
!s->root_directory_start_only,
false,
true,
EXEC_APPLY_PERMISSIONS|EXEC_APPLY_CHROOT|EXEC_IS_CONTROL,
&s->control_pid);
if (r < 0)
goto fail;
@ -1754,11 +1762,7 @@ static void service_enter_start(Service *s) {
r = service_spawn(s,
c,
timeout,
true,
true,
true,
true,
false,
EXEC_PASS_FDS|EXEC_APPLY_PERMISSIONS|EXEC_APPLY_CHROOT|EXEC_APPLY_TTY_STDIN|EXEC_SET_WATCHDOG,
&pid);
if (r < 0)
goto fail;
@ -1817,11 +1821,7 @@ static void service_enter_start_pre(Service *s) {
r = service_spawn(s,
s->control_command,
s->timeout_start_usec,
false,
!s->permissions_start_only,
!s->root_directory_start_only,
true,
true,
EXEC_APPLY_PERMISSIONS|EXEC_APPLY_CHROOT|EXEC_IS_CONTROL|EXEC_APPLY_TTY_STDIN,
&s->control_pid);
if (r < 0)
goto fail;
@ -1896,11 +1896,7 @@ static void service_enter_reload(Service *s) {
r = service_spawn(s,
s->control_command,
s->timeout_start_usec,
false,
!s->permissions_start_only,
!s->root_directory_start_only,
false,
true,
EXEC_APPLY_PERMISSIONS|EXEC_APPLY_CHROOT|EXEC_IS_CONTROL,
&s->control_pid);
if (r < 0)
goto fail;
@ -1938,12 +1934,9 @@ static void service_run_next_control(Service *s) {
r = service_spawn(s,
s->control_command,
timeout,
false,
!s->permissions_start_only,
!s->root_directory_start_only,
s->control_command_id == SERVICE_EXEC_START_PRE ||
s->control_command_id == SERVICE_EXEC_STOP_POST,
true,
EXEC_APPLY_PERMISSIONS|EXEC_APPLY_CHROOT|EXEC_IS_CONTROL|
(IN_SET(s->control_command_id, SERVICE_EXEC_START_PRE, SERVICE_EXEC_STOP_POST) ? EXEC_APPLY_TTY_STDIN : 0)|
(IN_SET(s->control_command_id, SERVICE_EXEC_STOP, SERVICE_EXEC_STOP_POST) ? EXEC_SETENV_RESULT : 0),
&s->control_pid);
if (r < 0)
goto fail;
@ -1981,11 +1974,7 @@ static void service_run_next_main(Service *s) {
r = service_spawn(s,
s->main_command,
s->timeout_start_usec,
true,
true,
true,
true,
false,
EXEC_PASS_FDS|EXEC_APPLY_PERMISSIONS|EXEC_APPLY_CHROOT|EXEC_APPLY_TTY_STDIN|EXEC_SET_WATCHDOG,
&pid);
if (r < 0)
goto fail;
@ -2656,7 +2645,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
"EXIT_STATUS=%i", status,
NULL);
if (f != SERVICE_SUCCESS)
if (s->result == SERVICE_SUCCESS)
s->result = f;
if (s->main_command &&
@ -2737,7 +2726,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
"Control process exited, code=%s status=%i",
sigchld_code_to_string(code), status);
if (f != SERVICE_SUCCESS)
if (s->result == SERVICE_SUCCESS)
s->result = f;
/* Immediately get rid of the cgroup, so that the

View File

@ -1749,9 +1749,7 @@ static int socket_spawn(Socket *s, ExecCommand *c, pid_t *_pid) {
pid_t pid;
int r;
ExecParameters exec_params = {
.apply_permissions = true,
.apply_chroot = true,
.apply_tty_stdin = true,
.flags = EXEC_APPLY_PERMISSIONS|EXEC_APPLY_CHROOT|EXEC_APPLY_TTY_STDIN,
.stdin_fd = -1,
.stdout_fd = -1,
.stderr_fd = -1,
@ -1785,7 +1783,7 @@ static int socket_spawn(Socket *s, ExecCommand *c, pid_t *_pid) {
exec_params.argv = argv;
exec_params.environment = UNIT(s)->manager->environment;
exec_params.confirm_spawn = UNIT(s)->manager->confirm_spawn;
exec_params.flags |= UNIT(s)->manager->confirm_spawn ? EXEC_CONFIRM_SPAWN : 0;
exec_params.cgroup_supported = UNIT(s)->manager->cgroup_supported;
exec_params.cgroup_path = UNIT(s)->cgroup_path;
exec_params.cgroup_delegate = s->cgroup_context.delegate;
@ -1897,7 +1895,7 @@ fail:
static void socket_enter_dead(Socket *s, SocketResult f) {
assert(s);
if (f != SOCKET_SUCCESS)
if (s->result == SOCKET_SUCCESS)
s->result = f;
socket_set_state(s, s->result != SOCKET_SUCCESS ? SOCKET_FAILED : SOCKET_DEAD);
@ -1916,7 +1914,7 @@ static void socket_enter_stop_post(Socket *s, SocketResult f) {
int r;
assert(s);
if (f != SOCKET_SUCCESS)
if (s->result == SOCKET_SUCCESS)
s->result = f;
socket_unwatch_control_pid(s);
@ -1944,7 +1942,7 @@ static void socket_enter_signal(Socket *s, SocketState state, SocketResult f) {
assert(s);
if (f != SOCKET_SUCCESS)
if (s->result == SOCKET_SUCCESS)
s->result = f;
r = unit_kill_context(
@ -1988,7 +1986,7 @@ static void socket_enter_stop_pre(Socket *s, SocketResult f) {
int r;
assert(s);
if (f != SOCKET_SUCCESS)
if (s->result == SOCKET_SUCCESS)
s->result = f;
socket_unwatch_control_pid(s);
@ -2770,7 +2768,7 @@ static void socket_sigchld_event(Unit *u, pid_t pid, int code, int status) {
"Control process exited, code=%s status=%i",
sigchld_code_to_string(code), status);
if (f != SOCKET_SUCCESS)
if (s->result == SOCKET_SUCCESS)
s->result = f;
if (s->control_command &&

View File

@ -611,9 +611,7 @@ static int swap_spawn(Swap *s, ExecCommand *c, pid_t *_pid) {
pid_t pid;
int r;
ExecParameters exec_params = {
.apply_permissions = true,
.apply_chroot = true,
.apply_tty_stdin = true,
.flags = EXEC_APPLY_PERMISSIONS|EXEC_APPLY_CHROOT|EXEC_APPLY_TTY_STDIN,
.stdin_fd = -1,
.stdout_fd = -1,
.stderr_fd = -1,
@ -642,7 +640,7 @@ static int swap_spawn(Swap *s, ExecCommand *c, pid_t *_pid) {
goto fail;
exec_params.environment = UNIT(s)->manager->environment;
exec_params.confirm_spawn = UNIT(s)->manager->confirm_spawn;
exec_params.flags |= UNIT(s)->manager->confirm_spawn ? EXEC_CONFIRM_SPAWN : 0;
exec_params.cgroup_supported = UNIT(s)->manager->cgroup_supported;
exec_params.cgroup_path = UNIT(s)->cgroup_path;
exec_params.cgroup_delegate = s->cgroup_context.delegate;
@ -675,7 +673,7 @@ fail:
static void swap_enter_dead(Swap *s, SwapResult f) {
assert(s);
if (f != SWAP_SUCCESS)
if (s->result == SWAP_SUCCESS)
s->result = f;
swap_set_state(s, s->result != SWAP_SUCCESS ? SWAP_FAILED : SWAP_DEAD);
@ -691,7 +689,7 @@ static void swap_enter_dead(Swap *s, SwapResult f) {
static void swap_enter_active(Swap *s, SwapResult f) {
assert(s);
if (f != SWAP_SUCCESS)
if (s->result == SWAP_SUCCESS)
s->result = f;
swap_set_state(s, SWAP_ACTIVE);
@ -702,7 +700,7 @@ static void swap_enter_signal(Swap *s, SwapState state, SwapResult f) {
assert(s);
if (f != SWAP_SUCCESS)
if (s->result == SWAP_SUCCESS)
s->result = f;
r = unit_kill_context(
@ -999,7 +997,7 @@ static void swap_sigchld_event(Unit *u, pid_t pid, int code, int status) {
else
assert_not_reached("Unknown code");
if (f != SWAP_SUCCESS)
if (s->result == SWAP_SUCCESS)
s->result = f;
if (s->control_command) {

View File

@ -291,7 +291,7 @@ static int timer_coldplug(Unit *u) {
static void timer_enter_dead(Timer *t, TimerResult f) {
assert(t);
if (f != TIMER_SUCCESS)
if (t->result == TIMER_SUCCESS)
t->result = f;
timer_set_state(t, t->result != TIMER_SUCCESS ? TIMER_FAILED : TIMER_DEAD);

View File

@ -30,6 +30,7 @@
#include "compress.h"
#include "fd-util.h"
#include "fileio.h"
#include "fs-util.h"
#include "journal-internal.h"
#include "log.h"
#include "macro.h"
@ -609,7 +610,13 @@ static int save_core(sd_journal *j, int fd, char **path, bool *unlink_temp) {
char *temp = NULL;
if (fd < 0) {
temp = strdup("/var/tmp/coredump-XXXXXX");
const char *vt;
r = var_tmp_dir(&vt);
if (r < 0)
return log_error_errno(r, "Failed to acquire temporary directory path: %m");
temp = strjoin(vt, "/coredump-XXXXXX", NULL);
if (!temp)
return log_oom();

View File

@ -826,7 +826,7 @@ int journal_file_verify(
int data_fd = -1, entry_fd = -1, entry_array_fd = -1;
unsigned i;
bool found_last = false;
_cleanup_free_ char *tmp_dir = NULL;
const char *tmp_dir = NULL;
#ifdef HAVE_GCRYPT
uint64_t last_tag = 0;
@ -846,7 +846,7 @@ int journal_file_verify(
} else if (f->seal)
return -ENOKEY;
r = var_tmp(&tmp_dir);
r = var_tmp_dir(&tmp_dir);
if (r < 0) {
log_error_errno(r, "Failed to determine temporary directory: %m");
goto fail;

View File

@ -954,7 +954,7 @@ static int method_clean_pool(sd_bus_message *message, void *userdata, sd_bus_err
/* Create a temporary file we can dump information about deleted images into. We use a temporary file for this
* instead of a pipe or so, since this might grow quit large in theory and we don't want to process this
* continuously */
result_fd = open_tmpfile_unlinkable("/tmp/", O_RDWR|O_CLOEXEC);
result_fd = open_tmpfile_unlinkable(NULL, O_RDWR|O_CLOEXEC);
if (result_fd < 0)
return -errno;

View File

@ -26,9 +26,52 @@
#include "macro.h"
#include "nss-util.h"
#include "signal-util.h"
#include "string-util.h"
#include "user-util.h"
#include "util.h"
#ifndef NOBODY_USER_NAME
#define NOBODY_USER_NAME "nobody"
#endif
#ifndef NOBODY_GROUP_NAME
#define NOBODY_GROUP_NAME "nobody"
#endif
static const struct passwd root_passwd = {
.pw_name = (char*) "root",
.pw_passwd = (char*) "x", /* see shadow file */
.pw_uid = 0,
.pw_gid = 0,
.pw_gecos = (char*) "Super User",
.pw_dir = (char*) "/root",
.pw_shell = (char*) "/bin/sh",
};
static const struct passwd nobody_passwd = {
.pw_name = (char*) NOBODY_USER_NAME,
.pw_passwd = (char*) "*", /* locked */
.pw_uid = 65534,
.pw_gid = 65534,
.pw_gecos = (char*) "User Nobody",
.pw_dir = (char*) "/",
.pw_shell = (char*) "/sbin/nologin",
};
static const struct group root_group = {
.gr_name = (char*) "root",
.gr_gid = 0,
.gr_passwd = (char*) "x", /* see shadow file */
.gr_mem = (char*[]) { NULL },
};
static const struct group nobody_group = {
.gr_name = (char*) NOBODY_GROUP_NAME,
.gr_gid = 65534,
.gr_passwd = (char*) "*", /* locked */
.gr_mem = (char*[]) { NULL },
};
NSS_GETPW_PROTOTYPES(systemd);
NSS_GETGR_PROTOTYPES(systemd);
@ -50,6 +93,23 @@ enum nss_status _nss_systemd_getpwnam_r(
assert(name);
assert(pwd);
if (!valid_user_group_name(name)) {
r = -EINVAL;
goto fail;
}
/* Synthesize entries for the root and nobody users, in case they are missing in /etc/passwd */
if (streq(name, root_passwd.pw_name)) {
*pwd = root_passwd;
*errnop = 0;
return NSS_STATUS_SUCCESS;
}
if (streq(name, nobody_passwd.pw_name)) {
*pwd = nobody_passwd;
*errnop = 0;
return NSS_STATUS_SUCCESS;
}
/* Make sure that we don't go in circles when allocating a dynamic UID by checking our own database */
if (getenv_bool("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0)
goto not_found;
@ -126,6 +186,18 @@ enum nss_status _nss_systemd_getpwuid_r(
goto fail;
}
/* Synthesize data for the root user and for nobody in case they are missing from /etc/passwd */
if (uid == root_passwd.pw_uid) {
*pwd = root_passwd;
*errnop = 0;
return NSS_STATUS_SUCCESS;
}
if (uid == nobody_passwd.pw_uid) {
*pwd = nobody_passwd;
*errnop = 0;
return NSS_STATUS_SUCCESS;
}
if (uid <= SYSTEM_UID_MAX)
goto not_found;
@ -202,6 +274,23 @@ enum nss_status _nss_systemd_getgrnam_r(
assert(name);
assert(gr);
if (!valid_user_group_name(name)) {
r = -EINVAL;
goto fail;
}
/* Synthesize records for root and nobody, in case they are missing form /etc/group */
if (streq(name, root_group.gr_name)) {
*gr = root_group;
*errnop = 0;
return NSS_STATUS_SUCCESS;
}
if (streq(name, nobody_group.gr_name)) {
*gr = nobody_group;
*errnop = 0;
return NSS_STATUS_SUCCESS;
}
if (getenv_bool("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0)
goto not_found;
@ -275,6 +364,18 @@ enum nss_status _nss_systemd_getgrgid_r(
goto fail;
}
/* Synthesize records for root and nobody, in case they are missing from /etc/group */
if (gid == root_group.gr_gid) {
*gr = root_group;
*errnop = 0;
return NSS_STATUS_SUCCESS;
}
if (gid == nobody_group.gr_gid) {
*gr = nobody_group;
*errnop = 0;
return NSS_STATUS_SUCCESS;
}
if (gid <= SYSTEM_GID_MAX)
goto not_found;

View File

@ -83,47 +83,35 @@ static void test_get_files_in_directory(void) {
}
static void test_var_tmp(void) {
char *tmp_dir = NULL;
char *tmpdir_backup = NULL;
const char *default_var_tmp = NULL;
const char *var_name;
bool do_overwrite = true;
_cleanup_free_ char *tmpdir_backup = NULL;
const char *tmp_dir = NULL, *t;
default_var_tmp = "/var/tmp";
var_name = "TMPDIR";
if (getenv(var_name) != NULL) {
tmpdir_backup = strdup(getenv(var_name));
assert_se(tmpdir_backup != NULL);
t = getenv("TMPDIR");
if (t) {
tmpdir_backup = strdup(t);
assert_se(tmpdir_backup);
}
unsetenv(var_name);
assert(unsetenv("TMPDIR") >= 0);
var_tmp(&tmp_dir);
assert_se(!strcmp(tmp_dir, default_var_tmp));
assert_se(var_tmp_dir(&tmp_dir) >= 0);
assert_se(streq(tmp_dir, "/var/tmp"));
free(tmp_dir);
assert_se(setenv("TMPDIR", "/tmp", true) >= 0);
assert_se(streq(getenv("TMPDIR"), "/tmp"));
setenv(var_name, "/tmp", do_overwrite);
assert_se(!strcmp(getenv(var_name), "/tmp"));
assert_se(var_tmp_dir(&tmp_dir) >= 0);
assert_se(streq(tmp_dir, "/tmp"));
var_tmp(&tmp_dir);
assert_se(!strcmp(tmp_dir, "/tmp"));
assert_se(setenv("TMPDIR", "/88_does_not_exist_88", true) >= 0);
assert_se(streq(getenv("TMPDIR"), "/88_does_not_exist_88"));
free(tmp_dir);
assert_se(var_tmp_dir(&tmp_dir) >= 0);
assert_se(streq(tmp_dir, "/var/tmp"));
setenv(var_name, "/88_does_not_exist_88", do_overwrite);
assert_se(!strcmp(getenv(var_name), "/88_does_not_exist_88"));
var_tmp(&tmp_dir);
assert_se(!strcmp(tmp_dir, default_var_tmp));
free(tmp_dir);
if (tmpdir_backup != NULL) {
setenv(var_name, tmpdir_backup, do_overwrite);
assert_se(!strcmp(getenv(var_name), tmpdir_backup));
free(tmpdir_backup);
if (tmpdir_backup) {
assert_se(setenv("TMPDIR", tmpdir_backup, true) >= 0);
assert_se(streq(getenv("TMPDIR"), tmpdir_backup));
}
}