diff options
author | Adrian Vovk <adrianvovk@gmail.com> | 2024-01-21 02:29:40 +0100 |
---|---|---|
committer | Lennart Poettering <lennart@poettering.net> | 2024-01-31 09:48:23 +0100 |
commit | 691b99160de4bc856e676dba6e07b33d2fa0908e (patch) | |
tree | 4f3c60188af2c1d66ddc9eaaca478ab295ce6031 /src/home | |
parent | Merge pull request #31039 from AdrianVovk/slice-freeze-thaw (diff) | |
download | systemd-691b99160de4bc856e676dba6e07b33d2fa0908e.tar.xz systemd-691b99160de4bc856e676dba6e07b33d2fa0908e.zip |
homed: Add InhibitSuspend() method
This returns an FD that can be used to temporarily inhibit the automatic
locking on system suspend behavior of homed. As long as the FD is open,
LockAllHomes() won't lock that home directory on suspend. This allows
desktop environments to implement custom more complicated behavior
Diffstat (limited to 'src/home')
-rw-r--r-- | src/home/homed-home-bus.c | 56 | ||||
-rw-r--r-- | src/home/homed-home-bus.h | 1 | ||||
-rw-r--r-- | src/home/homed-home.c | 60 | ||||
-rw-r--r-- | src/home/homed-home.h | 23 | ||||
-rw-r--r-- | src/home/homed-manager-bus.c | 10 | ||||
-rw-r--r-- | src/home/org.freedesktop.home1.conf | 8 | ||||
-rw-r--r-- | src/home/org.freedesktop.home1.policy | 9 |
7 files changed, 141 insertions, 26 deletions
diff --git a/src/home/homed-home-bus.c b/src/home/homed-home-bus.c index 413a706f4c..30f5735443 100644 --- a/src/home/homed-home-bus.c +++ b/src/home/homed-home-bus.c @@ -595,7 +595,7 @@ int bus_home_method_acquire( return r; /* This operation might not be something we can executed immediately, hence queue it */ - fd = home_create_fifo(h, please_suspend); + fd = home_create_fifo(h, please_suspend ? HOME_FIFO_PLEASE_SUSPEND : HOME_FIFO_DONT_SUSPEND); if (fd < 0) return sd_bus_reply_method_errnof(message, fd, "Failed to allocate FIFO for %s: %m", h->user_name); @@ -646,7 +646,7 @@ int bus_home_method_ref( return sd_bus_error_setf(error, BUS_ERROR_HOME_BUSY, "An operation on home %s is currently being executed.", h->user_name); } - fd = home_create_fifo(h, please_suspend); + fd = home_create_fifo(h, please_suspend ? HOME_FIFO_PLEASE_SUSPEND : HOME_FIFO_DONT_SUSPEND); if (fd < 0) return sd_bus_reply_method_errnof(message, fd, "Failed to allocate FIFO for %s: %m", h->user_name); @@ -675,6 +675,53 @@ int bus_home_method_release( return 1; } +int bus_home_method_inhibit_suspend( + sd_bus_message *message, + void *userdata, + sd_bus_error *error) { + + _cleanup_close_ int fd = -EBADF; + Home *h = ASSERT_PTR(userdata); + HomeState state; + int r; + + r = bus_verify_polkit_async_full( + message, + "org.freedesktop.home1.inhibit-suspend", + /* details= */ NULL, + /* interactive= */ false, + h->uid, + &h->manager->polkit_registry, + error); + if (r < 0) + return r; + if (r == 0) + return 1; /* Will call us back */ + + state = home_get_state(h); + switch (state) { + 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_UNFIXATED: + 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); + default: + if (HOME_STATE_IS_ACTIVE(state)) + break; + + return sd_bus_error_setf(error, BUS_ERROR_HOME_BUSY, "An operation on home %s is currently being executed.", h->user_name); + } + + fd = home_create_fifo(h, HOME_FIFO_INHIBIT_SUSPEND); + if (fd < 0) + return sd_bus_reply_method_errnof(message, fd, "Failed to allocate FIFO for %s: %m", h->user_name); + + return sd_bus_reply_method_return(message, "h", fd); +} + /* We map a uid_t as uint32_t bus property, let's ensure this is safe. */ assert_cc(sizeof(uid_t) == sizeof(uint32_t)); @@ -819,6 +866,11 @@ const sd_bus_vtable home_vtable[] = { bus_home_method_ref, 0), SD_BUS_METHOD("Release", NULL, NULL, bus_home_method_release, 0), + SD_BUS_METHOD_WITH_ARGS("InhibitSuspend", + SD_BUS_NO_ARGS, + SD_BUS_RESULT("h", send_fd), + bus_home_method_inhibit_suspend, + SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_VTABLE_END }; diff --git a/src/home/homed-home-bus.h b/src/home/homed-home-bus.h index 5522178055..a791c76312 100644 --- a/src/home/homed-home-bus.h +++ b/src/home/homed-home-bus.h @@ -25,6 +25,7 @@ int bus_home_method_unlock(sd_bus_message *message, void *userdata, sd_bus_error int bus_home_method_acquire(sd_bus_message *message, void *userdata, sd_bus_error *error); int bus_home_method_ref(sd_bus_message *message, void *userdata, sd_bus_error *error); int bus_home_method_release(sd_bus_message *message, void *userdata, sd_bus_error *error); +int bus_home_method_inhibit_suspend(sd_bus_message *message, void *userdata, sd_bus_error *error); extern const BusObjectImplementation home_object; diff --git a/src/home/homed-home.c b/src/home/homed-home.c index 00fc7f114a..fc9dfcff24 100644 --- a/src/home/homed-home.c +++ b/src/home/homed-home.c @@ -211,6 +211,7 @@ Home *home_free(Home *h) { h->ref_event_source_please_suspend = sd_event_source_disable_unref(h->ref_event_source_please_suspend); h->ref_event_source_dont_suspend = sd_event_source_disable_unref(h->ref_event_source_dont_suspend); + h->inhibit_suspend_event_source = sd_event_source_disable_unref(h->inhibit_suspend_event_source); h->pending_operations = ordered_set_free(h->pending_operations); h->pending_event_source = sd_event_source_disable_unref(h->pending_event_source); @@ -2614,6 +2615,9 @@ static int on_home_ref_eof(sd_event_source *s, int fd, uint32_t revents, void *u if (h->ref_event_source_dont_suspend == s) h->ref_event_source_dont_suspend = sd_event_source_disable_unref(h->ref_event_source_dont_suspend); + if (h->inhibit_suspend_event_source == s) + h->inhibit_suspend_event_source = sd_event_source_disable_unref(h->inhibit_suspend_event_source); + if (home_is_referenced(h)) return 0; @@ -2629,25 +2633,42 @@ static int on_home_ref_eof(sd_event_source *s, int fd, uint32_t revents, void *u return 0; } -int home_create_fifo(Home *h, bool please_suspend) { +int home_create_fifo(Home *h, HomeFifoType type) { + static const struct { + const char *suffix; + const char *description; + size_t event_source; + } table[_HOME_FIFO_TYPE_MAX] = { + [HOME_FIFO_PLEASE_SUSPEND] = { + .suffix = ".please-suspend", + .description = "acquire-ref", + .event_source = offsetof(Home, ref_event_source_please_suspend), + }, + [HOME_FIFO_DONT_SUSPEND] = { + .suffix = ".dont-suspend", + .description = "acquire-ref-dont-suspend", + .event_source = offsetof(Home, ref_event_source_dont_suspend), + }, + [HOME_FIFO_INHIBIT_SUSPEND] = { + .suffix = ".inhibit-suspend", + .description = "inhibit-suspend", + .event_source = offsetof(Home, inhibit_suspend_event_source), + }, + }; + _cleanup_close_ int ret_fd = -EBADF; - sd_event_source **ss; - const char *fn, *suffix; + sd_event_source **evt; + const char *fn; int r; assert(h); + assert(type >= 0 && type < _HOME_FIFO_TYPE_MAX); - if (please_suspend) { - suffix = ".please-suspend"; - ss = &h->ref_event_source_please_suspend; - } else { - suffix = ".dont-suspend"; - ss = &h->ref_event_source_dont_suspend; - } + evt = (sd_event_source**) ((uint8_t*) h + table[type].event_source); - fn = strjoina("/run/systemd/home/", h->user_name, suffix); + fn = strjoina("/run/systemd/home/", h->user_name, table[type].suffix); - if (!*ss) { + if (!*evt) { _cleanup_close_ int ref_fd = -EBADF; (void) mkdir("/run/systemd/home/", 0755); @@ -2658,20 +2679,19 @@ int home_create_fifo(Home *h, bool please_suspend) { if (ref_fd < 0) return log_error_errno(errno, "Failed to open FIFO %s for reading: %m", fn); - r = sd_event_add_io(h->manager->event, ss, ref_fd, 0, on_home_ref_eof, h); + r = sd_event_add_io(h->manager->event, evt, ref_fd, 0, on_home_ref_eof, h); if (r < 0) return log_error_errno(r, "Failed to allocate reference FIFO event source: %m"); - (void) sd_event_source_set_description(*ss, "acquire-ref"); + (void) sd_event_source_set_description(*evt, table[type].description); - r = sd_event_source_set_priority(*ss, SD_EVENT_PRIORITY_IDLE-1); + r = sd_event_source_set_priority(*evt, SD_EVENT_PRIORITY_IDLE-1); if (r < 0) return r; - r = sd_event_source_set_io_fd_own(*ss, true); + r = sd_event_source_set_io_fd_own(*evt, true); if (r < 0) return log_error_errno(r, "Failed to pass ownership of FIFO event fd to event source: %m"); - TAKE_FD(ref_fd); } @@ -2751,8 +2771,10 @@ bool home_is_referenced(Home *h) { bool home_shall_suspend(Home *h) { assert(h); - /* Suspend if there's at least one client referencing this home directory that wants a suspend and none who does not. */ - return h->ref_event_source_please_suspend && !h->ref_event_source_dont_suspend; + /* We lock the home area on suspend if... */ + return h->ref_event_source_please_suspend && /* at least one client supports suspend, and... */ + !h->ref_event_source_dont_suspend && /* no clients lack support for suspend, and... */ + !h->inhibit_suspend_event_source; /* no client is temporarily inhibiting suspend */ } static int home_dispatch_release(Home *h, Operation *o) { diff --git a/src/home/homed-home.h b/src/home/homed-home.h index 1226c0c6ba..b8b0046422 100644 --- a/src/home/homed-home.h +++ b/src/home/homed-home.h @@ -138,12 +138,17 @@ struct Home { bool unregister_on_failure; /* The reading side of a FIFO stored in /run/systemd/home/, the writing side being used for reference - * counting. The references dropped to zero as soon as we see EOF. This concept exists twice: once - * for clients that are fine if we suspend the home directory on system suspend, and once for clients - * that are not ok with that. This allows us to determine for each home whether there are any clients - * that support unsuspend. */ + * counting. The references dropped to zero as soon as we see EOF. This concept exists thrice: once + * for clients that are fine if we lock the home directory on system suspend, once for clients + * that are not ok with that, and once for clients that are usually ok with it but temporarily + * want to opt-out so that they can implement more advanced behavior on their own. This allows + * us to determine for each home whether there are any clients that don't support suspend at this + * moment. */ sd_event_source *ref_event_source_please_suspend; sd_event_source *ref_event_source_dont_suspend; + /* This is distinct from ref_event_source_dont_suspend because it can be obtained from unprivileged + * code, and thus we don't count it as a reference on the home area. */ + sd_event_source *inhibit_suspend_event_source; /* Any pending operations we still need to execute. These are for operations we want to queue if we * can't execute them right-away. */ @@ -209,7 +214,15 @@ int home_killall(Home *h); int home_augment_status(Home *h, UserRecordLoadFlags flags, UserRecord **ret); -int home_create_fifo(Home *h, bool please_suspend); +typedef enum { + HOME_FIFO_PLEASE_SUSPEND, + HOME_FIFO_DONT_SUSPEND, + HOME_FIFO_INHIBIT_SUSPEND, + _HOME_FIFO_TYPE_MAX, + _HOME_FIFO_TYPE_INVALID = -EINVAL, +} HomeFifoType; +int home_create_fifo(Home *h, HomeFifoType mode); + int home_schedule_operation(Home *h, Operation *o, sd_bus_error *error); int home_auto_login(Home *h, char ***ret_seats); diff --git a/src/home/homed-manager-bus.c b/src/home/homed-manager-bus.c index d45e0f1238..c613eed4d5 100644 --- a/src/home/homed-manager-bus.c +++ b/src/home/homed-manager-bus.c @@ -591,6 +591,10 @@ static int method_release_home(sd_bus_message *message, void *userdata, sd_bus_e return generic_home_method(userdata, message, bus_home_method_release, error); } +static int method_inhibit_suspend_home(sd_bus_message *message, void *userdata, sd_bus_error *error) { + return generic_home_method(userdata, message, bus_home_method_inhibit_suspend, error); +} + static int method_lock_all_homes(sd_bus_message *message, void *userdata, sd_bus_error *error) { _cleanup_(operation_unrefp) Operation *o = NULL; bool waiting = false; @@ -845,6 +849,12 @@ static const sd_bus_vtable manager_vtable[] = { method_release_home, 0), + SD_BUS_METHOD_WITH_ARGS("InhibitSuspendHome", + SD_BUS_ARGS("s", user_name), + SD_BUS_RESULT("h", send_fd), + method_inhibit_suspend_home, + SD_BUS_VTABLE_UNPRIVILEGED), + /* 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), diff --git a/src/home/org.freedesktop.home1.conf b/src/home/org.freedesktop.home1.conf index 5af1a68607..6d13535f95 100644 --- a/src/home/org.freedesktop.home1.conf +++ b/src/home/org.freedesktop.home1.conf @@ -123,6 +123,10 @@ <allow send_destination="org.freedesktop.home1" send_interface="org.freedesktop.home1.Manager" + send_member="InhibitSuspendHome"/> + + <allow send_destination="org.freedesktop.home1" + send_interface="org.freedesktop.home1.Manager" send_member="LockAllHomes"/> <allow send_destination="org.freedesktop.home1" @@ -195,6 +199,10 @@ send_interface="org.freedesktop.home1.Home" send_member="Release"/> + <allow send_destination="org.freedesktop.home1" + send_interface="org.freedesktop.home1.Home" + send_member="InhibitSuspend"/> + <allow receive_sender="org.freedesktop.home1"/> </policy> diff --git a/src/home/org.freedesktop.home1.policy b/src/home/org.freedesktop.home1.policy index a337b32237..2ac710d66c 100644 --- a/src/home/org.freedesktop.home1.policy +++ b/src/home/org.freedesktop.home1.policy @@ -69,4 +69,13 @@ </defaults> </action> + <action id="org.freedesktop.home1.inhibit-suspend"> + <description gettext-domain="systemd">Inhibit automatic lock of a home area</description> + <message gettext-domain="systemd">Authentication is required to inhibit automatic lock of a user's home area.</message> + <defaults> + <allow_any>auth_admin_keep</allow_any> + <allow_inactive>auth_admin_keep</allow_inactive> + <allow_active>auth_admin_keep</allow_active> + </defaults> + </action> </policyconfig> |