diff --git a/man/org.freedesktop.home1.xml b/man/org.freedesktop.home1.xml
index b977e1b46f..537c373089 100644
--- a/man/org.freedesktop.home1.xml
+++ b/man/org.freedesktop.home1.xml
@@ -96,6 +96,7 @@ node /org/freedesktop/home1 {
ReleaseHome(in s user_name);
LockAllHomes();
DeactivateAllHomes();
+ Rebalance();
properties:
readonly a(sso) AutoLogin = [...];
};
@@ -159,6 +160,8 @@ node /org/freedesktop/home1 {
+
+
@@ -346,6 +349,10 @@ node /org/freedesktop/home1 {
DeactivateAllHomes() deactivates all home areas that are currently
active. This is usually invoked automatically shortly before system shutdown.
+
+ Rebalance() synchronously rebalances free disk space between home
+ areas. This only executes an operation if at least one home area using the LUKS2 backend is active and
+ has rebalancing enabled, and is otherwise a NOP.
diff --git a/src/home/homed-manager-bus.c b/src/home/homed-manager-bus.c
index 7ac5b8d0fc..31f82dc1dc 100644
--- a/src/home/homed-manager-bus.c
+++ b/src/home/homed-manager-bus.c
@@ -635,6 +635,27 @@ static int method_deactivate_all_homes(sd_bus_message *message, void *userdata,
return sd_bus_reply_method_return(message, NULL);
}
+static int method_rebalance(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ Manager *m = userdata;
+ int r;
+
+ assert(m);
+
+ r = manager_schedule_rebalance(m, /* immediately= */ true);
+ if (r == 0)
+ return sd_bus_reply_method_errorf(message, BUS_ERROR_REBALANCE_NOT_NEEDED, "No home directories need rebalancing.");
+ if (r < 0)
+ return r;
+
+ /* Keep a reference to this message, so that we can reply to it once we are done */
+ r = set_ensure_put(&m->rebalance_queued_method_calls, &bus_message_hash_ops, message);
+ if (r < 0)
+ return log_error_errno(r, "Failed to track rebalance bus message: %m");
+
+ sd_bus_message_ref(message);
+ return 1;
+}
+
static const sd_bus_vtable manager_vtable[] = {
SD_BUS_VTABLE_START(0),
@@ -843,6 +864,7 @@ static const sd_bus_vtable manager_vtable[] = {
/* An operation that acts on all homes that allow it */
SD_BUS_METHOD("LockAllHomes", NULL, NULL, method_lock_all_homes, 0),
SD_BUS_METHOD("DeactivateAllHomes", NULL, NULL, method_deactivate_all_homes, 0),
+ SD_BUS_METHOD("Rebalance", NULL, NULL, method_rebalance, 0),
SD_BUS_VTABLE_END
};
diff --git a/src/home/homed-manager.c b/src/home/homed-manager.c
index 3851234a37..3e5bd9c29d 100644
--- a/src/home/homed-manager.c
+++ b/src/home/homed-manager.c
@@ -1985,6 +1985,24 @@ static int manager_rebalance_apply(Manager *m) {
return c;
}
+static void manager_rebalance_reply_messages(Manager *m) {
+ int r;
+
+ assert(m);
+
+ for (;;) {
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *msg =
+ set_steal_first(m->rebalance_pending_method_calls);
+
+ if (!msg)
+ break;
+
+ r = sd_bus_reply_method_return(msg, NULL);
+ if (r < 0)
+ log_debug_errno(r, "Failed to reply to rebalance method call, ignoring: %m");
+ }
+}
+
static int manager_rebalance_now(Manager *m) {
RebalanceState busy_state; /* the state to revert to when operation fails if busy */
int r;
@@ -2006,6 +2024,13 @@ static int manager_rebalance_now(Manager *m) {
/* First shrink large home dirs */
m->rebalance_state = REBALANCE_SHRINKING;
busy_state = REBALANCE_PENDING;
+
+ /* We are initiating the next rebalancing cycle now, let's make the queued methods
+ * calls the pending ones, and flush out any pending ones (which shouldn't exist at
+ * this time anyway) */
+ set_clear(m->rebalance_pending_method_calls);
+ SWAP_TWO(m->rebalance_pending_method_calls, m->rebalance_queued_method_calls);
+
log_debug("Shrinking phase..");
break;
@@ -2055,6 +2080,7 @@ static int manager_rebalance_now(Manager *m) {
finish:
/* Reset state and schedule next rebalance */
m->rebalance_state = REBALANCE_IDLE;
+ manager_rebalance_reply_messages(m);
(void) manager_schedule_rebalance(m, /* immediately= */ false);
return r;
}
@@ -2078,6 +2104,7 @@ int manager_schedule_rebalance(Manager *m, bool immediately) {
/* Check if there are any records where rebalancing is requested */
if (!manager_shall_rebalance(m)) {
log_debug("Not scheduling rebalancing, not needed.");
+ r = 0; /* report that we didn't schedule anything because nothing needed it */
goto turn_off;
}
@@ -2118,13 +2145,13 @@ int manager_schedule_rebalance(Manager *m, bool immediately) {
m->rebalance_state = REBALANCE_PENDING;
log_debug("Scheduled immediate rebalancing...");
- return 0;
+ return 1; /* report that we scheduled something */
}
/* If we are told to schedule a rebalancing eventually, then do so only if we are not executing
* anything yet. Also if we have something scheduled already, leave it in place */
if (!IN_SET(m->rebalance_state, REBALANCE_OFF, REBALANCE_IDLE))
- return 0;
+ return 1; /* report that there's already something scheduled */
if (m->rebalance_event_source) {
r = sd_event_source_set_time_relative(m->rebalance_event_source, m->rebalance_interval_usec);
@@ -2156,11 +2183,12 @@ int manager_schedule_rebalance(Manager *m, bool immediately) {
m->rebalance_state = REBALANCE_WAITING; /* We managed to enqueue a timer event, we now wait until it fires */
log_debug("Scheduled rebalancing in %s...", FORMAT_TIMESPAN(m->rebalance_interval_usec, 0));
- return 0;
+ return 1; /* report that we scheduled something */
turn_off:
m->rebalance_event_source = sd_event_source_disable_unref(m->rebalance_event_source);
m->rebalance_state = REBALANCE_OFF;
+ manager_rebalance_reply_messages(m);
return r;
}
diff --git a/src/home/homed-manager.h b/src/home/homed-manager.h
index cf6d58b258..18e7542e13 100644
--- a/src/home/homed-manager.h
+++ b/src/home/homed-manager.h
@@ -63,6 +63,12 @@ struct Manager {
RebalanceState rebalance_state;
usec_t rebalance_interval_usec;
+
+ /* In order to allow synchronous rebalance requests via bus calls we maintain two pools of bus
+ * messages: 'rebalance_pending_methods' are the method calls we are currently operating on and
+ * running a rebalancing operation for. 'rebalance_queued_method_calls' are the method calls that
+ * have been queued since then and that we'll operate on once we complete the current run. */
+ Set *rebalance_pending_method_calls, *rebalance_queued_method_calls;
};
int manager_new(Manager **ret);
diff --git a/src/home/org.freedesktop.home1.conf b/src/home/org.freedesktop.home1.conf
index 1975d5f1a2..bac6587b8e 100644
--- a/src/home/org.freedesktop.home1.conf
+++ b/src/home/org.freedesktop.home1.conf
@@ -125,6 +125,10 @@
send_interface="org.freedesktop.home1.Manager"
send_member="LockAllHomes"/>
+
+