xinit.c revision 7aed6334
11.1Sjmcneill/*
21.1Sjmcneill
31.1.1.2SjmcneillCopyright 1986, 1998  The Open Group
41.1Sjmcneill
51.1SjmcneillPermission to use, copy, modify, distribute, and sell this software and its
61.1Sjmcneilldocumentation for any purpose is hereby granted without fee, provided that
71.1Sjmcneillthe above copyright notice appear in all copies and that both that
81.1Sjmcneillcopyright notice and this permission notice appear in supporting
91.1Sjmcneilldocumentation.
101.1Sjmcneill
111.1SjmcneillThe above copyright notice and this permission notice shall be included in
121.1Sjmcneillall copies or substantial portions of the Software.
131.1Sjmcneill
141.1SjmcneillTHE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
151.1SjmcneillIMPLIED, 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;
104pid_t serverpid = -1;
105pid_t clientpid = -1;
106volatile int gotSignal = 0;
107
108static void Execute(char **vec);
109static Bool waitforserver(void);
110static Bool processTimeout(int timeout, const char *string);
111static pid_t startServer(char *server[]);
112static pid_t startClient(char *client[]);
113static int ignorexio(Display *dpy);
114static void shutdown(void);
115static void set_environment(void);
116
117static void Fatal(const char *fmt, ...) _X_ATTRIBUTE_PRINTF(1,2) _X_NORETURN;
118static void Error(const char *fmt, ...) _X_ATTRIBUTE_PRINTF(1,2);
119static void Fatalx(const char *fmt, ...) _X_ATTRIBUTE_PRINTF(1,2) _X_NORETURN;
120static void Errorx(const char *fmt, ...) _X_ATTRIBUTE_PRINTF(1,2);
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    pid_t pid;
153    int client_given = 0, server_given = 0;
154    int start_of_client_args, start_of_server_args;
155    struct sigaction sa, si;
156#ifdef __APPLE__
157#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060
158    vproc_transaction_t vt;
159#endif
160#endif
161
162    program = *argv++;
163    argc--;
164    /*
165     * copy the client args.
166     */
167    if (argc == 0 ||
168        (**argv != '/' && **argv != '.')) {
169        for (ptr = default_client; *ptr; )
170            *cptr++ = *ptr++;
171    } else {
172        client_given = 1;
173    }
174    start_of_client_args = (cptr - client);
175    while (argc && strcmp(*argv, "--")) {
176        if (cptr > clientargv + sizeof(clientargv) / sizeof(*clientargv) - 2)
177            Fatalx("too many client arguments");
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        if (sptr > serverargv + sizeof(serverargv) / sizeof(*serverargv) - 2)
206            Fatalx("too many server arguments");
207        *sptr++ = *argv++;
208    }
209    *sptr = NULL;
210
211    /*
212     * if no client arguments given, check for a startup file and copy
213     * that into the argument list
214     */
215    if (!client_given) {
216        char *cp;
217        Bool required = False;
218
219        xinitrcbuf[0] = '\0';
220        if ((cp = getenv("XINITRC")) != NULL) {
221            snprintf(xinitrcbuf, sizeof(xinitrcbuf), "%s", cp);
222            required = True;
223        } else if ((cp = getenv("HOME")) != NULL) {
224            snprintf(xinitrcbuf, sizeof(xinitrcbuf),
225                     "%s/%s", cp, XINITRC);
226        }
227        if (xinitrcbuf[0]) {
228            if (access(xinitrcbuf, F_OK) == 0) {
229                client += start_of_client_args - 1;
230                client[0] = xinitrcbuf;
231            } else if (required) {
232                Error("warning, no client init file \"%s\"", xinitrcbuf);
233            }
234        }
235    }
236
237    /*
238     * if no server arguments given, check for a startup file and copy
239     * that into the argument list
240     */
241    if (!server_given) {
242        char *cp;
243        Bool required = False;
244
245        xserverrcbuf[0] = '\0';
246        if ((cp = getenv("XSERVERRC")) != NULL) {
247            snprintf(xserverrcbuf, sizeof(xserverrcbuf), "%s", cp);
248            required = True;
249        } else if ((cp = getenv("HOME")) != NULL) {
250            snprintf(xserverrcbuf, sizeof(xserverrcbuf),
251                     "%s/%s", cp, XSERVERRC);
252        }
253        if (xserverrcbuf[0]) {
254            if (access(xserverrcbuf, F_OK) == 0) {
255                server += start_of_server_args - 1;
256                server[0] = xserverrcbuf;
257            } else if (required) {
258                Error("warning, no server init file \"%s\"", xserverrcbuf);
259            }
260        }
261    }
262
263    /*
264     * Start the server and client.
265     */
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    sigaction(SIGCHLD, &si, NULL);
287
288#ifdef __APPLE__
289#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060
290    vt = vproc_transaction_begin(NULL);
291#endif
292#endif
293
294    if (startServer(server) > 0
295        && startClient(client) > 0) {
296        pid = -1;
297        while (pid != clientpid && pid != serverpid
298               && gotSignal == 0
299            )
300            pid = wait(NULL);
301    }
302
303#ifdef __APPLE__
304#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060
305    vproc_transaction_end(NULL, vt);
306#endif
307#endif
308
309    signal(SIGTERM, SIG_IGN);
310    signal(SIGQUIT, SIG_IGN);
311    signal(SIGINT, SIG_IGN);
312    signal(SIGHUP, SIG_IGN);
313    signal(SIGPIPE, SIG_IGN);
314
315    shutdown();
316
317    if (gotSignal != 0) {
318        Errorx("unexpected signal %d", gotSignal);
319        exit(EXIT_FAILURE);
320    }
321
322    if (serverpid < 0)
323        Fatalx("server error");
324    if (clientpid < 0)
325        Fatalx("client error");
326    exit(EXIT_SUCCESS);
327}
328
329
330/*
331 *    waitforserver - wait for X server to start up
332 */
333static Bool
334waitforserver(void)
335{
336    int    ncycles     = 120;        /* # of cycles to wait */
337    int    cycles;            /* Wait cycle count */
338
339#ifdef __APPLE__
340    /* For Apple, we don't get signaled by the server when it's ready, so we just
341     * want to sleep now since we're going to sleep later anyways and this allows us
342     * to avoid the awkard, "why is there an error message in the log" questions
343     * from users.
344     */
345
346    sleep(2);
347#endif
348
349    for (cycles = 0; cycles < ncycles; cycles++) {
350        if ((xd = XOpenDisplay(displayNum))) {
351            return(TRUE);
352        }
353        else {
354            if (!processTimeout(1, "X server to begin accepting connections"))
355              break;
356        }
357    }
358
359    Errorx("giving up");
360
361    return(FALSE);
362}
363
364/*
365 * return TRUE if we timeout waiting for pid to exit, FALSE otherwise.
366 */
367static Bool
368processTimeout(int timeout, const char *string)
369{
370    int    i = 0;
371    pid_t  pidfound = -1;
372    static const 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 pid_t
394startServer(char *server_argv[])
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_argv);
427
428        Error("unable to run server \"%s\"", server_argv[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_argv[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 pid_t
560startClient(char *client_argv[])
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_argv);
573        Error("Unable to run program \"%s\"", client_argv[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#ifdef __sun
633    else {
634        /* Restore keyboard mode. */
635        serverpid = fork();
636        switch (serverpid) {
637        case 0:
638            execlp ("kbd_mode", "kbd_mode", "-a", NULL);
639            Fatal("Unable to run program \"%s\"", "kbd_mode");
640            break;
641
642        case -1:
643            Error("fork failed");
644            break;
645
646        default:
647            fprintf (stderr, "\r\nRestoring keyboard mode\r\n");
648            processTimeout(1, "kbd_mode");
649        }
650    }
651#endif
652}
653
654static void
655set_environment(void)
656{
657    if (setenv("DISPLAY", displayNum, TRUE) == -1)
658        Fatal("unable to set DISPLAY");
659}
660
661static void _X_ATTRIBUTE_PRINTF(1,0)
662verror(const char *fmt, va_list ap)
663{
664    fprintf(stderr, "%s: ", program);
665    vfprintf(stderr, fmt, ap);
666    fprintf(stderr, ": %s\n", strerror(errno));
667}
668
669static void _X_ATTRIBUTE_PRINTF(1,0)
670verrorx(const char *fmt, va_list ap)
671{
672    fprintf(stderr, "%s: ", program);
673    vfprintf(stderr, fmt, ap);
674    fprintf(stderr, "\n");
675}
676
677static void
678Fatal(const char *fmt, ...)
679{
680    va_list ap;
681    va_start(ap, fmt);
682    verror(fmt, ap);
683    va_end(ap);
684    exit(EXIT_FAILURE);
685}
686
687static void
688Fatalx(const char *fmt, ...)
689{
690    va_list ap;
691    va_start(ap, fmt);
692    verrorx(fmt, ap);
693    va_end(ap);
694    exit(EXIT_FAILURE);
695}
696
697static void
698Error(const char *fmt, ...)
699{
700    va_list ap;
701    va_start(ap, fmt);
702    verror(fmt, ap);
703    va_end(ap);
704}
705
706static void
707Errorx(const char *fmt, ...)
708{
709    va_list ap;
710    va_start(ap, fmt);
711    verrorx(fmt, ap);
712    va_end(ap);
713}
714