diff options
41 files changed, 1586 insertions, 1121 deletions
diff --git a/man/org.freedesktop.systemd1.xml b/man/org.freedesktop.systemd1.xml index 1d419ac495..2da0ff0579 100644 --- a/man/org.freedesktop.systemd1.xml +++ b/man/org.freedesktop.systemd1.xml @@ -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> diff --git a/man/sd_bus_message_read_strv.xml b/man/sd_bus_message_read_strv.xml index a90ae84098..50580d86bc 100644 --- a/man/sd_bus_message_read_strv.xml +++ b/man/sd_bus_message_read_strv.xml @@ -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> diff --git a/man/systemctl.xml b/man/systemctl.xml index 2ed58eb33b..be414ebb1e 100644 --- a/man/systemctl.xml +++ b/man/systemctl.xml @@ -2305,6 +2305,18 @@ Jan 12 10:46:45 example.com bluetoothd[8900]: gatt-time-server: Input/output err </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> <listitem><para>When used with <command>bind</command>, creates a read-only bind mount.</para></listitem> diff --git a/meson.build b/meson.build index 09d8122091..226dc90f9e 100644 --- a/meson.build +++ b/meson.build @@ -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') diff --git a/src/analyze/analyze-verify.c b/src/analyze/analyze-verify.c index bf28624d41..1b06e07019 100644 --- a/src/analyze/analyze-verify.c +++ b/src/analyze/analyze-verify.c @@ -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; diff --git a/src/analyze/analyze.c b/src/analyze/analyze.c index c25d11e0de..72ebd25525 100644 --- a/src/analyze/analyze.c +++ b/src/analyze/analyze.c @@ -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)); diff --git a/src/basic/unit-def.c b/src/basic/unit-def.c index bb4fa1ec6b..6fbb947f09 100644 --- a/src/basic/unit-def.c +++ b/src/basic/unit-def.c @@ -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", diff --git a/src/basic/unit-def.h b/src/basic/unit-def.h index ff05799c1d..7742c5c078 100644 --- a/src/basic/unit-def.h +++ b/src/basic/unit-def.h @@ -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_; diff --git a/src/busctl/busctl.c b/src/busctl/busctl.c index 6a492ebd02..142aba0b92 100644 --- a/src/busctl/busctl.c +++ b/src/busctl/busctl.c @@ -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)); diff --git a/src/core/dbus-manager.c b/src/core/dbus-manager.c index 9053d48149..86d0b3744d 100644 --- a/src/core/dbus-manager.c +++ b/src/core/dbus-manager.c @@ -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; diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c index 8d92a4e763..859e35eee4 100644 --- a/src/core/dbus-unit.c +++ b/src/core/dbus-unit.c @@ -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,50 +1716,20 @@ 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( +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_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; - _cleanup_free_ char *job_path = NULL, *unit_path = NULL; _cleanup_set_free_ Set *affected = NULL; + _cleanup_free_ char *job_path = NULL, *unit_path = NULL; Job *j, *a; int r; - assert(message); - assert(u); - assert(type >= 0 && type < _JOB_TYPE_MAX); - assert(mode >= 0 && mode < _JOB_MODE_MAX); - - r = mac_selinux_unit_access_check( - u, message, - job_type_to_access_method(type), - error); - if (r < 0) - return r; - - if (FLAGS_SET(flags, BUS_UNIT_QUEUE_RELOAD_IF_POSSIBLE) && unit_can_reload(u)) { - if (type == JOB_RESTART) - type = JOB_RELOAD_OR_START; - else if (type == JOB_TRY_RESTART) - type = JOB_TRY_RELOAD; - } - - if (type == JOB_STOP && - IN_SET(u->load_state, UNIT_NOT_FOUND, UNIT_ERROR, UNIT_BAD_SETTING) && - unit_active_state(u) == UNIT_INACTIVE) - return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s not loaded.", u->id); - - if ((type == JOB_START && u->refuse_manual_start) || - (type == JOB_STOP && u->refuse_manual_stop) || - (IN_SET(type, JOB_RESTART, JOB_TRY_RESTART) && (u->refuse_manual_start || u->refuse_manual_stop)) || - (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) @@ -1750,12 +1753,9 @@ int bus_unit_queue_job( /* 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); + return sd_bus_message_append(reply, "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) @@ -1773,7 +1773,6 @@ int bus_unit_queue_job( return r; SET_FOREACH(a, affected) { - if (a->id == j->id) continue; @@ -1797,7 +1796,55 @@ int bus_unit_queue_job( return r; } - r = sd_bus_message_close_container(reply); + return sd_bus_message_close_container(reply); +} + +int bus_unit_queue_job( + sd_bus_message *message, + Unit *u, + JobType type, + JobMode mode, + BusUnitQueueFlags flags, + sd_bus_error *error) { + + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + int r; + + assert(message); + assert(u); + assert(type >= 0 && type < _JOB_TYPE_MAX); + assert(mode >= 0 && mode < _JOB_MODE_MAX); + + r = mac_selinux_unit_access_check( + u, message, + job_type_to_access_method(type), + error); + if (r < 0) + return r; + + if (FLAGS_SET(flags, BUS_UNIT_QUEUE_RELOAD_IF_POSSIBLE) && unit_can_reload(u)) { + if (type == JOB_RESTART) + type = JOB_RELOAD_OR_START; + else if (type == JOB_TRY_RESTART) + type = JOB_TRY_RELOAD; + } + + if (type == JOB_STOP && + IN_SET(u->load_state, UNIT_NOT_FOUND, UNIT_ERROR, UNIT_BAD_SETTING) && + unit_active_state(u) == UNIT_INACTIVE) + return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_UNIT, "Unit %s not loaded.", u->id); + + if ((type == JOB_START && u->refuse_manual_start) || + (type == JOB_STOP && u->refuse_manual_stop) || + (IN_SET(type, JOB_RESTART, JOB_TRY_RESTART) && (u->refuse_manual_start || u->refuse_manual_stop)) || + (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); + + r = sd_bus_message_new_method_return(message, &reply); + if (r < 0) + return r; + + 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; diff --git a/src/core/dbus-unit.h b/src/core/dbus-unit.h index 1da3cfeb96..a3ac320496 100644 --- a/src/core/dbus-unit.h +++ b/src/core/dbus-unit.h @@ -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); diff --git a/src/core/execute.c b/src/core/execute.c index 2c35a917f8..d14aec47d0 100644 --- a/src/core/execute.c +++ b/src/core/execute.c @@ -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" diff --git a/src/core/fuzz-unit-file.c b/src/core/fuzz-unit-file.c index e67f6e9199..311ffcecc1 100644 --- a/src/core/fuzz-unit-file.c +++ b/src/core/fuzz-unit-file.c @@ -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) { diff --git a/src/core/manager.c b/src/core/manager.c index 0a7a451835..82ca4c2482 100644 --- a/src/core/manager.c +++ b/src/core/manager.c @@ -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) diff --git a/src/core/meson.build b/src/core/meson.build index edfc73e627..a389c906b3 100644 --- a/src/core/meson.build +++ b/src/core/meson.build @@ -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] diff --git a/src/core/unit-serialize.c b/src/core/unit-serialize.c new file mode 100644 index 0000000000..3f099248ce --- /dev/null +++ b/src/core/unit-serialize.c @@ -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); +} diff --git a/src/core/unit-serialize.h b/src/core/unit-serialize.h new file mode 100644 index 0000000000..599d883dec --- /dev/null +++ b/src/core/unit-serialize.h @@ -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); diff --git a/src/core/unit.c b/src/core/unit.c index e6d55db88b..3452712590 100644 --- a/src/core/unit.c +++ b/src/core/unit.c @@ -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; diff --git a/src/core/unit.h b/src/core/unit.h index 1b3b146369..7c13e50878 100644 --- a/src/core/unit.h +++ b/src/core/unit.h @@ -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); diff --git a/src/libsystemd/sd-bus/bus-message.c b/src/libsystemd/sd-bus/bus-message.c index f358a8699e..894d681260 100644 --- a/src/libsystemd/sd-bus/bus-message.c +++ b/src/libsystemd/sd-bus/bus-message.c @@ -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; diff --git a/src/libsystemd/sd-bus/test-bus-objects.c b/src/libsystemd/sd-bus/test-bus-objects.c index 80d2a5d98b..5ef3f30c70 100644 --- a/src/libsystemd/sd-bus/test-bus-objects.c +++ b/src/libsystemd/sd-bus/test-bus-objects.c @@ -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); diff --git a/src/core/macros.systemd.in b/src/rpm/macros.systemd.in index 1c40328db0..24996de10a 100644 --- a/src/core/macros.systemd.in +++ b/src/rpm/macros.systemd.in @@ -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} diff --git a/src/rpm/meson.build b/src/rpm/meson.build new file mode 100644 index 0000000000..d299b18019 --- /dev/null +++ b/src/rpm/meson.build @@ -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) diff --git a/src/core/triggers.systemd.in b/src/rpm/triggers.systemd.in index 2d25db3696..3a89f9169d 100644 --- a/src/core/triggers.systemd.in +++ b/src/rpm/triggers.systemd.in @@ -6,14 +6,13 @@ # 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 @@ -21,33 +20,22 @@ if posix.access("/run/systemd/system") then 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 -%transfiletriggerun -p <lua> -- @systemunitdir@ /etc/systemd/system +%transfiletriggerpostun -P 1000100 -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. +-- 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. %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. - +-- executed. 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")) @@ -56,33 +44,31 @@ if posix.access("%{_localstatedir}/lib/rpm-state/systemd/needs-reload") then end end -%transfiletriggerin -P 100700 -p <lua> -- @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. +%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 pid = posix.fork() if pid == 0 then - assert(posix.exec("%{_bindir}/systemd-sysusers")) + assert(posix.exec("%{_bindir}/systemctl", "reload-or-restart", "--marked")) elseif pid > 0 then posix.wait(pid) end end -%transfiletriggerin -P 100500 -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. +%transfiletriggerin -P 100700 -p <lua> -- @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 posix.access("/run/systemd/system") then pid = posix.fork() if pid == 0 then - assert(posix.exec("%{_bindir}/systemd-tmpfiles", "--create")) + assert(posix.exec("%{_bindir}/systemd-sysusers")) elseif pid > 0 then posix.wait(pid) end end -%transfiletriggerin -p <lua> -- @udevhwdbdir@ +%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 @@ -94,7 +80,7 @@ if posix.access("/run/systemd/system") then end end -%transfiletriggerin -p <lua> -- @catalogdir@ +%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 @@ -106,37 +92,50 @@ if posix.access("/run/systemd/system") then end end -%transfiletriggerin -p <lua> -- @udevrulesdir@ --- This script will automatically update udev with new rules if files --- have been installed or updated in @udevrulesdir@. +%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("%{_bindir}/udevadm", "control", "--reload")) + assert(posix.exec("@rootlibexecdir@/systemd-binfmt")) elseif pid > 0 then posix.wait(pid) end end -%transfiletriggerin -p <lua> -- @sysctldir@ --- This script will automatically apply sysctl rules if files have been --- installed or updated in @sysctldir@. +%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. if posix.access("/run/systemd/system") then pid = posix.fork() if pid == 0 then - assert(posix.exec("@rootlibexecdir@/systemd-sysctl")) + assert(posix.exec("%{_bindir}/systemd-tmpfiles", "--create")) elseif pid > 0 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@. +%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 pid = posix.fork() if pid == 0 then - assert(posix.exec("@rootlibexecdir@/systemd-binfmt")) + assert(posix.exec("%{_bindir}/udevadm", "control", "--reload")) + elseif pid > 0 then + posix.wait(pid) + end +end + +%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 + pid = posix.fork() + if pid == 0 then + assert(posix.exec("@rootlibexecdir@/systemd-sysctl")) elseif pid > 0 then posix.wait(pid) end diff --git a/src/rpm/triggers.systemd.sh.in b/src/rpm/triggers.systemd.sh.in new file mode 100644 index 0000000000..0080040de4 --- /dev/null +++ b/src/rpm/triggers.systemd.sh.in @@ -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 diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c index 5bbaa07dd1..2ce507aa1c 100644 --- a/src/shared/bus-unit-util.c +++ b/src/shared/bus-unit-util.c @@ -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); diff --git a/src/systemctl/systemctl-is-active.c b/src/systemctl/systemctl-is-active.c index 3d99b0deb6..d83736e94a 100644 --- a/src/systemctl/systemctl-is-active.c +++ b/src/systemctl/systemctl-is-active.c @@ -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; } diff --git a/src/systemctl/systemctl-is-enabled.c b/src/systemctl/systemctl-is-enabled.c index babd5902c9..de1d016bb3 100644 --- a/src/systemctl/systemctl-is-enabled.c +++ b/src/systemctl/systemctl-is-enabled.c @@ -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); diff --git a/src/systemctl/systemctl-list-dependencies.c b/src/systemctl/systemctl-list-dependencies.c index fbb81d90fa..5d19f338d8 100644 --- a/src/systemctl/systemctl-list-dependencies.c +++ b/src/systemctl/systemctl-list-dependencies.c @@ -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 ? "" : " "); diff --git a/src/systemctl/systemctl-list-jobs.c b/src/systemctl/systemctl-list-jobs.c index 8b028c013a..27eecb548c 100644 --- a/src/systemctl/systemctl-list-jobs.c +++ b/src/systemctl/systemctl-list-jobs.c @@ -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 diff --git a/src/systemctl/systemctl-list-machines.c b/src/systemctl/systemctl-list-machines.c index 48d0e8bde4..63adcec5f3 100644 --- a/src/systemctl/systemctl-list-machines.c +++ b/src/systemctl/systemctl-list-machines.c @@ -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; diff --git a/src/systemctl/systemctl-list-unit-files.c b/src/systemctl/systemctl-list-unit-files.c index e1bf876620..6da2914eff 100644 --- a/src/systemctl/systemctl-list-unit-files.c +++ b/src/systemctl/systemctl-list-unit-files.c @@ -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) diff --git a/src/systemctl/systemctl-list-units.c b/src/systemctl/systemctl-list-units.c index 7f0e79eedd..a413ef6d5b 100644 --- a/src/systemctl/systemctl-list-units.c +++ b/src/systemctl/systemctl-list-units.c @@ -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; - - if (t->machine) { - j = strjoin(t->machine, ":", t->id); - if (!j) - return log_oom(); - unit = j; - } else - unit = t->id; + for (struct timer_info *t = timer_infos; t < timer_infos + n; t++) { + _cleanup_free_ char *j = NULL, *activates = NULL; + const char *unit; - 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; diff --git a/src/systemctl/systemctl-show.c b/src/systemctl/systemctl-show.c index 9b04b698be..57e708095d 100644 --- a/src/systemctl/systemctl-show.c +++ b/src/systemctl/systemctl-show.c @@ -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); diff --git a/src/systemctl/systemctl-start-unit.c b/src/systemctl/systemctl-start-unit.c index b398e77eb2..c40e807212 100644 --- a/src/systemctl/systemctl-start-unit.c +++ b/src/systemctl/systemctl-start-unit.c @@ -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]; diff --git a/src/systemctl/systemctl-util.c b/src/systemctl/systemctl-util.c index 89c342a0b1..d1490c906d 100644 --- a/src/systemctl/systemctl-util.c +++ b/src/systemctl/systemctl-util.c @@ -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(); diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index 08a54a469e..b82e541d7a 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -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 */ diff --git a/src/systemctl/systemctl.h b/src/systemctl/systemctl.h index 0ebe4580c6..3c5a4023a9 100644 --- a/src/systemctl/systemctl.h +++ b/src/systemctl/systemctl.h @@ -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[]); diff --git a/src/test/test-bpf-firewall.c b/src/test/test-bpf-firewall.c index b6fd22904f..b29c0d7844 100644 --- a/src/test/test-bpf-firewall.c +++ b/src/test/test-bpf-firewall.c @@ -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[]) { diff --git a/src/udev/test-udev-builtin.c b/src/udev/test-udev-builtin.c index 1bd1dbddf5..21a8ea3fa6 100644 --- a/src/udev/test-udev-builtin.c +++ b/src/udev/test-udev-builtin.c @@ -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); |