1
0
mirror of https://github.com/systemd/systemd.git synced 2025-01-11 09:18:07 +03:00

Merge pull request #18481 from keszybz/rpm-restart-post-trans

Restart units after the rpm transaction
This commit is contained in:
Zbigniew Jędrzejewski-Szmek 2021-02-16 08:25:49 +01:00 committed by GitHub
commit 8f50eb04ac
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
41 changed files with 1635 additions and 1170 deletions

View File

@ -176,6 +176,7 @@ node /org/freedesktop/systemd1 {
UnsetEnvironment(in as names);
UnsetAndSetEnvironment(in as names,
in as assignments);
EnqueueMarkedJobs(out ao jobs);
ListUnitFiles(out a(ss) unit_files);
ListUnitFilesByPatterns(in as states,
in as patterns,
@ -848,6 +849,8 @@ node /org/freedesktop/systemd1 {
<variablelist class="dbus-method" generated="True" extra-ref="UnsetAndSetEnvironment()"/>
<variablelist class="dbus-method" generated="True" extra-ref="EnqueueMarkedJobs()"/>
<variablelist class="dbus-method" generated="True" extra-ref="ListUnitFiles()"/>
<variablelist class="dbus-method" generated="True" extra-ref="ListUnitFilesByPatterns()"/>
@ -1171,6 +1174,11 @@ node /org/freedesktop/systemd1 {
the "Try" flavor is used in which case a service that isn't running is not affected by the restart. The
"ReloadOrRestart" flavors attempt a reload if the unit supports it and use a restart otherwise.</para>
<para><function>EnqueueMarkedJobs()</function> creates reload/restart jobs for units which have been
appropriately marked, see <varname>Marks</varname> property above. This is equivalent to calling
<function>TryRestartUnit()</function> or <function>ReloadOrTryRestartUnit()</function> for the marked
units.</para>
<para><function>BindMountUnit()</function> can be used to bind mount new files or directories into
a running service mount namespace.</para>
@ -1685,6 +1693,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
readonly b IgnoreOnIsolate = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly b NeedDaemonReload = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("false")
readonly as Markers = ['...', ...];
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
readonly t JobTimeoutUSec = ...;
@org.freedesktop.DBus.Property.EmitsChangedSignal("const")
@ -1969,6 +1979,8 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
<variablelist class="dbus-property" generated="True" extra-ref="NeedDaemonReload"/>
<variablelist class="dbus-property" generated="True" extra-ref="Markers"/>
<variablelist class="dbus-property" generated="True" extra-ref="JobTimeoutUSec"/>
<variablelist class="dbus-property" generated="True" extra-ref="JobRunningTimeoutUSec"/>
@ -2160,8 +2172,16 @@ node /org/freedesktop/systemd1/unit/avahi_2ddaemon_2eservice {
<para><varname>NeedDaemonReload</varname> is a boolean that indicates whether the configuration file
this unit is loaded from (i.e. <varname>FragmentPath</varname> or <varname>SourcePath</varname>) has
changed since the configuration was read and hence whether a configuration reload is
recommended.</para>
changed since the configuration was read and hence whether a configuration reload is recommended.
</para>
<para><varname>Markers</varname> is an array of string flags that can be set using
<function>SetUnitProperties()</function> to indicate that the service should be reloaded or
restarted. Currently known values are <literal>needs-restart</literal> and
<literal>needs-reload</literal>. Package scripts may use the first to mark units for later restart when
a new version of the package is installed. Configuration management scripts may use the second to mark
units for a later reload when the configuration is adjusted. Those flags are not set by the manager,
except to unset as appropriate when when the unit is stopped, restarted, or reloaded.</para>
<para><varname>JobTimeoutUSec</varname> maps directly to the corresponding configuration setting in the
unit file.</para>

View File

@ -36,11 +36,13 @@
<refsect1>
<title>Description</title>
<para><function>sd_bus_message_read_strv()</function> gives access to an array of strings in message
<parameter>m</parameter>. The "read pointer" in the message must be right before an array of strings. On
success, a pointer to the <constant>NULL</constant>-terminated array of strings is returned in the output
parameter <parameter>l</parameter>. Note that ownership of this array is transferred to the caller.
Hence, the caller is responsible for freeing this array and its contents.</para>
<para><function>sd_bus_message_read_strv()</function> gives access to an array of string-like items in
message <parameter>m</parameter>. The "read pointer" in the message must be right before an array of
strings (D-Bus type <literal>as</literal>), object paths (D-Bus type <literal>ao</literal>), or
signatures (D-Bus type <literal>ag</literal>). On success, a pointer to a
<constant>NULL</constant>-terminated array of strings is returned in the output parameter
<parameter>l</parameter>. Note that ownership of this array is transferred to the caller. Hence, the
caller is responsible for freeing this array and its contents.</para>
</refsect1>
<refsect1>
@ -73,6 +75,13 @@
<listitem><para>The message cannot be parsed.</para></listitem>
</varlistentry>
<varlistentry>
<term><constant>-ENXIO</constant></term>
<listitem><para>The message "read pointer" is not right before an array of the appropriate type.
</para></listitem>
</varlistentry>
</variablelist>
</refsect2>
</refsect1>

View File

@ -2304,6 +2304,18 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err
a directory, but a regular file, device node, socket or FIFO.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--marked</option></term>
<listitem><para>Only allowed with <command>reload-or-restart</command>. Enqueues restart jobs for all
units that have the <literal>needs-restart</literal> mark, and reload jobs for units that have the
<literal>needs-reload</literal> mark. When a unit marked for reload does not support reload, restart
will be queued. Those properties can be set using <command>set-property Marks</command>.</para>
<para>Unless <option>--no-block</option> is used, <command>systemctl</command> will wait for the
queued jobs to finish.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--read-only</option></term>

View File

@ -1746,6 +1746,7 @@ subdir('src/partition')
subdir('src/portable')
subdir('src/pstore')
subdir('src/resolve')
subdir('src/rpm')
subdir('src/shutdown')
subdir('src/sysext')
subdir('src/systemctl')

View File

@ -13,6 +13,7 @@
#include "path-util.h"
#include "strv.h"
#include "unit-name.h"
#include "unit-serialize.h"
static int prepare_filename(const char *filename, char **ret) {
int r;

View File

@ -1277,7 +1277,7 @@ static int dot(int argc, char *argv[], void *userdata) {
if (r < 0)
return r;
r = bus_call_method(bus, bus_systemd_mgr, "ListUnits", &error, &reply, "");
r = bus_call_method(bus, bus_systemd_mgr, "ListUnits", &error, &reply, NULL);
if (r < 0)
log_error_errno(r, "Failed to list units: %s", bus_error_message(&error, r));

View File

@ -117,6 +117,13 @@ static const char* const freezer_state_table[_FREEZER_STATE_MAX] = {
DEFINE_STRING_TABLE_LOOKUP(freezer_state, FreezerState);
static const char* const unit_marker_table[_UNIT_MARKER_MAX] = {
[UNIT_MARKER_NEEDS_RELOAD] = "needs-reload",
[UNIT_MARKER_NEEDS_RESTART] = "needs-restart",
};
DEFINE_STRING_TABLE_LOOKUP(unit_marker, UnitMarker);
static const char* const automount_state_table[_AUTOMOUNT_STATE_MAX] = {
[AUTOMOUNT_DEAD] = "dead",
[AUTOMOUNT_WAITING] = "waiting",

View File

@ -58,6 +58,13 @@ typedef enum FreezerState {
_FREEZER_STATE_INVALID = -EINVAL,
} FreezerState;
typedef enum UnitMarker {
UNIT_MARKER_NEEDS_RELOAD,
UNIT_MARKER_NEEDS_RESTART,
_UNIT_MARKER_MAX,
_UNIT_MARKER_INVALID = -1
} UnitMarker;
typedef enum AutomountState {
AUTOMOUNT_DEAD,
AUTOMOUNT_WAITING,
@ -267,6 +274,9 @@ 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 *unit_marker_to_string(UnitMarker m) _const_;
UnitMarker unit_marker_from_string(const char *s) _pure_;
const char* automount_state_to_string(AutomountState i) _const_;
AutomountState automount_state_from_string(const char *s) _pure_;

View File

@ -443,7 +443,7 @@ static int find_nodes(sd_bus *bus, const char *service, const char *path, Set *p
r = sd_bus_call_method(bus, service, path,
"org.freedesktop.DBus.Introspectable", "Introspect",
&error, &reply, "");
&error, &reply, NULL);
if (r < 0) {
printf("%sFailed to introspect object %s of service %s: %s%s\n",
ansi_highlight_red(),
@ -982,7 +982,7 @@ static int introspect(int argc, char **argv, void *userdata) {
r = sd_bus_call_method(bus, argv[1], argv[2],
"org.freedesktop.DBus.Introspectable", "Introspect",
&error, &reply_xml, "");
&error, &reply_xml, NULL);
if (r < 0)
return log_error_errno(r, "Failed to introspect object %s of service %s: %s",
argv[2], argv[1], bus_error_message(&error, r));

View File

@ -38,8 +38,8 @@
#include "virt.h"
#include "watchdog.h"
/* Require 16MiB free in /run/systemd for reloading/reexecing. After all we need to serialize our state there, and if
* we can't we'll fail badly. */
/* Require 16MiB free in /run/systemd for reloading/reexecing. After all we need to serialize our state
* there, and if we can't we'll fail badly. */
#define RELOAD_DISK_SPACE_MIN (UINT64_C(16) * UINT64_C(1024) * UINT64_C(1024))
static UnitFileFlags unit_file_bools_to_flags(bool runtime, bool force) {
@ -363,8 +363,8 @@ static int bus_get_unit_by_name(Manager *m, sd_bus_message *message, const char
assert(message);
assert(ret_unit);
/* More or less a wrapper around manager_get_unit() that generates nice errors and has one trick up its sleeve:
* if the name is specified empty we use the client's unit. */
/* More or less a wrapper around manager_get_unit() that generates nice errors and has one trick up
* its sleeve: if the name is specified empty we use the client's unit. */
if (isempty(name)) {
_cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
@ -520,7 +520,8 @@ static int method_get_unit_by_invocation_id(sd_bus_message *message, void *userd
u = manager_get_unit_by_pid(m, pid);
if (!u)
return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_UNIT, "Client " PID_FMT " not member of any unit.", pid);
return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_UNIT,
"Client " PID_FMT " not member of any unit.", pid);
} else {
u = hashmap_get(m->units_by_invocation_id, &id);
if (!u)
@ -531,8 +532,9 @@ static int method_get_unit_by_invocation_id(sd_bus_message *message, void *userd
if (r < 0)
return r;
/* So here's a special trick: the bus path we return actually references the unit by its invocation ID instead
* of the unit name. This means it stays valid only as long as the invocation ID stays the same. */
/* So here's a special trick: the bus path we return actually references the unit by its invocation
* ID instead of the unit name. This means it stays valid only as long as the invocation ID stays the
* same. */
path = unit_dbus_path_invocation_id(u);
if (!path)
return -ENOMEM;
@ -552,7 +554,9 @@ static int method_get_unit_by_control_group(sd_bus_message *message, void *userd
u = manager_get_unit_by_cgroup(m, cgroup);
if (!u)
return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_UNIT, "Control group '%s' is not valid or not managed by this instance", cgroup);
return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_UNIT,
"Control group '%s' is not valid or not managed by this instance",
cgroup);
return reply_unit_path(u, message, error);
}
@ -851,17 +855,21 @@ static int transient_unit_from_message(
t = unit_name_to_type(name);
if (t < 0)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid unit name or type.");
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
"Invalid unit name or type.");
if (!unit_vtable[t]->can_transient)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unit type %s does not support transient units.", unit_type_to_string(t));
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
"Unit type %s does not support transient units.",
unit_type_to_string(t));
r = manager_load_unit(m, name, NULL, error, &u);
if (r < 0)
return r;
if (!unit_is_pristine(u))
return sd_bus_error_setf(error, BUS_ERROR_UNIT_EXISTS, "Unit %s already exists.", name);
return sd_bus_error_setf(error, BUS_ERROR_UNIT_EXISTS,
"Unit %s already exists.", name);
/* OK, the unit failed to load and is unreferenced, now let's
* fill in the transient data instead */
@ -1435,7 +1443,8 @@ static int method_reboot(sd_bus_message *message, void *userdata, sd_bus_error *
return r;
if (!MANAGER_IS_SYSTEM(m))
return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Reboot is only supported for system managers.");
return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED,
"Reboot is only supported for system managers.");
m->objective = MANAGER_REBOOT;
@ -1454,7 +1463,8 @@ static int method_poweroff(sd_bus_message *message, void *userdata, sd_bus_error
return r;
if (!MANAGER_IS_SYSTEM(m))
return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Powering off is only supported for system managers.");
return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED,
"Powering off is only supported for system managers.");
m->objective = MANAGER_POWEROFF;
@ -1473,7 +1483,8 @@ static int method_halt(sd_bus_message *message, void *userdata, sd_bus_error *er
return r;
if (!MANAGER_IS_SYSTEM(m))
return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Halt is only supported for system managers.");
return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED,
"Halt is only supported for system managers.");
m->objective = MANAGER_HALT;
@ -1492,7 +1503,8 @@ static int method_kexec(sd_bus_message *message, void *userdata, sd_bus_error *e
return r;
if (!MANAGER_IS_SYSTEM(m))
return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "KExec is only supported for system managers.");
return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED,
"KExec is only supported for system managers.");
m->objective = MANAGER_KEXEC;
@ -1517,7 +1529,7 @@ static int method_switch_root(sd_bus_message *message, void *userdata, sd_bus_er
if (available < RELOAD_DISK_SPACE_MIN) {
char fb_available[FORMAT_BYTES_MAX], fb_need[FORMAT_BYTES_MAX];
log_warning("Dangerously low amount of free space on /run/systemd, root switching operation might not complete successfully. "
log_warning("Dangerously low amount of free space on /run/systemd, root switching might fail.\n"
"Currently, %s are free, but %s are suggested. Proceeding anyway.",
format_bytes(fb_available, sizeof(fb_available), available),
format_bytes(fb_need, sizeof(fb_need), RELOAD_DISK_SPACE_MIN));
@ -1528,41 +1540,53 @@ static int method_switch_root(sd_bus_message *message, void *userdata, sd_bus_er
return r;
if (!MANAGER_IS_SYSTEM(m))
return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Root switching is only supported by system manager.");
return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED,
"Root switching is only supported by system manager.");
r = sd_bus_message_read(message, "ss", &root, &init);
if (r < 0)
return r;
if (isempty(root))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "New root directory may not be the empty string.");
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
"New root directory may not be the empty string.");
if (!path_is_absolute(root))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "New root path '%s' is not absolute.", root);
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
"New root path '%s' is not absolute.", root);
if (path_equal(root, "/"))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "New root directory cannot be the old root directory.");
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
"New root directory cannot be the old root directory.");
/* Safety check */
if (isempty(init)) {
r = path_is_os_tree(root);
if (r < 0)
return sd_bus_error_set_errnof(error, r, "Failed to determine whether root path '%s' contains an OS tree: %m", root);
return sd_bus_error_set_errnof(error, r,
"Failed to determine whether root path '%s' contains an OS tree: %m",
root);
if (r == 0)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Specified switch root path '%s' does not seem to be an OS tree. os-release file is missing.", root);
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
"Specified switch root path '%s' does not seem to be an OS tree. os-release file is missing.",
root);
} else {
_cleanup_free_ char *chased = NULL;
if (!path_is_absolute(init))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Path to init binary '%s' not absolute.", init);
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
"Path to init binary '%s' not absolute.", init);
r = chase_symlinks(init, root, CHASE_PREFIX_ROOT|CHASE_TRAIL_SLASH, &chased, NULL);
if (r < 0)
return sd_bus_error_set_errnof(error, r, "Could not resolve init executable %s: %m", init);
return sd_bus_error_set_errnof(error, r,
"Could not resolve init executable %s: %m", init);
if (laccess(chased, X_OK) < 0) {
if (errno == EACCES)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Init binary %s is not executable.", init);
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
"Init binary %s is not executable.", init);
return sd_bus_error_set_errnof(error, r, "Could not check whether init binary %s is executable: %m", init);
return sd_bus_error_set_errnof(error, r,
"Could not check whether init binary %s is executable: %m", init);
}
}
@ -1632,7 +1656,8 @@ static int method_unset_environment(sd_bus_message *message, void *userdata, sd_
return r;
if (!strv_env_name_or_assignment_is_valid(minus))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid environment variable names or assignments");
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
"Invalid environment variable names or assignments");
r = bus_verify_set_environment_async(m, message, error);
if (r < 0)
@ -1668,9 +1693,11 @@ static int method_unset_and_set_environment(sd_bus_message *message, void *userd
return r;
if (!strv_env_name_or_assignment_is_valid(minus))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid environment variable names or assignments");
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
"Invalid environment variable names or assignments");
if (!strv_env_is_valid(plus))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid environment assignments");
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
"Invalid environment assignments");
r = bus_verify_set_environment_async(m, message, error);
if (r < 0)
@ -1723,13 +1750,16 @@ static int method_lookup_dynamic_user_by_name(sd_bus_message *message, void *use
return r;
if (!MANAGER_IS_SYSTEM(m))
return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Dynamic users are only supported in the system instance.");
return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED,
"Dynamic users are only supported in the system instance.");
if (!valid_user_group_name(name, VALID_USER_RELAX))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "User name invalid: %s", name);
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
"User name invalid: %s", name);
r = dynamic_user_lookup_name(m, name, &uid);
if (r == -ESRCH)
return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_DYNAMIC_USER, "Dynamic user %s does not exist.", name);
return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_DYNAMIC_USER,
"Dynamic user %s does not exist.", name);
if (r < 0)
return r;
@ -1751,13 +1781,16 @@ static int method_lookup_dynamic_user_by_uid(sd_bus_message *message, void *user
return r;
if (!MANAGER_IS_SYSTEM(m))
return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Dynamic users are only supported in the system instance.");
return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED,
"Dynamic users are only supported in the system instance.");
if (!uid_is_valid(uid))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "User ID invalid: " UID_FMT, uid);
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
"User ID invalid: " UID_FMT, uid);
r = dynamic_user_lookup_uid(m, uid, &name);
if (r == -ESRCH)
return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_DYNAMIC_USER, "Dynamic user ID " UID_FMT " does not exist.", uid);
return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_DYNAMIC_USER,
"Dynamic user ID " UID_FMT " does not exist.", uid);
if (r < 0)
return r;
@ -1776,7 +1809,8 @@ static int method_get_dynamic_users(sd_bus_message *message, void *userdata, sd_
assert_cc(sizeof(uid_t) == sizeof(uint32_t));
if (!MANAGER_IS_SYSTEM(m))
return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Dynamic users are only supported in the system instance.");
return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED,
"Dynamic users are only supported in the system instance.");
r = sd_bus_message_new_method_return(message, &reply);
if (r < 0)
@ -1793,7 +1827,8 @@ static int method_get_dynamic_users(sd_bus_message *message, void *userdata, sd_
if (r == -EAGAIN) /* not realized yet? */
continue;
if (r < 0)
return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Failed to look up a dynamic user.");
return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED,
"Failed to look up a dynamic user.");
r = sd_bus_message_append(reply, "(us)", uid, d->name);
if (r < 0)
@ -1807,6 +1842,75 @@ static int method_get_dynamic_users(sd_bus_message *message, void *userdata, sd_
return sd_bus_send(NULL, reply, NULL);
}
static int method_enqueue_marked_jobs(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Manager *m = userdata;
int r;
assert(message);
assert(m);
r = mac_selinux_access_check(message, "start", error);
if (r < 0)
return r;
r = bus_verify_manage_units_async(m, 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 */
log_info("Queuing reload/restart jobs for marked units…");
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
r = sd_bus_message_new_method_return(message, &reply);
if (r < 0)
return r;
r = sd_bus_message_open_container(reply, 'a', "o");
if (r < 0)
return r;
Unit *u;
char *k;
int ret = 0;
HASHMAP_FOREACH_KEY(u, k, m->units) {
/* ignore aliases */
if (u->id != k)
continue;
BusUnitQueueFlags flags;
if (FLAGS_SET(u->markers, 1u << UNIT_MARKER_NEEDS_RESTART))
flags = 0;
else if (FLAGS_SET(u->markers, 1u << UNIT_MARKER_NEEDS_RELOAD))
flags = BUS_UNIT_QUEUE_RELOAD_IF_POSSIBLE;
else
continue;
r = mac_selinux_unit_access_check(u, message, "start", error);
if (r >= 0)
r = bus_unit_queue_job_one(message, u,
JOB_TRY_RESTART, JOB_FAIL, flags,
reply, error);
if (r < 0) {
if (ERRNO_IS_RESOURCE(r))
return r;
if (ret >= 0)
ret = r;
sd_bus_error_free(error);
}
}
if (ret < 0)
return sd_bus_error_set_errnof(error, ret,
"Failed to enqueue some jobs, see logs for details: %m");
r = sd_bus_message_close_container(reply);
if (r < 0)
return r;
return sd_bus_send(NULL, reply, NULL);
}
static int list_unit_files_by_patterns(sd_bus_message *message, void *userdata, sd_bus_error *error, char **states, char **patterns) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
Manager *m = userdata;
@ -1932,7 +2036,10 @@ static int send_unit_files_changed(sd_bus *bus, void *userdata) {
assert(bus);
r = sd_bus_message_new_signal(bus, &message, "/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "UnitFilesChanged");
r = sd_bus_message_new_signal(bus, &message,
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
"UnitFilesChanged");
if (r < 0)
return r;
@ -2060,8 +2167,8 @@ static int reply_unit_file_changes_and_free(
good = true;
}
/* If there was a failed change, and no successful change, then return the first failure as proper method call
* error. */
/* If there was a failed change, and no successful change, then return the first failure as proper
* method call error. */
if (bad && !good)
return install_error(error, 0, changes, n_changes);
@ -2486,7 +2593,8 @@ static int method_abandon_scope(sd_bus_message *message, void *userdata, sd_bus_
return r;
if (u->type != UNIT_SCOPE)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unit '%s' is not a scope unit, refusing.", name);
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
"Unit '%s' is not a scope unit, refusing.", name);
return bus_scope_method_abandon(message, u, error);
}
@ -2507,7 +2615,8 @@ static int method_set_show_status(sd_bus_message *message, void *userdata, sd_bu
if (!isempty(t)) {
mode = show_status_from_string(t);
if (mode < 0)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid show status '%s'", t);
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
"Invalid show status '%s'", t);
}
manager_override_show_status(m, mode, "bus");
@ -3007,6 +3116,12 @@ const sd_bus_vtable bus_manager_vtable[] = {
NULL,,
method_unset_and_set_environment,
SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD_WITH_NAMES("EnqueueMarkedJobs",
NULL,,
"ao",
SD_BUS_PARAM(jobs),
method_enqueue_marked_jobs,
SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD_WITH_NAMES("ListUnitFiles",
NULL,,
"a(ss)",
@ -3260,7 +3375,11 @@ static int send_finished(sd_bus *bus, void *userdata) {
assert(bus);
assert(times);
r = sd_bus_message_new_signal(bus, &message, "/org/freedesktop/systemd1", "org.freedesktop.systemd1.Manager", "StartupFinished");
r = sd_bus_message_new_signal(bus,
&message,
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
"StartupFinished");
if (r < 0)
return r;

View File

@ -323,6 +323,39 @@ static int property_get_load_error(
return sd_bus_message_append(reply, "(ss)", NULL, NULL);
}
static int property_get_markers(
sd_bus *bus,
const char *path,
const char *interface,
const char *property,
sd_bus_message *reply,
void *userdata,
sd_bus_error *error) {
unsigned *markers = userdata;
int r;
assert(bus);
assert(reply);
assert(markers);
r = sd_bus_message_open_container(reply, 'a', "s");
if (r < 0)
return r;
/* Make sure out values fit in the bitfield. */
assert_cc(_UNIT_MARKER_MAX <= sizeof(((Unit){}).markers) * 8);
for (UnitMarker m = 0; m < _UNIT_MARKER_MAX; m++)
if (FLAGS_SET(*markers, 1u << m)) {
r = sd_bus_message_append(reply, "s", unit_marker_to_string(m));
if (r < 0)
return r;
}
return sd_bus_message_close_container(reply);
}
static const char *const polkit_message_for_job[_JOB_TYPE_MAX] = {
[JOB_START] = N_("Authentication is required to start '$(unit)'."),
[JOB_STOP] = N_("Authentication is required to stop '$(unit)'."),
@ -778,7 +811,6 @@ static int property_get_refs(
sd_bus_error *error) {
Unit *u = userdata;
const char *i;
int r;
assert(bus);
@ -788,15 +820,15 @@ static int property_get_refs(
if (r < 0)
return r;
for (i = sd_bus_track_first(u->bus_track); i; i = sd_bus_track_next(u->bus_track)) {
int c, k;
for (const char *i = sd_bus_track_first(u->bus_track); i; i = sd_bus_track_next(u->bus_track)) {
int c;
c = sd_bus_track_count_name(u->bus_track, i);
if (c < 0)
return c;
/* Add the item multiple times if the ref count for each is above 1 */
for (k = 0; k < c; k++) {
for (int k = 0; k < c; k++) {
r = sd_bus_message_append(reply, "s", i);
if (r < 0)
return r;
@ -864,6 +896,7 @@ const sd_bus_vtable bus_unit_vtable[] = {
SD_BUS_PROPERTY("OnFailureJobMode", "s", property_get_job_mode, offsetof(Unit, on_failure_job_mode), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("IgnoreOnIsolate", "b", bus_property_get_bool, offsetof(Unit, ignore_on_isolate), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("NeedDaemonReload", "b", property_get_need_daemon_reload, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("Markers", "as", property_get_markers, offsetof(Unit, markers), 0),
SD_BUS_PROPERTY("JobTimeoutUSec", "t", bus_property_get_usec, offsetof(Unit, job_timeout), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("JobRunningTimeoutUSec", "t", bus_property_get_usec, offsetof(Unit, job_running_timeout), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("JobTimeoutAction", "s", property_get_emergency_action, offsetof(Unit, job_timeout_action), SD_BUS_VTABLE_PROPERTY_CONST),
@ -1683,6 +1716,89 @@ void bus_unit_send_removed_signal(Unit *u) {
log_unit_debug_errno(u, r, "Failed to send unit remove signal for %s: %m", u->id);
}
int bus_unit_queue_job_one(
sd_bus_message *message,
Unit *u,
JobType type,
JobMode mode,
BusUnitQueueFlags flags,
sd_bus_message *reply,
sd_bus_error *error) {
_cleanup_set_free_ Set *affected = NULL;
_cleanup_free_ char *job_path = NULL, *unit_path = NULL;
Job *j, *a;
int r;
if (FLAGS_SET(flags, BUS_UNIT_QUEUE_VERBOSE_REPLY)) {
affected = set_new(NULL);
if (!affected)
return -ENOMEM;
}
r = manager_add_job(u->manager, type, u, mode, affected, error, &j);
if (r < 0)
return r;
r = bus_job_track_sender(j, message);
if (r < 0)
return r;
/* Before we send the method reply, force out the announcement JobNew for this job */
bus_job_send_pending_change_signal(j, true);
job_path = job_dbus_path(j);
if (!job_path)
return -ENOMEM;
/* The classic response is just a job object path */
if (!FLAGS_SET(flags, BUS_UNIT_QUEUE_VERBOSE_REPLY))
return sd_bus_message_append(reply, "o", job_path);
/* In verbose mode respond with the anchor job plus everything that has been affected */
unit_path = unit_dbus_path(j->unit);
if (!unit_path)
return -ENOMEM;
r = sd_bus_message_append(reply, "uosos",
j->id, job_path,
j->unit->id, unit_path,
job_type_to_string(j->type));
if (r < 0)
return r;
r = sd_bus_message_open_container(reply, 'a', "(uosos)");
if (r < 0)
return r;
SET_FOREACH(a, affected) {
if (a->id == j->id)
continue;
/* Free paths from previous iteration */
job_path = mfree(job_path);
unit_path = mfree(unit_path);
job_path = job_dbus_path(a);
if (!job_path)
return -ENOMEM;
unit_path = unit_dbus_path(a->unit);
if (!unit_path)
return -ENOMEM;
r = sd_bus_message_append(reply, "(uosos)",
a->id, job_path,
a->unit->id, unit_path,
job_type_to_string(a->type));
if (r < 0)
return r;
}
return sd_bus_message_close_container(reply);
}
int bus_unit_queue_job(
sd_bus_message *message,
Unit *u,
@ -1692,9 +1808,6 @@ int bus_unit_queue_job(
sd_bus_error *error) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
_cleanup_free_ char *job_path = NULL, *unit_path = NULL;
_cleanup_set_free_ Set *affected = NULL;
Job *j, *a;
int r;
assert(message);
@ -1727,77 +1840,11 @@ int bus_unit_queue_job(
(type == JOB_RELOAD_OR_START && job_type_collapse(type, u) == JOB_START && u->refuse_manual_start))
return sd_bus_error_setf(error, BUS_ERROR_ONLY_BY_DEPENDENCY, "Operation refused, unit %s may be requested by dependency only (it is configured to refuse manual start/stop).", u->id);
if (FLAGS_SET(flags, BUS_UNIT_QUEUE_VERBOSE_REPLY)) {
affected = set_new(NULL);
if (!affected)
return -ENOMEM;
}
r = manager_add_job(u->manager, type, u, mode, affected, error, &j);
if (r < 0)
return r;
r = bus_job_track_sender(j, message);
if (r < 0)
return r;
/* Before we send the method reply, force out the announcement JobNew for this job */
bus_job_send_pending_change_signal(j, true);
job_path = job_dbus_path(j);
if (!job_path)
return -ENOMEM;
/* The classic response is just a job object path */
if (!FLAGS_SET(flags, BUS_UNIT_QUEUE_VERBOSE_REPLY))
return sd_bus_reply_method_return(message, "o", job_path);
/* In verbose mode respond with the anchor job plus everything that has been affected */
r = sd_bus_message_new_method_return(message, &reply);
if (r < 0)
return r;
unit_path = unit_dbus_path(j->unit);
if (!unit_path)
return -ENOMEM;
r = sd_bus_message_append(reply, "uosos",
j->id, job_path,
j->unit->id, unit_path,
job_type_to_string(j->type));
if (r < 0)
return r;
r = sd_bus_message_open_container(reply, 'a', "(uosos)");
if (r < 0)
return r;
SET_FOREACH(a, affected) {
if (a->id == j->id)
continue;
/* Free paths from previous iteration */
job_path = mfree(job_path);
unit_path = mfree(unit_path);
job_path = job_dbus_path(a);
if (!job_path)
return -ENOMEM;
unit_path = unit_dbus_path(a->unit);
if (!unit_path)
return -ENOMEM;
r = sd_bus_message_append(reply, "(uosos)",
a->id, job_path,
a->unit->id, unit_path,
job_type_to_string(a->type));
if (r < 0)
return r;
}
r = sd_bus_message_close_container(reply);
r = bus_unit_queue_job_one(message, u, type, mode, flags, reply, error);
if (r < 0)
return r;
@ -1817,8 +1864,8 @@ static int bus_unit_set_live_property(
assert(name);
assert(message);
/* Handles setting properties both "live" (i.e. at any time during runtime), and during creation (for transient
* units that are being created). */
/* Handles setting properties both "live" (i.e. at any time during runtime), and during creation (for
* transient units that are being created). */
if (streq(name, "Description")) {
const char *d;
@ -1838,6 +1885,63 @@ static int bus_unit_set_live_property(
return 1;
}
/* A setting that only applies to active units. We don't actually write this to /run, this state is
* managed internally. "+foo" sets flag foo, "-foo" unsets flag foo, just "foo" resets flags to
* foo. The last type cannot be mixed with "+" or "-". */
if (streq(name, "Markers")) {
unsigned settings = 0, mask = 0;
bool some_plus_minus = false, some_absolute = false;
r = sd_bus_message_enter_container(message, 'a', "s");
if (r < 0)
return r;
for (;;) {
const char *word;
bool b;
r = sd_bus_message_read(message, "s", &word);
if (r < 0)
return r;
if (r == 0)
break;
if (IN_SET(word[0], '+', '-')) {
b = word[0] == '+';
word++;
some_plus_minus = true;
} else {
b = true;
some_absolute = true;
}
UnitMarker m = unit_marker_from_string(word);
if (m < 0)
return sd_bus_error_setf(error, BUS_ERROR_BAD_UNIT_SETTING,
"Unknown marker \"%s\".", word);
SET_FLAG(settings, 1u << m, b);
SET_FLAG(mask, 1u << m, true);
}
r = sd_bus_message_exit_container(message);
if (r < 0)
return r;
if (some_plus_minus && some_absolute)
return sd_bus_error_setf(error, BUS_ERROR_BAD_UNIT_SETTING, "Bad marker syntax.");
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
if (some_absolute)
u->markers = settings;
else
u->markers = settings | (u->markers & ~mask);
}
return 1;
}
return 0;
}
@ -1989,8 +2093,8 @@ static int bus_unit_set_transient_property(
assert(name);
assert(message);
/* Handles settings when transient units are created. This settings cannot be altered anymore after the unit
* has been created. */
/* Handles settings when transient units are created. This settings cannot be altered anymore after
* the unit has been created. */
if (streq(name, "SourcePath"))
return bus_set_transient_path(u, name, &u->source_path, message, flags, error);
@ -2298,7 +2402,8 @@ int bus_unit_set_properties(
return r;
if (!UNIT_VTABLE(u)->bus_set_property)
return sd_bus_error_setf(error, SD_BUS_ERROR_PROPERTY_READ_ONLY, "Objects of this type do not support setting properties.");
return sd_bus_error_setf(error, SD_BUS_ERROR_PROPERTY_READ_ONLY,
"Objects of this type do not support setting properties.");
r = sd_bus_message_enter_container(message, 'v', NULL);
if (r < 0)
@ -2316,7 +2421,8 @@ int bus_unit_set_properties(
return r;
if (r == 0)
return sd_bus_error_setf(error, SD_BUS_ERROR_PROPERTY_READ_ONLY, "Cannot set property %s, or unknown property.", name);
return sd_bus_error_setf(error, SD_BUS_ERROR_PROPERTY_READ_ONLY,
"Cannot set property %s, or unknown property.", name);
r = sd_bus_message_exit_container(message);
if (r < 0)
@ -2435,8 +2541,8 @@ int bus_unit_track_add_sender(Unit *u, sd_bus_message *m) {
int bus_unit_track_remove_sender(Unit *u, sd_bus_message *m) {
assert(u);
/* If we haven't allocated the bus track object yet, then there's definitely no reference taken yet, return an
* error */
/* If we haven't allocated the bus track object yet, then there's definitely no reference taken yet,
* return an error */
if (!u->bus_track)
return -EUNATCH;

View File

@ -33,7 +33,21 @@ typedef enum BusUnitQueueFlags {
BUS_UNIT_QUEUE_VERBOSE_REPLY = 1 << 1,
} BusUnitQueueFlags;
int bus_unit_queue_job(sd_bus_message *message, Unit *u, JobType type, JobMode mode, BusUnitQueueFlags flags, sd_bus_error *error);
int bus_unit_queue_job_one(
sd_bus_message *message,
Unit *u,
JobType type,
JobMode mode,
BusUnitQueueFlags flags,
sd_bus_message *reply,
sd_bus_error *error);
int bus_unit_queue_job(
sd_bus_message *message,
Unit *u,
JobType type,
JobMode mode,
BusUnitQueueFlags flags,
sd_bus_error *error);
int bus_unit_validate_load_state(Unit *u, sd_bus_error *error);
int bus_unit_track_add_name(Unit *u, const char *name);

View File

@ -93,7 +93,7 @@
#include "terminal-util.h"
#include "tmpfile-util.h"
#include "umask-util.h"
#include "unit.h"
#include "unit-serialize.h"
#include "user-util.h"
#include "utmp-wtmp.h"

View File

@ -7,7 +7,7 @@
#include "install.h"
#include "load-fragment.h"
#include "string-util.h"
#include "unit.h"
#include "unit-serialize.h"
#include "utf8.h"
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {

View File

@ -80,6 +80,7 @@
#include "transaction.h"
#include "umask-util.h"
#include "unit-name.h"
#include "unit-serialize.h"
#include "user-util.h"
#include "virt.h"
#include "watchdog.h"
@ -1187,18 +1188,15 @@ static void unit_gc_sweep(Unit *u, unsigned gc_marker) {
is_bad = false;
}
if (u->refs_by_target) {
const UnitRef *ref;
const UnitRef *ref;
LIST_FOREACH(refs_by_target, ref, u->refs_by_target) {
unit_gc_sweep(ref->source, gc_marker);
LIST_FOREACH(refs_by_target, ref, u->refs_by_target) {
unit_gc_sweep(ref->source, gc_marker);
if (ref->source->gc_marker == gc_marker + GC_OFFSET_GOOD)
goto good;
if (ref->source->gc_marker == gc_marker + GC_OFFSET_GOOD)
goto good;
if (ref->source->gc_marker != gc_marker + GC_OFFSET_BAD)
is_bad = false;
}
if (ref->source->gc_marker != gc_marker + GC_OFFSET_BAD)
is_bad = false;
}
if (is_bad)

View File

@ -115,6 +115,8 @@ libcore_sources = '''
transaction.h
unit-printf.c
unit-printf.h
unit-serialize.c
unit-serialize.h
unit.c
unit.h
'''.split()
@ -162,11 +164,9 @@ core_includes = [includes, include_directories('.')]
systemd_sources = files('main.c')
in_files = [['macros.systemd', rpmmacrosdir],
['system.conf', pkgsysconfdir],
in_files = [['system.conf', pkgsysconfdir],
['user.conf', pkgsysconfdir],
['systemd.pc', pkgconfigdatadir],
['triggers.systemd', '']]
['systemd.pc', pkgconfigdatadir]]
foreach item : in_files
file = item[0]

780
src/core/unit-serialize.c Normal file
View File

@ -0,0 +1,780 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "bus-util.h"
#include "dbus.h"
#include "fileio-label.h"
#include "fileio.h"
#include "format-util.h"
#include "parse-util.h"
#include "serialize.h"
#include "string-table.h"
#include "unit-serialize.h"
#include "user-util.h"
static int serialize_cgroup_mask(FILE *f, const char *key, CGroupMask mask) {
_cleanup_free_ char *s = NULL;
int r;
assert(f);
assert(key);
if (mask == 0)
return 0;
r = cg_mask_to_string(mask, &s);
if (r < 0)
return log_error_errno(r, "Failed to format cgroup mask: %m");
return serialize_item(f, key, s);
}
/* Make sure out values fit in the bitfield. */
assert_cc(_UNIT_MARKER_MAX <= sizeof(((Unit){}).markers) * 8);
static int serialize_markers(FILE *f, unsigned markers) {
assert(f);
if (markers == 0)
return 0;
fputs("markers=", f);
for (UnitMarker m = 0; m < _UNIT_MARKER_MAX; m++)
if (FLAGS_SET(markers, 1u << m))
fputs(unit_marker_to_string(m), f);
fputc('\n', f);
return 0;
}
static int deserialize_markers(Unit *u, const char *value) {
assert(u);
assert(value);
int r;
for (const char *p = value;;) {
_cleanup_free_ char *word = NULL;
r = extract_first_word(&p, &word, NULL, 0);
if (r <= 0)
return r;
UnitMarker m = unit_marker_from_string(word);
if (m < 0) {
log_unit_debug_errno(u, m, "Unknown unit marker \"%s\", ignoring.", word);
continue;
}
u->markers |= 1u << m;
}
}
static const char *const ip_accounting_metric_field[_CGROUP_IP_ACCOUNTING_METRIC_MAX] = {
[CGROUP_IP_INGRESS_BYTES] = "ip-accounting-ingress-bytes",
[CGROUP_IP_INGRESS_PACKETS] = "ip-accounting-ingress-packets",
[CGROUP_IP_EGRESS_BYTES] = "ip-accounting-egress-bytes",
[CGROUP_IP_EGRESS_PACKETS] = "ip-accounting-egress-packets",
};
static const char *const io_accounting_metric_field_base[_CGROUP_IO_ACCOUNTING_METRIC_MAX] = {
[CGROUP_IO_READ_BYTES] = "io-accounting-read-bytes-base",
[CGROUP_IO_WRITE_BYTES] = "io-accounting-write-bytes-base",
[CGROUP_IO_READ_OPERATIONS] = "io-accounting-read-operations-base",
[CGROUP_IO_WRITE_OPERATIONS] = "io-accounting-write-operations-base",
};
static const char *const io_accounting_metric_field_last[_CGROUP_IO_ACCOUNTING_METRIC_MAX] = {
[CGROUP_IO_READ_BYTES] = "io-accounting-read-bytes-last",
[CGROUP_IO_WRITE_BYTES] = "io-accounting-write-bytes-last",
[CGROUP_IO_READ_OPERATIONS] = "io-accounting-read-operations-last",
[CGROUP_IO_WRITE_OPERATIONS] = "io-accounting-write-operations-last",
};
int unit_serialize(Unit *u, FILE *f, FDSet *fds, bool serialize_jobs) {
int r;
assert(u);
assert(f);
assert(fds);
if (unit_can_serialize(u)) {
r = UNIT_VTABLE(u)->serialize(u, f, fds);
if (r < 0)
return r;
}
(void) serialize_dual_timestamp(f, "state-change-timestamp", &u->state_change_timestamp);
(void) serialize_dual_timestamp(f, "inactive-exit-timestamp", &u->inactive_exit_timestamp);
(void) serialize_dual_timestamp(f, "active-enter-timestamp", &u->active_enter_timestamp);
(void) serialize_dual_timestamp(f, "active-exit-timestamp", &u->active_exit_timestamp);
(void) serialize_dual_timestamp(f, "inactive-enter-timestamp", &u->inactive_enter_timestamp);
(void) serialize_dual_timestamp(f, "condition-timestamp", &u->condition_timestamp);
(void) serialize_dual_timestamp(f, "assert-timestamp", &u->assert_timestamp);
if (dual_timestamp_is_set(&u->condition_timestamp))
(void) serialize_bool(f, "condition-result", u->condition_result);
if (dual_timestamp_is_set(&u->assert_timestamp))
(void) serialize_bool(f, "assert-result", u->assert_result);
(void) serialize_bool(f, "transient", u->transient);
(void) serialize_bool(f, "in-audit", u->in_audit);
(void) serialize_bool(f, "exported-invocation-id", u->exported_invocation_id);
(void) serialize_bool(f, "exported-log-level-max", u->exported_log_level_max);
(void) serialize_bool(f, "exported-log-extra-fields", u->exported_log_extra_fields);
(void) serialize_bool(f, "exported-log-rate-limit-interval", u->exported_log_ratelimit_interval);
(void) serialize_bool(f, "exported-log-rate-limit-burst", u->exported_log_ratelimit_burst);
(void) serialize_item_format(f, "cpu-usage-base", "%" PRIu64, u->cpu_usage_base);
if (u->cpu_usage_last != NSEC_INFINITY)
(void) serialize_item_format(f, "cpu-usage-last", "%" PRIu64, u->cpu_usage_last);
if (u->managed_oom_kill_last > 0)
(void) serialize_item_format(f, "managed-oom-kill-last", "%" PRIu64, u->managed_oom_kill_last);
if (u->oom_kill_last > 0)
(void) serialize_item_format(f, "oom-kill-last", "%" PRIu64, u->oom_kill_last);
for (CGroupIOAccountingMetric im = 0; im < _CGROUP_IO_ACCOUNTING_METRIC_MAX; im++) {
(void) serialize_item_format(f, io_accounting_metric_field_base[im], "%" PRIu64, u->io_accounting_base[im]);
if (u->io_accounting_last[im] != UINT64_MAX)
(void) serialize_item_format(f, io_accounting_metric_field_last[im], "%" PRIu64, u->io_accounting_last[im]);
}
if (u->cgroup_path)
(void) serialize_item(f, "cgroup", u->cgroup_path);
(void) serialize_bool(f, "cgroup-realized", u->cgroup_realized);
(void) serialize_cgroup_mask(f, "cgroup-realized-mask", u->cgroup_realized_mask);
(void) serialize_cgroup_mask(f, "cgroup-enabled-mask", u->cgroup_enabled_mask);
(void) serialize_cgroup_mask(f, "cgroup-invalidated-mask", u->cgroup_invalidated_mask);
if (uid_is_valid(u->ref_uid))
(void) serialize_item_format(f, "ref-uid", UID_FMT, u->ref_uid);
if (gid_is_valid(u->ref_gid))
(void) serialize_item_format(f, "ref-gid", GID_FMT, u->ref_gid);
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, "freezer-state", "%s", freezer_state_to_string(unit_freezer_state(u)));
(void) serialize_markers(f, u->markers);
bus_track_serialize(u->bus_track, f, "ref");
for (CGroupIPAccountingMetric m = 0; m < _CGROUP_IP_ACCOUNTING_METRIC_MAX; m++) {
uint64_t v;
r = unit_get_ip_accounting(u, m, &v);
if (r >= 0)
(void) serialize_item_format(f, ip_accounting_metric_field[m], "%" PRIu64, v);
}
if (serialize_jobs) {
if (u->job) {
fputs("job\n", f);
job_serialize(u->job, f);
}
if (u->nop_job) {
fputs("job\n", f);
job_serialize(u->nop_job, f);
}
}
/* End marker */
fputc('\n', f);
return 0;
}
static int unit_deserialize_job(Unit *u, FILE *f) {
_cleanup_(job_freep) Job *j = NULL;
int r;
assert(u);
assert(f);
j = job_new_raw(u);
if (!j)
return log_oom();
r = job_deserialize(j, f);
if (r < 0)
return r;
r = job_install_deserialized(j);
if (r < 0)
return r;
TAKE_PTR(j);
return 0;
}
#define MATCH_DESERIALIZE(key, l, v, parse_func, target) \
({ \
bool _deserialize_matched = streq(l, key); \
if (_deserialize_matched) { \
int _deserialize_r = parse_func(v); \
if (_deserialize_r < 0) \
log_unit_debug_errno(u, _deserialize_r, \
"Failed to parse \"%s=%s\", ignoring.", l, v); \
else \
target = _deserialize_r; \
}; \
_deserialize_matched; \
})
#define MATCH_DESERIALIZE_IMMEDIATE(key, l, v, parse_func, target) \
({ \
bool _deserialize_matched = streq(l, key); \
if (_deserialize_matched) { \
int _deserialize_r = parse_func(v, &target); \
if (_deserialize_r < 0) \
log_unit_debug_errno(u, _deserialize_r, \
"Failed to parse \"%s=%s\", ignoring", l, v); \
}; \
_deserialize_matched; \
})
int unit_deserialize(Unit *u, FILE *f, FDSet *fds) {
int r;
assert(u);
assert(f);
assert(fds);
for (;;) {
_cleanup_free_ char *line = NULL;
char *l, *v;
ssize_t m;
size_t k;
r = read_line(f, LONG_LINE_MAX, &line);
if (r < 0)
return log_error_errno(r, "Failed to read serialization line: %m");
if (r == 0) /* eof */
break;
l = strstrip(line);
if (isempty(l)) /* End marker */
break;
k = strcspn(l, "=");
if (l[k] == '=') {
l[k] = 0;
v = l+k+1;
} else
v = l+k;
if (streq(l, "job")) {
if (v[0] == '\0') {
/* New-style serialized job */
r = unit_deserialize_job(u, f);
if (r < 0)
return r;
} else /* Legacy for pre-44 */
log_unit_warning(u, "Update from too old systemd versions are unsupported, cannot deserialize job: %s", v);
continue;
} else if (streq(l, "state-change-timestamp")) {
(void) deserialize_dual_timestamp(v, &u->state_change_timestamp);
continue;
} else if (streq(l, "inactive-exit-timestamp")) {
(void) deserialize_dual_timestamp(v, &u->inactive_exit_timestamp);
continue;
} else if (streq(l, "active-enter-timestamp")) {
(void) deserialize_dual_timestamp(v, &u->active_enter_timestamp);
continue;
} else if (streq(l, "active-exit-timestamp")) {
(void) deserialize_dual_timestamp(v, &u->active_exit_timestamp);
continue;
} else if (streq(l, "inactive-enter-timestamp")) {
(void) deserialize_dual_timestamp(v, &u->inactive_enter_timestamp);
continue;
} else if (streq(l, "condition-timestamp")) {
(void) deserialize_dual_timestamp(v, &u->condition_timestamp);
continue;
} else if (streq(l, "assert-timestamp")) {
(void) deserialize_dual_timestamp(v, &u->assert_timestamp);
continue;
} else if (MATCH_DESERIALIZE("condition-result", l, v, parse_boolean, u->condition_result))
continue;
else if (MATCH_DESERIALIZE("assert-result", l, v, parse_boolean, u->assert_result))
continue;
else if (MATCH_DESERIALIZE("transient", l, v, parse_boolean, u->transient))
continue;
else if (MATCH_DESERIALIZE("in-audit", l, v, parse_boolean, u->in_audit))
continue;
else if (MATCH_DESERIALIZE("exported-invocation-id", l, v, parse_boolean, u->exported_invocation_id))
continue;
else if (MATCH_DESERIALIZE("exported-log-level-max", l, v, parse_boolean, u->exported_log_level_max))
continue;
else if (MATCH_DESERIALIZE("exported-log-extra-fields", l, v, parse_boolean, u->exported_log_extra_fields))
continue;
else if (MATCH_DESERIALIZE("exported-log-rate-limit-interval", l, v, parse_boolean, u->exported_log_ratelimit_interval))
continue;
else if (MATCH_DESERIALIZE("exported-log-rate-limit-burst", l, v, parse_boolean, u->exported_log_ratelimit_burst))
continue;
else if (MATCH_DESERIALIZE_IMMEDIATE("cpu-usage-base", l, v, safe_atou64, u->cpu_usage_base) ||
MATCH_DESERIALIZE_IMMEDIATE("cpuacct-usage-base", l, v, safe_atou64, u->cpu_usage_base))
continue;
else if (MATCH_DESERIALIZE_IMMEDIATE("cpu-usage-last", l, v, safe_atou64, u->cpu_usage_last))
continue;
else if (MATCH_DESERIALIZE_IMMEDIATE("managed-oom-kill-last", l, v, safe_atou64, u->managed_oom_kill_last))
continue;
else if (MATCH_DESERIALIZE_IMMEDIATE("oom-kill-last", l, v, safe_atou64, u->oom_kill_last))
continue;
else if (streq(l, "cgroup")) {
r = unit_set_cgroup_path(u, v);
if (r < 0)
log_unit_debug_errno(u, r, "Failed to set cgroup path %s, ignoring: %m", v);
(void) unit_watch_cgroup(u);
(void) unit_watch_cgroup_memory(u);
continue;
} else if (MATCH_DESERIALIZE("cgroup-realized", l, v, parse_boolean, u->cgroup_realized))
continue;
else if (MATCH_DESERIALIZE_IMMEDIATE("cgroup-realized-mask", l, v, cg_mask_from_string, u->cgroup_realized_mask))
continue;
else if (MATCH_DESERIALIZE_IMMEDIATE("cgroup-enabled-mask", l, v, cg_mask_from_string, u->cgroup_enabled_mask))
continue;
else if (MATCH_DESERIALIZE_IMMEDIATE("cgroup-invalidated-mask", l, v, cg_mask_from_string, u->cgroup_invalidated_mask))
continue;
else if (streq(l, "ref-uid")) {
uid_t uid;
r = parse_uid(v, &uid);
if (r < 0)
log_unit_debug(u, "Failed to parse \"%s=%s\", ignoring.", l, v);
else
unit_ref_uid_gid(u, uid, GID_INVALID);
continue;
} else if (streq(l, "ref-gid")) {
gid_t gid;
r = parse_gid(v, &gid);
if (r < 0)
log_unit_debug(u, "Failed to parse \"%s=%s\", ignoring.", l, v);
else
unit_ref_uid_gid(u, UID_INVALID, gid);
continue;
} else if (streq(l, "ref")) {
r = strv_extend(&u->deserialized_refs, v);
if (r < 0)
return log_oom();
continue;
} else if (streq(l, "invocation-id")) {
sd_id128_t id;
r = sd_id128_from_string(v, &id);
if (r < 0)
log_unit_debug(u, "Failed to parse \"%s=%s\", ignoring.", l, v);
else {
r = unit_set_invocation_id(u, id);
if (r < 0)
log_unit_warning_errno(u, r, "Failed to set invocation ID for unit: %m");
}
continue;
} else if (MATCH_DESERIALIZE("freezer-state", l, v, freezer_state_from_string, u->freezer_state))
continue;
else if (streq(l, "markers")) {
r = deserialize_markers(u, v);
if (r < 0)
log_unit_debug_errno(u, r, "Failed to deserialize \"%s=%s\", ignoring: %m", l, v);
continue;
}
/* Check if this is an IP accounting metric serialization field */
m = string_table_lookup(ip_accounting_metric_field, ELEMENTSOF(ip_accounting_metric_field), l);
if (m >= 0) {
uint64_t c;
r = safe_atou64(v, &c);
if (r < 0)
log_unit_debug(u, "Failed to parse IP accounting value %s, ignoring.", v);
else
u->ip_accounting_extra[m] = c;
continue;
}
m = string_table_lookup(io_accounting_metric_field_base, ELEMENTSOF(io_accounting_metric_field_base), l);
if (m >= 0) {
uint64_t c;
r = safe_atou64(v, &c);
if (r < 0)
log_unit_debug(u, "Failed to parse IO accounting base value %s, ignoring.", v);
else
u->io_accounting_base[m] = c;
continue;
}
m = string_table_lookup(io_accounting_metric_field_last, ELEMENTSOF(io_accounting_metric_field_last), l);
if (m >= 0) {
uint64_t c;
r = safe_atou64(v, &c);
if (r < 0)
log_unit_debug(u, "Failed to parse IO accounting last value %s, ignoring.", v);
else
u->io_accounting_last[m] = c;
continue;
}
if (unit_can_serialize(u)) {
r = exec_runtime_deserialize_compat(u, l, v, fds);
if (r < 0) {
log_unit_warning(u, "Failed to deserialize runtime parameter '%s', ignoring.", l);
continue;
}
/* Returns positive if key was handled by the call */
if (r > 0)
continue;
r = UNIT_VTABLE(u)->deserialize_item(u, l, v, fds);
if (r < 0)
log_unit_warning(u, "Failed to deserialize unit parameter '%s', ignoring.", l);
}
}
/* Versions before 228 did not carry a state change timestamp. In this case, take the current
* time. This is useful, so that timeouts based on this timestamp don't trigger too early, and is
* in-line with the logic from before 228 where the base for timeouts was not persistent across
* reboots. */
if (!dual_timestamp_is_set(&u->state_change_timestamp))
dual_timestamp_get(&u->state_change_timestamp);
/* Let's make sure that everything that is deserialized also gets any potential new cgroup settings
* applied after we are done. For that we invalidate anything already realized, so that we can
* realize it again. */
unit_invalidate_cgroup(u, _CGROUP_MASK_ALL);
unit_invalidate_cgroup_bpf(u);
return 0;
}
int unit_deserialize_skip(FILE *f) {
int r;
assert(f);
/* Skip serialized data for this unit. We don't know what it is. */
for (;;) {
_cleanup_free_ char *line = NULL;
char *l;
r = read_line(f, LONG_LINE_MAX, &line);
if (r < 0)
return log_error_errno(r, "Failed to read serialization line: %m");
if (r == 0)
return 0;
l = strstrip(line);
/* End marker */
if (isempty(l))
return 1;
}
}
static void print_unit_dependency_mask(FILE *f, const char *kind, UnitDependencyMask mask, bool *space) {
const struct {
UnitDependencyMask mask;
const char *name;
} table[] = {
{ UNIT_DEPENDENCY_FILE, "file" },
{ UNIT_DEPENDENCY_IMPLICIT, "implicit" },
{ UNIT_DEPENDENCY_DEFAULT, "default" },
{ UNIT_DEPENDENCY_UDEV, "udev" },
{ UNIT_DEPENDENCY_PATH, "path" },
{ UNIT_DEPENDENCY_MOUNTINFO_IMPLICIT, "mountinfo-implicit" },
{ UNIT_DEPENDENCY_MOUNTINFO_DEFAULT, "mountinfo-default" },
{ UNIT_DEPENDENCY_PROC_SWAP, "proc-swap" },
};
assert(f);
assert(kind);
assert(space);
for (size_t i = 0; i < ELEMENTSOF(table); i++) {
if (mask == 0)
break;
if (FLAGS_SET(mask, table[i].mask)) {
if (*space)
fputc(' ', f);
else
*space = true;
fputs(kind, f);
fputs("-", f);
fputs(table[i].name, f);
mask &= ~table[i].mask;
}
}
assert(mask == 0);
}
void unit_dump(Unit *u, FILE *f, const char *prefix) {
char *t, **j;
const char *prefix2;
char timestamp[5][FORMAT_TIMESTAMP_MAX], timespan[FORMAT_TIMESPAN_MAX];
Unit *following;
_cleanup_set_free_ Set *following_set = NULL;
CGroupMask m;
int r;
assert(u);
assert(u->type >= 0);
prefix = strempty(prefix);
prefix2 = strjoina(prefix, "\t");
fprintf(f,
"%s-> Unit %s:\n",
prefix, u->id);
SET_FOREACH(t, u->aliases)
fprintf(f, "%s\tAlias: %s\n", prefix, t);
fprintf(f,
"%s\tDescription: %s\n"
"%s\tInstance: %s\n"
"%s\tUnit Load State: %s\n"
"%s\tUnit Active State: %s\n"
"%s\tState Change Timestamp: %s\n"
"%s\tInactive Exit Timestamp: %s\n"
"%s\tActive Enter Timestamp: %s\n"
"%s\tActive Exit Timestamp: %s\n"
"%s\tInactive Enter Timestamp: %s\n"
"%s\tMay GC: %s\n"
"%s\tNeed Daemon Reload: %s\n"
"%s\tTransient: %s\n"
"%s\tPerpetual: %s\n"
"%s\tGarbage Collection Mode: %s\n"
"%s\tSlice: %s\n"
"%s\tCGroup: %s\n"
"%s\tCGroup realized: %s\n",
prefix, unit_description(u),
prefix, strna(u->instance),
prefix, unit_load_state_to_string(u->load_state),
prefix, unit_active_state_to_string(unit_active_state(u)),
prefix, strna(format_timestamp(timestamp[0], sizeof(timestamp[0]), u->state_change_timestamp.realtime)),
prefix, strna(format_timestamp(timestamp[1], sizeof(timestamp[1]), u->inactive_exit_timestamp.realtime)),
prefix, strna(format_timestamp(timestamp[2], sizeof(timestamp[2]), u->active_enter_timestamp.realtime)),
prefix, strna(format_timestamp(timestamp[3], sizeof(timestamp[3]), u->active_exit_timestamp.realtime)),
prefix, strna(format_timestamp(timestamp[4], sizeof(timestamp[4]), u->inactive_enter_timestamp.realtime)),
prefix, yes_no(unit_may_gc(u)),
prefix, yes_no(unit_need_daemon_reload(u)),
prefix, yes_no(u->transient),
prefix, yes_no(u->perpetual),
prefix, collect_mode_to_string(u->collect_mode),
prefix, strna(unit_slice_name(u)),
prefix, strna(u->cgroup_path),
prefix, yes_no(u->cgroup_realized));
if (u->markers != 0) {
fprintf(f, "%s\tMarkers:", prefix);
for (UnitMarker marker = 0; marker < _UNIT_MARKER_MAX; marker++)
if (FLAGS_SET(u->markers, 1u << marker))
fprintf(f, " %s", unit_marker_to_string(marker));
fputs("\n", f);
}
if (u->cgroup_realized_mask != 0) {
_cleanup_free_ char *s = NULL;
(void) cg_mask_to_string(u->cgroup_realized_mask, &s);
fprintf(f, "%s\tCGroup realized mask: %s\n", prefix, strnull(s));
}
if (u->cgroup_enabled_mask != 0) {
_cleanup_free_ char *s = NULL;
(void) cg_mask_to_string(u->cgroup_enabled_mask, &s);
fprintf(f, "%s\tCGroup enabled mask: %s\n", prefix, strnull(s));
}
m = unit_get_own_mask(u);
if (m != 0) {
_cleanup_free_ char *s = NULL;
(void) cg_mask_to_string(m, &s);
fprintf(f, "%s\tCGroup own mask: %s\n", prefix, strnull(s));
}
m = unit_get_members_mask(u);
if (m != 0) {
_cleanup_free_ char *s = NULL;
(void) cg_mask_to_string(m, &s);
fprintf(f, "%s\tCGroup members mask: %s\n", prefix, strnull(s));
}
m = unit_get_delegate_mask(u);
if (m != 0) {
_cleanup_free_ char *s = NULL;
(void) cg_mask_to_string(m, &s);
fprintf(f, "%s\tCGroup delegate mask: %s\n", prefix, strnull(s));
}
if (!sd_id128_is_null(u->invocation_id))
fprintf(f, "%s\tInvocation ID: " SD_ID128_FORMAT_STR "\n",
prefix, SD_ID128_FORMAT_VAL(u->invocation_id));
STRV_FOREACH(j, u->documentation)
fprintf(f, "%s\tDocumentation: %s\n", prefix, *j);
following = unit_following(u);
if (following)
fprintf(f, "%s\tFollowing: %s\n", prefix, following->id);
r = unit_following_set(u, &following_set);
if (r >= 0) {
Unit *other;
SET_FOREACH(other, following_set)
fprintf(f, "%s\tFollowing Set Member: %s\n", prefix, other->id);
}
if (u->fragment_path)
fprintf(f, "%s\tFragment Path: %s\n", prefix, u->fragment_path);
if (u->source_path)
fprintf(f, "%s\tSource Path: %s\n", prefix, u->source_path);
STRV_FOREACH(j, u->dropin_paths)
fprintf(f, "%s\tDropIn Path: %s\n", prefix, *j);
if (u->failure_action != EMERGENCY_ACTION_NONE)
fprintf(f, "%s\tFailure Action: %s\n", prefix, emergency_action_to_string(u->failure_action));
if (u->failure_action_exit_status >= 0)
fprintf(f, "%s\tFailure Action Exit Status: %i\n", prefix, u->failure_action_exit_status);
if (u->success_action != EMERGENCY_ACTION_NONE)
fprintf(f, "%s\tSuccess Action: %s\n", prefix, emergency_action_to_string(u->success_action));
if (u->success_action_exit_status >= 0)
fprintf(f, "%s\tSuccess Action Exit Status: %i\n", prefix, u->success_action_exit_status);
if (u->job_timeout != USEC_INFINITY)
fprintf(f, "%s\tJob Timeout: %s\n", prefix, format_timespan(timespan, sizeof(timespan), u->job_timeout, 0));
if (u->job_timeout_action != EMERGENCY_ACTION_NONE)
fprintf(f, "%s\tJob Timeout Action: %s\n", prefix, emergency_action_to_string(u->job_timeout_action));
if (u->job_timeout_reboot_arg)
fprintf(f, "%s\tJob Timeout Reboot Argument: %s\n", prefix, u->job_timeout_reboot_arg);
condition_dump_list(u->conditions, f, prefix, condition_type_to_string);
condition_dump_list(u->asserts, f, prefix, assert_type_to_string);
if (dual_timestamp_is_set(&u->condition_timestamp))
fprintf(f,
"%s\tCondition Timestamp: %s\n"
"%s\tCondition Result: %s\n",
prefix, strna(format_timestamp(timestamp[0], sizeof(timestamp[0]), u->condition_timestamp.realtime)),
prefix, yes_no(u->condition_result));
if (dual_timestamp_is_set(&u->assert_timestamp))
fprintf(f,
"%s\tAssert Timestamp: %s\n"
"%s\tAssert Result: %s\n",
prefix, strna(format_timestamp(timestamp[0], sizeof(timestamp[0]), u->assert_timestamp.realtime)),
prefix, yes_no(u->assert_result));
for (UnitDependency d = 0; d < _UNIT_DEPENDENCY_MAX; d++) {
UnitDependencyInfo di;
Unit *other;
HASHMAP_FOREACH_KEY(di.data, other, u->dependencies[d]) {
bool space = false;
fprintf(f, "%s\t%s: %s (", prefix, unit_dependency_to_string(d), other->id);
print_unit_dependency_mask(f, "origin", di.origin_mask, &space);
print_unit_dependency_mask(f, "destination", di.destination_mask, &space);
fputs(")\n", f);
}
}
if (!hashmap_isempty(u->requires_mounts_for)) {
UnitDependencyInfo di;
const char *path;
HASHMAP_FOREACH_KEY(di.data, path, u->requires_mounts_for) {
bool space = false;
fprintf(f, "%s\tRequiresMountsFor: %s (", prefix, path);
print_unit_dependency_mask(f, "origin", di.origin_mask, &space);
print_unit_dependency_mask(f, "destination", di.destination_mask, &space);
fputs(")\n", f);
}
}
if (u->load_state == UNIT_LOADED) {
fprintf(f,
"%s\tStopWhenUnneeded: %s\n"
"%s\tRefuseManualStart: %s\n"
"%s\tRefuseManualStop: %s\n"
"%s\tDefaultDependencies: %s\n"
"%s\tOnFailureJobMode: %s\n"
"%s\tIgnoreOnIsolate: %s\n",
prefix, yes_no(u->stop_when_unneeded),
prefix, yes_no(u->refuse_manual_start),
prefix, yes_no(u->refuse_manual_stop),
prefix, yes_no(u->default_dependencies),
prefix, job_mode_to_string(u->on_failure_job_mode),
prefix, yes_no(u->ignore_on_isolate));
if (UNIT_VTABLE(u)->dump)
UNIT_VTABLE(u)->dump(u, f, prefix2);
} else if (u->load_state == UNIT_MERGED)
fprintf(f,
"%s\tMerged into: %s\n",
prefix, u->merged_into->id);
else if (u->load_state == UNIT_ERROR)
fprintf(f, "%s\tLoad Error Code: %s\n", prefix, strerror_safe(u->load_error));
for (const char *n = sd_bus_track_first(u->bus_track); n; n = sd_bus_track_next(u->bus_track))
fprintf(f, "%s\tBus Ref: %s\n", prefix, n);
if (u->job)
job_dump(u->job, f, prefix2);
if (u->nop_job)
job_dump(u->nop_job, f, prefix2);
}

13
src/core/unit-serialize.h Normal file
View File

@ -0,0 +1,13 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include <stdio.h>
#include "unit.h"
#include "fdset.h"
int unit_serialize(Unit *u, FILE *f, FDSet *fds, bool serialize_jobs);
int unit_deserialize(Unit *u, FILE *f, FDSet *fds);
int unit_deserialize_skip(FILE *f);
void unit_dump(Unit *u, FILE *f, const char *prefix);

View File

@ -26,8 +26,8 @@
#include "fileio.h"
#include "format-util.h"
#include "id128-util.h"
#include "io-util.h"
#include "install.h"
#include "io-util.h"
#include "label.h"
#include "load-dropin.h"
#include "load-fragment.h"
@ -35,11 +35,9 @@
#include "macro.h"
#include "missing_audit.h"
#include "mkdir.h"
#include "parse-util.h"
#include "path-util.h"
#include "process-util.h"
#include "rm-rf.h"
#include "serialize.h"
#include "set.h"
#include "signal-util.h"
#include "sparse-endian.h"
@ -1166,269 +1164,6 @@ const char *unit_status_string(Unit *u) {
return unit_description(u);
}
static void print_unit_dependency_mask(FILE *f, const char *kind, UnitDependencyMask mask, bool *space) {
const struct {
UnitDependencyMask mask;
const char *name;
} table[] = {
{ UNIT_DEPENDENCY_FILE, "file" },
{ UNIT_DEPENDENCY_IMPLICIT, "implicit" },
{ UNIT_DEPENDENCY_DEFAULT, "default" },
{ UNIT_DEPENDENCY_UDEV, "udev" },
{ UNIT_DEPENDENCY_PATH, "path" },
{ UNIT_DEPENDENCY_MOUNTINFO_IMPLICIT, "mountinfo-implicit" },
{ UNIT_DEPENDENCY_MOUNTINFO_DEFAULT, "mountinfo-default" },
{ UNIT_DEPENDENCY_PROC_SWAP, "proc-swap" },
};
assert(f);
assert(kind);
assert(space);
for (size_t i = 0; i < ELEMENTSOF(table); i++) {
if (mask == 0)
break;
if (FLAGS_SET(mask, table[i].mask)) {
if (*space)
fputc(' ', f);
else
*space = true;
fputs(kind, f);
fputs("-", f);
fputs(table[i].name, f);
mask &= ~table[i].mask;
}
}
assert(mask == 0);
}
void unit_dump(Unit *u, FILE *f, const char *prefix) {
char *t, **j;
const char *prefix2;
char timestamp[5][FORMAT_TIMESTAMP_MAX], timespan[FORMAT_TIMESPAN_MAX];
Unit *following;
_cleanup_set_free_ Set *following_set = NULL;
CGroupMask m;
int r;
assert(u);
assert(u->type >= 0);
prefix = strempty(prefix);
prefix2 = strjoina(prefix, "\t");
fprintf(f,
"%s-> Unit %s:\n",
prefix, u->id);
SET_FOREACH(t, u->aliases)
fprintf(f, "%s\tAlias: %s\n", prefix, t);
fprintf(f,
"%s\tDescription: %s\n"
"%s\tInstance: %s\n"
"%s\tUnit Load State: %s\n"
"%s\tUnit Active State: %s\n"
"%s\tState Change Timestamp: %s\n"
"%s\tInactive Exit Timestamp: %s\n"
"%s\tActive Enter Timestamp: %s\n"
"%s\tActive Exit Timestamp: %s\n"
"%s\tInactive Enter Timestamp: %s\n"
"%s\tMay GC: %s\n"
"%s\tNeed Daemon Reload: %s\n"
"%s\tTransient: %s\n"
"%s\tPerpetual: %s\n"
"%s\tGarbage Collection Mode: %s\n"
"%s\tSlice: %s\n"
"%s\tCGroup: %s\n"
"%s\tCGroup realized: %s\n",
prefix, unit_description(u),
prefix, strna(u->instance),
prefix, unit_load_state_to_string(u->load_state),
prefix, unit_active_state_to_string(unit_active_state(u)),
prefix, strna(format_timestamp(timestamp[0], sizeof(timestamp[0]), u->state_change_timestamp.realtime)),
prefix, strna(format_timestamp(timestamp[1], sizeof(timestamp[1]), u->inactive_exit_timestamp.realtime)),
prefix, strna(format_timestamp(timestamp[2], sizeof(timestamp[2]), u->active_enter_timestamp.realtime)),
prefix, strna(format_timestamp(timestamp[3], sizeof(timestamp[3]), u->active_exit_timestamp.realtime)),
prefix, strna(format_timestamp(timestamp[4], sizeof(timestamp[4]), u->inactive_enter_timestamp.realtime)),
prefix, yes_no(unit_may_gc(u)),
prefix, yes_no(unit_need_daemon_reload(u)),
prefix, yes_no(u->transient),
prefix, yes_no(u->perpetual),
prefix, collect_mode_to_string(u->collect_mode),
prefix, strna(unit_slice_name(u)),
prefix, strna(u->cgroup_path),
prefix, yes_no(u->cgroup_realized));
if (u->cgroup_realized_mask != 0) {
_cleanup_free_ char *s = NULL;
(void) cg_mask_to_string(u->cgroup_realized_mask, &s);
fprintf(f, "%s\tCGroup realized mask: %s\n", prefix, strnull(s));
}
if (u->cgroup_enabled_mask != 0) {
_cleanup_free_ char *s = NULL;
(void) cg_mask_to_string(u->cgroup_enabled_mask, &s);
fprintf(f, "%s\tCGroup enabled mask: %s\n", prefix, strnull(s));
}
m = unit_get_own_mask(u);
if (m != 0) {
_cleanup_free_ char *s = NULL;
(void) cg_mask_to_string(m, &s);
fprintf(f, "%s\tCGroup own mask: %s\n", prefix, strnull(s));
}
m = unit_get_members_mask(u);
if (m != 0) {
_cleanup_free_ char *s = NULL;
(void) cg_mask_to_string(m, &s);
fprintf(f, "%s\tCGroup members mask: %s\n", prefix, strnull(s));
}
m = unit_get_delegate_mask(u);
if (m != 0) {
_cleanup_free_ char *s = NULL;
(void) cg_mask_to_string(m, &s);
fprintf(f, "%s\tCGroup delegate mask: %s\n", prefix, strnull(s));
}
if (!sd_id128_is_null(u->invocation_id))
fprintf(f, "%s\tInvocation ID: " SD_ID128_FORMAT_STR "\n",
prefix, SD_ID128_FORMAT_VAL(u->invocation_id));
STRV_FOREACH(j, u->documentation)
fprintf(f, "%s\tDocumentation: %s\n", prefix, *j);
following = unit_following(u);
if (following)
fprintf(f, "%s\tFollowing: %s\n", prefix, following->id);
r = unit_following_set(u, &following_set);
if (r >= 0) {
Unit *other;
SET_FOREACH(other, following_set)
fprintf(f, "%s\tFollowing Set Member: %s\n", prefix, other->id);
}
if (u->fragment_path)
fprintf(f, "%s\tFragment Path: %s\n", prefix, u->fragment_path);
if (u->source_path)
fprintf(f, "%s\tSource Path: %s\n", prefix, u->source_path);
STRV_FOREACH(j, u->dropin_paths)
fprintf(f, "%s\tDropIn Path: %s\n", prefix, *j);
if (u->failure_action != EMERGENCY_ACTION_NONE)
fprintf(f, "%s\tFailure Action: %s\n", prefix, emergency_action_to_string(u->failure_action));
if (u->failure_action_exit_status >= 0)
fprintf(f, "%s\tFailure Action Exit Status: %i\n", prefix, u->failure_action_exit_status);
if (u->success_action != EMERGENCY_ACTION_NONE)
fprintf(f, "%s\tSuccess Action: %s\n", prefix, emergency_action_to_string(u->success_action));
if (u->success_action_exit_status >= 0)
fprintf(f, "%s\tSuccess Action Exit Status: %i\n", prefix, u->success_action_exit_status);
if (u->job_timeout != USEC_INFINITY)
fprintf(f, "%s\tJob Timeout: %s\n", prefix, format_timespan(timespan, sizeof(timespan), u->job_timeout, 0));
if (u->job_timeout_action != EMERGENCY_ACTION_NONE)
fprintf(f, "%s\tJob Timeout Action: %s\n", prefix, emergency_action_to_string(u->job_timeout_action));
if (u->job_timeout_reboot_arg)
fprintf(f, "%s\tJob Timeout Reboot Argument: %s\n", prefix, u->job_timeout_reboot_arg);
condition_dump_list(u->conditions, f, prefix, condition_type_to_string);
condition_dump_list(u->asserts, f, prefix, assert_type_to_string);
if (dual_timestamp_is_set(&u->condition_timestamp))
fprintf(f,
"%s\tCondition Timestamp: %s\n"
"%s\tCondition Result: %s\n",
prefix, strna(format_timestamp(timestamp[0], sizeof(timestamp[0]), u->condition_timestamp.realtime)),
prefix, yes_no(u->condition_result));
if (dual_timestamp_is_set(&u->assert_timestamp))
fprintf(f,
"%s\tAssert Timestamp: %s\n"
"%s\tAssert Result: %s\n",
prefix, strna(format_timestamp(timestamp[0], sizeof(timestamp[0]), u->assert_timestamp.realtime)),
prefix, yes_no(u->assert_result));
for (UnitDependency d = 0; d < _UNIT_DEPENDENCY_MAX; d++) {
UnitDependencyInfo di;
Unit *other;
HASHMAP_FOREACH_KEY(di.data, other, u->dependencies[d]) {
bool space = false;
fprintf(f, "%s\t%s: %s (", prefix, unit_dependency_to_string(d), other->id);
print_unit_dependency_mask(f, "origin", di.origin_mask, &space);
print_unit_dependency_mask(f, "destination", di.destination_mask, &space);
fputs(")\n", f);
}
}
if (!hashmap_isempty(u->requires_mounts_for)) {
UnitDependencyInfo di;
const char *path;
HASHMAP_FOREACH_KEY(di.data, path, u->requires_mounts_for) {
bool space = false;
fprintf(f, "%s\tRequiresMountsFor: %s (", prefix, path);
print_unit_dependency_mask(f, "origin", di.origin_mask, &space);
print_unit_dependency_mask(f, "destination", di.destination_mask, &space);
fputs(")\n", f);
}
}
if (u->load_state == UNIT_LOADED) {
fprintf(f,
"%s\tStopWhenUnneeded: %s\n"
"%s\tRefuseManualStart: %s\n"
"%s\tRefuseManualStop: %s\n"
"%s\tDefaultDependencies: %s\n"
"%s\tOnFailureJobMode: %s\n"
"%s\tIgnoreOnIsolate: %s\n",
prefix, yes_no(u->stop_when_unneeded),
prefix, yes_no(u->refuse_manual_start),
prefix, yes_no(u->refuse_manual_stop),
prefix, yes_no(u->default_dependencies),
prefix, job_mode_to_string(u->on_failure_job_mode),
prefix, yes_no(u->ignore_on_isolate));
if (UNIT_VTABLE(u)->dump)
UNIT_VTABLE(u)->dump(u, f, prefix2);
} else if (u->load_state == UNIT_MERGED)
fprintf(f,
"%s\tMerged into: %s\n",
prefix, u->merged_into->id);
else if (u->load_state == UNIT_ERROR)
fprintf(f, "%s\tLoad Error Code: %s\n", prefix, strerror_safe(u->load_error));
for (const char *n = sd_bus_track_first(u->bus_track); n; n = sd_bus_track_next(u->bus_track))
fprintf(f, "%s\tBus Ref: %s\n", prefix, n);
if (u->job)
job_dump(u->job, f, prefix2);
if (u->nop_job)
job_dump(u->nop_job, f, prefix2);
}
/* Common implementation for multiple backends */
int unit_load_fragment_and_dropin(Unit *u, bool fragment_required) {
int r;
@ -2655,9 +2390,13 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, UnitNotifyFlag
/* Make sure the cgroup and state files are always removed when we become inactive */
if (UNIT_IS_INACTIVE_OR_FAILED(ns)) {
SET_FLAG(u->markers,
(1u << UNIT_MARKER_NEEDS_RELOAD)|(1u << UNIT_MARKER_NEEDS_RESTART),
false);
unit_prune_cgroup(u);
unit_unlink_state_files(u);
}
} else if (ns != os && ns == UNIT_RELOADING)
SET_FLAG(u->markers, 1u << UNIT_MARKER_NEEDS_RELOAD, false);
unit_update_on_console(u);
@ -3241,7 +2980,7 @@ char *unit_dbus_path_invocation_id(Unit *u) {
return unit_dbus_path_from_name(u->invocation_id_string);
}
static int unit_set_invocation_id(Unit *u, sd_id128_t id) {
int unit_set_invocation_id(Unit *u, sd_id128_t id) {
int r;
assert(u);
@ -3526,539 +3265,6 @@ bool unit_can_serialize(Unit *u) {
return UNIT_VTABLE(u)->serialize && UNIT_VTABLE(u)->deserialize_item;
}
static int serialize_cgroup_mask(FILE *f, const char *key, CGroupMask mask) {
_cleanup_free_ char *s = NULL;
int r;
assert(f);
assert(key);
if (mask == 0)
return 0;
r = cg_mask_to_string(mask, &s);
if (r < 0)
return log_error_errno(r, "Failed to format cgroup mask: %m");
return serialize_item(f, key, s);
}
static const char *const ip_accounting_metric_field[_CGROUP_IP_ACCOUNTING_METRIC_MAX] = {
[CGROUP_IP_INGRESS_BYTES] = "ip-accounting-ingress-bytes",
[CGROUP_IP_INGRESS_PACKETS] = "ip-accounting-ingress-packets",
[CGROUP_IP_EGRESS_BYTES] = "ip-accounting-egress-bytes",
[CGROUP_IP_EGRESS_PACKETS] = "ip-accounting-egress-packets",
};
static const char *const io_accounting_metric_field_base[_CGROUP_IO_ACCOUNTING_METRIC_MAX] = {
[CGROUP_IO_READ_BYTES] = "io-accounting-read-bytes-base",
[CGROUP_IO_WRITE_BYTES] = "io-accounting-write-bytes-base",
[CGROUP_IO_READ_OPERATIONS] = "io-accounting-read-operations-base",
[CGROUP_IO_WRITE_OPERATIONS] = "io-accounting-write-operations-base",
};
static const char *const io_accounting_metric_field_last[_CGROUP_IO_ACCOUNTING_METRIC_MAX] = {
[CGROUP_IO_READ_BYTES] = "io-accounting-read-bytes-last",
[CGROUP_IO_WRITE_BYTES] = "io-accounting-write-bytes-last",
[CGROUP_IO_READ_OPERATIONS] = "io-accounting-read-operations-last",
[CGROUP_IO_WRITE_OPERATIONS] = "io-accounting-write-operations-last",
};
int unit_serialize(Unit *u, FILE *f, FDSet *fds, bool serialize_jobs) {
int r;
assert(u);
assert(f);
assert(fds);
if (unit_can_serialize(u)) {
r = UNIT_VTABLE(u)->serialize(u, f, fds);
if (r < 0)
return r;
}
(void) serialize_dual_timestamp(f, "state-change-timestamp", &u->state_change_timestamp);
(void) serialize_dual_timestamp(f, "inactive-exit-timestamp", &u->inactive_exit_timestamp);
(void) serialize_dual_timestamp(f, "active-enter-timestamp", &u->active_enter_timestamp);
(void) serialize_dual_timestamp(f, "active-exit-timestamp", &u->active_exit_timestamp);
(void) serialize_dual_timestamp(f, "inactive-enter-timestamp", &u->inactive_enter_timestamp);
(void) serialize_dual_timestamp(f, "condition-timestamp", &u->condition_timestamp);
(void) serialize_dual_timestamp(f, "assert-timestamp", &u->assert_timestamp);
if (dual_timestamp_is_set(&u->condition_timestamp))
(void) serialize_bool(f, "condition-result", u->condition_result);
if (dual_timestamp_is_set(&u->assert_timestamp))
(void) serialize_bool(f, "assert-result", u->assert_result);
(void) serialize_bool(f, "transient", u->transient);
(void) serialize_bool(f, "in-audit", u->in_audit);
(void) serialize_bool(f, "exported-invocation-id", u->exported_invocation_id);
(void) serialize_bool(f, "exported-log-level-max", u->exported_log_level_max);
(void) serialize_bool(f, "exported-log-extra-fields", u->exported_log_extra_fields);
(void) serialize_bool(f, "exported-log-rate-limit-interval", u->exported_log_ratelimit_interval);
(void) serialize_bool(f, "exported-log-rate-limit-burst", u->exported_log_ratelimit_burst);
(void) serialize_item_format(f, "cpu-usage-base", "%" PRIu64, u->cpu_usage_base);
if (u->cpu_usage_last != NSEC_INFINITY)
(void) serialize_item_format(f, "cpu-usage-last", "%" PRIu64, u->cpu_usage_last);
if (u->managed_oom_kill_last > 0)
(void) serialize_item_format(f, "managed-oom-kill-last", "%" PRIu64, u->managed_oom_kill_last);
if (u->oom_kill_last > 0)
(void) serialize_item_format(f, "oom-kill-last", "%" PRIu64, u->oom_kill_last);
for (CGroupIOAccountingMetric im = 0; im < _CGROUP_IO_ACCOUNTING_METRIC_MAX; im++) {
(void) serialize_item_format(f, io_accounting_metric_field_base[im], "%" PRIu64, u->io_accounting_base[im]);
if (u->io_accounting_last[im] != UINT64_MAX)
(void) serialize_item_format(f, io_accounting_metric_field_last[im], "%" PRIu64, u->io_accounting_last[im]);
}
if (u->cgroup_path)
(void) serialize_item(f, "cgroup", u->cgroup_path);
(void) serialize_bool(f, "cgroup-realized", u->cgroup_realized);
(void) serialize_cgroup_mask(f, "cgroup-realized-mask", u->cgroup_realized_mask);
(void) serialize_cgroup_mask(f, "cgroup-enabled-mask", u->cgroup_enabled_mask);
(void) serialize_cgroup_mask(f, "cgroup-invalidated-mask", u->cgroup_invalidated_mask);
if (uid_is_valid(u->ref_uid))
(void) serialize_item_format(f, "ref-uid", UID_FMT, u->ref_uid);
if (gid_is_valid(u->ref_gid))
(void) serialize_item_format(f, "ref-gid", GID_FMT, u->ref_gid);
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, "freezer-state", "%s", freezer_state_to_string(unit_freezer_state(u)));
bus_track_serialize(u->bus_track, f, "ref");
for (CGroupIPAccountingMetric m = 0; m < _CGROUP_IP_ACCOUNTING_METRIC_MAX; m++) {
uint64_t v;
r = unit_get_ip_accounting(u, m, &v);
if (r >= 0)
(void) serialize_item_format(f, ip_accounting_metric_field[m], "%" PRIu64, v);
}
if (serialize_jobs) {
if (u->job) {
fputs("job\n", f);
job_serialize(u->job, f);
}
if (u->nop_job) {
fputs("job\n", f);
job_serialize(u->nop_job, f);
}
}
/* End marker */
fputc('\n', f);
return 0;
}
static int unit_deserialize_job(Unit *u, FILE *f) {
_cleanup_(job_freep) Job *j = NULL;
int r;
assert(u);
assert(f);
j = job_new_raw(u);
if (!j)
return log_oom();
r = job_deserialize(j, f);
if (r < 0)
return r;
r = job_install_deserialized(j);
if (r < 0)
return r;
TAKE_PTR(j);
return 0;
}
int unit_deserialize(Unit *u, FILE *f, FDSet *fds) {
int r;
assert(u);
assert(f);
assert(fds);
for (;;) {
_cleanup_free_ char *line = NULL;
char *l, *v;
ssize_t m;
size_t k;
r = read_line(f, LONG_LINE_MAX, &line);
if (r < 0)
return log_error_errno(r, "Failed to read serialization line: %m");
if (r == 0) /* eof */
break;
l = strstrip(line);
if (isempty(l)) /* End marker */
break;
k = strcspn(l, "=");
if (l[k] == '=') {
l[k] = 0;
v = l+k+1;
} else
v = l+k;
if (streq(l, "job")) {
if (v[0] == '\0') {
/* New-style serialized job */
r = unit_deserialize_job(u, f);
if (r < 0)
return r;
} else /* Legacy for pre-44 */
log_unit_warning(u, "Update from too old systemd versions are unsupported, cannot deserialize job: %s", v);
continue;
} else if (streq(l, "state-change-timestamp")) {
(void) deserialize_dual_timestamp(v, &u->state_change_timestamp);
continue;
} else if (streq(l, "inactive-exit-timestamp")) {
(void) deserialize_dual_timestamp(v, &u->inactive_exit_timestamp);
continue;
} else if (streq(l, "active-enter-timestamp")) {
(void) deserialize_dual_timestamp(v, &u->active_enter_timestamp);
continue;
} else if (streq(l, "active-exit-timestamp")) {
(void) deserialize_dual_timestamp(v, &u->active_exit_timestamp);
continue;
} else if (streq(l, "inactive-enter-timestamp")) {
(void) deserialize_dual_timestamp(v, &u->inactive_enter_timestamp);
continue;
} else if (streq(l, "condition-timestamp")) {
(void) deserialize_dual_timestamp(v, &u->condition_timestamp);
continue;
} else if (streq(l, "assert-timestamp")) {
(void) deserialize_dual_timestamp(v, &u->assert_timestamp);
continue;
} else if (streq(l, "condition-result")) {
r = parse_boolean(v);
if (r < 0)
log_unit_debug(u, "Failed to parse condition result value %s, ignoring.", v);
else
u->condition_result = r;
continue;
} else if (streq(l, "assert-result")) {
r = parse_boolean(v);
if (r < 0)
log_unit_debug(u, "Failed to parse assert result value %s, ignoring.", v);
else
u->assert_result = r;
continue;
} else if (streq(l, "transient")) {
r = parse_boolean(v);
if (r < 0)
log_unit_debug(u, "Failed to parse transient bool %s, ignoring.", v);
else
u->transient = r;
continue;
} else if (streq(l, "in-audit")) {
r = parse_boolean(v);
if (r < 0)
log_unit_debug(u, "Failed to parse in-audit bool %s, ignoring.", v);
else
u->in_audit = r;
continue;
} else if (streq(l, "exported-invocation-id")) {
r = parse_boolean(v);
if (r < 0)
log_unit_debug(u, "Failed to parse exported invocation ID bool %s, ignoring.", v);
else
u->exported_invocation_id = r;
continue;
} else if (streq(l, "exported-log-level-max")) {
r = parse_boolean(v);
if (r < 0)
log_unit_debug(u, "Failed to parse exported log level max bool %s, ignoring.", v);
else
u->exported_log_level_max = r;
continue;
} else if (streq(l, "exported-log-extra-fields")) {
r = parse_boolean(v);
if (r < 0)
log_unit_debug(u, "Failed to parse exported log extra fields bool %s, ignoring.", v);
else
u->exported_log_extra_fields = r;
continue;
} else if (streq(l, "exported-log-rate-limit-interval")) {
r = parse_boolean(v);
if (r < 0)
log_unit_debug(u, "Failed to parse exported log rate limit interval %s, ignoring.", v);
else
u->exported_log_ratelimit_interval = r;
continue;
} else if (streq(l, "exported-log-rate-limit-burst")) {
r = parse_boolean(v);
if (r < 0)
log_unit_debug(u, "Failed to parse exported log rate limit burst %s, ignoring.", v);
else
u->exported_log_ratelimit_burst = r;
continue;
} else if (STR_IN_SET(l, "cpu-usage-base", "cpuacct-usage-base")) {
r = safe_atou64(v, &u->cpu_usage_base);
if (r < 0)
log_unit_debug(u, "Failed to parse CPU usage base %s, ignoring.", v);
continue;
} else if (streq(l, "cpu-usage-last")) {
r = safe_atou64(v, &u->cpu_usage_last);
if (r < 0)
log_unit_debug(u, "Failed to read CPU usage last %s, ignoring.", v);
continue;
} else if (streq(l, "managed-oom-kill-last")) {
r = safe_atou64(v, &u->managed_oom_kill_last);
if (r < 0)
log_unit_debug(u, "Failed to read managed OOM kill last %s, ignoring.", v);
continue;
} else if (streq(l, "oom-kill-last")) {
r = safe_atou64(v, &u->oom_kill_last);
if (r < 0)
log_unit_debug(u, "Failed to read OOM kill last %s, ignoring.", v);
continue;
} else if (streq(l, "cgroup")) {
r = unit_set_cgroup_path(u, v);
if (r < 0)
log_unit_debug_errno(u, r, "Failed to set cgroup path %s, ignoring: %m", v);
(void) unit_watch_cgroup(u);
(void) unit_watch_cgroup_memory(u);
continue;
} else if (streq(l, "cgroup-realized")) {
int b;
b = parse_boolean(v);
if (b < 0)
log_unit_debug(u, "Failed to parse cgroup-realized bool %s, ignoring.", v);
else
u->cgroup_realized = b;
continue;
} else if (streq(l, "cgroup-realized-mask")) {
r = cg_mask_from_string(v, &u->cgroup_realized_mask);
if (r < 0)
log_unit_debug(u, "Failed to parse cgroup-realized-mask %s, ignoring.", v);
continue;
} else if (streq(l, "cgroup-enabled-mask")) {
r = cg_mask_from_string(v, &u->cgroup_enabled_mask);
if (r < 0)
log_unit_debug(u, "Failed to parse cgroup-enabled-mask %s, ignoring.", v);
continue;
} else if (streq(l, "cgroup-invalidated-mask")) {
r = cg_mask_from_string(v, &u->cgroup_invalidated_mask);
if (r < 0)
log_unit_debug(u, "Failed to parse cgroup-invalidated-mask %s, ignoring.", v);
continue;
} else if (streq(l, "ref-uid")) {
uid_t uid;
r = parse_uid(v, &uid);
if (r < 0)
log_unit_debug(u, "Failed to parse referenced UID %s, ignoring.", v);
else
unit_ref_uid_gid(u, uid, GID_INVALID);
continue;
} else if (streq(l, "ref-gid")) {
gid_t gid;
r = parse_gid(v, &gid);
if (r < 0)
log_unit_debug(u, "Failed to parse referenced GID %s, ignoring.", v);
else
unit_ref_uid_gid(u, UID_INVALID, gid);
continue;
} else if (streq(l, "ref")) {
r = strv_extend(&u->deserialized_refs, v);
if (r < 0)
return log_oom();
continue;
} else if (streq(l, "invocation-id")) {
sd_id128_t id;
r = sd_id128_from_string(v, &id);
if (r < 0)
log_unit_debug(u, "Failed to parse invocation id %s, ignoring.", v);
else {
r = unit_set_invocation_id(u, id);
if (r < 0)
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;
}
/* Check if this is an IP accounting metric serialization field */
m = string_table_lookup(ip_accounting_metric_field, ELEMENTSOF(ip_accounting_metric_field), l);
if (m >= 0) {
uint64_t c;
r = safe_atou64(v, &c);
if (r < 0)
log_unit_debug(u, "Failed to parse IP accounting value %s, ignoring.", v);
else
u->ip_accounting_extra[m] = c;
continue;
}
m = string_table_lookup(io_accounting_metric_field_base, ELEMENTSOF(io_accounting_metric_field_base), l);
if (m >= 0) {
uint64_t c;
r = safe_atou64(v, &c);
if (r < 0)
log_unit_debug(u, "Failed to parse IO accounting base value %s, ignoring.", v);
else
u->io_accounting_base[m] = c;
continue;
}
m = string_table_lookup(io_accounting_metric_field_last, ELEMENTSOF(io_accounting_metric_field_last), l);
if (m >= 0) {
uint64_t c;
r = safe_atou64(v, &c);
if (r < 0)
log_unit_debug(u, "Failed to parse IO accounting last value %s, ignoring.", v);
else
u->io_accounting_last[m] = c;
continue;
}
if (unit_can_serialize(u)) {
r = exec_runtime_deserialize_compat(u, l, v, fds);
if (r < 0) {
log_unit_warning(u, "Failed to deserialize runtime parameter '%s', ignoring.", l);
continue;
}
/* Returns positive if key was handled by the call */
if (r > 0)
continue;
r = UNIT_VTABLE(u)->deserialize_item(u, l, v, fds);
if (r < 0)
log_unit_warning(u, "Failed to deserialize unit parameter '%s', ignoring.", l);
}
}
/* Versions before 228 did not carry a state change timestamp. In this case, take the current time. This is
* useful, so that timeouts based on this timestamp don't trigger too early, and is in-line with the logic from
* before 228 where the base for timeouts was not persistent across reboots. */
if (!dual_timestamp_is_set(&u->state_change_timestamp))
dual_timestamp_get(&u->state_change_timestamp);
/* Let's make sure that everything that is deserialized also gets any potential new cgroup settings applied
* after we are done. For that we invalidate anything already realized, so that we can realize it again. */
unit_invalidate_cgroup(u, _CGROUP_MASK_ALL);
unit_invalidate_cgroup_bpf(u);
return 0;
}
int unit_deserialize_skip(FILE *f) {
int r;
assert(f);
/* Skip serialized data for this unit. We don't know what it is. */
for (;;) {
_cleanup_free_ char *line = NULL;
char *l;
r = read_line(f, LONG_LINE_MAX, &line);
if (r < 0)
return log_error_errno(r, "Failed to read serialization line: %m");
if (r == 0)
return 0;
l = strstrip(line);
/* End marker */
if (isempty(l))
return 1;
}
}
int unit_add_node_dependency(Unit *u, const char *what, UnitDependency dep, UnitDependencyMask mask) {
_cleanup_free_ char *e = NULL;
Unit *device;

View File

@ -5,6 +5,8 @@
#include <stdlib.h>
#include <unistd.h>
#include "sd-id128.h"
#include "bpf-program.h"
#include "condition.h"
#include "emergency-action.h"
@ -118,9 +120,6 @@ typedef struct Unit {
UnitLoadState load_state;
Unit *merged_into;
FreezerState freezer_state;
sd_bus_message *pending_freezer_message;
char *id; /* The one special name that we use for identification */
char *instance;
@ -148,6 +147,16 @@ typedef struct Unit {
/* If this is a transient unit we are currently writing, this is where we are writing it to */
FILE *transient_file;
/* Freezer state */
sd_bus_message *pending_freezer_message;
FreezerState freezer_state;
/* Job timeout and action to take */
EmergencyAction job_timeout_action;
usec_t job_timeout;
usec_t job_running_timeout;
char *job_timeout_reboot_arg;
/* If there is something to do with this unit, then this is the installed job for it */
Job *job;
@ -162,13 +171,6 @@ typedef struct Unit {
sd_bus_track *bus_track;
char **deserialized_refs;
/* Job timeout and action to take */
usec_t job_timeout;
usec_t job_running_timeout;
bool job_running_timeout_set:1;
EmergencyAction job_timeout_action;
char *job_timeout_reboot_arg;
/* References to this */
LIST_HEAD(UnitRef, refs_by_target);
@ -240,6 +242,9 @@ typedef struct Unit {
RateLimit start_ratelimit;
EmergencyAction start_limit_action;
/* The unit has been marked for reload, restart, etc. Stored as 1u << marker1 | 1u << marker2. */
unsigned markers;
/* What to do on failure or success */
EmergencyAction success_action, failure_action;
int success_action_exit_status, failure_action_exit_status;
@ -357,6 +362,8 @@ typedef struct Unit {
bool sent_dbus_new_signal:1;
bool job_running_timeout_set:1;
bool in_audit:1;
bool on_console:1;
@ -721,8 +728,6 @@ int unit_freezer_state_kernel(Unit *u, FreezerState *ret);
const char* unit_sub_state_to_string(Unit *u);
void unit_dump(Unit *u, FILE *f, const char *prefix);
bool unit_can_reload(Unit *u) _pure_;
bool unit_can_start(Unit *u) _pure_;
bool unit_can_stop(Unit *u) _pure_;
@ -764,10 +769,6 @@ int unit_load_related_unit(Unit *u, const char *type, Unit **_found);
bool unit_can_serialize(Unit *u) _pure_;
int unit_serialize(Unit *u, FILE *f, FDSet *fds, bool serialize_jobs);
int unit_deserialize(Unit *u, FILE *f, FDSet *fds);
int unit_deserialize_skip(FILE *f);
int unit_add_node_dependency(Unit *u, const char *what, UnitDependency d, UnitDependencyMask mask);
int unit_add_blockdev_dependency(Unit *u, const char *what, UnitDependencyMask mask);
@ -847,6 +848,7 @@ void unit_unref_uid_gid(Unit *u, bool destroy_now);
void unit_notify_user_lookup(Unit *u, uid_t uid, gid_t gid);
int unit_set_invocation_id(Unit *u, sd_id128_t id);
int unit_acquire_invocation_id(Unit *u);
bool unit_shall_confirm_spawn(Unit *u);

View File

@ -5587,17 +5587,26 @@ int bus_message_get_blob(sd_bus_message *m, void **buffer, size_t *sz) {
}
int bus_message_read_strv_extend(sd_bus_message *m, char ***l) {
const char *s;
char type;
const char *contents, *s;
int r;
assert(m);
assert(l);
r = sd_bus_message_enter_container(m, 'a', "s");
r = sd_bus_message_peek_type(m, &type, &contents);
if (r < 0)
return r;
if (type != SD_BUS_TYPE_ARRAY || !STR_IN_SET(contents, "s", "o", "g"))
return -ENXIO;
r = sd_bus_message_enter_container(m, 'a', NULL);
if (r <= 0)
return r;
while ((r = sd_bus_message_read_basic(m, 's', &s)) > 0) {
/* sd_bus_message_read_basic() does content validation for us. */
while ((r = sd_bus_message_read_basic(m, *contents, &s)) > 0) {
r = strv_extend(l, s);
if (r < 0)
return r;

View File

@ -297,8 +297,7 @@ static int client(struct context *c) {
assert_se(r >= 0);
assert_se(streq(s, "<<<hallo>>>"));
sd_bus_message_unref(reply);
reply = NULL;
reply = sd_bus_message_unref(reply);
r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "Doesntexist", &error, &reply, "");
assert_se(r < 0);
@ -306,6 +305,12 @@ static int client(struct context *c) {
sd_bus_error_free(&error);
r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "Doesntexist", &error, &reply, NULL); /* NULL and "" are equivalent */
assert_se(r < 0);
assert_se(sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_METHOD));
sd_bus_error_free(&error);
r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "AlterSomething", &error, &reply, "as", 1, "hallo");
assert_se(r < 0);
assert_se(sd_bus_error_has_name(&error, SD_BUS_ERROR_INVALID_ARGS));
@ -319,8 +324,7 @@ static int client(struct context *c) {
assert_se(r >= 0);
assert_se(streq(s, "<<<hallo>>>"));
sd_bus_message_unref(reply);
reply = NULL;
reply = sd_bus_message_unref(reply);
r = sd_bus_set_property(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "Something", &error, "s", "test");
assert_se(r >= 0);
@ -332,8 +336,7 @@ static int client(struct context *c) {
assert_se(r >= 0);
assert_se(streq(s, "test"));
sd_bus_message_unref(reply);
reply = NULL;
reply = sd_bus_message_unref(reply);
r = sd_bus_set_property(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "AutomaticIntegerProperty", &error, "u", 815);
assert_se(r >= 0);
@ -352,8 +355,16 @@ static int client(struct context *c) {
assert_se(r >= 0);
fputs(s, stdout);
sd_bus_message_unref(reply);
reply = NULL;
reply = sd_bus_message_unref(reply);
r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.DBus.Introspectable", "Introspect", &error, &reply, NULL); /* NULL and "" are equivalent */
assert_se(r >= 0);
r = sd_bus_message_read(reply, "s", &s);
assert_se(r >= 0);
fputs(s, stdout);
reply = sd_bus_message_unref(reply);
r = sd_bus_get_property(bus, "org.freedesktop.systemd.test", "/value/xuzz", "org.freedesktop.systemd.ValueTest", "Value", &error, &reply, "s");
assert_se(r >= 0);
@ -362,66 +373,60 @@ static int client(struct context *c) {
assert_se(r >= 0);
log_info("read %s", s);
sd_bus_message_unref(reply);
reply = NULL;
reply = sd_bus_message_unref(reply);
r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/", "org.freedesktop.DBus.Introspectable", "Introspect", &error, &reply, "");
r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/", "org.freedesktop.DBus.Introspectable", "Introspect", &error, &reply, NULL);
assert_se(r >= 0);
r = sd_bus_message_read(reply, "s", &s);
assert_se(r >= 0);
fputs(s, stdout);
sd_bus_message_unref(reply);
reply = NULL;
reply = sd_bus_message_unref(reply);
r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/value", "org.freedesktop.DBus.Introspectable", "Introspect", &error, &reply, "");
r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/value", "org.freedesktop.DBus.Introspectable", "Introspect", &error, &reply, NULL);
assert_se(r >= 0);
r = sd_bus_message_read(reply, "s", &s);
assert_se(r >= 0);
fputs(s, stdout);
sd_bus_message_unref(reply);
reply = NULL;
reply = sd_bus_message_unref(reply);
r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/value/a", "org.freedesktop.DBus.Introspectable", "Introspect", &error, &reply, "");
r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/value/a", "org.freedesktop.DBus.Introspectable", "Introspect", &error, &reply, NULL);
assert_se(r >= 0);
r = sd_bus_message_read(reply, "s", &s);
assert_se(r >= 0);
fputs(s, stdout);
sd_bus_message_unref(reply);
reply = NULL;
reply = sd_bus_message_unref(reply);
r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.DBus.Properties", "GetAll", &error, &reply, "s", "");
r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.DBus.Properties", "GetAll", &error, &reply, "s", NULL);
assert_se(r >= 0);
sd_bus_message_dump(reply, stdout, SD_BUS_MESSAGE_DUMP_WITH_HEADER);
sd_bus_message_unref(reply);
reply = NULL;
reply = sd_bus_message_unref(reply);
r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/value/a", "org.freedesktop.DBus.Properties", "GetAll", &error, &reply, "s", "org.freedesktop.systemd.ValueTest2");
assert_se(r < 0);
assert_se(sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_INTERFACE));
sd_bus_error_free(&error);
r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.DBus.ObjectManager", "GetManagedObjects", &error, &reply, "");
r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.DBus.ObjectManager", "GetManagedObjects", &error, &reply, NULL);
assert_se(r < 0);
assert_se(sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_METHOD));
sd_bus_error_free(&error);
r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/value", "org.freedesktop.DBus.ObjectManager", "GetManagedObjects", &error, &reply, "");
r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/value", "org.freedesktop.DBus.ObjectManager", "GetManagedObjects", &error, &reply, NULL);
assert_se(r >= 0);
sd_bus_message_dump(reply, stdout, SD_BUS_MESSAGE_DUMP_WITH_HEADER);
sd_bus_message_unref(reply);
reply = NULL;
reply = sd_bus_message_unref(reply);
r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/value/a", "org.freedesktop.systemd.ValueTest", "NotifyTest", &error, NULL, "");
r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/value/a", "org.freedesktop.systemd.ValueTest", "NotifyTest", &error, NULL, NULL);
assert_se(r >= 0);
r = sd_bus_process(bus, &reply);
@ -430,10 +435,9 @@ static int client(struct context *c) {
assert_se(sd_bus_message_is_signal(reply, "org.freedesktop.DBus.Properties", "PropertiesChanged"));
sd_bus_message_dump(reply, stdout, SD_BUS_MESSAGE_DUMP_WITH_HEADER);
sd_bus_message_unref(reply);
reply = NULL;
reply = sd_bus_message_unref(reply);
r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/value/a", "org.freedesktop.systemd.ValueTest", "NotifyTest2", &error, NULL, "");
r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/value/a", "org.freedesktop.systemd.ValueTest", "NotifyTest2", &error, NULL, NULL);
assert_se(r >= 0);
r = sd_bus_process(bus, &reply);
@ -442,10 +446,9 @@ static int client(struct context *c) {
assert_se(sd_bus_message_is_signal(reply, "org.freedesktop.DBus.Properties", "PropertiesChanged"));
sd_bus_message_dump(reply, stdout, SD_BUS_MESSAGE_DUMP_WITH_HEADER);
sd_bus_message_unref(reply);
reply = NULL;
reply = sd_bus_message_unref(reply);
r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "EmitInterfacesAdded", &error, NULL, "");
r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "EmitInterfacesAdded", &error, NULL, NULL);
assert_se(r >= 0);
r = sd_bus_process(bus, &reply);
@ -454,10 +457,9 @@ static int client(struct context *c) {
assert_se(sd_bus_message_is_signal(reply, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded"));
sd_bus_message_dump(reply, stdout, SD_BUS_MESSAGE_DUMP_WITH_HEADER);
sd_bus_message_unref(reply);
reply = NULL;
reply = sd_bus_message_unref(reply);
r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "EmitInterfacesRemoved", &error, NULL, "");
r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "EmitInterfacesRemoved", &error, NULL, NULL);
assert_se(r >= 0);
r = sd_bus_process(bus, &reply);
@ -466,10 +468,9 @@ static int client(struct context *c) {
assert_se(sd_bus_message_is_signal(reply, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved"));
sd_bus_message_dump(reply, stdout, SD_BUS_MESSAGE_DUMP_WITH_HEADER);
sd_bus_message_unref(reply);
reply = NULL;
reply = sd_bus_message_unref(reply);
r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "EmitObjectAdded", &error, NULL, "");
r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "EmitObjectAdded", &error, NULL, NULL);
assert_se(r >= 0);
r = sd_bus_process(bus, &reply);
@ -478,10 +479,9 @@ static int client(struct context *c) {
assert_se(sd_bus_message_is_signal(reply, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded"));
sd_bus_message_dump(reply, stdout, SD_BUS_MESSAGE_DUMP_WITH_HEADER);
sd_bus_message_unref(reply);
reply = NULL;
reply = sd_bus_message_unref(reply);
r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "EmitObjectRemoved", &error, NULL, "");
r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "EmitObjectRemoved", &error, NULL, NULL);
assert_se(r >= 0);
r = sd_bus_process(bus, &reply);
@ -490,10 +490,9 @@ static int client(struct context *c) {
assert_se(sd_bus_message_is_signal(reply, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved"));
sd_bus_message_dump(reply, stdout, SD_BUS_MESSAGE_DUMP_WITH_HEADER);
sd_bus_message_unref(reply);
reply = NULL;
reply = sd_bus_message_unref(reply);
r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "Exit", &error, NULL, "");
r = sd_bus_call_method(bus, "org.freedesktop.systemd.test", "/foo", "org.freedesktop.systemd.test", "Exit", &error, NULL, NULL);
assert_se(r >= 0);
sd_bus_flush(bus);

View File

@ -82,7 +82,9 @@ fi \
%{expand:%%{?__systemd_someargs_%#:%%__systemd_someargs_%# systemd_postun_with_restart}} \
if [ $1 -ge 1 ] && [ -x @bindir@/systemctl ]; then \
# Package upgrade, not uninstall \
@bindir@/systemctl try-restart %{?*} || : \
for unit in %{?*}; do \
@bindir@/systemctl set-property $unit Markers=+needs-restart || : \
done \
fi \
%{nil}

18
src/rpm/meson.build Normal file
View File

@ -0,0 +1,18 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
configure_file(
input : 'macros.systemd.in',
output : 'macros.systemd',
configuration : substs,
install_dir : rpmmacrosdir == 'no' ? '' : rpmmacrosdir)
# Those doesn't get installed anywhere, one of them needs to included in the
# rpm spec file definition.
configure_file(
input : 'triggers.systemd.in',
output : 'triggers.systemd',
configuration : substs)
configure_file(
input : 'triggers.systemd.sh.in',
output : 'triggers.systemd.sh',
configuration : substs)

View File

@ -6,14 +6,35 @@
# The contents of this are an example to be copied into systemd.spec.
#
# Minimum rpm version supported: 4.13.0
# Minimum rpm version supported: 4.14.0
%transfiletriggerin -P 900900 -p <lua> -- @systemunitdir@ /etc/systemd/system
-- This script will run after any package is initially installed or
-- upgraded. We care about the case where a package is initially
-- installed, because other cases are covered by the *un scriptlets,
-- so sometimes we will reload needlessly.
if posix.access("/run/systemd/system") then
pid = posix.fork()
if pid == 0 then
assert(posix.exec("%{_bindir}/systemctl", "daemon-reload"))
elseif pid > 0 then
posix.wait(pid)
end
pid = posix.fork()
if pid == 0 then
assert(posix.exec("%{_bindir}/systemctl", "reload-or-restart", "--marked"))
elseif pid > 0 then
posix.wait(pid)
end
end
%transfiletriggerpostun -P 1000100 -p <lua> -- @systemunitdir@ /etc/systemd/system
-- On removal, we need to run daemon-reload after any units have been
-- removed.
-- On upgrade, we need to run daemon-reload after any new unit files
-- have been installed, but before %postun scripts in packages get
-- executed.
if posix.access("/run/systemd/system") then
pid = posix.fork()
if pid == 0 then
@ -23,34 +44,12 @@ if posix.access("/run/systemd/system") then
end
end
%transfiletriggerun -p <lua> -- @systemunitdir@ /etc/systemd/system
-- On removal, we need to run daemon-reload after any units have been
-- removed. %transfiletriggerpostun would be ideal, but it does not get
-- executed for some reason.
-- On upgrade, we need to run daemon-reload after any new unit files
-- have been installed, but before %postun scripts in packages get
-- executed. %transfiletriggerun gets the right list of files
-- but it is invoked too early (before changes happen).
-- %filetriggerpostun happens at the right time, but it fires for
-- every package.
-- To execute the reload at the right time, we create a state
-- file in %transfiletriggerun and execute the daemon-reload in
-- the first %filetriggerpostun.
%transfiletriggerpostun -P 10000 -p <lua> -- @systemunitdir@ /etc/systemd/system
-- We restart remaining services that should be restarted here.
if posix.access("/run/systemd/system") then
posix.mkdir("%{_localstatedir}/lib")
posix.mkdir("%{_localstatedir}/lib/rpm-state")
posix.mkdir("%{_localstatedir}/lib/rpm-state/systemd")
io.open("%{_localstatedir}/lib/rpm-state/systemd/needs-reload", "w")
end
%filetriggerpostun -P 1000100 -p <lua> -- @systemunitdir@ /etc/systemd/system
if posix.access("%{_localstatedir}/lib/rpm-state/systemd/needs-reload") then
posix.unlink("%{_localstatedir}/lib/rpm-state/systemd/needs-reload")
posix.rmdir("%{_localstatedir}/lib/rpm-state/systemd")
pid = posix.fork()
if pid == 0 then
assert(posix.exec("%{_bindir}/systemctl", "daemon-reload"))
assert(posix.exec("%{_bindir}/systemctl", "reload-or-restart", "--marked"))
elseif pid > 0 then
posix.wait(pid)
end
@ -69,7 +68,43 @@ if posix.access("/run/systemd/system") then
end
end
%transfiletriggerin -P 100500 -p <lua> -- @tmpfilesdir@
%transfiletriggerin -P 1000700 udev -p <lua> -- @udevhwdbdir@
-- This script will automatically invoke hwdb update if files have been
-- installed or updated in @udevhwdbdir@.
if posix.access("/run/systemd/system") then
pid = posix.fork()
if pid == 0 then
assert(posix.exec("%{_bindir}/systemd-hwdb", "update"))
elseif pid > 0 then
posix.wait(pid)
end
end
%transfiletriggerin -P 1000700 -p <lua> -- @catalogdir@
-- This script will automatically invoke journal catalog update if files
-- have been installed or updated in @catalogdir@.
if posix.access("/run/systemd/system") then
pid = posix.fork()
if pid == 0 then
assert(posix.exec("%{_bindir}/journalctl", "--update-catalog"))
elseif pid > 0 then
posix.wait(pid)
end
end
%transfiletriggerin -P 1000700 -p <lua> -- @binfmtdir@
-- This script will automatically apply binfmt rules if files have been
-- installed or updated in @binfmtdir@.
if posix.access("/run/systemd/system") then
pid = posix.fork()
if pid == 0 then
assert(posix.exec("@rootlibexecdir@/systemd-binfmt"))
elseif pid > 0 then
posix.wait(pid)
end
end
%transfiletriggerin -P 1000600 -p <lua> -- @tmpfilesdir@
-- This script will process files installed in @tmpfilesdir@ to create
-- tmpfiles automatically. The priority is set such that it will run
-- after the sysusers file trigger, but before any other triggers.
@ -82,31 +117,7 @@ if posix.access("/run/systemd/system") then
end
end
%transfiletriggerin -p <lua> -- @udevhwdbdir@
-- This script will automatically invoke hwdb update if files have been
-- installed or updated in @udevhwdbdir@.
if posix.access("/run/systemd/system") then
pid = posix.fork()
if pid == 0 then
assert(posix.exec("%{_bindir}/systemd-hwdb", "update"))
elseif pid > 0 then
posix.wait(pid)
end
end
%transfiletriggerin -p <lua> -- @catalogdir@
-- This script will automatically invoke journal catalog update if files
-- have been installed or updated in @catalogdir@.
if posix.access("/run/systemd/system") then
pid = posix.fork()
if pid == 0 then
assert(posix.exec("%{_bindir}/journalctl", "--update-catalog"))
elseif pid > 0 then
posix.wait(pid)
end
end
%transfiletriggerin -p <lua> -- @udevrulesdir@
%transfiletriggerin -P 1000600 udev -p <lua> -- @udevrulesdir@
-- This script will automatically update udev with new rules if files
-- have been installed or updated in @udevrulesdir@.
if posix.access("/run/systemd/system") then
@ -118,7 +129,7 @@ if posix.access("/run/systemd/system") then
end
end
%transfiletriggerin -p <lua> -- @sysctldir@
%transfiletriggerin -P 1000500 -p <lua> -- @sysctldir@
-- This script will automatically apply sysctl rules if files have been
-- installed or updated in @sysctldir@.
if posix.access("/run/systemd/system") then
@ -129,15 +140,3 @@ if posix.access("/run/systemd/system") then
posix.wait(pid)
end
end
%transfiletriggerin -p <lua> -- @binfmtdir@
-- This script will automatically apply binfmt rules if files have been
-- installed or updated in @binfmtdir@.
if posix.access("/run/systemd/system") then
pid = posix.fork()
if pid == 0 then
assert(posix.exec("@rootlibexecdir@/systemd-binfmt"))
elseif pid > 0 then
posix.wait(pid)
end
end

View File

@ -0,0 +1,89 @@
# -*- Mode: rpm-spec; indent-tabs-mode: nil -*- */
# SPDX-License-Identifier: LGPL-2.1-or-later
#
# This file is part of systemd.
#
# Copyright 2018 Neal Gompa
# The contents of this are an example to be copied into systemd.spec.
#
# Minimum rpm version supported: 4.14.0
%transfiletriggerin -P 900900 -- @systemunitdir@ /etc/systemd/system
# This script will run after any package is initially installed or
# upgraded. We care about the case where a package is initially
# installed, because other cases are covered by the *un scriptlets,
# so sometimes we will reload needlessly.
if test -d "/run/systemd/system"; then
%{_bindir}/systemctl daemon-reload || :
%{_bindir}/systemctl reload-or-restart --marked || :
fi
%transfiletriggerpostun -P 1000100 -- @systemunitdir@ /etc/systemd/system
# On removal, we need to run daemon-reload after any units have been
# removed.
# On upgrade, we need to run daemon-reload after any new unit files
# have been installed, but before %postun scripts in packages get
# executed.
if test -d "/run/systemd/system"; then
%{_bindir}/systemctl daemon-reload || :
fi
%transfiletriggerpostun -P 10000 -- @systemunitdir@ /etc/systemd/system
# We restart remaining services that should be restarted here.
if test -d "/run/systemd/system"; then
%{_bindir}/systemctl reload-or-restart --marked || :
fi
%transfiletriggerin -P 1000700 -- @sysusersdir@
# This script will process files installed in @sysusersdir@ to create
# specified users automatically. The priority is set such that it
# will run before the tmpfiles file trigger.
if test -d "/run/systemd/system"; then
%{_bindir}/systemd-sysusers || :
fi
%transfiletriggerin -P 1000700 udev -- @udevhwdbdir@
# This script will automatically invoke hwdb update if files have been
# installed or updated in @udevhwdbdir@.
if test -d "/run/systemd/system"; then
%{_bindir}/systemd-hwdb update || :
fi
%transfiletriggerin -P 1000700 -- @catalogdir@
# This script will automatically invoke journal catalog update if files
# have been installed or updated in @catalogdir@.
if test -d "/run/systemd/system"; then
%{_bindir}/journalctl --update-catalog || :
fi
%transfiletriggerin -P 1000700 -- @binfmtdir@
# This script will automatically apply binfmt rules if files have been
# installed or updated in @binfmtdir@.
if test -d "/run/systemd/system"; then
# systemd-binfmt might fail if binfmt_misc kernel module is not loaded
# during install
@rootlibexecdir@/systemd-binfmt || :
fi
%transfiletriggerin -P 1000600 -- @tmpfilesdir@
# This script will process files installed in @tmpfilesdir@ to create
# tmpfiles automatically. The priority is set such that it will run
# after the sysusers file trigger, but before any other triggers.
if test -d "/run/systemd/system"; then
%{_bindir}/systemd-tmpfiles --create || :
fi
%transfiletriggerin -P 1000600 udev -- @udevrulesdir@
# This script will automatically update udev with new rules if files
# have been installed or updated in @udevrulesdir@.
if test -e /run/udev/control; then
%{_bindir}/udevadm control --reload || :
fi
%transfiletriggerin -P 1000500 -- @sysctldir@
# This script will automatically apply sysctl rules if files have been
# installed or updated in @sysctldir@.
if test -d "/run/systemd/system"; then
@rootlibexecdir@/systemd-sysctl || :
fi

View File

@ -2192,7 +2192,8 @@ static int bus_append_unit_property(sd_bus_message *m, const char *field, const
if (unit_dependency_from_string(field) >= 0 ||
STR_IN_SET(field, "Documentation",
"RequiresMountsFor"))
"RequiresMountsFor",
"Markers"))
return bus_append_strv(m, field, eq, EXTRACT_UNQUOTE);
t = condition_type_from_string(field);

View File

@ -14,7 +14,7 @@ static int check_unit_generic(int code, const UnitActiveState good_states[], int
UnitActiveState active_state;
sd_bus *bus;
char **name;
int r, i;
int r;
bool found = false;
r = acquire_bus(BUS_MANAGER, &bus);
@ -33,7 +33,7 @@ static int check_unit_generic(int code, const UnitActiveState good_states[], int
if (!arg_quiet)
puts(unit_active_state_to_string(active_state));
for (i = 0; i < nb_states; ++i)
for (int i = 0; i < nb_states; ++i)
if (good_states[i] == active_state)
found = true;
}

View File

@ -9,7 +9,7 @@
static int show_installation_targets_client_side(const char *name) {
UnitFileChange *changes = NULL;
size_t n_changes = 0, i;
size_t n_changes = 0;
UnitFileFlags flags;
char **p;
int r;
@ -22,7 +22,7 @@ static int show_installation_targets_client_side(const char *name) {
if (r < 0)
return log_error_errno(r, "Failed to get file links for %s: %m", name);
for (i = 0; i < n_changes; i++)
for (size_t i = 0; i < n_changes; i++)
if (changes[i].type == UNIT_FILE_UNLINK)
printf(" %s\n", changes[i].path);

View File

@ -12,11 +12,10 @@ static int list_dependencies_print(const char *name, int level, unsigned branche
_cleanup_free_ char *n = NULL;
size_t max_len = MAX(columns(),20u);
size_t len = 0;
int i;
if (!arg_plain) {
for (i = level - 1; i >= 0; i--) {
for (int i = level - 1; i >= 0; i--) {
len += 2;
if (len > max_len - 3 && !arg_full) {
printf("%s...\n",max_len % 2 ? "" : " ");

View File

@ -58,7 +58,6 @@ struct job_info {
static int output_jobs_list(sd_bus *bus, const struct job_info* jobs, unsigned n, bool skipped) {
_cleanup_(table_unrefp) Table *table = NULL;
const struct job_info *j;
const char *on, *off;
int r;
@ -86,7 +85,7 @@ static int output_jobs_list(sd_bus *bus, const struct job_info* jobs, unsigned n
(void) table_set_empty_string(table, "-");
for (j = jobs; j < jobs + n; j++) {
for (const struct job_info *j = jobs; j < jobs + n; j++) {
if (streq(j->state, "running"))
on = ansi_highlight();
else

View File

@ -33,12 +33,10 @@ void machine_info_clear(struct machine_info *info) {
}
static void free_machines_list(struct machine_info *machine_infos, int n) {
int i;
if (!machine_infos)
return;
for (i = 0; i < n; i++)
for (int i = 0; i < n; i++)
machine_info_clear(&machine_infos[i]);
free(machine_infos);
@ -150,7 +148,6 @@ static int get_machine_list(
static int output_machines_list(struct machine_info *machine_infos, unsigned n) {
_cleanup_(table_unrefp) Table *table = NULL;
struct machine_info *m;
bool state_missing = false;
int r;
@ -172,7 +169,7 @@ static int output_machines_list(struct machine_info *machine_infos, unsigned n)
(void) table_set_empty_string(table, "-");
for (m = machine_infos; m < machine_infos + n; m++) {
for (struct machine_info *m = machine_infos; m < machine_infos + n; m++) {
_cleanup_free_ char *mname = NULL;
const char *on_state = "", *on_failed = "";
bool circle = false;

View File

@ -136,7 +136,6 @@ static int output_unit_file_list(const UnitFileList *units, unsigned c) {
int list_unit_files(int argc, char *argv[], void *userdata) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
_cleanup_free_ UnitFileList *units = NULL;
UnitFileList *unit;
size_t size = 0;
unsigned c = 0;
const char *state;
@ -265,7 +264,7 @@ int list_unit_files(int argc, char *argv[], void *userdata) {
return r;
if (install_client_side())
for (unit = units; unit < units + c; unit++)
for (UnitFileList *unit = units; unit < units + c; unit++)
free(unit->path);
if (c == 0)

View File

@ -351,7 +351,6 @@ static int socket_info_compare(const struct socket_info *a, const struct socket_
static int output_sockets_list(struct socket_info *socket_infos, unsigned cs) {
_cleanup_(table_unrefp) Table *table = NULL;
struct socket_info *s;
const char *on, *off;
int r;
@ -373,7 +372,7 @@ static int output_sockets_list(struct socket_info *socket_infos, unsigned cs) {
(void) table_set_empty_string(table, "-");
if (cs) {
for (s = socket_infos; s < socket_infos + cs; s++) {
for (struct socket_info *s = socket_infos; s < socket_infos + cs; s++) {
_cleanup_free_ char *j = NULL;
const char *path;
@ -432,8 +431,6 @@ int list_sockets(int argc, char *argv[], void *userdata) {
_cleanup_strv_free_ char **sockets_with_suffix = NULL;
_cleanup_free_ UnitInfo *unit_infos = NULL;
_cleanup_free_ struct socket_info *socket_infos = NULL;
const UnitInfo *u;
struct socket_info *s;
unsigned cs = 0;
size_t size = 0;
int r, n;
@ -454,9 +451,9 @@ int list_sockets(int argc, char *argv[], void *userdata) {
if (n < 0)
return n;
for (u = unit_infos; u < unit_infos + n; u++) {
for (const UnitInfo *u = unit_infos; u < unit_infos + n; u++) {
_cleanup_strv_free_ char **listening = NULL, **triggered = NULL;
int i, c;
int c;
if (!endswith(u->id, ".socket"))
continue;
@ -476,7 +473,7 @@ int list_sockets(int argc, char *argv[], void *userdata) {
goto cleanup;
}
for (i = 0; i < c; i++)
for (int i = 0; i < c; i++)
socket_infos[cs + i] = (struct socket_info) {
.machine = u->machine,
.id = u->id,
@ -499,7 +496,7 @@ int list_sockets(int argc, char *argv[], void *userdata) {
cleanup:
assert(cs == 0 || socket_infos);
for (s = socket_infos; s < socket_infos + cs; s++) {
for (struct socket_info *s = socket_infos; s < socket_infos + cs; s++) {
free(s->type);
free(s->path);
if (s->own_triggered)
@ -604,7 +601,6 @@ static int timer_info_compare(const struct timer_info *a, const struct timer_inf
static int output_timers_list(struct timer_info *timer_infos, unsigned n) {
_cleanup_(table_unrefp) Table *table = NULL;
struct timer_info *t;
const char *on, *off;
int r;
@ -620,34 +616,34 @@ static int output_timers_list(struct timer_info *timer_infos, unsigned n) {
(void) table_set_empty_string(table, "-");
if (n > 0) {
for (t = timer_infos; t < timer_infos + n; t++) {
_cleanup_free_ char *j = NULL, *activates = NULL;
const char *unit;
for (struct timer_info *t = timer_infos; t < timer_infos + n; t++) {
_cleanup_free_ char *j = NULL, *activates = NULL;
const char *unit;
if (t->machine) {
j = strjoin(t->machine, ":", t->id);
if (!j)
return log_oom();
unit = j;
} else
unit = t->id;
activates = strv_join(t->triggered, ", ");
if (!activates)
if (t->machine) {
j = strjoin(t->machine, ":", t->id);
if (!j)
return log_oom();
unit = j;
} else
unit = t->id;
r = table_add_many(table,
TABLE_TIMESTAMP, t->next_elapse,
TABLE_TIMESTAMP_RELATIVE, t->next_elapse,
TABLE_TIMESTAMP, t->last_trigger,
TABLE_TIMESTAMP_RELATIVE, t->last_trigger,
TABLE_STRING, unit,
TABLE_STRING, activates);
if (r < 0)
return table_log_add_error(r);
}
activates = strv_join(t->triggered, ", ");
if (!activates)
return log_oom();
r = table_add_many(table,
TABLE_TIMESTAMP, t->next_elapse,
TABLE_TIMESTAMP_RELATIVE, t->next_elapse,
TABLE_TIMESTAMP, t->last_trigger,
TABLE_TIMESTAMP_RELATIVE, t->last_trigger,
TABLE_STRING, unit,
TABLE_STRING, activates);
if (r < 0)
return table_log_add_error(r);
}
if (n > 0) {
on = ansi_highlight();
off = ansi_normal();
} else {
@ -699,8 +695,6 @@ int list_timers(int argc, char *argv[], void *userdata) {
_cleanup_strv_free_ char **timers_with_suffix = NULL;
_cleanup_free_ struct timer_info *timer_infos = NULL;
_cleanup_free_ UnitInfo *unit_infos = NULL;
struct timer_info *t;
const UnitInfo *u;
size_t size = 0;
int n, c = 0;
dual_timestamp nw;
@ -724,7 +718,7 @@ int list_timers(int argc, char *argv[], void *userdata) {
dual_timestamp_get(&nw);
for (u = unit_infos; u < unit_infos + n; u++) {
for (const UnitInfo *u = unit_infos; u < unit_infos + n; u++) {
_cleanup_strv_free_ char **triggered = NULL;
dual_timestamp next = DUAL_TIMESTAMP_NULL;
usec_t m, last = 0;
@ -764,7 +758,7 @@ int list_timers(int argc, char *argv[], void *userdata) {
output_timers_list(timer_infos, c);
cleanup:
for (t = timer_infos; t < timer_infos + c; t++)
for (struct timer_info *t = timer_infos; t < timer_infos + c; t++)
strv_free(t->triggered);
return r;

View File

@ -1103,7 +1103,7 @@ static int print_property(const char *name, const char *expected_value, sd_bus_m
} else if (endswith(name, "ExitStatus") && streq(contents, "aiai")) {
const int32_t *status, *signal;
size_t n_status, n_signal, i;
size_t n_status, n_signal;
r = sd_bus_message_enter_container(m, 'r', "aiai");
if (r < 0)
@ -1132,7 +1132,7 @@ static int print_property(const char *name, const char *expected_value, sd_bus_m
fputc('=', stdout);
}
for (i = 0; i < n_status; i++) {
for (size_t i = 0; i < n_status; i++) {
if (first)
first = false;
else
@ -1141,7 +1141,7 @@ static int print_property(const char *name, const char *expected_value, sd_bus_m
printf("%"PRIi32, status[i]);
}
for (i = 0; i < n_signal; i++) {
for (size_t i = 0; i < n_signal; i++) {
const char *str;
str = signal_to_string((int) signal[i]);
@ -1933,7 +1933,6 @@ static int show_all(
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
_cleanup_free_ UnitInfo *unit_infos = NULL;
const UnitInfo *u;
unsigned c;
int r, ret = 0;
@ -1947,7 +1946,7 @@ static int show_all(
typesafe_qsort(unit_infos, c, unit_info_compare);
for (u = unit_infos; u < unit_infos + c; u++) {
for (const UnitInfo *u = unit_infos; u < unit_infos + c; u++) {
_cleanup_free_ char *p = NULL;
p = unit_dbus_path_from_name(u->id);

View File

@ -36,9 +36,7 @@ static const struct {
};
static const char *verb_to_method(const char *verb) {
size_t i;
for (i = 0; i < ELEMENTSOF(unit_actions); i++)
for (size_t i = 0; i < ELEMENTSOF(unit_actions); i++)
if (streq_ptr(unit_actions[i].verb, verb))
return unit_actions[i].method;
@ -46,9 +44,7 @@ static const char *verb_to_method(const char *verb) {
}
static const char *verb_to_job_type(const char *verb) {
size_t i;
for (i = 0; i < ELEMENTSOF(unit_actions); i++)
for (size_t i = 0; i < ELEMENTSOF(unit_actions); i++)
if (streq_ptr(unit_actions[i].verb, verb))
return unit_actions[i].job_type;
@ -180,6 +176,43 @@ fail:
return r;
}
static int enqueue_marked_jobs(
sd_bus *bus,
BusWaitForJobs *w) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
int r;
log_debug("%s dbus call org.freedesktop.systemd1.Manager EnqueueMarkedJobs()",
arg_dry_run ? "Would execute" : "Executing");
if (arg_dry_run)
return 0;
r = bus_call_method(bus, bus_systemd_mgr, "EnqueueMarkedJobs", &error, &reply, NULL);
if (r < 0)
return log_error_errno(r, "Failed to start jobs: %s", bus_error_message(&error, r));
_cleanup_strv_free_ char **paths = NULL;
r = sd_bus_message_read_strv(reply, &paths);
if (r < 0)
return bus_log_parse_error(r);
if (w) {
char **path;
STRV_FOREACH(path, paths) {
log_debug("Adding %s to the set", *path);
r = bus_wait_for_jobs_add(w, *path);
if (r < 0)
return log_error_errno(r, "Failed to watch job %s: %m", *path);
}
}
return 0;
}
const struct action_metadata action_table[_ACTION_MAX] = {
[ACTION_HALT] = { SPECIAL_HALT_TARGET, "halt", "replace-irreversibly" },
[ACTION_POWEROFF] = { SPECIAL_POWEROFF_TARGET, "poweroff", "replace-irreversibly" },
@ -200,9 +233,7 @@ const struct action_metadata action_table[_ACTION_MAX] = {
};
enum action verb_to_action(const char *verb) {
enum action i;
for (i = 0; i < _ACTION_MAX; i++)
for (enum action i = 0; i < _ACTION_MAX; i++)
if (streq_ptr(action_table[i].verb, verb))
return i;
@ -271,7 +302,7 @@ int start_unit(int argc, char *argv[], void *userdata) {
job_type = "start";
mode = "isolate";
suffix = ".target";
} else {
} else if (!arg_marked) {
/* A command in style of "systemctl start <unit1> <unit2> …", "sysemctl stop <unit1> <unit2> …" and so on */
method = verb_to_method(argv[0]);
job_type = verb_to_job_type(argv[0]);
@ -295,7 +326,7 @@ int start_unit(int argc, char *argv[], void *userdata) {
names = strv_new(one_name);
if (!names)
return log_oom();
} else {
} else if (!arg_marked) {
bool expanded;
r = expand_unit_names(bus, strv_skip(argv, 1), suffix, &names, &expanded);
@ -328,19 +359,23 @@ int start_unit(int argc, char *argv[], void *userdata) {
return log_error_errno(r, "Failed to allocate unit watch context: %m");
}
STRV_FOREACH(name, names) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
if (arg_marked)
ret = enqueue_marked_jobs(bus, w);
r = start_unit_one(bus, method, job_type, *name, mode, &error, w, wu);
if (ret == EXIT_SUCCESS && r < 0)
ret = translate_bus_error_to_exit_status(r, &error);
else
STRV_FOREACH(name, names) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
if (r >= 0 && streq(method, "StopUnit")) {
r = strv_push(&stopped_units, *name);
if (r < 0)
return log_oom();
r = start_unit_one(bus, method, job_type, *name, mode, &error, w, wu);
if (ret == EXIT_SUCCESS && r < 0)
ret = translate_bus_error_to_exit_status(r, &error);
if (r >= 0 && streq(method, "StopUnit")) {
r = strv_push(&stopped_units, *name);
if (r < 0)
return log_oom();
}
}
}
if (!arg_no_block) {
const char* extra_args[4];

View File

@ -62,9 +62,7 @@ int acquire_bus(BusFocus focus, sd_bus **ret) {
}
void release_busses(void) {
BusFocus w;
for (w = 0; w < _BUS_FOCUS_MAX; w++)
for (BusFocus w = 0; w < _BUS_FOCUS_MAX; w++)
buses[w] = sd_bus_flush_close_unref(buses[w]);
}
@ -237,7 +235,7 @@ int get_unit_list(
int expand_unit_names(sd_bus *bus, char **names, const char* suffix, char ***ret, bool *ret_expanded) {
_cleanup_strv_free_ char **mangled = NULL, **globs = NULL;
char **name;
int r, i;
int r;
assert(bus);
assert(ret);
@ -272,7 +270,7 @@ int expand_unit_names(sd_bus *bus, char **names, const char* suffix, char ***ret
n = strv_length(mangled);
allocated = n + 1;
for (i = 0; i < r; i++) {
for (int i = 0; i < r; i++) {
if (!GREEDY_REALLOC(mangled, allocated, n+2))
return log_oom();

View File

@ -109,6 +109,7 @@ char **arg_clean_what = NULL;
TimestampStyle arg_timestamp_style = TIMESTAMP_PRETTY;
bool arg_read_only = false;
bool arg_mkdir = false;
bool arg_marked = false;
STATIC_DESTRUCTOR_REGISTER(arg_wall, strv_freep);
STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
@ -296,6 +297,7 @@ static int systemctl_help(void) {
" 'us+utc': 'Day YYYY-MM-DD HH:MM:SS.UUUUUU UTC\n"
" --read-only Create read-only bind mount\n"
" --mkdir Create directory before mounting, if missing\n"
" --marked Restart/reload previously marked units\n"
"\nSee the %2$s for details.\n",
program_invocation_short_name,
link,
@ -414,6 +416,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
ARG_TIMESTAMP_STYLE,
ARG_READ_ONLY,
ARG_MKDIR,
ARG_MARKED,
};
static const struct option options[] = {
@ -472,6 +475,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
{ "timestamp", required_argument, NULL, ARG_TIMESTAMP_STYLE },
{ "read-only", no_argument, NULL, ARG_READ_ONLY },
{ "mkdir", no_argument, NULL, ARG_MKDIR },
{ "marked", no_argument, NULL, ARG_MARKED },
{}
};
@ -882,6 +886,10 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
arg_mkdir = true;
break;
case ARG_MARKED:
arg_marked = true;
break;
case '.':
/* Output an error mimicking getopt, and print a hint afterwards */
log_error("%s: invalid option -- '.'", program_invocation_name);
@ -905,6 +913,27 @@ static int systemctl_parse_argv(int argc, char *argv[]) {
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"--wait may not be combined with --no-block.");
bool do_reload_or_restart = streq_ptr(argv[optind], "reload-or-restart");
if (arg_marked) {
if (!do_reload_or_restart)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"--marked may only be used with 'reload-or-restart'.");
if (optind + 1 < argc)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"No additional arguments allowed with 'reload-or-restart --marked'.");
if (arg_wait)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"--marked --wait is not supported.");
if (arg_show_transaction)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"--marked --show-transaction is not supported.");
} else if (do_reload_or_restart) {
if (optind + 1 >= argc)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"List of units to restart/reload is required.");
}
return 1;
}
@ -982,7 +1011,7 @@ static int systemctl_main(int argc, char *argv[]) {
{ "reload", 2, VERB_ANY, VERB_ONLINE_ONLY, start_unit },
{ "restart", 2, VERB_ANY, VERB_ONLINE_ONLY, start_unit },
{ "try-restart", 2, VERB_ANY, VERB_ONLINE_ONLY, start_unit },
{ "reload-or-restart", 2, VERB_ANY, VERB_ONLINE_ONLY, start_unit },
{ "reload-or-restart", VERB_ANY, VERB_ANY, VERB_ONLINE_ONLY, start_unit },
{ "reload-or-try-restart", 2, VERB_ANY, VERB_ONLINE_ONLY, start_unit }, /* For compatibility with old systemctl <= 228 */
{ "try-reload-or-restart", 2, VERB_ANY, VERB_ONLINE_ONLY, start_unit },
{ "force-reload", 2, VERB_ANY, VERB_ONLINE_ONLY, start_unit }, /* For compatibility with SysV */

View File

@ -93,5 +93,6 @@ extern char **arg_clean_what;
extern TimestampStyle arg_timestamp_style;
extern bool arg_read_only;
extern bool arg_mkdir;
extern bool arg_marked;
int systemctl_dispatch_parse_argv(int argc, char *argv[]);

View File

@ -12,7 +12,7 @@
#include "rm-rf.h"
#include "service.h"
#include "tests.h"
#include "unit.h"
#include "unit-serialize.h"
#include "virt.h"
int main(int argc, char *argv[]) {

View File

@ -6,7 +6,7 @@
static void test_udev_builtin_cmd_to_ptr(void) {
log_info("/* %s */", __func__);
/* Those could have been static_assert()s, but ({}) is not allowed there. */
/* Those could have been static asserts, but ({}) is not allowed there. */
#if HAVE_BLKID
assert_se(UDEV_BUILTIN_CMD_TO_PTR(UDEV_BUILTIN_BLKID));
assert_se(PTR_TO_UDEV_BUILTIN_CMD(UDEV_BUILTIN_CMD_TO_PTR(UDEV_BUILTIN_BLKID)) == UDEV_BUILTIN_BLKID);