2 * Copyright © 2012 Benjamin Franzke
4 * Permission to use, copy, modify, distribute, and sell this software and
5 * its documentation for any purpose is hereby granted without fee, provided
6 * that the above copyright notice appear in all copies and that both that
7 * copyright notice and this permission notice appear in supporting
8 * documentation, and that the name of the copyright holders not be used in
9 * advertising or publicity pertaining to distribution of the software
10 * without specific, written prior permission. The copyright holders make
11 * no representations about the suitability of this software for any
12 * purpose. It is provided "as is" without express or implied warranty.
14 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
15 * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
16 * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
17 * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
18 * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
19 * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
20 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
35 #include <sys/types.h>
36 #include <sys/ioctl.h>
39 #include <sys/socket.h>
40 #include <sys/signalfd.h>
46 #include <linux/major.h>
51 #include <security/pam_appl.h>
53 #ifdef HAVE_SYSTEMD_LOGIN
54 #include <systemd/sd-login.h>
57 #include "weston-launch.h"
62 #define KDSKBMUTE 0x4B51
66 #define EVIOCREVOKE _IOW('E', 0x91, int)
69 #define MAX_ARGV_SIZE 256
78 drmDropMaster(int drm_fd)
84 drmSetMaster(int drm_fd)
91 struct weston_launch {
109 union cmsg_data { unsigned char b[4]; int fd; };
117 n = getgroups(0, NULL);
120 fprintf(stderr, "Unable to retrieve groups: %m\n");
124 groups = malloc(n * sizeof(gid_t));
128 if (getgroups(n, groups) < 0) {
129 fprintf(stderr, "Unable to retrieve groups: %m\n");
137 weston_launch_allowed(struct weston_launch *wl)
142 #ifdef HAVE_SYSTEMD_LOGIN
143 char *session, *seat;
150 gr = getgrnam("weston-launch");
152 groups = read_groups();
154 for (i = 0; groups[i]; ++i) {
155 if (groups[i] == gr->gr_gid) {
164 #ifdef HAVE_SYSTEMD_LOGIN
165 err = sd_pid_get_session(getpid(), &session);
166 if (err == 0 && session) {
167 if (sd_session_is_active(session) &&
168 sd_session_get_seat(session, &seat) == 0) {
181 pam_conversation_fn(int msg_count,
182 const struct pam_message **messages,
183 struct pam_response **responses,
190 setup_pam(struct weston_launch *wl)
194 wl->pc.conv = pam_conversation_fn;
195 wl->pc.appdata_ptr = wl;
197 err = pam_start("login", wl->pw->pw_name, &wl->pc, &wl->ph);
198 if (err != PAM_SUCCESS) {
199 fprintf(stderr, "failed to start pam transaction: %d: %s\n",
200 err, pam_strerror(wl->ph, err));
204 err = pam_set_item(wl->ph, PAM_TTY, ttyname(wl->tty));
205 if (err != PAM_SUCCESS) {
206 fprintf(stderr, "failed to set PAM_TTY item: %d: %s\n",
207 err, pam_strerror(wl->ph, err));
211 err = pam_open_session(wl->ph, 0);
212 if (err != PAM_SUCCESS) {
213 fprintf(stderr, "failed to open pam session: %d: %s\n",
214 err, pam_strerror(wl->ph, err));
222 setup_launcher_socket(struct weston_launch *wl)
224 if (socketpair(AF_LOCAL, SOCK_SEQPACKET, 0, wl->sock) < 0)
225 error(1, errno, "socketpair failed");
227 if (fcntl(wl->sock[0], F_SETFD, FD_CLOEXEC) < 0)
228 error(1, errno, "fcntl failed");
234 setup_signals(struct weston_launch *wl)
240 memset(&sa, 0, sizeof sa);
241 sa.sa_handler = SIG_DFL;
242 sa.sa_flags = SA_NOCLDSTOP | SA_RESTART;
243 ret = sigaction(SIGCHLD, &sa, NULL);
246 sa.sa_handler = SIG_IGN;
248 sigaction(SIGHUP, &sa, NULL);
250 ret = sigemptyset(&mask);
252 sigaddset(&mask, SIGCHLD);
253 sigaddset(&mask, SIGINT);
254 sigaddset(&mask, SIGTERM);
255 sigaddset(&mask, SIGUSR1);
256 sigaddset(&mask, SIGUSR2);
257 ret = sigprocmask(SIG_BLOCK, &mask, NULL);
260 wl->signalfd = signalfd(-1, &mask, SFD_NONBLOCK | SFD_CLOEXEC);
261 if (wl->signalfd < 0)
268 setenv_fd(const char *env, int fd)
272 snprintf(buf, sizeof buf, "%d", fd);
277 send_reply(struct weston_launch *wl, int reply)
282 len = send(wl->sock[0], &reply, sizeof reply, 0);
283 } while (len < 0 && errno == EINTR);
289 handle_open(struct weston_launch *wl, struct msghdr *msg, ssize_t len)
291 int fd = -1, ret = -1;
292 char control[CMSG_SPACE(sizeof(fd))];
293 struct cmsghdr *cmsg;
297 struct weston_launcher_open *message;
298 union cmsg_data *data;
300 message = msg->msg_iov->iov_base;
301 if ((size_t)len < sizeof(*message))
304 /* Ensure path is null-terminated */
305 ((char *) message)[len-1] = '\0';
307 fd = open(message->path, message->flags);
309 fprintf(stderr, "Error opening device %s: %m\n",
314 if (fstat(fd, &s) < 0) {
317 fprintf(stderr, "Failed to stat %s\n", message->path);
321 if (major(s.st_rdev) != INPUT_MAJOR &&
322 major(s.st_rdev) != DRM_MAJOR) {
325 fprintf(stderr, "Device %s is not an input or drm device\n",
331 memset(&nmsg, 0, sizeof nmsg);
335 nmsg.msg_control = control;
336 nmsg.msg_controllen = sizeof control;
337 cmsg = CMSG_FIRSTHDR(&nmsg);
338 cmsg->cmsg_level = SOL_SOCKET;
339 cmsg->cmsg_type = SCM_RIGHTS;
340 cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
341 data = (union cmsg_data *) CMSG_DATA(cmsg);
343 nmsg.msg_controllen = cmsg->cmsg_len;
347 iov.iov_len = sizeof ret;
350 fprintf(stderr, "weston-launch: opened %s: ret: %d, fd: %d\n",
351 message->path, ret, fd);
353 len = sendmsg(wl->sock[0], &nmsg, 0);
354 } while (len < 0 && errno == EINTR);
359 if (fd != -1 && major(s.st_rdev) == DRM_MAJOR)
361 if (fd != -1 && major(s.st_rdev) == INPUT_MAJOR &&
362 wl->last_input_fd < fd)
363 wl->last_input_fd = fd;
369 handle_socket_msg(struct weston_launch *wl)
371 char control[CMSG_SPACE(sizeof(int))];
377 struct weston_launcher_message *message;
379 memset(&msg, 0, sizeof(msg));
381 iov.iov_len = sizeof buf;
384 msg.msg_control = control;
385 msg.msg_controllen = sizeof control;
388 len = recvmsg(wl->sock[0], &msg, 0);
389 } while (len < 0 && errno == EINTR);
394 message = (void *) buf;
395 switch (message->opcode) {
396 case WESTON_LAUNCHER_OPEN:
397 ret = handle_open(wl, &msg, len);
405 quit(struct weston_launch *wl, int status)
407 struct vt_mode mode = { 0 };
414 err = pam_close_session(wl->ph, 0);
416 fprintf(stderr, "pam_close_session failed: %d: %s\n",
417 err, pam_strerror(wl->ph, err));
418 pam_end(wl->ph, err);
421 if (ioctl(wl->tty, KDSKBMUTE, 0) &&
422 ioctl(wl->tty, KDSKBMODE, wl->kb_mode))
423 fprintf(stderr, "failed to restore keyboard mode: %m\n");
425 if (ioctl(wl->tty, KDSETMODE, KD_TEXT))
426 fprintf(stderr, "failed to set KD_TEXT mode on tty: %m\n");
428 /* We have to drop master before we switch the VT back in
429 * VT_AUTO, so we don't risk switching to a VT with another
430 * display server, that will then fail to set drm master. */
431 drmDropMaster(wl->drm_fd);
434 if (ioctl(wl->tty, VT_SETMODE, &mode) < 0)
435 fprintf(stderr, "could not reset vt handling\n");
441 close_input_fds(struct weston_launch *wl)
446 for (fd = 3; fd <= wl->last_input_fd; fd++) {
447 if (fstat(fd, &s) == 0 && major(s.st_rdev) == INPUT_MAJOR) {
448 /* EVIOCREVOKE may fail if the kernel doesn't
449 * support it, but all we can do is ignore it. */
450 ioctl(fd, EVIOCREVOKE, 0);
457 handle_signal(struct weston_launch *wl)
459 struct signalfd_siginfo sig;
460 int pid, status, ret;
462 if (read(wl->signalfd, &sig, sizeof sig) != sizeof sig) {
463 error(0, errno, "reading signalfd failed");
467 switch (sig.ssi_signo) {
469 pid = waitpid(-1, &status, 0);
470 if (pid == wl->child) {
472 if (WIFEXITED(status))
473 ret = WEXITSTATUS(status);
474 else if (WIFSIGNALED(status))
476 * If weston dies because of signal N, we
477 * return 10+N. This is distinct from
478 * weston-launch dying because of a signal
481 ret = 10 + WTERMSIG(status);
490 kill(wl->child, sig.ssi_signo);
493 send_reply(wl, WESTON_LAUNCHER_DEACTIVATE);
495 drmDropMaster(wl->drm_fd);
496 ioctl(wl->tty, VT_RELDISP, 1);
499 ioctl(wl->tty, VT_RELDISP, VT_ACKACQ);
500 drmSetMaster(wl->drm_fd);
501 send_reply(wl, WESTON_LAUNCHER_ACTIVATE);
511 setup_tty(struct weston_launch *wl, const char *tty)
514 struct vt_mode mode = { 0 };
518 wl->tty = STDIN_FILENO;
520 t = ttyname(STDIN_FILENO);
521 if (t && strcmp(t, tty) == 0)
522 wl->tty = STDIN_FILENO;
524 wl->tty = open(tty, O_RDWR | O_NOCTTY);
526 int tty0 = open("/dev/tty0", O_WRONLY | O_CLOEXEC);
530 error(1, errno, "could not open tty0");
532 if (ioctl(tty0, VT_OPENQRY, &wl->ttynr) < 0 || wl->ttynr == -1)
533 error(1, errno, "failed to find non-opened console");
535 snprintf(filename, sizeof filename, "/dev/tty%d", wl->ttynr);
536 wl->tty = open(filename, O_RDWR | O_NOCTTY);
541 error(1, errno, "failed to open tty");
543 if (fstat(wl->tty, &buf) == -1 ||
544 major(buf.st_rdev) != TTY_MAJOR || minor(buf.st_rdev) == 0)
545 error(1, 0, "weston-launch must be run from a virtual terminal");
548 if (fstat(wl->tty, &buf) < 0)
549 error(1, errno, "stat %s failed", tty);
551 if (major(buf.st_rdev) != TTY_MAJOR)
552 error(1, 0, "invalid tty device: %s", tty);
554 wl->ttynr = minor(buf.st_rdev);
557 if (ioctl(wl->tty, KDGKBMODE, &wl->kb_mode))
558 error(1, errno, "failed to get current keyboard mode: %m\n");
560 if (ioctl(wl->tty, KDSKBMUTE, 1) &&
561 ioctl(wl->tty, KDSKBMODE, K_OFF))
562 error(1, errno, "failed to set K_OFF keyboard mode: %m\n");
564 if (ioctl(wl->tty, KDSETMODE, KD_GRAPHICS))
565 error(1, errno, "failed to set KD_GRAPHICS mode on tty: %m\n");
567 mode.mode = VT_PROCESS;
568 mode.relsig = SIGUSR1;
569 mode.acqsig = SIGUSR2;
570 if (ioctl(wl->tty, VT_SETMODE, &mode) < 0)
571 error(1, errno, "failed to take control of vt handling\n");
577 setup_session(struct weston_launch *wl)
583 if (wl->tty != STDIN_FILENO) {
585 error(1, errno, "setsid failed");
586 if (ioctl(wl->tty, TIOCSCTTY, 0) < 0)
587 error(1, errno, "TIOCSCTTY failed - tty is in use");
590 term = getenv("TERM");
593 setenv("TERM", term, 1);
594 setenv("USER", wl->pw->pw_name, 1);
595 setenv("LOGNAME", wl->pw->pw_name, 1);
596 setenv("HOME", wl->pw->pw_dir, 1);
597 setenv("SHELL", wl->pw->pw_shell, 1);
599 env = pam_getenvlist(wl->ph);
601 for (i = 0; env[i]; ++i) {
602 if (putenv(env[i]) < 0)
603 error(0, 0, "putenv %s failed", env[i]);
610 drop_privileges(struct weston_launch *wl)
612 if (setgid(wl->pw->pw_gid) < 0 ||
613 #ifdef HAVE_INITGROUPS
614 initgroups(wl->pw->pw_name, wl->pw->pw_gid) < 0 ||
616 setuid(wl->pw->pw_uid) < 0)
617 error(1, errno, "dropping privileges failed");
621 launch_compositor(struct weston_launch *wl, int argc, char *argv[])
623 char *child_argv[MAX_ARGV_SIZE];
628 printf("weston-launch: spawned weston with pid: %d\n", getpid());
635 setenv_fd("WESTON_TTY_FD", wl->tty);
636 setenv_fd("WESTON_LAUNCHER_SOCK", wl->sock[1]);
640 /* Do not give our signal mask to the new process. */
642 sigaddset(&mask, SIGTERM);
643 sigaddset(&mask, SIGCHLD);
644 sigaddset(&mask, SIGINT);
645 sigprocmask(SIG_UNBLOCK, &mask, NULL);
647 child_argv[0] = "/bin/sh";
648 child_argv[1] = "-l";
649 child_argv[2] = "-c";
650 child_argv[3] = BINDIR "/weston \"$@\"";
651 child_argv[4] = "weston";
652 for (i = 0; i < argc; ++i)
653 child_argv[5 + i] = argv[i];
654 child_argv[5 + i] = NULL;
656 execv(child_argv[0], child_argv);
657 error(1, errno, "exec failed");
661 help(const char *name)
663 fprintf(stderr, "Usage: %s [args...] [-- [weston args..]]\n", name);
664 fprintf(stderr, " -u, --user Start session as specified username\n");
665 fprintf(stderr, " -t, --tty Start session on alternative tty\n");
666 fprintf(stderr, " -v, --verbose Be verbose\n");
667 fprintf(stderr, " -h, --help Display this help message\n");
671 main(int argc, char *argv[])
673 struct weston_launch wl;
676 struct option opts[] = {
677 { "user", required_argument, NULL, 'u' },
678 { "tty", required_argument, NULL, 't' },
679 { "verbose", no_argument, NULL, 'v' },
680 { "help", no_argument, NULL, 'h' },
684 memset(&wl, 0, sizeof wl);
686 while ((c = getopt_long(argc, argv, "u:t::vh", opts, &i)) != -1) {
689 wl.new_user = optarg;
691 error(1, 0, "Permission denied. -u allowed for root only");
700 help("weston-launch");
705 if ((argc - optind) > (MAX_ARGV_SIZE - 6))
706 error(1, E2BIG, "Too many arguments to pass to weston");
709 wl.pw = getpwnam(wl.new_user);
711 wl.pw = getpwuid(getuid());
713 error(1, errno, "failed to get username");
715 if (!weston_launch_allowed(&wl))
716 error(1, 0, "Permission denied. You should either:\n"
717 #ifdef HAVE_SYSTEMD_LOGIN
718 " - run from an active and local (systemd) session.\n"
720 " - enable systemd session support for weston-launch.\n"
722 " - or add yourself to the 'weston-launch' group.");
724 if (setup_tty(&wl, tty) < 0)
727 if (wl.new_user && setup_pam(&wl) < 0)
730 if (setup_launcher_socket(&wl) < 0)
733 if (setup_signals(&wl) < 0)
737 if (wl.child == -1) {
738 error(1, errno, "fork failed");
743 launch_compositor(&wl, argc - optind, argv + optind);
746 if (wl.tty != STDIN_FILENO)
750 struct pollfd fds[2];
753 fds[0].fd = wl.sock[0];
754 fds[0].events = POLLIN;
755 fds[1].fd = wl.signalfd;
756 fds[1].events = POLLIN;
758 n = poll(fds, 2, -1);
760 error(0, errno, "poll failed");
761 if (fds[0].revents & POLLIN)
762 handle_socket_msg(&wl);