xinit.c revision d712a854
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;
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 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, const char *string)
368{
369    int    i = 0;
370    pid_t  pidfound = -1;
371    static const char    *laststring;
372
373    for (;;) {
374        if ((pidfound = waitpid(serverpid, &status, WNOHANG)) == serverpid)
375            break;
376        if (timeout) {
377            if (i == 0 && string != laststring)
378                fprintf(stderr, "\r\nwaiting for %s ", string);
379            else
380                fprintf(stderr, ".");
381            fflush(stderr);
382            sleep(1);
383        }
384        if (++i > timeout)
385            break;
386    }
387    if (i > 0) fputc('\n', stderr);     /* tidy up after message */
388    laststring = string;
389    return (serverpid != pidfound);
390}
391
392static pid_t
393startServer(char *server_argv[])
394{
395    sigset_t mask, old;
396    const char * const *cpp;
397
398    sigemptyset(&mask);
399    sigaddset(&mask, SIGUSR1);
400    sigprocmask(SIG_BLOCK, &mask, &old);
401
402    serverpid = fork();
403
404    switch(serverpid) {
405    case 0:
406        /* Unblock */
407        sigprocmask(SIG_SETMASK, &old, NULL);
408
409        /*
410         * don't hang on read/write to control tty
411         */
412        signal(SIGTTIN, SIG_IGN);
413        signal(SIGTTOU, SIG_IGN);
414        /*
415         * ignore SIGUSR1 in child.  The server
416         * will notice this and send SIGUSR1 back
417         * at xinit when ready to accept connections
418         */
419        signal(SIGUSR1, SIG_IGN);
420        /*
421         * prevent server from getting sighup from vhangup()
422         * if client is xterm -L
423         */
424        setpgid(0,getpid());
425        Execute(server_argv);
426
427        Error("unable to run server \"%s\"", server_argv[0]);
428
429        fprintf(stderr, "Use the -- option, or make sure that %s is in your path and\n", bindir);
430        fprintf(stderr, "that \"%s\" is a program or a link to the right type of server\n", server_argv[0]);
431        fprintf(stderr, "for your display.  Possible server names include:\n\n");
432        for (cpp = server_names; *cpp; cpp++)
433            fprintf(stderr, "    %s\n", *cpp);
434        fprintf(stderr, "\n");
435
436        exit(EXIT_FAILURE);
437
438        break;
439    case -1:
440        break;
441    default:
442        /*
443         * don't nice server
444         */
445        setpriority(PRIO_PROCESS, serverpid, -1);
446
447        errno = 0;
448        if(! processTimeout(0, "")) {
449            serverpid = -1;
450            break;
451        }
452        /*
453         * kludge to avoid race with TCP, giving server time to
454         * set his socket options before we try to open it,
455         * either use the 15 second timeout, or await SIGUSR1.
456         *
457         * If your machine is substantially slower than 15 seconds,
458         * you can easily adjust this value.
459         */
460        alarm(15);
461
462        sigsuspend(&old);
463        alarm(0);
464        sigprocmask(SIG_SETMASK, &old, NULL);
465
466        if (waitforserver() == 0) {
467            Error("unable to connect to X server");
468            shutdown();
469            serverpid = -1;
470        }
471        break;
472    }
473
474    return(serverpid);
475}
476
477static void
478setWindowPath(void)
479{
480    /* setting WINDOWPATH for clients */
481    Atom prop;
482    Atom actualtype;
483    int actualformat;
484    unsigned long nitems;
485    unsigned long bytes_after;
486    unsigned char *buf;
487    const char *windowpath;
488    char *newwindowpath;
489    unsigned long num;
490    char nums[10];
491    int numn;
492    size_t len;
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    numn = snprintf(nums, sizeof(nums), "%lu", num);
537    if (!windowpath) {
538        len = numn + 1;
539        newwindowpath = malloc(len);
540        if (newwindowpath == NULL)
541            return;
542        snprintf(newwindowpath, len, "%s", nums);
543    } else {
544        len = strlen(windowpath) + 1 + numn + 1;
545        newwindowpath = malloc(len);
546        if (newwindowpath == NULL)
547            return;
548        snprintf(newwindowpath, len, "%s:%s",
549                 windowpath, nums);
550    }
551    if (setenv("WINDOWPATH", newwindowpath, TRUE) == -1)
552        Error("unable to set WINDOWPATH");
553
554
555    free(newwindowpath);
556}
557
558static pid_t
559startClient(char *client_argv[])
560{
561    clientpid = fork();
562    if (clientpid == 0) {
563        set_environment();
564        setWindowPath();
565
566        if (setuid(getuid()) == -1) {
567            Error("cannot change uid");
568            _exit(EXIT_FAILURE);
569        }
570        setpgid(0, getpid());
571        Execute(client_argv);
572        Error("Unable to run program \"%s\"", client_argv[0]);
573
574        fprintf(stderr, "Specify a program on the command line or make sure that %s\n", bindir);
575        fprintf(stderr, "is in your path.\n\n");
576
577        _exit(EXIT_FAILURE);
578    } else {
579        return clientpid;
580    }
581}
582
583static jmp_buf close_env;
584
585static int
586ignorexio(Display *dpy)
587{
588    Errorx("connection to X server lost");
589    longjmp(close_env, 1);
590    /*NOTREACHED*/
591    return 0;
592}
593
594static void
595shutdown(void)
596{
597    /* have kept display opened, so close it now */
598    if (clientpid > 0) {
599        XSetIOErrorHandler(ignorexio);
600        if (! setjmp(close_env)) {
601            XCloseDisplay(xd);
602        }
603
604        /* HUP all local clients to allow them to clean up */
605        if (killpg(clientpid, SIGHUP) < 0 && errno != ESRCH)
606            Error("can't send HUP to process group %d", clientpid);
607    }
608
609    if (serverpid < 0)
610        return;
611
612    if (killpg(serverpid, SIGTERM) < 0) {
613        if (errno == ESRCH)
614            return;
615        Fatal("can't kill X server");
616    }
617
618    if (!processTimeout(10, "X server to shut down"))
619        return;
620
621    Errorx("X server slow to shut down, sending KILL signal");
622
623    if (killpg(serverpid, SIGKILL) < 0) {
624        if (errno == ESRCH)
625            return;
626        Error("can't SIGKILL X server");
627    }
628
629    if (processTimeout(3, "server to die"))
630        Fatalx("X server refuses to die");
631#ifdef __sun
632    else {
633        /* Restore keyboard mode. */
634        serverpid = fork();
635        switch (serverpid) {
636        case 0:
637            execlp ("kbd_mode", "kbd_mode", "-a", NULL);
638            Fatal("Unable to run program \"%s\"", "kbd_mode");
639            break;
640
641        case -1:
642            Error("fork failed");
643            break;
644
645        default:
646            fprintf (stderr, "\r\nRestoring keyboard mode\r\n");
647            processTimeout(1, "kbd_mode");
648        }
649    }
650#endif
651}
652
653static void
654set_environment(void)
655{
656    if (setenv("DISPLAY", displayNum, TRUE) == -1)
657        Fatal("unable to set DISPLAY");
658}
659
660static void _X_ATTRIBUTE_PRINTF(1,0)
661verror(const char *fmt, va_list ap)
662{
663    fprintf(stderr, "%s: ", program);
664    vfprintf(stderr, fmt, ap);
665    fprintf(stderr, ": %s\n", strerror(errno));
666}
667
668static void _X_ATTRIBUTE_PRINTF(1,0)
669verrorx(const char *fmt, va_list ap)
670{
671    fprintf(stderr, "%s: ", program);
672    vfprintf(stderr, fmt, ap);
673    fprintf(stderr, "\n");
674}
675
676static void
677Fatal(const char *fmt, ...)
678{
679    va_list ap;
680    va_start(ap, fmt);
681    verror(fmt, ap);
682    va_end(ap);
683    exit(EXIT_FAILURE);
684}
685
686static void
687Fatalx(const char *fmt, ...)
688{
689    va_list ap;
690    va_start(ap, fmt);
691    verrorx(fmt, ap);
692    va_end(ap);
693    exit(EXIT_FAILURE);
694}
695
696static void
697Error(const char *fmt, ...)
698{
699    va_list ap;
700    va_start(ap, fmt);
701    verror(fmt, ap);
702    va_end(ap);
703}
704
705static void
706Errorx(const char *fmt, ...)
707{
708    va_list ap;
709    va_start(ap, fmt);
710    verrorx(fmt, ap);
711    va_end(ap);
712}
713