seat: Open/close tty on activation/deactivation

The VT and KD ioctl's are picky about the tty fd used. In order to
satisfy these, and to improve state cleanup, we now only and store the
current tty when opening a client, and use this fd to perform teardown
later. The presence of the fd is also used to signal that teardown is
needed.
This commit is contained in:
Kenny Levinsen 2020-08-02 20:21:19 +02:00
parent b731b18e0a
commit b751481e5c
4 changed files with 129 additions and 183 deletions

View file

@ -1,26 +1,25 @@
#include "string.h"
#include <errno.h> #include <errno.h>
#include <fcntl.h> #include <fcntl.h>
#include <signal.h> #include <signal.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdio.h> #include <stdio.h>
#include <string.h>
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <unistd.h> #include <unistd.h>
#if defined(__linux__) #if defined(__linux__)
#include <linux/kd.h> #include <linux/kd.h>
#include <linux/vt.h> #include <linux/vt.h>
#define TTY0 "/dev/tty0"
#define TTYF "/dev/tty%d" #define TTYF "/dev/tty%d"
#define K_ON K_UNICODE #define K_ENABLE K_UNICODE
#define K_DISABLE K_OFF
#define FRSIG 0 #define FRSIG 0
#elif defined(__FreeBSD__) #elif defined(__FreeBSD__)
#include <sys/consio.h> #include <sys/consio.h>
#include <sys/kbio.h> #include <sys/kbio.h>
#define TTY0 "/dev/ttyv0"
#define TTYF "/dev/ttyv%d" #define TTYF "/dev/ttyv%d"
#define K_ON K_XLATE #define K_ENABLE K_XLATE
#define K_OFF K_CODE #define K_DISABLE K_CODE
#define FRSIG SIGIO #define FRSIG SIGIO
#else #else
#error Unsupported platform #error Unsupported platform
@ -31,13 +30,21 @@
#define TTYPATHLEN 64 #define TTYPATHLEN 64
int terminal_current_vt(void) { int terminal_open(int vt) {
int fd = open(TTY0, O_RDWR | O_NOCTTY); char path[TTYPATHLEN];
if (fd == -1) { if (snprintf(path, TTYPATHLEN, TTYF, vt) == -1) {
log_errorf("could not open tty0: %s", strerror(errno)); log_errorf("could not generate tty path: %s", strerror(errno));
return -1; return -1;
} }
int fd = open(path, O_RDWR | O_NOCTTY);
if (fd == -1) {
log_errorf("could not open target tty: %s", strerror(errno));
return -1;
}
return fd;
}
int terminal_current_vt(int fd) {
#if defined(__linux__) #if defined(__linux__)
struct vt_stat st; struct vt_stat st;
int res = ioctl(fd, VT_GETSTATE, &st); int res = ioctl(fd, VT_GETSTATE, &st);
@ -61,173 +68,57 @@ int terminal_current_vt(void) {
#endif #endif
} }
int terminal_setup(int vt) { int terminal_set_process_switching(int fd, bool enable) {
log_debugf("setting up vt %d", vt); log_debug("setting process switching");
if (vt == -1) { struct vt_mode mode = {
vt = 0; .mode = enable ? VT_PROCESS : VT_AUTO,
}
char path[TTYPATHLEN];
if (snprintf(path, TTYPATHLEN, TTYF, vt) == -1) {
log_errorf("could not generate tty path: %s", strerror(errno));
return -1;
}
int fd = open(path, O_RDWR | O_NOCTTY);
if (fd == -1) {
log_errorf("could not open target tty: %s", strerror(errno));
return -1;
}
static struct vt_mode mode = {
.mode = VT_PROCESS,
.waitv = 0, .waitv = 0,
.relsig = SIGUSR1, .relsig = enable ? SIGUSR1 : 0,
.acqsig = SIGUSR2, .acqsig = enable ? SIGUSR2 : 0,
.frsig = FRSIG, .frsig = FRSIG,
}; };
int res = ioctl(fd, VT_SETMODE, &mode);
close(fd);
if (res == -1) {
log_errorf("could not set VT mode: %s", strerror(errno));
}
return res;
}
int terminal_teardown(int vt) {
log_debugf("tearing down vt %d", vt);
if (vt == -1) {
vt = 0;
}
char path[TTYPATHLEN];
if (snprintf(path, TTYPATHLEN, TTYF, vt) == -1) {
log_errorf("could not generate tty path: %s", strerror(errno));
return -1;
}
int fd = open(path, O_RDWR | O_NOCTTY);
if (fd == -1) {
log_errorf("could not open target tty: %s", strerror(errno));
return -1;
}
if (ioctl(fd, KDSETMODE, KD_TEXT) == -1) {
log_errorf("could not set KD graphics mode: %s", strerror(errno));
close(fd);
return -1;
}
if (ioctl(fd, KDSKBMODE, K_ON) == -1) {
log_errorf("could not set KD keyboard mode: %s", strerror(errno));
close(fd);
return -1;
}
static struct vt_mode mode = {
.mode = VT_PROCESS,
.waitv = 0,
.relsig = SIGUSR1,
.acqsig = SIGUSR2,
.frsig = FRSIG,
};
if (ioctl(fd, VT_SETMODE, &mode) == -1) { if (ioctl(fd, VT_SETMODE, &mode) == -1) {
log_errorf("could not set VT mode: %s", strerror(errno)); log_errorf("could not set VT mode: %s", strerror(errno));
close(fd);
return -1; return -1;
} }
close(fd);
return 0; return 0;
} }
int terminal_switch_vt(int vt) { int terminal_switch_vt(int fd, int vt) {
log_debugf("switching to vt %d", vt); log_debugf("switching to vt %d", vt);
int fd = open(TTY0, O_RDWR | O_NOCTTY);
if (fd == -1) {
log_errorf("could not open tty0: %s", strerror(errno));
return -1;
}
static struct vt_mode mode = {
.mode = VT_PROCESS,
.waitv = 0,
.relsig = SIGUSR1,
.acqsig = SIGUSR2,
.frsig = FRSIG,
};
if (ioctl(fd, VT_SETMODE, &mode) == -1) {
log_errorf("could not set VT mode: %s", strerror(errno));
close(fd);
return -1;
}
if (ioctl(fd, VT_ACTIVATE, vt) == -1) { if (ioctl(fd, VT_ACTIVATE, vt) == -1) {
log_errorf("could not activate VT: %s", strerror(errno)); log_errorf("could not activate VT: %s", strerror(errno));
close(fd);
return -1; return -1;
} }
close(fd);
return 0; return 0;
} }
int terminal_ack_switch(void) { int terminal_ack_switch(int fd) {
log_debug("acking vt switch"); log_debug("acking vt switch");
int fd = open(TTY0, O_RDWR | O_NOCTTY); if (ioctl(fd, VT_RELDISP, VT_ACKACQ) == -1) {
if (fd == -1) {
log_errorf("could not open tty0: %s", strerror(errno));
return -1;
}
int res = ioctl(fd, VT_RELDISP, VT_ACKACQ);
close(fd);
if (res == -1) {
log_errorf("could not ack VT switch: %s", strerror(errno)); log_errorf("could not ack VT switch: %s", strerror(errno));
}
return res;
}
int terminal_set_keyboard(int vt, bool enable) {
log_debugf("setting KD keyboard state to %d on vt %d", enable, vt);
if (vt == -1) {
vt = 0;
}
char path[TTYPATHLEN];
if (snprintf(path, TTYPATHLEN, TTYF, vt) == -1) {
log_errorf("could not generate tty path: %s", strerror(errno));
return -1; return -1;
} }
int fd = open(path, O_RDWR | O_NOCTTY);
if (fd == -1) { return 0;
log_errorf("could not generate tty path: %s", strerror(errno));
return -1;
} }
int res = ioctl(fd, KDSKBMODE, enable ? K_ON : K_OFF);
close(fd); int terminal_set_keyboard(int fd, bool enable) {
if (res == -1) { log_debugf("setting KD keyboard state to %d", enable);
if (ioctl(fd, KDSKBMODE, enable ? K_ENABLE : K_DISABLE) == -1) {
log_errorf("could not set KD keyboard mode: %s", strerror(errno)); log_errorf("could not set KD keyboard mode: %s", strerror(errno));
return -1;
} }
return res; return 0;
} }
int terminal_set_graphics(int vt, bool enable) { int terminal_set_graphics(int fd, bool enable) {
log_debugf("setting KD graphics state to %d on vt %d", enable, vt); log_debugf("setting KD graphics state to %d", enable);
if (vt == -1) { if (ioctl(fd, KDSETMODE, enable ? KD_GRAPHICS : KD_TEXT) == -1) {
vt = 0;
}
char path[TTYPATHLEN];
if (snprintf(path, TTYPATHLEN, TTYF, vt) == -1) {
log_errorf("could not generate tty path: %s", strerror(errno));
return -1;
}
int fd = open(path, O_RDWR | O_NOCTTY);
if (fd == -1) {
log_errorf("could not generate tty path: %s", strerror(errno));
return -1;
}
int res = ioctl(fd, KDSETMODE, enable ? KD_GRAPHICS : KD_TEXT);
close(fd);
if (res == -1) {
log_errorf("could not set KD graphics mode: %s", strerror(errno)); log_errorf("could not set KD graphics mode: %s", strerror(errno));
return -1;
} }
return res; return 0;
} }

View file

@ -32,6 +32,7 @@ struct seat {
bool vt_bound; bool vt_bound;
bool vt_pending_ack; bool vt_pending_ack;
int next_vt; int next_vt;
int curttyfd;
}; };
struct seat *seat_create(const char *name, bool vt_bound); struct seat *seat_create(const char *name, bool vt_bound);

View file

@ -3,12 +3,15 @@
#include <stdbool.h> #include <stdbool.h>
int terminal_open(int vt);
int terminal_set_process_switching(int fd, bool enable);
int terminal_setup(int vt); int terminal_setup(int vt);
int terminal_teardown(int vt); int terminal_teardown(int vt);
int terminal_current_vt(void); int terminal_current_vt(int fd);
int terminal_switch_vt(int vt); int terminal_switch_vt(int fd, int vt);
int terminal_ack_switch(void); int terminal_ack_switch(int fd);
int terminal_set_keyboard(int vt, bool enable); int terminal_set_keyboard(int fd, bool enable);
int terminal_set_graphics(int vt, bool enable); int terminal_set_graphics(int fd, bool enable);
#endif #endif

View file

@ -25,13 +25,12 @@ struct seat *seat_create(const char *seat_name, bool vt_bound) {
} }
list_init(&seat->clients); list_init(&seat->clients);
seat->vt_bound = vt_bound; seat->vt_bound = vt_bound;
seat->curttyfd = -1;
seat->seat_name = strdup(seat_name); seat->seat_name = strdup(seat_name);
if (seat->seat_name == NULL) { if (seat->seat_name == NULL) {
free(seat); free(seat);
return NULL; return NULL;
} }
log_debugf("created seat '%s' (vt_bound: %d)", seat_name, vt_bound); log_debugf("created seat '%s' (vt_bound: %d)", seat_name, vt_bound);
return seat; return seat;
} }
@ -44,6 +43,7 @@ void seat_destroy(struct seat *seat) {
assert(client->seat); assert(client->seat);
client_kill(client); client_kill(client);
} }
assert(seat->curttyfd == -1);
free(seat->seat_name); free(seat->seat_name);
free(seat); free(seat);
@ -331,7 +331,18 @@ int seat_open_client(struct seat *seat, struct client *client) {
assert(client); assert(client);
if (seat->vt_bound && client->seat_vt == 0) { if (seat->vt_bound && client->seat_vt == 0) {
client->seat_vt = terminal_current_vt(); int tty0fd = terminal_open(0);
if (tty0fd == -1) {
log_errorf("unable to open tty0: %s", strerror(errno));
return -1;
}
client->seat_vt = terminal_current_vt(tty0fd);
close(tty0fd);
if (client->seat_vt == -1) {
log_errorf("unable to get current VT for client: %s", strerror(errno));
client->seat_vt = 0;
return -1;
}
} }
if (seat->active_client != NULL) { if (seat->active_client != NULL) {
@ -341,8 +352,16 @@ int seat_open_client(struct seat *seat, struct client *client) {
} }
if (seat->vt_bound) { if (seat->vt_bound) {
terminal_setup(client->seat_vt); int ttyfd = terminal_open(client->seat_vt);
terminal_set_keyboard(client->seat_vt, false); if (ttyfd == -1) {
log_errorf("unable to open tty for vt %d: %s", client->seat_vt,
strerror(errno));
return -1;
}
terminal_set_process_switching(ttyfd, true);
terminal_set_keyboard(ttyfd, false);
terminal_set_graphics(ttyfd, true);
seat->curttyfd = ttyfd;
} }
for (size_t idx = 0; idx < client->devices.length; idx++) { for (size_t idx = 0; idx < client->devices.length; idx++) {
@ -387,18 +406,20 @@ int seat_close_client(struct seat *seat, struct client *client) {
log_debugf("deactivated %zd devices", client->devices.length); log_debugf("deactivated %zd devices", client->devices.length);
int vt = seat->active_client->seat_vt;
seat->active_client = NULL; seat->active_client = NULL;
if (seat->vt_bound) { if (seat->vt_bound && seat->vt_pending_ack) {
if (seat->vt_pending_ack) {
log_debug("acking pending VT switch"); log_debug("acking pending VT switch");
seat->vt_pending_ack = false; seat->vt_pending_ack = false;
terminal_teardown(vt); assert(seat->curttyfd != -1);
terminal_ack_switch(); terminal_set_process_switching(seat->curttyfd, true);
terminal_set_keyboard(seat->curttyfd, true);
terminal_set_graphics(seat->curttyfd, false);
terminal_ack_switch(seat->curttyfd);
close(seat->curttyfd);
seat->curttyfd = -1;
return 0; return 0;
} }
}
seat_activate(seat); seat_activate(seat);
log_debug("closed client"); log_debug("closed client");
@ -457,17 +478,31 @@ int seat_activate(struct seat *seat) {
return 0; return 0;
} }
int vt = -1;
if (seat->vt_bound) {
int ttyfd = terminal_open(0);
if (ttyfd == -1) {
log_errorf("unable to open tty0: %s", strerror(errno));
return -1;
}
// If we're asked to do a simple VT switch, do that // If we're asked to do a simple VT switch, do that
if (seat->vt_bound && seat->next_vt > 0) { if (seat->next_vt > 0) {
log_info("executing VT switch"); log_info("executing VT switch");
terminal_switch_vt(seat->next_vt); terminal_switch_vt(ttyfd, seat->next_vt);
seat->next_vt = 0; seat->next_vt = 0;
close(ttyfd);
return 0; return 0;
} }
int vt = -1; // We'll need the VT below
if (seat->vt_bound) { vt = terminal_current_vt(ttyfd);
vt = terminal_current_vt(); if (vt == -1) {
log_errorf("unable to get vt: %s", strerror(errno));
close(ttyfd);
return -1;
}
close(ttyfd);
} }
// Try to pick a client for activation // Try to pick a client for activation
@ -492,15 +527,25 @@ int seat_activate(struct seat *seat) {
if (next_client == NULL) { if (next_client == NULL) {
// No suitable client found // No suitable client found
log_info("no client suitable for activation"); log_info("no client suitable for activation");
if (seat->vt_bound) { if (seat->vt_bound && seat->curttyfd != -1) {
terminal_teardown(vt); terminal_set_process_switching(seat->curttyfd, false);
terminal_set_keyboard(seat->curttyfd, true);
terminal_set_graphics(seat->curttyfd, false);
close(seat->curttyfd);
seat->curttyfd = -1;
} }
return -1; return -1;
} }
log_info("activating next client"); log_info("activating next client");
if (seat->vt_bound && next_client->seat_vt != vt) { if (seat->vt_bound && next_client->seat_vt != vt) {
terminal_switch_vt(next_client->seat_vt); int ttyfd = terminal_open(0);
if (ttyfd == -1) {
log_errorf("unable to open tty0: %s", strerror(errno));
return -1;
}
terminal_switch_vt(ttyfd, next_client->seat_vt);
close(ttyfd);
} }
return seat_open_client(seat, next_client); return seat_open_client(seat, next_client);
@ -511,7 +556,13 @@ int seat_prepare_vt_switch(struct seat *seat) {
if (seat->active_client == NULL) { if (seat->active_client == NULL) {
log_info("no active client, performing switch immediately"); log_info("no active client, performing switch immediately");
terminal_ack_switch(); int tty0fd = terminal_open(0);
if (tty0fd == -1) {
log_errorf("unable to open tty0: %s", strerror(errno));
return -1;
}
terminal_ack_switch(tty0fd);
close(tty0fd);
return 0; return 0;
} }