summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/login/logind-seat-dbus.c56
-rw-r--r--src/login/logind-seat.c96
-rw-r--r--src/login/logind-seat.h8
-rw-r--r--src/login/logind-session.c13
-rw-r--r--src/login/logind-session.h1
5 files changed, 174 insertions, 0 deletions
diff --git a/src/login/logind-seat-dbus.c b/src/login/logind-seat-dbus.c
index 236af5eb9e..909007c30c 100644
--- a/src/login/logind-seat-dbus.c
+++ b/src/login/logind-seat-dbus.c
@@ -236,6 +236,59 @@ static int method_activate_session(sd_bus *bus, sd_bus_message *message, void *u
return sd_bus_reply_method_return(message, NULL);
}
+static int method_switch_to(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ Seat *s = userdata;
+ unsigned int to;
+ int r;
+
+ assert(bus);
+ assert(message);
+ assert(s);
+
+ r = sd_bus_message_read(message, "u", &to);
+ if (r < 0)
+ return r;
+
+ if (to <= 0)
+ return -EINVAL;
+
+ r = seat_switch_to(s, to);
+ if (r < 0)
+ return r;
+
+ return sd_bus_reply_method_return(message, NULL);
+}
+
+static int method_switch_to_next(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ Seat *s = userdata;
+ int r;
+
+ assert(bus);
+ assert(message);
+ assert(s);
+
+ r = seat_switch_to_next(s);
+ if (r < 0)
+ return r;
+
+ return sd_bus_reply_method_return(message, NULL);
+}
+
+static int method_switch_to_previous(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
+ Seat *s = userdata;
+ int r;
+
+ assert(bus);
+ assert(message);
+ assert(s);
+
+ r = seat_switch_to_previous(s);
+ if (r < 0)
+ return r;
+
+ return sd_bus_reply_method_return(message, NULL);
+}
+
const sd_bus_vtable seat_vtable[] = {
SD_BUS_VTABLE_START(0),
@@ -251,6 +304,9 @@ const sd_bus_vtable seat_vtable[] = {
SD_BUS_METHOD("Terminate", NULL, NULL, method_terminate, SD_BUS_VTABLE_CAPABILITY(CAP_KILL)),
SD_BUS_METHOD("ActivateSession", "s", NULL, method_activate_session, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("SwitchTo", "u", NULL, method_switch_to, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("SwitchToNext", NULL, NULL, method_switch_to_next, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("SwitchToPrevious", NULL, NULL, method_switch_to_previous, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_VTABLE_END
};
diff --git a/src/login/logind-seat.c b/src/login/logind-seat.c
index c73860478d..b8f18c47a3 100644
--- a/src/login/logind-seat.c
+++ b/src/login/logind-seat.c
@@ -79,6 +79,7 @@ void seat_free(Seat *s) {
hashmap_remove(s->manager->seats, s->id);
+ free(s->positions);
free(s->state_file);
free(s);
}
@@ -270,6 +271,60 @@ int seat_set_active(Seat *s, Session *session) {
return 0;
}
+int seat_switch_to(Seat *s, unsigned int num) {
+ /* Public session positions skip 0 (there is only F1-F12). Maybe it
+ * will get reassigned in the future, so return error for now. */
+ if (!num)
+ return -EINVAL;
+
+ if (num >= s->position_count || !s->positions[num])
+ return -EINVAL;
+
+ return session_activate(s->positions[num]);
+}
+
+int seat_switch_to_next(Seat *s) {
+ unsigned int start, i;
+
+ if (!s->position_count)
+ return -EINVAL;
+
+ start = 1;
+ if (s->active && s->active->pos > 0)
+ start = s->active->pos;
+
+ for (i = start + 1; i < s->position_count; ++i)
+ if (s->positions[i])
+ return session_activate(s->positions[i]);
+
+ for (i = 1; i < start; ++i)
+ if (s->positions[i])
+ return session_activate(s->positions[i]);
+
+ return -EINVAL;
+}
+
+int seat_switch_to_previous(Seat *s) {
+ unsigned int start, i;
+
+ if (!s->position_count)
+ return -EINVAL;
+
+ start = 1;
+ if (s->active && s->active->pos > 0)
+ start = s->active->pos;
+
+ for (i = start - 1; i > 0; --i)
+ if (s->positions[i])
+ return session_activate(s->positions[i]);
+
+ for (i = s->position_count - 1; i > start; --i)
+ if (s->positions[i])
+ return session_activate(s->positions[i]);
+
+ return -EINVAL;
+}
+
int seat_active_vt_changed(Seat *s, unsigned int vtnr) {
Session *i, *new_active = NULL;
int r;
@@ -403,6 +458,46 @@ int seat_stop_sessions(Seat *s) {
return r;
}
+void seat_evict_position(Seat *s, Session *session) {
+ unsigned int pos = session->pos;
+
+ session->pos = 0;
+
+ if (!pos)
+ return;
+
+ if (pos < s->position_count && s->positions[pos] == session)
+ s->positions[pos] = NULL;
+}
+
+void seat_claim_position(Seat *s, Session *session, unsigned int pos) {
+ /* with VTs, the position is always the same as the VTnr */
+ if (seat_has_vts(s))
+ pos = session->vtnr;
+
+ if (!GREEDY_REALLOC0(s->positions, s->position_count, pos + 1))
+ return;
+
+ seat_evict_position(s, session);
+
+ session->pos = pos;
+ if (pos > 0 && !s->positions[pos])
+ s->positions[pos] = session;
+}
+
+static void seat_assign_position(Seat *s, Session *session) {
+ unsigned int pos;
+
+ if (session->pos > 0)
+ return;
+
+ for (pos = 1; pos < s->position_count; ++pos)
+ if (!s->positions[pos])
+ break;
+
+ seat_claim_position(s, session, pos);
+}
+
int seat_attach_session(Seat *s, Session *session) {
assert(s);
assert(session);
@@ -413,6 +508,7 @@ int seat_attach_session(Seat *s, Session *session) {
session->seat = s;
LIST_PREPEND(sessions_by_seat, s->sessions, session);
+ seat_assign_position(s, session);
seat_send_changed(s, "Sessions", NULL);
diff --git a/src/login/logind-seat.h b/src/login/logind-seat.h
index 1254268f10..9e21e3a8a3 100644
--- a/src/login/logind-seat.h
+++ b/src/login/logind-seat.h
@@ -41,6 +41,9 @@ struct Seat {
Session *pending_switch;
LIST_HEAD(Session, sessions);
+ Session **positions;
+ size_t position_count;
+
bool in_gc_queue:1;
bool started:1;
@@ -55,12 +58,17 @@ int seat_load(Seat *s);
int seat_apply_acls(Seat *s, Session *old_active);
int seat_set_active(Seat *s, Session *session);
+int seat_switch_to(Seat *s, unsigned int num);
+int seat_switch_to_next(Seat *s);
+int seat_switch_to_previous(Seat *s);
int seat_active_vt_changed(Seat *s, unsigned int vtnr);
int seat_read_active_vt(Seat *s);
int seat_preallocate_vts(Seat *s);
int seat_attach_session(Seat *s, Session *session);
void seat_complete_switch(Seat *s);
+void seat_evict_position(Seat *s, Session *session);
+void seat_claim_position(Seat *s, Session *session, unsigned int pos);
bool seat_has_vts(Seat *s);
bool seat_is_seat0(Seat *s);
diff --git a/src/login/logind-session.c b/src/login/logind-session.c
index 4476f5820a..ff0a7a4f2e 100644
--- a/src/login/logind-session.c
+++ b/src/login/logind-session.c
@@ -125,6 +125,7 @@ void session_free(Session *s) {
if (s->seat->pending_switch == s)
s->seat->pending_switch = NULL;
+ seat_evict_position(s->seat, s);
LIST_REMOVE(sessions_by_seat, s->seat->sessions, s);
}
@@ -231,6 +232,9 @@ int session_save(Session *s) {
if (s->seat && seat_has_vts(s->seat))
fprintf(f, "VTNR=%u\n", s->vtnr);
+ if (!s->vtnr)
+ fprintf(f, "POS=%u\n", s->pos);
+
if (s->leader > 0)
fprintf(f, "LEADER=%lu\n", (unsigned long) s->leader);
@@ -266,6 +270,7 @@ int session_load(Session *s) {
_cleanup_free_ char *remote = NULL,
*seat = NULL,
*vtnr = NULL,
+ *pos = NULL,
*leader = NULL,
*type = NULL,
*class = NULL,
@@ -290,6 +295,7 @@ int session_load(Session *s) {
"REMOTE_USER", &s->remote_user,
"SERVICE", &s->service,
"VTNR", &vtnr,
+ "POS", &pos,
"LEADER", &leader,
"TYPE", &type,
"CLASS", &class,
@@ -350,6 +356,13 @@ int session_load(Session *s) {
if (!s->seat || !seat_has_vts(s->seat))
s->vtnr = 0;
+ if (pos && s->seat) {
+ unsigned int npos;
+
+ safe_atou(pos, &npos);
+ seat_claim_position(s->seat, s, npos);
+ }
+
if (leader) {
k = parse_pid(leader, &s->leader);
if (k >= 0)
diff --git a/src/login/logind-session.h b/src/login/logind-session.h
index ee931013dd..d724e20e7f 100644
--- a/src/login/logind-session.h
+++ b/src/login/logind-session.h
@@ -69,6 +69,7 @@ struct Session {
Manager *manager;
char *id;
+ unsigned int pos;
SessionType type;
SessionClass class;