return manager_load_unit(m, name, NULL, error, ret_unit);
}
-static int method_get_unit(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int reply_unit_path(Unit *u, sd_bus_message *message, sd_bus_error *error) {
_cleanup_free_ char *path = NULL;
+ int r;
+
+ assert(u);
+ assert(message);
+
+ r = mac_selinux_unit_access_check(u, message, "status", error);
+ if (r < 0)
+ return r;
+
+ path = unit_dbus_path(u);
+ if (!path)
+ return log_oom();
+
+ return sd_bus_reply_method_return(message, "o", path);
+}
+
+static int method_get_unit(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Manager *m = userdata;
const char *name;
Unit *u;
if (r < 0)
return r;
- r = mac_selinux_unit_access_check(u, message, "status", error);
- if (r < 0)
- return r;
-
- path = unit_dbus_path(u);
- if (!path)
- return -ENOMEM;
-
- return sd_bus_reply_method_return(message, "o", path);
+ return reply_unit_path(u, message, error);
}
static int method_get_unit_by_pid(sd_bus_message *message, void *userdata, sd_bus_error *error) {
- _cleanup_free_ char *path = NULL;
Manager *m = userdata;
pid_t pid;
Unit *u;
if (!u)
return sd_bus_error_setf(error, BUS_ERROR_NO_UNIT_FOR_PID, "PID "PID_FMT" does not belong to any loaded unit.", pid);
- r = mac_selinux_unit_access_check(u, message, "status", error);
- if (r < 0)
- return r;
-
- path = unit_dbus_path(u);
- if (!path)
- return -ENOMEM;
-
- return sd_bus_reply_method_return(message, "o", path);
+ return reply_unit_path(u, message, error);
}
static int method_get_unit_by_invocation_id(sd_bus_message *message, void *userdata, sd_bus_error *error) {
}
static int method_get_unit_by_control_group(sd_bus_message *message, void *userdata, sd_bus_error *error) {
- _cleanup_free_ char *path = NULL;
Manager *m = userdata;
const char *cgroup;
Unit *u;
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);
- r = mac_selinux_unit_access_check(u, message, "status", error);
- if (r < 0)
- return r;
-
- path = unit_dbus_path(u);
- if (!path)
- return -ENOMEM;
-
- return sd_bus_reply_method_return(message, "o", path);
+ return reply_unit_path(u, message, error);
}
static int method_load_unit(sd_bus_message *message, void *userdata, sd_bus_error *error) {
- _cleanup_free_ char *path = NULL;
Manager *m = userdata;
const char *name;
Unit *u;
if (r < 0)
return r;
- r = mac_selinux_unit_access_check(u, message, "status", error);
- if (r < 0)
- return r;
-
- path = unit_dbus_path(u);
- if (!path)
- return -ENOMEM;
-
- return sd_bus_reply_method_return(message, "o", path);
+ return reply_unit_path(u, message, error);
}
static int method_start_unit_generic(sd_bus_message *message, Manager *m, JobType job_type, bool reload_if_possible, sd_bus_error *error) {
return method_start_unit_generic(message, userdata, JOB_TRY_RESTART, true, error);
}
-static int method_start_unit_replace(sd_bus_message *message, void *userdata, sd_bus_error *error) {
- Manager *m = userdata;
- const char *old_name;
- Unit *u;
- int r;
-
- assert(message);
- assert(m);
-
- r = sd_bus_message_read(message, "s", &old_name);
- if (r < 0)
- return r;
-
- r = bus_get_unit_by_name(m, message, old_name, &u, error);
- if (r < 0)
- return r;
- if (!u->job || u->job->type != JOB_START)
- return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_JOB, "No job queued for unit %s", old_name);
+typedef enum GenericUnitOperationFlags {
+ GENERIC_UNIT_LOAD = 1 << 0, /* Load if the unit is not loaded yet */
+ GENERIC_UNIT_VALIDATE_LOADED = 1 << 1, /* Verify unit is properly loaded before forwarding call */
+} GenericUnitOperationFlags;
- return method_start_unit_generic(message, m, JOB_START, false, error);
-}
+static int method_generic_unit_operation(
+ sd_bus_message *message,
+ Manager *m,
+ sd_bus_error *error,
+ sd_bus_message_handler_t handler,
+ GenericUnitOperationFlags flags) {
-static int method_kill_unit(sd_bus_message *message, void *userdata, sd_bus_error *error) {
- Manager *m = userdata;
const char *name;
Unit *u;
int r;
assert(message);
assert(m);
+ /* Read the first argument from the command and pass the operation to the specified per-unit
+ * method. */
+
r = sd_bus_message_read(message, "s", &name);
if (r < 0)
return r;
- r = bus_get_unit_by_name(m, message, name, &u, error);
+ if (!isempty(name) && FLAGS_SET(flags, GENERIC_UNIT_LOAD))
+ r = manager_load_unit(m, name, NULL, error, &u);
+ else
+ r = bus_get_unit_by_name(m, message, name, &u, error);
if (r < 0)
return r;
- return bus_unit_method_kill(message, u, error);
-}
-
-static int method_reset_failed_unit(sd_bus_message *message, void *userdata, sd_bus_error *error) {
- Manager *m = userdata;
- const char *name;
- Unit *u;
- int r;
-
- assert(message);
- assert(m);
-
- r = sd_bus_message_read(message, "s", &name);
- if (r < 0)
- return r;
+ if (FLAGS_SET(flags, GENERIC_UNIT_VALIDATE_LOADED)) {
+ r = bus_unit_validate_load_state(u, error);
+ if (r < 0)
+ return r;
+ }
- r = bus_get_unit_by_name(m, message, name, &u, error);
- if (r < 0)
- return r;
+ return handler(message, u, error);
+}
- return bus_unit_method_reset_failed(message, u, error);
+static int method_enqueue_unit_job(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ /* We don't bother with GENERIC_UNIT_VALIDATE_LOADED here, as the job logic validates that anyway */
+ return method_generic_unit_operation(message, userdata, error, bus_unit_method_enqueue_job, GENERIC_UNIT_LOAD);
}
-static int method_set_unit_properties(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+static int method_start_unit_replace(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Manager *m = userdata;
- const char *name;
+ const char *old_name;
Unit *u;
int r;
assert(message);
assert(m);
- r = sd_bus_message_read(message, "s", &name);
- if (r < 0)
- return r;
-
- r = bus_load_unit_by_name(m, message, name, &u, error);
+ r = sd_bus_message_read(message, "s", &old_name);
if (r < 0)
return r;
- r = bus_unit_validate_load_state(u, error);
+ r = bus_get_unit_by_name(m, message, old_name, &u, error);
if (r < 0)
return r;
+ if (!u->job || u->job->type != JOB_START)
+ return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_JOB, "No job queued for unit %s", old_name);
- return bus_unit_method_set_properties(message, u, error);
+ return method_start_unit_generic(message, m, JOB_START, false, error);
}
-static int method_ref_unit(sd_bus_message *message, void *userdata, sd_bus_error *error) {
- Manager *m = userdata;
- const char *name;
- Unit *u;
- int r;
-
- assert(message);
- assert(m);
-
- r = sd_bus_message_read(message, "s", &name);
- if (r < 0)
- return r;
+static int method_kill_unit(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ /* We don't bother with GENERIC_UNIT_LOAD nor GENERIC_UNIT_VALIDATE_LOADED here, as it shouldn't
+ * matter whether a unit is loaded for killing any processes possibly in the unit's cgroup. */
+ return method_generic_unit_operation(message, userdata, error, bus_unit_method_kill, 0);
+}
- r = bus_load_unit_by_name(m, message, name, &u, error);
- if (r < 0)
- return r;
+static int method_reset_failed_unit(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ /* Don't load the unit (because unloaded units can't be in failed state), and don't insist on the
+ * unit to be loaded properly (since a failed unit might have its unit file disappeared) */
+ return method_generic_unit_operation(message, userdata, error, bus_unit_method_reset_failed, 0);
+}
- r = bus_unit_validate_load_state(u, error);
- if (r < 0)
- return r;
+static int method_set_unit_properties(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ /* Only change properties on fully loaded units, and load them in order to set properties */
+ return method_generic_unit_operation(message, userdata, error, bus_unit_method_set_properties, GENERIC_UNIT_LOAD|GENERIC_UNIT_VALIDATE_LOADED);
+}
- return bus_unit_method_ref(message, u, error);
+static int method_ref_unit(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ /* Only allow reffing of fully loaded units, and make sure reffing a unit loads it. */
+ return method_generic_unit_operation(message, userdata, error, bus_unit_method_ref, GENERIC_UNIT_LOAD|GENERIC_UNIT_VALIDATE_LOADED);
}
static int method_unref_unit(sd_bus_message *message, void *userdata, sd_bus_error *error) {
- Manager *m = userdata;
- const char *name;
- Unit *u;
- int r;
-
- assert(message);
- assert(m);
-
- r = sd_bus_message_read(message, "s", &name);
- if (r < 0)
- return r;
-
- r = bus_load_unit_by_name(m, message, name, &u, error);
- if (r < 0)
- return r;
-
- r = bus_unit_validate_load_state(u, error);
- if (r < 0)
- return r;
-
- return bus_unit_method_unref(message, u, error);
+ /* Dropping a ref OTOH should not require the unit to still be loaded. And since a reffed unit is a
+ * loaded unit there's no need to load the unit for unreffing it. */
+ return method_generic_unit_operation(message, userdata, error, bus_unit_method_unref, 0);
}
static int reply_unit_info(sd_bus_message *reply, Unit *u) {
}
static int method_get_unit_processes(sd_bus_message *message, void *userdata, sd_bus_error *error) {
- Manager *m = userdata;
- const char *name;
- Unit *u;
- int r;
-
- assert(message);
- assert(m);
-
- r = sd_bus_message_read(message, "s", &name);
- if (r < 0)
- return r;
-
- r = bus_get_unit_by_name(m, message, name, &u, error);
- if (r < 0)
- return r;
-
- return bus_unit_method_get_processes(message, u, error);
+ /* Don't load a unit (since it won't have any processes if it's not loaded), but don't insist on the
+ * unit being loaded (because even improperly loaded units might still have processes around */
+ return method_generic_unit_operation(message, userdata, error, bus_unit_method_get_processes, 0);
}
static int method_attach_processes_to_unit(sd_bus_message *message, void *userdata, sd_bus_error *error) {
- Manager *m = userdata;
- const char *name;
- Unit *u;
- int r;
-
- assert(message);
- assert(m);
-
- r = sd_bus_message_read(message, "s", &name);
- if (r < 0)
- return r;
-
- r = bus_get_unit_by_name(m, message, name, &u, error);
- if (r < 0)
- return r;
-
- return bus_unit_method_attach_processes(message, u, error);
+ /* Don't allow attaching new processes to units that aren't loaded. Don't bother with loading a unit
+ * for this purpose though, as an unloaded unit is a stopped unit, and we don't allow attaching
+ * processes to stopped units anyway. */
+ return method_generic_unit_operation(message, userdata, error, bus_unit_method_attach_processes, GENERIC_UNIT_VALIDATE_LOADED);
}
static int transient_unit_from_message(
return r;
/* Finally, start it */
- return bus_unit_queue_job(message, u, JOB_START, mode, false, error);
+ return bus_unit_queue_job(message, u, JOB_START, mode, 0, error);
}
static int method_get_job(sd_bus_message *message, void *userdata, sd_bus_error *error) {
}
static int method_switch_root(sd_bus_message *message, void *userdata, sd_bus_error *error) {
- char *ri = NULL, *rt = NULL;
+ _cleanup_free_ char *ri = NULL, *rt = NULL;
const char *root, *init;
Manager *m = userdata;
struct statvfs svfs;
if (!isempty(init)) {
ri = strdup(init);
- if (!ri) {
- free(rt);
+ if (!ri)
return -ENOMEM;
- }
}
- free(m->switch_root);
- m->switch_root = rt;
-
- free(m->switch_root_init);
- m->switch_root_init = ri;
+ free_and_replace(m->switch_root, rt);
+ free_and_replace(m->switch_root_init, ri);
m->objective = MANAGER_SWITCH_ROOT;
SD_BUS_METHOD("TryRestartUnit", "ss", "o", method_try_restart_unit, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("ReloadOrRestartUnit", "ss", "o", method_reload_or_restart_unit, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("ReloadOrTryRestartUnit", "ss", "o", method_reload_or_try_restart_unit, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("EnqueueUnitJob", "sss", "uososa(uosos)", method_enqueue_unit_job, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("KillUnit", "ssi", NULL, method_kill_unit, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("ResetFailedUnit", "s", NULL, method_reset_failed_unit, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("SetUnitProperties", "sba(sv)", NULL, method_set_unit_properties, SD_BUS_VTABLE_UNPRIVILEGED),