upload tizen2.0 source
[framework/uifw/xorg/app/xinit.git] / xinit.c
1 /*
2
3 Copyright 1986, 1998  The Open Group
4
5 Permission to use, copy, modify, distribute, and sell this software and its
6 documentation for any purpose is hereby granted without fee, provided that
7 the above copyright notice appear in all copies and that both that
8 copyright notice and this permission notice appear in supporting
9 documentation.
10
11 The above copyright notice and this permission notice shall be included in
12 all copies or substantial portions of the Software.
13
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
17 OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
18 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20
21 Except as contained in this notice, the name of The Open Group shall not be
22 used in advertising or otherwise to promote the sale, use or other dealings
23 in 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
61 const char *bindir = BINDIR;
62 const 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
83 char xinitrcbuf[256];
84
85 #ifndef XSERVERRC
86 #define XSERVERRC ".xserverrc"
87 #endif
88 char xserverrcbuf[256];
89
90 #define TRUE 1
91 #define FALSE 0
92
93 static char *default_server = "X";
94 static char *default_display = ":0";        /* choose most efficient */
95 static char *default_client[] = {"xterm", "-geometry", "+1+1", "-n", "login", NULL};
96 static char *serverargv[100];
97 static char *clientargv[100];
98 static char **server = serverargv + 2;        /* make sure room for sh .xserverrc args */
99 static char **client = clientargv + 2;        /* make sure room for sh .xinitrc args */
100 static char *displayNum = NULL;
101 static char *program = NULL;
102 static Display *xd = NULL;            /* server connection */
103 int status;
104 int serverpid = -1;
105 int clientpid = -1;
106 volatile int gotSignal = 0;
107
108 static void Execute(char **vec);
109 static Bool waitforserver(void);
110 static Bool processTimeout(int timeout, char *string);
111 static int startServer(char *server[]);
112 static int startClient(char *client[]);
113 static int ignorexio(Display *dpy);
114 static void shutdown(void);
115 static void set_environment(void);
116
117 static void Fatal(const char *fmt, ...);
118 static void Error(const char *fmt, ...);
119 static void Fatalx(const char *fmt, ...);
120 static void Errorx(const char *fmt, ...);
121
122 static void
123 sigCatch(int sig)
124 {
125     /* On system with POSIX signals, just interrupt the system call */
126     gotSignal = sig;
127 }
128
129 static void
130 sigIgnore(int sig)
131 {
132 }
133
134 static void
135 Execute(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
146 int
147 main(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 #ifdef _F_EXIT_AFTER_XORG_AND_XCLIENT_LAUNCHED_
296         exit(0);
297 #endif//_F_EXIT_AFTER_XORG_AND_XCLIENT_LAUNCHED_
298         pid = -1;
299         while (pid != clientpid && pid != serverpid
300                && gotSignal == 0
301             )
302             pid = wait(NULL);
303     }
304
305 #ifdef __APPLE__
306 #if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060
307     vproc_transaction_end(NULL, vt);
308 #endif
309 #endif
310
311     signal(SIGTERM, SIG_IGN);
312     signal(SIGQUIT, SIG_IGN);
313     signal(SIGINT, SIG_IGN);
314     signal(SIGHUP, SIG_IGN);
315     signal(SIGPIPE, SIG_IGN);
316
317     shutdown();
318
319     if (gotSignal != 0) {
320         Errorx("unexpected signal %d", gotSignal);
321         exit(EXIT_FAILURE);
322     }
323
324     if (serverpid < 0)
325         Fatalx("server error");
326     if (clientpid < 0)
327         Fatalx("client error");
328     exit(EXIT_SUCCESS);
329 }
330
331
332 /*
333  *    waitforserver - wait for X server to start up
334  */
335 static Bool
336 waitforserver(void)
337 {
338     int    ncycles     = 120;        /* # of cycles to wait */
339     int    cycles;            /* Wait cycle count */
340
341 #ifdef __APPLE__
342     /* For Apple, we don't get signaled by the server when it's ready, so we just
343      * want to sleep now since we're going to sleep later anyways and this allows us
344      * to avoid the awkard, "why is there an error message in the log" questions
345      * from users.
346      */
347
348     sleep(2);
349 #endif
350
351     for (cycles = 0; cycles < ncycles; cycles++) {
352         if ((xd = XOpenDisplay(displayNum))) {
353             return(TRUE);
354         }
355         else {
356             if (!processTimeout(1, "X server to begin accepting connections"))
357               break;
358         }
359     }
360
361     Errorx("giving up");
362
363     return(FALSE);
364 }
365
366 /*
367  * return TRUE if we timeout waiting for pid to exit, FALSE otherwise.
368  */
369 static Bool
370 processTimeout(int timeout, char *string)
371 {
372     int    i = 0, pidfound = -1;
373     static char    *laststring;
374
375     for (;;) {
376         if ((pidfound = waitpid(serverpid, &status, WNOHANG)) == serverpid)
377             break;
378         if (timeout) {
379             if (i == 0 && string != laststring)
380                 fprintf(stderr, "\r\nwaiting for %s ", string);
381             else
382                 fprintf(stderr, ".");
383             fflush(stderr);
384             sleep(1);
385         }
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
394 static int
395 startServer(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
479 static void
480 setWindowPath(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
560 static int
561 startClient(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
585 static jmp_buf close_env;
586
587 static int
588 ignorexio(Display *dpy)
589 {
590     Errorx("connection to X server lost");
591     longjmp(close_env, 1);
592     /*NOTREACHED*/
593     return 0;
594 }
595
596 static void
597 shutdown(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, sending 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
635 static void
636 set_environment(void)
637 {
638     if (setenv("DISPLAY", displayNum, TRUE) == -1)
639         Fatal("unable to set DISPLAY");
640 }
641
642 static void
643 verror(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
650 static void
651 verrorx(const char *fmt, va_list ap)
652 {
653     fprintf(stderr, "%s: ", program);
654     vfprintf(stderr, fmt, ap);
655     fprintf(stderr, "\n");
656 }
657
658 static void
659 Fatal(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
668 static void
669 Fatalx(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
678 static void
679 Error(const char *fmt, ...)
680 {
681     va_list ap;
682     va_start(ap, fmt);
683     verror(fmt, ap);
684     va_end(ap);
685 }
686
687 static void
688 Errorx(const char *fmt, ...)
689 {
690     va_list ap;
691     va_start(ap, fmt);
692     verrorx(fmt, ap);
693     va_end(ap);
694 }