]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
homed: add explicit API for requesting rebalancing too
authorLennart Poettering <lennart@poettering.net>
Thu, 4 Nov 2021 15:32:05 +0000 (16:32 +0100)
committerLennart Poettering <lennart@poettering.net>
Thu, 25 Nov 2021 17:28:44 +0000 (18:28 +0100)
man/org.freedesktop.home1.xml
src/home/homed-manager-bus.c
src/home/homed-manager.c
src/home/homed-manager.h
src/home/org.freedesktop.home1.conf
src/libsystemd/sd-bus/bus-common-errors.c
src/libsystemd/sd-bus/bus-common-errors.h

index b977e1b46f6d3ec43055ae882eb6162f33ddf218..537c3730893122b53658dec34502350cb76c7914 100644 (file)
@@ -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 {
 
     <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-->
@@ -346,6 +349,10 @@ node /org/freedesktop/home1 {
 
       <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>
index 7ac5b8d0fc7c3a9741dba95eab34866b6fb80b0a..31f82dc1dc64d4e4faa5ca62c770634152431b9e 100644 (file)
@@ -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
 };
index 3851234a37b2c65cf6263eb27acd5c1e58c6c495..3e5bd9c29d1d0350b3b22db17cf261dc439358ba 100644 (file)
@@ -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;
 }
 
index cf6d58b2586e2db468454a73e7b8268b58f65696..18e7542e1353ae501cf3309dab934019c6b1eb0d 100644 (file)
@@ -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);
index 1975d5f1a2bf8cf8ab04e0e5bd6ac45f55a8925b..bac6587b8e8665622789a038b6d55dbc6de9134f 100644 (file)
                        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"
index 61c5509e1c82c2911928d2486f7a864417ac9d12..cb22d62d2cc299c91ce761113dd18393de296e6c 100644 (file)
@@ -143,6 +143,7 @@ BUS_ERROR_MAP_ELF_REGISTER const sd_bus_error_map bus_common_errors[] = {
         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
 };
index 348cd5094b3861d9790a8775e378ea44e71f8bb5..c3c25d69c3feaa0e6d191b61a48370d0375d4788 100644 (file)
 #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);