From bfeda130de340c090895d23ea0a5742521ff0078 Mon Sep 17 00:00:00 2001 From: Benjamin Franzke Date: Mon, 30 Jan 2012 14:04:04 +0100 Subject: [PATCH] Introduce weston-launch weston-launch starts weston and provides mechanism for weston to set/drop drm master, open a tty, and read input devices without being root. Execution is allowed for local-active sessions or users in the group weston-launch. --- configure.ac | 16 ++ src/.gitignore | 1 + src/Makefile.am | 24 +- src/compositor-drm.c | 8 +- src/compositor.c | 1 + src/compositor.h | 4 + src/evdev.c | 3 +- src/launcher-util.c | 156 +++++++++++ src/launcher-util.h | 35 +++ src/tty.c | 37 ++- src/util.c | 26 ++ src/weston-launch.c | 639 +++++++++++++++++++++++++++++++++++++++++++ src/weston-launch.h | 46 ++++ 13 files changed, 977 insertions(+), 19 deletions(-) create mode 100644 src/launcher-util.c create mode 100644 src/launcher-util.h create mode 100644 src/weston-launch.c create mode 100644 src/weston-launch.h diff --git a/configure.ac b/configure.ac index ffed655f..038e6198 100644 --- a/configure.ac +++ b/configure.ac @@ -151,6 +151,22 @@ if test x$enable_clients = xyes; then [AC_MSG_WARN([Cairo-EGL not found - clients will use cairo image])]) fi +AC_ARG_ENABLE(weston-launch, [ --enable-weston-launch],, enable_weston_launch=yes) +AM_CONDITIONAL(BUILD_WESTON_LAUNCH, test x$enable_weston_launch == xyes) +if test x$enable_weston_launch == xyes; then + PKG_CHECK_MODULES(WESTON_LAUNCH, [libdrm]) + PKG_CHECK_MODULES(SYSTEMD_LOGIN, [libsystemd-login], + [have_systemd_login=yes], [have_systemd_login=no]) + AS_IF([test "x$have_systemd_login" = "xyes"], + [AC_DEFINE([HAVE_SYSTEMD_LOGIN], [1], [Have systemd-login])]) + + AC_CHECK_LIB([pam], [pam_open_session], [have_pam=yes], [have_pam=no]) + if test x$have_pam == xno; then + AC_ERROR([weston-launch requires pam]) + fi + WESTON_LAUNCH_LIBS="$WESTON_LAUNCH_LIBS -lpam" +fi + AM_CONDITIONAL(HAVE_POPPLER, test "x$have_poppler" = "xyes") AM_CONDITIONAL(BUILD_FULL_GL_CLIENTS, diff --git a/src/.gitignore b/src/.gitignore index c53b4022..56fc1ae0 100644 --- a/src/.gitignore +++ b/src/.gitignore @@ -1,4 +1,5 @@ weston +weston-launch screenshooter-protocol.c screenshooter-server-protocol.h tablet-shell-protocol.c diff --git a/src/Makefile.am b/src/Makefile.am index 048e58fd..c9fdc5a6 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,4 +1,5 @@ -bin_PROGRAMS = weston +bin_PROGRAMS = weston \ + $(weston_launch) AM_CPPFLAGS = \ -DDATADIR='"$(datadir)"' \ @@ -22,14 +23,33 @@ weston_SOURCES = \ util.c \ matrix.c \ matrix.h \ + weston-launch.h \ $(xserver_launcher_sources) +if BUILD_WESTON_LAUNCH +weston_launch = weston-launch +weston_launch_SOURCES = weston-launch.c weston-launch.h +weston_launch_CFLAGS= $(GCC_CFLAGS) +weston_launch_CPPFLAGS = $(WESTON_LAUNCH_CFLAGS) $(SYSTEMD_LOGIN_CFLAGS) \ + -DBINDIR='"$(bindir)"' +weston_launch_LDADD = $(WESTON_LAUNCH_LIBS) $(SYSTEMD_LOGIN_LIBS) + +if ENABLE_SETUID_INSTALL +install-exec-hook: + chown root $(DESTDIR)$(bindir)/weston-launch + chmod u+s $(DESTDIR)$(bindir)/weston-launch +endif + +else # BUILD_WESTON_LAUNCH + if ENABLE_SETUID_INSTALL install-exec-hook: chown root $(DESTDIR)$(bindir)/weston chmod u+s $(DESTDIR)$(bindir)/weston endif +endif # BUILD_WESTON_LAUNCH + if ENABLE_XSERVER_LAUNCHER xserver_launcher_sources = \ xserver-launcher.c \ @@ -67,6 +87,8 @@ drm_backend_la_SOURCES = \ tty.c \ evdev.c \ evdev.h \ + launcher-util.c \ + launcher-util.h \ libbacklight.c \ libbacklight.h endif diff --git a/src/compositor-drm.c b/src/compositor-drm.c index 57476624..27533bae 100644 --- a/src/compositor-drm.c +++ b/src/compositor-drm.c @@ -39,6 +39,7 @@ #include "compositor.h" #include "evdev.h" +#include "launcher-util.h" struct drm_compositor { struct weston_compositor base; @@ -1449,7 +1450,8 @@ drm_destroy(struct weston_compositor *ec) gbm_device_destroy(d->gbm); destroy_sprites(d); - drmDropMaster(d->drm.fd); + if (weston_launcher_drm_set_master(&d->base, d->drm.fd, 0) < 0) + fprintf(stderr, "failed to drop master: %m\n"); tty_destroy(d->tty); free(d); @@ -1489,7 +1491,7 @@ vt_func(struct weston_compositor *compositor, int event) switch (event) { case TTY_ENTER_VT: compositor->focus = 1; - if (drmSetMaster(ec->drm.fd)) { + if (weston_launcher_drm_set_master(&ec->base, ec->drm.fd, 1)) { fprintf(stderr, "failed to set master: %m\n"); wl_display_terminate(compositor->wl_display); } @@ -1529,7 +1531,7 @@ vt_func(struct weston_compositor *compositor, int event) wl_list_for_each(input, &compositor->input_device_list, link) evdev_remove_devices(input); - if (drmDropMaster(ec->drm.fd) < 0) + if (weston_launcher_drm_set_master(&ec->base, ec->drm.fd, 0) < 0) fprintf(stderr, "failed to drop master: %m\n"); break; diff --git a/src/compositor.c b/src/compositor.c index 1fce69a3..5c5331e8 100644 --- a/src/compositor.c +++ b/src/compositor.c @@ -2331,6 +2331,7 @@ weston_compositor_init(struct weston_compositor *ec, struct wl_display *display) const char *extensions; ec->wl_display = display; + ec->launcher_sock = weston_environment_get_fd("WESTON_LAUNCHER_SOCK"); if (!wl_display_add_global(display, &wl_compositor_interface, ec, compositor_bind)) diff --git a/src/compositor.h b/src/compositor.h index ea18b7b4..507f5493 100644 --- a/src/compositor.h +++ b/src/compositor.h @@ -231,6 +231,7 @@ struct weston_compositor { int (*authenticate)(struct weston_compositor *c, uint32_t id); struct screenshooter *screenshooter; + int launcher_sock; }; #define MODIFIER_CTRL (1 << 8) @@ -443,6 +444,9 @@ weston_compositor_run_binding(struct weston_compositor *compositor, struct weston_input_device *device, uint32_t time, uint32_t key, uint32_t button, uint32_t axis, int32_t state); +int +weston_environment_get_fd(const char *env); + struct wl_list * weston_compositor_top(struct weston_compositor *compositor); diff --git a/src/evdev.c b/src/evdev.c index 16f0e935..9967e525 100644 --- a/src/evdev.c +++ b/src/evdev.c @@ -30,6 +30,7 @@ #include "compositor.h" #include "evdev.h" +#include "launcher-util.h" struct evdev_input { struct weston_input_device base; @@ -492,7 +493,7 @@ evdev_input_device_create(struct evdev_input *master, /* Use non-blocking mode so that we can loop on read on * evdev_input_device_data() until all events on the fd are * read. mtdev_get() also expects this. */ - device->fd = open(path, O_RDONLY | O_NONBLOCK); + device->fd = weston_launcher_open(ec, path, O_RDONLY | O_NONBLOCK); if (device->fd < 0) goto err0; diff --git a/src/launcher-util.c b/src/launcher-util.c new file mode 100644 index 00000000..acfcc3ee --- /dev/null +++ b/src/launcher-util.c @@ -0,0 +1,156 @@ +/* + * Copyright © 2012 Benjamin Franzke + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of the copyright holders not be used in + * advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. The copyright holders make + * no representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER + * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF + * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include "compositor.h" +#include "launcher-util.h" +#include "weston-launch.h" + +int +weston_launcher_open(struct weston_compositor *compositor, + const char *path, int flags) +{ + int sock = compositor->launcher_sock; + int fd, n, ret = -1; + struct msghdr msg; + struct cmsghdr *cmsg; + struct iovec iov; + char control[CMSG_SPACE(sizeof fd)]; + ssize_t len; + struct weston_launcher_open *message; + + if (sock == -1) + return open(path, flags); + + n = sizeof(*message) + strlen(path) + 1; + message = malloc(n); + if (!message) + return -1; + + message->header.opcode = WESTON_LAUNCHER_OPEN; + message->flags = flags; + strcpy(message->path, path); + + do { + len = send(sock, message, n, 0); + } while (len < 0 && errno == EINTR); + + memset(&msg, 0, sizeof msg); + iov.iov_base = &ret; + iov.iov_len = sizeof ret; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = control; + msg.msg_controllen = sizeof control; + + do { + len = recvmsg(sock, &msg, MSG_CMSG_CLOEXEC); + } while (len < 0 && errno == EINTR); + + if (len != sizeof ret || + ret < 0) + goto out; + + cmsg = CMSG_FIRSTHDR(&msg); + if (!cmsg || + cmsg->cmsg_level != SOL_SOCKET || + cmsg->cmsg_type != SCM_RIGHTS) { + fprintf(stderr, "invalid control message\n"); + goto out; + } + + fd = *(int *) CMSG_DATA(cmsg); + if (fd == -1) { + fprintf(stderr, "missing drm fd in socket request"); + return -1; + } + +out: + free(message); + return ret < 0 ? ret : fd; +} + +int +weston_launcher_drm_set_master(struct weston_compositor *compositor, + int drm_fd, char master) +{ + struct msghdr msg; + struct cmsghdr *cmsg; + struct iovec iov; + char control[CMSG_SPACE(sizeof(drm_fd))]; + int ret; + ssize_t len; + struct weston_launcher_set_master message; + + if (compositor->launcher_sock == -1) { + if (master) + return drmSetMaster(drm_fd); + else + return drmDropMaster(drm_fd); + } + + memset(&msg, 0, sizeof msg); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = control; + msg.msg_controllen = sizeof control; + cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + cmsg->cmsg_len = CMSG_LEN(sizeof(drm_fd)); + + *(int *) CMSG_DATA(cmsg) = drm_fd; + msg.msg_controllen = cmsg->cmsg_len; + + iov.iov_base = &message; + iov.iov_len = sizeof message; + + message.header.opcode = WESTON_LAUNCHER_DRM_SET_MASTER; + message.set_master = master; + + do { + len = sendmsg(compositor->launcher_sock, &msg, 0); + } while (len < 0 && errno == EINTR); + if (len < 0) + return -1; + + do { + len = recv(compositor->launcher_sock, &ret, sizeof ret, 0); + } while (len < 0 && errno == EINTR); + if (len < 0) + return -1; + + return ret; +} + diff --git a/src/launcher-util.h b/src/launcher-util.h new file mode 100644 index 00000000..da110287 --- /dev/null +++ b/src/launcher-util.h @@ -0,0 +1,35 @@ +/* + * Copyright © 2012 Benjamin Franzke + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of the copyright holders not be used in + * advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. The copyright holders make + * no representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER + * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF + * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _WESTON_LAUNCHER_UTIL_H_ +#define _WESTON_LAUNCHER_UTIL_H_ + +#include "compositor.h" + +int +weston_launcher_open(struct weston_compositor *compositor, + const char *path, int flags); +int +weston_launcher_drm_set_master(struct weston_compositor *compositor, + int drm_fd, char master); + +#endif diff --git a/src/tty.c b/src/tty.c index 85c16d8a..651c5cd2 100644 --- a/src/tty.c +++ b/src/tty.c @@ -81,7 +81,6 @@ try_open_vt(struct tty *tty) { int tty0, fd; char filename[16]; - struct vt_stat vts; tty0 = open("/dev/tty0", O_WRONLY | O_CLOEXEC); if (tty0 < 0) { @@ -102,18 +101,6 @@ try_open_vt(struct tty *tty) if (fd < 0) return fd; - if (ioctl(fd, VT_GETSTATE, &vts) == 0) - tty->starting_vt = vts.v_active; - else - tty->starting_vt = tty->vt; - - if (ioctl(fd, VT_ACTIVATE, tty->vt) < 0 || - ioctl(fd, VT_WAITACTIVE, tty->vt) < 0) { - fprintf(stderr, "failed to swtich to new vt\n"); - close(fd); - return -1; - } - return fd; } @@ -128,6 +115,7 @@ tty_create(struct weston_compositor *compositor, tty_vt_func_t vt_func, struct wl_event_loop *loop; struct stat buf; char filename[16]; + struct vt_stat vts; tty = malloc(sizeof *tty); if (tty == NULL) @@ -136,14 +124,22 @@ tty_create(struct weston_compositor *compositor, tty_vt_func_t vt_func, memset(tty, 0, sizeof *tty); tty->compositor = compositor; tty->vt_func = vt_func; + + tty->fd = weston_environment_get_fd("WESTON_TTY_FD"); + if (tty->fd < 0) + tty->fd = STDIN_FILENO; + if (tty_nr > 0) { snprintf(filename, sizeof filename, "/dev/tty%d", tty_nr); fprintf(stderr, "compositor: using %s\n", filename); tty->fd = open(filename, O_RDWR | O_NOCTTY | O_CLOEXEC); + tty->vt = tty_nr; } else if (fstat(tty->fd, &buf) == 0 && major(buf.st_rdev) == TTY_MAJOR && minor(buf.st_rdev) > 0) { - tty->fd = fcntl(0, F_DUPFD_CLOEXEC, 0); + if (tty->fd == STDIN_FILENO) + tty->fd = fcntl(STDIN_FILENO, F_DUPFD_CLOEXEC, 0); + tty->vt = minor(buf.st_rdev); } else { /* Fall back to try opening a new VT. This typically * requires root. */ @@ -156,6 +152,19 @@ tty_create(struct weston_compositor *compositor, tty_vt_func_t vt_func, return NULL; } + if (ioctl(tty->fd, VT_GETSTATE, &vts) == 0) + tty->starting_vt = vts.v_active; + else + tty->starting_vt = tty->vt; + + if (tty->starting_vt != tty->vt) { + if (ioctl(tty->fd, VT_ACTIVATE, tty->vt) < 0 || + ioctl(tty->fd, VT_WAITACTIVE, tty->vt) < 0) { + fprintf(stderr, "failed to swtich to new vt\n"); + return NULL; + } + } + if (tcgetattr(tty->fd, &tty->terminal_attributes) < 0) { fprintf(stderr, "could not get terminal attributes: %m\n"); goto err; diff --git a/src/util.c b/src/util.c index bb122c2c..11fa5ff6 100644 --- a/src/util.c +++ b/src/util.c @@ -25,6 +25,9 @@ #include #include +#include +#include + #include "compositor.h" WL_EXPORT void @@ -296,3 +299,26 @@ weston_compositor_run_binding(struct weston_compositor *compositor, } } } + +WL_EXPORT int +weston_environment_get_fd(const char *env) +{ + char *e, *end; + int fd, flags; + + e = getenv(env); + if (!e) + return -1; + fd = strtol(e, &end, 0); + if (*end != '\0') + return -1; + + flags = fcntl(fd, F_GETFD); + if (flags == -1) + return -1; + + fcntl(fd, F_SETFD, flags | FD_CLOEXEC); + unsetenv(env); + + return fd; +} diff --git a/src/weston-launch.c b/src/weston-launch.c new file mode 100644 index 00000000..4e0927fd --- /dev/null +++ b/src/weston-launch.c @@ -0,0 +1,639 @@ +/* + * Copyright © 2012 Benjamin Franzke + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of the copyright holders not be used in + * advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. The copyright holders make + * no representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER + * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF + * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#define _GNU_SOURCE + +#include "config.h" + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include + +#ifdef HAVE_SYSTEMD_LOGIN +#include +#endif + +#include "weston-launch.h" + +struct weston_launch { + struct pam_conv pc; + pam_handle_t *ph; + int tty; + int ttynr; + int sock[2]; + struct passwd *pw; + + int epollfd; + int signalfd; + + pid_t child; + int verbose; +}; + +static gid_t * +read_groups(void) +{ + int n; + gid_t *groups; + + n = getgroups(0, NULL); + groups = malloc(n * sizeof(gid_t)); + if (!groups) + return NULL; + + if (getgroups(n, groups) < 0) { + free(groups); + return NULL; + } + return groups; +} + +static int +weston_launch_allowed(struct weston_launch *wl) +{ + struct group *gr; + gid_t *groups; + int i; +#ifdef HAVE_SYSTEMD_LOGIN + char *session, *seat; + int err; +#endif + + if (getuid() == 0) + return 1; + + gr = getgrnam("weston-launch"); + if (gr) { + groups = read_groups(); + if (groups) { + for (i = 0; groups[i]; ++i) { + if (groups[i] == gr->gr_gid) { + free(groups); + return 1; + } + } + free(groups); + } + } + +#ifdef HAVE_SYSTEMD_LOGIN + err = sd_pid_get_session(getpid(), &session); + if (err == 0 && session) { + if (sd_session_is_active(session) && + sd_session_get_seat(session, &seat) == 0) { + free(seat); + free(session); + return 1; + } + free(session); + } +#endif + + return 0; +} + +static int +pam_conversation_fn(int msg_count, + const struct pam_message **messages, + struct pam_response **responses, + void *user_data) +{ + return PAM_SUCCESS; +} + +static int +setup_pam(struct weston_launch *wl) +{ + int err; + + wl->pc.conv = pam_conversation_fn; + wl->pc.appdata_ptr = wl; + + err = pam_start("login", wl->pw->pw_name, &wl->pc, &wl->ph); + err = pam_set_item(wl->ph, PAM_TTY, ttyname(wl->tty)); + if (err != PAM_SUCCESS) { + fprintf(stderr, "failed to set PAM_TTY item: %d: %s\n", + err, pam_strerror(wl->ph, err)); + return -1; + } + + err = pam_open_session(wl->ph, 0); + if (err != PAM_SUCCESS) { + fprintf(stderr, "failed to open pam session: %d: %s\n", + err, pam_strerror(wl->ph, err)); + return -1; + } + + return 0; +} + +static int +setup_launcher_socket(struct weston_launch *wl) +{ + struct epoll_event ev; + + if (socketpair(AF_LOCAL, SOCK_DGRAM, 0, wl->sock) < 0) + error(1, errno, "socketpair failed"); + + fcntl(wl->sock[0], F_SETFD, O_CLOEXEC); + + memset(&ev, 0, sizeof ev); + ev.events = EPOLLIN; + ev.data.fd = wl->sock[0]; + if (epoll_ctl(wl->epollfd, EPOLL_CTL_ADD, ev.data.fd, &ev) < 0) + return -errno; + + return 0; +} + +static int +setup_signals(struct weston_launch *wl) +{ + sigset_t mask; + struct sigaction sa; + struct epoll_event ev; + + memset(&sa, 0, sizeof sa); + sa.sa_handler = SIG_DFL; + sa.sa_flags = SA_NOCLDSTOP | SA_RESTART; + assert(sigaction(SIGCHLD, &sa, NULL) == 0); + + assert(sigemptyset(&mask) == 0); + sigaddset(&mask, SIGCHLD); + sigaddset(&mask, SIGINT); + sigaddset(&mask, SIGTERM); + assert(sigprocmask(SIG_BLOCK, &mask, NULL) == 0); + + wl->signalfd = signalfd(-1, &mask, SFD_NONBLOCK | SFD_CLOEXEC); + if (wl->signalfd < 0) + return -errno; + + memset(&ev, 0, sizeof ev); + ev.events = EPOLLIN; + ev.data.fd = wl->signalfd; + if (epoll_ctl(wl->epollfd, EPOLL_CTL_ADD, ev.data.fd, &ev) < 0) + return -errno; + + return 0; +} + +static void +setenv_fd(const char *env, int fd) +{ + char buf[32]; + + snprintf(buf, sizeof buf, "%d", fd); + setenv(env, buf, 1); +} + +static int +handle_setmaster(struct weston_launch *wl, struct msghdr *msg, ssize_t len) +{ + int drm_fd = -1, ret = -1; + struct cmsghdr *cmsg; + struct weston_launcher_set_master *message; + + if (len != sizeof(*message)) { + error(0, 0, "missing value in setmaster request"); + 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"); + goto out; + } + + drm_fd = *(int *) CMSG_DATA(cmsg); + if (drm_fd == -1) { + error(0, 0, "missing drm fd in socket request"); + goto out; + } + + if (message->set_master) + ret = drmSetMaster(drm_fd); + else + ret = drmDropMaster(drm_fd); + + close(drm_fd); +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) +{ + int fd = -1, ret = -1; + char control[CMSG_SPACE(sizeof(fd))]; + struct cmsghdr *cmsg; + struct stat s; + struct msghdr nmsg; + struct iovec iov; + struct weston_launcher_open *message; + + message = msg->msg_iov->iov_base; + if ((size_t)len < sizeof(*message)) + goto err0; + + /* Ensure path is null-terminated */ + ((char *) message)[len-1] = '\0'; + + if (stat(message->path, &s) < 0) + goto err0; + + fd = open(message->path, message->flags); + if (fd < 0) + goto err0; + + if (major(s.st_rdev) != INPUT_MAJOR) { + close(fd); + fd = -1; + goto err0; + } + +err0: + memset(&nmsg, 0, sizeof nmsg); + nmsg.msg_iov = &iov; + nmsg.msg_iovlen = 1; + if (fd != -1) { + nmsg.msg_control = control; + nmsg.msg_controllen = sizeof control; + cmsg = CMSG_FIRSTHDR(&nmsg); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + cmsg->cmsg_len = CMSG_LEN(sizeof(fd)); + *(int *) CMSG_DATA(cmsg) = fd; + nmsg.msg_controllen = cmsg->cmsg_len; + ret = 0; + } + iov.iov_base = &ret; + iov.iov_len = sizeof ret; + + if (wl->verbose) + fprintf(stderr, "weston-launch: opened %s: ret: %d, fd: %d\n", + message->path, ret, fd); + do { + len = sendmsg(wl->sock[0], &nmsg, 0); + } while (len < 0 && errno == EINTR); + + if (len < 0) + return -1; + + return 0; +} + +static int +handle_socket_msg(struct weston_launch *wl) +{ + char control[CMSG_SPACE(sizeof(int))]; + char buf[BUFSIZ]; + struct msghdr msg; + struct iovec iov; + int ret = -1; + ssize_t len; + struct weston_launcher_message *message; + + memset(&msg, 0, sizeof(msg)); + iov.iov_base = buf; + iov.iov_len = sizeof buf; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = control; + msg.msg_controllen = sizeof control; + + do { + len = recvmsg(wl->sock[0], &msg, 0); + } while (len < 0 && errno == EINTR); + + if (len < 1) + return -1; + + message = (void *) buf; + switch (message->opcode) { + case WESTON_LAUNCHER_OPEN: + ret = handle_open(wl, &msg, len); + break; + case WESTON_LAUNCHER_DRM_SET_MASTER: + ret = handle_setmaster(wl, &msg, len); + break; + } + + return ret; +} + +static void +quit(struct weston_launch *wl, int status) +{ + int err; + + close(wl->epollfd); + close(wl->signalfd); + close(wl->sock[0]); + + err = pam_close_session(wl->ph, 0); + if (err) + fprintf(stderr, "pam_close_session failed: %d: %s\n", + err, pam_strerror(wl->ph, err)); + pam_end(wl->ph, err); + + exit(status); +} + +static int +handle_signal(struct weston_launch *wl) +{ + struct signalfd_siginfo sig; + int pid, status; + + if (read(wl->signalfd, &sig, sizeof sig) != sizeof sig) { + error(0, errno, "reading signalfd failed"); + return -1; + } + + switch (sig.ssi_signo) { + case SIGCHLD: + pid = waitpid(-1, &status, 0); + if (pid == wl->child) { + wl->child = 0; + quit(wl, WIFEXITED(status) ? WEXITSTATUS(status) : 0); + } + break; + case SIGTERM: + if (wl->child) + kill(wl->child, SIGTERM); + quit(wl, 0); + break; + case SIGINT: + if (wl->child) + kill(wl->child, SIGTERM); + break; + default: + return -1; + } + + return 0; +} + +static int +setup_tty(struct weston_launch *wl, const char *tty) +{ + struct stat buf; + char *t; + + if (tty) { + t = ttyname(STDIN_FILENO); + if (t && strcmp(t, tty) == 0) + wl->tty = STDIN_FILENO; + else + wl->tty = open(tty, O_RDWR | O_NOCTTY); + } else { + int tty0 = open("/dev/tty0", O_WRONLY | O_CLOEXEC); + char filename[16]; + + if (tty0 < 0) + error(1, errno, "count not open tty0"); + + if (ioctl(tty0, VT_OPENQRY, &wl->ttynr) < 0 || wl->ttynr == -1) + error(1, errno, "failed to find non-opened console"); + + snprintf(filename, sizeof filename, "/dev/tty%d", wl->ttynr); + wl->tty = open(filename, O_RDWR | O_NOCTTY); + close(tty0); + } + + if (wl->tty < 0) + error(1, errno, "failed to open tty"); + + if (tty) { + if (fstat(wl->tty, &buf) < 0) + error(1, errno, "stat %s failed", tty); + + if (major(buf.st_rdev) != TTY_MAJOR) + error(1, 0, "invalid tty device: %s", tty); + + wl->ttynr = minor(buf.st_rdev); + } + + return 0; +} + +static void +help(const char *name) +{ + fprintf(stderr, "Usage: %s [args...] [-- [weston args..]]\n", name); + fprintf(stderr, " -u, --user Start session as specified username\n"); + fprintf(stderr, " -t, --tty Start session on alternative tty device\n"); + fprintf(stderr, " -v, --verbose Be verbose\n"); + fprintf(stderr, " -s, --sleep Sleep specified amount of time before exec\n"); + fprintf(stderr, " -h, --help Display this help message\n"); +} + +int +main(int argc, char *argv[]) +{ + struct weston_launch wl; + char **env; + int i, c; + char **child_argv; + char *tty = NULL, *new_user = NULL; + int sleep_fork = 0; + struct option opts[] = { + { "user", required_argument, NULL, 'u' }, + { "tty", required_argument, NULL, 't' }, + { "verbose", no_argument, NULL, 'v' }, + { "sleep", optional_argument, NULL, 's' }, + { "help", no_argument, NULL, 'h' }, + { 0, 0, NULL, 0 } + }; + + memset(&wl, 0, sizeof wl); + + while ((c = getopt_long(argc, argv, "u:t:s::vh", opts, &i)) != -1) { + switch (c) { + case 'u': + new_user = optarg; + if (getuid() != 0) + error(1, 0, "Permission denied. -u allowed for root only"); + break; + case 't': + tty = optarg; + break; + case 'v': + wl.verbose = 1; + break; + case 's': + if (optarg) + sleep_fork = atoi(optarg); + else + sleep_fork = 10; + break; + case 'h': + help("weston"); + exit(1); + } + } + + child_argv = &argv[optind-1]; + child_argv[0] = BINDIR "/weston"; + + if (new_user) + wl.pw = getpwnam(new_user); + else + wl.pw = getpwuid(getuid()); + if (wl.pw == NULL) + error(1, errno, "failed to get username"); + + if (!weston_launch_allowed(&wl)) + error(1, 0, "Permission denied. You should..\n" +#ifdef HAVE_SYSTEMD_LOGIN + " - run from an active and local (systemd) session.\n" +#else + " - enable systemd session support for weston-launch.\n" +#endif + " - add yourself to the 'weston-launch' group."); + + if (setup_tty(&wl, tty) < 0) + return 1; + + if (setup_pam(&wl) < 0) + return 1; + + wl.epollfd = epoll_create1(EPOLL_CLOEXEC); + if (wl.epollfd < 0) + error(1, errno, "epoll create failed"); + + if (setup_launcher_socket(&wl) < 0) + return 1; + + if (setup_signals(&wl) < 0) + return 1; + + switch ((wl.child = fork())) { + case -1: + error(1, errno, "fork failed"); + break; + case 0: + if (wl.verbose) + printf("weston-launch: spawned weston with pid: %d\n", getpid()); + if (wl.tty != STDIN_FILENO) { + if (setsid() < 0) + error(1, errno, "setsid failed"); + if (ioctl(wl.tty, TIOCSCTTY, 0) < 0) + error(1, errno, "TIOCSCTTY failed - tty is in use"); + } + + if (setgid(wl.pw->pw_gid) < 0 || + setuid(wl.pw->pw_uid) < 0) + error(1, errno, "dropping privilidges failed"); + + if (sleep_fork) { + if (wl.verbose) + printf("weston-launch: waiting %d seconds\n", sleep_fork); + sleep(sleep_fork); + } + + if (new_user) { + setenv("USER", wl.pw->pw_name, 1); + setenv("LOGNAME", wl.pw->pw_name, 1); + setenv("HOME", wl.pw->pw_dir, 1); + setenv("SHELL", wl.pw->pw_shell, 1); + } + env = pam_getenvlist(wl.ph); + if (env) { + for (i = 0; env[i]; ++i) { + if (putenv(env[i]) < 0) + error(0, 0, "putenv %s failed", env[i]); + } + free(env); + } + + if (wl.tty != STDIN_FILENO) + setenv_fd("WESTON_TTY_FD", wl.tty); + + setenv_fd("WESTON_LAUNCHER_SOCK", wl.sock[1]); + + unsetenv("DISPLAY"); + + execv(child_argv[0], child_argv); + error(1, errno, "exec failed"); + break; + default: + close(wl.sock[1]); + if (wl.tty != STDIN_FILENO) + close(wl.tty); + + while (1) { + struct epoll_event ev; + int n; + + n = epoll_wait(wl.epollfd, &ev, 1, -1); + if (n < 0) + error(0, errno, "epoll_wait failed"); + if (n != 1) + continue; + + if (ev.data.fd == wl.sock[0]) + handle_socket_msg(&wl); + else if (ev.data.fd == wl.signalfd) + handle_signal(&wl); + } + break; + } + + return 0; +} diff --git a/src/weston-launch.h b/src/weston-launch.h new file mode 100644 index 00000000..5be013ef --- /dev/null +++ b/src/weston-launch.h @@ -0,0 +1,46 @@ +/* + * Copyright © 2012 Benjamin Franzke + * + * Permission to use, copy, modify, distribute, and sell this software and + * its documentation for any purpose is hereby granted without fee, provided + * that the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of the copyright holders not be used in + * advertising or publicity pertaining to distribution of the software + * without specific, written prior permission. The copyright holders make + * no representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER + * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF + * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _WESTON_LAUNCH_H_ +#define _WESTON_LAUNCH_H_ + +enum weston_launcher_opcode { + WESTON_LAUNCHER_OPEN, + WESTON_LAUNCHER_DRM_SET_MASTER +}; + +struct weston_launcher_message { + int opcode; +}; + +struct weston_launcher_open { + struct weston_launcher_message header; + int flags; + char path[0]; +}; + +struct weston_launcher_set_master { + struct weston_launcher_message header; + int set_master; +}; + +#endif