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