1 /* 2 3 Copyright 1986, 1998 The Open Group 4 5 Permission to use, copy, modify, distribute, and sell this software and its 6 documentation for any purpose is hereby granted without fee, provided that 7 the above copyright notice appear in all copies and that both that 8 copyright notice and this permission notice appear in supporting 9 documentation. 10 11 The above copyright notice and this permission notice shall be included in 12 all copies or substantial portions of the Software. 13 14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN 18 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 19 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 21 Except as contained in this notice, the name of The Open Group shall not be 22 used in advertising or otherwise to promote the sale, use or other dealings 23 in this Software without prior written authorization from The Open Group. 24 25 */ 26 27 #ifdef HAVE_CONFIG_H 28 # include "config.h" 29 #endif 30 31 #include <X11/Xlib.h> 32 #include <X11/Xos.h> 33 #include <X11/Xatom.h> 34 #include <X11/Xfuncproto.h> 35 #include <stdio.h> 36 #include <ctype.h> 37 #include <stddef.h> 38 #include <stdint.h> 39 40 #include <signal.h> 41 #include <sys/wait.h> 42 #include <errno.h> 43 #include <setjmp.h> 44 #include <stdarg.h> 45 46 #ifdef __APPLE__ 47 #include <AvailabilityMacros.h> 48 #if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060 49 #include <vproc.h> 50 #endif 51 #endif 52 53 /* For PRIO_PROCESS and setpriority() */ 54 #include <sys/time.h> 55 #include <sys/resource.h> 56 57 #include <stdlib.h> 58 59 #ifndef SHELL 60 #define SHELL "sh" 61 #endif 62 63 static const char *bindir = BINDIR; 64 static const char * const server_names[] = { 65 #ifdef __APPLE__ 66 "Xquartz Mac OSX Quartz displays.", 67 #else 68 # ifdef __CYGWIN__ 69 "XWin X Server for the Cygwin environment on Microsoft Windows", 70 # else 71 "Xorg Common X server for most displays", 72 # endif 73 #endif 74 "Xvfb Virtual frame buffer", 75 "Xfake kdrive-based virtual frame buffer", 76 "Xnest X server nested in a window on another X server", 77 "Xephyr kdrive-based nested X server", 78 "Xvnc X server accessed over VNC's RFB protocol", 79 NULL}; 80 81 #ifndef XINITRC 82 #define XINITRC ".xinitrc" 83 #endif 84 static char xinitrcbuf[256]; 85 86 #ifndef XSERVERRC 87 #define XSERVERRC ".xserverrc" 88 #endif 89 static char xserverrcbuf[256]; 90 91 #define TRUE 1 92 #define FALSE 0 93 94 static char *default_server = "X"; 95 static char *default_display = ":0"; /* choose most efficient */ 96 static char *default_client[] = {"xterm", "-geometry", "+1+1", "-n", "login", NULL}; 97 static char *serverargv[100]; 98 static char *clientargv[100]; 99 static char **server = serverargv + 2; /* make sure room for sh .xserverrc args */ 100 static char **client = clientargv + 2; /* make sure room for sh .xinitrc args */ 101 static char *displayNum = NULL; 102 static char *program = NULL; 103 static Display *xd = NULL; /* server connection */ 104 static int status; 105 static pid_t serverpid = -1; 106 static pid_t clientpid = -1; 107 static volatile int gotSignal = 0; 108 109 static void Execute(char **vec); 110 static Bool waitforserver(void); 111 static Bool processTimeout(int timeout, const char *string); 112 static pid_t startServer(char *server[]); 113 static pid_t startClient(char *client[]); 114 static int ignorexio(Display *dpy); 115 static void shutdown(void); 116 static void set_environment(void); 117 118 static void Fatal(const char *fmt, ...) _X_ATTRIBUTE_PRINTF(1,2) _X_NORETURN; 119 static void Error(const char *fmt, ...) _X_ATTRIBUTE_PRINTF(1,2); 120 static void Fatalx(const char *fmt, ...) _X_ATTRIBUTE_PRINTF(1,2) _X_NORETURN; 121 static void Errorx(const char *fmt, ...) _X_ATTRIBUTE_PRINTF(1,2); 122 123 static void 124 sigCatch(int sig) 125 { 126 /* On system with POSIX signals, just interrupt the system call */ 127 gotSignal = sig; 128 } 129 130 static void 131 sigIgnore(_X_UNUSED int sig) 132 { 133 } 134 135 static void 136 Execute(char **vec) /* has room from up above */ 137 { 138 execvp(vec[0], vec); 139 if (access(vec[0], R_OK) == 0) { 140 vec--; /* back it up to stuff shell in */ 141 vec[0] = SHELL; 142 execvp(vec[0], vec); 143 } 144 return; 145 } 146 147 int 148 main(int argc, char *argv[]) 149 { 150 register char **sptr = server; 151 register char **cptr = client; 152 register char **ptr; 153 pid_t pid; 154 int client_given = 0, server_given = 0; 155 ptrdiff_t start_of_client_args, start_of_server_args; 156 struct sigaction sa, si; 157 #ifdef __APPLE__ 158 #if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060 159 vproc_transaction_t vt; 160 #endif 161 #endif 162 163 program = *argv++; 164 argc--; 165 /* 166 * copy the client args. 167 */ 168 if (argc == 0 || 169 (**argv != '/' && **argv != '.')) { 170 for (ptr = default_client; *ptr; ) 171 *cptr++ = *ptr++; 172 } else { 173 client_given = 1; 174 } 175 start_of_client_args = (cptr - client); 176 while (argc && strcmp(*argv, "--")) { 177 if (cptr > clientargv + sizeof(clientargv) / sizeof(*clientargv) - 2) 178 Fatalx("too many client arguments"); 179 *cptr++ = *argv++; 180 argc--; 181 } 182 *cptr = NULL; 183 if (argc) { 184 argv++; 185 argc--; 186 } 187 188 /* 189 * Copy the server args. 190 */ 191 if (argc == 0 || 192 (**argv != '/' && **argv != '.')) { 193 *sptr++ = default_server; 194 } else { 195 server_given = 1; 196 *sptr++ = *argv++; 197 argc--; 198 } 199 if (argc > 0 && (argv[0][0] == ':' && isdigit(argv[0][1]))) 200 displayNum = *argv; 201 else 202 displayNum = *sptr++ = default_display; 203 204 start_of_server_args = (sptr - server); 205 while (--argc >= 0) { 206 if (sptr > serverargv + sizeof(serverargv) / sizeof(*serverargv) - 2) 207 Fatalx("too many server arguments"); 208 *sptr++ = *argv++; 209 } 210 *sptr = NULL; 211 212 /* 213 * if no client arguments given, check for a startup file and copy 214 * that into the argument list 215 */ 216 if (!client_given) { 217 char *cp; 218 Bool required = False; 219 220 xinitrcbuf[0] = '\0'; 221 if ((cp = getenv("XINITRC")) != NULL) { 222 snprintf(xinitrcbuf, sizeof(xinitrcbuf), "%s", cp); 223 required = True; 224 } else if ((cp = getenv("HOME")) != NULL) { 225 snprintf(xinitrcbuf, sizeof(xinitrcbuf), 226 "%s/%s", cp, XINITRC); 227 } 228 if (xinitrcbuf[0]) { 229 if (access(xinitrcbuf, F_OK) == 0) { 230 client += start_of_client_args - 1; 231 client[0] = xinitrcbuf; 232 } else if (required) { 233 Error("warning, no client init file \"%s\"", xinitrcbuf); 234 } 235 } 236 } 237 238 /* 239 * if no server arguments given, check for a startup file and copy 240 * that into the argument list 241 */ 242 if (!server_given) { 243 char *cp; 244 Bool required = False; 245 246 xserverrcbuf[0] = '\0'; 247 if ((cp = getenv("XSERVERRC")) != NULL) { 248 snprintf(xserverrcbuf, sizeof(xserverrcbuf), "%s", cp); 249 required = True; 250 } else if ((cp = getenv("HOME")) != NULL) { 251 snprintf(xserverrcbuf, sizeof(xserverrcbuf), 252 "%s/%s", cp, XSERVERRC); 253 } 254 if (xserverrcbuf[0]) { 255 if (access(xserverrcbuf, F_OK) == 0) { 256 server += start_of_server_args - 1; 257 server[0] = xserverrcbuf; 258 } else if (required) { 259 Error("warning, no server init file \"%s\"", xserverrcbuf); 260 } 261 } 262 } 263 264 /* 265 * Start the server and client. 266 */ 267 268 /* Let those signal interrupt the wait() call in the main loop */ 269 memset(&sa, 0, sizeof sa); 270 sa.sa_handler = sigCatch; 271 sigemptyset(&sa.sa_mask); 272 sa.sa_flags = 0; /* do not set SA_RESTART */ 273 274 sigaction(SIGTERM, &sa, NULL); 275 sigaction(SIGQUIT, &sa, NULL); 276 sigaction(SIGINT, &sa, NULL); 277 sigaction(SIGHUP, &sa, NULL); 278 sigaction(SIGPIPE, &sa, NULL); 279 280 memset(&si, 0, sizeof(si)); 281 si.sa_handler = sigIgnore; 282 sigemptyset(&si.sa_mask); 283 si.sa_flags = SA_RESTART; 284 285 sigaction(SIGALRM, &si, NULL); 286 sigaction(SIGUSR1, &si, NULL); 287 sigaction(SIGCHLD, &si, NULL); 288 289 #ifdef __APPLE__ 290 #if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060 291 vt = vproc_transaction_begin(NULL); 292 #endif 293 #endif 294 295 if (startServer(server) > 0 296 && startClient(client) > 0) { 297 pid = -1; 298 while (pid != clientpid && pid != serverpid 299 && gotSignal == 0 300 ) 301 pid = wait(NULL); 302 } 303 304 #ifdef __APPLE__ 305 #if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060 306 vproc_transaction_end(NULL, vt); 307 #endif 308 #endif 309 310 signal(SIGTERM, SIG_IGN); 311 signal(SIGQUIT, SIG_IGN); 312 signal(SIGINT, SIG_IGN); 313 signal(SIGHUP, SIG_IGN); 314 signal(SIGPIPE, SIG_IGN); 315 316 shutdown(); 317 318 if (serverpid < 0) 319 Fatalx("server error"); 320 if (clientpid < 0) 321 Fatalx("client error"); 322 exit(EXIT_SUCCESS); 323 } 324 325 326 /* 327 * waitforserver - wait for X server to start up 328 */ 329 static Bool 330 waitforserver(void) 331 { 332 int ncycles = 120; /* # of cycles to wait */ 333 int cycles; /* Wait cycle count */ 334 335 #ifdef __APPLE__ 336 /* For Apple, we don't get signaled by the server when it's ready, so we just 337 * want to sleep now since we're going to sleep later anyways and this allows us 338 * to avoid the awkard, "why is there an error message in the log" questions 339 * from users. 340 */ 341 342 sleep(2); 343 #endif 344 345 for (cycles = 0; cycles < ncycles; cycles++) { 346 if ((xd = XOpenDisplay(displayNum))) { 347 return(TRUE); 348 } 349 else { 350 if (!processTimeout(1, "X server to begin accepting connections")) 351 break; 352 } 353 } 354 355 Errorx("giving up"); 356 357 return(FALSE); 358 } 359 360 /* 361 * return TRUE if we timeout waiting for pid to exit, FALSE otherwise. 362 */ 363 static Bool 364 processTimeout(int timeout, const char *string) 365 { 366 int i = 0; 367 pid_t pidfound = -1; 368 static const char *laststring; 369 370 for (;;) { 371 if ((pidfound = waitpid(serverpid, &status, WNOHANG)) == serverpid) 372 break; 373 if (timeout) { 374 if (i == 0 && string != laststring) 375 fprintf(stderr, "\r\nwaiting for %s ", string); 376 else 377 fprintf(stderr, "."); 378 fflush(stderr); 379 sleep(1); 380 } 381 if (++i > timeout) 382 break; 383 } 384 if (i > 0) fputc('\n', stderr); /* tidy up after message */ 385 laststring = string; 386 return (serverpid != pidfound); 387 } 388 389 static pid_t 390 startServer(char *server_argv[]) 391 { 392 sigset_t mask, old; 393 const char * const *cpp; 394 395 sigemptyset(&mask); 396 sigaddset(&mask, SIGUSR1); 397 sigprocmask(SIG_BLOCK, &mask, &old); 398 399 serverpid = fork(); 400 401 switch(serverpid) { 402 case 0: 403 /* Unblock */ 404 sigprocmask(SIG_SETMASK, &old, NULL); 405 406 /* 407 * don't hang on read/write to control tty 408 */ 409 signal(SIGTTIN, SIG_IGN); 410 signal(SIGTTOU, SIG_IGN); 411 /* 412 * ignore SIGUSR1 in child. The server 413 * will notice this and send SIGUSR1 back 414 * at xinit when ready to accept connections 415 */ 416 signal(SIGUSR1, SIG_IGN); 417 /* 418 * prevent server from getting sighup from vhangup() 419 * if client is xterm -L 420 */ 421 setpgid(0,getpid()); 422 Execute(server_argv); 423 424 Error("unable to run server \"%s\"", server_argv[0]); 425 426 fprintf(stderr, "Use the -- option, or make sure that %s is in your path and\n", bindir); 427 fprintf(stderr, "that \"%s\" is a program or a link to the right type of server\n", server_argv[0]); 428 fprintf(stderr, "for your display. Possible server names include:\n\n"); 429 for (cpp = server_names; *cpp; cpp++) 430 fprintf(stderr, " %s\n", *cpp); 431 fprintf(stderr, "\n"); 432 433 exit(EXIT_FAILURE); 434 435 break; 436 case -1: 437 break; 438 default: 439 /* 440 * don't nice server 441 */ 442 setpriority(PRIO_PROCESS, serverpid, -1); 443 444 errno = 0; 445 if(! processTimeout(0, "")) { 446 serverpid = -1; 447 break; 448 } 449 /* 450 * kludge to avoid race with TCP, giving server time to 451 * set his socket options before we try to open it, 452 * either use the 15 second timeout, or await SIGUSR1. 453 * 454 * If your machine is substantially slower than 15 seconds, 455 * you can easily adjust this value. 456 */ 457 alarm(15); 458 459 sigsuspend(&old); 460 alarm(0); 461 sigprocmask(SIG_SETMASK, &old, NULL); 462 463 if (waitforserver() == 0) { 464 Error("unable to connect to X server"); 465 shutdown(); 466 serverpid = -1; 467 } 468 break; 469 } 470 471 return(serverpid); 472 } 473 474 static void 475 setWindowPath(void) 476 { 477 /* setting WINDOWPATH for clients */ 478 Atom prop; 479 Atom actualtype; 480 int actualformat; 481 unsigned long nitems; 482 unsigned long bytes_after; 483 unsigned char *buf; 484 const char *windowpath; 485 char *newwindowpath; 486 unsigned long num; 487 #ifndef HAVE_ASPRINTF 488 char nums[10]; 489 int numn; 490 size_t len; 491 #endif 492 493 prop = XInternAtom(xd, "XFree86_VT", False); 494 if (prop == None) { 495 Errorx("Unable to intern XFree86_VT atom"); 496 return; 497 } 498 if (XGetWindowProperty(xd, DefaultRootWindow(xd), prop, 0, 1, 499 False, AnyPropertyType, &actualtype, &actualformat, 500 &nitems, &bytes_after, &buf)) { 501 Errorx("No XFree86_VT property detected on X server, WINDOWPATH won't be set"); 502 return; 503 } 504 if (nitems != 1) { 505 Errorx("XFree86_VT property unexpectedly has %lu items instead of 1", nitems); 506 XFree(buf); 507 return; 508 } 509 switch (actualtype) { 510 case XA_CARDINAL: 511 case XA_INTEGER: 512 case XA_WINDOW: 513 switch (actualformat) { 514 case 8: 515 num = (*(uint8_t *)(void *)buf); 516 break; 517 case 16: 518 num = (*(uint16_t *)(void *)buf); 519 break; 520 case 32: 521 num = (*(uint32_t *)(void *)buf); 522 break; 523 default: 524 Errorx("XFree86_VT property has unexpected format %d", actualformat); 525 XFree(buf); 526 return; 527 } 528 break; 529 default: 530 Errorx("XFree86_VT property has unexpected type %lx", actualtype); 531 XFree(buf); 532 return; 533 } 534 XFree(buf); 535 windowpath = getenv("WINDOWPATH"); 536 #ifdef HAVE_ASPRINTF 537 if (!windowpath) { 538 if (asprintf(&newwindowpath, "%lu", num) < 0) 539 return; 540 } else { 541 if (asprintf(&newwindowpath, "%s:%lu", windowpath, num) < 0) 542 return; 543 } 544 #else 545 numn = snprintf(nums, sizeof(nums), "%lu", num); 546 if (!windowpath) { 547 len = numn + 1; 548 newwindowpath = malloc(len); 549 if (newwindowpath == NULL) 550 return; 551 snprintf(newwindowpath, len, "%s", nums); 552 } else { 553 len = strlen(windowpath) + 1 + numn + 1; 554 newwindowpath = malloc(len); 555 if (newwindowpath == NULL) 556 return; 557 snprintf(newwindowpath, len, "%s:%s", 558 windowpath, nums); 559 } 560 #endif /* HAVE_ASPRINTF */ 561 if (setenv("WINDOWPATH", newwindowpath, TRUE) == -1) 562 Error("unable to set WINDOWPATH"); 563 564 565 free(newwindowpath); 566 } 567 568 static pid_t 569 startClient(char *client_argv[]) 570 { 571 clientpid = fork(); 572 if (clientpid == 0) { 573 set_environment(); 574 setWindowPath(); 575 576 if (setuid(getuid()) == -1) { 577 Error("cannot change uid"); 578 _exit(EXIT_FAILURE); 579 } 580 setpgid(0, getpid()); 581 Execute(client_argv); 582 Error("Unable to run program \"%s\"", client_argv[0]); 583 584 fprintf(stderr, "Specify a program on the command line or make sure that %s\n", bindir); 585 fprintf(stderr, "is in your path.\n\n"); 586 587 _exit(EXIT_FAILURE); 588 } else { 589 return clientpid; 590 } 591 } 592 593 static jmp_buf close_env; 594 595 static int 596 ignorexio(_X_UNUSED Display *dpy) 597 { 598 Errorx("connection to X server lost"); 599 longjmp(close_env, 1); 600 /*NOTREACHED*/ 601 return 0; 602 } 603 604 static void 605 shutdown(void) 606 { 607 /* have kept display opened, so close it now */ 608 if (clientpid > 0) { 609 XSetIOErrorHandler(ignorexio); 610 if (! setjmp(close_env)) { 611 XCloseDisplay(xd); 612 } 613 614 /* HUP all local clients to allow them to clean up */ 615 if (killpg(clientpid, SIGHUP) < 0 && errno != ESRCH) 616 Error("can't send HUP to process group %d", clientpid); 617 } 618 619 if (serverpid < 0) 620 return; 621 622 if (killpg(serverpid, SIGTERM) < 0) { 623 if (errno == ESRCH) 624 return; 625 Fatal("can't kill X server"); 626 } 627 628 if (!processTimeout(10, "X server to shut down")) 629 return; 630 631 Errorx("X server slow to shut down, sending KILL signal"); 632 633 if (killpg(serverpid, SIGKILL) < 0) { 634 if (errno == ESRCH) 635 return; 636 Error("can't SIGKILL X server"); 637 } 638 639 if (processTimeout(3, "server to die")) 640 Fatalx("X server refuses to die"); 641 #ifdef __sun 642 else { 643 /* Restore keyboard mode. */ 644 serverpid = fork(); 645 switch (serverpid) { 646 case 0: 647 execlp ("kbd_mode", "kbd_mode", "-a", NULL); 648 Fatal("Unable to run program \"%s\"", "kbd_mode"); 649 break; 650 651 case -1: 652 Error("fork failed"); 653 break; 654 655 default: 656 fprintf (stderr, "\r\nRestoring keyboard mode\r\n"); 657 processTimeout(1, "kbd_mode"); 658 } 659 } 660 #endif 661 } 662 663 static void 664 set_environment(void) 665 { 666 if (setenv("DISPLAY", displayNum, TRUE) == -1) 667 Fatal("unable to set DISPLAY"); 668 } 669 670 static void _X_ATTRIBUTE_PRINTF(1,0) 671 verror(const char *fmt, va_list ap) 672 { 673 fprintf(stderr, "%s: ", program); 674 vfprintf(stderr, fmt, ap); 675 fprintf(stderr, ": %s\n", strerror(errno)); 676 } 677 678 static void _X_ATTRIBUTE_PRINTF(1,0) 679 verrorx(const char *fmt, va_list ap) 680 { 681 fprintf(stderr, "%s: ", program); 682 vfprintf(stderr, fmt, ap); 683 fprintf(stderr, "\n"); 684 } 685 686 static void 687 Fatal(const char *fmt, ...) 688 { 689 va_list ap; 690 va_start(ap, fmt); 691 verror(fmt, ap); 692 va_end(ap); 693 exit(EXIT_FAILURE); 694 } 695 696 static void 697 Fatalx(const char *fmt, ...) 698 { 699 va_list ap; 700 va_start(ap, fmt); 701 verrorx(fmt, ap); 702 va_end(ap); 703 exit(EXIT_FAILURE); 704 } 705 706 static void 707 Error(const char *fmt, ...) 708 { 709 va_list ap; 710 va_start(ap, fmt); 711 verror(fmt, ap); 712 va_end(ap); 713 } 714 715 static void 716 Errorx(const char *fmt, ...) 717 { 718 va_list ap; 719 va_start(ap, fmt); 720 verrorx(fmt, ap); 721 va_end(ap); 722 } 723