mirror of
https://github.com/systemd/systemd
synced 2026-03-11 07:34:58 +01:00
Compare commits
No commits in common. "71de0a22ff805954b3e6df95868ee82f9c7ff092" and "155ae657b1ae8b69fae811b28117cd7c83895c76" have entirely different histories.
71de0a22ff
...
155ae657b1
1
TODO
1
TODO
@ -124,6 +124,7 @@ Features:
|
||||
* report:
|
||||
- should the list of metrics use JSON-SEQ? or maybe be wrapped in a json
|
||||
array (the latter might be necessary, once we sign the combination)
|
||||
- "io.systemd.Manager.unit_active_state" is a weird mix of CamelCase and snake_case
|
||||
- metrics from pid1: suppress metrics form units that are inactive and have nothing to report
|
||||
- how to plug facts into this? i.e. hostname, ssh keys, and so on
|
||||
- switch to daan's suggested hierarchy?
|
||||
|
||||
@ -931,6 +931,13 @@ evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHP*:pnHPEliteDragonflyG2*:pvr*
|
||||
evdev:name:Intel HID events:dmi:bvn*:bvr*:bd*:svnHP*:pnHPEliteDragonflyG2*:pvr*
|
||||
KEYBOARD_KEY_08=unknown # rfkill is also reported by HP Wireless hotkeys
|
||||
|
||||
# HP Elite Dragonfly G3
|
||||
evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHP:pnHPEliteDragonfly13.5inchG3NotebookPC:pvr*
|
||||
KEYBOARD_KEY_c9=up
|
||||
KEYBOARD_KEY_d1=down
|
||||
KEYBOARD_KEY_c8=pageup
|
||||
KEYBOARD_KEY_d0=pagedown
|
||||
|
||||
# HP 430 Programmable Wireless Keypad
|
||||
evdev:input:b0005v03F0p854Ae044C*
|
||||
KEYBOARD_KEY_700f3=macro1
|
||||
|
||||
@ -2833,8 +2833,6 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
|
||||
readonly as ExtraFileDescriptorNames = ['...', ...];
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly i ReloadSignal = ...;
|
||||
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
|
||||
readonly as RefreshOnReload = ['...', ...];
|
||||
readonly t ExecMainStartTimestamp = ...;
|
||||
readonly t ExecMainStartTimestampMonotonic = ...;
|
||||
readonly t ExecMainExitTimestamp = ...;
|
||||
@ -3536,8 +3534,6 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
|
||||
|
||||
<!--property ReloadSignal is not documented!-->
|
||||
|
||||
<!--property RefreshOnReload is not documented!-->
|
||||
|
||||
<!--property ExecCondition is not documented!-->
|
||||
|
||||
<!--property ExecConditionEx is not documented!-->
|
||||
@ -4184,8 +4180,6 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="ReloadSignal"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="RefreshOnReload"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="ExecMainStartTimestamp"/>
|
||||
|
||||
<variablelist class="dbus-property" generated="True" extra-ref="ExecMainStartTimestampMonotonic"/>
|
||||
@ -12520,9 +12514,8 @@ $ gdbus introspect --system --dest org.freedesktop.systemd1 \
|
||||
<varname>ManagedOOMKills</varname>,
|
||||
<varname>ExecReloadPost</varname>, and
|
||||
<varname>ExecReloadPostEx</varname> were added in version 259.</para>
|
||||
<para><varname>BindNetworkInterface</varname>,
|
||||
<varname>MemoryTHP</varname>, and
|
||||
<varname>RefreshOnReload</varname> were added in version 260.</para>
|
||||
<para><varname>BindNetworkInterface</varname>, and
|
||||
<varname>MemoryTHP</varname> were added in version 260.</para>
|
||||
</refsect2>
|
||||
<refsect2>
|
||||
<title>Socket Unit Objects</title>
|
||||
|
||||
@ -572,13 +572,14 @@
|
||||
To disable the safety check that the extension-release file name matches the image file name, the
|
||||
<varname>x-systemd.relax-extension-release-check</varname> mount option may be appended.</para>
|
||||
|
||||
<para>If a service employs this option with
|
||||
<citerefentry><refentrytitle>systemd.v</refentrytitle><manvolnum>7</manvolnum></citerefentry>,
|
||||
and has <option>RefreshOnReload=extensions</option> enabled (the default), the confexts will
|
||||
be refreshed to pick up any changes on service reload. This only applies to confext extensions.
|
||||
Note that in case a service has this configuration enabled at first, and then it is subsequently
|
||||
removed in an update followed by a daemon-reload operation, reloading the confexts will be a no-op,
|
||||
and a full service restart is required instead. See
|
||||
<para>This option can be used together with a <option>notify-reload</option> service type and
|
||||
<citerefentry><refentrytitle>systemd.v</refentrytitle><manvolnum>7</manvolnum></citerefentry>
|
||||
to manage configuration updates. When such a service carrying confext images is reloaded via
|
||||
<command>systemctl reload foo.service</command> or equivalent D-Bus method, the confext itself will
|
||||
be reloaded to pick up any changes. This only applies to confext extensions. Note that in case a
|
||||
service has this configuration enabled at first, and then it is subsequently removed in an update
|
||||
followed by a daemon-reload operation, reloading the confexts will be a no-op, and a full service
|
||||
restart is required instead. See
|
||||
<citerefentry><refentrytitle>systemd.service</refentrytitle><manvolnum>5</manvolnum></citerefentry>
|
||||
also for details.</para>
|
||||
|
||||
@ -629,13 +630,14 @@
|
||||
or the host. See:
|
||||
<citerefentry><refentrytitle>os-release</refentrytitle><manvolnum>5</manvolnum></citerefentry>.</para>
|
||||
|
||||
<para>If a service employs this option with
|
||||
<citerefentry><refentrytitle>systemd.v</refentrytitle><manvolnum>7</manvolnum></citerefentry>,
|
||||
and has <option>RefreshOnReload=extensions</option> enabled (the default), the confexts will
|
||||
be refreshed to pick up any changes on service reload. This only applies to confext extensions.
|
||||
Note that in case a service has this configuration enabled at first, and then it is subsequently
|
||||
removed in an update followed by a daemon-reload operation, reloading the confexts will be a no-op,
|
||||
and a full service restart is required instead. See
|
||||
<para>This option can be used together with a <option>notify-reload</option> service type and
|
||||
<citerefentry><refentrytitle>systemd.v</refentrytitle><manvolnum>7</manvolnum></citerefentry>
|
||||
to manage configuration updates. When such a service carrying confext directories is reloaded via
|
||||
<command>systemctl reload foo.service</command> or equivalent D-Bus method, the confext itself will
|
||||
be reloaded to pick up any changes. This only applies to confext extensions. Note that in case a
|
||||
service has this configuration enabled at first, and then it is subsequently removed in an update
|
||||
followed by a daemon-reload operation, reloading the confexts will be a no-op, and a full service
|
||||
restart is required instead. See
|
||||
<citerefentry><refentrytitle>systemd.service</refentrytitle><manvolnum>5</manvolnum></citerefentry>
|
||||
also for details.</para>
|
||||
|
||||
|
||||
@ -1349,28 +1349,6 @@
|
||||
<xi:include href="version-info.xml" xpointer="v253"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>RefreshOnReload=</varname></term>
|
||||
|
||||
<listitem><para>Takes a boolean argument, or a list of resources defined in
|
||||
<citerefentry><refentrytitle>systemd.exec</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
|
||||
Possible values are <option>extensions</option> and <option>credentials</option>, separated by space.
|
||||
Prepending the list with a single tilde character (<literal>~</literal>) inverts the effect.
|
||||
Defaults to <option>extensions</option>. An empty assignment resets the list to default. If enabled,
|
||||
the corresponding resources (<option>ExtensionImages=</option>/<option>ExtensionDirectories=</option>
|
||||
for <option>extensions</option> and <option>LoadCredential=</option>/<option>ImportCredential=</option>/
|
||||
<option>SetCredential=</option> (along with their <option>Encrypted</option> counterparts)
|
||||
for <option>credentials</option>) will be refreshed on service reload. If <option>yes</option>,
|
||||
all resources listed above that are used by the service shall be refreshed.</para>
|
||||
|
||||
<para>Specially, if this option is set explicitly, and the respective resources are in use,
|
||||
the service may be reloaded without any actual reload mechanism (<option>ExecReload=</option>
|
||||
or <option>Type=notify-reload</option>) for notifying the main process, in which case the reload
|
||||
is considered complete immediately after refreshing.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v260"/></listitem>
|
||||
</varlistentry>
|
||||
|
||||
</variablelist>
|
||||
|
||||
<para id='shared-unit-options'>Check
|
||||
|
||||
@ -44,4 +44,3 @@ kn
|
||||
ar
|
||||
km
|
||||
kw
|
||||
kk
|
||||
|
||||
@ -262,71 +262,6 @@ int namespace_enter(int pidns_fd, int mntns_fd, int netns_fd, int userns_fd, int
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int namespace_enter_one_idempotent(int nsfd, NamespaceType type) {
|
||||
int r;
|
||||
|
||||
/* Join a namespace, but only if we're not part of it already. This is important if we don't necessarily
|
||||
* own the namespace in question, as kernel would unconditionally return EPERM otherwise. */
|
||||
|
||||
assert(nsfd >= 0);
|
||||
assert(type >= 0 && type < _NAMESPACE_TYPE_MAX);
|
||||
|
||||
r = is_our_namespace(nsfd, type);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r > 0)
|
||||
return 0;
|
||||
|
||||
if (setns(nsfd, namespace_info[type].clone_flag) < 0)
|
||||
return -errno;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int namespace_enter_delegated(int userns_fd, int pidns_fd, int mntns_fd, int netns_fd, int root_fd) {
|
||||
int r;
|
||||
|
||||
/* Similar to namespace_enter(), but operates on a set of namespaces that are potentially owned
|
||||
* by the userns ("delegated"), in which case we'll need to gain CAP_SYS_ADMIN by joining
|
||||
* the userns first, and the rest later. */
|
||||
|
||||
assert(userns_fd >= 0);
|
||||
|
||||
/* Block dlopen() now, to avoid us inadvertently loading shared library from another namespace */
|
||||
block_dlopen();
|
||||
|
||||
if (setns(userns_fd, CLONE_NEWUSER) < 0)
|
||||
return -errno;
|
||||
|
||||
if (pidns_fd >= 0) {
|
||||
r = namespace_enter_one_idempotent(pidns_fd, NAMESPACE_PID);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (mntns_fd >= 0) {
|
||||
r = namespace_enter_one_idempotent(mntns_fd, NAMESPACE_MOUNT);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (netns_fd >= 0) {
|
||||
r = namespace_enter_one_idempotent(netns_fd, NAMESPACE_NET);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (root_fd >= 0) {
|
||||
if (fchdir(root_fd) < 0)
|
||||
return -errno;
|
||||
|
||||
if (chroot(".") < 0)
|
||||
return -errno;
|
||||
}
|
||||
|
||||
return maybe_setgroups(/* size = */ 0, NULL);
|
||||
}
|
||||
|
||||
int fd_is_namespace(int fd, NamespaceType type) {
|
||||
int r;
|
||||
|
||||
|
||||
@ -47,7 +47,6 @@ int namespace_open(
|
||||
int *ret_root_fd);
|
||||
|
||||
int namespace_enter(int pidns_fd, int mntns_fd, int netns_fd, int userns_fd, int root_fd);
|
||||
int namespace_enter_delegated(int userns_fd, int pidns_fd, int mntns_fd, int netns_fd, int root_fd);
|
||||
|
||||
int fd_is_namespace(int fd, NamespaceType type);
|
||||
int is_our_namespace(int fd, NamespaceType type);
|
||||
|
||||
@ -1778,7 +1778,6 @@ int namespace_fork_full(
|
||||
int netns_fd,
|
||||
int userns_fd,
|
||||
int root_fd,
|
||||
bool delegated,
|
||||
PidRef *ret) {
|
||||
|
||||
_cleanup_(pidref_done_sigkill_wait) PidRef pidref_outer = PIDREF_NULL;
|
||||
@ -1824,9 +1823,6 @@ int namespace_fork_full(
|
||||
|
||||
errno_pipe_fd[0] = safe_close(errno_pipe_fd[0]);
|
||||
|
||||
if (delegated)
|
||||
r = namespace_enter_delegated(userns_fd, pidns_fd, mntns_fd, netns_fd, root_fd);
|
||||
else
|
||||
r = namespace_enter(pidns_fd, mntns_fd, netns_fd, userns_fd, root_fd);
|
||||
if (r < 0) {
|
||||
log_full_errno(prio, r, "Failed to join namespace: %m");
|
||||
|
||||
@ -201,7 +201,6 @@ int namespace_fork_full(
|
||||
int netns_fd,
|
||||
int userns_fd,
|
||||
int root_fd,
|
||||
bool delegated,
|
||||
PidRef *ret);
|
||||
|
||||
static inline int namespace_fork(
|
||||
@ -216,7 +215,7 @@ static inline int namespace_fork(
|
||||
PidRef *ret) {
|
||||
|
||||
return namespace_fork_full(outer_name, inner_name, NULL, 0, flags,
|
||||
pidns_fd, mntns_fd, netns_fd, userns_fd, root_fd, false,
|
||||
pidns_fd, mntns_fd, netns_fd, userns_fd, root_fd,
|
||||
ret);
|
||||
}
|
||||
|
||||
|
||||
@ -223,7 +223,6 @@ static const char* const service_state_table[_SERVICE_STATE_MAX] = {
|
||||
[SERVICE_RUNNING] = "running",
|
||||
[SERVICE_EXITED] = "exited",
|
||||
[SERVICE_REFRESH_EXTENSIONS] = "refresh-extensions",
|
||||
[SERVICE_REFRESH_CREDENTIALS] = "refresh-credentials",
|
||||
[SERVICE_RELOAD] = "reload",
|
||||
[SERVICE_RELOAD_SIGNAL] = "reload-signal",
|
||||
[SERVICE_RELOAD_NOTIFY] = "reload-notify",
|
||||
|
||||
@ -132,7 +132,6 @@ typedef enum ServiceState {
|
||||
SERVICE_RUNNING,
|
||||
SERVICE_EXITED, /* Nothing is running anymore, but RemainAfterExit is true hence this is OK */
|
||||
SERVICE_REFRESH_EXTENSIONS, /* Refreshing extensions for a reload request */
|
||||
SERVICE_REFRESH_CREDENTIALS, /* ditto, but for credentials */
|
||||
SERVICE_RELOAD, /* Reloading via ExecReload= */
|
||||
SERVICE_RELOAD_SIGNAL, /* Reloading via SIGHUP requested */
|
||||
SERVICE_RELOAD_NOTIFY, /* Waiting for READY=1 after RELOADING=1 notify */
|
||||
|
||||
@ -30,7 +30,6 @@
|
||||
#include "signal-util.h"
|
||||
#include "stat-util.h"
|
||||
#include "string-util.h"
|
||||
#include "strv.h"
|
||||
#include "unit.h"
|
||||
|
||||
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_type, service_type, ServiceType);
|
||||
@ -101,29 +100,6 @@ static int property_get_extra_file_descriptors(
|
||||
return sd_bus_message_close_container(reply);
|
||||
}
|
||||
|
||||
static int property_get_refresh_on_reload(
|
||||
sd_bus *bus,
|
||||
const char *path,
|
||||
const char *interface,
|
||||
const char *property,
|
||||
sd_bus_message *reply,
|
||||
void *userdata,
|
||||
sd_bus_error *reterr_error) {
|
||||
|
||||
Service *s = ASSERT_PTR(userdata);
|
||||
_cleanup_strv_free_ char **l = NULL;
|
||||
int r;
|
||||
|
||||
assert(bus);
|
||||
assert(reply);
|
||||
|
||||
r = service_refresh_on_reload_to_strv(s->refresh_on_reload_flags, &l);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return sd_bus_message_append_strv(reply, l);
|
||||
}
|
||||
|
||||
static int property_get_exit_status_set(
|
||||
sd_bus *bus,
|
||||
const char *path,
|
||||
@ -397,7 +373,6 @@ const sd_bus_vtable bus_service_vtable[] = {
|
||||
SD_BUS_PROPERTY("OpenFile", "a(sst)", property_get_open_files, offsetof(Service, open_files), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("ExtraFileDescriptorNames", "as", property_get_extra_file_descriptors, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("ReloadSignal", "i", bus_property_get_int, offsetof(Service, reload_signal), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("RefreshOnReload", "as", property_get_refresh_on_reload, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
|
||||
BUS_EXEC_STATUS_VTABLE("ExecMain", offsetof(Service, main_exec_status), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
|
||||
BUS_EXEC_COMMAND_LIST_VTABLE("ExecCondition", offsetof(Service, exec_command[SERVICE_EXEC_CONDITION]), SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION),
|
||||
@ -865,47 +840,6 @@ static int bus_service_set_transient_property(
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (streq(name, "RefreshOnReload")) {
|
||||
const char *t;
|
||||
int invert;
|
||||
|
||||
r = sd_bus_message_enter_container(message, 'a', "(bs)");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
while ((r = sd_bus_message_read(message, "(bs)", &invert, &t)) > 0) {
|
||||
ServiceRefreshOnReload f;
|
||||
|
||||
f = service_refresh_on_reload_flag_from_string(t);
|
||||
if (f < 0)
|
||||
return sd_bus_error_setf(reterr_error, SD_BUS_ERROR_INVALID_ARGS, "Invalid RefreshOnReload= value: %s", t);
|
||||
|
||||
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
|
||||
if (!s->refresh_on_reload_set)
|
||||
s->refresh_on_reload_flags = invert ? (SERVICE_REFRESH_ON_RELOAD_DEFAULT & ~f) : f;
|
||||
else
|
||||
SET_FLAG(s->refresh_on_reload_flags, f, !invert);
|
||||
|
||||
s->refresh_on_reload_set = true;
|
||||
unit_write_settingf(u, flags, name, "%s=%s%s", name, invert ? "~" : "", t);
|
||||
}
|
||||
}
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_bus_message_exit_container(message);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (!UNIT_WRITE_FLAGS_NOOP(flags) && !s->refresh_on_reload_set) { /* empty array? */
|
||||
s->refresh_on_reload_flags = 0;
|
||||
s->refresh_on_reload_set = true;
|
||||
unit_write_settingf(u, flags, name, "%s=no", name);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@ -16,25 +16,19 @@
|
||||
#include "iovec-util.h"
|
||||
#include "label-util.h"
|
||||
#include "log.h"
|
||||
#include "manager.h"
|
||||
#include "mkdir-label.h"
|
||||
#include "mount-util.h"
|
||||
#include "mountpoint-util.h"
|
||||
#include "namespace-util.h"
|
||||
#include "ordered-set.h"
|
||||
#include "path-lookup.h"
|
||||
#include "path-util.h"
|
||||
#include "pidref.h"
|
||||
#include "process-util.h"
|
||||
#include "random-util.h"
|
||||
#include "recurse-dir.h"
|
||||
#include "rm-rf.h"
|
||||
#include "siphash24.h"
|
||||
#include "socket-util.h"
|
||||
#include "stat-util.h"
|
||||
#include "string-util.h"
|
||||
#include "strv.h"
|
||||
#include "unit.h"
|
||||
#include "user-util.h"
|
||||
|
||||
ExecSetCredential* exec_set_credential_free(ExecSetCredential *sc) {
|
||||
@ -316,34 +310,6 @@ int exec_context_destroy_credentials(const ExecContext *c, const char *runtime_p
|
||||
return 0;
|
||||
}
|
||||
|
||||
typedef struct SetupCredentialsContext {
|
||||
RuntimeScope scope;
|
||||
|
||||
const ExecContext *exec_context;
|
||||
const char *unit;
|
||||
|
||||
const char *runtime_prefix;
|
||||
|
||||
const char *received_credentials_directory;
|
||||
const char *received_encrypted_credentials_directory;
|
||||
|
||||
bool always_ipc;
|
||||
|
||||
uid_t uid;
|
||||
gid_t gid;
|
||||
} SetupCredentialsContext;
|
||||
|
||||
typedef struct LoadCredentialArguments {
|
||||
const SetupCredentialsContext *context;
|
||||
|
||||
bool encrypted;
|
||||
|
||||
int write_dfd;
|
||||
bool ownership_ok;
|
||||
|
||||
uint64_t left;
|
||||
} LoadCredentialArguments;
|
||||
|
||||
typedef enum CredentialSearchPath {
|
||||
CREDENTIAL_SEARCH_PATH_TRUSTED,
|
||||
CREDENTIAL_SEARCH_PATH_ENCRYPTED,
|
||||
@ -352,15 +318,11 @@ typedef enum CredentialSearchPath {
|
||||
_CREDENTIAL_SEARCH_PATH_INVALID = -EINVAL,
|
||||
} CredentialSearchPath;
|
||||
|
||||
static int credential_search_path(
|
||||
const SetupCredentialsContext *context,
|
||||
CredentialSearchPath path,
|
||||
char ***ret) {
|
||||
|
||||
static int credential_search_path(const ExecParameters *params, CredentialSearchPath path, char ***ret) {
|
||||
_cleanup_strv_free_ char **l = NULL;
|
||||
int r;
|
||||
|
||||
assert(context);
|
||||
assert(params);
|
||||
assert(path >= 0 && path < _CREDENTIAL_SEARCH_PATH_MAX);
|
||||
assert(ret);
|
||||
|
||||
@ -369,12 +331,12 @@ static int credential_search_path(
|
||||
* credentials, we'll look in /etc/credstore.encrypted/ (and similar dirs). */
|
||||
|
||||
if (IN_SET(path, CREDENTIAL_SEARCH_PATH_ENCRYPTED, CREDENTIAL_SEARCH_PATH_ALL)) {
|
||||
r = strv_extend(&l, context->received_encrypted_credentials_directory);
|
||||
r = strv_extend(&l, params->received_encrypted_credentials_directory);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
_cleanup_strv_free_ char **add = NULL;
|
||||
r = credential_store_path_encrypted(context->scope, &add);
|
||||
r = credential_store_path_encrypted(params->runtime_scope, &add);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@ -384,12 +346,12 @@ static int credential_search_path(
|
||||
}
|
||||
|
||||
if (IN_SET(path, CREDENTIAL_SEARCH_PATH_TRUSTED, CREDENTIAL_SEARCH_PATH_ALL)) {
|
||||
r = strv_extend(&l, context->received_credentials_directory);
|
||||
r = strv_extend(&l, params->received_credentials_directory);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
_cleanup_strv_free_ char **add = NULL;
|
||||
r = credential_store_path(context->scope, &add);
|
||||
r = credential_store_path(params->runtime_scope, &add);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@ -407,6 +369,23 @@ static int credential_search_path(
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct load_cred_args {
|
||||
const ExecContext *context;
|
||||
const ExecParameters *params;
|
||||
const char *unit;
|
||||
|
||||
bool always_ipc;
|
||||
|
||||
bool encrypted;
|
||||
|
||||
int write_dfd;
|
||||
uid_t uid;
|
||||
gid_t gid;
|
||||
bool ownership_ok;
|
||||
|
||||
uint64_t left;
|
||||
};
|
||||
|
||||
static int write_credential(
|
||||
int dfd,
|
||||
const char *id,
|
||||
@ -452,7 +431,7 @@ static int write_credential(
|
||||
}
|
||||
|
||||
static int maybe_decrypt_and_write_credential(
|
||||
LoadCredentialArguments *args,
|
||||
struct load_cred_args *args,
|
||||
const char *id,
|
||||
const char *data,
|
||||
size_t size,
|
||||
@ -470,7 +449,7 @@ static int maybe_decrypt_and_write_credential(
|
||||
if (args->encrypted) {
|
||||
CredentialFlags flags = 0; /* only allow user creds in user scope */
|
||||
|
||||
switch (args->context->scope) {
|
||||
switch (args->params->runtime_scope) {
|
||||
|
||||
case RUNTIME_SCOPE_SYSTEM:
|
||||
/* In system mode talk directly to the TPM – unless we live in a device sandbox
|
||||
@ -478,7 +457,7 @@ static int maybe_decrypt_and_write_credential(
|
||||
|
||||
flags |= CREDENTIAL_ANY_SCOPE;
|
||||
|
||||
if (!args->context->always_ipc) {
|
||||
if (!args->always_ipc) {
|
||||
r = decrypt_credential_and_warn(
|
||||
id,
|
||||
now(CLOCK_REALTIME),
|
||||
@ -527,7 +506,7 @@ static int maybe_decrypt_and_write_credential(
|
||||
if (add > args->left)
|
||||
return -E2BIG;
|
||||
|
||||
r = write_credential(args->write_dfd, id, data, size, args->context->uid, args->context->gid, args->ownership_ok);
|
||||
r = write_credential(args->write_dfd, id, data, size, args->uid, args->gid, args->ownership_ok);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to write credential '%s': %m", id);
|
||||
|
||||
@ -537,7 +516,7 @@ static int maybe_decrypt_and_write_credential(
|
||||
}
|
||||
|
||||
static int load_credential_glob(
|
||||
LoadCredentialArguments *args,
|
||||
struct load_cred_args *args,
|
||||
const ExecImportCredential *ic,
|
||||
char * const *search_path,
|
||||
ReadFullFileFlags flags) {
|
||||
@ -616,7 +595,7 @@ static int load_credential_glob(
|
||||
}
|
||||
|
||||
static int load_credential(
|
||||
LoadCredentialArguments *args,
|
||||
struct load_cred_args *args,
|
||||
const char *id,
|
||||
int read_dfd,
|
||||
const char *path) {
|
||||
@ -632,8 +611,9 @@ static int load_credential(
|
||||
|
||||
assert(args);
|
||||
assert(args->context);
|
||||
assert(args->context->exec_context);
|
||||
assert(args->context->unit);
|
||||
assert(args->params);
|
||||
assert(args->unit);
|
||||
assert(args->write_dfd >= 0);
|
||||
assert(id);
|
||||
assert(read_dfd >= 0 || read_dfd == AT_FDCWD);
|
||||
assert(path);
|
||||
@ -661,7 +641,7 @@ static int load_credential(
|
||||
|
||||
/* Pass some minimal info about the unit and the credential name we are looking to acquire
|
||||
* via the source socket address in case we read off an AF_UNIX socket. */
|
||||
if (asprintf(&bindname, "@%" PRIx64 "/unit/%s/%s", random_u64(), args->context->unit, id) < 0)
|
||||
if (asprintf(&bindname, "@%" PRIx64 "/unit/%s/%s", random_u64(), args->unit, id) < 0)
|
||||
return -ENOMEM;
|
||||
|
||||
missing_ok = false;
|
||||
@ -672,7 +652,7 @@ static int load_credential(
|
||||
* directory we received ourselves. We don't support the AF_UNIX stuff in this mode, since we
|
||||
* are operating on a credential store, i.e. this is guaranteed to be regular files. */
|
||||
|
||||
r = credential_search_path(args->context, CREDENTIAL_SEARCH_PATH_ALL, &search_path);
|
||||
r = credential_search_path(args->params, CREDENTIAL_SEARCH_PATH_ALL, &search_path);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@ -715,9 +695,7 @@ static int load_credential(
|
||||
else
|
||||
assert_not_reached();
|
||||
|
||||
if (r == -ENOENT) {
|
||||
bool in_set_credentials = hashmap_contains(args->context->exec_context->set_credentials, id);
|
||||
if (missing_ok || in_set_credentials) {
|
||||
if (r == -ENOENT && (missing_ok || hashmap_contains(args->context->set_credentials, id))) {
|
||||
/* Make a missing inherited credential non-fatal, let's just continue. After all apps
|
||||
* will get clear errors if we don't pass such a missing credential on as they
|
||||
* themselves will get ENOENT when trying to read them, which should not be much
|
||||
@ -725,11 +703,10 @@ static int load_credential(
|
||||
*
|
||||
* Also, if the source file doesn't exist, but a fallback is set via SetCredentials=
|
||||
* we are fine, too. */
|
||||
log_full_errno(in_set_credentials ? LOG_DEBUG : LOG_INFO,
|
||||
log_full_errno(hashmap_contains(args->context->set_credentials, id) ? LOG_DEBUG : LOG_INFO,
|
||||
r, "Couldn't read inherited credential '%s', skipping: %m", path);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to read credential '%s': %m", path);
|
||||
|
||||
@ -745,7 +722,7 @@ static int load_cred_recurse_dir_cb(
|
||||
const struct statx *sx,
|
||||
void *userdata) {
|
||||
|
||||
LoadCredentialArguments *args = ASSERT_PTR(userdata);
|
||||
struct load_cred_args *args = ASSERT_PTR(userdata);
|
||||
_cleanup_free_ char *sub_id = NULL;
|
||||
int r;
|
||||
|
||||
@ -802,27 +779,38 @@ static bool device_nodes_restricted(
|
||||
}
|
||||
|
||||
static int acquire_credentials(
|
||||
const SetupCredentialsContext *context,
|
||||
const ExecContext *context,
|
||||
const CGroupContext *cgroup_context,
|
||||
const ExecParameters *params,
|
||||
const char *unit,
|
||||
int dfd,
|
||||
uid_t uid,
|
||||
gid_t gid,
|
||||
bool ownership_ok) {
|
||||
|
||||
int r;
|
||||
|
||||
assert(context);
|
||||
assert(context->exec_context);
|
||||
assert(cgroup_context);
|
||||
assert(params);
|
||||
assert(unit);
|
||||
assert(dfd >= 0);
|
||||
|
||||
LoadCredentialArguments args = {
|
||||
struct load_cred_args args = {
|
||||
.context = context,
|
||||
|
||||
.params = params,
|
||||
.unit = unit,
|
||||
.always_ipc = device_nodes_restricted(context, cgroup_context),
|
||||
.write_dfd = dfd,
|
||||
.uid = uid,
|
||||
.gid = gid,
|
||||
.ownership_ok = ownership_ok,
|
||||
.left = CREDENTIALS_TOTAL_SIZE_MAX,
|
||||
};
|
||||
|
||||
/* First, load credentials off disk (or acquire via AF_UNIX socket) */
|
||||
ExecLoadCredential *lc;
|
||||
HASHMAP_FOREACH(lc, context->exec_context->load_credentials) {
|
||||
HASHMAP_FOREACH(lc, context->load_credentials) {
|
||||
_cleanup_close_ int sub_fd = -EBADF;
|
||||
|
||||
args.encrypted = lc->encrypted;
|
||||
@ -861,10 +849,10 @@ static int acquire_credentials(
|
||||
/* Next, look for system credentials and credentials in the credentials store. Note that these do not
|
||||
* override any credentials found earlier. */
|
||||
ExecImportCredential *ic;
|
||||
ORDERED_SET_FOREACH(ic, context->exec_context->import_credentials) {
|
||||
ORDERED_SET_FOREACH(ic, context->import_credentials) {
|
||||
_cleanup_free_ char **search_path = NULL;
|
||||
|
||||
r = credential_search_path(context, CREDENTIAL_SEARCH_PATH_TRUSTED, &search_path);
|
||||
r = credential_search_path(params, CREDENTIAL_SEARCH_PATH_TRUSTED, &search_path);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@ -880,7 +868,7 @@ static int acquire_credentials(
|
||||
|
||||
search_path = strv_free(search_path);
|
||||
|
||||
r = credential_search_path(context, CREDENTIAL_SEARCH_PATH_ENCRYPTED, &search_path);
|
||||
r = credential_search_path(params, CREDENTIAL_SEARCH_PATH_ENCRYPTED, &search_path);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@ -898,7 +886,7 @@ static int acquire_credentials(
|
||||
/* Finally, we add in literally specified credentials. If the credentials already exist, we'll not
|
||||
* add them, so that they can act as a "default" if the same credential is specified multiple times. */
|
||||
ExecSetCredential *sc;
|
||||
HASHMAP_FOREACH(sc, context->exec_context->set_credentials) {
|
||||
HASHMAP_FOREACH(sc, context->set_credentials) {
|
||||
args.encrypted = sc->encrypted;
|
||||
|
||||
if (faccessat(dfd, sc->id, F_OK, AT_SYMLINK_NOFOLLOW) >= 0) {
|
||||
@ -946,8 +934,13 @@ static int credentials_dir_finalize_permissions(int dfd, uid_t uid, gid_t gid, b
|
||||
}
|
||||
|
||||
static int setup_credentials_plain_dir(
|
||||
const SetupCredentialsContext *context,
|
||||
const char *cred_dir) {
|
||||
const ExecContext *context,
|
||||
const CGroupContext *cgroup_context,
|
||||
const ExecParameters *params,
|
||||
const char *unit,
|
||||
const char *cred_dir,
|
||||
uid_t uid,
|
||||
gid_t gid) {
|
||||
|
||||
_cleanup_free_ char *t = NULL, *workspace = NULL;
|
||||
_cleanup_(rm_rf_safep) const char *workspace_rm = NULL;
|
||||
@ -955,13 +948,13 @@ static int setup_credentials_plain_dir(
|
||||
int r;
|
||||
|
||||
assert(context);
|
||||
assert(context->unit);
|
||||
assert(context->runtime_prefix);
|
||||
assert(params);
|
||||
assert(unit);
|
||||
assert(cred_dir);
|
||||
|
||||
/* Temporary workspace, that remains inaccessible all the time. We prepare stuff there before moving
|
||||
* it into place, so that users can't access half-initialized credential stores. */
|
||||
t = path_join(context->runtime_prefix, "systemd/temporary-credentials");
|
||||
t = path_join(params->prefix[EXEC_DIRECTORY_RUNTIME], "systemd/temporary-credentials");
|
||||
if (!t)
|
||||
return -ENOMEM;
|
||||
|
||||
@ -969,7 +962,7 @@ static int setup_credentials_plain_dir(
|
||||
if (r < 0 && r != -EEXIST)
|
||||
return r;
|
||||
|
||||
workspace = path_join(t, context->unit);
|
||||
workspace = path_join(t, unit);
|
||||
if (!workspace)
|
||||
return -ENOMEM;
|
||||
|
||||
@ -980,7 +973,7 @@ static int setup_credentials_plain_dir(
|
||||
|
||||
(void) label_fix_full(dfd, /* inode_path= */ NULL, cred_dir, /* flags= */ 0);
|
||||
|
||||
r = acquire_credentials(context, dfd, /* ownership_ok= */ false);
|
||||
r = acquire_credentials(context, cgroup_context, params, unit, dfd, uid, gid, /* ownership_ok= */ false);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@ -1002,7 +995,7 @@ static int setup_credentials_plain_dir(
|
||||
|
||||
/* rename() requires both the source and target to be writable, hence lock down write permission
|
||||
* as last step. */
|
||||
r = credentials_dir_finalize_permissions(dfd, context->uid, context->gid, /* ownership_ok= */ false);
|
||||
r = credentials_dir_finalize_permissions(dfd, uid, gid, /* ownership_ok= */ false);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to adjust ACLs of credentials dir: %m");
|
||||
|
||||
@ -1010,47 +1003,43 @@ static int setup_credentials_plain_dir(
|
||||
}
|
||||
|
||||
static int setup_credentials_internal(
|
||||
const SetupCredentialsContext *context,
|
||||
bool may_reuse,
|
||||
const char *cred_dir) {
|
||||
const ExecContext *context,
|
||||
const CGroupContext *cgroup_context,
|
||||
const ExecParameters *params,
|
||||
const char *unit,
|
||||
const char *cred_dir,
|
||||
uid_t uid,
|
||||
gid_t gid) {
|
||||
|
||||
_cleanup_close_ int fs_fd = -EBADF, mfd = -EBADF, dfd = -EBADF;
|
||||
bool dir_mounted;
|
||||
int r;
|
||||
|
||||
assert(context);
|
||||
assert(params);
|
||||
assert(unit);
|
||||
assert(cred_dir);
|
||||
|
||||
if (!FLAGS_SET(params->flags, EXEC_SETUP_CREDENTIALS_FRESH)) {
|
||||
/* We may reuse the previous credential dir */
|
||||
r = dir_is_empty(cred_dir, /* ignore_hidden_or_backup= */ false);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0) {
|
||||
log_debug("Credential dir for unit '%s' already set up, skipping.", unit);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
r = path_is_mount_point(cred_dir);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to determine if '%s' is a mountpoint: %m", cred_dir);
|
||||
dir_mounted = r > 0;
|
||||
|
||||
if (may_reuse) {
|
||||
bool populated;
|
||||
|
||||
/* If the cred dir is a mount, let's treat it as populated, and only look at the contents
|
||||
* if it's a plain dir, where we can't reasonably differentiate populated yet empty vs
|
||||
* not set up. */
|
||||
|
||||
if (dir_mounted)
|
||||
populated = true;
|
||||
else {
|
||||
r = dir_is_empty(cred_dir, /* ignore_hidden_or_backup= */ false);
|
||||
if (r < 0)
|
||||
return r;
|
||||
populated = r == 0;
|
||||
}
|
||||
if (populated) {
|
||||
log_debug("Credential dir for unit '%s' already set up, skipping.", context->unit);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
mfd = fsmount_credentials_fs(&fs_fd);
|
||||
if (ERRNO_IS_NEG_PRIVILEGE(mfd) && !dir_mounted) {
|
||||
log_debug_errno(mfd, "Lacking privilege to mount credentials fs, falling back to plain directory.");
|
||||
return setup_credentials_plain_dir(context, cred_dir);
|
||||
return setup_credentials_plain_dir(context, cgroup_context, params, unit, cred_dir, uid, gid);
|
||||
}
|
||||
if (mfd < 0)
|
||||
return log_debug_errno(mfd, "Failed to mount credentials fs: %m");
|
||||
@ -1061,11 +1050,11 @@ static int setup_credentials_internal(
|
||||
|
||||
(void) label_fix_full(dfd, /* inode_path= */ NULL, cred_dir, /* flags= */ 0);
|
||||
|
||||
r = acquire_credentials(context, dfd, /* ownership_ok= */ true);
|
||||
r = acquire_credentials(context, cgroup_context, params, unit, dfd, uid, gid, /* ownership_ok= */ true);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = credentials_dir_finalize_permissions(dfd, context->uid, context->gid, /* ownership_ok= */ true);
|
||||
r = credentials_dir_finalize_permissions(dfd, uid, gid, /* ownership_ok= */ true);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to adjust ACLs of credentials dir: %m");
|
||||
|
||||
@ -1110,6 +1099,7 @@ int exec_setup_credentials(
|
||||
const ExecContext *context,
|
||||
const CGroupContext *cgroup_context,
|
||||
const ExecParameters *params,
|
||||
const char *unit,
|
||||
uid_t uid,
|
||||
gid_t gid) {
|
||||
|
||||
@ -1118,6 +1108,7 @@ int exec_setup_credentials(
|
||||
|
||||
assert(context);
|
||||
assert(params);
|
||||
assert(unit);
|
||||
|
||||
if (!exec_params_need_credentials(params) || !exec_context_has_credentials(context))
|
||||
return 0;
|
||||
@ -1135,7 +1126,7 @@ int exec_setup_credentials(
|
||||
if (r < 0 && r != -EEXIST)
|
||||
return r;
|
||||
|
||||
p = path_join(q, params->unit_id);
|
||||
p = path_join(q, unit);
|
||||
if (!p)
|
||||
return -ENOMEM;
|
||||
|
||||
@ -1143,208 +1134,12 @@ int exec_setup_credentials(
|
||||
if (r < 0 && r != -EEXIST)
|
||||
return r;
|
||||
|
||||
SetupCredentialsContext ctx = {
|
||||
.scope = params->runtime_scope,
|
||||
.exec_context = context,
|
||||
.unit = params->unit_id,
|
||||
r = setup_credentials_internal(context, cgroup_context, params, unit, p, uid, gid);
|
||||
|
||||
.runtime_prefix = params->prefix[EXEC_DIRECTORY_RUNTIME],
|
||||
.received_credentials_directory = params->received_credentials_directory,
|
||||
.received_encrypted_credentials_directory = params->received_encrypted_credentials_directory,
|
||||
|
||||
.always_ipc = device_nodes_restricted(context, cgroup_context),
|
||||
|
||||
.uid = uid,
|
||||
.gid = gid,
|
||||
};
|
||||
|
||||
r = setup_credentials_internal(&ctx, /* may_reuse = */ !FLAGS_SET(params->flags, EXEC_SETUP_CREDENTIALS_FRESH), p);
|
||||
if (r < 0)
|
||||
/* If the credentials dir is empty and not a mount point, then there's no point in having it. Let's
|
||||
* try to remove it. This matters in particular if we created the dir as mount point but then didn't
|
||||
* actually end up mounting anything on it. In that case we'd rather have ENOENT than EACCESS being
|
||||
* seen by users when trying access this inode. */
|
||||
(void) rmdir(p);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int refresh_credentials_in_namespace_child(int cfd, const char *cred_dir) {
|
||||
int r;
|
||||
|
||||
assert(cfd >= 0);
|
||||
assert(cred_dir);
|
||||
|
||||
/* Paranoia: before doing anything, check if the credentials tree inside the mountns is available.
|
||||
*
|
||||
* Note that setup_namespace() always installs a mount for cred dir, hence path_is_mount_point()
|
||||
* is the appropriate check here. */
|
||||
r = path_is_mount_point(cred_dir);
|
||||
if (IN_SET(r, 0, -ENOENT)) {
|
||||
log_full_errno_zerook(LOG_WARNING, r,
|
||||
"Credentials tree in the unit mount namespace is masked, skipping refresh.");
|
||||
return 0;
|
||||
}
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to check whether '%s' is a mountpoint in unit mount namespace: %m",
|
||||
cred_dir);
|
||||
|
||||
/* Inform the parent that we're good to go */
|
||||
ssize_t n = write(cfd, &r, sizeof(r));
|
||||
if (n < 0)
|
||||
return log_error_errno(errno, "Failed to write to socket: %m");
|
||||
|
||||
_cleanup_close_ int mfd = receive_one_fd(cfd, /* flags = */ 0);
|
||||
if (mfd < 0)
|
||||
return log_error_errno(mfd, "Failed to receive credentials tree fd from socket: %m");
|
||||
|
||||
r = mount_exchange_graceful(mfd, cred_dir, /* mount_beneath = */ true);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to update credentials mount in namespace: %m");
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int unit_refresh_credentials(Unit *u) {
|
||||
_cleanup_free_ char *cred_dir = NULL;
|
||||
int r;
|
||||
|
||||
/* Refresh the credentials for a unit, potentially forking off a second process to join the mountns
|
||||
* if needed. Returns > 0 on successful refresh, == 0 if the credentials tree is masked and the operation
|
||||
* is skipped. */
|
||||
|
||||
assert(u);
|
||||
assert(u->manager);
|
||||
|
||||
r = get_credential_directory(u->manager->prefix[EXEC_DIRECTORY_RUNTIME], u->id, &cred_dir);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
assert(r > 0);
|
||||
|
||||
if (access(cred_dir, F_OK) < 0) {
|
||||
if (errno == ENOENT) {
|
||||
log_warning_errno(errno, "Requested to refresh credentials, but credentials aren't populated, skipping.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return log_error_errno(errno, "Failed to check if credentials dir '%s' exists: %m", cred_dir);
|
||||
}
|
||||
|
||||
_cleanup_close_pair_ int tunnel_fds[2] = EBADF_PAIR;
|
||||
_cleanup_(pidref_done) PidRef child = PIDREF_NULL;
|
||||
_cleanup_close_ int userns_fd = -EBADF;
|
||||
|
||||
PidRef *main_pid = unit_main_pid(u);
|
||||
if (pidref_is_set(main_pid)) {
|
||||
_cleanup_close_ int mntns_fd = -EBADF, root_fd = -EBADF, pidns_fd = -EBADF;
|
||||
|
||||
r = pidref_namespace_open(main_pid,
|
||||
&pidns_fd,
|
||||
&mntns_fd,
|
||||
/* ret_netns_fd = */ NULL,
|
||||
MANAGER_IS_USER(u->manager) ? &userns_fd : NULL,
|
||||
&root_fd);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to open namespace of unit main process '" PID_FMT "': %m",
|
||||
main_pid->pid);
|
||||
|
||||
r = is_our_namespace(mntns_fd, NAMESPACE_MOUNT);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to check if main process resides in a separate mount namespace: %m");
|
||||
if (r == 0) {
|
||||
if (socketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0, tunnel_fds) < 0)
|
||||
return log_error_errno(errno, "Failed to allocate socket pair: %m");
|
||||
|
||||
r = namespace_fork_full("(sd-creds-ns)", "(sd-creds-ns-inner)",
|
||||
(int[]) { tunnel_fds[1] }, 1,
|
||||
FORK_RESET_SIGNALS|FORK_DEATHSIG_SIGKILL|FORK_CLOSE_ALL_FDS|FORK_REOPEN_LOG,
|
||||
pidns_fd, mntns_fd, /* netns_fd = */ -EBADF, userns_fd, root_fd,
|
||||
/* delegated = */ MANAGER_IS_USER(u->manager),
|
||||
&child);
|
||||
if (r < 0)
|
||||
return log_full_errno(ERRNO_IS_NEG_PRIVILEGE(r) ? LOG_WARNING : LOG_ERR, r,
|
||||
"Failed to fork off process into unit namespace to refresh credentials: %m");
|
||||
if (r == 0) {
|
||||
r = refresh_credentials_in_namespace_child(tunnel_fds[1], cred_dir);
|
||||
report_errno_and_exit(tunnel_fds[1], r);
|
||||
}
|
||||
|
||||
tunnel_fds[1] = safe_close(tunnel_fds[1]);
|
||||
|
||||
/* Wait for the child to validate the creds tree in the unit namespace is populated. */
|
||||
ssize_t n = read(tunnel_fds[0], &r, sizeof(r));
|
||||
if (n < 0)
|
||||
return log_error_errno(errno, "Failed to read from socket: %m");
|
||||
if (!IN_SET(n, 0, sizeof(r)))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EIO), "Received unexpected amount of bytes (%zi) while reading errno.", n);
|
||||
if (n == 0 || r == 0) {
|
||||
/* The child exited without sending anything or 0 is received signifying
|
||||
* the credentials are masked? Check the exit status to be sure. */
|
||||
r = pidref_wait_for_terminate_and_check("(sd-creds-ns)", &child, WAIT_LOG);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r != EXIT_SUCCESS)
|
||||
return -EPROTO;
|
||||
|
||||
return 0; /* skipped */
|
||||
}
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* Yay! Got > 0 from child indicating all good, proceed with refreshing. */
|
||||
}
|
||||
}
|
||||
|
||||
SetupCredentialsContext ctx = {
|
||||
.scope = u->manager->runtime_scope,
|
||||
.exec_context = ASSERT_PTR(unit_get_exec_context(u)),
|
||||
.unit = u->id,
|
||||
|
||||
.runtime_prefix = u->manager->prefix[EXEC_DIRECTORY_RUNTIME],
|
||||
.received_credentials_directory = u->manager->received_credentials_directory,
|
||||
.received_encrypted_credentials_directory = u->manager->received_encrypted_credentials_directory,
|
||||
|
||||
.always_ipc = false, /* we don't migrate to unit cgroup, hence cannot be restricted by cgroup bpf */
|
||||
|
||||
.uid = u->ref_uid,
|
||||
.gid = u->ref_gid,
|
||||
};
|
||||
|
||||
r = setup_credentials_internal(&ctx, /* may_reuse = */ false, cred_dir);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to set up credentials: %m");
|
||||
|
||||
/* The main process doesn't run in a mountns hence nothing got forked off? Then we're all set. */
|
||||
if (!pidref_is_set(&child))
|
||||
return 1;
|
||||
|
||||
if (userns_fd >= 0) {
|
||||
assert(MANAGER_IS_USER(u->manager));
|
||||
|
||||
/* Enter the unit userns now and unshare mountns, so that we have permissions to clone
|
||||
* the mount tree using open_tree() */
|
||||
|
||||
if (setns(userns_fd, CLONE_NEWUSER) < 0)
|
||||
return log_error_errno(errno, "Failed to enter user namespace: %m");
|
||||
|
||||
if (unshare(CLONE_NEWNS) < 0)
|
||||
return log_error_errno(errno, "Failed to unshare mount namespace: %m");
|
||||
}
|
||||
|
||||
_cleanup_close_ int tfd = open_tree(AT_FDCWD, cred_dir, OPEN_TREE_CLONE|OPEN_TREE_CLOEXEC|AT_SYMLINK_NOFOLLOW);
|
||||
if (tfd < 0)
|
||||
return log_error_errno(errno, "Failed to clone mount tree at '%s': %m", cred_dir);
|
||||
|
||||
r = send_one_fd(tunnel_fds[0], tfd, /* flags = */ 0);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to send mount fd to child: %m");
|
||||
|
||||
r = pidref_wait_for_terminate_and_check("(sd-creds-ns)", &child, WAIT_LOG_ABNORMAL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r != EXIT_SUCCESS) {
|
||||
r = read_errno(tunnel_fds[0]);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return -EPROTO;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -57,9 +57,8 @@ int exec_setup_credentials(
|
||||
const ExecContext *context,
|
||||
const CGroupContext *cgroup_context,
|
||||
const ExecParameters *params,
|
||||
const char *unit,
|
||||
uid_t uid,
|
||||
gid_t gid);
|
||||
|
||||
int unit_refresh_credentials(Unit *u);
|
||||
|
||||
bool mount_point_is_credentials(const char *runtime_prefix, const char *path);
|
||||
|
||||
@ -5705,7 +5705,7 @@ int exec_invoke(
|
||||
return log_error_errno(r, "Failed to set up special execution directory in %s: %m", params->prefix[dt]);
|
||||
}
|
||||
|
||||
r = exec_setup_credentials(context, cgroup_context, params, uid, gid);
|
||||
r = exec_setup_credentials(context, cgroup_context, params, params->unit_id, uid, gid);
|
||||
if (r < 0) {
|
||||
*exit_status = EXIT_CREDENTIALS;
|
||||
return log_error_errno(r, "Failed to set up credentials: %m");
|
||||
|
||||
@ -479,7 +479,6 @@ Service.USBFunctionStrings, config_parse_unit_path_printf,
|
||||
Service.OOMPolicy, config_parse_oom_policy, 0, offsetof(Service, oom_policy)
|
||||
Service.OpenFile, config_parse_open_file, 0, offsetof(Service, open_files)
|
||||
Service.ReloadSignal, config_parse_signal, 0, offsetof(Service, reload_signal)
|
||||
Service.RefreshOnReload, config_parse_service_refresh_on_reload, 0, 0
|
||||
{{ EXEC_CONTEXT_CONFIG_ITEMS('Service') }}
|
||||
{{ CGROUP_CONTEXT_CONFIG_ITEMS('Service') }}
|
||||
{{ KILL_CONTEXT_CONFIG_ITEMS('Service') }}
|
||||
|
||||
@ -4816,55 +4816,6 @@ int config_parse_import_credential(
|
||||
return 0;
|
||||
}
|
||||
|
||||
int config_parse_service_refresh_on_reload(
|
||||
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) {
|
||||
|
||||
Service *s = ASSERT_PTR(userdata);
|
||||
int r;
|
||||
|
||||
if (isempty(rvalue)) {
|
||||
s->refresh_on_reload_set = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = parse_boolean(rvalue);
|
||||
if (r >= 0) {
|
||||
s->refresh_on_reload_flags = r > 0 ? _SERVICE_REFRESH_ON_RELOAD_ALL : 0;
|
||||
s->refresh_on_reload_set = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
ServiceRefreshOnReload f;
|
||||
bool invert = false;
|
||||
|
||||
if (rvalue[0] == '~') {
|
||||
invert = true;
|
||||
rvalue++;
|
||||
}
|
||||
|
||||
r = service_refresh_on_reload_from_string_many(rvalue, &f);
|
||||
if (r < 0)
|
||||
return log_syntax_parse_error(unit, filename, line, r, lvalue, rvalue);
|
||||
|
||||
/* If the first entry is negated, mask off from default; otherwise assign "positive" values directly */
|
||||
if (!s->refresh_on_reload_set)
|
||||
s->refresh_on_reload_flags = invert ? (SERVICE_REFRESH_ON_RELOAD_DEFAULT & ~f) : f;
|
||||
else
|
||||
SET_FLAG(s->refresh_on_reload_flags, f, !invert);
|
||||
|
||||
s->refresh_on_reload_set = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int config_parse_set_status(
|
||||
const char *unit,
|
||||
const char *filename,
|
||||
|
||||
@ -105,7 +105,6 @@ CONFIG_PARSER_PROTOTYPE(config_parse_exec_quota);
|
||||
CONFIG_PARSER_PROTOTYPE(config_parse_set_credential);
|
||||
CONFIG_PARSER_PROTOTYPE(config_parse_load_credential);
|
||||
CONFIG_PARSER_PROTOTYPE(config_parse_import_credential);
|
||||
CONFIG_PARSER_PROTOTYPE(config_parse_service_refresh_on_reload);
|
||||
CONFIG_PARSER_PROTOTYPE(config_parse_set_status);
|
||||
CONFIG_PARSER_PROTOTYPE(config_parse_namespace_path_strv);
|
||||
CONFIG_PARSER_PROTOTYPE(config_parse_temporary_filesystems);
|
||||
|
||||
@ -1888,15 +1888,13 @@ static bool manager_dbus_is_running(Manager *m, bool deserialized) {
|
||||
u = manager_get_unit(m, SPECIAL_DBUS_SERVICE);
|
||||
if (!u)
|
||||
return false;
|
||||
if (!IN_SET(deserialized ? SERVICE(u)->deserialized_state : SERVICE(u)->state,
|
||||
if (!IN_SET((deserialized ? SERVICE(u)->deserialized_state : SERVICE(u)->state),
|
||||
SERVICE_RUNNING,
|
||||
SERVICE_REFRESH_EXTENSIONS,
|
||||
SERVICE_REFRESH_CREDENTIALS,
|
||||
SERVICE_MOUNTING,
|
||||
SERVICE_RELOAD,
|
||||
SERVICE_RELOAD_SIGNAL,
|
||||
SERVICE_RELOAD_NOTIFY,
|
||||
SERVICE_RELOAD_POST,
|
||||
SERVICE_MOUNTING))
|
||||
SERVICE_REFRESH_EXTENSIONS,
|
||||
SERVICE_RELOAD_SIGNAL))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
|
||||
@ -2845,6 +2845,7 @@ int setup_namespace(const NamespaceParameters *p, char **reterr_path) {
|
||||
.mode = MOUNT_BIND,
|
||||
.read_only = true,
|
||||
.source_const = p->creds_path,
|
||||
.ignore = true,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -69,7 +69,6 @@ static const UnitActiveState state_translation_table[_SERVICE_STATE_MAX] = {
|
||||
[SERVICE_RUNNING] = UNIT_ACTIVE,
|
||||
[SERVICE_EXITED] = UNIT_ACTIVE,
|
||||
[SERVICE_REFRESH_EXTENSIONS] = UNIT_REFRESHING,
|
||||
[SERVICE_REFRESH_CREDENTIALS] = UNIT_REFRESHING,
|
||||
[SERVICE_RELOAD] = UNIT_RELOADING,
|
||||
[SERVICE_RELOAD_SIGNAL] = UNIT_RELOADING,
|
||||
[SERVICE_RELOAD_NOTIFY] = UNIT_RELOADING,
|
||||
@ -103,7 +102,6 @@ static const UnitActiveState state_translation_table_idle[_SERVICE_STATE_MAX] =
|
||||
[SERVICE_RUNNING] = UNIT_ACTIVE,
|
||||
[SERVICE_EXITED] = UNIT_ACTIVE,
|
||||
[SERVICE_REFRESH_EXTENSIONS] = UNIT_REFRESHING,
|
||||
[SERVICE_REFRESH_CREDENTIALS] = UNIT_REFRESHING,
|
||||
[SERVICE_RELOAD] = UNIT_RELOADING,
|
||||
[SERVICE_RELOAD_SIGNAL] = UNIT_RELOADING,
|
||||
[SERVICE_RELOAD_NOTIFY] = UNIT_RELOADING,
|
||||
@ -136,14 +134,11 @@ static void service_enter_signal(Service *s, ServiceState state, ServiceResult f
|
||||
static void service_reload_finish(Service *s, ServiceResult f);
|
||||
static void service_enter_reload_by_notify(Service *s);
|
||||
|
||||
static bool service_can_reload_extensions(Service *s, bool warn);
|
||||
|
||||
static bool SERVICE_STATE_WITH_MAIN_PROCESS(ServiceState state) {
|
||||
return IN_SET(state,
|
||||
SERVICE_START, SERVICE_START_POST,
|
||||
SERVICE_RUNNING,
|
||||
SERVICE_REFRESH_EXTENSIONS, SERVICE_REFRESH_CREDENTIALS,
|
||||
SERVICE_RELOAD, SERVICE_RELOAD_SIGNAL, SERVICE_RELOAD_NOTIFY, SERVICE_RELOAD_POST,
|
||||
SERVICE_REFRESH_EXTENSIONS, SERVICE_RELOAD, SERVICE_RELOAD_SIGNAL, SERVICE_RELOAD_NOTIFY, SERVICE_RELOAD_POST,
|
||||
SERVICE_MOUNTING,
|
||||
SERVICE_STOP, SERVICE_STOP_WATCHDOG, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST,
|
||||
SERVICE_FINAL_WATCHDOG, SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL);
|
||||
@ -153,7 +148,7 @@ static bool SERVICE_STATE_WITH_CONTROL_PROCESS(ServiceState state) {
|
||||
return IN_SET(state,
|
||||
SERVICE_CONDITION,
|
||||
SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST,
|
||||
SERVICE_REFRESH_EXTENSIONS, SERVICE_REFRESH_CREDENTIALS, SERVICE_RELOAD, SERVICE_RELOAD_POST,
|
||||
SERVICE_REFRESH_EXTENSIONS, SERVICE_RELOAD, SERVICE_RELOAD_POST,
|
||||
SERVICE_MOUNTING,
|
||||
SERVICE_STOP, SERVICE_STOP_WATCHDOG, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST,
|
||||
SERVICE_FINAL_WATCHDOG, SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL,
|
||||
@ -164,8 +159,7 @@ static bool SERVICE_STATE_WITH_WATCHDOG(ServiceState state) {
|
||||
return IN_SET(state,
|
||||
SERVICE_START_POST,
|
||||
SERVICE_RUNNING,
|
||||
SERVICE_REFRESH_EXTENSIONS, SERVICE_REFRESH_CREDENTIALS,
|
||||
SERVICE_RELOAD, SERVICE_RELOAD_SIGNAL, SERVICE_RELOAD_NOTIFY, SERVICE_RELOAD_POST,
|
||||
SERVICE_REFRESH_EXTENSIONS, SERVICE_RELOAD, SERVICE_RELOAD_SIGNAL, SERVICE_RELOAD_NOTIFY, SERVICE_RELOAD_POST,
|
||||
SERVICE_MOUNTING);
|
||||
}
|
||||
|
||||
@ -760,15 +754,6 @@ static int service_verify(Service *s) {
|
||||
s->restart_usec = s->restart_max_delay_usec;
|
||||
}
|
||||
|
||||
if (s->refresh_on_reload_set && s->refresh_on_reload_flags != _SERVICE_REFRESH_ON_RELOAD_ALL) {
|
||||
if (FLAGS_SET(s->refresh_on_reload_flags, SERVICE_RELOAD_EXTENSIONS))
|
||||
service_can_reload_extensions(s, /* warn = */ true);
|
||||
|
||||
if (FLAGS_SET(s->refresh_on_reload_flags, SERVICE_RELOAD_CREDENTIALS) &&
|
||||
!exec_context_has_credentials(&s->exec_context))
|
||||
log_unit_warning(UNIT(s), "Service has RefreshOnReload=credentials, but no credentials are in use. The credentials tree will be masked which blocks further refreshing. Continuing.");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -906,11 +891,6 @@ static int service_add_extras(Service *s) {
|
||||
(IN_SET(s->type, SERVICE_NOTIFY, SERVICE_NOTIFY_RELOAD) || s->watchdog_usec > 0 || s->n_fd_store_max > 0))
|
||||
s->notify_access = NOTIFY_MAIN;
|
||||
|
||||
if (!s->refresh_on_reload_set) {
|
||||
assert_cc(SERVICE_REFRESH_ON_RELOAD_DEFAULT == SERVICE_RELOAD_EXTENSIONS);
|
||||
s->refresh_on_reload_flags = service_can_reload_extensions(s, /* warn = */ false) ? SERVICE_RELOAD_EXTENSIONS : 0;
|
||||
}
|
||||
|
||||
/* If no OOM policy was explicitly set, then default to the configure default OOM policy. Except when
|
||||
* delegation is on, in that case it we assume the payload knows better what to do and can process
|
||||
* things in a more focused way. */
|
||||
@ -1316,8 +1296,7 @@ static void service_set_state(Service *s, ServiceState state) {
|
||||
if (!IN_SET(state,
|
||||
SERVICE_CONDITION, SERVICE_START_PRE, SERVICE_START, SERVICE_START_POST,
|
||||
SERVICE_RUNNING,
|
||||
SERVICE_REFRESH_EXTENSIONS, SERVICE_REFRESH_CREDENTIALS,
|
||||
SERVICE_RELOAD, SERVICE_RELOAD_SIGNAL, SERVICE_RELOAD_NOTIFY, SERVICE_RELOAD_POST,
|
||||
SERVICE_REFRESH_EXTENSIONS, SERVICE_RELOAD, SERVICE_RELOAD_SIGNAL, SERVICE_RELOAD_NOTIFY, SERVICE_RELOAD_POST,
|
||||
SERVICE_MOUNTING,
|
||||
SERVICE_STOP, SERVICE_STOP_WATCHDOG, SERVICE_STOP_SIGTERM, SERVICE_STOP_SIGKILL, SERVICE_STOP_POST,
|
||||
SERVICE_FINAL_WATCHDOG, SERVICE_FINAL_SIGTERM, SERVICE_FINAL_SIGKILL,
|
||||
@ -1388,7 +1367,6 @@ static usec_t service_coldplug_timeout(Service *s) {
|
||||
case SERVICE_START:
|
||||
case SERVICE_START_POST:
|
||||
case SERVICE_REFRESH_EXTENSIONS:
|
||||
case SERVICE_REFRESH_CREDENTIALS:
|
||||
case SERVICE_RELOAD:
|
||||
case SERVICE_RELOAD_SIGNAL:
|
||||
case SERVICE_RELOAD_NOTIFY:
|
||||
@ -1689,16 +1667,11 @@ static Service *service_get_triggering_service(Service *s) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static ExecFlags service_exec_flags(
|
||||
const Service *s,
|
||||
ServiceExecCommand command_id,
|
||||
ExecFlags cred_flag) {
|
||||
|
||||
static ExecFlags service_exec_flags(ServiceExecCommand command_id, ExecFlags cred_flag) {
|
||||
/* All service main/control processes honor sandboxing and namespacing options (except those
|
||||
explicitly excluded in service_spawn()) */
|
||||
ExecFlags flags = EXEC_APPLY_SANDBOXING|EXEC_APPLY_CHROOT;
|
||||
|
||||
assert(s);
|
||||
assert(command_id >= 0);
|
||||
assert(command_id < _SERVICE_EXEC_COMMAND_MAX);
|
||||
assert((cred_flag & ~(EXEC_SETUP_CREDENTIALS_FRESH|EXEC_SETUP_CREDENTIALS)) == 0);
|
||||
@ -1730,12 +1703,6 @@ static ExecFlags service_exec_flags(
|
||||
if (!IN_SET(command_id, SERVICE_EXEC_CONDITION, SERVICE_EXEC_START_PRE))
|
||||
flags |= EXEC_CONTROL_CGROUP;
|
||||
|
||||
/* Pass credentials to ExecReload*= too, but only if the credentials are actually refreshed,
|
||||
* to make sure they have the same understanding of the world as the main process. */
|
||||
if (IN_SET(command_id, SERVICE_EXEC_RELOAD, SERVICE_EXEC_RELOAD_POST) &&
|
||||
FLAGS_SET(s->refreshed_mask, SERVICE_RELOAD_CREDENTIALS))
|
||||
flags |= EXEC_SETUP_CREDENTIALS;
|
||||
|
||||
if (IN_SET(command_id, SERVICE_EXEC_STOP, SERVICE_EXEC_STOP_POST))
|
||||
flags |= EXEC_SETENV_RESULT;
|
||||
|
||||
@ -2261,7 +2228,7 @@ static void service_enter_stop_post(Service *s, ServiceResult f) {
|
||||
|
||||
r = service_spawn(s,
|
||||
s->control_command,
|
||||
service_exec_flags(s, s->control_command_id, /* cred_flag = */ 0),
|
||||
service_exec_flags(s->control_command_id, /* cred_flag= */ 0),
|
||||
s->timeout_stop_usec,
|
||||
&s->control_pid);
|
||||
if (r < 0) {
|
||||
@ -2373,7 +2340,7 @@ static void service_enter_stop(Service *s, ServiceResult f) {
|
||||
|
||||
r = service_spawn(s,
|
||||
s->control_command,
|
||||
service_exec_flags(s, s->control_command_id, /* cred_flag = */ 0),
|
||||
service_exec_flags(s->control_command_id, /* cred_flag= */ 0),
|
||||
s->timeout_stop_usec,
|
||||
&s->control_pid);
|
||||
if (r < 0) {
|
||||
@ -2458,7 +2425,7 @@ static void service_enter_start_post(Service *s) {
|
||||
|
||||
r = service_spawn(s,
|
||||
s->control_command,
|
||||
service_exec_flags(s, s->control_command_id, /* cred_flag = */ 0),
|
||||
service_exec_flags(s->control_command_id, /* cred_flag= */ 0),
|
||||
s->timeout_start_usec,
|
||||
&s->control_pid);
|
||||
if (r < 0) {
|
||||
@ -2568,7 +2535,7 @@ static void service_enter_start(Service *s) {
|
||||
|
||||
r = service_spawn(s,
|
||||
c,
|
||||
service_exec_flags(s, SERVICE_EXEC_START, EXEC_SETUP_CREDENTIALS_FRESH),
|
||||
service_exec_flags(SERVICE_EXEC_START, EXEC_SETUP_CREDENTIALS_FRESH),
|
||||
timeout,
|
||||
&pidref);
|
||||
if (r < 0) {
|
||||
@ -2628,7 +2595,7 @@ static void service_enter_start_pre(Service *s) {
|
||||
|
||||
r = service_spawn(s,
|
||||
s->control_command,
|
||||
service_exec_flags(s, s->control_command_id, /* cred_flag = */ 0),
|
||||
service_exec_flags(s->control_command_id, /* cred_flag= */ 0),
|
||||
s->timeout_start_usec,
|
||||
&s->control_pid);
|
||||
if (r < 0) {
|
||||
@ -2664,7 +2631,7 @@ static void service_enter_condition(Service *s) {
|
||||
|
||||
r = service_spawn(s,
|
||||
s->control_command,
|
||||
service_exec_flags(s, s->control_command_id, /* cred_flag = */ 0),
|
||||
service_exec_flags(s->control_command_id, /* cred_flag= */ 0),
|
||||
s->timeout_start_usec,
|
||||
&s->control_pid);
|
||||
if (r < 0) {
|
||||
@ -2768,7 +2735,7 @@ static void service_enter_reload_post(Service *s) {
|
||||
|
||||
r = service_spawn(s,
|
||||
s->control_command,
|
||||
service_exec_flags(s, s->control_command_id, /* cred_flag = */ 0),
|
||||
service_exec_flags(s->control_command_id, /* cred_flag= */ 0),
|
||||
s->timeout_start_usec,
|
||||
&s->control_pid);
|
||||
if (r < 0) {
|
||||
@ -2843,7 +2810,7 @@ static void service_enter_reload(Service *s) {
|
||||
|
||||
r = service_spawn(s,
|
||||
s->control_command,
|
||||
service_exec_flags(s, s->control_command_id, /* cred_flag = */ 0),
|
||||
service_exec_flags(s->control_command_id, /* cred_flag= */ 0),
|
||||
s->timeout_start_usec,
|
||||
&s->control_pid);
|
||||
if (r < 0) {
|
||||
@ -2856,114 +2823,28 @@ static void service_enter_reload(Service *s) {
|
||||
service_enter_reload_signal(s);
|
||||
}
|
||||
|
||||
static bool service_get_effective_reload_credentials(Service *s) {
|
||||
assert(s);
|
||||
|
||||
return FLAGS_SET(s->refresh_on_reload_flags, SERVICE_RELOAD_CREDENTIALS) &&
|
||||
exec_context_has_credentials(&s->exec_context);
|
||||
}
|
||||
|
||||
static void service_enter_refresh_credentials(Service *s) {
|
||||
_cleanup_(pidref_done) PidRef worker = PIDREF_NULL;
|
||||
static bool service_should_reload_extensions(Service *s) {
|
||||
int r;
|
||||
|
||||
assert(s);
|
||||
|
||||
if (!service_get_effective_reload_credentials(s))
|
||||
return service_enter_reload(s);
|
||||
|
||||
service_unwatch_control_pid(s);
|
||||
s->control_command = NULL;
|
||||
s->control_command_id = _SERVICE_EXEC_COMMAND_INVALID;
|
||||
|
||||
r = service_arm_timer(s, /* relative = */ true, s->timeout_start_usec);
|
||||
if (r < 0) {
|
||||
log_unit_warning_errno(UNIT(s), r, "Failed to install timer: %m");
|
||||
goto fail;
|
||||
if (!pidref_is_set(&s->main_pid)) {
|
||||
log_unit_debug(UNIT(s), "Not reloading extensions for service without main PID.");
|
||||
return false;
|
||||
}
|
||||
|
||||
r = unit_fork_helper_process_full(UNIT(s), "(sd-refresh-creds)", /* into_cgroup = */ false,
|
||||
FORK_ALLOW_DLOPEN, /* allow loading libacl to avoid doing so in pid1 */
|
||||
&worker);
|
||||
if (r < 0) {
|
||||
log_unit_error_errno(UNIT(s), r, "Failed to fork process to refresh credentials in unit's namespace: %m");
|
||||
goto fail;
|
||||
}
|
||||
if (r == 0) {
|
||||
LOG_CONTEXT_PUSH_UNIT(UNIT(s));
|
||||
|
||||
r = unit_refresh_credentials(UNIT(s));
|
||||
if (ERRNO_IS_NEG_PRIVILEGE(r))
|
||||
_exit(EXIT_NOPERMISSION);
|
||||
r = exec_context_has_vpicked_extensions(&s->exec_context);
|
||||
if (r < 0)
|
||||
_exit(EXIT_FAILURE);
|
||||
log_unit_warning_errno(UNIT(s), r, "Failed to determine if service should reload extensions, assuming false: %m");
|
||||
if (r == 0)
|
||||
_exit(EXIT_NOTINSTALLED);
|
||||
|
||||
_exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
r = unit_watch_pidref(UNIT(s), &worker, /* exclusive = */ true);
|
||||
if (r < 0) {
|
||||
log_unit_warning_errno(UNIT(s), r, "Failed to watch credentials refresh helper process: %m");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
s->control_pid = TAKE_PIDREF(worker);
|
||||
service_set_state(s, SERVICE_REFRESH_CREDENTIALS);
|
||||
return;
|
||||
|
||||
fail:
|
||||
service_reload_finish(s, SERVICE_FAILURE_RESOURCES);
|
||||
}
|
||||
|
||||
static bool service_can_reload_extensions(Service *s, bool warn) {
|
||||
assert(s);
|
||||
log_unit_debug(UNIT(s), "Service has no extensions to reload.");
|
||||
if (r <= 0)
|
||||
return false;
|
||||
|
||||
// TODO: Add support for user services, which can use ExtensionDirectories= + notify-reload.
|
||||
// For now, skip for user services.
|
||||
|
||||
if (exec_context_has_vpicked_extensions(&s->exec_context) <= 0) {
|
||||
if (warn)
|
||||
log_unit_warning(UNIT(s), "Service uses RefreshOnReload=extensions, but has no extensions using vpick. Ignoring.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!s->exec_command[SERVICE_EXEC_START]) {
|
||||
if (warn)
|
||||
log_unit_warning(UNIT(s), "Service uses RefreshOnReload=extensions, but has no main process (ExecStart=). Ignoring.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!MANAGER_IS_SYSTEM(UNIT(s)->manager)) {
|
||||
if (warn)
|
||||
log_unit_warning(UNIT(s), "Service uses RefreshOnReload=extensions, which is not supported in user mode. Ignoring.");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool service_get_effective_reload_extensions(Service *s) {
|
||||
assert(s);
|
||||
|
||||
if (!FLAGS_SET(s->refresh_on_reload_flags, SERVICE_RELOAD_EXTENSIONS))
|
||||
return false;
|
||||
|
||||
if (!service_can_reload_extensions(s, /* warn = */ false))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool service_should_reload_extensions(Service *s) {
|
||||
assert(s);
|
||||
|
||||
if (!service_get_effective_reload_extensions(s))
|
||||
return false;
|
||||
|
||||
if (!pidref_is_set(&s->main_pid)) {
|
||||
log_unit_debug(UNIT(s), "Not reloading extensions for service without main PID.");
|
||||
log_once(LOG_WARNING, "Not reloading extensions for user services.");
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -2976,9 +2857,9 @@ static void service_enter_refresh_extensions(Service *s) {
|
||||
|
||||
assert(s);
|
||||
|
||||
/* If we don't have extensions to refresh, immediately transition to next state */
|
||||
/* If we don't have extensions to refresh, immediately transition to reload state */
|
||||
if (!service_should_reload_extensions(s))
|
||||
return service_enter_refresh_credentials(s);
|
||||
return service_enter_reload(s);
|
||||
|
||||
service_unwatch_control_pid(s);
|
||||
s->control_command = NULL;
|
||||
@ -3074,7 +2955,7 @@ static void service_run_next_control(Service *s) {
|
||||
|
||||
r = service_spawn(s,
|
||||
s->control_command,
|
||||
service_exec_flags(s, s->control_command_id, /* cred_flag = */ 0),
|
||||
service_exec_flags(s->control_command_id, /* cred_flag= */ 0),
|
||||
timeout,
|
||||
&s->control_pid);
|
||||
if (r < 0) {
|
||||
@ -3105,7 +2986,7 @@ static void service_run_next_main(Service *s) {
|
||||
|
||||
r = service_spawn(s,
|
||||
s->main_command,
|
||||
service_exec_flags(s, SERVICE_EXEC_START, EXEC_SETUP_CREDENTIALS),
|
||||
service_exec_flags(SERVICE_EXEC_START, EXEC_SETUP_CREDENTIALS),
|
||||
s->timeout_start_usec,
|
||||
&pidref);
|
||||
if (r < 0) {
|
||||
@ -3243,7 +3124,6 @@ static int service_stop(Unit *u) {
|
||||
service_live_mount_finish(s, SERVICE_FAILURE_PROTOCOL, BUS_ERROR_UNIT_INACTIVE);
|
||||
_fallthrough_;
|
||||
case SERVICE_REFRESH_EXTENSIONS:
|
||||
case SERVICE_REFRESH_CREDENTIALS:
|
||||
service_kill_control_process(s);
|
||||
_fallthrough_;
|
||||
case SERVICE_CONDITION:
|
||||
@ -3286,7 +3166,6 @@ static int service_reload(Unit *u) {
|
||||
assert(IN_SET(s->state, SERVICE_RUNNING, SERVICE_EXITED));
|
||||
|
||||
s->reload_result = SERVICE_SUCCESS;
|
||||
s->refreshed_mask = 0;
|
||||
|
||||
service_enter_refresh_extensions(s);
|
||||
|
||||
@ -3297,10 +3176,7 @@ static bool service_can_reload(Unit *u) {
|
||||
Service *s = ASSERT_PTR(SERVICE(u));
|
||||
|
||||
return s->exec_command[SERVICE_EXEC_RELOAD] ||
|
||||
s->type == SERVICE_NOTIFY_RELOAD ||
|
||||
(s->refresh_on_reload_set &&
|
||||
(service_get_effective_reload_extensions(s) ||
|
||||
service_get_effective_reload_credentials(s)));
|
||||
s->type == SERVICE_NOTIFY_RELOAD;
|
||||
}
|
||||
|
||||
static unsigned service_exec_command_index(Unit *u, ServiceExecCommand id, const ExecCommand *current) {
|
||||
@ -3510,21 +3386,6 @@ static int service_serialize(Unit *u, FILE *f, FDSet *fds) {
|
||||
|
||||
(void) serialize_usec(f, "reload-begin-usec", s->reload_begin_usec);
|
||||
|
||||
if (s->refreshed_mask > 0) {
|
||||
_cleanup_strv_free_ char **l = NULL;
|
||||
_cleanup_free_ char *t = NULL;
|
||||
|
||||
r = service_refresh_on_reload_to_strv(s->refreshed_mask, &l);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
t = strv_join(l, " ");
|
||||
if (!t)
|
||||
return log_oom();
|
||||
|
||||
(void) serialize_item(f, "refreshed-mask", t);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -3914,11 +3775,7 @@ static int service_deserialize_item(Unit *u, const char *key, const char *value,
|
||||
|
||||
} else if (streq(key, "reload-begin-usec"))
|
||||
(void) deserialize_usec(value, &s->reload_begin_usec);
|
||||
else if (streq(key, "refreshed-mask")) {
|
||||
r = service_refresh_on_reload_from_string_many(value, &s->refreshed_mask);
|
||||
if (r < 0)
|
||||
log_unit_debug_errno(u, r, "Failed to parse refresh-mask value: %s", value);
|
||||
} else
|
||||
else
|
||||
log_unit_debug(u, "Unknown serialization key: %s", key);
|
||||
|
||||
return 0;
|
||||
@ -4331,7 +4188,6 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
|
||||
|
||||
case SERVICE_START_POST:
|
||||
case SERVICE_REFRESH_EXTENSIONS:
|
||||
case SERVICE_REFRESH_CREDENTIALS:
|
||||
case SERVICE_RELOAD:
|
||||
case SERVICE_RELOAD_SIGNAL:
|
||||
case SERVICE_RELOAD_NOTIFY:
|
||||
@ -4451,7 +4307,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
|
||||
success,
|
||||
code, status);
|
||||
|
||||
if (!IN_SET(s->state, SERVICE_REFRESH_EXTENSIONS, SERVICE_REFRESH_CREDENTIALS, SERVICE_RELOAD, SERVICE_RELOAD_POST, SERVICE_MOUNTING) &&
|
||||
if (!IN_SET(s->state, SERVICE_REFRESH_EXTENSIONS, SERVICE_RELOAD, SERVICE_RELOAD_POST, SERVICE_MOUNTING) &&
|
||||
s->result == SERVICE_SUCCESS)
|
||||
s->result = f;
|
||||
|
||||
@ -4540,21 +4396,10 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
|
||||
break;
|
||||
|
||||
case SERVICE_REFRESH_EXTENSIONS:
|
||||
if (f == SERVICE_SUCCESS) {
|
||||
s->refreshed_mask |= SERVICE_RELOAD_EXTENSIONS;
|
||||
service_enter_refresh_credentials(s);
|
||||
} else
|
||||
service_reload_finish(s, f);
|
||||
break;
|
||||
|
||||
case SERVICE_REFRESH_CREDENTIALS:
|
||||
if (f == SERVICE_SUCCESS ||
|
||||
(f == SERVICE_FAILURE_EXIT_CODE && IN_SET(status, EXIT_NOTINSTALLED, EXIT_NOPERMISSION))) {
|
||||
|
||||
/* Refreshing asynchronously done, proceed to reload */
|
||||
s->refreshed_mask |= f == SERVICE_SUCCESS ? SERVICE_RELOAD_CREDENTIALS : 0;
|
||||
if (f == SERVICE_SUCCESS)
|
||||
/* Remounting extensions asynchronously done, proceed to reload */
|
||||
service_enter_reload(s);
|
||||
} else
|
||||
else
|
||||
service_reload_finish(s, f);
|
||||
break;
|
||||
|
||||
@ -4670,7 +4515,6 @@ static int service_dispatch_timer(sd_event_source *source, usec_t usec, void *us
|
||||
break;
|
||||
|
||||
case SERVICE_REFRESH_EXTENSIONS:
|
||||
case SERVICE_REFRESH_CREDENTIALS:
|
||||
case SERVICE_RELOAD:
|
||||
case SERVICE_RELOAD_SIGNAL:
|
||||
case SERVICE_RELOAD_NOTIFY:
|
||||
@ -4994,9 +4838,7 @@ static void service_notify_message_process_state(Service *s, char * const *tags)
|
||||
if (strv_contains(tags, "STOPPING=1")) {
|
||||
s->notify_state = NOTIFY_STOPPING;
|
||||
|
||||
if (IN_SET(s->state, SERVICE_RUNNING,
|
||||
SERVICE_REFRESH_EXTENSIONS, SERVICE_REFRESH_CREDENTIALS,
|
||||
SERVICE_RELOAD_SIGNAL, SERVICE_RELOAD_NOTIFY))
|
||||
if (IN_SET(s->state, SERVICE_RUNNING, SERVICE_RELOAD_SIGNAL, SERVICE_RELOAD_NOTIFY, SERVICE_REFRESH_EXTENSIONS))
|
||||
service_enter_stop_by_notify(s);
|
||||
|
||||
return;
|
||||
@ -5093,8 +4935,7 @@ static void service_notify_message(
|
||||
r = service_notify_message_parse_new_pid(u, tags, fds, &new_main_pid);
|
||||
if (r > 0 &&
|
||||
IN_SET(s->state, SERVICE_START, SERVICE_START_POST, SERVICE_RUNNING,
|
||||
SERVICE_REFRESH_EXTENSIONS, SERVICE_REFRESH_CREDENTIALS,
|
||||
SERVICE_RELOAD, SERVICE_RELOAD_SIGNAL, SERVICE_RELOAD_NOTIFY, SERVICE_RELOAD_POST,
|
||||
SERVICE_REFRESH_EXTENSIONS, SERVICE_RELOAD, SERVICE_RELOAD_SIGNAL, SERVICE_RELOAD_NOTIFY, SERVICE_RELOAD_POST,
|
||||
SERVICE_STOP, SERVICE_STOP_SIGTERM) &&
|
||||
(!s->main_pid_known || !pidref_equal(&new_main_pid, &s->main_pid))) {
|
||||
|
||||
@ -5363,7 +5204,6 @@ static bool pick_up_pid_from_bus_name(Service *s) {
|
||||
SERVICE_START_POST,
|
||||
SERVICE_RUNNING,
|
||||
SERVICE_REFRESH_EXTENSIONS,
|
||||
SERVICE_REFRESH_CREDENTIALS,
|
||||
SERVICE_RELOAD,
|
||||
SERVICE_RELOAD_SIGNAL,
|
||||
SERVICE_RELOAD_NOTIFY,
|
||||
@ -5550,7 +5390,6 @@ static bool service_needs_console(Unit *u) {
|
||||
SERVICE_START_POST,
|
||||
SERVICE_RUNNING,
|
||||
SERVICE_REFRESH_EXTENSIONS,
|
||||
SERVICE_REFRESH_CREDENTIALS,
|
||||
SERVICE_RELOAD,
|
||||
SERVICE_RELOAD_SIGNAL,
|
||||
SERVICE_RELOAD_NOTIFY,
|
||||
@ -6049,73 +5888,6 @@ static const char* const service_timeout_failure_mode_table[_SERVICE_TIMEOUT_FAI
|
||||
|
||||
DEFINE_STRING_TABLE_LOOKUP(service_timeout_failure_mode, ServiceTimeoutFailureMode);
|
||||
|
||||
static const struct {
|
||||
ServiceRefreshOnReload flag;
|
||||
const char *name;
|
||||
} service_refresh_on_reload_table[] = {
|
||||
{ SERVICE_RELOAD_EXTENSIONS, "extensions" },
|
||||
{ SERVICE_RELOAD_CREDENTIALS, "credentials" },
|
||||
};
|
||||
|
||||
ServiceRefreshOnReload service_refresh_on_reload_flag_from_string(const char *s) {
|
||||
assert(s);
|
||||
|
||||
FOREACH_ELEMENT(i, service_refresh_on_reload_table)
|
||||
if (streq(s, i->name))
|
||||
return i->flag;
|
||||
|
||||
return _SERVICE_REFRESH_ON_RELOAD_INVALID;
|
||||
}
|
||||
|
||||
int service_refresh_on_reload_from_string_many(const char *s, ServiceRefreshOnReload *ret) {
|
||||
ServiceRefreshOnReload flags = 0;
|
||||
int r;
|
||||
|
||||
assert(s);
|
||||
assert(ret);
|
||||
|
||||
for (;;) {
|
||||
_cleanup_free_ char *v = NULL;
|
||||
ServiceRefreshOnReload f;
|
||||
|
||||
r = extract_first_word(&s, &v, NULL, 0);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
break;
|
||||
|
||||
f = service_refresh_on_reload_flag_from_string(v);
|
||||
if (f < 0)
|
||||
return f;
|
||||
assert(f > 0);
|
||||
|
||||
flags |= f;
|
||||
}
|
||||
|
||||
*ret = flags;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int service_refresh_on_reload_to_strv(ServiceRefreshOnReload flags, char ***ret) {
|
||||
_cleanup_strv_free_ char **l = NULL;
|
||||
int r;
|
||||
|
||||
assert(flags >= 0);
|
||||
assert(ret);
|
||||
|
||||
FOREACH_ELEMENT(i, service_refresh_on_reload_table) {
|
||||
if (!FLAGS_SET(flags, i->flag))
|
||||
continue;
|
||||
|
||||
r = strv_extend(&l, i->name);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
*ret = TAKE_PTR(l);
|
||||
return 0;
|
||||
}
|
||||
|
||||
const UnitVTable service_vtable = {
|
||||
.object_size = sizeof(Service),
|
||||
.exec_context_offset = offsetof(Service, exec_context),
|
||||
|
||||
@ -96,15 +96,6 @@ typedef enum ServiceRestartMode {
|
||||
_SERVICE_RESTART_MODE_INVALID = -EINVAL,
|
||||
} ServiceRestartMode;
|
||||
|
||||
typedef enum ServiceRefreshOnReload {
|
||||
SERVICE_RELOAD_EXTENSIONS = 1 << 0,
|
||||
SERVICE_RELOAD_CREDENTIALS = 1 << 1,
|
||||
_SERVICE_REFRESH_ON_RELOAD_ALL = (1 << 2) - 1,
|
||||
_SERVICE_REFRESH_ON_RELOAD_INVALID = -EINVAL,
|
||||
} ServiceRefreshOnReload;
|
||||
|
||||
#define SERVICE_REFRESH_ON_RELOAD_DEFAULT SERVICE_RELOAD_EXTENSIONS
|
||||
|
||||
typedef struct ServiceFDStore {
|
||||
Service *service;
|
||||
|
||||
@ -246,10 +237,6 @@ typedef struct Service {
|
||||
int reload_signal;
|
||||
usec_t reload_begin_usec;
|
||||
|
||||
bool refresh_on_reload_set;
|
||||
ServiceRefreshOnReload refresh_on_reload_flags;
|
||||
ServiceRefreshOnReload refreshed_mask;
|
||||
|
||||
OOMPolicy oom_policy;
|
||||
|
||||
char *usb_function_descriptors;
|
||||
@ -301,10 +288,6 @@ DECLARE_STRING_TABLE_LOOKUP(service_result, ServiceResult);
|
||||
|
||||
DECLARE_STRING_TABLE_LOOKUP(service_timeout_failure_mode, ServiceTimeoutFailureMode);
|
||||
|
||||
ServiceRefreshOnReload service_refresh_on_reload_flag_from_string(const char *s) _pure_;
|
||||
int service_refresh_on_reload_from_string_many(const char *s, ServiceRefreshOnReload *ret);
|
||||
int service_refresh_on_reload_to_strv(ServiceRefreshOnReload flags, char ***ret);
|
||||
|
||||
DEFINE_CAST(SERVICE, Service);
|
||||
|
||||
/* Only exported for unit tests */
|
||||
|
||||
@ -141,33 +141,33 @@ static int units_by_state_total_build_json(MetricFamilyContext *context, void *u
|
||||
}
|
||||
|
||||
const MetricFamily metric_family_table[] = {
|
||||
/* Keep metrics ordered alphabetically */
|
||||
// Keep metrics ordered alphabetically
|
||||
{
|
||||
.name = METRIC_IO_SYSTEMD_MANAGER_PREFIX "nRestarts",
|
||||
.name = METRIC_IO_SYSTEMD_MANAGER_PREFIX "nrestarts",
|
||||
.description = "Per unit metric: number of restarts",
|
||||
.type = METRIC_FAMILY_TYPE_COUNTER,
|
||||
.generate = nrestarts_build_json,
|
||||
},
|
||||
{
|
||||
.name = METRIC_IO_SYSTEMD_MANAGER_PREFIX "unitActiveState",
|
||||
.name = METRIC_IO_SYSTEMD_MANAGER_PREFIX "unit_active_state",
|
||||
.description = "Per unit metric: active state",
|
||||
.type = METRIC_FAMILY_TYPE_STRING,
|
||||
.generate = unit_active_state_build_json,
|
||||
},
|
||||
{
|
||||
.name = METRIC_IO_SYSTEMD_MANAGER_PREFIX "unitLoadState",
|
||||
.name = METRIC_IO_SYSTEMD_MANAGER_PREFIX "unit_load_state",
|
||||
.description = "Per unit metric: load state",
|
||||
.type = METRIC_FAMILY_TYPE_STRING,
|
||||
.generate = unit_load_state_build_json,
|
||||
},
|
||||
{
|
||||
.name = METRIC_IO_SYSTEMD_MANAGER_PREFIX "unitsByStateTotal",
|
||||
.name = METRIC_IO_SYSTEMD_MANAGER_PREFIX "units_by_state_total",
|
||||
.description = "Total number of units of different state",
|
||||
.type = METRIC_FAMILY_TYPE_GAUGE,
|
||||
.generate = units_by_state_total_build_json,
|
||||
},
|
||||
{
|
||||
.name = METRIC_IO_SYSTEMD_MANAGER_PREFIX "unitsByTypeTotal",
|
||||
.name = METRIC_IO_SYSTEMD_MANAGER_PREFIX "units_by_type_total",
|
||||
.description = "Total number of units of different types",
|
||||
.type = METRIC_FAMILY_TYPE_GAUGE,
|
||||
.generate = units_by_type_total_build_json,
|
||||
|
||||
@ -351,11 +351,10 @@ assert_cc(sizeof(long long) == sizeof(intmax_t));
|
||||
#define CASE_F_19(X, ...) case X: CASE_F_18( __VA_ARGS__)
|
||||
#define CASE_F_20(X, ...) case X: CASE_F_19( __VA_ARGS__)
|
||||
#define CASE_F_21(X, ...) case X: CASE_F_20( __VA_ARGS__)
|
||||
#define CASE_F_22(X, ...) case X: CASE_F_21( __VA_ARGS__)
|
||||
|
||||
#define GET_CASE_F(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,_21,_22,NAME,...) NAME
|
||||
#define GET_CASE_F(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,_21,NAME,...) NAME
|
||||
#define FOR_EACH_MAKE_CASE(...) \
|
||||
GET_CASE_F(__VA_ARGS__,CASE_F_22,CASE_F_21,CASE_F_20,CASE_F_19,CASE_F_18,CASE_F_17,CASE_F_16,CASE_F_15,CASE_F_14,CASE_F_13,CASE_F_12, \
|
||||
GET_CASE_F(__VA_ARGS__,CASE_F_21,CASE_F_20,CASE_F_19,CASE_F_18,CASE_F_17,CASE_F_16,CASE_F_15,CASE_F_14,CASE_F_13,CASE_F_12, \
|
||||
CASE_F_11,CASE_F_10,CASE_F_9,CASE_F_8,CASE_F_7,CASE_F_6,CASE_F_5,CASE_F_4,CASE_F_3,CASE_F_2,CASE_F_1) \
|
||||
(__VA_ARGS__)
|
||||
|
||||
@ -365,7 +364,7 @@ assert_cc(sizeof(long long) == sizeof(intmax_t));
|
||||
/* If the build breaks in the line below, you need to extend the case macros. We use typeof(+x) \
|
||||
* here to widen the type of x if it is a bit-field as this would otherwise be illegal. */ \
|
||||
static const typeof(+x) __assert_in_set[] _unused_ = { first, __VA_ARGS__ }; \
|
||||
assert_cc(ELEMENTSOF(__assert_in_set) <= 22); \
|
||||
assert_cc(ELEMENTSOF(__assert_in_set) <= 21); \
|
||||
switch (x) { \
|
||||
FOR_EACH_MAKE_CASE(first, __VA_ARGS__) \
|
||||
_found = true; \
|
||||
|
||||
@ -13,7 +13,6 @@
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "errno-util.h"
|
||||
#include "escape.h"
|
||||
#include "extract-word.h"
|
||||
#include "fd-util.h"
|
||||
#include "fs-util.h"
|
||||
@ -634,11 +633,7 @@ static int pid_notify_with_fds_internal(
|
||||
return log_debug_errno(SYNTHETIC_ERRNO(EPROTO), "Unexpectedly received data on notify socket.");
|
||||
}
|
||||
|
||||
if (DEBUG_LOGGING) {
|
||||
_cleanup_free_ char *escaped = xescape_full(state, "\"", /* console_width = */ SIZE_MAX, XESCAPE_8_BIT);
|
||||
log_debug("Notify message sent to '%s': \"%s\"", e, escaped ?: state);
|
||||
}
|
||||
|
||||
log_debug("Notify message sent to '%s': \"%s\"", e, state);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
@ -1465,7 +1465,7 @@ static int install_chroot_dropin(
|
||||
ext->path,
|
||||
/* With --force tell PID1 to avoid enforcing that the image <name> and
|
||||
* extension-release.<name> have to match. */
|
||||
!IN_SET(ext->type, IMAGE_DIRECTORY, IMAGE_SUBVOLUME) &&
|
||||
!IN_SET(type, IMAGE_DIRECTORY, IMAGE_SUBVOLUME) &&
|
||||
FLAGS_SET(flags, PORTABLE_FORCE_EXTENSION) ?
|
||||
":x-systemd.relax-extension-release-check\n" :
|
||||
"\n",
|
||||
@ -1475,7 +1475,7 @@ static int install_chroot_dropin(
|
||||
"LogExtraFields=PORTABLE_EXTENSION=", extension_base_name, "\n"))
|
||||
return -ENOMEM;
|
||||
|
||||
if (pinned_ext_image_policy && !IN_SET(ext->type, IMAGE_DIRECTORY, IMAGE_SUBVOLUME)) {
|
||||
if (pinned_ext_image_policy) {
|
||||
_cleanup_free_ char *policy_str = NULL;
|
||||
|
||||
r = image_policy_to_string(pinned_ext_image_policy, /* simplify= */ true, &policy_str);
|
||||
|
||||
@ -3007,9 +3007,7 @@ static int partition_read_definition(
|
||||
}
|
||||
|
||||
/* Verity partitions are read only, let's imply the RO flag hence, unless explicitly configured otherwise. */
|
||||
if ((partition_designator_is_verity_hash(p->type.designator) ||
|
||||
partition_designator_is_verity_sig(p->type.designator) ||
|
||||
IN_SET(p->verity, VERITY_DATA, VERITY_SIG)) && p->read_only < 0)
|
||||
if ((partition_designator_is_verity_hash(p->type.designator) || p->verity == VERITY_DATA) && p->read_only < 0)
|
||||
p->read_only = true;
|
||||
|
||||
/* Default to "growfs" on, unless read-only */
|
||||
|
||||
@ -1172,56 +1172,6 @@ static int bus_append_import_credential(sd_bus_message *m, const char *field, co
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int bus_append_refresh_on_reload(sd_bus_message *m, const char *field, const char *eq) {
|
||||
int r;
|
||||
|
||||
r = sd_bus_message_open_container(m, 'r', "sv");
|
||||
if (r < 0)
|
||||
return bus_log_create_error(r);
|
||||
|
||||
r = sd_bus_message_append_basic(m, 's', field);
|
||||
if (r < 0)
|
||||
return bus_log_create_error(r);
|
||||
|
||||
r = sd_bus_message_open_container(m, 'v', "a(bs)");
|
||||
if (r < 0)
|
||||
return bus_log_create_error(r);
|
||||
|
||||
r = sd_bus_message_open_container(m, 'a', "(bs)");
|
||||
if (r < 0)
|
||||
return bus_log_create_error(r);
|
||||
|
||||
bool invert = *eq == '~';
|
||||
|
||||
for (const char *p = eq + invert;;) {
|
||||
_cleanup_free_ char *word = NULL;
|
||||
|
||||
r = extract_first_word(&p, &word, NULL, 0);
|
||||
if (r < 0)
|
||||
return parse_log_error(r, field, eq);
|
||||
if (r == 0)
|
||||
break;
|
||||
|
||||
r = sd_bus_message_append(m, "(bs)", invert, word);
|
||||
if (r < 0)
|
||||
return bus_log_create_error(r);
|
||||
}
|
||||
|
||||
r = sd_bus_message_close_container(m);
|
||||
if (r < 0)
|
||||
return bus_log_create_error(r);
|
||||
|
||||
r = sd_bus_message_close_container(m);
|
||||
if (r < 0)
|
||||
return bus_log_create_error(r);
|
||||
|
||||
r = sd_bus_message_close_container(m);
|
||||
if (r < 0)
|
||||
return bus_log_create_error(r);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int bus_append_log_extra_fields(sd_bus_message *m, const char *field, const char *eq) {
|
||||
int r;
|
||||
|
||||
@ -2715,7 +2665,6 @@ static const BusProperty service_properties[] = {
|
||||
{ "SuccessExitStatus", bus_append_exit_status },
|
||||
{ "OpenFile", bus_append_open_file },
|
||||
{ "ReloadSignal", bus_append_signal_from_string },
|
||||
{ "RefreshOnReload", bus_append_refresh_on_reload },
|
||||
{}
|
||||
};
|
||||
|
||||
|
||||
@ -325,8 +325,6 @@ bool gpt_partition_type_knows_read_only(GptPartitionType type) {
|
||||
/* pretty much implied, but let's set the bit to make things really clear */
|
||||
PARTITION_ROOT_VERITY,
|
||||
PARTITION_USR_VERITY,
|
||||
PARTITION_ROOT_VERITY_SIG,
|
||||
PARTITION_USR_VERITY_SIG,
|
||||
PARTITION_HOME,
|
||||
PARTITION_SRV,
|
||||
PARTITION_VAR,
|
||||
|
||||
@ -45,8 +45,8 @@ typedef enum UserRecordLoadFlags UserRecordLoadFlags;
|
||||
typedef enum UserStorage UserStorage;
|
||||
|
||||
typedef struct Bitmap Bitmap;
|
||||
typedef struct BootConfig BootConfig;
|
||||
typedef struct BPFProgram BPFProgram;
|
||||
typedef struct BootConfig BootConfig;
|
||||
typedef struct BusObjectImplementation BusObjectImplementation;
|
||||
typedef struct CalendarSpec CalendarSpec;
|
||||
typedef struct Condition Condition;
|
||||
|
||||
@ -18,7 +18,7 @@ static SD_VARLINK_DEFINE_ERROR(NoSuchMetric);
|
||||
static SD_VARLINK_DEFINE_METHOD_FULL(
|
||||
List,
|
||||
SD_VARLINK_REQUIRES_MORE,
|
||||
SD_VARLINK_FIELD_COMMENT("Metric name, e.g. io.systemd.Manager.unitsByTypeTotal or io.systemd.Manager.unitActiveState"),
|
||||
SD_VARLINK_FIELD_COMMENT("Metric name, e.g. io.systemd.Manager.units_by_type_total or io.systemd.Manager.unit_active_state"),
|
||||
SD_VARLINK_DEFINE_OUTPUT(name, SD_VARLINK_STRING, 0),
|
||||
/* metric value has various types depending on MetricFamilyType and actual data double/int/uint */
|
||||
SD_VARLINK_FIELD_COMMENT("Metric value"),
|
||||
|
||||
@ -1171,7 +1171,7 @@ static int run_callout(
|
||||
/* Build the filenames and paths which is normally done by transfer_acquire_instance(), but for partial
|
||||
* and pending instances which are about to be installed (in which case, transfer_acquire_instance() is
|
||||
* skipped). */
|
||||
int transfer_compute_temporary_paths(Transfer *t, Instance *i, InstanceMetadata *f) {
|
||||
static int transfer_compute_temporary_paths(Transfer *t, Instance *i, InstanceMetadata *f) {
|
||||
_cleanup_free_ char *formatted_pattern = NULL, *formatted_partial_pattern = NULL, *formatted_pending_pattern = NULL;
|
||||
int r;
|
||||
|
||||
@ -1260,16 +1260,16 @@ int transfer_compute_temporary_paths(Transfer *t, Instance *i, InstanceMetadata
|
||||
return 0;
|
||||
}
|
||||
|
||||
int transfer_acquire_instance(Transfer *t, Instance *i, InstanceMetadata *f, TransferProgress cb, void *userdata) {
|
||||
int transfer_acquire_instance(Transfer *t, Instance *i, TransferProgress cb, void *userdata) {
|
||||
_cleanup_free_ char *digest = NULL;
|
||||
char offset[DECIMAL_STR_MAX(uint64_t)+1], max_size[DECIMAL_STR_MAX(uint64_t)+1];
|
||||
const char *where = NULL;
|
||||
InstanceMetadata f;
|
||||
Instance *existing;
|
||||
int r;
|
||||
|
||||
assert(t);
|
||||
assert(i);
|
||||
assert(f);
|
||||
assert(i->resource == &t->source);
|
||||
assert(cb);
|
||||
|
||||
@ -1282,6 +1282,11 @@ int transfer_acquire_instance(Transfer *t, Instance *i, InstanceMetadata *f, Tra
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Compute up the temporary paths */
|
||||
r = transfer_compute_temporary_paths(t, i, &f);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (RESOURCE_IS_FILESYSTEM(t->target.type)) {
|
||||
r = mkdir_parents(t->temporary_partial_path, 0755);
|
||||
if (r < 0)
|
||||
@ -1497,10 +1502,10 @@ int transfer_acquire_instance(Transfer *t, Instance *i, InstanceMetadata *f, Tra
|
||||
assert(t->temporary_pending_path);
|
||||
|
||||
/* Apply file attributes if set */
|
||||
if (f->mtime != USEC_INFINITY) {
|
||||
if (f.mtime != USEC_INFINITY) {
|
||||
struct timespec ts;
|
||||
|
||||
timespec_store(&ts, f->mtime);
|
||||
timespec_store(&ts, f.mtime);
|
||||
|
||||
if (utimensat(AT_FDCWD, t->temporary_partial_path, (struct timespec[2]) { ts, ts }, AT_SYMLINK_NOFOLLOW) < 0)
|
||||
return log_error_errno(errno, "Failed to adjust mtime of '%s': %m", t->temporary_partial_path);
|
||||
@ -1508,12 +1513,12 @@ int transfer_acquire_instance(Transfer *t, Instance *i, InstanceMetadata *f, Tra
|
||||
need_sync = true;
|
||||
}
|
||||
|
||||
if (f->mode != MODE_INVALID) {
|
||||
if (f.mode != MODE_INVALID) {
|
||||
/* Try with AT_SYMLINK_NOFOLLOW first, because it's the safe thing to do. Older
|
||||
* kernels don't support that however, in that case we fall back to chmod(). Not as
|
||||
* safe, but shouldn't be a problem, given that we don't create symlinks here. */
|
||||
if (fchmodat(AT_FDCWD, t->temporary_partial_path, f->mode, AT_SYMLINK_NOFOLLOW) < 0 &&
|
||||
(!ERRNO_IS_NOT_SUPPORTED(errno) || chmod(t->temporary_partial_path, f->mode) < 0))
|
||||
if (fchmodat(AT_FDCWD, t->temporary_partial_path, f.mode, AT_SYMLINK_NOFOLLOW) < 0 &&
|
||||
(!ERRNO_IS_NOT_SUPPORTED(errno) || chmod(t->temporary_partial_path, f.mode) < 0))
|
||||
return log_error_errno(errno, "Failed to adjust mode of '%s': %m", t->temporary_partial_path);
|
||||
|
||||
need_sync = true;
|
||||
@ -1531,7 +1536,7 @@ int transfer_acquire_instance(Transfer *t, Instance *i, InstanceMetadata *f, Tra
|
||||
return log_error_errno(r, "Failed to synchronize file system backing '%s': %m", t->temporary_partial_path);
|
||||
}
|
||||
|
||||
t->install_read_only = f->read_only;
|
||||
t->install_read_only = f.read_only;
|
||||
|
||||
/* Rename the file from `.sysupdate.partial.<VERSION>` to `.sysupdate.pending.<VERSION>` to indicate it’s ready to install. */
|
||||
log_debug("Renaming resource instance '%s' to '%s'.", t->temporary_partial_path, t->temporary_pending_path);
|
||||
@ -1552,28 +1557,28 @@ int transfer_acquire_instance(Transfer *t, Instance *i, InstanceMetadata *f, Tra
|
||||
return r;
|
||||
t->partition_change = PARTITION_LABEL;
|
||||
|
||||
if (f->partition_uuid_set) {
|
||||
t->partition_info.uuid = f->partition_uuid;
|
||||
if (f.partition_uuid_set) {
|
||||
t->partition_info.uuid = f.partition_uuid;
|
||||
t->partition_change |= PARTITION_UUID;
|
||||
}
|
||||
|
||||
if (f->partition_flags_set) {
|
||||
t->partition_info.flags = f->partition_flags;
|
||||
if (f.partition_flags_set) {
|
||||
t->partition_info.flags = f.partition_flags;
|
||||
t->partition_change |= PARTITION_FLAGS;
|
||||
}
|
||||
|
||||
if (f->no_auto >= 0) {
|
||||
t->partition_info.no_auto = f->no_auto;
|
||||
if (f.no_auto >= 0) {
|
||||
t->partition_info.no_auto = f.no_auto;
|
||||
t->partition_change |= PARTITION_NO_AUTO;
|
||||
}
|
||||
|
||||
if (f->read_only >= 0) {
|
||||
t->partition_info.read_only = f->read_only;
|
||||
if (f.read_only >= 0) {
|
||||
t->partition_info.read_only = f.read_only;
|
||||
t->partition_change |= PARTITION_READ_ONLY;
|
||||
}
|
||||
|
||||
if (f->growfs >= 0) {
|
||||
t->partition_info.growfs = f->growfs;
|
||||
if (f.growfs >= 0) {
|
||||
t->partition_info.growfs = f.growfs;
|
||||
t->partition_change |= PARTITION_GROWFS;
|
||||
}
|
||||
|
||||
|
||||
@ -66,8 +66,7 @@ int transfer_resolve_paths(Transfer *t, const char *root, const char *node);
|
||||
|
||||
int transfer_vacuum(Transfer *t, uint64_t space, const char *extra_protected_version);
|
||||
|
||||
int transfer_compute_temporary_paths(Transfer *t, Instance *i, InstanceMetadata *f);
|
||||
int transfer_acquire_instance(Transfer *t, Instance *i, InstanceMetadata *f, TransferProgress cb, void *userdata);
|
||||
int transfer_acquire_instance(Transfer *t, Instance *i, TransferProgress cb, void *userdata);
|
||||
int transfer_process_partial_and_pending_instance(Transfer *t, Instance *i);
|
||||
|
||||
int transfer_install_instance(Transfer *t, Instance *i, const char *root);
|
||||
|
||||
@ -1055,23 +1055,6 @@ static int context_acquire(
|
||||
|
||||
log_info("Selected update '%s' for install.", us->version);
|
||||
|
||||
_cleanup_free_ InstanceMetadata *metadata = new0(InstanceMetadata, c->n_transfers);
|
||||
if (!metadata)
|
||||
return log_oom();
|
||||
|
||||
/* Compute up the temporary paths before vacuuming so we don't vacuum anything if we fail to compute
|
||||
* any paths because of failed validations (e.g. exceeding the gpt partition label size). */
|
||||
for (size_t i = 0; i < c->n_transfers; i++) {
|
||||
Instance *inst = us->instances[i];
|
||||
Transfer *t = c->transfers[i];
|
||||
|
||||
assert(inst);
|
||||
|
||||
r = transfer_compute_temporary_paths(t, inst, metadata + i);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
(void) sd_notifyf(/* unset_environment= */ false,
|
||||
"READY=1\n"
|
||||
"X_SYSUPDATE_VERSION=%s\n"
|
||||
@ -1107,7 +1090,7 @@ static int context_acquire(
|
||||
continue;
|
||||
}
|
||||
|
||||
r = transfer_acquire_instance(t, inst, metadata + i, context_on_acquire_progress, c);
|
||||
r = transfer_acquire_instance(t, inst, context_on_acquire_progress, c);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
@ -21,7 +21,6 @@
|
||||
#include "open-file.h"
|
||||
#include "pcre2-util.h"
|
||||
#include "rm-rf.h"
|
||||
#include "service.h"
|
||||
#include "set.h"
|
||||
#include "specifier.h"
|
||||
#include "string-util.h"
|
||||
@ -1034,86 +1033,6 @@ TEST(config_parse_open_file) {
|
||||
ASSERT_NULL(of);
|
||||
}
|
||||
|
||||
TEST(config_parse_service_refresh_on_reload) {
|
||||
ServiceRefreshOnReload flags;
|
||||
_cleanup_strv_free_ char **l = NULL;
|
||||
int r;
|
||||
|
||||
r = service_refresh_on_reload_from_string_many("extensions", &flags);
|
||||
ASSERT_OK(r);
|
||||
ASSERT_EQ(flags, SERVICE_RELOAD_EXTENSIONS);
|
||||
ASSERT_OK(service_refresh_on_reload_to_strv(flags, &l));
|
||||
ASSERT_TRUE(strv_equal(l, STRV_MAKE("extensions")));
|
||||
|
||||
l = strv_free(l);
|
||||
|
||||
r = service_refresh_on_reload_from_string_many("credentials extensions", &flags);
|
||||
ASSERT_OK(r);
|
||||
ASSERT_EQ(flags, SERVICE_RELOAD_EXTENSIONS|SERVICE_RELOAD_CREDENTIALS);
|
||||
ASSERT_OK(service_refresh_on_reload_to_strv(flags, &l));
|
||||
ASSERT_TRUE(strv_equal(l, STRV_MAKE("extensions", "credentials")));
|
||||
|
||||
ASSERT_ERROR(service_refresh_on_reload_from_string_many("hoge", &flags), EINVAL);
|
||||
|
||||
_cleanup_(manager_freep) Manager *m = NULL;
|
||||
_cleanup_(unit_freep) Unit *u = NULL;
|
||||
|
||||
r = manager_new(RUNTIME_SCOPE_USER, MANAGER_TEST_RUN_MINIMAL, &m);
|
||||
if (manager_errno_skip_test(r)) {
|
||||
log_notice_errno(r, "Skipping test: manager_new: %m");
|
||||
return;
|
||||
}
|
||||
|
||||
ASSERT_OK(r);
|
||||
ASSERT_OK(manager_startup(m, NULL, NULL, NULL));
|
||||
|
||||
ASSERT_NOT_NULL(u = unit_new(m, sizeof(Service)));
|
||||
ASSERT_OK_ZERO(unit_add_name(u, "foobar.service"));
|
||||
|
||||
ASSERT_FALSE(SERVICE(u)->refresh_on_reload_set);
|
||||
|
||||
ASSERT_OK(config_parse_service_refresh_on_reload(
|
||||
NULL, "fake", 1, "section", 1,
|
||||
"RefreshOnReload", 0, "no",
|
||||
NULL, u));
|
||||
ASSERT_TRUE(SERVICE(u)->refresh_on_reload_set);
|
||||
ASSERT_EQ(SERVICE(u)->refresh_on_reload_flags, 0);
|
||||
|
||||
ASSERT_OK(config_parse_service_refresh_on_reload(
|
||||
NULL, "fake", 1, "section", 1,
|
||||
"RefreshOnReload", 0, "yes",
|
||||
NULL, u));
|
||||
ASSERT_TRUE(SERVICE(u)->refresh_on_reload_set);
|
||||
ASSERT_EQ(SERVICE(u)->refresh_on_reload_flags, _SERVICE_REFRESH_ON_RELOAD_ALL);
|
||||
|
||||
ASSERT_OK(config_parse_service_refresh_on_reload(
|
||||
NULL, "fake", 1, "section", 1,
|
||||
"RefreshOnReload", 0, "~extensions",
|
||||
NULL, u));
|
||||
ASSERT_TRUE(SERVICE(u)->refresh_on_reload_set);
|
||||
ASSERT_EQ(SERVICE(u)->refresh_on_reload_flags, _SERVICE_REFRESH_ON_RELOAD_ALL & ~SERVICE_RELOAD_EXTENSIONS);
|
||||
|
||||
ASSERT_OK(config_parse_service_refresh_on_reload(
|
||||
NULL, "fake", 1, "section", 1,
|
||||
"RefreshOnReload", 0, "~extensions credentials",
|
||||
NULL, u));
|
||||
ASSERT_TRUE(SERVICE(u)->refresh_on_reload_set);
|
||||
ASSERT_EQ(SERVICE(u)->refresh_on_reload_flags, 0);
|
||||
|
||||
ASSERT_OK(config_parse_service_refresh_on_reload(
|
||||
NULL, "fake", 1, "section", 1,
|
||||
"RefreshOnReload", 0, "",
|
||||
NULL, u));
|
||||
ASSERT_FALSE(SERVICE(u)->refresh_on_reload_set);
|
||||
|
||||
ASSERT_OK(config_parse_service_refresh_on_reload(
|
||||
NULL, "fake", 1, "section", 1,
|
||||
"RefreshOnReload", 0, "~extensions",
|
||||
NULL, u));
|
||||
ASSERT_TRUE(SERVICE(u)->refresh_on_reload_set);
|
||||
ASSERT_EQ(SERVICE(u)->refresh_on_reload_flags, SERVICE_REFRESH_ON_RELOAD_DEFAULT & ~SERVICE_RELOAD_EXTENSIONS);
|
||||
}
|
||||
|
||||
static int intro(void) {
|
||||
if (enter_cgroup_subroot(NULL) == -ENOMEDIUM)
|
||||
return log_tests_skipped("cgroupfs not available");
|
||||
|
||||
@ -1,29 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
# shellcheck disable=SC2016
|
||||
set -eux
|
||||
set -o pipefail
|
||||
|
||||
OUTPUT_FILE="$1"
|
||||
|
||||
dump_creds_tree() {
|
||||
grep . "$CREDENTIALS_DIRECTORY"/* >"$OUTPUT_FILE"
|
||||
}
|
||||
|
||||
on_sighup() {
|
||||
systemd-notify --reloading
|
||||
dump_creds_tree
|
||||
systemd-notify --ready
|
||||
}
|
||||
|
||||
trap on_sighup SIGHUP
|
||||
|
||||
export SYSTEMD_LOG_LEVEL=debug
|
||||
|
||||
dump_creds_tree
|
||||
systemd-notify --ready
|
||||
|
||||
sleep infinity &
|
||||
while :; do
|
||||
wait || :
|
||||
done
|
||||
@ -351,7 +351,6 @@ if install_tests
|
||||
'integration-tests/TEST-30-ONCLOCKCHANGE/TEST-30-ONCLOCKCHANGE.units',
|
||||
'integration-tests/TEST-38-FREEZER/TEST-38-FREEZER.units',
|
||||
'integration-tests/TEST-52-HONORFIRSTSHUTDOWN/TEST-52-HONORFIRSTSHUTDOWN.units',
|
||||
'integration-tests/TEST-54-CREDS/TEST-54-CREDS.units',
|
||||
'integration-tests/TEST-55-OOMD/TEST-55-OOMD.units',
|
||||
'integration-tests/TEST-62-RESTRICT-IFACES/TEST-62-RESTRICT-IFACES.units',
|
||||
'integration-tests/TEST-63-PATH/TEST-63-PATH.units',
|
||||
|
||||
@ -127,19 +127,6 @@ test -L /run/systemd/system.attached/app0.service.d/10-profile.conf
|
||||
test -L /run/systemd/system.attached/app1.service.d/10-profile.conf
|
||||
portablectl detach --runtime --extension /tmp/app0 --extension /tmp/app1 /tmp/rootdir app0 app1
|
||||
|
||||
# Ensure that --force works with directory extensions, and that ExtensionDirectories=
|
||||
# is not decorated with :x-systemd.relax-extension-release-check
|
||||
portablectl "${ARGS[@]}" attach --force --copy=symlink --now --runtime --extension /tmp/app0 /tmp/rootdir app0
|
||||
|
||||
systemctl is-active app0.service
|
||||
status="$(portablectl is-attached --extension app0 rootdir)"
|
||||
[[ "${status}" == "running-runtime" ]]
|
||||
|
||||
grep -q -F "ExtensionDirectories=" /run/systemd/system.attached/app0.service.d/20-portable.conf
|
||||
(! grep -q -F "x-systemd.relax-extension-release-check" /run/systemd/system.attached/app0.service.d/20-portable.conf)
|
||||
|
||||
portablectl detach --now --runtime --extension /tmp/app0 /tmp/rootdir app0
|
||||
|
||||
# Attempt to disable the app unit during detaching. Requires --copy=symlink to reproduce.
|
||||
# Provides coverage for https://github.com/systemd/systemd/issues/23481
|
||||
portablectl "${ARGS[@]}" attach --copy=symlink --now --runtime /tmp/rootdir minimal-app0
|
||||
|
||||
@ -189,10 +189,6 @@ systemctl is-active app0.service
|
||||
status="$(portablectl is-attached --extension /tmp/app10.raw /usr/share/minimal_0.raw)"
|
||||
[[ "${status}" == "running-runtime" ]]
|
||||
|
||||
# Ensure --force adds relax-extension-release-check for image extensions
|
||||
grep -q -F "ExtensionImages=" /run/systemd/system.attached/app0.service.d/20-portable.conf
|
||||
grep -q -F "ExtensionImagePolicy=" /run/systemd/system.attached/app0.service.d/20-portable.conf
|
||||
|
||||
portablectl inspect --force --cat --extension /tmp/app10.raw /usr/share/minimal_0.raw app0 | grep -F "Extension Release: /tmp/app10.raw" >/dev/null
|
||||
|
||||
# Ensure that we can detach even when an image has been deleted already (stop the unit manually as
|
||||
@ -249,19 +245,3 @@ status="$(portablectl is-attached --extension app1 minimal_0)"
|
||||
[[ "${status}" == "attached-runtime" ]]
|
||||
|
||||
portablectl detach --runtime --extension /tmp/app1.raw /usr/share/minimal_0.raw app
|
||||
|
||||
# Ensure that when mixing directory and image extensions, ExtensionImagePolicy= is only
|
||||
# applied to image extensions and not to directory extensions
|
||||
mkdir -p /tmp/app1
|
||||
mount /tmp/app1.raw /tmp/app1
|
||||
portablectl "${ARGS[@]}" attach --copy=symlink --now --runtime --extension /tmp/app1 --extension /tmp/app0.raw /usr/share/minimal_0.raw app0
|
||||
|
||||
systemctl is-active app0.service
|
||||
|
||||
grep -q -F "ExtensionDirectories=/tmp/app1" /run/systemd/system.attached/app0.service.d/20-portable.conf
|
||||
grep -q -F "ExtensionImages=/tmp/app0.raw" /run/systemd/system.attached/app0.service.d/20-portable.conf
|
||||
# ExtensionImagePolicy= should appear exactly once (for the image, not the directory)
|
||||
[[ "$(grep -c -F "ExtensionImagePolicy=" /run/systemd/system.attached/app0.service.d/20-portable.conf)" == "1" ]]
|
||||
|
||||
portablectl detach --now --runtime --extension /tmp/app1 --extension /tmp/app0.raw /usr/share/minimal_0.raw app0
|
||||
umount -l /tmp/app1
|
||||
|
||||
@ -571,106 +571,4 @@ systemd-run -M testuser@ --user --wait -p ImportCredential=brummbaer \
|
||||
|
||||
kill "$PID"
|
||||
|
||||
# Now test credential refreshing
|
||||
|
||||
UNIT_NAME="TEST-54-CREDS-refreshing-$RANDOM.service"
|
||||
OUTPUT_FILE="/tmp/$UNIT_NAME.out"
|
||||
POST_FLAG_FILE="/tmp/$UNIT_NAME.post-flag"
|
||||
|
||||
cat >/run/systemd/system/"$UNIT_NAME" <<EOF
|
||||
[Service]
|
||||
Type=notify-reload
|
||||
ImportCredential=test.creds.*
|
||||
ExecStart=/usr/lib/systemd/tests/testdata/TEST-54-CREDS.units/refresh.sh $OUTPUT_FILE
|
||||
EOF
|
||||
|
||||
systemctl start "$UNIT_NAME"
|
||||
ls -l /run/credentials/"$UNIT_NAME"/
|
||||
|
||||
echo "neu" >/run/credstore/test.creds.new-refresh-1
|
||||
[[ ! -e /run/credentials/"$UNIT_NAME"/test.creds.new-refresh-1 ]]
|
||||
|
||||
systemctl reload "$UNIT_NAME"
|
||||
[[ ! -e /run/credentials/"$UNIT_NAME"/test.creds.new-refresh-1 ]]
|
||||
(! grep -q "test.creds.new-refresh-1" "$OUTPUT_FILE")
|
||||
|
||||
echo "RefreshOnReload=credentials" >>/run/systemd/system/"$UNIT_NAME"
|
||||
systemctl daemon-reload
|
||||
systemctl reload "$UNIT_NAME"
|
||||
diff /run/credstore/test.creds.new-refresh-1 /run/credentials/"$UNIT_NAME"/test.creds.new-refresh-1
|
||||
diff "$OUTPUT_FILE" <(grep . /run/credentials/"$UNIT_NAME"/*)
|
||||
|
||||
systemctl stop "$UNIT_NAME"
|
||||
cat >>/run/systemd/system/"$UNIT_NAME" <<EOF
|
||||
ProtectSystem=strict
|
||||
ReadWritePaths=/tmp
|
||||
ExecReloadPost=bash -c 'diff $OUTPUT_FILE <(grep . %d/*) || rm -v $POST_FLAG_FILE'
|
||||
EOF
|
||||
|
||||
systemctl daemon-reload
|
||||
systemctl start "$UNIT_NAME"
|
||||
diff /run/credstore/test.creds.new-refresh-1 /run/credentials/"$UNIT_NAME"/test.creds.new-refresh-1
|
||||
diff "$OUTPUT_FILE" <(grep . /run/credentials/"$UNIT_NAME"/*)
|
||||
|
||||
systemctl reload "$UNIT_NAME"
|
||||
diff "$OUTPUT_FILE" <(grep . /run/credentials/"$UNIT_NAME"/*)
|
||||
|
||||
echo "2" >/run/credstore/test.creds.new-refresh-2
|
||||
[[ ! -e /run/credentials/"$UNIT_NAME"/test.creds.new-refresh-2 ]]
|
||||
|
||||
systemctl reload "$UNIT_NAME"
|
||||
diff /run/credstore/test.creds.new-refresh-2 /run/credentials/"$UNIT_NAME"/test.creds.new-refresh-2
|
||||
diff "$OUTPUT_FILE" <(grep . /run/credentials/"$UNIT_NAME"/*)
|
||||
|
||||
echo "3" >/run/credstore/test.creds.new-refresh-3
|
||||
[[ ! -e /run/credentials/"$UNIT_NAME"/test.creds.new-refresh-3 ]]
|
||||
|
||||
rm "$OUTPUT_FILE"
|
||||
systemctl edit --runtime --stdin "$UNIT_NAME" <<EOF
|
||||
[Service]
|
||||
Type=notify
|
||||
ExecReloadPost=
|
||||
EOF
|
||||
systemctl reload "$UNIT_NAME"
|
||||
diff /run/credstore/test.creds.new-refresh-3 /run/credentials/"$UNIT_NAME"/test.creds.new-refresh-3
|
||||
[[ ! -e "$OUTPUT_FILE" ]]
|
||||
|
||||
echo "RefreshOnReload=no" >>/run/systemd/system/"$UNIT_NAME"
|
||||
systemctl daemon-reload
|
||||
assert_eq "$(systemctl show "$UNIT_NAME" -P CanReload)" "no"
|
||||
systemctl revert "$UNIT_NAME"
|
||||
assert_eq "$(systemctl show "$UNIT_NAME" -P CanReload)" "yes"
|
||||
|
||||
echo "BOGUS" >/run/credstore/test.creds.refresh-bogus
|
||||
touch "$POST_FLAG_FILE"
|
||||
systemctl reload "$UNIT_NAME"
|
||||
diff /run/credstore/test.creds.new-refresh-3 /run/credentials/"$UNIT_NAME"/test.creds.new-refresh-3
|
||||
[[ ! -e /run/credentials/"$UNIT_NAME"/test.creds.refresh-bogus ]]
|
||||
diff "$OUTPUT_FILE" <(grep . /run/credentials/"$UNIT_NAME"/*)
|
||||
[[ ! -e "$POST_FLAG_FILE" ]]
|
||||
|
||||
OUTPUT_FILE_USER="/tmp/TEST-54-CREDS-refreshing-user.out"
|
||||
|
||||
systemd-notify --fork -- \
|
||||
systemd-run -M testuser@ --user --wait \
|
||||
--unit=brummbaer-refresh.service \
|
||||
--service-type=notify-reload \
|
||||
-p NotifyAccess=all \
|
||||
-p 'ImportCredential=brummbaer*' \
|
||||
-p RefreshOnReload=credentials \
|
||||
-p ProtectSystem=strict \
|
||||
-p ReadWritePaths=/tmp \
|
||||
/usr/lib/systemd/tests/testdata/TEST-54-CREDS.units/refresh.sh "$OUTPUT_FILE_USER"
|
||||
|
||||
[[ -f "$TESTUSER_CRED_DIR/brummbaer-refresh.service/brummbaer" ]]
|
||||
diff "$OUTPUT_FILE_USER" <(grep . "$TESTUSER_CRED_DIR"/brummbaer-refresh.service/*)
|
||||
|
||||
run0 -u testuser --pipe -i \
|
||||
--property=EnvironmentFile=-/usr/lib/systemd/systemd-asan-env \
|
||||
'mkdir -p .config/credstore && echo "refreshed" >.config/credstore/brummbaer.refreshed'
|
||||
|
||||
systemctl -M testuser@ --user reload brummbaer-refresh.service
|
||||
assert_eq "$(cat "$TESTUSER_CRED_DIR"/brummbaer-refresh.service/brummbaer.refreshed)" "refreshed"
|
||||
diff "$OUTPUT_FILE_USER" <(grep . "$TESTUSER_CRED_DIR"/brummbaer-refresh.service/*)
|
||||
|
||||
touch /testok
|
||||
|
||||
@ -204,7 +204,7 @@ MatchPattern=part2-@v.raw.gz
|
||||
[Target]
|
||||
Type=partition
|
||||
Path=$blockdev
|
||||
MatchPattern=a-very-long-partition-name-@v
|
||||
MatchPattern=part2-@v
|
||||
MatchPartitionType=root-x86-64-verity
|
||||
EOF
|
||||
|
||||
@ -397,7 +397,7 @@ MatchPattern=part2-@v.raw.gz
|
||||
[Target]
|
||||
Type=partition
|
||||
Path=$blockdev
|
||||
MatchPattern=a-very-long-partition-name-@v
|
||||
MatchPattern=part2-@v
|
||||
MatchPartitionType=root-x86-64-verity
|
||||
EOF
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user