Merge pull request #26447 from poettering/sigqueue
pid1/systemctl: add ability to enqueue POSIX RT sigs with associated value to service processes
This commit is contained in:
@@ -112,6 +112,10 @@ node /org/freedesktop/systemd1 {
|
||||
KillUnit(in s name,
|
||||
in s whom,
|
||||
in i signal);
|
||||
QueueSignalUnit(in s name,
|
||||
in s whom,
|
||||
in i signal,
|
||||
in i value);
|
||||
CleanUnit(in s name,
|
||||
in as mask);
|
||||
FreezeUnit(in s name);
|
||||
@@ -826,6 +830,8 @@ node /org/freedesktop/systemd1 {
|
||||
|
||||
<variablelist class="dbus-method" generated="True" extra-ref="KillUnit()"/>
|
||||
|
||||
<variablelist class="dbus-method" generated="True" extra-ref="QueueSignalUnit()"/>
|
||||
|
||||
<variablelist class="dbus-method" generated="True" extra-ref="CleanUnit()"/>
|
||||
|
||||
<variablelist class="dbus-method" generated="True" extra-ref="FreezeUnit()"/>
|
||||
@@ -1286,6 +1292,13 @@ node /org/freedesktop/systemd1 {
|
||||
<varname>ExecStop=</varname> and is spawned in parallel to the main daemon process in order to shut it
|
||||
down.</para>
|
||||
|
||||
<para><function>QueueSignalUnit()</function> is similar to <function>KillUnit()</function> but may be
|
||||
used to enqueue a POSIX Realtime Signal (i.e. <constant>SIGRTMIN+…</constant> and
|
||||
<constant>SIGRTMAX-…</constant>) to the selected process(es). Takes the same paramaters as
|
||||
<function>KillUnit()</function> with one additional argument: an integer that is passed in the
|
||||
<varname>sival_int</varname> value accompanying the queued signal. See <citerefentry project="man-pages"><refentrytitle>sigqueue</refentrytitle><manvolnum>3</manvolnum></citerefentry> for
|
||||
details.</para>
|
||||
|
||||
<para><function>GetJob()</function> returns the job object path for a specific job, identified by its
|
||||
id.</para>
|
||||
|
||||
@@ -1731,7 +1744,8 @@ node /org/freedesktop/systemd1 {
|
||||
<para>Read access is generally granted to all clients. Additionally, for unprivileged clients, some
|
||||
operations are allowed through the polkit privilege system. Operations which modify unit state
|
||||
(<function>StartUnit()</function>, <function>StopUnit()</function>, <function>KillUnit()</function>,
|
||||
<function>RestartUnit()</function> and similar, <function>SetProperty()</function>) require
|
||||
<function>QueueSignalUnit()</function>, <function>RestartUnit()</function> and similar,
|
||||
<function>SetProperty()</function>) require
|
||||
<interfacename>org.freedesktop.systemd1.manage-units</interfacename>. Operations which modify unit file
|
||||
enablement state (<function>EnableUnitFiles()</function>, <function>DisableUnitFiles()</function>,
|
||||
<function>EnableUnitFilesWithFlags()</function>, <function>DisableUnitFilesWithFlags()</function>,
|
||||
@@ -1778,6 +1792,9 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
|
||||
out a(uosos) affected_jobs);
|
||||
Kill(in s whom,
|
||||
in i signal);
|
||||
QueueSignal(in s whom,
|
||||
in i signal,
|
||||
in i value);
|
||||
ResetFailed();
|
||||
SetProperties(in b runtime,
|
||||
in a(sv) properties);
|
||||
@@ -2084,6 +2101,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
|
||||
|
||||
<variablelist class="dbus-method" generated="True" extra-ref="Kill()"/>
|
||||
|
||||
<variablelist class="dbus-method" generated="True" extra-ref="QueueSignal()"/>
|
||||
|
||||
<variablelist class="dbus-method" generated="True" extra-ref="ResetFailed()"/>
|
||||
|
||||
<variablelist class="dbus-method" generated="True" extra-ref="SetProperties()"/>
|
||||
@@ -2302,12 +2321,12 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
|
||||
<para><function>Start()</function>, <function>Stop()</function>, <function>Reload()</function>,
|
||||
<function>Restart()</function>, <function>TryRestart()</function>,
|
||||
<function>ReloadOrRestart()</function>, <function>ReloadOrTryRestart()</function>,
|
||||
<function>Kill()</function>, <function>ResetFailed()</function>, and
|
||||
<function>SetProperties()</function> implement the same operation as the respective methods on the
|
||||
<function>Kill()</function>, <function>QueueSignal()</function>, <function>ResetFailed()</function>,
|
||||
and <function>SetProperties()</function> implement the same operation as the respective methods on the
|
||||
<interfacename>Manager</interfacename> object (see above). However, these methods operate on the unit
|
||||
object and hence do not take a unit name parameter. Invoking the methods directly on the Manager
|
||||
object has the advantage of not requiring a <function>GetUnit()</function> call to get the unit object
|
||||
for a specific unit name. Calling the methods on the Manager object is hence a round trip
|
||||
object and hence do not take a unit name parameter. Invoking the methods directly on the Manager object
|
||||
has the advantage of not requiring a <function>GetUnit()</function> call to get the unit object for a
|
||||
specific unit name. Calling the methods on the Manager object is hence a round trip
|
||||
optimization.</para>
|
||||
</refsect2>
|
||||
|
||||
|
||||
+41
-34
@@ -501,10 +501,11 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
|
||||
<term><command>kill <replaceable>PATTERN</replaceable>…</command></term>
|
||||
|
||||
<listitem>
|
||||
<para>Send a signal to one or more processes of the
|
||||
unit. Use <option>--kill-whom=</option> to select which
|
||||
process to kill. Use <option>--signal=</option> to select
|
||||
the signal to send.</para>
|
||||
<para>Send a UNIX process signal to one or more processes of the unit. Use
|
||||
<option>--kill-whom=</option> to select which process to send the signal to. Use
|
||||
<option>--signal=</option> to select the signal to send. Combine with
|
||||
<option>--kill-value=</option> to enqueue a POSIX Realtime Signal with an associated
|
||||
value.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
<varlistentry>
|
||||
@@ -2124,40 +2125,46 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
|
||||
<term><option>--kill-whom=</option></term>
|
||||
|
||||
<listitem>
|
||||
<para>When used with <command>kill</command>, choose which
|
||||
processes to send a signal to. Must be one of
|
||||
<option>main</option>, <option>control</option> or
|
||||
<option>all</option> to select whether to kill only the main
|
||||
process, the control process or all processes of the
|
||||
unit. The main process of the unit is the one that defines
|
||||
the life-time of it. A control process of a unit is one that
|
||||
is invoked by the manager to induce state changes of it. For
|
||||
example, all processes started due to the
|
||||
<varname>ExecStartPre=</varname>,
|
||||
<varname>ExecStop=</varname> or
|
||||
<varname>ExecReload=</varname> settings of service units are
|
||||
control processes. Note that there is only one control
|
||||
process per unit at a time, as only one state change is
|
||||
executed at a time. For services of type
|
||||
<varname>Type=forking</varname>, the initial process started
|
||||
by the manager for <varname>ExecStart=</varname> is a
|
||||
control process, while the process ultimately forked off by
|
||||
that one is then considered the main process of the unit (if
|
||||
it can be determined). This is different for service units
|
||||
of other types, where the process forked off by the manager
|
||||
for <varname>ExecStart=</varname> is always the main process
|
||||
itself. A service unit consists of zero or one main process,
|
||||
zero or one control process plus any number of additional
|
||||
processes. Not all unit types manage processes of these
|
||||
types however. For example, for mount units, control processes
|
||||
are defined (which are the invocations of
|
||||
<filename>&MOUNT_PATH;</filename> and
|
||||
<filename>&UMOUNT_PATH;</filename>), but no main process
|
||||
is defined. If omitted, defaults to
|
||||
<para>When used with <command>kill</command>, choose which processes to send a UNIX process signal
|
||||
to. Must be one of <option>main</option>, <option>control</option> or <option>all</option> to
|
||||
select whether to kill only the main process, the control process or all processes of the unit. The
|
||||
main process of the unit is the one that defines the life-time of it. A control process of a unit
|
||||
is one that is invoked by the manager to induce state changes of it. For example, all processes
|
||||
started due to the <varname>ExecStartPre=</varname>, <varname>ExecStop=</varname> or
|
||||
<varname>ExecReload=</varname> settings of service units are control processes. Note that there is
|
||||
only one control process per unit at a time, as only one state change is executed at a time. For
|
||||
services of type <varname>Type=forking</varname>, the initial process started by the manager for
|
||||
<varname>ExecStart=</varname> is a control process, while the process ultimately forked off by that
|
||||
one is then considered the main process of the unit (if it can be determined). This is different
|
||||
for service units of other types, where the process forked off by the manager for
|
||||
<varname>ExecStart=</varname> is always the main process itself. A service unit consists of zero or
|
||||
one main process, zero or one control process plus any number of additional processes. Not all unit
|
||||
types manage processes of these types however. For example, for mount units, control processes are
|
||||
defined (which are the invocations of <filename>&MOUNT_PATH;</filename> and
|
||||
<filename>&UMOUNT_PATH;</filename>), but no main process is defined. If omitted, defaults to
|
||||
<option>all</option>.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--kill-value=</option><replaceable>INT</replaceable></term>
|
||||
|
||||
<listitem><para>If used with the <command>kill</command> command, enqueues a signal along with the
|
||||
specified integer value parameter to the specified process(es). This operation is only available for
|
||||
POSIX Realtime Signals (i.e. <option>--signal=SIGRTMIN+…</option> or
|
||||
<option>--signal=SIGRTMAX-…</option>), and ensures the signals are generated via the <citerefentry
|
||||
project='man-pages'><refentrytitle>sigqueue</refentrytitle><manvolnum>3</manvolnum></citerefentry>
|
||||
system call, rather than <citerefentry
|
||||
project='man-pages'><refentrytitle>kill</refentrytitle><manvolnum>3</manvolnum></citerefentry>. The
|
||||
specified value must be a 32bit signed integer, and may be specified either in decimal, in
|
||||
hexademical (if prefixed with <literal>0x</literal>), octal (if prefixed with <literal>0o</literal>)
|
||||
or binary (if prefixed with <literal>0b</literal>)</para>
|
||||
|
||||
<para>If this option is used the signal will only be enqueued on the control or main process of the
|
||||
unit, never on other processes belonging to the unit, i.e. <option>--kill-whom=all</option> will only
|
||||
affect main and control processes but no other processes.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<xi:include href="standard-options.xml" xpointer="signal" />
|
||||
|
||||
<varlistentry>
|
||||
|
||||
@@ -25,6 +25,9 @@
|
||||
<cmdsynopsis>
|
||||
<command>systemd-notify <arg choice="opt" rep="repeat">OPTIONS</arg> <arg choice="opt" rep="repeat">VARIABLE=VALUE</arg></command>
|
||||
</cmdsynopsis>
|
||||
<cmdsynopsis>
|
||||
<command>systemd-notify --exec <arg choice="opt" rep="repeat">OPTIONS</arg> <arg choice="opt" rep="repeat">VARIABLE=VALUE</arg> ; <arg rep="repeat">CMDLINE</arg></command>
|
||||
</cmdsynopsis>
|
||||
</refsynopsisdiv>
|
||||
|
||||
<refsect1>
|
||||
@@ -174,6 +177,20 @@
|
||||
this option set is prone to race conditions in all other cases.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--exec</option></term>
|
||||
|
||||
<listitem><para>If specified <command>systemd-notify</command> will execute another command line
|
||||
after it completed its operation, replacing its own process. If used, the list of assignments to
|
||||
include in the message sent must be followed by a <literal>;</literal> character (as separate
|
||||
argument), followed by the command line to execute. This permits "chaining" of commands, i.e. issuing
|
||||
one operation, followed immediately by another, without changing PIDs.</para>
|
||||
|
||||
<para>Note that many shells interpret <literal>;</literal> as their own separator for command lines,
|
||||
hence when <command>systemd-notify</command> is invoked from a shell the semicolon must usually be
|
||||
escaped as <literal>\;</literal>.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<xi:include href="standard-options.xml" xpointer="help" />
|
||||
<xi:include href="standard-options.xml" xpointer="version" />
|
||||
</variablelist>
|
||||
|
||||
+8
-2
@@ -77,20 +77,26 @@ char** strv_free_erase(char **l) {
|
||||
return mfree(l);
|
||||
}
|
||||
|
||||
char** strv_copy(char * const *l) {
|
||||
char** strv_copy_n(char * const *l, size_t m) {
|
||||
_cleanup_strv_free_ char **result = NULL;
|
||||
char **k;
|
||||
|
||||
result = new(char*, strv_length(l) + 1);
|
||||
result = new(char*, MIN(strv_length(l), m) + 1);
|
||||
if (!result)
|
||||
return NULL;
|
||||
|
||||
k = result;
|
||||
STRV_FOREACH(i, l) {
|
||||
if (m == 0)
|
||||
break;
|
||||
|
||||
*k = strdup(*i);
|
||||
if (!*k)
|
||||
return NULL;
|
||||
k++;
|
||||
|
||||
if (m != SIZE_MAX)
|
||||
m--;
|
||||
}
|
||||
|
||||
*k = NULL;
|
||||
|
||||
+4
-1
@@ -29,7 +29,10 @@ char** strv_free_erase(char **l);
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(char**, strv_free_erase);
|
||||
#define _cleanup_strv_free_erase_ _cleanup_(strv_free_erasep)
|
||||
|
||||
char** strv_copy(char * const *l);
|
||||
char** strv_copy_n(char * const *l, size_t n);
|
||||
static inline char** strv_copy(char * const *l) {
|
||||
return strv_copy_n(l, SIZE_MAX);
|
||||
}
|
||||
size_t strv_length(char * const *l) _pure_;
|
||||
|
||||
int strv_extend_strv(char ***a, char * const *b, bool filter_duplicates);
|
||||
|
||||
@@ -3034,6 +3034,11 @@ const sd_bus_vtable bus_manager_vtable[] = {
|
||||
SD_BUS_NO_RESULT,
|
||||
method_kill_unit,
|
||||
SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_METHOD_WITH_ARGS("QueueSignalUnit",
|
||||
SD_BUS_ARGS("s", name, "s", whom, "i", signal, "i", value),
|
||||
SD_BUS_NO_RESULT,
|
||||
method_kill_unit,
|
||||
SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_METHOD_WITH_ARGS("CleanUnit",
|
||||
SD_BUS_ARGS("s", name, "as", mask),
|
||||
SD_BUS_NO_RESULT,
|
||||
|
||||
+22
-3
@@ -513,10 +513,11 @@ int bus_unit_method_enqueue_job(sd_bus_message *message, void *userdata, sd_bus_
|
||||
|
||||
int bus_unit_method_kill(sd_bus_message *message, void *userdata, sd_bus_error *error) {
|
||||
Unit *u = ASSERT_PTR(userdata);
|
||||
int32_t value = 0;
|
||||
const char *swho;
|
||||
int32_t signo;
|
||||
KillWho who;
|
||||
int r;
|
||||
int r, code;
|
||||
|
||||
assert(message);
|
||||
|
||||
@@ -528,17 +529,30 @@ int bus_unit_method_kill(sd_bus_message *message, void *userdata, sd_bus_error *
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (startswith(sd_bus_message_get_member(message), "QueueSignal")) {
|
||||
r = sd_bus_message_read(message, "i", &value);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
code = SI_QUEUE;
|
||||
} else
|
||||
code = SI_USER;
|
||||
|
||||
if (isempty(swho))
|
||||
who = KILL_ALL;
|
||||
else {
|
||||
who = kill_who_from_string(swho);
|
||||
if (who < 0)
|
||||
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid who argument %s", swho);
|
||||
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid who argument: %s", swho);
|
||||
}
|
||||
|
||||
if (!SIGNAL_VALID(signo))
|
||||
return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Signal number out of range.");
|
||||
|
||||
if (code == SI_QUEUE && !((signo >= SIGRTMIN) && (signo <= SIGRTMAX)))
|
||||
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
|
||||
"Value parameter only accepted for realtime signals (SIGRTMIN…SIGRTMAX), refusing for signal SIG%s.", signal_to_string(signo));
|
||||
|
||||
r = bus_verify_manage_units_async_full(
|
||||
u,
|
||||
"kill",
|
||||
@@ -552,7 +566,7 @@ int bus_unit_method_kill(sd_bus_message *message, void *userdata, sd_bus_error *
|
||||
if (r == 0)
|
||||
return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
|
||||
|
||||
r = unit_kill(u, who, signo, error);
|
||||
r = unit_kill(u, who, signo, code, value, error);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
@@ -984,6 +998,11 @@ const sd_bus_vtable bus_unit_vtable[] = {
|
||||
SD_BUS_NO_RESULT,
|
||||
bus_unit_method_kill,
|
||||
SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_METHOD_WITH_ARGS("QueueSignal",
|
||||
SD_BUS_ARGS("s", whom, "i", signal, "i", value),
|
||||
SD_BUS_NO_RESULT,
|
||||
bus_unit_method_kill,
|
||||
SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_METHOD("ResetFailed",
|
||||
NULL,
|
||||
NULL,
|
||||
|
||||
+2
-2
@@ -2158,12 +2158,12 @@ static void mount_reset_failed(Unit *u) {
|
||||
m->clean_result = MOUNT_SUCCESS;
|
||||
}
|
||||
|
||||
static int mount_kill(Unit *u, KillWho who, int signo, sd_bus_error *error) {
|
||||
static int mount_kill(Unit *u, KillWho who, int signo, int code, int value, sd_bus_error *error) {
|
||||
Mount *m = MOUNT(u);
|
||||
|
||||
assert(m);
|
||||
|
||||
return unit_kill_common(u, who, signo, -1, m->control_pid, error);
|
||||
return unit_kill_common(u, who, signo, code, value, -1, m->control_pid, error);
|
||||
}
|
||||
|
||||
static int mount_control_pid(Unit *u) {
|
||||
|
||||
@@ -246,6 +246,10 @@
|
||||
send_interface="org.freedesktop.systemd1.Manager"
|
||||
send_member="KillUnit"/>
|
||||
|
||||
<allow send_destination="org.freedesktop.systemd1"
|
||||
send_interface="org.freedesktop.systemd1.Manager"
|
||||
send_member="QueueSignalUnit"/>
|
||||
|
||||
<allow send_destination="org.freedesktop.systemd1"
|
||||
send_interface="org.freedesktop.systemd1.Manager"
|
||||
send_member="ResetFailedUnit"/>
|
||||
@@ -390,6 +394,10 @@
|
||||
send_interface="org.freedesktop.systemd1.Unit"
|
||||
send_member="Kill"/>
|
||||
|
||||
<allow send_destination="org.freedesktop.systemd1"
|
||||
send_interface="org.freedesktop.systemd1.Unit"
|
||||
send_member="QueueSignal"/>
|
||||
|
||||
<allow send_destination="org.freedesktop.systemd1"
|
||||
send_interface="org.freedesktop.systemd1.Unit"
|
||||
send_member="ResetFailed"/>
|
||||
|
||||
+2
-2
@@ -530,8 +530,8 @@ static void scope_reset_failed(Unit *u) {
|
||||
s->result = SCOPE_SUCCESS;
|
||||
}
|
||||
|
||||
static int scope_kill(Unit *u, KillWho who, int signo, sd_bus_error *error) {
|
||||
return unit_kill_common(u, who, signo, -1, -1, error);
|
||||
static int scope_kill(Unit *u, KillWho who, int signo, int code, int value, sd_bus_error *error) {
|
||||
return unit_kill_common(u, who, signo, code, value, -1, -1, error);
|
||||
}
|
||||
|
||||
static int scope_get_timeout(Unit *u, usec_t *timeout) {
|
||||
|
||||
+2
-2
@@ -4596,12 +4596,12 @@ static void service_reset_failed(Unit *u) {
|
||||
s->flush_n_restarts = false;
|
||||
}
|
||||
|
||||
static int service_kill(Unit *u, KillWho who, int signo, sd_bus_error *error) {
|
||||
static int service_kill(Unit *u, KillWho who, int signo, int code, int value, sd_bus_error *error) {
|
||||
Service *s = SERVICE(u);
|
||||
|
||||
assert(s);
|
||||
|
||||
return unit_kill_common(u, who, signo, s->main_pid, s->control_pid, error);
|
||||
return unit_kill_common(u, who, signo, code, value, s->main_pid, s->control_pid, error);
|
||||
}
|
||||
|
||||
static int service_main_pid(Unit *u) {
|
||||
|
||||
+2
-2
@@ -247,8 +247,8 @@ static int slice_stop(Unit *u) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int slice_kill(Unit *u, KillWho who, int signo, sd_bus_error *error) {
|
||||
return unit_kill_common(u, who, signo, -1, -1, error);
|
||||
static int slice_kill(Unit *u, KillWho who, int signo, int code, int value, sd_bus_error *error) {
|
||||
return unit_kill_common(u, who, signo, code, value, -1, -1, error);
|
||||
}
|
||||
|
||||
static int slice_serialize(Unit *u, FILE *f, FDSet *fds) {
|
||||
|
||||
+2
-2
@@ -3299,8 +3299,8 @@ static void socket_trigger_notify(Unit *u, Unit *other) {
|
||||
socket_set_state(s, SOCKET_RUNNING);
|
||||
}
|
||||
|
||||
static int socket_kill(Unit *u, KillWho who, int signo, sd_bus_error *error) {
|
||||
return unit_kill_common(u, who, signo, -1, SOCKET(u)->control_pid, error);
|
||||
static int socket_kill(Unit *u, KillWho who, int signo, int code, int value, sd_bus_error *error) {
|
||||
return unit_kill_common(u, who, signo, code, value, -1, SOCKET(u)->control_pid, error);
|
||||
}
|
||||
|
||||
static int socket_get_timeout(Unit *u, usec_t *timeout) {
|
||||
|
||||
+2
-2
@@ -1480,8 +1480,8 @@ static void swap_reset_failed(Unit *u) {
|
||||
s->clean_result = SWAP_SUCCESS;
|
||||
}
|
||||
|
||||
static int swap_kill(Unit *u, KillWho who, int signo, sd_bus_error *error) {
|
||||
return unit_kill_common(u, who, signo, -1, SWAP(u)->control_pid, error);
|
||||
static int swap_kill(Unit *u, KillWho who, int signo, int code, int value, sd_bus_error *error) {
|
||||
return unit_kill_common(u, who, signo, code, value, -1, SWAP(u)->control_pid, error);
|
||||
}
|
||||
|
||||
static int swap_get_timeout(Unit *u, usec_t *timeout) {
|
||||
|
||||
+96
-54
@@ -3785,15 +3785,16 @@ bool unit_will_restart(Unit *u) {
|
||||
return UNIT_VTABLE(u)->will_restart(u);
|
||||
}
|
||||
|
||||
int unit_kill(Unit *u, KillWho w, int signo, sd_bus_error *error) {
|
||||
int unit_kill(Unit *u, KillWho w, int signo, int code, int value, sd_bus_error *error) {
|
||||
assert(u);
|
||||
assert(w >= 0 && w < _KILL_WHO_MAX);
|
||||
assert(SIGNAL_VALID(signo));
|
||||
assert(IN_SET(code, SI_USER, SI_QUEUE));
|
||||
|
||||
if (!UNIT_VTABLE(u)->kill)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
return UNIT_VTABLE(u)->kill(u, w, signo, error);
|
||||
return UNIT_VTABLE(u)->kill(u, w, signo, code, value, error);
|
||||
}
|
||||
|
||||
void unit_notify_cgroup_oom(Unit *u, bool managed_oom) {
|
||||
@@ -3838,21 +3839,48 @@ static int kill_common_log(pid_t pid, int signo, void *userdata) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int kill_or_sigqueue(pid_t pid, int signo, int code, int value) {
|
||||
assert(pid > 0);
|
||||
assert(SIGNAL_VALID(signo));
|
||||
|
||||
switch (code) {
|
||||
|
||||
case SI_USER:
|
||||
log_debug("Killing " PID_FMT " with signal SIG%s.", pid, signal_to_string(signo));
|
||||
return RET_NERRNO(kill(pid, signo));
|
||||
|
||||
case SI_QUEUE:
|
||||
log_debug("Enqueuing value %i to " PID_FMT " on signal SIG%s.", value, pid, signal_to_string(signo));
|
||||
return RET_NERRNO(sigqueue(pid, signo, (const union sigval) { .sival_int = value }));
|
||||
|
||||
default:
|
||||
assert_not_reached();
|
||||
}
|
||||
}
|
||||
|
||||
int unit_kill_common(
|
||||
Unit *u,
|
||||
KillWho who,
|
||||
int signo,
|
||||
int code,
|
||||
int value,
|
||||
pid_t main_pid,
|
||||
pid_t control_pid,
|
||||
sd_bus_error *error) {
|
||||
|
||||
int r = 0;
|
||||
bool killed = false;
|
||||
int ret = 0, r;
|
||||
|
||||
/* This is the common implementation for explicit user-requested killing of unit processes, shared by
|
||||
* various unit types. Do not confuse with unit_kill_context(), which is what we use when we want to
|
||||
* stop a service ourselves. */
|
||||
|
||||
assert(u);
|
||||
assert(who >= 0);
|
||||
assert(who < _KILL_WHO_MAX);
|
||||
assert(SIGNAL_VALID(signo));
|
||||
assert(IN_SET(code, SI_USER, SI_QUEUE));
|
||||
|
||||
if (IN_SET(who, KILL_MAIN, KILL_MAIN_FAIL)) {
|
||||
if (main_pid < 0)
|
||||
return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_PROCESS, "%s units have no main processes", unit_type_to_string(u->type));
|
||||
@@ -3867,71 +3895,85 @@ int unit_kill_common(
|
||||
return sd_bus_error_set_const(error, BUS_ERROR_NO_SUCH_PROCESS, "No control process to kill");
|
||||
}
|
||||
|
||||
if (IN_SET(who, KILL_CONTROL, KILL_CONTROL_FAIL, KILL_ALL, KILL_ALL_FAIL))
|
||||
if (control_pid > 0) {
|
||||
_cleanup_free_ char *comm = NULL;
|
||||
(void) get_process_comm(control_pid, &comm);
|
||||
if (control_pid > 0 &&
|
||||
IN_SET(who, KILL_CONTROL, KILL_CONTROL_FAIL, KILL_ALL, KILL_ALL_FAIL)) {
|
||||
_cleanup_free_ char *comm = NULL;
|
||||
(void) get_process_comm(control_pid, &comm);
|
||||
|
||||
r = kill_or_sigqueue(control_pid, signo, code, value);
|
||||
if (r < 0) {
|
||||
ret = r;
|
||||
|
||||
/* Report this failure both to the logs and to the client */
|
||||
sd_bus_error_set_errnof(
|
||||
error, r,
|
||||
"Failed to send signal SIG%s to control process " PID_FMT " (%s): %m",
|
||||
signal_to_string(signo), control_pid, strna(comm));
|
||||
log_unit_warning_errno(
|
||||
u, r,
|
||||
"Failed to send signal SIG%s to control process " PID_FMT " (%s) on client request: %m",
|
||||
signal_to_string(signo), control_pid, strna(comm));
|
||||
} else {
|
||||
log_unit_info(u, "Sent signal SIG%s to control process " PID_FMT " (%s) on client request.",
|
||||
signal_to_string(signo), control_pid, strna(comm));
|
||||
killed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (main_pid > 0 &&
|
||||
IN_SET(who, KILL_MAIN, KILL_MAIN_FAIL, KILL_ALL, KILL_ALL_FAIL)) {
|
||||
|
||||
_cleanup_free_ char *comm = NULL;
|
||||
(void) get_process_comm(main_pid, &comm);
|
||||
|
||||
r = kill_or_sigqueue(main_pid, signo, code, value);
|
||||
if (r < 0) {
|
||||
if (ret == 0) {
|
||||
ret = r;
|
||||
|
||||
if (kill(control_pid, signo) < 0) {
|
||||
/* Report this failure both to the logs and to the client */
|
||||
sd_bus_error_set_errnof(
|
||||
error, errno,
|
||||
"Failed to send signal SIG%s to control process " PID_FMT " (%s): %m",
|
||||
signal_to_string(signo), control_pid, strna(comm));
|
||||
r = log_unit_warning_errno(
|
||||
u, errno,
|
||||
"Failed to send signal SIG%s to control process " PID_FMT " (%s) on client request: %m",
|
||||
signal_to_string(signo), control_pid, strna(comm));
|
||||
} else {
|
||||
log_unit_info(u, "Sent signal SIG%s to control process " PID_FMT " (%s) on client request.",
|
||||
signal_to_string(signo), control_pid, strna(comm));
|
||||
killed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (IN_SET(who, KILL_MAIN, KILL_MAIN_FAIL, KILL_ALL, KILL_ALL_FAIL))
|
||||
if (main_pid > 0) {
|
||||
_cleanup_free_ char *comm = NULL;
|
||||
(void) get_process_comm(main_pid, &comm);
|
||||
|
||||
if (kill(main_pid, signo) < 0) {
|
||||
if (r == 0)
|
||||
sd_bus_error_set_errnof(
|
||||
error, errno,
|
||||
"Failed to send signal SIG%s to main process " PID_FMT " (%s): %m",
|
||||
signal_to_string(signo), main_pid, strna(comm));
|
||||
|
||||
r = log_unit_warning_errno(
|
||||
u, errno,
|
||||
"Failed to send signal SIG%s to main process " PID_FMT " (%s) on client request: %m",
|
||||
error, r,
|
||||
"Failed to send signal SIG%s to main process " PID_FMT " (%s): %m",
|
||||
signal_to_string(signo), main_pid, strna(comm));
|
||||
} else {
|
||||
log_unit_info(u, "Sent signal SIG%s to main process " PID_FMT " (%s) on client request.",
|
||||
signal_to_string(signo), main_pid, strna(comm));
|
||||
killed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (IN_SET(who, KILL_ALL, KILL_ALL_FAIL) && u->cgroup_path) {
|
||||
log_unit_warning_errno(
|
||||
u, r,
|
||||
"Failed to send signal SIG%s to main process " PID_FMT " (%s) on client request: %m",
|
||||
signal_to_string(signo), main_pid, strna(comm));
|
||||
|
||||
} else {
|
||||
log_unit_info(u, "Sent signal SIG%s to main process " PID_FMT " (%s) on client request.",
|
||||
signal_to_string(signo), main_pid, strna(comm));
|
||||
killed = true;
|
||||
}
|
||||
}
|
||||
|
||||
/* Note: if we shall enqueue rather than kill we won't do this via the cgroup mechanism, since it
|
||||
* doesn't really make much sense (and given that enqueued values are a relatively expensive
|
||||
* resource, and we shouldn't allow us to be subjects for such allocation sprees) */
|
||||
if (IN_SET(who, KILL_ALL, KILL_ALL_FAIL) && u->cgroup_path && code == SI_USER) {
|
||||
_cleanup_set_free_ Set *pid_set = NULL;
|
||||
int q;
|
||||
|
||||
/* Exclude the main/control pids from being killed via the cgroup */
|
||||
pid_set = unit_pid_set(main_pid, control_pid);
|
||||
if (!pid_set)
|
||||
return log_oom();
|
||||
|
||||
q = cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, signo, 0, pid_set, kill_common_log, u);
|
||||
if (q < 0) {
|
||||
if (!IN_SET(q, -ESRCH, -ENOENT)) {
|
||||
if (r == 0)
|
||||
r = cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, signo, 0, pid_set, kill_common_log, u);
|
||||
if (r < 0) {
|
||||
if (!IN_SET(r, -ESRCH, -ENOENT)) {
|
||||
if (ret == 0) {
|
||||
ret = r;
|
||||
|
||||
sd_bus_error_set_errnof(
|
||||
error, q,
|
||||
error, r,
|
||||
"Failed to send signal SIG%s to auxiliary processes: %m",
|
||||
signal_to_string(signo));
|
||||
}
|
||||
|
||||
r = log_unit_warning_errno(
|
||||
u, q,
|
||||
log_unit_warning_errno(
|
||||
u, r,
|
||||
"Failed to send signal SIG%s to auxiliary processes on client request: %m",
|
||||
signal_to_string(signo));
|
||||
}
|
||||
@@ -3940,10 +3982,10 @@ int unit_kill_common(
|
||||
}
|
||||
|
||||
/* If the "fail" versions of the operation are requested, then complain if the set of processes we killed is empty */
|
||||
if (r == 0 && !killed && IN_SET(who, KILL_ALL_FAIL, KILL_CONTROL_FAIL, KILL_MAIN_FAIL))
|
||||
if (ret == 0 && !killed && IN_SET(who, KILL_ALL_FAIL, KILL_CONTROL_FAIL, KILL_MAIN_FAIL))
|
||||
return sd_bus_error_set_const(error, BUS_ERROR_NO_SUCH_PROCESS, "No matching processes to kill");
|
||||
|
||||
return r;
|
||||
return ret;
|
||||
}
|
||||
|
||||
int unit_following_set(Unit *u, Set **s) {
|
||||
|
||||
+3
-3
@@ -619,7 +619,7 @@ typedef struct UnitVTable {
|
||||
int (*stop)(Unit *u);
|
||||
int (*reload)(Unit *u);
|
||||
|
||||
int (*kill)(Unit *u, KillWho w, int signo, sd_bus_error *error);
|
||||
int (*kill)(Unit *u, KillWho w, int signo, int code, int value, sd_bus_error *error);
|
||||
|
||||
/* Clear out the various runtime/state/cache/logs/configuration data */
|
||||
int (*clean)(Unit *u, ExecCleanMask m);
|
||||
@@ -889,8 +889,8 @@ int unit_start(Unit *u, ActivationDetails *details);
|
||||
int unit_stop(Unit *u);
|
||||
int unit_reload(Unit *u);
|
||||
|
||||
int unit_kill(Unit *u, KillWho w, int signo, sd_bus_error *error);
|
||||
int unit_kill_common(Unit *u, KillWho who, int signo, pid_t main_pid, pid_t control_pid, sd_bus_error *error);
|
||||
int unit_kill(Unit *u, KillWho w, int signo, int code, int value, sd_bus_error *error);
|
||||
int unit_kill_common(Unit *u, KillWho who, int signo, int code, int value, pid_t main_pid, pid_t control_pid, sd_bus_error *error);
|
||||
|
||||
void unit_notify_cgroup_oom(Unit *u, bool managed_oom);
|
||||
|
||||
|
||||
+56
-3
@@ -31,6 +31,11 @@ static bool arg_booted = false;
|
||||
static uid_t arg_uid = UID_INVALID;
|
||||
static gid_t arg_gid = GID_INVALID;
|
||||
static bool arg_no_block = false;
|
||||
static char **arg_env = NULL;
|
||||
static char **arg_exec = NULL;
|
||||
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_env, strv_freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_exec, strv_freep);
|
||||
|
||||
static int help(void) {
|
||||
_cleanup_free_ char *link = NULL;
|
||||
@@ -41,6 +46,7 @@ static int help(void) {
|
||||
return log_oom();
|
||||
|
||||
printf("%s [OPTIONS...] [VARIABLE=VALUE...]\n"
|
||||
"%s [OPTIONS...] --exec [VARIABLE=VALUE...] ; CMDLINE...\n"
|
||||
"\n%sNotify the init system about service status updates.%s\n\n"
|
||||
" -h --help Show this help\n"
|
||||
" --version Show package version\n"
|
||||
@@ -53,8 +59,10 @@ static int help(void) {
|
||||
" --status=TEXT Set status text\n"
|
||||
" --booted Check if the system was booted up with systemd\n"
|
||||
" --no-block Do not wait until operation finished\n"
|
||||
" --exec Execute command line separated by ';' once done\n"
|
||||
"\nSee the %s for details.\n",
|
||||
program_invocation_short_name,
|
||||
program_invocation_short_name,
|
||||
ansi_highlight(),
|
||||
ansi_normal(),
|
||||
link);
|
||||
@@ -93,7 +101,8 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
ARG_STATUS,
|
||||
ARG_BOOTED,
|
||||
ARG_UID,
|
||||
ARG_NO_BLOCK
|
||||
ARG_NO_BLOCK,
|
||||
ARG_EXEC,
|
||||
};
|
||||
|
||||
static const struct option options[] = {
|
||||
@@ -107,10 +116,12 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
{ "booted", no_argument, NULL, ARG_BOOTED },
|
||||
{ "uid", required_argument, NULL, ARG_UID },
|
||||
{ "no-block", no_argument, NULL, ARG_NO_BLOCK },
|
||||
{ "exec", no_argument, NULL, ARG_EXEC },
|
||||
{}
|
||||
};
|
||||
|
||||
int c, r;
|
||||
bool do_exec = false;
|
||||
int c, r, n_env;
|
||||
|
||||
assert(argc >= 0);
|
||||
assert(argv);
|
||||
@@ -183,6 +194,10 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
arg_no_block = true;
|
||||
break;
|
||||
|
||||
case ARG_EXEC:
|
||||
do_exec = true;
|
||||
break;
|
||||
|
||||
case '?':
|
||||
return -EINVAL;
|
||||
|
||||
@@ -202,6 +217,32 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (do_exec) {
|
||||
int i;
|
||||
|
||||
for (i = optind; i < argc; i++)
|
||||
if (streq(argv[i], ";"))
|
||||
break;
|
||||
|
||||
if (i >= argc)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "If --exec is used argument list must contain ';' separator, refusing.");
|
||||
if (i+1 == argc)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Empty command line specified after ';' separator, refusing");
|
||||
|
||||
arg_exec = strv_copy_n(argv + i + 1, argc - i - 1);
|
||||
if (!arg_exec)
|
||||
return log_oom();
|
||||
|
||||
n_env = i - optind;
|
||||
} else
|
||||
n_env = argc - optind;
|
||||
|
||||
if (n_env > 0) {
|
||||
arg_env = strv_copy_n(argv + optind, n_env);
|
||||
if (!arg_env)
|
||||
return log_oom();
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -263,7 +304,7 @@ static int run(int argc, char* argv[]) {
|
||||
|
||||
our_env[i++] = NULL;
|
||||
|
||||
final_env = strv_env_merge(our_env, argv + optind);
|
||||
final_env = strv_env_merge(our_env, arg_env);
|
||||
if (!final_env)
|
||||
return log_oom();
|
||||
|
||||
@@ -313,6 +354,18 @@ static int run(int argc, char* argv[]) {
|
||||
"No status data could be sent: $NOTIFY_SOCKET was not set");
|
||||
}
|
||||
|
||||
if (arg_exec) {
|
||||
_cleanup_free_ char *cmdline = NULL;
|
||||
|
||||
execvp(arg_exec[0], arg_exec);
|
||||
|
||||
cmdline = strv_join(arg_exec, " ");
|
||||
if (!cmdline)
|
||||
return log_oom();
|
||||
|
||||
return log_error_errno(errno, "Failed to execute command line: %s", cmdline);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
int verb_kill(int argc, char *argv[], void *userdata) {
|
||||
_cleanup_strv_free_ char **names = NULL;
|
||||
char *kill_whom = NULL;
|
||||
const char *kill_whom;
|
||||
sd_bus *bus;
|
||||
int r, q;
|
||||
|
||||
@@ -18,12 +18,11 @@ int verb_kill(int argc, char *argv[], void *userdata) {
|
||||
|
||||
polkit_agent_open_maybe();
|
||||
|
||||
if (!arg_kill_whom)
|
||||
arg_kill_whom = "all";
|
||||
kill_whom = arg_kill_whom ?: "all";
|
||||
|
||||
/* --fail was specified */
|
||||
if (streq(arg_job_mode(), "fail"))
|
||||
kill_whom = strjoina(arg_kill_whom, "-fail");
|
||||
kill_whom = strjoina(kill_whom, "-fail");
|
||||
|
||||
r = expand_unit_names(bus, strv_skip(argv, 1), NULL, &names, NULL);
|
||||
if (r < 0)
|
||||
@@ -32,13 +31,22 @@ int verb_kill(int argc, char *argv[], void *userdata) {
|
||||
STRV_FOREACH(name, names) {
|
||||
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||
|
||||
q = bus_call_method(
|
||||
bus,
|
||||
bus_systemd_mgr,
|
||||
"KillUnit",
|
||||
&error,
|
||||
NULL,
|
||||
"ssi", *name, kill_whom ?: arg_kill_whom, arg_signal);
|
||||
if (arg_kill_value_set)
|
||||
q = bus_call_method(
|
||||
bus,
|
||||
bus_systemd_mgr,
|
||||
"QueueSignalUnit",
|
||||
&error,
|
||||
NULL,
|
||||
"ssii", *name, kill_whom, arg_signal, arg_kill_value);
|
||||
else
|
||||
q = bus_call_method(
|
||||
bus,
|
||||
bus_systemd_mgr,
|
||||
"KillUnit",
|
||||
&error,
|
||||
NULL,
|
||||
"ssi", *name, kill_whom, arg_signal);
|
||||
if (q < 0) {
|
||||
log_error_errno(q, "Failed to kill unit %s: %s", *name, bus_error_message(&error, q));
|
||||
if (r == 0)
|
||||
|
||||
@@ -97,6 +97,8 @@ UnitFilePresetMode arg_preset_mode = UNIT_FILE_PRESET_FULL;
|
||||
char **arg_wall = NULL;
|
||||
const char *arg_kill_whom = NULL;
|
||||
int arg_signal = SIGTERM;
|
||||
int arg_kill_value;
|
||||
bool arg_kill_value_set = false;
|
||||
char *arg_root = NULL;
|
||||
char *arg_image = NULL;
|
||||
usec_t arg_when = 0;
|
||||
@@ -273,6 +275,7 @@ static int systemctl_help(void) {
|
||||
" sleeping, or hibernating\n"
|
||||
" -i Shortcut for --check-inhibitors=no\n"
|
||||
" --kill-whom=WHOM Whom to send signal to\n"
|
||||
" --kill-value=INT Signal value to enqueue\n"
|
||||
" -s --signal=SIGNAL Which signal to send\n"
|
||||
" --what=RESOURCES Which types of resources to remove\n"
|
||||
" --now Start or stop unit after enabling or disabling it\n"
|
||||
@@ -419,6 +422,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
|
||||
ARG_IMAGE,
|
||||
ARG_NO_RELOAD,
|
||||
ARG_KILL_WHOM,
|
||||
ARG_KILL_VALUE,
|
||||
ARG_NO_ASK_PASSWORD,
|
||||
ARG_FAILED,
|
||||
ARG_RUNTIME,
|
||||
@@ -479,6 +483,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
|
||||
{ "force", no_argument, NULL, 'f' },
|
||||
{ "no-reload", no_argument, NULL, ARG_NO_RELOAD },
|
||||
{ "kill-whom", required_argument, NULL, ARG_KILL_WHOM },
|
||||
{ "kill-value", required_argument, NULL, ARG_KILL_VALUE },
|
||||
{ "signal", required_argument, NULL, 's' },
|
||||
{ "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
|
||||
{ "host", required_argument, NULL, 'H' },
|
||||
@@ -723,6 +728,30 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
|
||||
arg_kill_whom = optarg;
|
||||
break;
|
||||
|
||||
case ARG_KILL_VALUE: {
|
||||
unsigned u;
|
||||
|
||||
if (isempty(optarg)) {
|
||||
arg_kill_value_set = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* First, try to parse unsigned, so that we can support the prefixes 0x, 0o, 0b */
|
||||
r = safe_atou_full(optarg, 0, &u);
|
||||
if (r < 0)
|
||||
/* If this didn't work, try as signed integer, without those prefixes */
|
||||
r = safe_atoi(optarg, &arg_kill_value);
|
||||
else if (u > INT_MAX)
|
||||
r = -ERANGE;
|
||||
else
|
||||
arg_kill_value = (int) u;
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Unable to parse signal queue value: %s", optarg);
|
||||
|
||||
arg_kill_value_set = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case 's':
|
||||
r = parse_signal_argument(optarg, &arg_signal);
|
||||
if (r <= 0)
|
||||
|
||||
@@ -77,6 +77,8 @@ extern UnitFilePresetMode arg_preset_mode;
|
||||
extern char **arg_wall;
|
||||
extern const char *arg_kill_whom;
|
||||
extern int arg_signal;
|
||||
extern int arg_kill_value;
|
||||
extern bool arg_kill_value_set;
|
||||
extern char *arg_root;
|
||||
extern usec_t arg_when;
|
||||
extern const char *arg_reboot_argument;
|
||||
|
||||
@@ -73,7 +73,7 @@ static void wait_for_service_finish(Manager *m, Unit *unit) {
|
||||
n = now(CLOCK_MONOTONIC);
|
||||
if (ts + timeout < n) {
|
||||
log_error("Test timeout when testing %s", unit->id);
|
||||
r = unit_kill(unit, KILL_ALL, SIGKILL, NULL);
|
||||
r = unit_kill(unit, KILL_ALL, SIGKILL, SI_USER, 0, NULL);
|
||||
if (r < 0)
|
||||
log_error_errno(r, "Failed to kill %s: %m", unit->id);
|
||||
exit(EXIT_FAILURE);
|
||||
|
||||
@@ -954,4 +954,44 @@ TEST(strv_extend_join) {
|
||||
assert_se(streq(v[1], "ABC=QER"));
|
||||
}
|
||||
|
||||
TEST(strv_copy_n) {
|
||||
char **x = STRV_MAKE("a", "b", "c", "d", "e");
|
||||
_cleanup_strv_free_ char **l = NULL;
|
||||
|
||||
l = strv_copy_n(x, 0);
|
||||
assert_se(strv_equal(l, NULL));
|
||||
strv_free(l);
|
||||
|
||||
l = strv_copy_n(x, 0);
|
||||
assert_se(strv_equal(l, (char**) { NULL }));
|
||||
strv_free(l);
|
||||
|
||||
l = strv_copy_n(x, 1);
|
||||
assert_se(strv_equal(l, STRV_MAKE("a")));
|
||||
strv_free(l);
|
||||
|
||||
l = strv_copy_n(x, 2);
|
||||
assert_se(strv_equal(l, STRV_MAKE("a", "b")));
|
||||
strv_free(l);
|
||||
|
||||
l = strv_copy_n(x, 3);
|
||||
assert_se(strv_equal(l, STRV_MAKE("a", "b", "c")));
|
||||
strv_free(l);
|
||||
|
||||
l = strv_copy_n(x, 4);
|
||||
assert_se(strv_equal(l, STRV_MAKE("a", "b", "c", "d")));
|
||||
strv_free(l);
|
||||
|
||||
l = strv_copy_n(x, 5);
|
||||
assert_se(strv_equal(l, STRV_MAKE("a", "b", "c", "d", "e")));
|
||||
strv_free(l);
|
||||
|
||||
l = strv_copy_n(x, 6);
|
||||
assert_se(strv_equal(l, STRV_MAKE("a", "b", "c", "d", "e")));
|
||||
strv_free(l);
|
||||
|
||||
l = strv_copy_n(x, SIZE_MAX);
|
||||
assert_se(strv_equal(l, STRV_MAKE("a", "b", "c", "d", "e")));
|
||||
}
|
||||
|
||||
DEFINE_TEST_MAIN(LOG_INFO);
|
||||
|
||||
Symlink
+1
@@ -0,0 +1 @@
|
||||
../TEST-01-BASIC/Makefile
|
||||
Executable
+10
@@ -0,0 +1,10 @@
|
||||
#!/usr/bin/env bash
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
set -e
|
||||
|
||||
TEST_DESCRIPTION="Test queue signal logic"
|
||||
|
||||
# shellcheck source=test/test-functions
|
||||
. "$TEST_BASE_DIR/test-functions"
|
||||
|
||||
do_test "$@"
|
||||
@@ -0,0 +1,7 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
[Unit]
|
||||
Description=TEST-78-SIGQUEUE
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
ExecStart=/usr/lib/systemd/tests/testdata/units/%N.sh
|
||||
Executable
+36
@@ -0,0 +1,36 @@
|
||||
#!/usr/bin/env bash
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
set -ex
|
||||
set -o pipefail
|
||||
|
||||
if ! env --block-signal=SIGUSR1 true 2> /dev/null ; then
|
||||
echo "env tool too old, can't block signals, skipping test." >&2
|
||||
echo OK >/testok
|
||||
exit 0
|
||||
fi
|
||||
|
||||
systemd-analyze log-level debug
|
||||
|
||||
UNIT="test-sigqueue-$RANDOM.service"
|
||||
|
||||
systemd-run -u "$UNIT" -p Type=notify -p DynamicUser=1 -- env --block-signal=SIGRTMIN+7 systemd-notify --exec --ready \; sleep infinity
|
||||
|
||||
systemctl kill --kill-whom=main --kill-value=4 --signal=SIGRTMIN+7 "$UNIT"
|
||||
systemctl kill --kill-whom=main --kill-value=4 --signal=SIGRTMIN+7 "$UNIT"
|
||||
systemctl kill --kill-whom=main --kill-value=7 --signal=SIGRTMIN+7 "$UNIT"
|
||||
systemctl kill --kill-whom=main --kill-value=16 --signal=SIGRTMIN+7 "$UNIT"
|
||||
systemctl kill --kill-whom=main --kill-value=32 --signal=SIGRTMIN+7 "$UNIT"
|
||||
systemctl kill --kill-whom=main --kill-value=16 --signal=SIGRTMIN+7 "$UNIT"
|
||||
|
||||
# We simply check that six signals are queued now. There's no easy way to check
|
||||
# from shell wich ones those are, hence we don't check that.
|
||||
P=$(systemctl show -P MainPID "$UNIT")
|
||||
|
||||
test "$(grep SigQ: /proc/"$P"/status | cut -d: -f2 | cut -d/ -f1)" -eq 6
|
||||
|
||||
systemctl stop $UNIT
|
||||
|
||||
systemd-analyze log-level info
|
||||
echo OK >/testok
|
||||
|
||||
exit 0
|
||||
Reference in New Issue
Block a user