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