mirror of
https://github.com/brl/mutter.git
synced 2024-11-13 01:36:10 -05:00
mutter-launch: augment with VT and TTY handling
Set the TTY mode appropriately at startup, and clean it up when the compositor exits. Also, take control of VT switching, including the calls to drmSetMaster and drmDropMaster as appropriate. In the future, we the kernel implements the mute evdev ioctl, we'll also make sure that input devices are appropriately released. https://bugzilla.gnome.org/show_bug.cgi?id=705861
This commit is contained in:
parent
e263b3624c
commit
96fa518576
@ -46,6 +46,7 @@
|
||||
#include <termios.h>
|
||||
#include <linux/vt.h>
|
||||
#include <linux/major.h>
|
||||
#include <linux/kd.h>
|
||||
|
||||
#include <pwd.h>
|
||||
#include <grp.h>
|
||||
@ -58,6 +59,13 @@
|
||||
#include "weston-launch.h"
|
||||
|
||||
#define MAX_ARGV_SIZE 256
|
||||
#define DRM_MAJOR 226
|
||||
|
||||
enum vt_state {
|
||||
VT_HAS_VT,
|
||||
VT_PENDING_CONFIRM,
|
||||
VT_NOT_HAVE_VT,
|
||||
};
|
||||
|
||||
struct weston_launch {
|
||||
struct pam_conv pc;
|
||||
@ -72,10 +80,18 @@ struct weston_launch {
|
||||
pid_t child;
|
||||
int verbose;
|
||||
char *new_user;
|
||||
|
||||
struct termios terminal_attributes;
|
||||
int kb_mode;
|
||||
enum vt_state vt_state;
|
||||
|
||||
int drm_fd;
|
||||
};
|
||||
|
||||
union cmsg_data { unsigned char b[4]; int fd; };
|
||||
|
||||
static void quit (struct weston_launch *wl, int status);
|
||||
|
||||
static int
|
||||
weston_launch_allowed(struct weston_launch *wl)
|
||||
{
|
||||
@ -132,6 +148,7 @@ setup_signals(struct weston_launch *wl)
|
||||
sigaddset(&mask, SIGCHLD);
|
||||
sigaddset(&mask, SIGINT);
|
||||
sigaddset(&mask, SIGTERM);
|
||||
sigaddset(&mask, SIGUSR1);
|
||||
ret = sigprocmask(SIG_BLOCK, &mask, NULL);
|
||||
assert(ret == 0);
|
||||
|
||||
@ -152,44 +169,54 @@ setenv_fd(const char *env, int fd)
|
||||
}
|
||||
|
||||
static int
|
||||
handle_setmaster(struct weston_launch *wl, struct msghdr *msg, ssize_t len)
|
||||
handle_setdrmfd(struct weston_launch *wl, struct msghdr *msg, ssize_t len)
|
||||
{
|
||||
int ret = -1;
|
||||
struct cmsghdr *cmsg;
|
||||
struct weston_launcher_set_master *message;
|
||||
union cmsg_data *data;
|
||||
struct stat s;
|
||||
|
||||
if (len != sizeof(*message)) {
|
||||
error(0, 0, "missing value in setmaster request");
|
||||
if (wl->drm_fd != -1) {
|
||||
error(0, 0, "DRM FD already set");
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
message = msg->msg_iov->iov_base;
|
||||
|
||||
cmsg = CMSG_FIRSTHDR(msg);
|
||||
if (!cmsg ||
|
||||
cmsg->cmsg_level != SOL_SOCKET ||
|
||||
cmsg->cmsg_type != SCM_RIGHTS) {
|
||||
error(0, 0, "invalid control message");
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
data = (union cmsg_data *) CMSG_DATA(cmsg);
|
||||
if (data->fd == -1) {
|
||||
if (data->fd < 0) {
|
||||
error(0, 0, "missing drm fd in socket request");
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (message->set_master)
|
||||
ret = drmSetMaster(data->fd);
|
||||
else
|
||||
ret = drmDropMaster(data->fd);
|
||||
if (fstat(data->fd, &s) < 0) {
|
||||
ret = -errno;
|
||||
goto out;
|
||||
}
|
||||
|
||||
close(data->fd);
|
||||
if (major(s.st_rdev) != DRM_MAJOR) {
|
||||
fprintf(stderr, "FD is not for DRM\n");
|
||||
ret = -EPERM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
wl->drm_fd = data->fd;
|
||||
ret = drmSetMaster(data->fd);
|
||||
if (ret < 0)
|
||||
ret = -errno;
|
||||
|
||||
if (wl->verbose)
|
||||
fprintf(stderr, "weston-launch: %sMaster, ret: %d, fd: %d\n",
|
||||
message->set_master ? "set" : "drop", ret, data->fd);
|
||||
fprintf(stderr, "weston-launch: set drm FD, ret: %d, fd: %d\n",
|
||||
ret, data->fd);
|
||||
|
||||
out:
|
||||
do {
|
||||
@ -201,6 +228,76 @@ out:
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
handle_confirm_vt_switch(struct weston_launch *wl, struct msghdr *msg, ssize_t len)
|
||||
{
|
||||
int ret = -1;
|
||||
|
||||
if (wl->vt_state != VT_PENDING_CONFIRM) {
|
||||
error(0, 0, "unexpected CONFIRM_VT_SWITCH");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (wl->drm_fd != -1) {
|
||||
int ret;
|
||||
|
||||
ret = drmDropMaster(wl->drm_fd);
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "failed to drop DRM master: %m\n");
|
||||
} else if (wl->verbose) {
|
||||
fprintf(stderr, "dropped DRM master for VT switch\n");
|
||||
}
|
||||
}
|
||||
|
||||
wl->vt_state = VT_NOT_HAVE_VT;
|
||||
ioctl(wl->tty, VT_RELDISP, 1);
|
||||
|
||||
if (wl->verbose)
|
||||
fprintf(stderr, "weston-launcher: confirmed VT switch\n");
|
||||
|
||||
ret = 0;
|
||||
|
||||
out:
|
||||
do {
|
||||
len = send(wl->sock[0], &ret, sizeof ret, 0);
|
||||
} while (len < 0 && errno == EINTR);
|
||||
if (len < 0)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
handle_activate_vt(struct weston_launch *wl, struct msghdr *msg, ssize_t len)
|
||||
{
|
||||
int ret = -1;
|
||||
struct weston_launcher_activate_vt *message;
|
||||
|
||||
if (len != sizeof(*message)) {
|
||||
error(0, 0, "missing value in activate_vt request");
|
||||
goto out;
|
||||
}
|
||||
|
||||
message = msg->msg_iov->iov_base;
|
||||
|
||||
ret = ioctl(wl->tty, VT_ACTIVATE, message->vt);
|
||||
if (ret < 0)
|
||||
ret = -errno;
|
||||
|
||||
if (wl->verbose)
|
||||
fprintf(stderr, "weston-launch: activate VT, ret: %d\n", ret);
|
||||
|
||||
out:
|
||||
do {
|
||||
len = send(wl->sock[0], &ret, sizeof ret, 0);
|
||||
} while (len < 0 && errno == EINTR);
|
||||
if (len < 0)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
handle_open(struct weston_launch *wl, struct msghdr *msg, ssize_t len)
|
||||
{
|
||||
@ -220,13 +317,16 @@ handle_open(struct weston_launch *wl, struct msghdr *msg, ssize_t len)
|
||||
/* Ensure path is null-terminated */
|
||||
((char *) message)[len-1] = '\0';
|
||||
|
||||
if (stat(message->path, &s) < 0)
|
||||
if (stat(message->path, &s) < 0) {
|
||||
ret = -errno;
|
||||
goto err0;
|
||||
}
|
||||
|
||||
fd = open(message->path, message->flags);
|
||||
if (fd < 0) {
|
||||
fprintf(stderr, "Error opening device %s: %m\n",
|
||||
message->path);
|
||||
ret = -errno;
|
||||
goto err0;
|
||||
}
|
||||
|
||||
@ -235,6 +335,7 @@ handle_open(struct weston_launch *wl, struct msghdr *msg, ssize_t len)
|
||||
fd = -1;
|
||||
fprintf(stderr, "Device %s is not an input device\n",
|
||||
message->path);
|
||||
ret = -EPERM;
|
||||
goto err0;
|
||||
}
|
||||
|
||||
@ -303,22 +404,55 @@ handle_socket_msg(struct weston_launch *wl)
|
||||
case WESTON_LAUNCHER_OPEN:
|
||||
ret = handle_open(wl, &msg, len);
|
||||
break;
|
||||
case WESTON_LAUNCHER_DRM_SET_MASTER:
|
||||
ret = handle_setmaster(wl, &msg, len);
|
||||
case WESTON_LAUNCHER_DRM_SET_FD:
|
||||
ret = handle_setdrmfd(wl, &msg, len);
|
||||
break;
|
||||
case WESTON_LAUNCHER_CONFIRM_VT_SWITCH:
|
||||
ret = handle_confirm_vt_switch(wl, &msg, len);
|
||||
break;
|
||||
case WESTON_LAUNCHER_ACTIVATE_VT:
|
||||
ret = handle_activate_vt(wl, &msg, len);
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
tty_reset(struct weston_launch *wl)
|
||||
{
|
||||
struct vt_mode mode = { 0 };
|
||||
|
||||
if (ioctl(wl->tty, KDSKBMODE, wl->kb_mode))
|
||||
fprintf(stderr, "failed to restore keyboard mode: %m\n");
|
||||
|
||||
if (ioctl(wl->tty, KDSETMODE, KD_TEXT))
|
||||
fprintf(stderr, "failed to set KD_TEXT mode on tty: %m\n");
|
||||
|
||||
if (tcsetattr(wl->tty, TCSANOW, &wl->terminal_attributes) < 0)
|
||||
fprintf(stderr, "could not restore terminal to canonical mode\n");
|
||||
|
||||
mode.mode = VT_AUTO;
|
||||
if (ioctl(wl->tty, VT_SETMODE, &mode) < 0)
|
||||
fprintf(stderr, "could not reset vt handling\n");
|
||||
}
|
||||
|
||||
static void
|
||||
quit(struct weston_launch *wl, int status)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (wl->child > 0)
|
||||
kill(wl->child, SIGKILL);
|
||||
|
||||
close(wl->signalfd);
|
||||
close(wl->sock[0]);
|
||||
|
||||
if (wl->drm_fd > 0)
|
||||
close(wl->drm_fd);
|
||||
|
||||
tty_reset(wl);
|
||||
|
||||
if (wl->new_user) {
|
||||
err = pam_close_session(wl->ph, 0);
|
||||
if (err)
|
||||
@ -330,6 +464,44 @@ quit(struct weston_launch *wl, int status)
|
||||
exit(status);
|
||||
}
|
||||
|
||||
static int
|
||||
handle_vt_switch(struct weston_launch *wl)
|
||||
{
|
||||
struct weston_launcher_message message;
|
||||
ssize_t len;
|
||||
|
||||
if (wl->vt_state == VT_HAS_VT) {
|
||||
wl->vt_state = VT_PENDING_CONFIRM;
|
||||
message.opcode = WESTON_LAUNCHER_SERVER_REQUEST_VT_SWITCH;
|
||||
} else if (wl->vt_state == VT_NOT_HAVE_VT) {
|
||||
wl->vt_state = VT_HAS_VT;
|
||||
ioctl(wl->tty, VT_RELDISP, VT_ACKACQ);
|
||||
|
||||
if (wl->drm_fd != -1) {
|
||||
int ret;
|
||||
|
||||
ret = drmSetMaster(wl->drm_fd);
|
||||
if (ret < 0) {
|
||||
fprintf(stderr, "failed to become DRM master: %m\n");
|
||||
/* This is very, very bad, and the compositor will crash soon,
|
||||
but oh well... */
|
||||
} else if (wl->verbose) {
|
||||
fprintf(stderr, "became DRM master after VT switch\n");
|
||||
}
|
||||
}
|
||||
|
||||
message.opcode = WESTON_LAUNCHER_SERVER_VT_ENTER;
|
||||
} else
|
||||
return -1;
|
||||
|
||||
do {
|
||||
len = send(wl->sock[0], &message, sizeof(message), 0);
|
||||
} while (len < 0 && errno == EINTR);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
handle_signal(struct weston_launch *wl)
|
||||
{
|
||||
@ -366,6 +538,8 @@ handle_signal(struct weston_launch *wl)
|
||||
if (wl->child)
|
||||
kill(wl->child, sig.ssi_signo);
|
||||
break;
|
||||
case SIGUSR1:
|
||||
return handle_vt_switch(wl);
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
@ -377,6 +551,8 @@ static int
|
||||
setup_tty(struct weston_launch *wl)
|
||||
{
|
||||
struct stat buf;
|
||||
struct termios raw_attributes;
|
||||
struct vt_mode mode = { 0 };
|
||||
char *session, *tty;
|
||||
char path[PATH_MAX];
|
||||
int ok;
|
||||
@ -388,7 +564,7 @@ setup_tty(struct weston_launch *wl)
|
||||
ok = sd_session_get_tty(session, &tty);
|
||||
if (ok == 0) {
|
||||
snprintf(path, PATH_MAX, "/dev/%s", tty);
|
||||
wl->tty = open(path, O_RDWR | O_NOCTTY);
|
||||
wl->tty = open(path, O_RDWR | O_NOCTTY | O_CLOEXEC);
|
||||
free(tty);
|
||||
#ifdef HAVE_SD_SESSION_GET_VT
|
||||
} else if (ok == -ENOENT) {
|
||||
@ -404,7 +580,7 @@ setup_tty(struct weston_launch *wl)
|
||||
error(1, -ok, "could not determine current TTY");
|
||||
|
||||
snprintf(path, PATH_MAX, "/dev/tty%s", tty);
|
||||
wl->tty = open(path, O_RDWR | O_NOCTTY);
|
||||
wl->tty = open(path, O_RDWR | O_NOCTTY | O_CLOEXEC);
|
||||
free(tty);
|
||||
#endif
|
||||
} else
|
||||
@ -421,6 +597,41 @@ setup_tty(struct weston_launch *wl)
|
||||
|
||||
wl->ttynr = minor(buf.st_rdev);
|
||||
|
||||
if (tcgetattr(wl->tty, &wl->terminal_attributes) < 0)
|
||||
error(1, errno, "could not get terminal attributes");
|
||||
|
||||
/* Ignore control characters and disable echo */
|
||||
raw_attributes = wl->terminal_attributes;
|
||||
cfmakeraw(&raw_attributes);
|
||||
|
||||
/* Fix up line endings to be normal (cfmakeraw hoses them) */
|
||||
raw_attributes.c_oflag |= OPOST | OCRNL;
|
||||
/* Don't generate ttou signals */
|
||||
raw_attributes.c_oflag &= ~TOSTOP;
|
||||
|
||||
if (tcsetattr(wl->tty, TCSANOW, &raw_attributes) < 0)
|
||||
error(1, errno, "could not put terminal into raw mode");
|
||||
|
||||
ioctl(wl->tty, KDGKBMODE, &wl->kb_mode);
|
||||
ok = ioctl(wl->tty, KDSKBMODE, K_OFF);
|
||||
if (ok < 0) {
|
||||
ok = ioctl(wl->tty, KDSKBMODE, K_RAW);
|
||||
if (ok < 0)
|
||||
error(1, errno, "failed to set keyboard mode on tty");
|
||||
}
|
||||
|
||||
ok = ioctl(wl->tty, KDSETMODE, KD_GRAPHICS);
|
||||
if (ok < 0)
|
||||
error(1, errno, "failed to set KD_GRAPHICS mode on tty");
|
||||
|
||||
wl->vt_state = VT_HAS_VT;
|
||||
mode.mode = VT_PROCESS;
|
||||
mode.relsig = SIGUSR1;
|
||||
mode.acqsig = SIGUSR1;
|
||||
ok = ioctl(wl->tty, VT_SETMODE, &mode);
|
||||
if (ok < 0)
|
||||
error(1, errno, "failed to take control of vt handling");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -448,7 +659,6 @@ launch_compositor(struct weston_launch *wl, int argc, char *argv[])
|
||||
|
||||
drop_privileges(wl);
|
||||
|
||||
setenv_fd("WESTON_TTY_FD", wl->tty);
|
||||
setenv_fd("WESTON_LAUNCHER_SOCK", wl->sock[1]);
|
||||
setenv("LD_LIBRARY_PATH", LIBDIR, 1);
|
||||
unsetenv("DISPLAY");
|
||||
@ -458,6 +668,7 @@ launch_compositor(struct weston_launch *wl, int argc, char *argv[])
|
||||
sigaddset(&mask, SIGTERM);
|
||||
sigaddset(&mask, SIGCHLD);
|
||||
sigaddset(&mask, SIGINT);
|
||||
sigaddset(&mask, SIGUSR1);
|
||||
sigprocmask(SIG_UNBLOCK, &mask, NULL);
|
||||
|
||||
snprintf (command, PATH_MAX, "%s \"$@\"", argv[0]);
|
||||
@ -495,6 +706,7 @@ main(int argc, char *argv[])
|
||||
};
|
||||
|
||||
memset(&wl, 0, sizeof wl);
|
||||
wl.drm_fd = -1;
|
||||
|
||||
while ((c = getopt_long(argc, argv, "u:t::vh", opts, &i)) != -1) {
|
||||
switch (c) {
|
||||
@ -540,7 +752,6 @@ main(int argc, char *argv[])
|
||||
launch_compositor(&wl, argc - optind, argv + optind);
|
||||
|
||||
close(wl.sock[1]);
|
||||
close(wl.tty);
|
||||
|
||||
while (1) {
|
||||
struct pollfd fds[2];
|
||||
|
@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright © 2012 Benjamin Franzke
|
||||
* 2013 Red Hat, Inc.
|
||||
*
|
||||
* Permission to use, copy, modify, distribute, and sell this software and
|
||||
* its documentation for any purpose is hereby granted without fee, provided
|
||||
@ -25,7 +26,14 @@
|
||||
|
||||
enum weston_launcher_opcode {
|
||||
WESTON_LAUNCHER_OPEN,
|
||||
WESTON_LAUNCHER_DRM_SET_MASTER
|
||||
WESTON_LAUNCHER_DRM_SET_FD,
|
||||
WESTON_LAUNCHER_ACTIVATE_VT,
|
||||
WESTON_LAUNCHER_CONFIRM_VT_SWITCH,
|
||||
};
|
||||
|
||||
enum weston_launcher_server_opcode {
|
||||
WESTON_LAUNCHER_SERVER_REQUEST_VT_SWITCH,
|
||||
WESTON_LAUNCHER_SERVER_VT_ENTER,
|
||||
};
|
||||
|
||||
struct weston_launcher_message {
|
||||
@ -38,9 +46,9 @@ struct weston_launcher_open {
|
||||
char path[0];
|
||||
};
|
||||
|
||||
struct weston_launcher_set_master {
|
||||
struct weston_launcher_activate_vt {
|
||||
struct weston_launcher_message header;
|
||||
int set_master;
|
||||
int vt;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user