Compare commits
8 Commits
cad6727906
...
6eb35fd695
Author | SHA1 | Date |
---|---|---|
Lennart Poettering | 6eb35fd695 | |
Zbigniew Jędrzejewski-Szmek | b76ef59756 | |
Kumar Kartikeya Dwivedi | 5ec7a9947e | |
Kumar Kartikeya Dwivedi | 4f07ddfa9b | |
Michal Sekletár | d446ae89c0 | |
Michal Sekletár | d9e45bc3ab | |
Michal Sekletár | 25a1f04c68 | |
Michal Sekletár | 08deac6e3e |
4
NEWS
4
NEWS
|
@ -10,6 +10,10 @@ CHANGES WITH 246 in spe:
|
||||||
their first output column with --no-legend. To hide the first column,
|
their first output column with --no-legend. To hide the first column,
|
||||||
use --plain.
|
use --plain.
|
||||||
|
|
||||||
|
* The service manager gained basic support for cgroup v2 freezer. Units
|
||||||
|
can now be suspended or resumed either using new systemctl verbs,
|
||||||
|
freeze and thaw respectively, or via D-Bus.
|
||||||
|
|
||||||
CHANGES WITH 245:
|
CHANGES WITH 245:
|
||||||
|
|
||||||
* A new tool "systemd-repart" has been added, that operates as an
|
* A new tool "systemd-repart" has been added, that operates as an
|
||||||
|
|
|
@ -718,7 +718,7 @@ manpages = [
|
||||||
['sd_machine_get_class', '3', ['sd_machine_get_ifindices'], ''],
|
['sd_machine_get_class', '3', ['sd_machine_get_ifindices'], ''],
|
||||||
['sd_notify',
|
['sd_notify',
|
||||||
'3',
|
'3',
|
||||||
['sd_notifyf', 'sd_pid_notify', 'sd_pid_notify_with_fds', 'sd_pid_notifyf'],
|
['sd_notifyf', 'sd_pid_notify', 'sd_pid_notify_with_fds', 'sd_pid_notifyf', 'sd_notify_barrier'],
|
||||||
''],
|
''],
|
||||||
['sd_path_lookup', '3', ['sd_path_lookup_strv'], ''],
|
['sd_path_lookup', '3', ['sd_path_lookup_strv'], ''],
|
||||||
['sd_pid_get_owner_uid',
|
['sd_pid_get_owner_uid',
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
<refname>sd_pid_notify</refname>
|
<refname>sd_pid_notify</refname>
|
||||||
<refname>sd_pid_notifyf</refname>
|
<refname>sd_pid_notifyf</refname>
|
||||||
<refname>sd_pid_notify_with_fds</refname>
|
<refname>sd_pid_notify_with_fds</refname>
|
||||||
|
<refname>sd_notify_barrier</refname>
|
||||||
<refpurpose>Notify service manager about start-up completion and other service status changes</refpurpose>
|
<refpurpose>Notify service manager about start-up completion and other service status changes</refpurpose>
|
||||||
</refnamediv>
|
</refnamediv>
|
||||||
|
|
||||||
|
@ -65,6 +66,12 @@
|
||||||
<paramdef>const int *<parameter>fds</parameter></paramdef>
|
<paramdef>const int *<parameter>fds</parameter></paramdef>
|
||||||
<paramdef>unsigned <parameter>n_fds</parameter></paramdef>
|
<paramdef>unsigned <parameter>n_fds</parameter></paramdef>
|
||||||
</funcprototype>
|
</funcprototype>
|
||||||
|
|
||||||
|
<funcprototype>
|
||||||
|
<funcdef>int <function>sd_notify_barrier</function></funcdef>
|
||||||
|
<paramdef>int <parameter>unset_environment</parameter></paramdef>
|
||||||
|
<paramdef>uint64_t <parameter>timeout</parameter></paramdef>
|
||||||
|
</funcprototype>
|
||||||
</funcsynopsis>
|
</funcsynopsis>
|
||||||
</refsynopsisdiv>
|
</refsynopsisdiv>
|
||||||
|
|
||||||
|
@ -261,6 +268,17 @@
|
||||||
as prematurely discarding file descriptors from the store.</para></listitem>
|
as prematurely discarding file descriptors from the store.</para></listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term>BARRIER=1</term>
|
||||||
|
|
||||||
|
<listitem><para>Tells the service manager that the client is explicitly requesting synchronization by means of
|
||||||
|
closing the file descriptor sent with this command. The service manager gurantees that the processing of a <varname>
|
||||||
|
BARRIER=1</varname> command will only happen after all previous notification messages sent before this command
|
||||||
|
have been processed. Hence, this command accompanied with a single file descriptor can be used to synchronize
|
||||||
|
against reception of all previous status messages. Note that this command cannot be mixed with other notifications,
|
||||||
|
and has to be sent in a separate message to the service manager, otherwise all assignments will be ignored. Note that
|
||||||
|
sending 0 or more than 1 file descriptor with this command is a violation of the protocol.</para></listitem>
|
||||||
|
</varlistentry>
|
||||||
</variablelist>
|
</variablelist>
|
||||||
|
|
||||||
<para>It is recommended to prefix variable names that are not
|
<para>It is recommended to prefix variable names that are not
|
||||||
|
@ -282,6 +300,13 @@
|
||||||
attribute the message to the unit, and thus will ignore it, even if
|
attribute the message to the unit, and thus will ignore it, even if
|
||||||
<varname>NotifyAccess=</varname><option>all</option> is set for it.</para>
|
<varname>NotifyAccess=</varname><option>all</option> is set for it.</para>
|
||||||
|
|
||||||
|
<para>Hence, to eliminate all race conditions involving lookup of the client's unit and attribution of notifications
|
||||||
|
to units correctly, <function>sd_notify_barrier()</function> may be used. This call acts as a synchronization point
|
||||||
|
and ensures all notifications sent before this call have been picked up by the service manager when it returns
|
||||||
|
successfully. Use of <function>sd_notify_barrier()</function> is needed for clients which are not invoked by the
|
||||||
|
service manager, otherwise this synchronization mechanism is unnecessary for attribution of notifications to the
|
||||||
|
unit.</para>
|
||||||
|
|
||||||
<para><function>sd_notifyf()</function> is similar to
|
<para><function>sd_notifyf()</function> is similar to
|
||||||
<function>sd_notify()</function> but takes a
|
<function>sd_notify()</function> but takes a
|
||||||
<function>printf()</function>-like format string plus
|
<function>printf()</function>-like format string plus
|
||||||
|
@ -312,6 +337,14 @@
|
||||||
to the service manager on messages that do not expect them (i.e.
|
to the service manager on messages that do not expect them (i.e.
|
||||||
without <literal>FDSTORE=1</literal>) they are immediately closed
|
without <literal>FDSTORE=1</literal>) they are immediately closed
|
||||||
on reception.</para>
|
on reception.</para>
|
||||||
|
|
||||||
|
<para><function>sd_notify_barrier()</function> allows the caller to
|
||||||
|
synchronize against reception of previously sent notification messages
|
||||||
|
and uses the <literal>BARRIER=1</literal> command. It takes a relative
|
||||||
|
<varname>timeout</varname> value in microseconds which is passed to
|
||||||
|
<citerefentry><refentrytitle>ppoll</refentrytitle><manvolnum>2</manvolnum>
|
||||||
|
</citerefentry>. A value of UINT64_MAX is interpreted as infinite timeout.
|
||||||
|
</para>
|
||||||
</refsect1>
|
</refsect1>
|
||||||
|
|
||||||
<refsect1>
|
<refsect1>
|
||||||
|
@ -402,6 +435,22 @@
|
||||||
|
|
||||||
<programlisting>sd_pid_notify_with_fds(0, 0, "FDSTORE=1\nFDNAME=foobar", &fd, 1);</programlisting>
|
<programlisting>sd_pid_notify_with_fds(0, 0, "FDSTORE=1\nFDNAME=foobar", &fd, 1);</programlisting>
|
||||||
</example>
|
</example>
|
||||||
|
|
||||||
|
<example>
|
||||||
|
<title>Eliminating race conditions</title>
|
||||||
|
|
||||||
|
<para>When the client sending the notifications is not spawned
|
||||||
|
by the service manager, it may exit too quickly and the service
|
||||||
|
manager may fail to attribute them correctly to the unit. To
|
||||||
|
prevent such races, use <function>sd_notify_barrier()</function>
|
||||||
|
to synchronize against reception of all notifications sent before
|
||||||
|
this call is made.</para>
|
||||||
|
|
||||||
|
<programlisting>sd_notify(0, "READY=1");
|
||||||
|
/* set timeout to 5 seconds */
|
||||||
|
sd_notify_barrier(0, 5 * 1000000);
|
||||||
|
</programlisting>
|
||||||
|
</example>
|
||||||
</refsect1>
|
</refsect1>
|
||||||
|
|
||||||
<refsect1>
|
<refsect1>
|
||||||
|
|
|
@ -303,6 +303,30 @@ Sun 2017-02-26 20:57:49 EST 2h 3min left Sun 2017-02-26 11:56:36 EST 6h ago
|
||||||
generally redundant and reproducible on the next invocation of the unit).</para>
|
generally redundant and reproducible on the next invocation of the unit).</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
<varlistentry>
|
||||||
|
<term><command>freeze <replaceable>PATTERN</replaceable>…</command></term>
|
||||||
|
|
||||||
|
<listitem>
|
||||||
|
<para>Freeze one or more units specified on the
|
||||||
|
command line using cgroup freezer</para>
|
||||||
|
|
||||||
|
<para>Freezing the unit will cause all processes contained within the cgroup corresponding to the unit
|
||||||
|
to be suspended. Being suspended means that unit's processes won't be scheduled to run on CPU until thawed.
|
||||||
|
Note that this command is supported only on systems that use unified cgroup hierarchy. Unit is automatically
|
||||||
|
thawed just before we execute a job against the unit, e.g. before the unit is stopped.</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
<varlistentry>
|
||||||
|
<term><command>thaw <replaceable>PATTERN</replaceable>…</command></term>
|
||||||
|
|
||||||
|
<listitem>
|
||||||
|
<para>Thaw (unfreeze) one or more units specified on the
|
||||||
|
command line.</para>
|
||||||
|
|
||||||
|
<para>This is the inverse operation to the <command>freeze</command> command and resumes the execution of
|
||||||
|
processes in the unit's cgroup.</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
<varlistentry>
|
<varlistentry>
|
||||||
<term><command>is-active <replaceable>PATTERN</replaceable>…</command></term>
|
<term><command>is-active <replaceable>PATTERN</replaceable>…</command></term>
|
||||||
|
|
||||||
|
|
|
@ -54,15 +54,19 @@
|
||||||
off the process, i.e. on all processes that match <varname>NotifyAccess=</varname><option>main</option> or
|
off the process, i.e. on all processes that match <varname>NotifyAccess=</varname><option>main</option> or
|
||||||
<varname>NotifyAccess=</varname><option>exec</option>. Conversely, if an auxiliary process of the unit sends an
|
<varname>NotifyAccess=</varname><option>exec</option>. Conversely, if an auxiliary process of the unit sends an
|
||||||
<function>sd_notify()</function> message and immediately exits, the service manager might not be able to properly
|
<function>sd_notify()</function> message and immediately exits, the service manager might not be able to properly
|
||||||
attribute the message to the unit, and thus will ignore it, even if
|
attribute the message to the unit, and thus will ignore it, even if <varname>NotifyAccess=</varname><option>all
|
||||||
<varname>NotifyAccess=</varname><option>all</option> is set for it.</para>
|
</option> is set for it. When <option>--no-block</option> is used, all synchronization for reception of notifications
|
||||||
|
is disabled, and hence the aforementioned race may occur if the invoking process is not the service manager or spawned
|
||||||
|
by the service manager.</para>
|
||||||
|
|
||||||
|
<para>Hence, <command>systemd-notify</command> will first attempt to invoke <function>sd_notify()</function>
|
||||||
|
pretending to have the PID of the invoking process. This will only succeed when invoked with sufficient privileges.
|
||||||
|
On failure, it will then fall back to invoking it under its own PID. This behaviour is useful in order that when
|
||||||
|
the tool is invoked from a shell script the shell process — and not the <command>systemd-notify</command> process
|
||||||
|
— appears as sender of the message, which in turn is helpful if the shell process is the main process of a service,
|
||||||
|
due to the limitations of <varname>NotifyAccess=</varname><option>all</option>. Use the <option>--pid=</option>
|
||||||
|
switch to tweak this behaviour.</para>
|
||||||
|
|
||||||
<para><command>systemd-notify</command> will first attempt to invoke <function>sd_notify()</function> pretending to
|
|
||||||
have the PID of the invoking process. This will only succeed when invoked with sufficient privileges. On failure,
|
|
||||||
it will then fall back to invoking it under its own PID. This behaviour is useful in order that when the tool is
|
|
||||||
invoked from a shell script the shell process — and not the <command>systemd-notify</command> process — appears as
|
|
||||||
sender of the message, which in turn is helpful if the shell process is the main process of a service, due to the
|
|
||||||
limitations of <varname>NotifyAccess=</varname><option>all</option> described above.</para>
|
|
||||||
</refsect1>
|
</refsect1>
|
||||||
|
|
||||||
<refsect1>
|
<refsect1>
|
||||||
|
@ -129,6 +133,17 @@
|
||||||
with systemd. </para></listitem>
|
with systemd. </para></listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term><option>--no-block</option></term>
|
||||||
|
|
||||||
|
<listitem><para>Do not synchronously wait for the requested operation to finish.
|
||||||
|
Use of this option is only recommended when <command>systemd-notify</command>
|
||||||
|
is spawned by the service manager, or when the invoking process is directly spawned
|
||||||
|
by the service manager and has enough privileges to allow <command>systemd-notify
|
||||||
|
</command> to send the notification on its behalf. Sending notifications with
|
||||||
|
this option set is prone to race conditions in all other cases.</para></listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
<xi:include href="standard-options.xml" xpointer="help" />
|
<xi:include href="standard-options.xml" xpointer="help" />
|
||||||
<xi:include href="standard-options.xml" xpointer="version" />
|
<xi:include href="standard-options.xml" xpointer="version" />
|
||||||
</variablelist>
|
</variablelist>
|
||||||
|
|
|
@ -959,7 +959,14 @@
|
||||||
<option>exec</option>. Conversely, if an auxiliary process of the unit sends an
|
<option>exec</option>. Conversely, if an auxiliary process of the unit sends an
|
||||||
<function>sd_notify()</function> message and immediately exits, the service manager might not be able to
|
<function>sd_notify()</function> message and immediately exits, the service manager might not be able to
|
||||||
properly attribute the message to the unit, and thus will ignore it, even if
|
properly attribute the message to the unit, and thus will ignore it, even if
|
||||||
<varname>NotifyAccess=</varname><option>all</option> is set for it.</para></listitem>
|
<varname>NotifyAccess=</varname><option>all</option> is set for it.</para>
|
||||||
|
|
||||||
|
<para>Hence, to eliminate all race conditions involving lookup of the client's unit and attribution of notifications
|
||||||
|
to units correctly, <function>sd_notify_barrier()</function> may be used. This call acts as a synchronization point
|
||||||
|
and ensures all notifications sent before this call have been picked up by the service manager when it returns
|
||||||
|
successfully. Use of <function>sd_notify_barrier()</function> is needed for clients which are not invoked by the
|
||||||
|
service manager, otherwise this synchronization mechanism is unnecessary for attribution of notifications to the
|
||||||
|
unit.</para></listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|
||||||
<varlistentry>
|
<varlistentry>
|
||||||
|
|
|
@ -257,7 +257,7 @@
|
||||||
execution compared to the target unit's state and is marked successful and
|
execution compared to the target unit's state and is marked successful and
|
||||||
complete when both satisfy. However, this job also pulls in other
|
complete when both satisfy. However, this job also pulls in other
|
||||||
dependencies due to the defined relationships and thus leads to, in our
|
dependencies due to the defined relationships and thus leads to, in our
|
||||||
our example, start jobs for any of those inactive units getting queued as
|
example, start jobs for any of those inactive units getting queued as
|
||||||
well.</para>
|
well.</para>
|
||||||
|
|
||||||
<para>systemd contains native implementations of various tasks
|
<para>systemd contains native implementations of various tasks
|
||||||
|
|
|
@ -149,6 +149,17 @@ bool cg_ns_supported(void) {
|
||||||
return enabled;
|
return enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool cg_freezer_supported(void) {
|
||||||
|
static thread_local int supported = -1;
|
||||||
|
|
||||||
|
if (supported >= 0)
|
||||||
|
return supported;
|
||||||
|
|
||||||
|
supported = cg_all_unified() > 0 && access("/sys/fs/cgroup/init.scope/cgroup.freeze", F_OK) == 0;
|
||||||
|
|
||||||
|
return supported;
|
||||||
|
}
|
||||||
|
|
||||||
int cg_enumerate_subgroups(const char *controller, const char *path, DIR **_d) {
|
int cg_enumerate_subgroups(const char *controller, const char *path, DIR **_d) {
|
||||||
_cleanup_free_ char *fs = NULL;
|
_cleanup_free_ char *fs = NULL;
|
||||||
int r;
|
int r;
|
||||||
|
@ -1684,12 +1695,13 @@ int cg_get_attribute_as_uint64(const char *controller, const char *path, const c
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int cg_get_keyed_attribute(
|
int cg_get_keyed_attribute_full(
|
||||||
const char *controller,
|
const char *controller,
|
||||||
const char *path,
|
const char *path,
|
||||||
const char *attribute,
|
const char *attribute,
|
||||||
char **keys,
|
char **keys,
|
||||||
char **ret_values) {
|
char **ret_values,
|
||||||
|
CGroupKeyMode mode) {
|
||||||
|
|
||||||
_cleanup_free_ char *filename = NULL, *contents = NULL;
|
_cleanup_free_ char *filename = NULL, *contents = NULL;
|
||||||
const char *p;
|
const char *p;
|
||||||
|
@ -1701,7 +1713,8 @@ int cg_get_keyed_attribute(
|
||||||
* all keys to retrieve. The 'ret_values' parameter should be passed as string size with the same number of
|
* all keys to retrieve. The 'ret_values' parameter should be passed as string size with the same number of
|
||||||
* entries as 'keys'. On success each entry will be set to the value of the matching key.
|
* entries as 'keys'. On success each entry will be set to the value of the matching key.
|
||||||
*
|
*
|
||||||
* If the attribute file doesn't exist at all returns ENOENT, if any key is not found returns ENXIO. */
|
* If the attribute file doesn't exist at all returns ENOENT, if any key is not found returns ENXIO. If mode
|
||||||
|
* is set to GG_KEY_MODE_GRACEFUL we ignore missing keys and return those that were parsed successfully. */
|
||||||
|
|
||||||
r = cg_get_path(controller, path, attribute, &filename);
|
r = cg_get_path(controller, path, attribute, &filename);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
|
@ -1749,6 +1762,9 @@ int cg_get_keyed_attribute(
|
||||||
p += strspn(p, NEWLINE);
|
p += strspn(p, NEWLINE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mode & CG_KEY_MODE_GRACEFUL)
|
||||||
|
goto done;
|
||||||
|
|
||||||
r = -ENXIO;
|
r = -ENXIO;
|
||||||
|
|
||||||
fail:
|
fail:
|
||||||
|
@ -1759,6 +1775,9 @@ fail:
|
||||||
|
|
||||||
done:
|
done:
|
||||||
memcpy(ret_values, v, sizeof(char*) * n);
|
memcpy(ret_values, v, sizeof(char*) * n);
|
||||||
|
if (mode & CG_KEY_MODE_GRACEFUL)
|
||||||
|
return n_done;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -180,9 +180,31 @@ int cg_pid_get_path(const char *controller, pid_t pid, char **path);
|
||||||
|
|
||||||
int cg_rmdir(const char *controller, const char *path);
|
int cg_rmdir(const char *controller, const char *path);
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
CG_KEY_MODE_GRACEFUL = 1 << 0,
|
||||||
|
} CGroupKeyMode;
|
||||||
|
|
||||||
int cg_set_attribute(const char *controller, const char *path, const char *attribute, const char *value);
|
int cg_set_attribute(const char *controller, const char *path, const char *attribute, const char *value);
|
||||||
int cg_get_attribute(const char *controller, const char *path, const char *attribute, char **ret);
|
int cg_get_attribute(const char *controller, const char *path, const char *attribute, char **ret);
|
||||||
int cg_get_keyed_attribute(const char *controller, const char *path, const char *attribute, char **keys, char **values);
|
int cg_get_keyed_attribute_full(const char *controller, const char *path, const char *attribute, char **keys, char **values, CGroupKeyMode mode);
|
||||||
|
|
||||||
|
static inline int cg_get_keyed_attribute(
|
||||||
|
const char *controller,
|
||||||
|
const char *path,
|
||||||
|
const char *attribute,
|
||||||
|
char **keys,
|
||||||
|
char **ret_values) {
|
||||||
|
return cg_get_keyed_attribute_full(controller, path, attribute, keys, ret_values, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int cg_get_keyed_attribute_graceful(
|
||||||
|
const char *controller,
|
||||||
|
const char *path,
|
||||||
|
const char *attribute,
|
||||||
|
char **keys,
|
||||||
|
char **ret_values) {
|
||||||
|
return cg_get_keyed_attribute_full(controller, path, attribute, keys, ret_values, CG_KEY_MODE_GRACEFUL);
|
||||||
|
}
|
||||||
|
|
||||||
int cg_get_attribute_as_uint64(const char *controller, const char *path, const char *attribute, uint64_t *ret);
|
int cg_get_attribute_as_uint64(const char *controller, const char *path, const char *attribute, uint64_t *ret);
|
||||||
|
|
||||||
|
@ -238,6 +260,7 @@ int cg_mask_to_string(CGroupMask mask, char **ret);
|
||||||
int cg_kernel_controllers(Set **controllers);
|
int cg_kernel_controllers(Set **controllers);
|
||||||
|
|
||||||
bool cg_ns_supported(void);
|
bool cg_ns_supported(void);
|
||||||
|
bool cg_freezer_supported(void);
|
||||||
|
|
||||||
int cg_all_unified(void);
|
int cg_all_unified(void);
|
||||||
int cg_hybrid_unified(void);
|
int cg_hybrid_unified(void);
|
||||||
|
|
|
@ -108,6 +108,15 @@ static const char* const unit_active_state_table[_UNIT_ACTIVE_STATE_MAX] = {
|
||||||
|
|
||||||
DEFINE_STRING_TABLE_LOOKUP(unit_active_state, UnitActiveState);
|
DEFINE_STRING_TABLE_LOOKUP(unit_active_state, UnitActiveState);
|
||||||
|
|
||||||
|
static const char* const freezer_state_table[_FREEZER_STATE_MAX] = {
|
||||||
|
[FREEZER_RUNNING] = "running",
|
||||||
|
[FREEZER_FREEZING] = "freezing",
|
||||||
|
[FREEZER_FROZEN] = "frozen",
|
||||||
|
[FREEZER_THAWING] = "thawing",
|
||||||
|
};
|
||||||
|
|
||||||
|
DEFINE_STRING_TABLE_LOOKUP(freezer_state, FreezerState);
|
||||||
|
|
||||||
static const char* const automount_state_table[_AUTOMOUNT_STATE_MAX] = {
|
static const char* const automount_state_table[_AUTOMOUNT_STATE_MAX] = {
|
||||||
[AUTOMOUNT_DEAD] = "dead",
|
[AUTOMOUNT_DEAD] = "dead",
|
||||||
[AUTOMOUNT_WAITING] = "waiting",
|
[AUTOMOUNT_WAITING] = "waiting",
|
||||||
|
|
|
@ -48,6 +48,15 @@ typedef enum UnitActiveState {
|
||||||
_UNIT_ACTIVE_STATE_INVALID = -1
|
_UNIT_ACTIVE_STATE_INVALID = -1
|
||||||
} UnitActiveState;
|
} UnitActiveState;
|
||||||
|
|
||||||
|
typedef enum FreezerState {
|
||||||
|
FREEZER_RUNNING,
|
||||||
|
FREEZER_FREEZING,
|
||||||
|
FREEZER_FROZEN,
|
||||||
|
FREEZER_THAWING,
|
||||||
|
_FREEZER_STATE_MAX,
|
||||||
|
_FREEZER_STATE_INVALID = -1
|
||||||
|
} FreezerState;
|
||||||
|
|
||||||
typedef enum AutomountState {
|
typedef enum AutomountState {
|
||||||
AUTOMOUNT_DEAD,
|
AUTOMOUNT_DEAD,
|
||||||
AUTOMOUNT_WAITING,
|
AUTOMOUNT_WAITING,
|
||||||
|
@ -253,6 +262,9 @@ UnitLoadState unit_load_state_from_string(const char *s) _pure_;
|
||||||
const char *unit_active_state_to_string(UnitActiveState i) _const_;
|
const char *unit_active_state_to_string(UnitActiveState i) _const_;
|
||||||
UnitActiveState unit_active_state_from_string(const char *s) _pure_;
|
UnitActiveState unit_active_state_from_string(const char *s) _pure_;
|
||||||
|
|
||||||
|
const char *freezer_state_to_string(FreezerState i) _const_;
|
||||||
|
FreezerState freezer_state_from_string(const char *s) _pure_;
|
||||||
|
|
||||||
const char* automount_state_to_string(AutomountState i) _const_;
|
const char* automount_state_to_string(AutomountState i) _const_;
|
||||||
AutomountState automount_state_from_string(const char *s) _pure_;
|
AutomountState automount_state_from_string(const char *s) _pure_;
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,9 @@
|
||||||
#include "fd-util.h"
|
#include "fd-util.h"
|
||||||
#include "fileio.h"
|
#include "fileio.h"
|
||||||
#include "fs-util.h"
|
#include "fs-util.h"
|
||||||
|
#include "io-util.h"
|
||||||
#include "limits-util.h"
|
#include "limits-util.h"
|
||||||
|
#include "nulstr-util.h"
|
||||||
#include "parse-util.h"
|
#include "parse-util.h"
|
||||||
#include "path-util.h"
|
#include "path-util.h"
|
||||||
#include "process-util.h"
|
#include "process-util.h"
|
||||||
|
@ -2661,6 +2663,16 @@ void unit_add_to_cgroup_empty_queue(Unit *u) {
|
||||||
log_debug_errno(r, "Failed to enable cgroup empty event source: %m");
|
log_debug_errno(r, "Failed to enable cgroup empty event source: %m");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void unit_remove_from_cgroup_empty_queue(Unit *u) {
|
||||||
|
assert(u);
|
||||||
|
|
||||||
|
if (!u->in_cgroup_empty_queue)
|
||||||
|
return;
|
||||||
|
|
||||||
|
LIST_REMOVE(cgroup_empty_queue, u->manager->cgroup_empty_queue, u);
|
||||||
|
u->in_cgroup_empty_queue = false;
|
||||||
|
}
|
||||||
|
|
||||||
int unit_check_oom(Unit *u) {
|
int unit_check_oom(Unit *u) {
|
||||||
_cleanup_free_ char *oom_kill = NULL;
|
_cleanup_free_ char *oom_kill = NULL;
|
||||||
bool increased;
|
bool increased;
|
||||||
|
@ -2761,6 +2773,41 @@ static void unit_add_to_cgroup_oom_queue(Unit *u) {
|
||||||
log_error_errno(r, "Failed to enable cgroup oom event source: %m");
|
log_error_errno(r, "Failed to enable cgroup oom event source: %m");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int unit_check_cgroup_events(Unit *u) {
|
||||||
|
char *values[2] = {};
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(u);
|
||||||
|
|
||||||
|
r = cg_get_keyed_attribute_graceful(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, "cgroup.events",
|
||||||
|
STRV_MAKE("populated", "frozen"), values);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
/* The cgroup.events notifications can be merged together so act as we saw the given state for the
|
||||||
|
* first time. The functions we call to handle given state are idempotent, which makes them
|
||||||
|
* effectively remember the previous state. */
|
||||||
|
if (values[0]) {
|
||||||
|
if (streq(values[0], "1"))
|
||||||
|
unit_remove_from_cgroup_empty_queue(u);
|
||||||
|
else
|
||||||
|
unit_add_to_cgroup_empty_queue(u);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Disregard freezer state changes due to operations not initiated by us */
|
||||||
|
if (values[1] && IN_SET(u->freezer_state, FREEZER_FREEZING, FREEZER_THAWING)) {
|
||||||
|
if (streq(values[1], "0"))
|
||||||
|
unit_thawed(u);
|
||||||
|
else
|
||||||
|
unit_frozen(u);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(values[0]);
|
||||||
|
free(values[1]);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int on_cgroup_inotify_event(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
|
static int on_cgroup_inotify_event(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
|
||||||
Manager *m = userdata;
|
Manager *m = userdata;
|
||||||
|
|
||||||
|
@ -2797,7 +2844,7 @@ static int on_cgroup_inotify_event(sd_event_source *s, int fd, uint32_t revents,
|
||||||
|
|
||||||
u = hashmap_get(m->cgroup_control_inotify_wd_unit, INT_TO_PTR(e->wd));
|
u = hashmap_get(m->cgroup_control_inotify_wd_unit, INT_TO_PTR(e->wd));
|
||||||
if (u)
|
if (u)
|
||||||
unit_add_to_cgroup_empty_queue(u);
|
unit_check_cgroup_events(u);
|
||||||
|
|
||||||
u = hashmap_get(m->cgroup_memory_inotify_wd_unit, INT_TO_PTR(e->wd));
|
u = hashmap_get(m->cgroup_memory_inotify_wd_unit, INT_TO_PTR(e->wd));
|
||||||
if (u)
|
if (u)
|
||||||
|
@ -3550,6 +3597,46 @@ int compare_job_priority(const void *a, const void *b) {
|
||||||
return strcmp(x->unit->id, y->unit->id);
|
return strcmp(x->unit->id, y->unit->id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int unit_cgroup_freezer_action(Unit *u, FreezerAction action) {
|
||||||
|
_cleanup_free_ char *path = NULL;
|
||||||
|
FreezerState target, kernel = _FREEZER_STATE_INVALID;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(u);
|
||||||
|
assert(IN_SET(action, FREEZER_FREEZE, FREEZER_THAW));
|
||||||
|
|
||||||
|
if (!u->cgroup_realized)
|
||||||
|
return -EBUSY;
|
||||||
|
|
||||||
|
target = action == FREEZER_FREEZE ? FREEZER_FROZEN : FREEZER_RUNNING;
|
||||||
|
|
||||||
|
r = unit_freezer_state_kernel(u, &kernel);
|
||||||
|
if (r < 0)
|
||||||
|
log_unit_debug_errno(u, r, "Failed to obtain cgroup freezer state: %m");
|
||||||
|
|
||||||
|
if (target == kernel) {
|
||||||
|
u->freezer_state = target;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, "cgroup.freeze", &path);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
log_unit_debug(u, "%s unit.", action == FREEZER_FREEZE ? "Freezing" : "Thawing");
|
||||||
|
|
||||||
|
if (action == FREEZER_FREEZE)
|
||||||
|
u->freezer_state = FREEZER_FREEZING;
|
||||||
|
else
|
||||||
|
u->freezer_state = FREEZER_THAWING;
|
||||||
|
|
||||||
|
r = write_string_file(path, one_zero(action == FREEZER_FREEZE), WRITE_STRING_FILE_DISABLE_BUFFER);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static const char* const cgroup_device_policy_table[_CGROUP_DEVICE_POLICY_MAX] = {
|
static const char* const cgroup_device_policy_table[_CGROUP_DEVICE_POLICY_MAX] = {
|
||||||
[CGROUP_DEVICE_POLICY_AUTO] = "auto",
|
[CGROUP_DEVICE_POLICY_AUTO] = "auto",
|
||||||
[CGROUP_DEVICE_POLICY_CLOSED] = "closed",
|
[CGROUP_DEVICE_POLICY_CLOSED] = "closed",
|
||||||
|
@ -3585,3 +3672,10 @@ int unit_get_cpuset(Unit *u, CPUSet *cpus, const char *name) {
|
||||||
}
|
}
|
||||||
|
|
||||||
DEFINE_STRING_TABLE_LOOKUP(cgroup_device_policy, CGroupDevicePolicy);
|
DEFINE_STRING_TABLE_LOOKUP(cgroup_device_policy, CGroupDevicePolicy);
|
||||||
|
|
||||||
|
static const char* const freezer_action_table[_FREEZER_ACTION_MAX] = {
|
||||||
|
[FREEZER_FREEZE] = "freeze",
|
||||||
|
[FREEZER_THAW] = "thaw",
|
||||||
|
};
|
||||||
|
|
||||||
|
DEFINE_STRING_TABLE_LOOKUP(freezer_action, FreezerAction);
|
||||||
|
|
|
@ -47,6 +47,14 @@ typedef enum CGroupDevicePolicy {
|
||||||
_CGROUP_DEVICE_POLICY_INVALID = -1
|
_CGROUP_DEVICE_POLICY_INVALID = -1
|
||||||
} CGroupDevicePolicy;
|
} CGroupDevicePolicy;
|
||||||
|
|
||||||
|
typedef enum FreezerAction {
|
||||||
|
FREEZER_FREEZE,
|
||||||
|
FREEZER_THAW,
|
||||||
|
|
||||||
|
_FREEZER_ACTION_MAX,
|
||||||
|
_FREEZER_ACTION_INVALID = -1,
|
||||||
|
} FreezerAction;
|
||||||
|
|
||||||
struct CGroupDeviceAllow {
|
struct CGroupDeviceAllow {
|
||||||
LIST_FIELDS(CGroupDeviceAllow, device_allow);
|
LIST_FIELDS(CGroupDeviceAllow, device_allow);
|
||||||
char *path;
|
char *path;
|
||||||
|
@ -274,3 +282,7 @@ bool unit_cgroup_delegate(Unit *u);
|
||||||
int compare_job_priority(const void *a, const void *b);
|
int compare_job_priority(const void *a, const void *b);
|
||||||
|
|
||||||
int unit_get_cpuset(Unit *u, CPUSet *cpus, const char *name);
|
int unit_get_cpuset(Unit *u, CPUSet *cpus, const char *name);
|
||||||
|
int unit_cgroup_freezer_action(Unit *u, FreezerAction action);
|
||||||
|
|
||||||
|
const char* freezer_action_to_string(FreezerAction a) _const_;
|
||||||
|
FreezerAction freezer_action_from_string(const char *s) _pure_;
|
||||||
|
|
|
@ -620,6 +620,14 @@ static int method_clean_unit(sd_bus_message *message, void *userdata, sd_bus_err
|
||||||
return method_generic_unit_operation(message, userdata, error, bus_unit_method_clean, GENERIC_UNIT_LOAD|GENERIC_UNIT_VALIDATE_LOADED);
|
return method_generic_unit_operation(message, userdata, error, bus_unit_method_clean, GENERIC_UNIT_LOAD|GENERIC_UNIT_VALIDATE_LOADED);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int method_freeze_unit(sd_bus_message *message, void *userdata, sd_bus_error *error) {
|
||||||
|
return method_generic_unit_operation(message, userdata, error, bus_unit_method_freeze, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int method_thaw_unit(sd_bus_message *message, void *userdata, sd_bus_error *error) {
|
||||||
|
return method_generic_unit_operation(message, userdata, error, bus_unit_method_thaw, 0);
|
||||||
|
}
|
||||||
|
|
||||||
static int method_reset_failed_unit(sd_bus_message *message, void *userdata, sd_bus_error *error) {
|
static int method_reset_failed_unit(sd_bus_message *message, void *userdata, sd_bus_error *error) {
|
||||||
/* Don't load the unit (because unloaded units can't be in failed state), and don't insist on the
|
/* Don't load the unit (because unloaded units can't be in failed state), and don't insist on the
|
||||||
* unit to be loaded properly (since a failed unit might have its unit file disappeared) */
|
* unit to be loaded properly (since a failed unit might have its unit file disappeared) */
|
||||||
|
@ -2584,6 +2592,18 @@ const sd_bus_vtable bus_manager_vtable[] = {
|
||||||
NULL,,
|
NULL,,
|
||||||
method_clean_unit,
|
method_clean_unit,
|
||||||
SD_BUS_VTABLE_UNPRIVILEGED),
|
SD_BUS_VTABLE_UNPRIVILEGED),
|
||||||
|
SD_BUS_METHOD_WITH_NAMES("FreezeUnit",
|
||||||
|
"s",
|
||||||
|
SD_BUS_PARAM(name),
|
||||||
|
NULL,,
|
||||||
|
method_freeze_unit,
|
||||||
|
SD_BUS_VTABLE_UNPRIVILEGED),
|
||||||
|
SD_BUS_METHOD_WITH_NAMES("ThawUnit",
|
||||||
|
"s",
|
||||||
|
SD_BUS_PARAM(name),
|
||||||
|
NULL,,
|
||||||
|
method_thaw_unit,
|
||||||
|
SD_BUS_VTABLE_UNPRIVILEGED),
|
||||||
SD_BUS_METHOD_WITH_NAMES("ResetFailedUnit",
|
SD_BUS_METHOD_WITH_NAMES("ResetFailedUnit",
|
||||||
"s",
|
"s",
|
||||||
SD_BUS_PARAM(name),
|
SD_BUS_PARAM(name),
|
||||||
|
|
|
@ -46,12 +46,14 @@ static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_job_mode, job_mode, JobMode);
|
||||||
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_emergency_action, emergency_action, EmergencyAction);
|
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_emergency_action, emergency_action, EmergencyAction);
|
||||||
static BUS_DEFINE_PROPERTY_GET(property_get_description, "s", Unit, unit_description);
|
static BUS_DEFINE_PROPERTY_GET(property_get_description, "s", Unit, unit_description);
|
||||||
static BUS_DEFINE_PROPERTY_GET2(property_get_active_state, "s", Unit, unit_active_state, unit_active_state_to_string);
|
static BUS_DEFINE_PROPERTY_GET2(property_get_active_state, "s", Unit, unit_active_state, unit_active_state_to_string);
|
||||||
|
static BUS_DEFINE_PROPERTY_GET2(property_get_freezer_state, "s", Unit, unit_freezer_state, freezer_state_to_string);
|
||||||
static BUS_DEFINE_PROPERTY_GET(property_get_sub_state, "s", Unit, unit_sub_state_to_string);
|
static BUS_DEFINE_PROPERTY_GET(property_get_sub_state, "s", Unit, unit_sub_state_to_string);
|
||||||
static BUS_DEFINE_PROPERTY_GET2(property_get_unit_file_state, "s", Unit, unit_get_unit_file_state, unit_file_state_to_string);
|
static BUS_DEFINE_PROPERTY_GET2(property_get_unit_file_state, "s", Unit, unit_get_unit_file_state, unit_file_state_to_string);
|
||||||
static BUS_DEFINE_PROPERTY_GET(property_get_can_reload, "b", Unit, unit_can_reload);
|
static BUS_DEFINE_PROPERTY_GET(property_get_can_reload, "b", Unit, unit_can_reload);
|
||||||
static BUS_DEFINE_PROPERTY_GET(property_get_can_start, "b", Unit, unit_can_start_refuse_manual);
|
static BUS_DEFINE_PROPERTY_GET(property_get_can_start, "b", Unit, unit_can_start_refuse_manual);
|
||||||
static BUS_DEFINE_PROPERTY_GET(property_get_can_stop, "b", Unit, unit_can_stop_refuse_manual);
|
static BUS_DEFINE_PROPERTY_GET(property_get_can_stop, "b", Unit, unit_can_stop_refuse_manual);
|
||||||
static BUS_DEFINE_PROPERTY_GET(property_get_can_isolate, "b", Unit, unit_can_isolate_refuse_manual);
|
static BUS_DEFINE_PROPERTY_GET(property_get_can_isolate, "b", Unit, unit_can_isolate_refuse_manual);
|
||||||
|
static BUS_DEFINE_PROPERTY_GET(property_get_can_freeze, "b", Unit, unit_can_freeze);
|
||||||
static BUS_DEFINE_PROPERTY_GET(property_get_need_daemon_reload, "b", Unit, unit_need_daemon_reload);
|
static BUS_DEFINE_PROPERTY_GET(property_get_need_daemon_reload, "b", Unit, unit_need_daemon_reload);
|
||||||
static BUS_DEFINE_PROPERTY_GET_GLOBAL(property_get_empty_strv, "as", 0);
|
static BUS_DEFINE_PROPERTY_GET_GLOBAL(property_get_empty_strv, "as", 0);
|
||||||
|
|
||||||
|
@ -724,6 +726,79 @@ int bus_unit_method_clean(sd_bus_message *message, void *userdata, sd_bus_error
|
||||||
return sd_bus_reply_method_return(message, NULL);
|
return sd_bus_reply_method_return(message, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int bus_unit_method_freezer_generic(sd_bus_message *message, void *userdata, sd_bus_error *error, FreezerAction action) {
|
||||||
|
const char* perm;
|
||||||
|
int (*method)(Unit*);
|
||||||
|
Unit *u = userdata;
|
||||||
|
bool reply_no_delay = false;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(message);
|
||||||
|
assert(u);
|
||||||
|
assert(IN_SET(action, FREEZER_FREEZE, FREEZER_THAW));
|
||||||
|
|
||||||
|
if (action == FREEZER_FREEZE) {
|
||||||
|
perm = "stop";
|
||||||
|
method = unit_freeze;
|
||||||
|
} else {
|
||||||
|
perm = "start";
|
||||||
|
method = unit_thaw;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = mac_selinux_unit_access_check(u, message, perm, error);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
r = bus_verify_manage_units_async_full(
|
||||||
|
u,
|
||||||
|
perm,
|
||||||
|
CAP_SYS_ADMIN,
|
||||||
|
N_("Authentication is required to freeze or thaw the processes of '$(unit)' unit."),
|
||||||
|
true,
|
||||||
|
message,
|
||||||
|
error);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
if (r == 0)
|
||||||
|
return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
|
||||||
|
|
||||||
|
r = method(u);
|
||||||
|
if (r == -EOPNOTSUPP)
|
||||||
|
return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Unit '%s' does not support freezing.", u->id);
|
||||||
|
if (r == -EBUSY)
|
||||||
|
return sd_bus_error_setf(error, BUS_ERROR_UNIT_BUSY, "Unit has a pending job.");
|
||||||
|
if (r == -EHOSTDOWN)
|
||||||
|
return sd_bus_error_setf(error, BUS_ERROR_UNIT_INACTIVE, "Unit is inactive.");
|
||||||
|
if (r == -EALREADY)
|
||||||
|
return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Previously requested freezer operation for unit '%s' is still in progress.", u->id);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
if (r == 0)
|
||||||
|
reply_no_delay = true;
|
||||||
|
|
||||||
|
assert(!u->pending_freezer_message);
|
||||||
|
|
||||||
|
r = sd_bus_message_new_method_return(message, &u->pending_freezer_message);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
if (reply_no_delay) {
|
||||||
|
r = bus_unit_send_pending_freezer_message(u);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int bus_unit_method_thaw(sd_bus_message *message, void *userdata, sd_bus_error *error) {
|
||||||
|
return bus_unit_method_freezer_generic(message, userdata, error, FREEZER_THAW);
|
||||||
|
}
|
||||||
|
|
||||||
|
int bus_unit_method_freeze(sd_bus_message *message, void *userdata, sd_bus_error *error) {
|
||||||
|
return bus_unit_method_freezer_generic(message, userdata, error, FREEZER_FREEZE);
|
||||||
|
}
|
||||||
|
|
||||||
static int property_get_refs(
|
static int property_get_refs(
|
||||||
sd_bus *bus,
|
sd_bus *bus,
|
||||||
const char *path,
|
const char *path,
|
||||||
|
@ -793,6 +868,7 @@ const sd_bus_vtable bus_unit_vtable[] = {
|
||||||
SD_BUS_PROPERTY("Description", "s", property_get_description, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
SD_BUS_PROPERTY("Description", "s", property_get_description, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
||||||
SD_BUS_PROPERTY("LoadState", "s", property_get_load_state, offsetof(Unit, load_state), SD_BUS_VTABLE_PROPERTY_CONST),
|
SD_BUS_PROPERTY("LoadState", "s", property_get_load_state, offsetof(Unit, load_state), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||||
SD_BUS_PROPERTY("ActiveState", "s", property_get_active_state, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
|
SD_BUS_PROPERTY("ActiveState", "s", property_get_active_state, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
|
||||||
|
SD_BUS_PROPERTY("FreezerState", "s", property_get_freezer_state, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
|
||||||
SD_BUS_PROPERTY("SubState", "s", property_get_sub_state, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
|
SD_BUS_PROPERTY("SubState", "s", property_get_sub_state, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
|
||||||
SD_BUS_PROPERTY("FragmentPath", "s", NULL, offsetof(Unit, fragment_path), SD_BUS_VTABLE_PROPERTY_CONST),
|
SD_BUS_PROPERTY("FragmentPath", "s", NULL, offsetof(Unit, fragment_path), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||||
SD_BUS_PROPERTY("SourcePath", "s", NULL, offsetof(Unit, source_path), SD_BUS_VTABLE_PROPERTY_CONST),
|
SD_BUS_PROPERTY("SourcePath", "s", NULL, offsetof(Unit, source_path), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||||
|
@ -809,6 +885,7 @@ const sd_bus_vtable bus_unit_vtable[] = {
|
||||||
SD_BUS_PROPERTY("CanReload", "b", property_get_can_reload, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
SD_BUS_PROPERTY("CanReload", "b", property_get_can_reload, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
||||||
SD_BUS_PROPERTY("CanIsolate", "b", property_get_can_isolate, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
SD_BUS_PROPERTY("CanIsolate", "b", property_get_can_isolate, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
||||||
SD_BUS_PROPERTY("CanClean", "as", property_get_can_clean, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
SD_BUS_PROPERTY("CanClean", "as", property_get_can_clean, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
||||||
|
SD_BUS_PROPERTY("CanFreeze", "b", property_get_can_freeze, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
||||||
SD_BUS_PROPERTY("Job", "(uo)", property_get_job, offsetof(Unit, job), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
|
SD_BUS_PROPERTY("Job", "(uo)", property_get_job, offsetof(Unit, job), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
|
||||||
SD_BUS_PROPERTY("StopWhenUnneeded", "b", bus_property_get_bool, offsetof(Unit, stop_when_unneeded), SD_BUS_VTABLE_PROPERTY_CONST),
|
SD_BUS_PROPERTY("StopWhenUnneeded", "b", bus_property_get_bool, offsetof(Unit, stop_when_unneeded), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||||
SD_BUS_PROPERTY("RefuseManualStart", "b", bus_property_get_bool, offsetof(Unit, refuse_manual_start), SD_BUS_VTABLE_PROPERTY_CONST),
|
SD_BUS_PROPERTY("RefuseManualStart", "b", bus_property_get_bool, offsetof(Unit, refuse_manual_start), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||||
|
@ -940,6 +1017,16 @@ const sd_bus_vtable bus_unit_vtable[] = {
|
||||||
NULL,,
|
NULL,,
|
||||||
bus_unit_method_clean,
|
bus_unit_method_clean,
|
||||||
SD_BUS_VTABLE_UNPRIVILEGED),
|
SD_BUS_VTABLE_UNPRIVILEGED),
|
||||||
|
SD_BUS_METHOD("Freeze",
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
bus_unit_method_freeze,
|
||||||
|
SD_BUS_VTABLE_UNPRIVILEGED),
|
||||||
|
SD_BUS_METHOD("Thaw",
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
bus_unit_method_thaw,
|
||||||
|
SD_BUS_VTABLE_UNPRIVILEGED),
|
||||||
|
|
||||||
/* For dependency types we don't support anymore always return an empty array */
|
/* For dependency types we don't support anymore always return an empty array */
|
||||||
SD_BUS_PROPERTY("RequiresOverridable", "as", property_get_empty_strv, 0, SD_BUS_VTABLE_HIDDEN),
|
SD_BUS_PROPERTY("RequiresOverridable", "as", property_get_empty_strv, 0, SD_BUS_VTABLE_HIDDEN),
|
||||||
|
@ -1566,6 +1653,23 @@ void bus_unit_send_pending_change_signal(Unit *u, bool including_new) {
|
||||||
bus_unit_send_change_signal(u);
|
bus_unit_send_change_signal(u);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int bus_unit_send_pending_freezer_message(Unit *u) {
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(u);
|
||||||
|
|
||||||
|
if (!u->pending_freezer_message)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
r = sd_bus_send(NULL, u->pending_freezer_message, NULL);
|
||||||
|
if (r < 0)
|
||||||
|
log_warning_errno(r, "Failed to send queued message, ignoring: %m");
|
||||||
|
|
||||||
|
u->pending_freezer_message = sd_bus_message_unref(u->pending_freezer_message);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int send_removed_signal(sd_bus *bus, void *userdata) {
|
static int send_removed_signal(sd_bus *bus, void *userdata) {
|
||||||
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
|
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
|
||||||
_cleanup_free_ char *p = NULL;
|
_cleanup_free_ char *p = NULL;
|
||||||
|
|
|
@ -11,6 +11,7 @@ extern const sd_bus_vtable bus_unit_cgroup_vtable[];
|
||||||
|
|
||||||
void bus_unit_send_change_signal(Unit *u);
|
void bus_unit_send_change_signal(Unit *u);
|
||||||
void bus_unit_send_pending_change_signal(Unit *u, bool including_new);
|
void bus_unit_send_pending_change_signal(Unit *u, bool including_new);
|
||||||
|
int bus_unit_send_pending_freezer_message(Unit *u);
|
||||||
void bus_unit_send_removed_signal(Unit *u);
|
void bus_unit_send_removed_signal(Unit *u);
|
||||||
|
|
||||||
int bus_unit_method_start_generic(sd_bus_message *message, Unit *u, JobType job_type, bool reload_if_possible, sd_bus_error *error);
|
int bus_unit_method_start_generic(sd_bus_message *message, Unit *u, JobType job_type, bool reload_if_possible, sd_bus_error *error);
|
||||||
|
@ -25,6 +26,8 @@ int bus_unit_method_attach_processes(sd_bus_message *message, void *userdata, sd
|
||||||
int bus_unit_method_ref(sd_bus_message *message, void *userdata, sd_bus_error *error);
|
int bus_unit_method_ref(sd_bus_message *message, void *userdata, sd_bus_error *error);
|
||||||
int bus_unit_method_unref(sd_bus_message *message, void *userdata, sd_bus_error *error);
|
int bus_unit_method_unref(sd_bus_message *message, void *userdata, sd_bus_error *error);
|
||||||
int bus_unit_method_clean(sd_bus_message *message, void *userdata, sd_bus_error *error);
|
int bus_unit_method_clean(sd_bus_message *message, void *userdata, sd_bus_error *error);
|
||||||
|
int bus_unit_method_freeze(sd_bus_message *message, void *userdata, sd_bus_error *error);
|
||||||
|
int bus_unit_method_thaw(sd_bus_message *message, void *userdata, sd_bus_error *error);
|
||||||
|
|
||||||
typedef enum BusUnitQueueFlags {
|
typedef enum BusUnitQueueFlags {
|
||||||
BUS_UNIT_QUEUE_RELOAD_IF_POSSIBLE = 1 << 0,
|
BUS_UNIT_QUEUE_RELOAD_IF_POSSIBLE = 1 << 0,
|
||||||
|
|
|
@ -960,10 +960,15 @@ static void destroy_bus(Manager *m, sd_bus **bus) {
|
||||||
if (j->bus_track && sd_bus_track_get_bus(j->bus_track) == *bus)
|
if (j->bus_track && sd_bus_track_get_bus(j->bus_track) == *bus)
|
||||||
j->bus_track = sd_bus_track_unref(j->bus_track);
|
j->bus_track = sd_bus_track_unref(j->bus_track);
|
||||||
|
|
||||||
HASHMAP_FOREACH(u, m->units, i)
|
HASHMAP_FOREACH(u, m->units, i) {
|
||||||
if (u->bus_track && sd_bus_track_get_bus(u->bus_track) == *bus)
|
if (u->bus_track && sd_bus_track_get_bus(u->bus_track) == *bus)
|
||||||
u->bus_track = sd_bus_track_unref(u->bus_track);
|
u->bus_track = sd_bus_track_unref(u->bus_track);
|
||||||
|
|
||||||
|
/* Get rid of pending freezer messages on this bus */
|
||||||
|
if (u->pending_freezer_message && sd_bus_message_get_bus(u->pending_freezer_message) == *bus)
|
||||||
|
u->pending_freezer_message = sd_bus_message_unref(u->pending_freezer_message);
|
||||||
|
}
|
||||||
|
|
||||||
/* Get rid of queued message on this bus */
|
/* Get rid of queued message on this bus */
|
||||||
if (m->pending_reload_message && sd_bus_message_get_bus(m->pending_reload_message) == *bus)
|
if (m->pending_reload_message && sd_bus_message_get_bus(m->pending_reload_message) == *bus)
|
||||||
m->pending_reload_message = sd_bus_message_unref(m->pending_reload_message);
|
m->pending_reload_message = sd_bus_message_unref(m->pending_reload_message);
|
||||||
|
|
|
@ -2284,6 +2284,20 @@ static int manager_dispatch_cgroups_agent_fd(sd_event_source *source, int fd, ui
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool manager_process_barrier_fd(const char *buf, FDSet *fds) {
|
||||||
|
assert(buf);
|
||||||
|
|
||||||
|
/* nothing else must be sent when using BARRIER=1 */
|
||||||
|
if (STR_IN_SET(buf, "BARRIER=1", "BARRIER=1\n")) {
|
||||||
|
if (fdset_size(fds) != 1)
|
||||||
|
log_warning("Got incorrect number of fds with BARRIER=1, closing them.");
|
||||||
|
return true;
|
||||||
|
} else if (startswith(buf, "BARRIER=1\n") || strstr(buf, "\nBARRIER=1\n") || endswith(buf, "\nBARRIER=1"))
|
||||||
|
log_warning("Extra notification messages sent with BARRIER=1, ignoring everything.");
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
static void manager_invoke_notify_message(
|
static void manager_invoke_notify_message(
|
||||||
Manager *m,
|
Manager *m,
|
||||||
Unit *u,
|
Unit *u,
|
||||||
|
@ -2417,6 +2431,10 @@ static int manager_dispatch_notify_fd(sd_event_source *source, int fd, uint32_t
|
||||||
/* Make sure it's NUL-terminated. */
|
/* Make sure it's NUL-terminated. */
|
||||||
buf[n] = 0;
|
buf[n] = 0;
|
||||||
|
|
||||||
|
/* possibly a barrier fd, let's see */
|
||||||
|
if (manager_process_barrier_fd(buf, fds))
|
||||||
|
return 0;
|
||||||
|
|
||||||
/* Increase the generation counter used for filtering out duplicate unit invocations. */
|
/* Increase the generation counter used for filtering out duplicate unit invocations. */
|
||||||
m->notifygen++;
|
m->notifygen++;
|
||||||
|
|
||||||
|
|
|
@ -635,6 +635,9 @@ const UnitVTable scope_vtable = {
|
||||||
|
|
||||||
.kill = scope_kill,
|
.kill = scope_kill,
|
||||||
|
|
||||||
|
.freeze = unit_freeze_vtable_common,
|
||||||
|
.thaw = unit_thaw_vtable_common,
|
||||||
|
|
||||||
.get_timeout = scope_get_timeout,
|
.get_timeout = scope_get_timeout,
|
||||||
|
|
||||||
.serialize = scope_serialize,
|
.serialize = scope_serialize,
|
||||||
|
|
|
@ -7,17 +7,8 @@
|
||||||
|
|
||||||
int mac_selinux_generic_access_check(sd_bus_message *message, const char *path, const char *permission, sd_bus_error *error);
|
int mac_selinux_generic_access_check(sd_bus_message *message, const char *path, const char *permission, sd_bus_error *error);
|
||||||
|
|
||||||
#if HAVE_SELINUX
|
|
||||||
|
|
||||||
#define mac_selinux_access_check(message, permission, error) \
|
#define mac_selinux_access_check(message, permission, error) \
|
||||||
mac_selinux_generic_access_check((message), NULL, (permission), (error))
|
mac_selinux_generic_access_check((message), NULL, (permission), (error))
|
||||||
|
|
||||||
#define mac_selinux_unit_access_check(unit, message, permission, error) \
|
#define mac_selinux_unit_access_check(unit, message, permission, error) \
|
||||||
mac_selinux_generic_access_check((message), unit_label_path(unit), (permission), (error))
|
mac_selinux_generic_access_check((message), unit_label_path(unit), (permission), (error))
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
#define mac_selinux_access_check(message, permission, error) 0
|
|
||||||
#define mac_selinux_unit_access_check(unit, message, permission, error) 0
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
|
@ -4458,6 +4458,9 @@ const UnitVTable service_vtable = {
|
||||||
.clean = service_clean,
|
.clean = service_clean,
|
||||||
.can_clean = service_can_clean,
|
.can_clean = service_can_clean,
|
||||||
|
|
||||||
|
.freeze = unit_freeze_vtable_common,
|
||||||
|
.thaw = unit_thaw_vtable_common,
|
||||||
|
|
||||||
.serialize = service_serialize,
|
.serialize = service_serialize,
|
||||||
.deserialize_item = service_deserialize_item,
|
.deserialize_item = service_deserialize_item,
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#include "alloc-util.h"
|
#include "alloc-util.h"
|
||||||
#include "dbus-slice.h"
|
#include "dbus-slice.h"
|
||||||
#include "dbus-unit.h"
|
#include "dbus-unit.h"
|
||||||
|
#include "fd-util.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "serialize.h"
|
#include "serialize.h"
|
||||||
#include "slice.h"
|
#include "slice.h"
|
||||||
|
@ -347,6 +348,82 @@ static void slice_enumerate_perpetual(Manager *m) {
|
||||||
(void) slice_make_perpetual(m, SPECIAL_SYSTEM_SLICE, NULL);
|
(void) slice_make_perpetual(m, SPECIAL_SYSTEM_SLICE, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool slice_freezer_action_supported_by_children(Unit *s) {
|
||||||
|
Unit *member;
|
||||||
|
void *v;
|
||||||
|
Iterator i;
|
||||||
|
|
||||||
|
assert(s);
|
||||||
|
|
||||||
|
HASHMAP_FOREACH_KEY(v, member, s->dependencies[UNIT_BEFORE], i) {
|
||||||
|
int r;
|
||||||
|
|
||||||
|
if (UNIT_DEREF(member->slice) != s)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (member->type == UNIT_SLICE) {
|
||||||
|
r = slice_freezer_action_supported_by_children(member);
|
||||||
|
if (!r)
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!UNIT_VTABLE(member)->freeze)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int slice_freezer_action(Unit *s, FreezerAction action) {
|
||||||
|
Unit *member;
|
||||||
|
void *v;
|
||||||
|
Iterator i;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(s);
|
||||||
|
assert(IN_SET(action, FREEZER_FREEZE, FREEZER_THAW));
|
||||||
|
|
||||||
|
if (!slice_freezer_action_supported_by_children(s))
|
||||||
|
return log_unit_warning(s, "Requested freezer operation is not supported by all children of the slice");
|
||||||
|
|
||||||
|
HASHMAP_FOREACH_KEY(v, member, s->dependencies[UNIT_BEFORE], i) {
|
||||||
|
if (UNIT_DEREF(member->slice) != s)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (action == FREEZER_FREEZE)
|
||||||
|
r = UNIT_VTABLE(member)->freeze(member);
|
||||||
|
else
|
||||||
|
r = UNIT_VTABLE(member)->thaw(member);
|
||||||
|
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = unit_cgroup_freezer_action(s, action);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int slice_freeze(Unit *s) {
|
||||||
|
assert(s);
|
||||||
|
|
||||||
|
return slice_freezer_action(s, FREEZER_FREEZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int slice_thaw(Unit *s) {
|
||||||
|
assert(s);
|
||||||
|
|
||||||
|
return slice_freezer_action(s, FREEZER_THAW);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool slice_can_freeze(Unit *s) {
|
||||||
|
assert(s);
|
||||||
|
|
||||||
|
return slice_freezer_action_supported_by_children(s);
|
||||||
|
}
|
||||||
|
|
||||||
const UnitVTable slice_vtable = {
|
const UnitVTable slice_vtable = {
|
||||||
.object_size = sizeof(Slice),
|
.object_size = sizeof(Slice),
|
||||||
.cgroup_context_offset = offsetof(Slice, cgroup_context),
|
.cgroup_context_offset = offsetof(Slice, cgroup_context),
|
||||||
|
@ -371,6 +448,10 @@ const UnitVTable slice_vtable = {
|
||||||
|
|
||||||
.kill = slice_kill,
|
.kill = slice_kill,
|
||||||
|
|
||||||
|
.freeze = slice_freeze,
|
||||||
|
.thaw = slice_thaw,
|
||||||
|
.can_freeze = slice_can_freeze,
|
||||||
|
|
||||||
.serialize = slice_serialize,
|
.serialize = slice_serialize,
|
||||||
.deserialize_item = slice_deserialize_item,
|
.deserialize_item = slice_deserialize_item,
|
||||||
|
|
||||||
|
|
123
src/core/unit.c
123
src/core/unit.c
|
@ -628,6 +628,7 @@ void unit_free(Unit *u) {
|
||||||
sd_bus_slot_unref(u->match_bus_slot);
|
sd_bus_slot_unref(u->match_bus_slot);
|
||||||
sd_bus_track_unref(u->bus_track);
|
sd_bus_track_unref(u->bus_track);
|
||||||
u->deserialized_refs = strv_free(u->deserialized_refs);
|
u->deserialized_refs = strv_free(u->deserialized_refs);
|
||||||
|
u->pending_freezer_message = sd_bus_message_unref(u->pending_freezer_message);
|
||||||
|
|
||||||
unit_free_requires_mounts_for(u);
|
unit_free_requires_mounts_for(u);
|
||||||
|
|
||||||
|
@ -737,6 +738,38 @@ void unit_free(Unit *u) {
|
||||||
free(u);
|
free(u);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FreezerState unit_freezer_state(Unit *u) {
|
||||||
|
assert(u);
|
||||||
|
|
||||||
|
return u->freezer_state;
|
||||||
|
}
|
||||||
|
|
||||||
|
int unit_freezer_state_kernel(Unit *u, FreezerState *ret) {
|
||||||
|
char *values[1] = {};
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(u);
|
||||||
|
|
||||||
|
r = cg_get_keyed_attribute(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, "cgroup.events",
|
||||||
|
STRV_MAKE("frozen"), values);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
r = _FREEZER_STATE_INVALID;
|
||||||
|
|
||||||
|
if (values[0]) {
|
||||||
|
if (streq(values[0], "0"))
|
||||||
|
r = FREEZER_RUNNING;
|
||||||
|
else if (streq(values[0], "1"))
|
||||||
|
r = FREEZER_FROZEN;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(values[0]);
|
||||||
|
*ret = r;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
UnitActiveState unit_active_state(Unit *u) {
|
UnitActiveState unit_active_state(Unit *u) {
|
||||||
assert(u);
|
assert(u);
|
||||||
|
|
||||||
|
@ -1846,6 +1879,7 @@ int unit_start(Unit *u) {
|
||||||
* waits for a holdoff timer to elapse before it will start again. */
|
* waits for a holdoff timer to elapse before it will start again. */
|
||||||
|
|
||||||
unit_add_to_dbus_queue(u);
|
unit_add_to_dbus_queue(u);
|
||||||
|
unit_cgroup_freezer_action(u, FREEZER_THAW);
|
||||||
|
|
||||||
return UNIT_VTABLE(u)->start(u);
|
return UNIT_VTABLE(u)->start(u);
|
||||||
}
|
}
|
||||||
|
@ -1898,6 +1932,7 @@ int unit_stop(Unit *u) {
|
||||||
return -EBADR;
|
return -EBADR;
|
||||||
|
|
||||||
unit_add_to_dbus_queue(u);
|
unit_add_to_dbus_queue(u);
|
||||||
|
unit_cgroup_freezer_action(u, FREEZER_THAW);
|
||||||
|
|
||||||
return UNIT_VTABLE(u)->stop(u);
|
return UNIT_VTABLE(u)->stop(u);
|
||||||
}
|
}
|
||||||
|
@ -1954,6 +1989,8 @@ int unit_reload(Unit *u) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unit_cgroup_freezer_action(u, FREEZER_THAW);
|
||||||
|
|
||||||
return UNIT_VTABLE(u)->reload(u);
|
return UNIT_VTABLE(u)->reload(u);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3497,6 +3534,8 @@ int unit_serialize(Unit *u, FILE *f, FDSet *fds, bool serialize_jobs) {
|
||||||
if (!sd_id128_is_null(u->invocation_id))
|
if (!sd_id128_is_null(u->invocation_id))
|
||||||
(void) serialize_item_format(f, "invocation-id", SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(u->invocation_id));
|
(void) serialize_item_format(f, "invocation-id", SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(u->invocation_id));
|
||||||
|
|
||||||
|
(void) serialize_item_format(f, "freezer-state", "%s", freezer_state_to_string(unit_freezer_state(u)));
|
||||||
|
|
||||||
bus_track_serialize(u->bus_track, f, "ref");
|
bus_track_serialize(u->bus_track, f, "ref");
|
||||||
|
|
||||||
for (m = 0; m < _CGROUP_IP_ACCOUNTING_METRIC_MAX; m++) {
|
for (m = 0; m < _CGROUP_IP_ACCOUNTING_METRIC_MAX; m++) {
|
||||||
|
@ -3805,6 +3844,16 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) {
|
||||||
log_unit_warning_errno(u, r, "Failed to set invocation ID for unit: %m");
|
log_unit_warning_errno(u, r, "Failed to set invocation ID for unit: %m");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
} else if (streq(l, "freezer-state")) {
|
||||||
|
FreezerState s;
|
||||||
|
|
||||||
|
s = freezer_state_from_string(v);
|
||||||
|
if (s < 0)
|
||||||
|
log_unit_debug(u, "Failed to deserialize freezer-state '%s', ignoring.", v);
|
||||||
|
else
|
||||||
|
u->freezer_state = s;
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6076,6 +6125,80 @@ int unit_can_clean(Unit *u, ExecCleanMask *ret) {
|
||||||
return UNIT_VTABLE(u)->can_clean(u, ret);
|
return UNIT_VTABLE(u)->can_clean(u, ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool unit_can_freeze(Unit *u) {
|
||||||
|
assert(u);
|
||||||
|
|
||||||
|
if (UNIT_VTABLE(u)->can_freeze)
|
||||||
|
return UNIT_VTABLE(u)->can_freeze(u);
|
||||||
|
|
||||||
|
return UNIT_VTABLE(u)->freeze;
|
||||||
|
}
|
||||||
|
|
||||||
|
void unit_frozen(Unit *u) {
|
||||||
|
assert(u);
|
||||||
|
|
||||||
|
u->freezer_state = FREEZER_FROZEN;
|
||||||
|
|
||||||
|
bus_unit_send_pending_freezer_message(u);
|
||||||
|
}
|
||||||
|
|
||||||
|
void unit_thawed(Unit *u) {
|
||||||
|
assert(u);
|
||||||
|
|
||||||
|
u->freezer_state = FREEZER_RUNNING;
|
||||||
|
|
||||||
|
bus_unit_send_pending_freezer_message(u);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int unit_freezer_action(Unit *u, FreezerAction action) {
|
||||||
|
UnitActiveState s;
|
||||||
|
int (*method)(Unit*);
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(u);
|
||||||
|
assert(IN_SET(action, FREEZER_FREEZE, FREEZER_THAW));
|
||||||
|
|
||||||
|
method = action == FREEZER_FREEZE ? UNIT_VTABLE(u)->freeze : UNIT_VTABLE(u)->thaw;
|
||||||
|
if (!method || !cg_freezer_supported())
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
|
||||||
|
if (u->job)
|
||||||
|
return -EBUSY;
|
||||||
|
|
||||||
|
if (u->load_state != UNIT_LOADED)
|
||||||
|
return -EHOSTDOWN;
|
||||||
|
|
||||||
|
s = unit_active_state(u);
|
||||||
|
if (s != UNIT_ACTIVE)
|
||||||
|
return -EHOSTDOWN;
|
||||||
|
|
||||||
|
if (IN_SET(u->freezer_state, FREEZER_FREEZING, FREEZER_THAWING))
|
||||||
|
return -EALREADY;
|
||||||
|
|
||||||
|
r = method(u);
|
||||||
|
if (r <= 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int unit_freeze(Unit *u) {
|
||||||
|
return unit_freezer_action(u, FREEZER_FREEZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
int unit_thaw(Unit *u) {
|
||||||
|
return unit_freezer_action(u, FREEZER_THAW);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Wrappers around low-level cgroup freezer operations common for service and scope units */
|
||||||
|
int unit_freeze_vtable_common(Unit *u) {
|
||||||
|
return unit_cgroup_freezer_action(u, FREEZER_FREEZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
int unit_thaw_vtable_common(Unit *u) {
|
||||||
|
return unit_cgroup_freezer_action(u, FREEZER_THAW);
|
||||||
|
}
|
||||||
|
|
||||||
static const char* const collect_mode_table[_COLLECT_MODE_MAX] = {
|
static const char* const collect_mode_table[_COLLECT_MODE_MAX] = {
|
||||||
[COLLECT_INACTIVE] = "inactive",
|
[COLLECT_INACTIVE] = "inactive",
|
||||||
[COLLECT_INACTIVE_OR_FAILED] = "inactive-or-failed",
|
[COLLECT_INACTIVE_OR_FAILED] = "inactive-or-failed",
|
||||||
|
|
|
@ -114,6 +114,9 @@ typedef struct Unit {
|
||||||
UnitLoadState load_state;
|
UnitLoadState load_state;
|
||||||
Unit *merged_into;
|
Unit *merged_into;
|
||||||
|
|
||||||
|
FreezerState freezer_state;
|
||||||
|
sd_bus_message *pending_freezer_message;
|
||||||
|
|
||||||
char *id; /* One name is special because we use it for identification. Points to an entry in the names set */
|
char *id; /* One name is special because we use it for identification. Points to an entry in the names set */
|
||||||
char *instance;
|
char *instance;
|
||||||
|
|
||||||
|
@ -483,6 +486,11 @@ typedef struct UnitVTable {
|
||||||
/* Clear out the various runtime/state/cache/logs/configuration data */
|
/* Clear out the various runtime/state/cache/logs/configuration data */
|
||||||
int (*clean)(Unit *u, ExecCleanMask m);
|
int (*clean)(Unit *u, ExecCleanMask m);
|
||||||
|
|
||||||
|
/* Freeze the unit */
|
||||||
|
int (*freeze)(Unit *u);
|
||||||
|
int (*thaw)(Unit *u);
|
||||||
|
bool (*can_freeze)(Unit *u);
|
||||||
|
|
||||||
/* Return which kind of data can be cleaned */
|
/* Return which kind of data can be cleaned */
|
||||||
int (*can_clean)(Unit *u, ExecCleanMask *ret);
|
int (*can_clean)(Unit *u, ExecCleanMask *ret);
|
||||||
|
|
||||||
|
@ -695,6 +703,8 @@ const char *unit_status_string(Unit *u) _pure_;
|
||||||
bool unit_has_name(const Unit *u, const char *name);
|
bool unit_has_name(const Unit *u, const char *name);
|
||||||
|
|
||||||
UnitActiveState unit_active_state(Unit *u);
|
UnitActiveState unit_active_state(Unit *u);
|
||||||
|
FreezerState unit_freezer_state(Unit *u);
|
||||||
|
int unit_freezer_state_kernel(Unit *u, FreezerState *ret);
|
||||||
|
|
||||||
const char* unit_sub_state_to_string(Unit *u);
|
const char* unit_sub_state_to_string(Unit *u);
|
||||||
|
|
||||||
|
@ -878,6 +888,16 @@ void unit_destroy_runtime_directory(Unit *u, const ExecContext *context);
|
||||||
int unit_clean(Unit *u, ExecCleanMask mask);
|
int unit_clean(Unit *u, ExecCleanMask mask);
|
||||||
int unit_can_clean(Unit *u, ExecCleanMask *ret_mask);
|
int unit_can_clean(Unit *u, ExecCleanMask *ret_mask);
|
||||||
|
|
||||||
|
bool unit_can_freeze(Unit *u);
|
||||||
|
int unit_freeze(Unit *u);
|
||||||
|
void unit_frozen(Unit *u);
|
||||||
|
|
||||||
|
int unit_thaw(Unit *u);
|
||||||
|
void unit_thawed(Unit *u);
|
||||||
|
|
||||||
|
int unit_freeze_vtable_common(Unit *u);
|
||||||
|
int unit_thaw_vtable_common(Unit *u);
|
||||||
|
|
||||||
/* Macros which append UNIT= or USER_UNIT= to the message */
|
/* Macros which append UNIT= or USER_UNIT= to the message */
|
||||||
|
|
||||||
#define log_unit_full(unit, level, error, ...) \
|
#define log_unit_full(unit, level, error, ...) \
|
||||||
|
|
|
@ -29,6 +29,7 @@
|
||||||
#define BUS_ERROR_DISK_FULL "org.freedesktop.systemd1.DiskFull"
|
#define BUS_ERROR_DISK_FULL "org.freedesktop.systemd1.DiskFull"
|
||||||
#define BUS_ERROR_NOTHING_TO_CLEAN "org.freedesktop.systemd1.NothingToClean"
|
#define BUS_ERROR_NOTHING_TO_CLEAN "org.freedesktop.systemd1.NothingToClean"
|
||||||
#define BUS_ERROR_UNIT_BUSY "org.freedesktop.systemd1.UnitBusy"
|
#define BUS_ERROR_UNIT_BUSY "org.freedesktop.systemd1.UnitBusy"
|
||||||
|
#define BUS_ERROR_UNIT_INACTIVE "org.freedesktop.systemd1.UnitInactive"
|
||||||
|
|
||||||
#define BUS_ERROR_NO_SUCH_MACHINE "org.freedesktop.machine1.NoSuchMachine"
|
#define BUS_ERROR_NO_SUCH_MACHINE "org.freedesktop.machine1.NoSuchMachine"
|
||||||
#define BUS_ERROR_NO_SUCH_IMAGE "org.freedesktop.machine1.NoSuchImage"
|
#define BUS_ERROR_NO_SUCH_IMAGE "org.freedesktop.machine1.NoSuchImage"
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#include <mqueue.h>
|
#include <mqueue.h>
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
|
#include <poll.h>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
@ -23,6 +24,7 @@
|
||||||
#include "process-util.h"
|
#include "process-util.h"
|
||||||
#include "socket-util.h"
|
#include "socket-util.h"
|
||||||
#include "strv.h"
|
#include "strv.h"
|
||||||
|
#include "time-util.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
#define SNDBUF_SIZE (8*1024*1024)
|
#define SNDBUF_SIZE (8*1024*1024)
|
||||||
|
@ -551,6 +553,34 @@ finish:
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_public_ int sd_notify_barrier(int unset_environment, uint64_t timeout) {
|
||||||
|
_cleanup_close_pair_ int pipe_fd[2] = { -1, -1 };
|
||||||
|
struct timespec ts;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
if (pipe2(pipe_fd, O_CLOEXEC) < 0)
|
||||||
|
return -errno;
|
||||||
|
|
||||||
|
r = sd_pid_notify_with_fds(0, unset_environment, "BARRIER=1", &pipe_fd[1], 1);
|
||||||
|
if (r <= 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
pipe_fd[1] = safe_close(pipe_fd[1]);
|
||||||
|
|
||||||
|
struct pollfd pfd = {
|
||||||
|
.fd = pipe_fd[0],
|
||||||
|
/* POLLHUP is implicit */
|
||||||
|
.events = 0,
|
||||||
|
};
|
||||||
|
r = ppoll(&pfd, 1, timeout == UINT64_MAX ? NULL : timespec_store(&ts, timeout), NULL);
|
||||||
|
if (r < 0)
|
||||||
|
return -errno;
|
||||||
|
if (r == 0)
|
||||||
|
return -ETIMEDOUT;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
_public_ int sd_pid_notify(pid_t pid, int unset_environment, const char *state) {
|
_public_ int sd_pid_notify(pid_t pid, int unset_environment, const char *state) {
|
||||||
return sd_pid_notify_with_fds(pid, unset_environment, state, NULL, 0);
|
return sd_pid_notify_with_fds(pid, unset_environment, state, NULL, 0);
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
#include "string-util.h"
|
#include "string-util.h"
|
||||||
#include "strv.h"
|
#include "strv.h"
|
||||||
#include "terminal-util.h"
|
#include "terminal-util.h"
|
||||||
|
#include "time-util.h"
|
||||||
#include "user-util.h"
|
#include "user-util.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
|
@ -27,6 +28,7 @@ static const char *arg_status = NULL;
|
||||||
static bool arg_booted = false;
|
static bool arg_booted = false;
|
||||||
static uid_t arg_uid = UID_INVALID;
|
static uid_t arg_uid = UID_INVALID;
|
||||||
static gid_t arg_gid = GID_INVALID;
|
static gid_t arg_gid = GID_INVALID;
|
||||||
|
static bool arg_no_block = false;
|
||||||
|
|
||||||
static int help(void) {
|
static int help(void) {
|
||||||
_cleanup_free_ char *link = NULL;
|
_cleanup_free_ char *link = NULL;
|
||||||
|
@ -45,6 +47,7 @@ static int help(void) {
|
||||||
" --uid=USER Set user to send from\n"
|
" --uid=USER Set user to send from\n"
|
||||||
" --status=TEXT Set status text\n"
|
" --status=TEXT Set status text\n"
|
||||||
" --booted Check if the system was booted up with systemd\n"
|
" --booted Check if the system was booted up with systemd\n"
|
||||||
|
" --no-block Do not wait until operation finished\n"
|
||||||
"\nSee the %s for details.\n"
|
"\nSee the %s for details.\n"
|
||||||
, program_invocation_short_name
|
, program_invocation_short_name
|
||||||
, ansi_highlight(), ansi_normal()
|
, ansi_highlight(), ansi_normal()
|
||||||
|
@ -83,6 +86,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||||
ARG_STATUS,
|
ARG_STATUS,
|
||||||
ARG_BOOTED,
|
ARG_BOOTED,
|
||||||
ARG_UID,
|
ARG_UID,
|
||||||
|
ARG_NO_BLOCK
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct option options[] = {
|
static const struct option options[] = {
|
||||||
|
@ -93,6 +97,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||||
{ "status", required_argument, NULL, ARG_STATUS },
|
{ "status", required_argument, NULL, ARG_STATUS },
|
||||||
{ "booted", no_argument, NULL, ARG_BOOTED },
|
{ "booted", no_argument, NULL, ARG_BOOTED },
|
||||||
{ "uid", required_argument, NULL, ARG_UID },
|
{ "uid", required_argument, NULL, ARG_UID },
|
||||||
|
{ "no-block", no_argument, NULL, ARG_NO_BLOCK },
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -157,6 +162,10 @@ static int parse_argv(int argc, char *argv[]) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case ARG_NO_BLOCK:
|
||||||
|
arg_no_block = true;
|
||||||
|
break;
|
||||||
|
|
||||||
case '?':
|
case '?':
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
|
@ -256,6 +265,16 @@ static int run(int argc, char* argv[]) {
|
||||||
if (r == 0)
|
if (r == 0)
|
||||||
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
|
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
|
||||||
"No status data could be sent: $NOTIFY_SOCKET was not set");
|
"No status data could be sent: $NOTIFY_SOCKET was not set");
|
||||||
|
|
||||||
|
if (!arg_no_block) {
|
||||||
|
r = sd_notify_barrier(0, 5 * USEC_PER_SEC);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to invoke barrier: %m");
|
||||||
|
if (r == 0)
|
||||||
|
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
|
||||||
|
"No status data could be sent: $NOTIFY_SOCKET was not set");
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3834,11 +3834,12 @@ static int kill_unit(int argc, char *argv[], void *userdata) {
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int clean_unit(int argc, char *argv[], void *userdata) {
|
static int clean_or_freeze_unit(int argc, char *argv[], void *userdata) {
|
||||||
_cleanup_(bus_wait_for_units_freep) BusWaitForUnits *w = NULL;
|
_cleanup_(bus_wait_for_units_freep) BusWaitForUnits *w = NULL;
|
||||||
_cleanup_strv_free_ char **names = NULL;
|
_cleanup_strv_free_ char **names = NULL;
|
||||||
int r, ret = EXIT_SUCCESS;
|
int r, ret = EXIT_SUCCESS;
|
||||||
char **name;
|
char **name;
|
||||||
|
const char *method;
|
||||||
sd_bus *bus;
|
sd_bus *bus;
|
||||||
|
|
||||||
r = acquire_bus(BUS_FULL, &bus);
|
r = acquire_bus(BUS_FULL, &bus);
|
||||||
|
@ -3863,6 +3864,13 @@ static int clean_unit(int argc, char *argv[], void *userdata) {
|
||||||
return log_error_errno(r, "Failed to allocate unit waiter: %m");
|
return log_error_errno(r, "Failed to allocate unit waiter: %m");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (streq(argv[0], "clean"))
|
||||||
|
method = "CleanUnit";
|
||||||
|
else if (streq(argv[0], "freeze"))
|
||||||
|
method = "FreezeUnit";
|
||||||
|
else if (streq(argv[0], "thaw"))
|
||||||
|
method = "ThawUnit";
|
||||||
|
|
||||||
STRV_FOREACH(name, names) {
|
STRV_FOREACH(name, names) {
|
||||||
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||||
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
|
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
|
||||||
|
@ -3892,7 +3900,7 @@ static int clean_unit(int argc, char *argv[], void *userdata) {
|
||||||
"org.freedesktop.systemd1",
|
"org.freedesktop.systemd1",
|
||||||
"/org/freedesktop/systemd1",
|
"/org/freedesktop/systemd1",
|
||||||
"org.freedesktop.systemd1.Manager",
|
"org.freedesktop.systemd1.Manager",
|
||||||
"CleanUnit");
|
method);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return bus_log_create_error(r);
|
return bus_log_create_error(r);
|
||||||
|
|
||||||
|
@ -3900,13 +3908,15 @@ static int clean_unit(int argc, char *argv[], void *userdata) {
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return bus_log_create_error(r);
|
return bus_log_create_error(r);
|
||||||
|
|
||||||
r = sd_bus_message_append_strv(m, arg_clean_what);
|
if (streq(method, "CleanUnit")) {
|
||||||
if (r < 0)
|
r = sd_bus_message_append_strv(m, arg_clean_what);
|
||||||
return bus_log_create_error(r);
|
if (r < 0)
|
||||||
|
return bus_log_create_error(r);
|
||||||
|
}
|
||||||
|
|
||||||
r = sd_bus_call(bus, m, 0, &error, NULL);
|
r = sd_bus_call(bus, m, 0, &error, NULL);
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
log_error_errno(r, "Failed to clean unit %s: %s", *name, bus_error_message(&error, r));
|
log_error_errno(r, "Failed to %s unit %s: %s", argv[0], *name, bus_error_message(&error, r));
|
||||||
if (ret == EXIT_SUCCESS) {
|
if (ret == EXIT_SUCCESS) {
|
||||||
ret = r;
|
ret = r;
|
||||||
continue;
|
continue;
|
||||||
|
@ -4046,6 +4056,7 @@ typedef struct UnitStatusInfo {
|
||||||
const char *id;
|
const char *id;
|
||||||
const char *load_state;
|
const char *load_state;
|
||||||
const char *active_state;
|
const char *active_state;
|
||||||
|
const char *freezer_state;
|
||||||
const char *sub_state;
|
const char *sub_state;
|
||||||
const char *unit_file_state;
|
const char *unit_file_state;
|
||||||
const char *unit_file_preset;
|
const char *unit_file_preset;
|
||||||
|
@ -4182,7 +4193,7 @@ static void print_status_info(
|
||||||
bool *ellipsized) {
|
bool *ellipsized) {
|
||||||
|
|
||||||
char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], since2[FORMAT_TIMESTAMP_MAX];
|
char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], since2[FORMAT_TIMESTAMP_MAX];
|
||||||
const char *s1, *s2, *active_on, *active_off, *on, *off, *ss;
|
const char *s1, *s2, *active_on, *active_off, *on, *off, *ss, *fs;
|
||||||
_cleanup_free_ char *formatted_path = NULL;
|
_cleanup_free_ char *formatted_path = NULL;
|
||||||
ExecStatusInfo *p;
|
ExecStatusInfo *p;
|
||||||
usec_t timestamp;
|
usec_t timestamp;
|
||||||
|
@ -4276,12 +4287,16 @@ static void print_status_info(
|
||||||
|
|
||||||
ss = streq_ptr(i->active_state, i->sub_state) ? NULL : i->sub_state;
|
ss = streq_ptr(i->active_state, i->sub_state) ? NULL : i->sub_state;
|
||||||
if (ss)
|
if (ss)
|
||||||
printf(" Active: %s%s (%s)%s",
|
printf(" Active: %s%s (%s)%s",
|
||||||
active_on, strna(i->active_state), ss, active_off);
|
active_on, strna(i->active_state), ss, active_off);
|
||||||
else
|
else
|
||||||
printf(" Active: %s%s%s",
|
printf(" Active: %s%s%s",
|
||||||
active_on, strna(i->active_state), active_off);
|
active_on, strna(i->active_state), active_off);
|
||||||
|
|
||||||
|
fs = !isempty(i->freezer_state) && !streq(i->freezer_state, "running") ? i->freezer_state : NULL;
|
||||||
|
if (fs)
|
||||||
|
printf(" %s(%s)%s", ansi_highlight_yellow(), fs, active_off);
|
||||||
|
|
||||||
if (!isempty(i->result) && !streq(i->result, "success"))
|
if (!isempty(i->result) && !streq(i->result, "success"))
|
||||||
printf(" (Result: %s)", i->result);
|
printf(" (Result: %s)", i->result);
|
||||||
|
|
||||||
|
@ -5539,12 +5554,14 @@ static int show_one(
|
||||||
static const struct bus_properties_map property_map[] = {
|
static const struct bus_properties_map property_map[] = {
|
||||||
{ "LoadState", "s", NULL, offsetof(UnitStatusInfo, load_state) },
|
{ "LoadState", "s", NULL, offsetof(UnitStatusInfo, load_state) },
|
||||||
{ "ActiveState", "s", NULL, offsetof(UnitStatusInfo, active_state) },
|
{ "ActiveState", "s", NULL, offsetof(UnitStatusInfo, active_state) },
|
||||||
|
{ "FreezerState", "s", NULL, offsetof(UnitStatusInfo, freezer_state) },
|
||||||
{ "Documentation", "as", NULL, offsetof(UnitStatusInfo, documentation) },
|
{ "Documentation", "as", NULL, offsetof(UnitStatusInfo, documentation) },
|
||||||
{}
|
{}
|
||||||
}, status_map[] = {
|
}, status_map[] = {
|
||||||
{ "Id", "s", NULL, offsetof(UnitStatusInfo, id) },
|
{ "Id", "s", NULL, offsetof(UnitStatusInfo, id) },
|
||||||
{ "LoadState", "s", NULL, offsetof(UnitStatusInfo, load_state) },
|
{ "LoadState", "s", NULL, offsetof(UnitStatusInfo, load_state) },
|
||||||
{ "ActiveState", "s", NULL, offsetof(UnitStatusInfo, active_state) },
|
{ "ActiveState", "s", NULL, offsetof(UnitStatusInfo, active_state) },
|
||||||
|
{ "FreezerState", "s", NULL, offsetof(UnitStatusInfo, freezer_state) },
|
||||||
{ "SubState", "s", NULL, offsetof(UnitStatusInfo, sub_state) },
|
{ "SubState", "s", NULL, offsetof(UnitStatusInfo, sub_state) },
|
||||||
{ "UnitFileState", "s", NULL, offsetof(UnitStatusInfo, unit_file_state) },
|
{ "UnitFileState", "s", NULL, offsetof(UnitStatusInfo, unit_file_state) },
|
||||||
{ "UnitFilePreset", "s", NULL, offsetof(UnitStatusInfo, unit_file_preset) },
|
{ "UnitFilePreset", "s", NULL, offsetof(UnitStatusInfo, unit_file_preset) },
|
||||||
|
@ -7887,6 +7904,8 @@ static int systemctl_help(void) {
|
||||||
" kill UNIT... Send signal to processes of a unit\n"
|
" kill UNIT... Send signal to processes of a unit\n"
|
||||||
" clean UNIT... Clean runtime, cache, state, logs or\n"
|
" clean UNIT... Clean runtime, cache, state, logs or\n"
|
||||||
" configuration of unit\n"
|
" configuration of unit\n"
|
||||||
|
" freeze PATTERN... Freeze execution of unit processes\n"
|
||||||
|
" thaw PATTERN... Resume execution of a frozen unit\n"
|
||||||
" is-active PATTERN... Check whether units are active\n"
|
" is-active PATTERN... Check whether units are active\n"
|
||||||
" is-failed PATTERN... Check whether units are failed\n"
|
" is-failed PATTERN... Check whether units are failed\n"
|
||||||
" status [PATTERN...|PID...] Show runtime status of one or more units\n"
|
" status [PATTERN...|PID...] Show runtime status of one or more units\n"
|
||||||
|
@ -9160,7 +9179,9 @@ static int systemctl_main(int argc, char *argv[]) {
|
||||||
{ "condrestart", 2, VERB_ANY, VERB_ONLINE_ONLY, start_unit }, /* For compatibility with RH */
|
{ "condrestart", 2, VERB_ANY, VERB_ONLINE_ONLY, start_unit }, /* For compatibility with RH */
|
||||||
{ "isolate", 2, 2, VERB_ONLINE_ONLY, start_unit },
|
{ "isolate", 2, 2, VERB_ONLINE_ONLY, start_unit },
|
||||||
{ "kill", 2, VERB_ANY, VERB_ONLINE_ONLY, kill_unit },
|
{ "kill", 2, VERB_ANY, VERB_ONLINE_ONLY, kill_unit },
|
||||||
{ "clean", 2, VERB_ANY, VERB_ONLINE_ONLY, clean_unit },
|
{ "clean", 2, VERB_ANY, VERB_ONLINE_ONLY, clean_or_freeze_unit },
|
||||||
|
{ "freeze", 2, VERB_ANY, VERB_ONLINE_ONLY, clean_or_freeze_unit },
|
||||||
|
{ "thaw", 2, VERB_ANY, VERB_ONLINE_ONLY, clean_or_freeze_unit },
|
||||||
{ "is-active", 2, VERB_ANY, VERB_ONLINE_ONLY, check_unit_active },
|
{ "is-active", 2, VERB_ANY, VERB_ONLINE_ONLY, check_unit_active },
|
||||||
{ "check", 2, VERB_ANY, VERB_ONLINE_ONLY, check_unit_active }, /* deprecated alias of is-active */
|
{ "check", 2, VERB_ANY, VERB_ONLINE_ONLY, check_unit_active }, /* deprecated alias of is-active */
|
||||||
{ "is-failed", 2, VERB_ANY, VERB_ONLINE_ONLY, check_unit_failed },
|
{ "is-failed", 2, VERB_ANY, VERB_ONLINE_ONLY, check_unit_failed },
|
||||||
|
|
|
@ -286,6 +286,19 @@ int sd_pid_notifyf(pid_t pid, int unset_environment, const char *format, ...) _s
|
||||||
*/
|
*/
|
||||||
int sd_pid_notify_with_fds(pid_t pid, int unset_environment, const char *state, const int *fds, unsigned n_fds);
|
int sd_pid_notify_with_fds(pid_t pid, int unset_environment, const char *state, const int *fds, unsigned n_fds);
|
||||||
|
|
||||||
|
/*
|
||||||
|
Returns > 0 if synchronization with systemd succeeded. Returns < 0
|
||||||
|
on error. Returns 0 if $NOTIFY_SOCKET was not set. Note that the
|
||||||
|
timeout parameter of this function call takes the timeout in µs, and
|
||||||
|
will be passed to ppoll(2), hence the behaviour will be similar to
|
||||||
|
ppoll(2). This function can be called after sending a status message
|
||||||
|
to systemd, if one needs to synchronize against reception of the
|
||||||
|
status messages sent before this call is made. Therefore, this
|
||||||
|
cannot be used to know if the status message was processed
|
||||||
|
successfully, but to only synchronize against its consumption.
|
||||||
|
*/
|
||||||
|
int sd_notify_barrier(int unset_environment, uint64_t timeout);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Returns > 0 if the system was booted with systemd. Returns < 0 on
|
Returns > 0 if the system was booted with systemd. Returns < 0 on
|
||||||
error. Returns 0 if the system was not booted with systemd. Note
|
error. Returns 0 if the system was not booted with systemd. Note
|
||||||
|
|
|
@ -384,22 +384,42 @@ static void test_cg_get_keyed_attribute(void) {
|
||||||
}
|
}
|
||||||
|
|
||||||
assert_se(cg_get_keyed_attribute("cpu", "/init.scope", "cpu.stat", STRV_MAKE("no_such_attr"), &val) == -ENXIO);
|
assert_se(cg_get_keyed_attribute("cpu", "/init.scope", "cpu.stat", STRV_MAKE("no_such_attr"), &val) == -ENXIO);
|
||||||
|
assert_se(cg_get_keyed_attribute_graceful("cpu", "/init.scope", "cpu.stat", STRV_MAKE("no_such_attr"), &val) == 0);
|
||||||
assert_se(val == NULL);
|
assert_se(val == NULL);
|
||||||
|
|
||||||
assert_se(cg_get_keyed_attribute("cpu", "/init.scope", "cpu.stat", STRV_MAKE("usage_usec"), &val) == 0);
|
assert_se(cg_get_keyed_attribute("cpu", "/init.scope", "cpu.stat", STRV_MAKE("usage_usec"), &val) == 0);
|
||||||
|
free(val);
|
||||||
|
|
||||||
|
assert_se(cg_get_keyed_attribute_graceful("cpu", "/init.scope", "cpu.stat", STRV_MAKE("usage_usec"), &val) == 1);
|
||||||
log_info("cpu /init.scope cpu.stat [usage_usec] → \"%s\"", val);
|
log_info("cpu /init.scope cpu.stat [usage_usec] → \"%s\"", val);
|
||||||
|
|
||||||
assert_se(cg_get_keyed_attribute("cpu", "/init.scope", "cpu.stat", STRV_MAKE("usage_usec", "no_such_attr"), vals3) == -ENXIO);
|
assert_se(cg_get_keyed_attribute("cpu", "/init.scope", "cpu.stat", STRV_MAKE("usage_usec", "no_such_attr"), vals3) == -ENXIO);
|
||||||
|
assert_se(cg_get_keyed_attribute_graceful("cpu", "/init.scope", "cpu.stat", STRV_MAKE("usage_usec", "no_such_attr"), vals3) == 1);
|
||||||
|
assert(vals3[0] && !vals3[1]);
|
||||||
|
free(vals3[0]);
|
||||||
|
|
||||||
assert_se(cg_get_keyed_attribute("cpu", "/init.scope", "cpu.stat", STRV_MAKE("usage_usec", "usage_usec"), vals3) == -ENXIO);
|
assert_se(cg_get_keyed_attribute("cpu", "/init.scope", "cpu.stat", STRV_MAKE("usage_usec", "usage_usec"), vals3) == -ENXIO);
|
||||||
|
assert_se(cg_get_keyed_attribute_graceful("cpu", "/init.scope", "cpu.stat", STRV_MAKE("usage_usec", "usage_usec"), vals3) == 1);
|
||||||
|
assert(vals3[0] && !vals3[1]);
|
||||||
|
free(vals3[0]);
|
||||||
|
|
||||||
assert_se(cg_get_keyed_attribute("cpu", "/init.scope", "cpu.stat",
|
assert_se(cg_get_keyed_attribute("cpu", "/init.scope", "cpu.stat",
|
||||||
STRV_MAKE("usage_usec", "user_usec", "system_usec"), vals3) == 0);
|
STRV_MAKE("usage_usec", "user_usec", "system_usec"), vals3) == 0);
|
||||||
|
for (i = 0; i < 3; i++)
|
||||||
|
free(vals3[i]);
|
||||||
|
|
||||||
|
assert_se(cg_get_keyed_attribute_graceful("cpu", "/init.scope", "cpu.stat",
|
||||||
|
STRV_MAKE("usage_usec", "user_usec", "system_usec"), vals3) == 3);
|
||||||
log_info("cpu /init.scope cpu.stat [usage_usec user_usec system_usec] → \"%s\", \"%s\", \"%s\"",
|
log_info("cpu /init.scope cpu.stat [usage_usec user_usec system_usec] → \"%s\", \"%s\", \"%s\"",
|
||||||
vals3[0], vals3[1], vals3[2]);
|
vals3[0], vals3[1], vals3[2]);
|
||||||
|
|
||||||
assert_se(cg_get_keyed_attribute("cpu", "/init.scope", "cpu.stat",
|
assert_se(cg_get_keyed_attribute("cpu", "/init.scope", "cpu.stat",
|
||||||
STRV_MAKE("system_usec", "user_usec", "usage_usec"), vals3a) == 0);
|
STRV_MAKE("system_usec", "user_usec", "usage_usec"), vals3a) == 0);
|
||||||
|
for (i = 0; i < 3; i++)
|
||||||
|
free(vals3a[i]);
|
||||||
|
|
||||||
|
assert_se(cg_get_keyed_attribute_graceful("cpu", "/init.scope", "cpu.stat",
|
||||||
|
STRV_MAKE("system_usec", "user_usec", "usage_usec"), vals3a) == 3);
|
||||||
log_info("cpu /init.scope cpu.stat [system_usec user_usec usage_usec] → \"%s\", \"%s\", \"%s\"",
|
log_info("cpu /init.scope cpu.stat [system_usec user_usec usage_usec] → \"%s\", \"%s\", \"%s\"",
|
||||||
vals3a[0], vals3a[1], vals3a[2]);
|
vals3a[0], vals3a[1], vals3a[2]);
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
../TEST-01-BASIC/Makefile
|
|
@ -0,0 +1,7 @@
|
||||||
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
TEST_DESCRIPTION="test unit freezing and thawing via DBus and systemctl"
|
||||||
|
TEST_NO_NSPAWN=1
|
||||||
|
. $TEST_BASE_DIR/test-functions
|
||||||
|
|
||||||
|
do_test "$@" 38
|
|
@ -102,6 +102,7 @@ BASICTOOLS=(
|
||||||
tar
|
tar
|
||||||
tee
|
tee
|
||||||
test
|
test
|
||||||
|
timeout
|
||||||
touch
|
touch
|
||||||
tr
|
tr
|
||||||
true
|
true
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
[Service]
|
||||||
|
ExecStart=/bin/sleep 3600
|
|
@ -0,0 +1,6 @@
|
||||||
|
[Unit]
|
||||||
|
Description=TEST-38-FREEZER
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
|
||||||
|
Type=oneshot
|
|
@ -0,0 +1,293 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -ex
|
||||||
|
set -o pipefail
|
||||||
|
|
||||||
|
systemd-analyze log-level debug
|
||||||
|
systemd-analyze log-target console
|
||||||
|
|
||||||
|
unit=testsuite-38-sleep.service
|
||||||
|
|
||||||
|
start_test_service() {
|
||||||
|
systemctl daemon-reload
|
||||||
|
systemctl start "${unit}"
|
||||||
|
}
|
||||||
|
|
||||||
|
dbus_freeze() {
|
||||||
|
local suffix=
|
||||||
|
suffix="${1##*.}"
|
||||||
|
|
||||||
|
local name="$(echo ${1%.$suffix} | sed s/-/_2d/g)"
|
||||||
|
local object_path="/org/freedesktop/systemd1/unit/${name}_2e${suffix}"
|
||||||
|
|
||||||
|
busctl call \
|
||||||
|
org.freedesktop.systemd1 \
|
||||||
|
"${object_path}" \
|
||||||
|
org.freedesktop.systemd1.Unit \
|
||||||
|
Freeze
|
||||||
|
}
|
||||||
|
|
||||||
|
dbus_thaw() {
|
||||||
|
local suffix=
|
||||||
|
suffix="${1##*.}"
|
||||||
|
|
||||||
|
local name="$(echo ${1%.$suffix} | sed s/-/_2d/g)"
|
||||||
|
local object_path="/org/freedesktop/systemd1/unit/${name}_2e${suffix}"
|
||||||
|
|
||||||
|
busctl call \
|
||||||
|
org.freedesktop.systemd1 \
|
||||||
|
"${object_path}" \
|
||||||
|
org.freedesktop.systemd1.Unit \
|
||||||
|
Thaw
|
||||||
|
}
|
||||||
|
|
||||||
|
dbus_freeze_unit() {
|
||||||
|
busctl call \
|
||||||
|
org.freedesktop.systemd1 \
|
||||||
|
/org/freedesktop/systemd1 \
|
||||||
|
org.freedesktop.systemd1.Manager \
|
||||||
|
FreezeUnit \
|
||||||
|
s \
|
||||||
|
"$1"
|
||||||
|
}
|
||||||
|
|
||||||
|
dbus_thaw_unit() {
|
||||||
|
busctl call \
|
||||||
|
org.freedesktop.systemd1 \
|
||||||
|
/org/freedesktop/systemd1 \
|
||||||
|
org.freedesktop.systemd1.Manager \
|
||||||
|
ThawUnit \
|
||||||
|
s \
|
||||||
|
"$1"
|
||||||
|
}
|
||||||
|
|
||||||
|
dbus_can_freeze() {
|
||||||
|
local suffix=
|
||||||
|
suffix="${1##*.}"
|
||||||
|
|
||||||
|
local name="$(echo ${1%.$suffix} | sed s/-/_2d/g)"
|
||||||
|
local object_path="/org/freedesktop/systemd1/unit/${name}_2e${suffix}"
|
||||||
|
|
||||||
|
busctl get-property \
|
||||||
|
org.freedesktop.systemd1 \
|
||||||
|
"${object_path}" \
|
||||||
|
org.freedesktop.systemd1.Unit \
|
||||||
|
CanFreeze
|
||||||
|
}
|
||||||
|
|
||||||
|
check_freezer_state() {
|
||||||
|
local suffix=
|
||||||
|
suffix="${1##*.}"
|
||||||
|
|
||||||
|
local name="$(echo ${1%.$suffix} | sed s/-/_2d/g)"
|
||||||
|
local object_path="/org/freedesktop/systemd1/unit/${name}_2e${suffix}"
|
||||||
|
|
||||||
|
state=$(busctl get-property \
|
||||||
|
org.freedesktop.systemd1 \
|
||||||
|
"${object_path}" \
|
||||||
|
org.freedesktop.systemd1.Unit \
|
||||||
|
FreezerState | cut -d " " -f2 | tr -d '"')
|
||||||
|
|
||||||
|
[ "$state" = "$2" ] || {
|
||||||
|
echo "error: unexpected freezer state, expected: $2, actual: $state" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
check_cgroup_state() {
|
||||||
|
grep -q "frozen $2" /sys/fs/cgroup/system.slice/"$1"/cgroup.events
|
||||||
|
}
|
||||||
|
|
||||||
|
test_dbus_api() {
|
||||||
|
echo "Test that DBus API works:"
|
||||||
|
echo -n " - Freeze(): "
|
||||||
|
dbus_freeze "${unit}"
|
||||||
|
check_freezer_state "${unit}" "frozen"
|
||||||
|
check_cgroup_state "$unit" 1
|
||||||
|
echo "[ OK ]"
|
||||||
|
|
||||||
|
echo -n " - Thaw(): "
|
||||||
|
dbus_thaw "${unit}"
|
||||||
|
check_freezer_state "${unit}" "running"
|
||||||
|
check_cgroup_state "$unit" 0
|
||||||
|
echo "[ OK ]"
|
||||||
|
|
||||||
|
echo -n " - FreezeUnit(): "
|
||||||
|
dbus_freeze_unit "${unit}"
|
||||||
|
check_freezer_state "${unit}" "frozen"
|
||||||
|
check_cgroup_state "$unit" 1
|
||||||
|
echo "[ OK ]"
|
||||||
|
|
||||||
|
echo -n " - ThawUnit(): "
|
||||||
|
dbus_thaw_unit "${unit}"
|
||||||
|
check_freezer_state "${unit}" "running"
|
||||||
|
check_cgroup_state "$unit" 0
|
||||||
|
echo "[ OK ]"
|
||||||
|
|
||||||
|
echo -n " - CanFreeze(): "
|
||||||
|
output=$(dbus_can_freeze "${unit}")
|
||||||
|
[ "$output" = "b true" ]
|
||||||
|
echo "[ OK ]"
|
||||||
|
|
||||||
|
echo
|
||||||
|
}
|
||||||
|
|
||||||
|
test_jobs() {
|
||||||
|
local pid_before=
|
||||||
|
local pid_after=
|
||||||
|
echo "Test that it is possible to apply jobs on frozen units:"
|
||||||
|
|
||||||
|
systemctl start "${unit}"
|
||||||
|
dbus_freeze "${unit}"
|
||||||
|
check_freezer_state "${unit}" "frozen"
|
||||||
|
|
||||||
|
echo -n " - restart: "
|
||||||
|
pid_before=$(systemctl show -p MainPID "${unit}" --value)
|
||||||
|
systemctl restart "${unit}"
|
||||||
|
pid_after=$(systemctl show -p MainPID "${unit}" --value)
|
||||||
|
[ "$pid_before" != "$pid_after" ] && echo "[ OK ]"
|
||||||
|
|
||||||
|
dbus_freeze "${unit}"
|
||||||
|
check_freezer_state "${unit}" "frozen"
|
||||||
|
|
||||||
|
echo -n " - stop: "
|
||||||
|
timeout 5s systemctl stop "${unit}"
|
||||||
|
echo "[ OK ]"
|
||||||
|
|
||||||
|
echo
|
||||||
|
}
|
||||||
|
|
||||||
|
test_systemctl() {
|
||||||
|
echo "Test that systemctl freeze/thaw verbs:"
|
||||||
|
|
||||||
|
systemctl start "$unit"
|
||||||
|
|
||||||
|
echo -n " - freeze: "
|
||||||
|
systemctl freeze "$unit"
|
||||||
|
check_freezer_state "${unit}" "frozen"
|
||||||
|
check_cgroup_state "$unit" 1
|
||||||
|
# Freezing already frozen unit should be NOP and return quickly
|
||||||
|
timeout 3s systemctl freeze "$unit"
|
||||||
|
echo "[ OK ]"
|
||||||
|
|
||||||
|
echo -n " - thaw: "
|
||||||
|
systemctl thaw "$unit"
|
||||||
|
check_freezer_state "${unit}" "running"
|
||||||
|
check_cgroup_state "$unit" 0
|
||||||
|
# Likewise thawing already running unit shouldn't block
|
||||||
|
timeout 3s systemctl thaw "$unit"
|
||||||
|
echo "[ OK ]"
|
||||||
|
|
||||||
|
systemctl stop "$unit"
|
||||||
|
|
||||||
|
echo
|
||||||
|
}
|
||||||
|
|
||||||
|
test_systemctl_show() {
|
||||||
|
echo "Test systemctl show integration:"
|
||||||
|
|
||||||
|
systemctl start "$unit"
|
||||||
|
|
||||||
|
echo -n " - FreezerState property: "
|
||||||
|
state=$(systemctl show -p FreezerState --value "$unit")
|
||||||
|
[ "$state" = "running" ]
|
||||||
|
systemctl freeze "$unit"
|
||||||
|
state=$(systemctl show -p FreezerState --value "$unit")
|
||||||
|
[ "$state" = "frozen" ]
|
||||||
|
systemctl thaw "$unit"
|
||||||
|
echo "[ OK ]"
|
||||||
|
|
||||||
|
echo -n " - CanFreeze property: "
|
||||||
|
state=$(systemctl show -p CanFreeze --value "$unit")
|
||||||
|
[ "$state" = "yes" ]
|
||||||
|
echo "[ OK ]"
|
||||||
|
|
||||||
|
systemctl stop "$unit"
|
||||||
|
echo
|
||||||
|
}
|
||||||
|
|
||||||
|
test_recursive() {
|
||||||
|
local slice="bar.slice"
|
||||||
|
local unit="baz.service"
|
||||||
|
|
||||||
|
systemd-run --unit "$unit" --slice "$slice" sleep 3600 >/dev/null 2>&1
|
||||||
|
|
||||||
|
echo "Test recursive freezing:"
|
||||||
|
|
||||||
|
echo -n " - freeze: "
|
||||||
|
systemctl freeze "$slice"
|
||||||
|
check_freezer_state "${slice}" "frozen"
|
||||||
|
check_freezer_state "${unit}" "frozen"
|
||||||
|
grep -q "frozen 1" /sys/fs/cgroup/"${slice}"/cgroup.events
|
||||||
|
grep -q "frozen 1" /sys/fs/cgroup/"${slice}"/"${unit}"/cgroup.events
|
||||||
|
echo "[ OK ]"
|
||||||
|
|
||||||
|
echo -n " - thaw: "
|
||||||
|
systemctl thaw "$slice"
|
||||||
|
check_freezer_state "${unit}" "running"
|
||||||
|
check_freezer_state "${slice}" "running"
|
||||||
|
grep -q "frozen 0" /sys/fs/cgroup/"${slice}"/cgroup.events
|
||||||
|
grep -q "frozen 0" /sys/fs/cgroup/"${slice}"/"${unit}"/cgroup.events
|
||||||
|
echo "[ OK ]"
|
||||||
|
|
||||||
|
systemctl stop "$unit"
|
||||||
|
systemctl stop "$slice"
|
||||||
|
|
||||||
|
echo
|
||||||
|
}
|
||||||
|
|
||||||
|
test_preserve_state() {
|
||||||
|
local slice="bar.slice"
|
||||||
|
local unit="baz.service"
|
||||||
|
|
||||||
|
systemd-run --unit "$unit" --slice "$slice" sleep 3600 >/dev/null 2>&1
|
||||||
|
|
||||||
|
echo "Test that freezer state is preserved when recursive freezing is initiated from outside (e.g. by manager up the tree):"
|
||||||
|
|
||||||
|
echo -n " - freeze from outside: "
|
||||||
|
echo 1 > /sys/fs/cgroup/"${slice}"/cgroup.freeze
|
||||||
|
|
||||||
|
# Our state should not be affected
|
||||||
|
check_freezer_state "${slice}" "running"
|
||||||
|
check_freezer_state "${unit}" "running"
|
||||||
|
|
||||||
|
# However actual kernel state should be frozen
|
||||||
|
grep -q "frozen 1" /sys/fs/cgroup/"${slice}"/cgroup.events
|
||||||
|
grep -q "frozen 1" /sys/fs/cgroup/"${slice}"/"${unit}"/cgroup.events
|
||||||
|
echo "[ OK ]"
|
||||||
|
|
||||||
|
echo -n " - thaw from outside: "
|
||||||
|
echo 0 > /sys/fs/cgroup/"${slice}"/cgroup.freeze
|
||||||
|
check_freezer_state "${unit}" "running"
|
||||||
|
check_freezer_state "${slice}" "running"
|
||||||
|
grep -q "frozen 0" /sys/fs/cgroup/"${slice}"/cgroup.events
|
||||||
|
grep -q "frozen 0" /sys/fs/cgroup/"${slice}"/"${unit}"/cgroup.events
|
||||||
|
echo "[ OK ]"
|
||||||
|
|
||||||
|
echo -n " - thaw from outside while inner service is frozen: "
|
||||||
|
systemctl freeze "$unit"
|
||||||
|
check_freezer_state "${unit}" "frozen"
|
||||||
|
echo 1 > /sys/fs/cgroup/"${slice}"/cgroup.freeze
|
||||||
|
echo 0 > /sys/fs/cgroup/"${slice}"/cgroup.freeze
|
||||||
|
check_freezer_state "${slice}" "running"
|
||||||
|
check_freezer_state "${unit}" "frozen"
|
||||||
|
echo "[ OK ]"
|
||||||
|
|
||||||
|
systemctl stop "$unit"
|
||||||
|
systemctl stop "$slice"
|
||||||
|
|
||||||
|
echo
|
||||||
|
}
|
||||||
|
|
||||||
|
test -e /sys/fs/cgroup/system.slice/cgroup.freeze && {
|
||||||
|
start_test_service
|
||||||
|
test_dbus_api
|
||||||
|
test_systemctl
|
||||||
|
test_systemctl_show
|
||||||
|
test_jobs
|
||||||
|
test_recursive
|
||||||
|
test_preserve_state
|
||||||
|
}
|
||||||
|
|
||||||
|
echo OK > /testok
|
||||||
|
exit 0
|
Loading…
Reference in New Issue