X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;f=src%2Flogin%2Flogind-seat.c;h=3f5efdc8ea706f8195b463163f838a5d4d34fd75;hb=f39d4a08e746e703d562076a0f622eb91dbdcd3e;hp=eac5a5f26b144de064d61ea0ea0a9183a52c248f;hpb=92bd5ff3a062c3f9475b9d9d39b9335bfeb7705e;p=elogind.git diff --git a/src/login/logind-seat.c b/src/login/logind-seat.c index eac5a5f26..3f5efdc8e 100644 --- a/src/login/logind-seat.c +++ b/src/login/logind-seat.c @@ -51,7 +51,7 @@ Seat *seat_new(Manager *m, const char *id) { return NULL; } - s->id = path_get_file_name(s->state_file); + s->id = basename(s->state_file); s->manager = m; if (hashmap_put(m->seats, s->id, s) < 0) { @@ -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); } @@ -138,8 +139,8 @@ int seat_save(Seat *s) { fputs("UIDS=", f); LIST_FOREACH(sessions_by_seat, i, s->sessions) fprintf(f, - "%lu%c", - (unsigned long) i->user->uid, + UID_FMT"%c", + i->user->uid, i->sessions_by_seat_next ? ' ' : '\n'); } @@ -153,7 +154,7 @@ int seat_save(Seat *s) { finish: if (r < 0) - log_error("Failed to save seat data for %s: %s", s->id, strerror(-r)); + log_error("Failed to save seat data %s: %s", s->state_file, strerror(-r)); return r; } @@ -167,14 +168,12 @@ int seat_load(Seat *s) { } static int vt_allocate(unsigned int vtnr) { - _cleanup_free_ char *p = NULL; + char p[sizeof("/dev/tty") + DECIMAL_STR_MAX(unsigned int)]; _cleanup_close_ int fd = -1; assert(vtnr >= 1); - if (asprintf(&p, "/dev/tty%u", vtnr) < 0) - return -ENOMEM; - + snprintf(p, sizeof(p), "/dev/tty%u", vtnr); fd = open_terminal(p, O_RDWR|O_NOCTTY|O_CLOEXEC); if (fd < 0) return -errno; @@ -270,6 +269,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; @@ -363,7 +416,7 @@ int seat_start(Seat *s) { return 0; } -int seat_stop(Seat *s) { +int seat_stop(Seat *s, bool force) { int r = 0; assert(s); @@ -375,7 +428,7 @@ int seat_stop(Seat *s) { "MESSAGE=Removed seat %s.", s->id, NULL); - seat_stop_sessions(s); + seat_stop_sessions(s, force); unlink(s->state_file); seat_add_to_gc_queue(s); @@ -388,14 +441,14 @@ int seat_stop(Seat *s) { return r; } -int seat_stop_sessions(Seat *s) { +int seat_stop_sessions(Seat *s, bool force) { Session *session; int r = 0, k; assert(s); LIST_FOREACH(sessions_by_seat, session, s->sessions) { - k = session_stop(session); + k = session_stop(session, force); if (k < 0) r = k; } @@ -403,13 +456,69 @@ int seat_stop_sessions(Seat *s) { return r; } +void seat_evict_position(Seat *s, Session *session) { + Session *iter; + unsigned int pos = session->pos; + + session->pos = 0; + + if (!pos) + return; + + if (pos < s->position_count && s->positions[pos] == session) { + s->positions[pos] = NULL; + + /* There might be another session claiming the same + * position (eg., during gdm->session transition), so lets look + * for it and set it on the free slot. */ + LIST_FOREACH(sessions_by_seat, iter, s->sessions) { + if (iter->pos == pos) { + s->positions[pos] = iter; + break; + } + } + } +} + +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_T(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); assert(!session->seat); + if (!seat_has_vts(s) != !session->vtnr) + return -EINVAL; + session->seat = s; LIST_PREPEND(sessions_by_seat, s->sessions, session); + seat_assign_position(s, session); seat_send_changed(s, "Sessions", NULL);