#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"
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,
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);
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);
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) {
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;
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),
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)",
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;
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
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);
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)
(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;
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(
* 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. */