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