return sd_bus_error_setf(error, BUS_ERROR_BAD_PASSWORD, "Password for home %s is incorrect or not sufficient for authentication.", h->user_name);
case -EBADSLT:
return sd_bus_error_setf(error, BUS_ERROR_BAD_PASSWORD_AND_NO_TOKEN, "Password for home %s is incorrect or not sufficient, and configured security token not found either.", h->user_name);
+ case -EREMOTEIO:
+ return sd_bus_error_setf(error, BUS_ERROR_BAD_RECOVERY_KEY, "Recovery key for home %s is incorrect or not sufficient for authentication.", h->user_name);
case -ENOANO:
return sd_bus_error_setf(error, BUS_ERROR_TOKEN_PIN_NEEDED, "PIN for security token required.");
case -ERFKILL:
if (r < 0)
return r;
if (r == 0) {
- const char *homework;
+ const char *homework, *suffix, *unix_path;
/* Child */
- if (setenv("NOTIFY_SOCKET", "/run/systemd/home/notify", 1) < 0) {
+ suffix = getenv("SYSTEMD_HOME_DEBUG_SUFFIX");
+ if (suffix)
+ unix_path = strjoina("/run/systemd/home/notify.", suffix);
+ else
+ unix_path = "/run/systemd/home/notify";
+
+ if (setenv("NOTIFY_SOCKET", unix_path, 1) < 0) {
log_error_errno(errno, "Failed to set $NOTIFY_SOCKET: %m");
_exit(EXIT_FAILURE);
}
homework = getenv("SYSTEMD_HOMEWORK_PATH") ?: SYSTEMD_HOMEWORK_PATH;
execl(homework, homework, verb, NULL);
- log_error_errno(errno, "Failed to invoke " SYSTEMD_HOMEWORK_PATH ": %m");
+ log_error_errno(errno, "Failed to invoke %s: %m", homework);
_exit(EXIT_FAILURE);
}
if (r < 0)
return r;
- if (for_state == HOME_FIXATING_FOR_ACTIVATION) {
+ if (IN_SET(for_state, HOME_FIXATING_FOR_ACTIVATION, HOME_FIXATING_FOR_ACQUIRE)) {
/* Remember the secret data, since we need it for the activation again, later on. */
user_record_unref(h->secret);
h->secret = user_record_ref(secret);
case HOME_ABSENT:
return sd_bus_error_setf(error, BUS_ERROR_HOME_ABSENT, "Home %s is currently missing or not plugged in.", h->user_name);
case HOME_INACTIVE:
+ case HOME_DIRTY:
case HOME_ACTIVE:
case HOME_LOCKED:
return sd_bus_error_setf(error, BUS_ERROR_HOME_ALREADY_FIXATED, "Home %s is already fixated.", h->user_name);
case HOME_LOCKED:
return sd_bus_error_setf(error, BUS_ERROR_HOME_LOCKED, "Home %s is currently locked.", h->user_name);
case HOME_INACTIVE:
+ case HOME_DIRTY:
break;
default:
return sd_bus_error_setf(error, BUS_ERROR_HOME_BUSY, "An operation on home %s is currently being executed.", h->user_name);
return sd_bus_error_setf(error, BUS_ERROR_HOME_LOCKED, "Home %s is currently locked.", h->user_name);
case HOME_UNFIXATED:
case HOME_INACTIVE:
+ case HOME_DIRTY:
case HOME_ACTIVE:
break;
default:
case HOME_UNFIXATED:
case HOME_ABSENT:
case HOME_INACTIVE:
+ case HOME_DIRTY:
return sd_bus_error_setf(error, BUS_ERROR_HOME_NOT_ACTIVE, "Home %s not active.", h->user_name);
case HOME_LOCKED:
return sd_bus_error_setf(error, BUS_ERROR_HOME_LOCKED, "Home %s is currently locked.", h->user_name);
assert(h);
switch (home_get_state(h)) {
- case HOME_INACTIVE:
+ case HOME_INACTIVE: {
+ int t;
+
if (h->record->storage < 0)
break; /* if no storage is defined we don't know what precisely to look for, hence
* HOME_INACTIVE is OK in that case too. */
- if (IN_SET(user_record_test_image_path(h->record), USER_TEST_MAYBE, USER_TEST_UNDEFINED))
+ t = user_record_test_image_path(h->record);
+ if (IN_SET(t, USER_TEST_MAYBE, USER_TEST_UNDEFINED))
break; /* And if the image path test isn't conclusive, let's also go on */
- _fallthrough_;
+ if (IN_SET(t, -EBADFD, -ENOTDIR))
+ return sd_bus_error_setf(error, BUS_ERROR_HOME_EXISTS, "Selected home image of user %s already exists or has wrong inode type.", h->user_name);
+
+ return sd_bus_error_setf(error, BUS_ERROR_HOME_EXISTS, "Selected home image of user %s already exists.", h->user_name);
+ }
case HOME_UNFIXATED:
+ case HOME_DIRTY:
return sd_bus_error_setf(error, BUS_ERROR_HOME_EXISTS, "Home of user %s already exists.", h->user_name);
case HOME_ABSENT:
break;
return sd_bus_error_setf(error, BUS_ERROR_HOME_LOCKED, "Home %s is currently locked.", h->user_name);
case HOME_UNFIXATED:
case HOME_INACTIVE:
+ case HOME_DIRTY:
break;
case HOME_ACTIVE:
default:
case HOME_LOCKED:
return sd_bus_error_setf(error, BUS_ERROR_HOME_LOCKED, "Home %s is currently locked.", h->user_name);
case HOME_INACTIVE:
+ case HOME_DIRTY:
case HOME_ACTIVE:
break;
default:
case HOME_LOCKED:
return sd_bus_error_setf(error, BUS_ERROR_HOME_LOCKED, "Home %s is currently locked.", h->user_name);
case HOME_INACTIVE:
+ case HOME_DIRTY:
case HOME_ACTIVE:
break;
default:
assert(h);
r = user_record_test_password_change_required(h->record);
- if (IN_SET(r, -EKEYREVOKED, -EOWNERDEAD, -EKEYEXPIRED))
+ if (IN_SET(r, -EKEYREVOKED, -EOWNERDEAD, -EKEYEXPIRED, -ESTALE))
return 0; /* expired in some form, but changing is allowed */
if (IN_SET(r, -EKEYREJECTED, -EROFS))
return sd_bus_error_setf(error, SD_BUS_ERROR_ACCESS_DENIED, "Expiration settings of account %s do not allow changing of password.", h->user_name);
case HOME_LOCKED:
return sd_bus_error_setf(error, BUS_ERROR_HOME_LOCKED, "Home %s is currently locked.", h->user_name);
case HOME_INACTIVE:
+ case HOME_DIRTY:
case HOME_ACTIVE:
break;
default:
return sd_bus_error_setf(error, BUS_ERROR_HOME_LOCKED, "Home %s is currently locked.", h->user_name);
case HOME_ABSENT:
case HOME_INACTIVE:
+ case HOME_DIRTY:
break;
case HOME_ACTIVE:
default:
case HOME_UNFIXATED:
case HOME_ABSENT:
case HOME_INACTIVE:
+ case HOME_DIRTY:
return sd_bus_error_setf(error, BUS_ERROR_HOME_NOT_ACTIVE, "Home %s is not active.", h->user_name);
case HOME_LOCKED:
return sd_bus_error_setf(error, BUS_ERROR_HOME_LOCKED, "Home %s is already locked.", h->user_name);
case HOME_ABSENT:
case HOME_INACTIVE:
case HOME_ACTIVE:
+ 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:
break;
}
HomeState home_get_state(Home *h) {
+ int r;
assert(h);
/* When the state field is initialized, it counts. */
return HOME_ACTIVE;
/* And if we see the image being gone, we report this as absent */
- if (user_record_test_image_path(h->record) == USER_TEST_ABSENT)
+ r = user_record_test_image_path(h->record);
+ if (r == USER_TEST_ABSENT)
return HOME_ABSENT;
+ if (r == USER_TEST_DIRTY)
+ return HOME_DIRTY;
/* And for all other cases we return "inactive". */
return HOME_INACTIVE;
break;
case HOME_INACTIVE:
+ case HOME_DIRTY:
for_state = HOME_ACTIVATING_FOR_ACQUIRE;
call = home_activate_internal;
break;
case HOME_UNFIXATED:
case HOME_ABSENT:
case HOME_INACTIVE:
+ case HOME_DIRTY:
r = 1; /* done */
break;
case HOME_UNFIXATED:
case HOME_ABSENT:
case HOME_INACTIVE:
+ case HOME_DIRTY:
log_info("Home %s is not active, no locking necessary.", h->user_name);
r = 1; /* done */
break;
return 1;
}
+static int home_dispatch_deactivate_all(Home *h, Operation *o) {
+ _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
+ int r;
+
+ assert(h);
+ assert(o);
+ assert(o->type == OPERATION_DEACTIVATE_ALL);
+
+ switch (home_get_state(h)) {
+
+ case HOME_UNFIXATED:
+ case HOME_ABSENT:
+ case HOME_INACTIVE:
+ case HOME_DIRTY:
+ log_info("Home %s is already deactivated.", h->user_name);
+ r = 1; /* done */
+ break;
+
+ case HOME_LOCKED:
+ log_info("Home %s is currently locked, not deactivating.", h->user_name);
+ r = 1; /* done */
+ break;
+
+ case HOME_ACTIVE:
+ log_info("Deactivating home %s.", h->user_name);
+ r = home_deactivate_internal(h, false, &error);
+ break;
+
+ default:
+ /* All other cases means we are currently executing an operation, which means the job remains
+ * pending. */
+ return 0;
+ }
+
+ assert(!h->current_operation);
+
+ if (r != 0) /* failure or completed */
+ operation_result(o, r, &error);
+ else /* ongoing */
+ h->current_operation = operation_ref(o);
+
+ return 1;
+}
+
static int home_dispatch_pipe_eof(Home *h, Operation *o) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
int r;
case HOME_UNFIXATED:
case HOME_ABSENT:
case HOME_INACTIVE:
+ case HOME_DIRTY:
log_info("Home %s already deactivated, no automatic deactivation needed.", h->user_name);
break;
case HOME_UNFIXATED:
case HOME_ABSENT:
case HOME_INACTIVE:
+ case HOME_DIRTY:
log_debug("Home %s already deactivated, no forced deactivation due to unplug needed.", h->user_name);
break;
[OPERATION_ACQUIRE] = home_dispatch_acquire,
[OPERATION_RELEASE] = home_dispatch_release,
[OPERATION_LOCK_ALL] = home_dispatch_lock_all,
+ [OPERATION_DEACTIVATE_ALL] = home_dispatch_deactivate_all,
[OPERATION_PIPE_EOF] = home_dispatch_pipe_eof,
[OPERATION_DEACTIVATE_FORCE] = home_dispatch_deactivate_force,
};
return 1;
}
+int home_wait_for_worker(Home *h) {
+ assert(h);
+
+ if (h->worker_pid <= 0)
+ return 0;
+
+ log_info("Worker process for home %s is still running while exiting. Waiting for it to finish.", h->user_name);
+ (void) wait_for_terminate(h->worker_pid, NULL);
+ (void) hashmap_remove_value(h->manager->homes_by_worker_pid, PID_TO_PTR(h->worker_pid), h);
+ h->worker_pid = 0;
+ return 1;
+}
+
static const char* const home_state_table[_HOME_STATE_MAX] = {
[HOME_UNFIXATED] = "unfixated",
[HOME_ABSENT] = "absent",
[HOME_INACTIVE] = "inactive",
+ [HOME_DIRTY] = "dirty",
[HOME_FIXATING] = "fixating",
[HOME_FIXATING_FOR_ACTIVATION] = "fixating-for-activation",
[HOME_FIXATING_FOR_ACQUIRE] = "fixating-for-acquire",