xinit.c revision 7abd9dad
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; 104int serverpid = -1; 105int clientpid = -1; 106volatile int gotSignal = 0; 107 108static void Execute(char **vec); 109static Bool waitforserver(void); 110static Bool processTimeout(int timeout, char *string); 111static int startServer(char *server[]); 112static int startClient(char *client[]); 113static int ignorexio(Display *dpy); 114static void shutdown(void); 115static void set_environment(void); 116 117static void Fatal(const char *fmt, ...); 118static void Error(const char *fmt, ...); 119static void Fatalx(const char *fmt, ...); 120static void Errorx(const char *fmt, ...); 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 int 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, char *string) 368{ 369 int i = 0, pidfound = -1; 370 static char *laststring; 371 372 for (;;) { 373 if ((pidfound = waitpid(serverpid, &status, WNOHANG)) == serverpid) 374 break; 375 if (timeout) { 376 if (i == 0 && string != laststring) 377 fprintf(stderr, "\r\nwaiting for %s ", string); 378 else 379 fprintf(stderr, "."); 380 fflush(stderr); 381 sleep(1); 382 } 383 if (++i > timeout) 384 break; 385 } 386 if (i > 0) fputc('\n', stderr); /* tidy up after message */ 387 laststring = string; 388 return (serverpid != pidfound); 389} 390 391static int 392startServer(char *server[]) 393{ 394 sigset_t mask, old; 395 const char * const *cpp; 396 397 sigemptyset(&mask); 398 sigaddset(&mask, SIGUSR1); 399 sigprocmask(SIG_BLOCK, &mask, &old); 400 401 serverpid = fork(); 402 403 switch(serverpid) { 404 case 0: 405 /* Unblock */ 406 sigprocmask(SIG_SETMASK, &old, NULL); 407 408 /* 409 * don't hang on read/write to control tty 410 */ 411 signal(SIGTTIN, SIG_IGN); 412 signal(SIGTTOU, SIG_IGN); 413 /* 414 * ignore SIGUSR1 in child. The server 415 * will notice this and send SIGUSR1 back 416 * at xinit when ready to accept connections 417 */ 418 signal(SIGUSR1, SIG_IGN); 419 /* 420 * prevent server from getting sighup from vhangup() 421 * if client is xterm -L 422 */ 423 setpgid(0,getpid()); 424 Execute(server); 425 426 Error("unable to run server \"%s\"", server[0]); 427 428 fprintf(stderr, "Use the -- option, or make sure that %s is in your path and\n", bindir); 429 fprintf(stderr, "that \"%s\" is a program or a link to the right type of server\n", server[0]); 430 fprintf(stderr, "for your display. Possible server names include:\n\n"); 431 for (cpp = server_names; *cpp; cpp++) 432 fprintf(stderr, " %s\n", *cpp); 433 fprintf(stderr, "\n"); 434 435 exit(EXIT_FAILURE); 436 437 break; 438 case -1: 439 break; 440 default: 441 /* 442 * don't nice server 443 */ 444 setpriority(PRIO_PROCESS, serverpid, -1); 445 446 errno = 0; 447 if(! processTimeout(0, "")) { 448 serverpid = -1; 449 break; 450 } 451 /* 452 * kludge to avoid race with TCP, giving server time to 453 * set his socket options before we try to open it, 454 * either use the 15 second timeout, or await SIGUSR1. 455 * 456 * If your machine is substantially slower than 15 seconds, 457 * you can easily adjust this value. 458 */ 459 alarm(15); 460 461 sigsuspend(&old); 462 alarm(0); 463 sigprocmask(SIG_SETMASK, &old, NULL); 464 465 if (waitforserver() == 0) { 466 Error("unable to connect to X server"); 467 shutdown(); 468 serverpid = -1; 469 } 470 break; 471 } 472 473 return(serverpid); 474} 475 476static void 477setWindowPath(void) 478{ 479 /* setting WINDOWPATH for clients */ 480 Atom prop; 481 Atom actualtype; 482 int actualformat; 483 unsigned long nitems; 484 unsigned long bytes_after; 485 unsigned char *buf; 486 const char *windowpath; 487 char *newwindowpath; 488 unsigned long num; 489 char nums[10]; 490 int numn; 491 size_t len; 492 prop = XInternAtom(xd, "XFree86_VT", False); 493 if (prop == None) { 494 Errorx("Unable to intern XFree86_VT atom"); 495 return; 496 } 497 if (XGetWindowProperty(xd, DefaultRootWindow(xd), prop, 0, 1, 498 False, AnyPropertyType, &actualtype, &actualformat, 499 &nitems, &bytes_after, &buf)) { 500 Errorx("No XFree86_VT property detected on X server, WINDOWPATH won't be set"); 501 return; 502 } 503 if (nitems != 1) { 504 Errorx("XFree86_VT property unexpectedly has %lu items instead of 1", nitems); 505 XFree(buf); 506 return; 507 } 508 switch (actualtype) { 509 case XA_CARDINAL: 510 case XA_INTEGER: 511 case XA_WINDOW: 512 switch (actualformat) { 513 case 8: 514 num = (*(uint8_t *)(void *)buf); 515 break; 516 case 16: 517 num = (*(uint16_t *)(void *)buf); 518 break; 519 case 32: 520 num = (*(uint32_t *)(void *)buf); 521 break; 522 default: 523 Errorx("XFree86_VT property has unexpected format %d", actualformat); 524 XFree(buf); 525 return; 526 } 527 break; 528 default: 529 Errorx("XFree86_VT property has unexpected type %lx", actualtype); 530 XFree(buf); 531 return; 532 } 533 XFree(buf); 534 windowpath = getenv("WINDOWPATH"); 535 numn = snprintf(nums, sizeof(nums), "%lu", num); 536 if (!windowpath) { 537 len = numn + 1; 538 newwindowpath = malloc(len); 539 if (newwindowpath == NULL) 540 return; 541 snprintf(newwindowpath, len, "%s", nums); 542 } else { 543 len = strlen(windowpath) + 1 + numn + 1; 544 newwindowpath = malloc(len); 545 if (newwindowpath == NULL) 546 return; 547 snprintf(newwindowpath, len, "%s:%s", 548 windowpath, nums); 549 } 550 if (setenv("WINDOWPATH", newwindowpath, TRUE) == -1) 551 Error("unable to set WINDOWPATH"); 552 553 554 free(newwindowpath); 555} 556 557static int 558startClient(char *client[]) 559{ 560 clientpid = fork(); 561 if (clientpid == 0) { 562 set_environment(); 563 setWindowPath(); 564 565 if (setuid(getuid()) == -1) { 566 Error("cannot change uid"); 567 _exit(EXIT_FAILURE); 568 } 569 setpgid(0, getpid()); 570 Execute(client); 571 Error("Unable to run program \"%s\"", client[0]); 572 573 fprintf(stderr, "Specify a program on the command line or make sure that %s\n", bindir); 574 fprintf(stderr, "is in your path.\n\n"); 575 576 _exit(EXIT_FAILURE); 577 } else { 578 return clientpid; 579 } 580} 581 582static jmp_buf close_env; 583 584static int 585ignorexio(Display *dpy) 586{ 587 Errorx("connection to X server lost"); 588 longjmp(close_env, 1); 589 /*NOTREACHED*/ 590 return 0; 591} 592 593static void 594shutdown(void) 595{ 596 /* have kept display opened, so close it now */ 597 if (clientpid > 0) { 598 XSetIOErrorHandler(ignorexio); 599 if (! setjmp(close_env)) { 600 XCloseDisplay(xd); 601 } 602 603 /* HUP all local clients to allow them to clean up */ 604 if (killpg(clientpid, SIGHUP) < 0 && errno != ESRCH) 605 Error("can't send HUP to process group %d", clientpid); 606 } 607 608 if (serverpid < 0) 609 return; 610 611 if (killpg(serverpid, SIGTERM) < 0) { 612 if (errno == ESRCH) 613 return; 614 Fatal("can't kill X server"); 615 } 616 617 if (!processTimeout(10, "X server to shut down")) 618 return; 619 620 Errorx("X server slow to shut down, sending KILL signal"); 621 622 if (killpg(serverpid, SIGKILL) < 0) { 623 if (errno == ESRCH) 624 return; 625 Error("can't SIGKILL X server"); 626 } 627 628 if (processTimeout(3, "server to die")) 629 Fatalx("X server refuses to die"); 630} 631 632static void 633set_environment(void) 634{ 635 if (setenv("DISPLAY", displayNum, TRUE) == -1) 636 Fatal("unable to set DISPLAY"); 637} 638 639static void 640verror(const char *fmt, va_list ap) 641{ 642 fprintf(stderr, "%s: ", program); 643 vfprintf(stderr, fmt, ap); 644 fprintf(stderr, ": %s\n", strerror(errno)); 645} 646 647static void 648verrorx(const char *fmt, va_list ap) 649{ 650 fprintf(stderr, "%s: ", program); 651 vfprintf(stderr, fmt, ap); 652 fprintf(stderr, "\n"); 653} 654 655static void 656Fatal(const char *fmt, ...) 657{ 658 va_list ap; 659 va_start(ap, fmt); 660 verror(fmt, ap); 661 va_end(ap); 662 exit(EXIT_FAILURE); 663} 664 665static void 666Fatalx(const char *fmt, ...) 667{ 668 va_list ap; 669 va_start(ap, fmt); 670 verrorx(fmt, ap); 671 va_end(ap); 672 exit(EXIT_FAILURE); 673} 674 675static void 676Error(const char *fmt, ...) 677{ 678 va_list ap; 679 va_start(ap, fmt); 680 verror(fmt, ap); 681 va_end(ap); 682} 683 684static void 685Errorx(const char *fmt, ...) 686{ 687 va_list ap; 688 va_start(ap, fmt); 689 verrorx(fmt, ap); 690 va_end(ap); 691} 692