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