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