homed: retry deactivation every 15s until successful

Fixes: #17445
This commit is contained in:
Lennart Poettering
2021-10-11 16:00:34 +02:00
parent 0c71e3ef24
commit 23cff6d4fe
2 changed files with 152 additions and 10 deletions
+120 -10
View File
@@ -38,6 +38,9 @@
#include "user-record.h"
#include "user-util.h"
/* Retry to deactivate home directories again and again every 15s until it works */
#define RETRY_DEACTIVATE_USEC (15U * USEC_PER_SEC)
#define HOME_USERS_MAX 500
#define PENDING_OPERATIONS_MAX 100
@@ -206,6 +209,8 @@ Home *home_free(Home *h) {
safe_close(h->pin_fd);
h->retry_deactivate_event_source = sd_event_source_disable_unref(h->retry_deactivate_event_source);
return mfree(h);
}
@@ -359,7 +364,83 @@ static void home_update_pin_fd(Home *h, HomeState state) {
if (state < 0)
state = home_get_state(h);
return HOME_STATE_IS_ACTIVE(state) ? home_pin(h) : home_unpin(h);
return HOME_STATE_SHALL_PIN(state) ? home_pin(h) : home_unpin(h);
}
static void home_maybe_stop_retry_deactivate(Home *h, HomeState state) {
assert(h);
/* Free the deactivation retry event source if we won't need it anymore. Specifically, we'll free the
* event source whenever the home directory is already deactivated (and we thus where successful) or
* if we start executing an operation that indicates that the home directory is going to be used or
* operated on again. Also, if the home is referenced again stop the timer */
if (HOME_STATE_MAY_RETRY_DEACTIVATE(state) &&
!h->ref_event_source_dont_suspend &&
!h->ref_event_source_please_suspend)
return;
h->retry_deactivate_event_source = sd_event_source_disable_unref(h->retry_deactivate_event_source);
}
static int home_deactivate_internal(Home *h, bool force, sd_bus_error *error);
static void home_start_retry_deactivate(Home *h);
static int home_on_retry_deactivate(sd_event_source *s, uint64_t usec, void *userdata) {
Home *h = userdata;
HomeState state;
assert(s);
assert(h);
/* 15s after the last attempt to deactivate the home directory passed. Let's try it one more time. */
h->retry_deactivate_event_source = sd_event_source_disable_unref(h->retry_deactivate_event_source);
state = home_get_state(h);
if (!HOME_STATE_MAY_RETRY_DEACTIVATE(state))
return 0;
if (IN_SET(state, HOME_ACTIVE, HOME_LINGERING)) {
log_info("Again trying to deactivate home directory.");
/* If we are not executing any operation, let's start deactivating now. Note that this will
* restart our timer again, we are gonna be called again if this doesn't work. */
(void) home_deactivate_internal(h, /* force= */ false, NULL);
} else
/* if we are executing an operation (specifically, area already running a deactivation
* operation), then simply reque the timer, so that we retry again. */
home_start_retry_deactivate(h);
return 0;
}
static void home_start_retry_deactivate(Home *h) {
int r;
assert(h);
assert(h->manager);
/* Alrady allocated? */
if (h->retry_deactivate_event_source)
return;
/* If the home directory is being used now don't start the timer */
if (h->ref_event_source_dont_suspend || h->ref_event_source_please_suspend)
return;
r = sd_event_add_time_relative(
h->manager->event,
&h->retry_deactivate_event_source,
CLOCK_MONOTONIC,
RETRY_DEACTIVATE_USEC,
1*USEC_PER_MINUTE,
home_on_retry_deactivate,
h);
if (r < 0)
return (void) log_warning_errno(r, "Failed to install retry-deactivate event source, ignoring: %m");
(void) sd_event_source_set_description(h->retry_deactivate_event_source, "retry-deactivate");
}
static void home_set_state(Home *h, HomeState state) {
@@ -377,6 +458,7 @@ static void home_set_state(Home *h, HomeState state) {
home_state_to_string(new_state));
home_update_pin_fd(h, new_state);
home_maybe_stop_retry_deactivate(h, new_state);
if (HOME_STATE_IS_EXECUTING_OPERATION(old_state) && !HOME_STATE_IS_EXECUTING_OPERATION(new_state)) {
/* If we just finished executing some operation, process the queue of pending operations. And
@@ -1196,6 +1278,7 @@ int home_fixate(Home *h, UserRecord *secret, sd_bus_error *error) {
case HOME_INACTIVE:
case HOME_DIRTY:
case HOME_ACTIVE:
case HOME_LINGERING:
case HOME_LOCKED:
return sd_bus_error_setf(error, BUS_ERROR_HOME_ALREADY_FIXATED, "Home %s is already fixated.", h->user_name);
case HOME_UNFIXATED:
@@ -1237,6 +1320,11 @@ int home_activate(Home *h, UserRecord *secret, sd_bus_error *error) {
return sd_bus_error_setf(error, BUS_ERROR_HOME_ABSENT, "Home %s is currently missing or not plugged in.", h->user_name);
case HOME_ACTIVE:
return sd_bus_error_setf(error, BUS_ERROR_HOME_ALREADY_ACTIVE, "Home %s is already active.", h->user_name);
case HOME_LINGERING:
/* If we are lingering, i.e. active but are supposed to be deactivated, then cancel this
* timer if the user explicitly asks us to be active */
h->retry_deactivate_event_source = sd_event_source_disable_unref(h->retry_deactivate_event_source);
return 0;
case HOME_LOCKED:
return sd_bus_error_setf(error, BUS_ERROR_HOME_LOCKED, "Home %s is currently locked.", h->user_name);
case HOME_INACTIVE:
@@ -1283,6 +1371,7 @@ int home_authenticate(Home *h, UserRecord *secret, sd_bus_error *error) {
case HOME_INACTIVE:
case HOME_DIRTY:
case HOME_ACTIVE:
case HOME_LINGERING:
break;
default:
return sd_bus_error_setf(error, BUS_ERROR_HOME_BUSY, "An operation on home %s is currently being executed.", h->user_name);
@@ -1292,7 +1381,7 @@ int home_authenticate(Home *h, UserRecord *secret, sd_bus_error *error) {
if (r < 0)
return r;
return home_authenticate_internal(h, secret, state == HOME_ACTIVE ? HOME_AUTHENTICATING_WHILE_ACTIVE : HOME_AUTHENTICATING, error);
return home_authenticate_internal(h, secret, HOME_STATE_IS_ACTIVE(state) ? HOME_AUTHENTICATING_WHILE_ACTIVE : HOME_AUTHENTICATING, error);
}
static int home_deactivate_internal(Home *h, bool force, sd_bus_error *error) {
@@ -1303,14 +1392,19 @@ static int home_deactivate_internal(Home *h, bool force, sd_bus_error *error) {
home_unpin(h); /* unpin so that we can deactivate */
r = home_start_work(h, force ? "deactivate-force" : "deactivate", h->record, NULL);
if (r < 0) {
if (r < 0)
/* Operation failed before it even started, reacquire pin fd, if state still dictates so */
home_update_pin_fd(h, _HOME_STATE_INVALID);
return r;
else {
home_set_state(h, HOME_DEACTIVATING);
r = 0;
}
home_set_state(h, HOME_DEACTIVATING);
return 0;
/* Let's start a timer to retry deactivation in 15. We'll stop the timer once we manage to deactivate
* the home directory again, or we we start any other operation. */
home_start_retry_deactivate(h);
return r;
}
int home_deactivate(Home *h, bool force, sd_bus_error *error) {
@@ -1325,6 +1419,7 @@ int home_deactivate(Home *h, bool force, sd_bus_error *error) {
case HOME_LOCKED:
return sd_bus_error_setf(error, BUS_ERROR_HOME_LOCKED, "Home %s is currently locked.", h->user_name);
case HOME_ACTIVE:
case HOME_LINGERING:
break;
default:
return sd_bus_error_setf(error, BUS_ERROR_HOME_BUSY, "An operation on home %s is currently being executed.", h->user_name);
@@ -1361,6 +1456,7 @@ int home_create(Home *h, UserRecord *secret, sd_bus_error *error) {
case HOME_ABSENT:
break;
case HOME_ACTIVE:
case HOME_LINGERING:
case HOME_LOCKED:
default:
return sd_bus_error_setf(error, BUS_ERROR_HOME_BUSY, "Home %s is currently being used, or an operation on home %s is currently being executed.", h->user_name, h->user_name);
@@ -1399,6 +1495,7 @@ int home_remove(Home *h, sd_bus_error *error) {
case HOME_DIRTY:
break;
case HOME_ACTIVE:
case HOME_LINGERING:
default:
return sd_bus_error_setf(error, BUS_ERROR_HOME_BUSY, "Home %s is currently being used, or an operation on home %s is currently being executed.", h->user_name, h->user_name);
}
@@ -1537,6 +1634,7 @@ int home_update(Home *h, UserRecord *hr, sd_bus_error *error) {
case HOME_INACTIVE:
case HOME_DIRTY:
case HOME_ACTIVE:
case HOME_LINGERING:
break;
default:
return sd_bus_error_setf(error, BUS_ERROR_HOME_BUSY, "An operation on home %s is currently being executed.", h->user_name);
@@ -1550,7 +1648,7 @@ int home_update(Home *h, UserRecord *hr, sd_bus_error *error) {
if (r < 0)
return r;
home_set_state(h, state == HOME_ACTIVE ? HOME_UPDATING_WHILE_ACTIVE : HOME_UPDATING);
home_set_state(h, HOME_STATE_IS_ACTIVE(state) ? HOME_UPDATING_WHILE_ACTIVE : HOME_UPDATING);
return 0;
}
@@ -1572,6 +1670,7 @@ int home_resize(Home *h, uint64_t disk_size, UserRecord *secret, sd_bus_error *e
case HOME_INACTIVE:
case HOME_DIRTY:
case HOME_ACTIVE:
case HOME_LINGERING:
break;
default:
return sd_bus_error_setf(error, BUS_ERROR_HOME_BUSY, "An operation on home %s is currently being executed.", h->user_name);
@@ -1620,7 +1719,7 @@ int home_resize(Home *h, uint64_t disk_size, UserRecord *secret, sd_bus_error *e
if (r < 0)
return r;
home_set_state(h, state == HOME_ACTIVE ? HOME_RESIZING_WHILE_ACTIVE : HOME_RESIZING);
home_set_state(h, HOME_STATE_IS_ACTIVE(state) ? HOME_RESIZING_WHILE_ACTIVE : HOME_RESIZING);
return 0;
}
@@ -1668,6 +1767,7 @@ int home_passwd(Home *h,
case HOME_INACTIVE:
case HOME_DIRTY:
case HOME_ACTIVE:
case HOME_LINGERING:
break;
default:
return sd_bus_error_setf(error, BUS_ERROR_HOME_BUSY, "An operation on home %s is currently being executed.", h->user_name);
@@ -1733,7 +1833,7 @@ int home_passwd(Home *h,
if (r < 0)
return r;
home_set_state(h, state == HOME_ACTIVE ? HOME_PASSWD_WHILE_ACTIVE : HOME_PASSWD);
home_set_state(h, HOME_STATE_IS_ACTIVE(state) ? HOME_PASSWD_WHILE_ACTIVE : HOME_PASSWD);
return 0;
}
@@ -1752,6 +1852,7 @@ int home_unregister(Home *h, sd_bus_error *error) {
case HOME_DIRTY:
break;
case HOME_ACTIVE:
case HOME_LINGERING:
default:
return sd_bus_error_setf(error, BUS_ERROR_HOME_BUSY, "Home %s is currently being used, or an operation on home %s is currently being executed.", h->user_name, h->user_name);
}
@@ -1779,6 +1880,7 @@ int home_lock(Home *h, sd_bus_error *error) {
case HOME_LOCKED:
return sd_bus_error_setf(error, BUS_ERROR_HOME_LOCKED, "Home %s is already locked.", h->user_name);
case HOME_ACTIVE:
case HOME_LINGERING:
break;
default:
return sd_bus_error_setf(error, BUS_ERROR_HOME_BUSY, "An operation on home %s is currently being executed.", h->user_name);
@@ -1819,6 +1921,7 @@ int home_unlock(Home *h, UserRecord *secret, sd_bus_error *error) {
case HOME_ABSENT:
case HOME_INACTIVE:
case HOME_ACTIVE:
case HOME_LINGERING:
case HOME_DIRTY:
return sd_bus_error_setf(error, BUS_ERROR_HOME_NOT_LOCKED, "Home %s is not locked.", h->user_name);
case HOME_LOCKED:
@@ -1841,7 +1944,7 @@ HomeState home_get_state(Home *h) {
/* Otherwise, let's see if the home directory is mounted. If so, we assume for sure the home
* directory is active */
if (user_record_test_home_directory(h->record) == USER_TEST_MOUNTED)
return HOME_ACTIVE;
return h->retry_deactivate_event_source ? HOME_LINGERING : HOME_ACTIVE;
/* And if we see the image being gone, we report this as absent */
r = user_record_test_image_path(h->record);
@@ -2424,6 +2527,7 @@ static int home_dispatch_acquire(Home *h, Operation *o) {
break;
case HOME_ACTIVE:
case HOME_LINGERING:
for_state = HOME_AUTHENTICATING_FOR_ACQUIRE;
call = home_authenticate_internal;
break;
@@ -2478,6 +2582,7 @@ static int home_dispatch_release(Home *h, Operation *o) {
break;
case HOME_ACTIVE:
case HOME_LINGERING:
r = home_deactivate_internal(h, false, &error);
break;
@@ -2522,6 +2627,7 @@ static int home_dispatch_lock_all(Home *h, Operation *o) {
break;
case HOME_ACTIVE:
case HOME_LINGERING:
log_info("Locking home %s.", h->user_name);
r = home_lock(h, &error);
break;
@@ -2566,6 +2672,7 @@ static int home_dispatch_deactivate_all(Home *h, Operation *o) {
break;
case HOME_ACTIVE:
case HOME_LINGERING:
log_info("Deactivating home %s.", h->user_name);
r = home_deactivate_internal(h, false, &error);
break;
@@ -2611,6 +2718,7 @@ static int home_dispatch_pipe_eof(Home *h, Operation *o) {
break;
case HOME_ACTIVE:
case HOME_LINGERING:
r = home_deactivate_internal(h, false, &error);
if (r < 0)
log_warning_errno(r, "Failed to deactivate %s, ignoring: %s", h->user_name, bus_error_message(&error, r));
@@ -2652,6 +2760,7 @@ static int home_dispatch_deactivate_force(Home *h, Operation *o) {
case HOME_ACTIVE:
case HOME_LOCKED:
case HOME_LINGERING:
r = home_deactivate_internal(h, true, &error);
if (r < 0)
log_warning_errno(r, "Failed to forcibly deactivate %s, ignoring: %s", h->user_name, bus_error_message(&error, r));
@@ -2872,6 +2981,7 @@ static const char* const home_state_table[_HOME_STATE_MAX] = {
[HOME_ACTIVATING_FOR_ACQUIRE] = "activating-for-acquire",
[HOME_DEACTIVATING] = "deactivating",
[HOME_ACTIVE] = "active",
[HOME_LINGERING] = "lingering",
[HOME_LOCKING] = "locking",
[HOME_LOCKED] = "locked",
[HOME_UNLOCKING] = "unlocking",
+32
View File
@@ -21,6 +21,7 @@ typedef enum HomeState {
HOME_ACTIVATING_FOR_ACQUIRE, /* activating because Acquire() was called */
HOME_DEACTIVATING,
HOME_ACTIVE, /* logged in right now */
HOME_LINGERING, /* not logged in anymore, but we didn't manage to deactivate (because some process keeps it busy?) but we'll keep trying */
HOME_LOCKING,
HOME_LOCKED,
HOME_UNLOCKING,
@@ -43,6 +44,7 @@ typedef enum HomeState {
static inline bool HOME_STATE_IS_ACTIVE(HomeState state) {
return IN_SET(state,
HOME_ACTIVE,
HOME_LINGERING,
HOME_UPDATING_WHILE_ACTIVE,
HOME_RESIZING_WHILE_ACTIVE,
HOME_PASSWD_WHILE_ACTIVE,
@@ -74,6 +76,33 @@ static inline bool HOME_STATE_IS_EXECUTING_OPERATION(HomeState state) {
HOME_AUTHENTICATING_FOR_ACQUIRE);
}
static inline bool HOME_STATE_SHALL_PIN(HomeState state) {
/* Like HOME_STATE_IS_ACTIVE() – but HOME_LINGERING is missing! */
return IN_SET(state,
HOME_ACTIVE,
HOME_UPDATING_WHILE_ACTIVE,
HOME_RESIZING_WHILE_ACTIVE,
HOME_PASSWD_WHILE_ACTIVE,
HOME_AUTHENTICATING_WHILE_ACTIVE,
HOME_AUTHENTICATING_FOR_ACQUIRE);
}
static inline bool HOME_STATE_MAY_RETRY_DEACTIVATE(HomeState state) {
/* Indicates when to leave the deactivate retry timer active */
return IN_SET(state,
HOME_ACTIVE,
HOME_LINGERING,
HOME_DEACTIVATING,
HOME_LOCKING,
HOME_UNLOCKING,
HOME_UNLOCKING_FOR_ACQUIRE,
HOME_UPDATING_WHILE_ACTIVE,
HOME_RESIZING_WHILE_ACTIVE,
HOME_PASSWD_WHILE_ACTIVE,
HOME_AUTHENTICATING_WHILE_ACTIVE,
HOME_AUTHENTICATING_FOR_ACQUIRE);
}
struct Home {
Manager *manager;
char *user_name;
@@ -129,6 +158,9 @@ struct Home {
/* An fd to the top-level home directory we keep while logged in, to keep the dir busy */
int pin_fd;
/* A time event used to repeatedly try to unmount home dir after use if it didn't work on first try */
sd_event_source *retry_deactivate_event_source;
};
int home_new(Manager *m, UserRecord *hr, const char *sysfs, Home **ret);