]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/core/dbus-unit.c
util-lib: split our string related calls from util.[ch] into its own file string...
[thirdparty/systemd.git] / src / core / dbus-unit.c
index 6d80ea95fa9b9b1f6cf8d2f4f0d5e3c1dcdfd7ab..b2b9eceeb3cf0561e9477ba52f5f3a42a8af6a5e 100644 (file)
 ***/
 
 #include "sd-bus.h"
+
+#include "bus-common-errors.h"
+#include "cgroup-util.h"
+#include "dbus.h"
 #include "log.h"
 #include "selinux-access.h"
-#include "cgroup-util.h"
+#include "special.h"
+#include "string-util.h"
 #include "strv.h"
-#include "bus-common-errors.h"
-#include "dbus.h"
 #include "dbus-unit.h"
 
 static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_load_state, unit_load_state, UnitLoadState);
@@ -390,6 +393,29 @@ static int property_get_load_error(
         return sd_bus_message_append(reply, "(ss)", e.name, e.message);
 }
 
+static int bus_verify_manage_units_async_full(
+                Unit *u,
+                const char *verb,
+                int capability,
+                const char *polkit_message,
+                sd_bus_message *call,
+                sd_bus_error *error) {
+
+        const char *details[9] = {
+                "unit", u->id,
+                "verb", verb,
+        };
+
+        if (polkit_message) {
+                details[4] = "polkit.message";
+                details[5] = polkit_message;
+                details[6] = "polkit.gettext_domain";
+                details[7] = GETTEXT_PACKAGE;
+        }
+
+        return bus_verify_polkit_async(call, capability, "org.freedesktop.systemd1.manage-units", details, false, UID_INVALID, &u->manager->polkit_registry, error);
+}
+
 int bus_unit_method_start_generic(
                 sd_bus_message *message,
                 Unit *u,
@@ -399,6 +425,14 @@ int bus_unit_method_start_generic(
 
         const char *smode;
         JobMode mode;
+        _cleanup_free_ char *verb = NULL;
+        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)'."),
+                [JOB_RELOAD]      = N_("Authentication is required to reload '$(unit)'."),
+                [JOB_RESTART]     = N_("Authentication is required to restart '$(unit)'."),
+                [JOB_TRY_RESTART] = N_("Authentication is required to restart '$(unit)'."),
+        };
         int r;
 
         assert(message);
@@ -417,7 +451,20 @@ int bus_unit_method_start_generic(
         if (mode < 0)
                 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Job mode %s invalid", smode);
 
-        r = bus_verify_manage_units_async(u->manager, message, error);
+        if (reload_if_possible)
+                verb = strjoin("reload-or-", job_type_to_string(job_type), NULL);
+        else
+                verb = strdup(job_type_to_string(job_type));
+        if (!verb)
+                return -ENOMEM;
+
+        r = bus_verify_manage_units_async_full(
+                        u,
+                        verb,
+                        CAP_SYS_ADMIN,
+                        job_type < _JOB_TYPE_MAX ? polkit_message_for_job[job_type] : NULL,
+                        message,
+                        error);
         if (r < 0)
                 return r;
         if (r == 0)
@@ -483,7 +530,13 @@ int bus_unit_method_kill(sd_bus_message *message, void *userdata, sd_bus_error *
         if (signo <= 0 || signo >= _NSIG)
                 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Signal number out of range.");
 
-        r = bus_verify_manage_units_async_for_kill(u->manager, message, error);
+        r = bus_verify_manage_units_async_full(
+                        u,
+                        "kill",
+                        CAP_KILL,
+                        N_("Authentication is required to kill '$(unit)'."),
+                        message,
+                        error);
         if (r < 0)
                 return r;
         if (r == 0)
@@ -507,7 +560,13 @@ int bus_unit_method_reset_failed(sd_bus_message *message, void *userdata, sd_bus
         if (r < 0)
                 return r;
 
-        r = bus_verify_manage_units_async(u->manager, message, error);
+        r = bus_verify_manage_units_async_full(
+                        u,
+                        "reset-failed",
+                        CAP_SYS_ADMIN,
+                        N_("Authentication is required to reset the \"failed\" state of '$(unit)'."),
+                        message,
+                        error);
         if (r < 0)
                 return r;
         if (r == 0)
@@ -533,7 +592,13 @@ int bus_unit_method_set_properties(sd_bus_message *message, void *userdata, sd_b
         if (r < 0)
                 return r;
 
-        r = bus_verify_manage_units_async(u->manager, message, error);
+        r = bus_verify_manage_units_async_full(
+                        u,
+                        "set-property",
+                        CAP_SYS_ADMIN,
+                        N_("Authentication is required to set properties on '$(unit)'."),
+                        message,
+                        error);
         if (r < 0)
                 return r;
         if (r == 0)
@@ -616,6 +681,7 @@ const sd_bus_vtable bus_unit_vtable[] = {
         SD_BUS_PROPERTY("Asserts", "a(sbbsi)", property_get_conditions, offsetof(Unit, asserts), 0),
         SD_BUS_PROPERTY("LoadError", "(ss)", property_get_load_error, 0, SD_BUS_VTABLE_PROPERTY_CONST),
         SD_BUS_PROPERTY("Transient", "b", bus_property_get_bool, offsetof(Unit, transient), SD_BUS_VTABLE_PROPERTY_CONST),
+        SD_BUS_PROPERTY("NetClass", "u", NULL, offsetof(Unit, cgroup_netclass_id), 0),
 
         SD_BUS_METHOD("Start", "s", "o", method_start, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD("Stop", "s", "o", method_stop, SD_BUS_VTABLE_UNPRIVILEGED),
@@ -673,6 +739,30 @@ static int property_get_current_memory(
         return sd_bus_message_append(reply, "t", sz);
 }
 
+static int property_get_current_tasks(
+                sd_bus *bus,
+                const char *path,
+                const char *interface,
+                const char *property,
+                sd_bus_message *reply,
+                void *userdata,
+                sd_bus_error *error) {
+
+        uint64_t cn = (uint64_t) -1;
+        Unit *u = userdata;
+        int r;
+
+        assert(bus);
+        assert(reply);
+        assert(u);
+
+        r = unit_get_tasks_current(u, &cn);
+        if (r < 0 && r != -ENODATA)
+                log_unit_warning_errno(u, r, "Failed to get pids.current attribute: %m");
+
+        return sd_bus_message_append(reply, "t", cn);
+}
+
 static int property_get_cpu_usage(
                 sd_bus *bus,
                 const char *path,
@@ -697,12 +787,43 @@ static int property_get_cpu_usage(
         return sd_bus_message_append(reply, "t", ns);
 }
 
+static int property_get_cgroup(
+                sd_bus *bus,
+                const char *path,
+                const char *interface,
+                const char *property,
+                sd_bus_message *reply,
+                void *userdata,
+                sd_bus_error *error) {
+
+        Unit *u = userdata;
+        const char *t;
+
+        assert(bus);
+        assert(reply);
+        assert(u);
+
+        /* Three cases: a) u->cgroup_path is NULL, in which case the
+         * unit has no control group, which we report as the empty
+         * string. b) u->cgroup_path is the empty string, which
+         * indicates the root cgroup, which we report as "/". c) all
+         * other cases we report as-is. */
+
+        if (u->cgroup_path)
+                t = isempty(u->cgroup_path) ? "/" : u->cgroup_path;
+        else
+                t = "";
+
+        return sd_bus_message_append(reply, "s", t);
+}
+
 const sd_bus_vtable bus_unit_cgroup_vtable[] = {
         SD_BUS_VTABLE_START(0),
         SD_BUS_PROPERTY("Slice", "s", property_get_slice, 0, 0),
-        SD_BUS_PROPERTY("ControlGroup", "s", NULL, offsetof(Unit, cgroup_path), 0),
+        SD_BUS_PROPERTY("ControlGroup", "s", property_get_cgroup, 0, 0),
         SD_BUS_PROPERTY("MemoryCurrent", "t", property_get_current_memory, 0, 0),
         SD_BUS_PROPERTY("CPUUsageNSec", "t", property_get_cpu_usage, 0, 0),
+        SD_BUS_PROPERTY("TasksCurrent", "t", property_get_current_tasks, 0, 0),
         SD_BUS_VTABLE_END
 };
 
@@ -716,7 +837,7 @@ static int send_new_signal(sd_bus *bus, void *userdata) {
         assert(u);
 
         p = unit_dbus_path(u);
-        if (!u)
+        if (!p)
                 return -ENOMEM;
 
         r = sd_bus_message_new_signal(
@@ -753,7 +874,7 @@ static int send_changed_signal(sd_bus *bus, void *userdata) {
 
         r = sd_bus_emit_properties_changed_strv(
                         bus, p,
-                        UNIT_VTABLE(u)->bus_interface,
+                        unit_dbus_interface_from_type(u->type),
                         NULL);
         if (r < 0)
                 return r;
@@ -793,7 +914,7 @@ static int send_removed_signal(sd_bus *bus, void *userdata) {
         assert(u);
 
         p = unit_dbus_path(u);
-        if (!u)
+        if (!p)
                 return -ENOMEM;
 
         r = sd_bus_message_new_signal(
@@ -935,38 +1056,41 @@ static int bus_unit_set_transient_property(
 
                 return 1;
 
-        } else if (streq(name, "Slice") && unit_get_cgroup_context(u)) {
+        } else if (streq(name, "Slice")) {
+                Unit *slice;
                 const char *s;
 
+                if (!UNIT_HAS_CGROUP_CONTEXT(u))
+                        return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "The slice property is only available for units with control groups.");
+                if (u->type == UNIT_SLICE)
+                        return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Slice may not be set for slice units.");
+                if (unit_has_name(u, SPECIAL_INIT_SCOPE))
+                        return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Cannot set slice for init.scope");
+
                 r = sd_bus_message_read(message, "s", &s);
                 if (r < 0)
                         return r;
 
-                if (!unit_name_is_valid(s, UNIT_NAME_PLAIN) || !endswith(s, ".slice"))
-                        return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid slice name %s", s);
+                if (!unit_name_is_valid(s, UNIT_NAME_PLAIN))
+                        return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid unit name '%s'", s);
 
-                if (isempty(s)) {
-                        if (mode != UNIT_CHECK) {
-                                unit_ref_unset(&u->slice);
-                                unit_remove_drop_in(u, mode, name);
-                        }
-                } else {
-                        Unit *slice;
+                r = manager_load_unit(u->manager, s, NULL, error, &slice);
+                if (r < 0)
+                        return r;
 
-                        r = manager_load_unit(u->manager, s, NULL, error, &slice);
+                if (slice->type != UNIT_SLICE)
+                        return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Unit name '%s' is not a slice", s);
+
+                if (mode != UNIT_CHECK) {
+                        r = unit_set_slice(u, slice);
                         if (r < 0)
                                 return r;
 
-                        if (slice->type != UNIT_SLICE)
-                                return -EINVAL;
-
-                        if (mode != UNIT_CHECK) {
-                                unit_ref_set(&u->slice, slice);
-                                unit_write_drop_in_private_format(u, mode, name, "Slice=%s\n", s);
-                        }
+                        unit_write_drop_in_private_format(u, mode, name, "Slice=%s\n", s);
                 }
 
                 return 1;
+
         } else if (STR_IN_SET(name,
                               "Requires", "RequiresOverridable",
                               "Requisite", "RequisiteOverridable",