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