]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/core/dbus-unit.c
Rework cmdline printing to use unicode
[thirdparty/systemd.git] / src / core / dbus-unit.c
index 28ae6070a60d0a0cbd8ec184d99322e6ebbbe2f6..1e996a2f1b0642b1872ec8fa0822da0d4346e1f2 100644 (file)
@@ -19,6 +19,7 @@
 #include "selinux-access.h"
 #include "signal-util.h"
 #include "special.h"
+#include "string-table.h"
 #include "string-util.h"
 #include "strv.h"
 #include "user-util.h"
@@ -312,6 +313,14 @@ static int bus_verify_manage_units_async_full(
                         error);
 }
 
+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 bus_unit_method_start_generic(
                 sd_bus_message *message,
                 Unit *u,
@@ -319,16 +328,8 @@ int bus_unit_method_start_generic(
                 bool reload_if_possible,
                 sd_bus_error *error) {
 
-        const char *smode;
+        const char *smode, *verb;
         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);
@@ -351,17 +352,15 @@ int bus_unit_method_start_generic(
                 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Job mode %s invalid", smode);
 
         if (reload_if_possible)
-                verb = strjoin("reload-or-", job_type_to_string(job_type));
+                verb = strjoina("reload-or-", job_type_to_string(job_type));
         else
-                verb = strdup(job_type_to_string(job_type));
-        if (!verb)
-                return -ENOMEM;
+                verb = job_type_to_string(job_type);
 
         r = bus_verify_manage_units_async_full(
                         u,
                         verb,
                         CAP_SYS_ADMIN,
-                        job_type < _JOB_TYPE_MAX ? polkit_message_for_job[job_type] : NULL,
+                        polkit_message_for_job[job_type],
                         true,
                         message,
                         error);
@@ -370,7 +369,8 @@ int bus_unit_method_start_generic(
         if (r == 0)
                 return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
 
-        return bus_unit_queue_job(message, u, job_type, mode, reload_if_possible, error);
+        return bus_unit_queue_job(message, u, job_type, mode,
+                                  reload_if_possible ? BUS_UNIT_QUEUE_RELOAD_IF_POSSIBLE : 0, error);
 }
 
 static int method_start(sd_bus_message *message, void *userdata, sd_bus_error *error) {
@@ -401,6 +401,62 @@ static int method_reload_or_try_restart(sd_bus_message *message, void *userdata,
         return bus_unit_method_start_generic(message, userdata, JOB_TRY_RESTART, true, error);
 }
 
+int bus_unit_method_enqueue_job(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+        BusUnitQueueFlags flags = BUS_UNIT_QUEUE_VERBOSE_REPLY;
+        const char *jtype, *smode;
+        Unit *u = userdata;
+        JobType type;
+        JobMode mode;
+        int r;
+
+        assert(message);
+        assert(u);
+
+        r = sd_bus_message_read(message, "ss", &jtype, &smode);
+        if (r < 0)
+                return r;
+
+        /* Parse the two magic reload types "reload-or-…" manually */
+        if (streq(jtype, "reload-or-restart")) {
+                type = JOB_RESTART;
+                flags |= BUS_UNIT_QUEUE_RELOAD_IF_POSSIBLE;
+        } else if (streq(jtype, "reload-or-try-restart")) {
+                type = JOB_TRY_RESTART;
+                flags |= BUS_UNIT_QUEUE_RELOAD_IF_POSSIBLE;
+        } else {
+                /* And the rest generically */
+                type = job_type_from_string(jtype);
+                if (type < 0)
+                        return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Job type %s invalid", jtype);
+        }
+
+        mode = job_mode_from_string(smode);
+        if (mode < 0)
+                return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Job mode %s invalid", smode);
+
+        r = mac_selinux_unit_access_check(
+                        u, message,
+                        job_type_to_access_method(type),
+                        error);
+        if (r < 0)
+                return r;
+
+        r = bus_verify_manage_units_async_full(
+                        u,
+                        jtype,
+                        CAP_SYS_ADMIN,
+                        polkit_message_for_job[type],
+                        true,
+                        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 */
+
+        return bus_unit_queue_job(message, u, type, mode, flags, error);
+}
+
 int bus_unit_method_kill(sd_bus_message *message, void *userdata, sd_bus_error *error) {
         Unit *u = userdata;
         const char *swho;
@@ -686,6 +742,7 @@ const sd_bus_vtable bus_unit_vtable[] = {
         SD_BUS_METHOD("TryRestart", "s", "o", method_try_restart, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD("ReloadOrRestart", "s", "o", method_reload_or_restart, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD("ReloadOrTryRestart", "s", "o", method_reload_or_try_restart, SD_BUS_VTABLE_UNPRIVILEGED),
+        SD_BUS_METHOD("EnqueueJob", "ss", "uososa(uosos)", bus_unit_method_enqueue_job, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD("Kill", "si", NULL, bus_unit_method_kill, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD("ResetFailed", NULL, NULL, bus_unit_method_reset_failed, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD("SetProperties", "ba(sv)", NULL, bus_unit_method_set_properties, SD_BUS_VTABLE_UNPRIVILEGED),
@@ -844,7 +901,7 @@ static int append_process(sd_bus_message *reply, const char *p, pid_t pid, Set *
                 p = buf;
         }
 
-        (void) get_process_cmdline(pid, 0, true, &cmdline);
+        (void) get_process_cmdline(pid, SIZE_MAX, true, &cmdline);
 
         return sd_bus_message_append(reply,
                                      "(sus)",
@@ -973,30 +1030,57 @@ static int property_get_ip_counter(
                 void *userdata,
                 sd_bus_error *error) {
 
-        CGroupIPAccountingMetric metric;
-        uint64_t value = (uint64_t) -1;
+        static const char *const table[_CGROUP_IP_ACCOUNTING_METRIC_MAX] = {
+                [CGROUP_IP_INGRESS_BYTES]   = "IPIngressBytes",
+                [CGROUP_IP_EGRESS_BYTES]    = "IPEgressBytes",
+                [CGROUP_IP_INGRESS_PACKETS] = "IPIngressPackets",
+                [CGROUP_IP_EGRESS_PACKETS]  = "IPEgressPackets",
+        };
+
+        uint64_t value = UINT64_MAX;
         Unit *u = userdata;
+        ssize_t metric;
 
         assert(bus);
         assert(reply);
         assert(property);
         assert(u);
 
-        if (streq(property, "IPIngressBytes"))
-                metric = CGROUP_IP_INGRESS_BYTES;
-        else if (streq(property, "IPIngressPackets"))
-                metric = CGROUP_IP_INGRESS_PACKETS;
-        else if (streq(property, "IPEgressBytes"))
-                metric = CGROUP_IP_EGRESS_BYTES;
-        else {
-                assert(streq(property, "IPEgressPackets"));
-                metric = CGROUP_IP_EGRESS_PACKETS;
-        }
-
+        assert_se((metric = string_table_lookup(table, ELEMENTSOF(table), property)) >= 0);
         (void) unit_get_ip_accounting(u, metric, &value);
         return sd_bus_message_append(reply, "t", value);
 }
 
+static int property_get_io_counter(
+                sd_bus *bus,
+                const char *path,
+                const char *interface,
+                const char *property,
+                sd_bus_message *reply,
+                void *userdata,
+                sd_bus_error *error) {
+
+        static const char *const table[_CGROUP_IO_ACCOUNTING_METRIC_MAX] = {
+                [CGROUP_IO_READ_BYTES]       = "IOReadBytes",
+                [CGROUP_IO_WRITE_BYTES]      = "IOWriteBytes",
+                [CGROUP_IO_READ_OPERATIONS]  = "IOReadOperations",
+                [CGROUP_IO_WRITE_OPERATIONS] = "IOWriteOperations",
+        };
+
+        uint64_t value = UINT64_MAX;
+        Unit *u = userdata;
+        ssize_t metric;
+
+        assert(bus);
+        assert(reply);
+        assert(property);
+        assert(u);
+
+        assert_se((metric = string_table_lookup(table, ELEMENTSOF(table), property)) >= 0);
+        (void) unit_get_io_accounting(u, metric, false, &value);
+        return sd_bus_message_append(reply, "t", value);
+}
+
 int bus_unit_method_attach_processes(sd_bus_message *message, void *userdata, sd_bus_error *error) {
 
         _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
@@ -1120,6 +1204,10 @@ const sd_bus_vtable bus_unit_cgroup_vtable[] = {
         SD_BUS_PROPERTY("IPIngressPackets", "t", property_get_ip_counter, 0, 0),
         SD_BUS_PROPERTY("IPEgressBytes", "t", property_get_ip_counter, 0, 0),
         SD_BUS_PROPERTY("IPEgressPackets", "t", property_get_ip_counter, 0, 0),
+        SD_BUS_PROPERTY("IOReadBytes", "t", property_get_io_counter, 0, 0),
+        SD_BUS_PROPERTY("IOReadOperations", "t", property_get_io_counter, 0, 0),
+        SD_BUS_PROPERTY("IOWriteBytes", "t", property_get_io_counter, 0, 0),
+        SD_BUS_PROPERTY("IOWriteOperations", "t", property_get_io_counter, 0, 0),
         SD_BUS_METHOD("GetProcesses", NULL, "a(sus)", bus_unit_method_get_processes, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_METHOD("AttachProcesses", "sau", NULL, bus_unit_method_attach_processes, SD_BUS_VTABLE_UNPRIVILEGED),
         SD_BUS_VTABLE_END
@@ -1272,11 +1360,14 @@ int bus_unit_queue_job(
                 Unit *u,
                 JobType type,
                 JobMode mode,
-                bool reload_if_possible,
+                BusUnitQueueFlags flags,
                 sd_bus_error *error) {
 
-        _cleanup_free_ char *path = NULL;
-        Job *j;
+        _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
+        _cleanup_free_ char *job_path = NULL, *unit_path = NULL;
+        _cleanup_(set_freep) Set *affected = NULL;
+        Iterator i;
+        Job *j, *a;
         int r;
 
         assert(message);
@@ -1291,7 +1382,7 @@ int bus_unit_queue_job(
         if (r < 0)
                 return r;
 
-        if (reload_if_possible && unit_can_reload(u)) {
+        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)
@@ -1309,7 +1400,13 @@ int bus_unit_queue_job(
             (type == JOB_RELOAD_OR_START && job_type_collapse(type, u) == JOB_START && u->refuse_manual_start))
                 return sd_bus_error_setf(error, BUS_ERROR_ONLY_BY_DEPENDENCY, "Operation refused, unit %s may be requested by dependency only (it is configured to refuse manual start/stop).", u->id);
 
-        r = manager_add_job(u->manager, type, u, mode, error, &j);
+        if (FLAGS_SET(flags, BUS_UNIT_QUEUE_VERBOSE_REPLY)) {
+                affected = set_new(NULL);
+                if (!affected)
+                        return -ENOMEM;
+        }
+
+        r = manager_add_job(u->manager, type, u, mode, affected, error, &j);
         if (r < 0)
                 return r;
 
@@ -1317,14 +1414,67 @@ int bus_unit_queue_job(
         if (r < 0)
                 return r;
 
-        path = job_dbus_path(j);
-        if (!path)
-                return -ENOMEM;
-
         /* Before we send the method reply, force out the announcement JobNew for this job */
         bus_job_send_pending_change_signal(j, true);
 
-        return sd_bus_reply_method_return(message, "o", path);
+        job_path = job_dbus_path(j);
+        if (!job_path)
+                return -ENOMEM;
+
+        /* The classic response is just a job object path */
+        if (!FLAGS_SET(flags, BUS_UNIT_QUEUE_VERBOSE_REPLY))
+                return sd_bus_reply_method_return(message, "o", job_path);
+
+        /* In verbose mode respond with the anchor job plus everything that has been affected */
+        r = sd_bus_message_new_method_return(message, &reply);
+        if (r < 0)
+                return r;
+
+        unit_path = unit_dbus_path(j->unit);
+        if (!unit_path)
+                return -ENOMEM;
+
+        r = sd_bus_message_append(reply, "uosos",
+                                  j->id, job_path,
+                                  j->unit->id, unit_path,
+                                  job_type_to_string(j->type));
+        if (r < 0)
+                return r;
+
+        r = sd_bus_message_open_container(reply, 'a', "(uosos)");
+        if (r < 0)
+                return r;
+
+        SET_FOREACH(a, affected, i) {
+
+                if (a->id == j->id)
+                        continue;
+
+                /* Free paths from previous iteration */
+                job_path = mfree(job_path);
+                unit_path = mfree(unit_path);
+
+                job_path = job_dbus_path(a);
+                if (!job_path)
+                        return -ENOMEM;
+
+                unit_path = unit_dbus_path(a->unit);
+                if (!unit_path)
+                        return -ENOMEM;
+
+                r = sd_bus_message_append(reply, "(uosos)",
+                                          a->id, job_path,
+                                          a->unit->id, unit_path,
+                                          job_type_to_string(a->type));
+                if (r < 0)
+                        return r;
+        }
+
+        r = sd_bus_message_close_container(reply);
+        if (r < 0)
+                return r;
+
+        return sd_bus_send(NULL, reply, NULL);
 }
 
 static int bus_unit_set_live_property(
@@ -1749,7 +1899,7 @@ static int bus_unit_set_transient_property(
                  * transient units, but still). And "References" and "ReferencedBy" is already used as unit reference
                  * dependency type, hence let's not confuse things with that.
                  *
-                 * Note that we don't acually add the reference to the bus track. We do that only after the setup of
+                 * Note that we don't actually add the reference to the bus track. We do that only after the setup of
                  * the transient unit is complete, so that setting this property multiple times in the same transient
                  * unit creation call doesn't count as individual references. */