launch.c (11624B)
1 /* swc: launch/launch.c 2 * 3 * Copyright (c) 2013, 2014, 2016 Michael Forney 4 * 5 * Based in part upon weston-launch.c from weston which is: 6 * 7 * Copyright © 2012 Benjamin Franzke 8 * 9 * Permission is hereby granted, free of charge, to any person obtaining a copy 10 * of this software and associated documentation files (the "Software"), to deal 11 * in the Software without restriction, including without limitation the rights 12 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 * copies of the Software, and to permit persons to whom the Software is 14 * furnished to do so, subject to the following conditions: 15 * 16 * The above copyright notice and this permission notice shall be included in 17 * all copies or substantial portions of the Software. 18 * 19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 25 * SOFTWARE. 26 */ 27 28 #include "protocol.h" 29 #include "devmajor.h" 30 31 #include <errno.h> 32 #include <fcntl.h> 33 #include <limits.h> 34 #include <poll.h> 35 #include <spawn.h> 36 #include <stdbool.h> 37 #include <stdio.h> 38 #include <stdlib.h> 39 #include <stdnoreturn.h> 40 #include <string.h> 41 #include <unistd.h> 42 #include <signal.h> 43 44 #include <sys/socket.h> 45 #include <sys/stat.h> 46 #include <sys/wait.h> 47 #include <sys/ioctl.h> 48 #include <sys/types.h> 49 #ifndef minor 50 #include <sys/sysmacros.h> 51 #endif 52 #ifdef __NetBSD__ 53 #include <dev/wscons/wsdisplay_usl_io.h> 54 #else 55 #include <linux/input.h> 56 #include <linux/kd.h> 57 #include <linux/vt.h> 58 #endif 59 #include <xf86drm.h> 60 61 #define ARRAY_LENGTH(array) (sizeof(array) / sizeof(array)[0]) 62 63 static bool nflag; 64 static int sigfd[2], sock[2]; 65 static int input_fds[128], num_input_fds; 66 static int drm_fds[16], num_drm_fds; 67 static int tty_fd; 68 static bool active; 69 70 static struct { 71 bool altered; 72 int vt; 73 long kb_mode; 74 long console_mode; 75 } original_vt_state; 76 77 static void cleanup(void); 78 79 static noreturn void usage(const char *name) 80 { 81 fprintf(stderr, "usage: %s [-n] [-t tty] [--] server [args...]\n", name); 82 exit(2); 83 } 84 85 static noreturn void __attribute__((format(printf, 1, 2))) 86 die(const char *format, ...) 87 { 88 va_list args; 89 90 va_start(args, format); 91 vfprintf(stderr, format, args); 92 va_end(args); 93 94 if (format[0] && format[strlen(format) - 1] == ':') 95 fprintf(stderr, " %s", strerror(errno)); 96 fputc('\n', stderr); 97 98 cleanup(); 99 exit(EXIT_FAILURE); 100 } 101 102 static void 103 start_devices(void) 104 { 105 int i; 106 107 for (i = 0; i < num_drm_fds; ++i) { 108 if (drmSetMaster(drm_fds[i]) < 0) 109 die("failed to set DRM master"); 110 } 111 } 112 113 static void 114 stop_devices(bool fatal) 115 { 116 int i; 117 118 for (i = 0; i < num_drm_fds; ++i) { 119 if (drmDropMaster(drm_fds[i]) < 0 && fatal) 120 die("drmDropMaster:"); 121 } 122 for (i = 0; i < num_input_fds; ++i) { 123 #ifdef EVIOCREVOKE 124 if (ioctl(input_fds[i], EVIOCREVOKE, 0) < 0 && errno != ENODEV && fatal) 125 die("ioctl EVIOCREVOKE:"); 126 #endif 127 close(input_fds[i]); 128 } 129 num_input_fds = 0; 130 } 131 132 static void 133 cleanup(void) 134 { 135 struct vt_mode mode = {.mode = VT_AUTO}; 136 137 if (!original_vt_state.altered) 138 return; 139 140 /* Cleanup VT */ 141 ioctl(tty_fd, VT_SETMODE, &mode); 142 ioctl(tty_fd, KDSETMODE, original_vt_state.console_mode); 143 ioctl(tty_fd, KDSKBMODE, original_vt_state.kb_mode); 144 145 /* Stop devices before switching the VT to make sure we have released the DRM 146 * device before the next session tries to claim it. */ 147 stop_devices(false); 148 ioctl(tty_fd, VT_ACTIVATE, original_vt_state.vt); 149 150 kill(0, SIGTERM); 151 } 152 153 static void 154 activate(void) 155 { 156 struct swc_launch_event event = {.type = SWC_LAUNCH_EVENT_ACTIVATE}; 157 158 start_devices(); 159 send(sock[0], &event, sizeof(event), 0); 160 active = true; 161 } 162 163 static void 164 deactivate(void) 165 { 166 struct swc_launch_event event = {.type = SWC_LAUNCH_EVENT_DEACTIVATE}; 167 168 send(sock[0], &event, sizeof(event), 0); 169 stop_devices(true); 170 active = false; 171 } 172 173 static void 174 handle_signal(int sig) 175 { 176 write(sigfd[1], (char[]){sig}, 1); 177 } 178 179 static void 180 handle_socket_data(int socket) 181 { 182 struct swc_launch_request request; 183 struct swc_launch_event response; 184 char path[PATH_MAX]; 185 struct iovec request_iov[2] = { 186 {.iov_base = &request, .iov_len = sizeof(request)}, 187 {.iov_base = path, .iov_len = sizeof(path)}, 188 }; 189 struct iovec response_iov[1] = { 190 {.iov_base = &response, .iov_len = sizeof(response)}, 191 }; 192 int fd = -1; 193 struct stat st; 194 ssize_t size; 195 196 size = receive_fd(socket, &fd, request_iov, 2); 197 if (size == -1 || size == 0 || size < sizeof(request)) 198 return; 199 size -= sizeof(request); 200 201 response.type = SWC_LAUNCH_EVENT_RESPONSE; 202 response.serial = request.serial; 203 204 switch (request.type) { 205 case SWC_LAUNCH_REQUEST_OPEN_DEVICE: 206 if (size == 0 || path[size - 1] != '\0') { 207 fprintf(stderr, "path is not NULL terminated\n"); 208 goto fail; 209 } 210 if ((request.flags & (O_ACCMODE|O_NONBLOCK|O_CLOEXEC)) != request.flags) { 211 fprintf(stderr, "invalid open flags\n"); 212 goto fail; 213 } 214 215 fd = open(path, request.flags); 216 if (fd == -1) { 217 fprintf(stderr, "open %s: %s\n", path, strerror(errno)); 218 goto fail; 219 } 220 if (fstat(fd, &st) == -1) { 221 fprintf(stderr, "stat %s: %s\n", path, strerror(errno)); 222 goto fail; 223 } 224 225 if (device_is_input(st.st_rdev)) { 226 if (!active) 227 goto fail; 228 if (num_input_fds == ARRAY_LENGTH(input_fds)) { 229 fprintf(stderr, "too many input devices opened\n"); 230 goto fail; 231 } 232 input_fds[num_input_fds++] = fd; 233 } else if (device_is_drm(st.st_rdev)) { 234 if (num_drm_fds == ARRAY_LENGTH(drm_fds)) { 235 fprintf(stderr, "too many DRM devices opened\n"); 236 goto fail; 237 } 238 drm_fds[num_drm_fds++] = fd; 239 } else { 240 fprintf(stderr, "requested fd is not a DRM or input device\n"); 241 goto fail; 242 } 243 break; 244 case SWC_LAUNCH_REQUEST_ACTIVATE_VT: 245 if (!active) 246 goto fail; 247 248 if (ioctl(tty_fd, VT_ACTIVATE, request.vt) == -1) 249 fprintf(stderr, "failed to activate VT %d: %s\n", request.vt, strerror(errno)); 250 break; 251 default: 252 fprintf(stderr, "unknown request %u\n", request.type); 253 goto fail; 254 } 255 256 response.success = true; 257 goto done; 258 259 fail: 260 response.success = false; 261 if (fd != -1) 262 close(fd); 263 fd = -1; 264 done: 265 send_fd(socket, fd, response_iov, 1); 266 } 267 268 static void 269 find_vt(char *vt, size_t size) 270 { 271 #ifdef __NetBSD__ 272 if (snprintf(vt, size, "/dev/ttyE1") >= size) 273 die("VT number is too large"); 274 #else 275 char *vtnr; 276 int tty0_fd, vt_num; 277 278 /* If we are running from an existing X or wayland session, always open a new 279 * VT instead of using the current one. */ 280 if (getenv("DISPLAY") || getenv("WAYLAND_DISPLAY") || !(vtnr = getenv("XDG_VTNR"))) { 281 tty0_fd = open("/dev/tty0", O_RDWR); 282 if (tty0_fd == -1) 283 die("open /dev/tty0:"); 284 if (ioctl(tty0_fd, VT_OPENQRY, &vt_num) != 0) 285 die("VT open query failed:"); 286 close(tty0_fd); 287 if (snprintf(vt, size, "/dev/tty%d", vt_num) >= size) 288 die("VT number is too large"); 289 } else { 290 if (snprintf(vt, size, "/dev/tty%s", vtnr) >= size) 291 die("XDG_VTNR is too long"); 292 } 293 #endif 294 } 295 296 static int 297 open_tty(const char *tty_name) 298 { 299 char *stdin_tty; 300 int fd; 301 302 /* Check if we are already running on the desired VT */ 303 if ((stdin_tty = ttyname(STDIN_FILENO)) && strcmp(tty_name, stdin_tty) == 0) 304 return STDIN_FILENO; 305 306 fd = open(tty_name, O_RDWR | O_NOCTTY); 307 if (fd < 0) 308 die("open %s:", tty_name); 309 310 return fd; 311 } 312 313 static void 314 setup_tty(int fd) 315 { 316 struct stat st; 317 int vt; 318 struct vt_stat state; 319 struct vt_mode mode = { 320 .mode = VT_PROCESS, 321 .relsig = SIGUSR1, 322 .acqsig = SIGUSR2 323 }; 324 325 if (fstat(fd, &st) == -1) 326 die("failed to stat TTY fd:"); 327 vt = minor(st.st_rdev); 328 329 if (!device_is_tty(st.st_rdev) || vt == 0) 330 die("not a valid VT"); 331 332 if (ioctl(fd, VT_GETSTATE, &state) == -1) 333 die("failed to get the current VT state:"); 334 original_vt_state.vt = state.v_active; 335 #ifdef KDGETMODE 336 if (ioctl(fd, KDGKBMODE, &original_vt_state.kb_mode)) 337 die("failed to get keyboard mode:"); 338 if (ioctl(fd, KDGETMODE, &original_vt_state.console_mode)) 339 die("failed to get console mode:"); 340 #else 341 original_vt_state.kb_mode = K_XLATE; 342 original_vt_state.console_mode = KD_TEXT; 343 #endif 344 345 #ifdef K_OFF 346 if (ioctl(fd, KDSKBMODE, K_OFF) == -1) 347 die("failed to set keyboard mode to K_OFF:"); 348 #endif 349 if (ioctl(fd, KDSETMODE, KD_GRAPHICS) == -1) { 350 perror("failed to set console mode to KD_GRAPHICS"); 351 goto error0; 352 } 353 if (ioctl(fd, VT_SETMODE, &mode) == -1) { 354 perror("failed to set VT mode"); 355 goto error1; 356 } 357 358 if (vt == original_vt_state.vt) { 359 activate(); 360 } else if (!nflag) { 361 if (ioctl(fd, VT_ACTIVATE, vt) == -1) { 362 perror("failed to activate VT"); 363 goto error2; 364 } 365 366 if (ioctl(fd, VT_WAITACTIVE, vt) == -1) { 367 perror("failed to wait for VT to become active"); 368 goto error2; 369 } 370 } 371 372 original_vt_state.altered = true; 373 374 return; 375 376 error2: 377 mode = (struct vt_mode){.mode = VT_AUTO }; 378 ioctl(fd, VT_SETMODE, &mode); 379 error1: 380 ioctl(fd, KDSETMODE, original_vt_state.console_mode); 381 error0: 382 ioctl(fd, KDSKBMODE, original_vt_state.kb_mode); 383 exit(EXIT_FAILURE); 384 } 385 386 static void 387 run(int fd) { 388 struct pollfd fds[] = { 389 {.fd = fd, .events = POLLIN}, 390 {.fd = sigfd[0], .events = POLLIN}, 391 }; 392 int status; 393 char sig; 394 395 for (;;) { 396 if (poll(fds, ARRAY_LENGTH(fds), -1) < 0) { 397 if (errno == EINTR) 398 continue; 399 die("poll:"); 400 } 401 if (fds[0].revents) 402 handle_socket_data(fd); 403 if (fds[1].revents) { 404 if (read(sigfd[0], &sig, 1) <= 0) 405 continue; 406 switch (sig) { 407 case SIGCHLD: 408 wait(&status); 409 cleanup(); 410 exit(WEXITSTATUS(status)); 411 case SIGUSR1: 412 deactivate(); 413 ioctl(tty_fd, VT_RELDISP, 1); 414 break; 415 case SIGUSR2: 416 ioctl(tty_fd, VT_RELDISP, VT_ACKACQ); 417 activate(); 418 break; 419 } 420 } 421 } 422 } 423 424 int 425 main(int argc, char *argv[]) 426 { 427 extern char **environ; 428 int option; 429 char *vt = NULL, buf[64]; 430 struct sigaction action = { 431 .sa_handler = handle_signal, 432 .sa_flags = SA_RESTART, 433 }; 434 sigset_t set; 435 pid_t pid; 436 posix_spawnattr_t attr; 437 438 while ((option = getopt(argc, argv, "nt:")) != -1) { 439 switch (option) { 440 case 'n': 441 nflag = true; 442 break; 443 case 't': 444 vt = optarg; 445 break; 446 default: 447 usage(argv[0]); 448 } 449 } 450 451 if (argc - optind < 1) 452 usage(argv[0]); 453 454 if (socketpair(AF_LOCAL, SOCK_SEQPACKET, 0, sock) == -1) 455 die("socketpair:"); 456 if (fcntl(sock[0], F_SETFD, FD_CLOEXEC) == -1) 457 die("failed set CLOEXEC on socket:"); 458 459 if (pipe2(sigfd, O_CLOEXEC) == -1) 460 die("pipe:"); 461 if (sigaction(SIGCHLD, &action, NULL) == -1) 462 die("sigaction SIGCHLD:"); 463 if (sigaction(SIGUSR1, &action, NULL) == -1) 464 die("sigaction SIGUSR1:"); 465 if (sigaction(SIGUSR2, &action, NULL) == -1) 466 die("sigaction SIGUSR2:"); 467 468 sigfillset(&set); 469 sigdelset(&set, SIGCHLD); 470 sigdelset(&set, SIGUSR1); 471 sigdelset(&set, SIGUSR2); 472 sigprocmask(SIG_SETMASK, &set, NULL); 473 474 if (!vt) { 475 find_vt(buf, sizeof(buf)); 476 vt = buf; 477 } 478 479 fprintf(stderr, "running on %s\n", vt); 480 tty_fd = open_tty(vt); 481 setup_tty(tty_fd); 482 483 sprintf(buf, "%d", sock[1]); 484 setenv(SWC_LAUNCH_SOCKET_ENV, buf, 1); 485 486 if ((errno = posix_spawnattr_init(&attr))) 487 die("posix_spawnattr_init:"); 488 if ((errno = posix_spawnattr_setflags(&attr, POSIX_SPAWN_RESETIDS|POSIX_SPAWN_SETSIGMASK))) 489 die("posix_spawnattr_setflags:"); 490 sigemptyset(&set); 491 if ((errno = posix_spawnattr_setsigmask(&attr, &set))) 492 die("posix_spawnattr_setsigmask:"); 493 if ((errno = posix_spawnp(&pid, argv[optind], NULL, &attr, argv + optind, environ))) 494 die("posix_spawnp %s:", argv[optind]); 495 posix_spawnattr_destroy(&attr); 496 497 close(sock[1]); 498 run(sock[0]); 499 500 return EXIT_SUCCESS; 501 }