xinit.c revision 7aed6334
11.1Sjmcneill/* 21.1Sjmcneill 31.1.1.2SjmcneillCopyright 1986, 1998 The Open Group 41.1Sjmcneill 51.1SjmcneillPermission to use, copy, modify, distribute, and sell this software and its 61.1Sjmcneilldocumentation for any purpose is hereby granted without fee, provided that 71.1Sjmcneillthe above copyright notice appear in all copies and that both that 81.1Sjmcneillcopyright notice and this permission notice appear in supporting 91.1Sjmcneilldocumentation. 101.1Sjmcneill 111.1SjmcneillThe above copyright notice and this permission notice shall be included in 121.1Sjmcneillall copies or substantial portions of the Software. 131.1Sjmcneill 141.1SjmcneillTHE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 151.1SjmcneillIMPLIED, 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 start_of_client_args, start_of_server_args; 155 struct sigaction sa, si; 156#ifdef __APPLE__ 157#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060 158 vproc_transaction_t vt; 159#endif 160#endif 161 162 program = *argv++; 163 argc--; 164 /* 165 * copy the client args. 166 */ 167 if (argc == 0 || 168 (**argv != '/' && **argv != '.')) { 169 for (ptr = default_client; *ptr; ) 170 *cptr++ = *ptr++; 171 } else { 172 client_given = 1; 173 } 174 start_of_client_args = (cptr - client); 175 while (argc && strcmp(*argv, "--")) { 176 if (cptr > clientargv + sizeof(clientargv) / sizeof(*clientargv) - 2) 177 Fatalx("too many client arguments"); 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 if (sptr > serverargv + sizeof(serverargv) / sizeof(*serverargv) - 2) 206 Fatalx("too many server arguments"); 207 *sptr++ = *argv++; 208 } 209 *sptr = NULL; 210 211 /* 212 * if no client arguments given, check for a startup file and copy 213 * that into the argument list 214 */ 215 if (!client_given) { 216 char *cp; 217 Bool required = False; 218 219 xinitrcbuf[0] = '\0'; 220 if ((cp = getenv("XINITRC")) != NULL) { 221 snprintf(xinitrcbuf, sizeof(xinitrcbuf), "%s", cp); 222 required = True; 223 } else if ((cp = getenv("HOME")) != NULL) { 224 snprintf(xinitrcbuf, sizeof(xinitrcbuf), 225 "%s/%s", cp, XINITRC); 226 } 227 if (xinitrcbuf[0]) { 228 if (access(xinitrcbuf, F_OK) == 0) { 229 client += start_of_client_args - 1; 230 client[0] = xinitrcbuf; 231 } else if (required) { 232 Error("warning, no client init file \"%s\"", xinitrcbuf); 233 } 234 } 235 } 236 237 /* 238 * if no server arguments given, check for a startup file and copy 239 * that into the argument list 240 */ 241 if (!server_given) { 242 char *cp; 243 Bool required = False; 244 245 xserverrcbuf[0] = '\0'; 246 if ((cp = getenv("XSERVERRC")) != NULL) { 247 snprintf(xserverrcbuf, sizeof(xserverrcbuf), "%s", cp); 248 required = True; 249 } else if ((cp = getenv("HOME")) != NULL) { 250 snprintf(xserverrcbuf, sizeof(xserverrcbuf), 251 "%s/%s", cp, XSERVERRC); 252 } 253 if (xserverrcbuf[0]) { 254 if (access(xserverrcbuf, F_OK) == 0) { 255 server += start_of_server_args - 1; 256 server[0] = xserverrcbuf; 257 } else if (required) { 258 Error("warning, no server init file \"%s\"", xserverrcbuf); 259 } 260 } 261 } 262 263 /* 264 * Start the server and client. 265 */ 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 sigaction(SIGCHLD, &si, NULL); 287 288#ifdef __APPLE__ 289#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060 290 vt = vproc_transaction_begin(NULL); 291#endif 292#endif 293 294 if (startServer(server) > 0 295 && startClient(client) > 0) { 296 pid = -1; 297 while (pid != clientpid && pid != serverpid 298 && gotSignal == 0 299 ) 300 pid = wait(NULL); 301 } 302 303#ifdef __APPLE__ 304#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060 305 vproc_transaction_end(NULL, vt); 306#endif 307#endif 308 309 signal(SIGTERM, SIG_IGN); 310 signal(SIGQUIT, SIG_IGN); 311 signal(SIGINT, SIG_IGN); 312 signal(SIGHUP, SIG_IGN); 313 signal(SIGPIPE, SIG_IGN); 314 315 shutdown(); 316 317 if (gotSignal != 0) { 318 Errorx("unexpected signal %d", gotSignal); 319 exit(EXIT_FAILURE); 320 } 321 322 if (serverpid < 0) 323 Fatalx("server error"); 324 if (clientpid < 0) 325 Fatalx("client error"); 326 exit(EXIT_SUCCESS); 327} 328 329 330/* 331 * waitforserver - wait for X server to start up 332 */ 333static Bool 334waitforserver(void) 335{ 336 int ncycles = 120; /* # of cycles to wait */ 337 int cycles; /* Wait cycle count */ 338 339#ifdef __APPLE__ 340 /* For Apple, we don't get signaled by the server when it's ready, so we just 341 * want to sleep now since we're going to sleep later anyways and this allows us 342 * to avoid the awkard, "why is there an error message in the log" questions 343 * from users. 344 */ 345 346 sleep(2); 347#endif 348 349 for (cycles = 0; cycles < ncycles; cycles++) { 350 if ((xd = XOpenDisplay(displayNum))) { 351 return(TRUE); 352 } 353 else { 354 if (!processTimeout(1, "X server to begin accepting connections")) 355 break; 356 } 357 } 358 359 Errorx("giving up"); 360 361 return(FALSE); 362} 363 364/* 365 * return TRUE if we timeout waiting for pid to exit, FALSE otherwise. 366 */ 367static Bool 368processTimeout(int timeout, const char *string) 369{ 370 int i = 0; 371 pid_t pidfound = -1; 372 static const char *laststring; 373 374 for (;;) { 375 if ((pidfound = waitpid(serverpid, &status, WNOHANG)) == serverpid) 376 break; 377 if (timeout) { 378 if (i == 0 && string != laststring) 379 fprintf(stderr, "\r\nwaiting for %s ", string); 380 else 381 fprintf(stderr, "."); 382 fflush(stderr); 383 sleep(1); 384 } 385 if (++i > timeout) 386 break; 387 } 388 if (i > 0) fputc('\n', stderr); /* tidy up after message */ 389 laststring = string; 390 return (serverpid != pidfound); 391} 392 393static pid_t 394startServer(char *server_argv[]) 395{ 396 sigset_t mask, old; 397 const char * const *cpp; 398 399 sigemptyset(&mask); 400 sigaddset(&mask, SIGUSR1); 401 sigprocmask(SIG_BLOCK, &mask, &old); 402 403 serverpid = fork(); 404 405 switch(serverpid) { 406 case 0: 407 /* Unblock */ 408 sigprocmask(SIG_SETMASK, &old, NULL); 409 410 /* 411 * don't hang on read/write to control tty 412 */ 413 signal(SIGTTIN, SIG_IGN); 414 signal(SIGTTOU, SIG_IGN); 415 /* 416 * ignore SIGUSR1 in child. The server 417 * will notice this and send SIGUSR1 back 418 * at xinit when ready to accept connections 419 */ 420 signal(SIGUSR1, SIG_IGN); 421 /* 422 * prevent server from getting sighup from vhangup() 423 * if client is xterm -L 424 */ 425 setpgid(0,getpid()); 426 Execute(server_argv); 427 428 Error("unable to run server \"%s\"", server_argv[0]); 429 430 fprintf(stderr, "Use the -- option, or make sure that %s is in your path and\n", bindir); 431 fprintf(stderr, "that \"%s\" is a program or a link to the right type of server\n", server_argv[0]); 432 fprintf(stderr, "for your display. Possible server names include:\n\n"); 433 for (cpp = server_names; *cpp; cpp++) 434 fprintf(stderr, " %s\n", *cpp); 435 fprintf(stderr, "\n"); 436 437 exit(EXIT_FAILURE); 438 439 break; 440 case -1: 441 break; 442 default: 443 /* 444 * don't nice server 445 */ 446 setpriority(PRIO_PROCESS, serverpid, -1); 447 448 errno = 0; 449 if(! processTimeout(0, "")) { 450 serverpid = -1; 451 break; 452 } 453 /* 454 * kludge to avoid race with TCP, giving server time to 455 * set his socket options before we try to open it, 456 * either use the 15 second timeout, or await SIGUSR1. 457 * 458 * If your machine is substantially slower than 15 seconds, 459 * you can easily adjust this value. 460 */ 461 alarm(15); 462 463 sigsuspend(&old); 464 alarm(0); 465 sigprocmask(SIG_SETMASK, &old, NULL); 466 467 if (waitforserver() == 0) { 468 Error("unable to connect to X server"); 469 shutdown(); 470 serverpid = -1; 471 } 472 break; 473 } 474 475 return(serverpid); 476} 477 478static void 479setWindowPath(void) 480{ 481 /* setting WINDOWPATH for clients */ 482 Atom prop; 483 Atom actualtype; 484 int actualformat; 485 unsigned long nitems; 486 unsigned long bytes_after; 487 unsigned char *buf; 488 const char *windowpath; 489 char *newwindowpath; 490 unsigned long num; 491 char nums[10]; 492 int numn; 493 size_t len; 494 prop = XInternAtom(xd, "XFree86_VT", False); 495 if (prop == None) { 496 Errorx("Unable to intern XFree86_VT atom"); 497 return; 498 } 499 if (XGetWindowProperty(xd, DefaultRootWindow(xd), prop, 0, 1, 500 False, AnyPropertyType, &actualtype, &actualformat, 501 &nitems, &bytes_after, &buf)) { 502 Errorx("No XFree86_VT property detected on X server, WINDOWPATH won't be set"); 503 return; 504 } 505 if (nitems != 1) { 506 Errorx("XFree86_VT property unexpectedly has %lu items instead of 1", nitems); 507 XFree(buf); 508 return; 509 } 510 switch (actualtype) { 511 case XA_CARDINAL: 512 case XA_INTEGER: 513 case XA_WINDOW: 514 switch (actualformat) { 515 case 8: 516 num = (*(uint8_t *)(void *)buf); 517 break; 518 case 16: 519 num = (*(uint16_t *)(void *)buf); 520 break; 521 case 32: 522 num = (*(uint32_t *)(void *)buf); 523 break; 524 default: 525 Errorx("XFree86_VT property has unexpected format %d", actualformat); 526 XFree(buf); 527 return; 528 } 529 break; 530 default: 531 Errorx("XFree86_VT property has unexpected type %lx", actualtype); 532 XFree(buf); 533 return; 534 } 535 XFree(buf); 536 windowpath = getenv("WINDOWPATH"); 537 numn = snprintf(nums, sizeof(nums), "%lu", num); 538 if (!windowpath) { 539 len = numn + 1; 540 newwindowpath = malloc(len); 541 if (newwindowpath == NULL) 542 return; 543 snprintf(newwindowpath, len, "%s", nums); 544 } else { 545 len = strlen(windowpath) + 1 + numn + 1; 546 newwindowpath = malloc(len); 547 if (newwindowpath == NULL) 548 return; 549 snprintf(newwindowpath, len, "%s:%s", 550 windowpath, nums); 551 } 552 if (setenv("WINDOWPATH", newwindowpath, TRUE) == -1) 553 Error("unable to set WINDOWPATH"); 554 555 556 free(newwindowpath); 557} 558 559static pid_t 560startClient(char *client_argv[]) 561{ 562 clientpid = fork(); 563 if (clientpid == 0) { 564 set_environment(); 565 setWindowPath(); 566 567 if (setuid(getuid()) == -1) { 568 Error("cannot change uid"); 569 _exit(EXIT_FAILURE); 570 } 571 setpgid(0, getpid()); 572 Execute(client_argv); 573 Error("Unable to run program \"%s\"", client_argv[0]); 574 575 fprintf(stderr, "Specify a program on the command line or make sure that %s\n", bindir); 576 fprintf(stderr, "is in your path.\n\n"); 577 578 _exit(EXIT_FAILURE); 579 } else { 580 return clientpid; 581 } 582} 583 584static jmp_buf close_env; 585 586static int 587ignorexio(Display *dpy) 588{ 589 Errorx("connection to X server lost"); 590 longjmp(close_env, 1); 591 /*NOTREACHED*/ 592 return 0; 593} 594 595static void 596shutdown(void) 597{ 598 /* have kept display opened, so close it now */ 599 if (clientpid > 0) { 600 XSetIOErrorHandler(ignorexio); 601 if (! setjmp(close_env)) { 602 XCloseDisplay(xd); 603 } 604 605 /* HUP all local clients to allow them to clean up */ 606 if (killpg(clientpid, SIGHUP) < 0 && errno != ESRCH) 607 Error("can't send HUP to process group %d", clientpid); 608 } 609 610 if (serverpid < 0) 611 return; 612 613 if (killpg(serverpid, SIGTERM) < 0) { 614 if (errno == ESRCH) 615 return; 616 Fatal("can't kill X server"); 617 } 618 619 if (!processTimeout(10, "X server to shut down")) 620 return; 621 622 Errorx("X server slow to shut down, sending KILL signal"); 623 624 if (killpg(serverpid, SIGKILL) < 0) { 625 if (errno == ESRCH) 626 return; 627 Error("can't SIGKILL X server"); 628 } 629 630 if (processTimeout(3, "server to die")) 631 Fatalx("X server refuses to die"); 632#ifdef __sun 633 else { 634 /* Restore keyboard mode. */ 635 serverpid = fork(); 636 switch (serverpid) { 637 case 0: 638 execlp ("kbd_mode", "kbd_mode", "-a", NULL); 639 Fatal("Unable to run program \"%s\"", "kbd_mode"); 640 break; 641 642 case -1: 643 Error("fork failed"); 644 break; 645 646 default: 647 fprintf (stderr, "\r\nRestoring keyboard mode\r\n"); 648 processTimeout(1, "kbd_mode"); 649 } 650 } 651#endif 652} 653 654static void 655set_environment(void) 656{ 657 if (setenv("DISPLAY", displayNum, TRUE) == -1) 658 Fatal("unable to set DISPLAY"); 659} 660 661static void _X_ATTRIBUTE_PRINTF(1,0) 662verror(const char *fmt, va_list ap) 663{ 664 fprintf(stderr, "%s: ", program); 665 vfprintf(stderr, fmt, ap); 666 fprintf(stderr, ": %s\n", strerror(errno)); 667} 668 669static void _X_ATTRIBUTE_PRINTF(1,0) 670verrorx(const char *fmt, va_list ap) 671{ 672 fprintf(stderr, "%s: ", program); 673 vfprintf(stderr, fmt, ap); 674 fprintf(stderr, "\n"); 675} 676 677static void 678Fatal(const char *fmt, ...) 679{ 680 va_list ap; 681 va_start(ap, fmt); 682 verror(fmt, ap); 683 va_end(ap); 684 exit(EXIT_FAILURE); 685} 686 687static void 688Fatalx(const char *fmt, ...) 689{ 690 va_list ap; 691 va_start(ap, fmt); 692 verrorx(fmt, ap); 693 va_end(ap); 694 exit(EXIT_FAILURE); 695} 696 697static void 698Error(const char *fmt, ...) 699{ 700 va_list ap; 701 va_start(ap, fmt); 702 verror(fmt, ap); 703 va_end(ap); 704} 705 706static void 707Errorx(const char *fmt, ...) 708{ 709 va_list ap; 710 va_start(ap, fmt); 711 verrorx(fmt, ap); 712 va_end(ap); 713} 714