ReleaseHome(in s user_name);
LockAllHomes();
DeactivateAllHomes();
+ Rebalance();
properties:
readonly a(sso) AutoLogin = [...];
};
<variablelist class="dbus-method" generated="True" extra-ref="DeactivateAllHomes()"/>
+ <variablelist class="dbus-method" generated="True" extra-ref="Rebalance()"/>
+
<variablelist class="dbus-property" generated="True" extra-ref="AutoLogin"/>
<!--End of Autogenerated section-->
<para><function>DeactivateAllHomes()</function> deactivates all home areas that are currently
active. This is usually invoked automatically shortly before system shutdown.</para>
+
+ <para><function>Rebalance()</function> 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.</para>
</refsect2>
<refsect2>
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),
/* 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
};
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;
/* 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;
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;
}
/* 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;
}
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);
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;
}
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);
send_interface="org.freedesktop.home1.Manager"
send_member="LockAllHomes"/>
+ <allow send_destination="org.freedesktop.home1"
+ send_interface="org.freedesktop.home1.Manager"
+ send_member="Rebalance"/>
+
<!-- Home object -->
<allow send_destination="org.freedesktop.home1"
SD_BUS_ERROR_MAP(BUS_ERROR_AUTHENTICATION_LIMIT_HIT, ETOOMANYREFS),
SD_BUS_ERROR_MAP(BUS_ERROR_HOME_CANT_AUTHENTICATE, EKEYREVOKED),
SD_BUS_ERROR_MAP(BUS_ERROR_HOME_IN_USE, EADDRINUSE),
+ SD_BUS_ERROR_MAP(BUS_ERROR_REBALANCE_NOT_NEEDED, EALREADY),
SD_BUS_ERROR_MAP_END
};
#define BUS_ERROR_AUTHENTICATION_LIMIT_HIT "org.freedesktop.home1.AuthenticationLimitHit"
#define BUS_ERROR_HOME_CANT_AUTHENTICATE "org.freedesktop.home1.HomeCantAuthenticate"
#define BUS_ERROR_HOME_IN_USE "org.freedesktop.home1.HomeInUse"
+#define BUS_ERROR_REBALANCE_NOT_NEEDED "org.freedesktop.home1.RebalanceNotNeeded"
BUS_ERROR_MAP_ELF_USE(bus_common_errors);