compositor: Don't set DPMS state on start up
[profile/ivi/weston.git] / src / weston-launch.c
1 /*
2  * Copyright © 2012 Benjamin Franzke
3  *
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.
13  *
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.
21  */
22
23 #define _GNU_SOURCE
24
25 #include "config.h"
26
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <assert.h>
31 #include <errno.h>
32
33 #include <error.h>
34 #include <getopt.h>
35
36 #include <sys/types.h>
37 #include <sys/ioctl.h>
38 #include <sys/stat.h>
39 #include <sys/wait.h>
40 #include <sys/socket.h>
41 #include <sys/epoll.h>
42 #include <sys/signalfd.h>
43 #include <signal.h>
44 #include <unistd.h>
45 #include <fcntl.h>
46
47 #include <termios.h>
48 #include <linux/vt.h>
49 #include <linux/major.h>
50
51 #include <pwd.h>
52 #include <grp.h>
53 #include <security/pam_appl.h>
54
55 #include <xf86drm.h>
56
57 #ifdef HAVE_SYSTEMD_LOGIN
58 #include <systemd/sd-login.h>
59 #endif
60
61 #include "weston-launch.h"
62
63 struct weston_launch {
64         struct pam_conv pc;
65         pam_handle_t *ph;
66         int tty;
67         int ttynr;
68         int sock[2];
69         struct passwd *pw;
70
71         int epollfd;
72         int signalfd;
73
74         pid_t child;
75         int verbose;
76 };
77
78 union cmsg_data { unsigned char b[4]; int fd; };
79
80 static gid_t *
81 read_groups(void)
82 {
83         int n;
84         gid_t *groups;
85         
86         n = getgroups(0, NULL);
87
88         if (n < 0) {
89                 fprintf(stderr, "Unable to retrieve groups: %m\n");
90                 return NULL;
91         }
92
93         groups = malloc(n * sizeof(gid_t));
94         if (!groups)
95                 return NULL;
96
97         if (getgroups(n, groups) < 0) {
98                 fprintf(stderr, "Unable to retrieve groups: %m\n");
99                 free(groups);
100                 return NULL;
101         }
102         return groups;
103 }
104
105 static int
106 weston_launch_allowed(struct weston_launch *wl)
107 {
108         struct group *gr;
109         gid_t *groups;
110         int i;
111 #ifdef HAVE_SYSTEMD_LOGIN
112         char *session, *seat;
113         int err;
114 #endif
115
116         if (getuid() == 0)
117                 return 1;
118
119         gr = getgrnam("weston-launch");
120         if (gr) {
121                 groups = read_groups();
122                 if (groups) {
123                         for (i = 0; groups[i]; ++i) {
124                                 if (groups[i] == gr->gr_gid) {
125                                         free(groups);
126                                         return 1;
127                                 }
128                         }
129                         free(groups);
130                 }
131         }
132
133 #ifdef HAVE_SYSTEMD_LOGIN
134         err = sd_pid_get_session(getpid(), &session);
135         if (err == 0 && session) {
136                 if (sd_session_is_active(session) &&
137                     sd_session_get_seat(session, &seat) == 0) {
138                         free(seat);
139                         free(session);
140                         return 1;
141                 }
142                 free(session);
143         }
144 #endif
145         
146         return 0;
147 }
148
149 static int
150 pam_conversation_fn(int msg_count,
151                     const struct pam_message **messages,
152                     struct pam_response **responses,
153                     void *user_data)
154 {
155         return PAM_SUCCESS;
156 }
157
158 static int
159 setup_pam(struct weston_launch *wl)
160 {
161         int err;
162
163         wl->pc.conv = pam_conversation_fn;
164         wl->pc.appdata_ptr = wl;
165
166         err = pam_start("login", wl->pw->pw_name, &wl->pc, &wl->ph);
167         if (err != PAM_SUCCESS) {
168                 fprintf(stderr, "failed to start pam transaction: %d: %s\n",
169                         err, pam_strerror(wl->ph, err));
170                 return -1;
171         }
172
173         err = pam_set_item(wl->ph, PAM_TTY, ttyname(wl->tty));
174         if (err != PAM_SUCCESS) {
175                 fprintf(stderr, "failed to set PAM_TTY item: %d: %s\n",
176                         err, pam_strerror(wl->ph, err));
177                 return -1;
178         }
179
180         err = pam_open_session(wl->ph, 0);
181         if (err != PAM_SUCCESS) {
182                 fprintf(stderr, "failed to open pam session: %d: %s\n",
183                         err, pam_strerror(wl->ph, err));
184                 return -1;
185         }
186
187         return 0;
188 }
189
190 static int
191 setup_launcher_socket(struct weston_launch *wl)
192 {
193         struct epoll_event ev;
194
195         if (socketpair(AF_LOCAL, SOCK_DGRAM, 0, wl->sock) < 0)
196                 error(1, errno, "socketpair failed");
197         
198         fcntl(wl->sock[0], F_SETFD, O_CLOEXEC);
199
200         memset(&ev, 0, sizeof ev);
201         ev.events = EPOLLIN;
202         ev.data.fd = wl->sock[0];
203         if (epoll_ctl(wl->epollfd, EPOLL_CTL_ADD, ev.data.fd, &ev) < 0)
204                 return -errno;
205
206         return 0;
207 }
208
209 static int
210 setup_signals(struct weston_launch *wl)
211 {
212         sigset_t mask;
213         struct sigaction sa;
214         struct epoll_event ev;
215
216         memset(&sa, 0, sizeof sa);
217         sa.sa_handler = SIG_DFL;
218         sa.sa_flags = SA_NOCLDSTOP | SA_RESTART;
219         assert(sigaction(SIGCHLD, &sa, NULL) == 0);
220
221         assert(sigemptyset(&mask) == 0);
222         sigaddset(&mask, SIGCHLD);
223         sigaddset(&mask, SIGINT);
224         sigaddset(&mask, SIGTERM);
225         assert(sigprocmask(SIG_BLOCK, &mask, NULL) == 0);
226
227         wl->signalfd = signalfd(-1, &mask, SFD_NONBLOCK | SFD_CLOEXEC);
228         if (wl->signalfd < 0)
229                 return -errno;
230
231         memset(&ev, 0, sizeof ev);
232         ev.events = EPOLLIN;
233         ev.data.fd = wl->signalfd;
234         if (epoll_ctl(wl->epollfd, EPOLL_CTL_ADD, ev.data.fd, &ev) < 0)
235                 return -errno;
236
237         return 0;
238 }
239
240 static void
241 setenv_fd(const char *env, int fd)
242 {
243         char buf[32];
244
245         snprintf(buf, sizeof buf, "%d", fd);
246         setenv(env, buf, 1);
247 }
248
249 static int
250 handle_setmaster(struct weston_launch *wl, struct msghdr *msg, ssize_t len)
251 {
252         int ret = -1;
253         struct cmsghdr *cmsg;
254         struct weston_launcher_set_master *message;
255         union cmsg_data *data;
256
257         if (len != sizeof(*message)) {
258                 error(0, 0, "missing value in setmaster request");
259                 goto out;
260         }
261
262         message = msg->msg_iov->iov_base;
263
264         cmsg = CMSG_FIRSTHDR(msg);
265         if (!cmsg ||
266             cmsg->cmsg_level != SOL_SOCKET ||
267             cmsg->cmsg_type != SCM_RIGHTS) {
268                 error(0, 0, "invalid control message");
269                 goto out;
270         }
271
272         data = (union cmsg_data *) CMSG_DATA(cmsg);
273         if (data->fd == -1) {
274                 error(0, 0, "missing drm fd in socket request");
275                 goto out;
276         }
277
278         if (message->set_master)
279                 ret = drmSetMaster(data->fd);
280         else
281                 ret = drmDropMaster(data->fd);
282
283         close(data->fd);
284 out:
285         do {
286                 len = send(wl->sock[0], &ret, sizeof ret, 0);
287         } while (len < 0 && errno == EINTR);
288         if (len < 0)
289                 return -1;
290
291         return 0;
292 }
293
294 static int
295 handle_open(struct weston_launch *wl, struct msghdr *msg, ssize_t len)
296 {
297         int fd = -1, ret = -1;
298         char control[CMSG_SPACE(sizeof(fd))];
299         struct cmsghdr *cmsg;
300         struct stat s;
301         struct msghdr nmsg;
302         struct iovec iov;
303         struct weston_launcher_open *message;
304         union cmsg_data *data;
305
306         message = msg->msg_iov->iov_base;
307         if ((size_t)len < sizeof(*message))
308                 goto err0;
309
310         /* Ensure path is null-terminated */
311         ((char *) message)[len-1] = '\0';
312
313         if (stat(message->path, &s) < 0)
314                 goto err0;
315
316         fd = open(message->path, message->flags);
317         if (fd < 0)
318                 goto err0;
319
320         if (major(s.st_rdev) != INPUT_MAJOR) {
321                 close(fd);
322                 fd = -1;
323                 goto err0;
324         }
325
326 err0:
327         memset(&nmsg, 0, sizeof nmsg);
328         nmsg.msg_iov = &iov;
329         nmsg.msg_iovlen = 1;
330         if (fd != -1) {
331                 nmsg.msg_control = control;
332                 nmsg.msg_controllen = sizeof control;
333                 cmsg = CMSG_FIRSTHDR(&nmsg);
334                 cmsg->cmsg_level = SOL_SOCKET;
335                 cmsg->cmsg_type = SCM_RIGHTS;
336                 cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
337                 data = (union cmsg_data *) CMSG_DATA(cmsg);
338                 data->fd = fd;
339                 nmsg.msg_controllen = cmsg->cmsg_len;
340                 ret = 0;
341         }
342         iov.iov_base = &ret;
343         iov.iov_len = sizeof ret;
344
345         if (wl->verbose)
346                 fprintf(stderr, "weston-launch: opened %s: ret: %d, fd: %d\n",
347                         message->path, ret, fd);
348         do {
349                 len = sendmsg(wl->sock[0], &nmsg, 0);
350         } while (len < 0 && errno == EINTR);
351
352         if (len < 0)
353                 return -1;
354
355         return 0;
356 }
357
358 static int
359 handle_socket_msg(struct weston_launch *wl)
360 {
361         char control[CMSG_SPACE(sizeof(int))];
362         char buf[BUFSIZ];
363         struct msghdr msg;
364         struct iovec iov;
365         int ret = -1;
366         ssize_t len;
367         struct weston_launcher_message *message;
368
369         memset(&msg, 0, sizeof(msg));
370         iov.iov_base = buf;
371         iov.iov_len  = sizeof buf;
372         msg.msg_iov = &iov;
373         msg.msg_iovlen = 1;
374         msg.msg_control = control;
375         msg.msg_controllen = sizeof control;
376
377         do {
378                 len = recvmsg(wl->sock[0], &msg, 0);
379         } while (len < 0 && errno == EINTR);
380
381         if (len < 1)
382                 return -1;
383
384         message = (void *) buf;
385         switch (message->opcode) {
386         case WESTON_LAUNCHER_OPEN:
387                 ret = handle_open(wl, &msg, len);
388                 break;
389         case WESTON_LAUNCHER_DRM_SET_MASTER:
390                 ret = handle_setmaster(wl, &msg, len);
391                 break;
392         }
393
394         return ret;
395 }
396
397 static void
398 quit(struct weston_launch *wl, int status)
399 {
400         int err;
401
402         close(wl->epollfd);
403         close(wl->signalfd);
404         close(wl->sock[0]);
405
406         err = pam_close_session(wl->ph, 0);
407         if (err)
408                 fprintf(stderr, "pam_close_session failed: %d: %s\n",
409                         err, pam_strerror(wl->ph, err));
410         pam_end(wl->ph, err);
411
412         exit(status);
413 }
414
415 static int
416 handle_signal(struct weston_launch *wl)
417 {
418         struct signalfd_siginfo sig;
419         int pid, status;
420
421         if (read(wl->signalfd, &sig, sizeof sig) != sizeof sig) {
422                 error(0, errno, "reading signalfd failed");
423                 return -1;
424         }
425
426         switch (sig.ssi_signo) {
427         case SIGCHLD:
428                 pid = waitpid(-1, &status, 0);
429                 if (pid == wl->child) {
430                         wl->child = 0;
431                         quit(wl, WIFEXITED(status) ? WEXITSTATUS(status) : 0);
432                 }
433                 break;
434         case SIGTERM:
435                 if (wl->child)
436                         kill(wl->child, SIGTERM);
437                 quit(wl, 0);
438                 break;
439         case SIGINT:
440                 if (wl->child)
441                         kill(wl->child, SIGTERM);
442                 break;
443         default:
444                 return -1;
445         }
446
447         return 0;
448 }
449
450 static int
451 setup_tty(struct weston_launch *wl, const char *tty)
452 {
453         struct stat buf;
454         char *t;
455
456         if (tty) {
457                 t = ttyname(STDIN_FILENO);
458                 if (t && strcmp(t, tty) == 0)
459                         wl->tty = STDIN_FILENO;
460                 else
461                         wl->tty = open(tty, O_RDWR | O_NOCTTY);
462         } else {
463                 int tty0 = open("/dev/tty0", O_WRONLY | O_CLOEXEC);
464                 char filename[16];
465
466                 if (tty0 < 0)
467                         error(1, errno, "could not open tty0");
468
469                 if (ioctl(tty0, VT_OPENQRY, &wl->ttynr) < 0 || wl->ttynr == -1)
470                         error(1, errno, "failed to find non-opened console"); 
471
472                 snprintf(filename, sizeof filename, "/dev/tty%d", wl->ttynr);
473                 wl->tty = open(filename, O_RDWR | O_NOCTTY);
474                 close(tty0);
475         }
476
477         if (wl->tty < 0)
478                 error(1, errno, "failed to open tty");
479
480         if (tty) {
481                 if (fstat(wl->tty, &buf) < 0)
482                         error(1, errno, "stat %s failed", tty);
483
484                 if (major(buf.st_rdev) != TTY_MAJOR)
485                         error(1, 0, "invalid tty device: %s", tty);
486
487                 wl->ttynr = minor(buf.st_rdev);
488         }
489
490         return 0;
491 }
492
493 static void
494 help(const char *name)
495 {
496         fprintf(stderr, "Usage: %s [args...] [-- [weston args..]]\n", name);
497         fprintf(stderr, "  -u, --user      Start session as specified username\n");
498         fprintf(stderr, "  -t, --tty       Start session on alternative tty\n");
499         fprintf(stderr, "  -v, --verbose   Be verbose\n");
500         fprintf(stderr, "  -s, --sleep     Sleep specified amount of time before exec\n");
501         fprintf(stderr, "  -h, --help      Display this help message\n");
502 }
503
504 int
505 main(int argc, char *argv[])
506 {
507         struct weston_launch wl;
508         char **env;
509         int i, c;
510         char **child_argv;
511         char *tty = NULL, *new_user = NULL;
512         int sleep_fork = 0;
513         struct option opts[] = {
514                 { "user",    required_argument, NULL, 'u' },
515                 { "tty",     required_argument, NULL, 't' },
516                 { "verbose", no_argument,       NULL, 'v' },
517                 { "sleep",   optional_argument, NULL, 's' },
518                 { "help",    no_argument,       NULL, 'h' },
519                 { 0,         0,                 NULL,  0  }
520         };      
521
522         memset(&wl, 0, sizeof wl);
523
524         while ((c = getopt_long(argc, argv, "u:t:s::vh", opts, &i)) != -1) {
525                 switch (c) {
526                 case 'u':
527                         new_user = optarg;
528                         if (getuid() != 0)
529                                 error(1, 0, "Permission denied. -u allowed for root only");
530                         break;
531                 case 't':
532                         tty = optarg;
533                         break;
534                 case 'v':
535                         wl.verbose = 1;
536                         break;
537                 case 's':
538                         if (optarg)
539                                 sleep_fork = atoi(optarg);
540                         else
541                                 sleep_fork = 10;
542                         break;
543                 case 'h':
544                         help("weston-launch");
545                         exit(1);
546                 }
547         }
548
549         child_argv = &argv[optind-1];
550         child_argv[0] = BINDIR "/weston";
551
552         if (new_user)
553                 wl.pw = getpwnam(new_user);
554         else
555                 wl.pw = getpwuid(getuid());
556         if (wl.pw == NULL)
557                 error(1, errno, "failed to get username");
558
559         clearenv();
560         setenv("USER", wl.pw->pw_name, 1);
561         setenv("LOGNAME", wl.pw->pw_name, 1);
562         setenv("HOME", wl.pw->pw_dir, 1);
563         setenv("SHELL", wl.pw->pw_shell, 1);
564
565         if (!weston_launch_allowed(&wl))
566                 error(1, 0, "Permission denied. You should either:\n"
567 #ifdef HAVE_SYSTEMD_LOGIN
568                       " - run from an active and local (systemd) session.\n"
569 #else
570                       " - enable systemd session support for weston-launch.\n"
571 #endif
572                       " - or add yourself to the 'weston-launch' group.");
573
574         if (setup_tty(&wl, tty) < 0)
575                 return 1;
576
577         if (setup_pam(&wl) < 0)
578                 return 1;
579
580         wl.epollfd = epoll_create1(EPOLL_CLOEXEC);
581         if (wl.epollfd < 0)
582                 error(1, errno, "epoll create failed");
583
584         if (setup_launcher_socket(&wl) < 0)
585                 return 1;
586
587         if (setup_signals(&wl) < 0)
588                 return 1;
589
590         switch ((wl.child = fork())) {
591         case -1:
592                 error(1, errno, "fork failed");
593                 break;
594         case 0:
595                 if (wl.verbose)
596                         printf("weston-launch: spawned weston with pid: %d\n", getpid());
597                 if (wl.tty != STDIN_FILENO) {
598                         if (setsid() < 0)
599                                 error(1, errno, "setsid failed");
600                         if (ioctl(wl.tty, TIOCSCTTY, 0) < 0)
601                                 error(1, errno, "TIOCSCTTY failed - tty is in use");
602                 }
603
604                 if (setgid(wl.pw->pw_gid) < 0 ||
605                     setuid(wl.pw->pw_uid) < 0)
606                         error(1, errno, "dropping privilidges failed");
607
608                 if (sleep_fork) {
609                         if (wl.verbose)
610                                 printf("weston-launch: waiting %d seconds\n", sleep_fork);
611                         sleep(sleep_fork);
612                 }
613
614                 env = pam_getenvlist(wl.ph);
615                 if (env) {
616                         for (i = 0; env[i]; ++i) {
617                                 if (putenv(env[i]) < 0)
618                                         error(0, 0, "putenv %s failed", env[i]);
619                         }
620                         free(env);
621                 }
622
623                 if (wl.tty != STDIN_FILENO)
624                         setenv_fd("WESTON_TTY_FD", wl.tty);
625
626                 setenv_fd("WESTON_LAUNCHER_SOCK", wl.sock[1]);
627
628                 unsetenv("DISPLAY");
629
630                 execv(child_argv[0], child_argv);
631                 error(1, errno, "exec failed");
632                 break;
633         default:
634                 close(wl.sock[1]);
635                 if (wl.tty != STDIN_FILENO)
636                         close(wl.tty);
637
638                 while (1) {
639                         struct epoll_event ev;
640                         int n;
641
642                         n = epoll_wait(wl.epollfd, &ev, 1, -1);
643                         if (n < 0)
644                                 error(0, errno, "epoll_wait failed");
645                         if (n != 1)
646                                 continue;
647
648                         if (ev.data.fd == wl.sock[0])
649                                 handle_socket_msg(&wl);
650                         else if (ev.data.fd == wl.signalfd)
651                                 handle_signal(&wl);
652                 }
653                 break;
654         }
655
656         return 0;
657 }