xinit.c revision 11f750ed
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#if defined(__DragonFly__) || defined(__NetBSD__) 53#include <sys/time.h> 54#include <sys/resource.h> 55#endif /* __DragonFly__ */ 56 57#include <stdlib.h> 58 59#ifndef SHELL 60#define SHELL "sh" 61#endif 62 63const char *bindir = BINDIR; 64const 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 "Xdmx Distributed Multi-head X server", 80 NULL}; 81 82#ifndef XINITRC 83#define XINITRC ".xinitrc" 84#endif 85char xinitrcbuf[256]; 86 87#ifndef XSERVERRC 88#define XSERVERRC ".xserverrc" 89#endif 90char xserverrcbuf[256]; 91 92#define TRUE 1 93#define FALSE 0 94 95static char *default_server = "X"; 96static char *default_display = ":0"; /* choose most efficient */ 97static char *default_client[] = {"xterm", "-geometry", "+1+1", "-n", "login", NULL}; 98static char *serverargv[100]; 99static char *clientargv[100]; 100static char **server = serverargv + 2; /* make sure room for sh .xserverrc args */ 101static char **client = clientargv + 2; /* make sure room for sh .xinitrc args */ 102static char *displayNum = NULL; 103static char *program = NULL; 104static Display *xd = NULL; /* server connection */ 105int status; 106int serverpid = -1; 107int clientpid = -1; 108volatile int gotSignal = 0; 109 110static void Execute(char **vec); 111static Bool waitforserver(void); 112static Bool processTimeout(int timeout, char *string); 113static int startServer(char *server[]); 114static int startClient(char *client[]); 115static int ignorexio(Display *dpy); 116static void shutdown(void); 117static void set_environment(void); 118 119static void Fatal(const char *fmt, ...); 120static void Error(const char *fmt, ...); 121static void Fatalx(const char *fmt, ...); 122static void Errorx(const char *fmt, ...); 123 124static void 125sigCatch(int sig) 126{ 127 /* On system with POSIX signals, just interrupt the system call */ 128 gotSignal = sig; 129} 130 131static void 132sigIgnore(int sig) 133{ 134} 135 136static void 137Execute(char **vec) /* has room from up above */ 138{ 139 execvp(vec[0], vec); 140 if (access(vec[0], R_OK) == 0) { 141 vec--; /* back it up to stuff shell in */ 142 vec[0] = SHELL; 143 execvp(vec[0], vec); 144 } 145 return; 146} 147 148int 149main(int argc, char *argv[]) 150{ 151 register char **sptr = server; 152 register char **cptr = client; 153 register char **ptr; 154 int pid; 155 int client_given = 0, server_given = 0; 156 int client_args_given = 0, server_args_given = 0; 157 int start_of_client_args, start_of_server_args; 158 struct sigaction sa, si; 159#ifdef __APPLE__ 160#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060 161 vproc_transaction_t vt; 162#endif 163#endif 164 165 program = *argv++; 166 argc--; 167 /* 168 * copy the client args. 169 */ 170 if (argc == 0 || 171 (**argv != '/' && **argv != '.')) { 172 for (ptr = default_client; *ptr; ) 173 *cptr++ = *ptr++; 174 } else { 175 client_given = 1; 176 } 177 start_of_client_args = (cptr - client); 178 while (argc && strcmp(*argv, "--")) { 179 client_args_given++; 180 *cptr++ = *argv++; 181 argc--; 182 } 183 *cptr = NULL; 184 if (argc) { 185 argv++; 186 argc--; 187 } 188 189 /* 190 * Copy the server args. 191 */ 192 if (argc == 0 || 193 (**argv != '/' && **argv != '.')) { 194 *sptr++ = default_server; 195 } else { 196 server_given = 1; 197 *sptr++ = *argv++; 198 argc--; 199 } 200 if (argc > 0 && (argv[0][0] == ':' && isdigit(argv[0][1]))) 201 displayNum = *argv; 202 else 203 displayNum = *sptr++ = default_display; 204 205 start_of_server_args = (sptr - server); 206 while (--argc >= 0) { 207 server_args_given++; 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 signal(SIGCHLD, SIG_DFL); /* Insurance */ 268 269 /* Let those signal interrupt the wait() call in the main loop */ 270 memset(&sa, 0, sizeof sa); 271 sa.sa_handler = sigCatch; 272 sigemptyset(&sa.sa_mask); 273 sa.sa_flags = 0; /* do not set SA_RESTART */ 274 275 sigaction(SIGTERM, &sa, NULL); 276 sigaction(SIGQUIT, &sa, NULL); 277 sigaction(SIGINT, &sa, NULL); 278 sigaction(SIGHUP, &sa, NULL); 279 sigaction(SIGPIPE, &sa, NULL); 280 281 memset(&si, 0, sizeof(si)); 282 si.sa_handler = sigIgnore; 283 sigemptyset(&si.sa_mask); 284 si.sa_flags = SA_RESTART; 285 286 sigaction(SIGALRM, &si, NULL); 287 sigaction(SIGUSR1, &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 (gotSignal != 0) { 319 Errorx("unexpected signal %d", gotSignal); 320 exit(EXIT_FAILURE); 321 } 322 323 if (serverpid < 0) 324 Fatalx("server error"); 325 if (clientpid < 0) 326 Fatalx("client error"); 327 exit(EXIT_SUCCESS); 328} 329 330 331/* 332 * waitforserver - wait for X server to start up 333 */ 334static Bool 335waitforserver(void) 336{ 337 int ncycles = 120; /* # of cycles to wait */ 338 int cycles; /* Wait cycle count */ 339 340#ifdef __APPLE__ 341 /* For Apple, we don't get signaled by the server when it's ready, so we just 342 * want to sleep now since we're going to sleep later anyways and this allows us 343 * to avoid the awkard, "why is there an error message in the log" questions 344 * from users. 345 */ 346 347 sleep(2); 348#endif 349 350 for (cycles = 0; cycles < ncycles; cycles++) { 351 if ((xd = XOpenDisplay(displayNum))) { 352 return(TRUE); 353 } 354 else { 355 if (!processTimeout(1, "X server to begin accepting connections")) 356 break; 357 } 358 } 359 360 Errorx("giving up"); 361 362 return(FALSE); 363} 364 365/* 366 * return TRUE if we timeout waiting for pid to exit, FALSE otherwise. 367 */ 368static Bool 369processTimeout(int timeout, char *string) 370{ 371 int i = 0, pidfound = -1; 372 static 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 int 394startServer(char *server[]) 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); 427 428 Error("unable to run server \"%s\"", server[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[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 int 560startClient(char *client[]) 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); 573 Error("Unable to run program \"%s\"", client[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} 633 634static void 635set_environment(void) 636{ 637 if (setenv("DISPLAY", displayNum, TRUE) == -1) 638 Fatal("unable to set DISPLAY"); 639} 640 641static void 642verror(const char *fmt, va_list ap) 643{ 644 fprintf(stderr, "%s: ", program); 645 vfprintf(stderr, fmt, ap); 646 fprintf(stderr, ": %s\n", strerror(errno)); 647} 648 649static void 650verrorx(const char *fmt, va_list ap) 651{ 652 fprintf(stderr, "%s: ", program); 653 vfprintf(stderr, fmt, ap); 654 fprintf(stderr, "\n"); 655} 656 657static void 658Fatal(const char *fmt, ...) 659{ 660 va_list ap; 661 va_start(ap, fmt); 662 verror(fmt, ap); 663 va_end(ap); 664 exit(EXIT_FAILURE); 665} 666 667static void 668Fatalx(const char *fmt, ...) 669{ 670 va_list ap; 671 va_start(ap, fmt); 672 verrorx(fmt, ap); 673 va_end(ap); 674 exit(EXIT_FAILURE); 675} 676 677static void 678Error(const char *fmt, ...) 679{ 680 va_list ap; 681 va_start(ap, fmt); 682 verror(fmt, ap); 683 va_end(ap); 684} 685 686static void 687Errorx(const char *fmt, ...) 688{ 689 va_list ap; 690 va_start(ap, fmt); 691 verrorx(fmt, ap); 692 va_end(ap); 693} 694