summaryrefslogtreecommitdiffstats
path: root/src/login/logind-session-dbus.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/login/logind-session-dbus.c')
-rw-r--r--src/login/logind-session-dbus.c149
1 files changed, 123 insertions, 26 deletions
diff --git a/src/login/logind-session-dbus.c b/src/login/logind-session-dbus.c
index a136ae4..f20ef4f 100644
--- a/src/login/logind-session-dbus.c
+++ b/src/login/logind-session-dbus.c
@@ -158,13 +158,12 @@ int bus_session_method_terminate(sd_bus_message *message, void *userdata, sd_bus
assert(message);
- r = bus_verify_polkit_async(
+ r = bus_verify_polkit_async_full(
message,
- CAP_KILL,
"org.freedesktop.login1.manage",
- NULL,
- false,
+ /* details= */ NULL,
s->user->user_record->uid,
+ /* flags= */ 0,
&s->manager->polkit_registry,
error);
if (r < 0)
@@ -204,13 +203,12 @@ int bus_session_method_lock(sd_bus_message *message, void *userdata, sd_bus_erro
assert(message);
- r = bus_verify_polkit_async(
+ r = bus_verify_polkit_async_full(
message,
- CAP_SYS_ADMIN,
"org.freedesktop.login1.lock-sessions",
- NULL,
- false,
+ /* details= */ NULL,
s->user->user_record->uid,
+ /* flags= */ 0,
&s->manager->polkit_registry,
error);
if (r < 0)
@@ -218,7 +216,9 @@ int bus_session_method_lock(sd_bus_message *message, void *userdata, sd_bus_erro
if (r == 0)
return 1; /* Will call us back */
- r = session_send_lock(s, strstr(sd_bus_message_get_member(message), "Lock"));
+ r = session_send_lock(s, /* lock= */ strstr(sd_bus_message_get_member(message), "Lock"));
+ if (r == -ENOTTY)
+ return sd_bus_error_set(error, SD_BUS_ERROR_NOT_SUPPORTED, "Session does not support lock screen.");
if (r < 0)
return r;
@@ -250,7 +250,7 @@ static int method_set_idle_hint(sd_bus_message *message, void *userdata, sd_bus_
r = session_set_idle_hint(s, b);
if (r == -ENOTTY)
- return sd_bus_error_set(error, SD_BUS_ERROR_NOT_SUPPORTED, "Idle hint control is not supported on non-graphical sessions.");
+ return sd_bus_error_set(error, SD_BUS_ERROR_NOT_SUPPORTED, "Idle hint control is not supported on non-graphical and non-user sessions.");
if (r < 0)
return r;
@@ -280,7 +280,11 @@ static int method_set_locked_hint(sd_bus_message *message, void *userdata, sd_bu
if (uid != 0 && uid != s->user->user_record->uid)
return sd_bus_error_set(error, SD_BUS_ERROR_ACCESS_DENIED, "Only owner of session may set locked hint");
- session_set_locked_hint(s, b);
+ r = session_set_locked_hint(s, b);
+ if (r == -ENOTTY)
+ return sd_bus_error_set(error, SD_BUS_ERROR_NOT_SUPPORTED, "Session does not support lock screen.");
+ if (r < 0)
+ return r;
return sd_bus_reply_method_return(message, NULL);
}
@@ -309,13 +313,12 @@ int bus_session_method_kill(sd_bus_message *message, void *userdata, sd_bus_erro
if (!SIGNAL_VALID(signo))
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid signal %i", signo);
- r = bus_verify_polkit_async(
+ r = bus_verify_polkit_async_full(
message,
- CAP_KILL,
"org.freedesktop.login1.manage",
- NULL,
- false,
+ /* details= */ NULL,
s->user->user_record->uid,
+ /* flags= */ 0,
&s->manager->polkit_registry,
error);
if (r < 0)
@@ -390,6 +393,9 @@ static int method_set_type(sd_bus_message *message, void *userdata, sd_bus_error
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
"Invalid session type '%s'", t);
+ if (!SESSION_CLASS_CAN_CHANGE_TYPE(s->class))
+ return sd_bus_error_set(error, SD_BUS_ERROR_NOT_SUPPORTED, "Session class doesn't support changing type.");
+
if (!session_is_controller(s, sd_bus_message_get_sender(message)))
return sd_bus_error_set(error, BUS_ERROR_NOT_IN_CONTROL, "You must be in control of this session to set type");
@@ -398,6 +404,62 @@ static int method_set_type(sd_bus_message *message, void *userdata, sd_bus_error
return sd_bus_reply_method_return(message, NULL);
}
+static int method_set_class(sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ _cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
+ Session *s = ASSERT_PTR(userdata);
+ SessionClass class;
+ const char *c;
+ uid_t uid;
+ int r;
+
+ assert(message);
+
+ r = sd_bus_message_read(message, "s", &c);
+ if (r < 0)
+ return r;
+
+ class = session_class_from_string(c);
+ if (class < 0)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
+ "Invalid session class '%s'", c);
+
+ /* For now, we'll allow only upgrades user-incomplete → user */
+ if (class != SESSION_USER)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
+ "Class may only be set to 'user'");
+
+ if (s->class == SESSION_USER) /* No change, shortcut */
+ return sd_bus_reply_method_return(message, NULL);
+ if (s->class != SESSION_USER_INCOMPLETE)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
+ "Only sessions with class 'user-incomplete' may change class");
+
+ if (s->upgrade_message)
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS,
+ "Set session class operation already in progress");
+
+ r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_EUID, &creds);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_creds_get_euid(creds, &uid);
+ if (r < 0)
+ return r;
+
+ if (uid != 0 && uid != s->user->user_record->uid)
+ return sd_bus_error_set(error, SD_BUS_ERROR_ACCESS_DENIED, "Only owner of session may change its class");
+
+ session_set_class(s, class);
+
+ s->upgrade_message = sd_bus_message_ref(message);
+
+ r = session_send_upgrade_reply(s, /* error= */ NULL);
+ if (r < 0)
+ return r;
+
+ return 1;
+}
+
static int method_set_display(sd_bus_message *message, void *userdata, sd_bus_error *error) {
Session *s = ASSERT_PTR(userdata);
const char *display;
@@ -473,6 +535,9 @@ static int method_take_device(sd_bus_message *message, void *userdata, sd_bus_er
if (!DEVICE_MAJOR_VALID(major) || !DEVICE_MINOR_VALID(minor))
return sd_bus_error_set(error, SD_BUS_ERROR_INVALID_ARGS, "Device major/minor is not valid.");
+ if (!SESSION_CLASS_CAN_TAKE_DEVICE(s->class))
+ return sd_bus_error_set(error, SD_BUS_ERROR_NOT_SUPPORTED, "Session class doesn't support taking device control.");
+
if (!session_is_controller(s, sd_bus_message_get_sender(message)))
return sd_bus_error_set(error, BUS_ERROR_NOT_IN_CONTROL, "You are not in control of this session");
@@ -768,6 +833,9 @@ int session_send_lock(Session *s, bool lock) {
assert(s);
+ if (!SESSION_CLASS_CAN_LOCK(s->class))
+ return -ENOTTY;
+
p = session_bus_path(s);
if (!p)
return -ENOMEM;
@@ -789,6 +857,9 @@ int session_send_lock_all(Manager *m, bool lock) {
HASHMAP_FOREACH(session, m->sessions) {
int k;
+ if (!SESSION_CLASS_CAN_LOCK(session->class))
+ continue;
+
k = session_send_lock(session, lock);
if (k < 0)
r = k;
@@ -797,20 +868,23 @@ int session_send_lock_all(Manager *m, bool lock) {
return r;
}
-static bool session_ready(Session *s) {
+static bool session_job_pending(Session *s) {
assert(s);
+ assert(s->user);
- /* Returns true when the session is ready, i.e. all jobs we enqueued for it are done (regardless if successful or not) */
+ /* Check if we have some jobs enqueued and not finished yet. Each time we get JobRemoved signal about
+ * relevant units, session_send_create_reply and hence us is called (see match_job_removed).
+ * Note that we don't care about job result here. */
- return !s->scope_job &&
- !s->user->service_job;
+ return s->scope_job ||
+ s->user->runtime_dir_job ||
+ (SESSION_CLASS_WANTS_SERVICE_MANAGER(s->class) && s->user->service_manager_job);
}
int session_send_create_reply(Session *s, sd_bus_error *error) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *c = NULL;
_cleanup_close_ int fifo_fd = -EBADF;
_cleanup_free_ char *p = NULL;
- int r;
assert(s);
@@ -820,7 +894,9 @@ int session_send_create_reply(Session *s, sd_bus_error *error) {
if (!s->create_message)
return 0;
- if (!sd_bus_error_is_set(error) && !session_ready(s))
+ /* If error occurred, return it immediately. Otherwise let's wait for all jobs to finish before
+ * continuing. */
+ if (!sd_bus_error_is_set(error) && session_job_pending(s))
return 0;
c = TAKE_PTR(s->create_message);
@@ -831,10 +907,6 @@ int session_send_create_reply(Session *s, sd_bus_error *error) {
if (fifo_fd < 0)
return fifo_fd;
- r = session_watch_pidfd(s);
- if (r < 0)
- return r;
-
/* Update the session state file before we notify the client about the result. */
session_save(s);
@@ -865,6 +937,26 @@ int session_send_create_reply(Session *s, sd_bus_error *error) {
false);
}
+int session_send_upgrade_reply(Session *s, sd_bus_error *error) {
+ _cleanup_(sd_bus_message_unrefp) sd_bus_message *c = NULL;
+ assert(s);
+
+ if (!s->upgrade_message)
+ return 0;
+
+ /* See comments in session_send_create_reply */
+ if (!sd_bus_error_is_set(error) && session_job_pending(s))
+ return 0;
+
+ c = TAKE_PTR(s->upgrade_message);
+ if (error)
+ return sd_bus_reply_method_error(c, error);
+
+ session_save(s);
+
+ return sd_bus_reply_method_return(c, NULL);
+}
+
static const sd_bus_vtable session_vtable[] = {
SD_BUS_VTABLE_START(0),
@@ -885,7 +977,7 @@ static const sd_bus_vtable session_vtable[] = {
SD_BUS_PROPERTY("Leader", "u", bus_property_get_pid, offsetof(Session, leader.pid), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("Audit", "u", NULL, offsetof(Session, audit_id), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("Type", "s", property_get_type, offsetof(Session, type), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
- SD_BUS_PROPERTY("Class", "s", property_get_class, offsetof(Session, class), SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("Class", "s", property_get_class, offsetof(Session, class), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("Active", "b", property_get_active, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("State", "s", property_get_state, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("IdleHint", "b", property_get_idle_hint, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
@@ -943,6 +1035,11 @@ static const sd_bus_vtable session_vtable[] = {
SD_BUS_NO_RESULT,
method_set_type,
SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD_WITH_ARGS("SetClass",
+ SD_BUS_ARGS("s", class),
+ SD_BUS_NO_RESULT,
+ method_set_class,
+ SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD_WITH_ARGS("SetDisplay",
SD_BUS_ARGS("s", display),
SD_BUS_NO_RESULT,