xinit.c revision 72e81212
1/* $Xorg: xinit.c,v 1.5 2001/02/09 02:05:49 xorgcvs Exp $ */
2/* $XdotOrg: $ */
3
4/*
5
6Copyright 1986, 1998  The Open Group
7
8Permission to use, copy, modify, distribute, and sell this software and its
9documentation for any purpose is hereby granted without fee, provided that
10the above copyright notice appear in all copies and that both that
11copyright notice and this permission notice appear in supporting
12documentation.
13
14The above copyright notice and this permission notice shall be included in
15all copies or substantial portions of the Software.
16
17THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
20OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
21AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
24Except as contained in this notice, the name of The Open Group shall not be
25used in advertising or otherwise to promote the sale, use or other dealings
26in this Software without prior written authorization from The Open Group.
27
28*/
29/* $XFree86: xc/programs/xinit/xinit.c,v 3.32 2002/05/31 18:46:13 dawes Exp $ */
30
31#ifdef HAVE_CONFIG_H
32# include "config.h"
33#endif
34
35#include <X11/Xlib.h>
36#include <X11/Xos.h>
37#include <X11/Xatom.h>
38#include <stdio.h>
39#include <ctype.h>
40#include <stdint.h>
41
42#ifdef X_POSIX_C_SOURCE
43#define _POSIX_C_SOURCE X_POSIX_C_SOURCE
44#include <signal.h>
45#undef _POSIX_C_SOURCE
46#else
47#if defined(X_NOT_POSIX) || defined(_POSIX_SOURCE)
48#include <signal.h>
49#else
50#define _POSIX_SOURCE
51#include <signal.h>
52#undef _POSIX_SOURCE
53#endif
54#endif
55
56#ifndef SYSV
57#include <sys/wait.h>
58#endif
59#include <errno.h>
60#include <setjmp.h>
61#include <stdarg.h>
62
63#ifdef __APPLE__
64#include <AvailabilityMacros.h>
65#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1060
66#include <vproc.h>
67#endif
68#endif
69
70#if !defined(SIGCHLD) && defined(SIGCLD)
71#define SIGCHLD SIGCLD
72#endif
73#ifdef __UNIXOS2__
74#define INCL_DOSMODULEMGR
75#include <os2.h>
76#define setpgid(a,b)
77#define setuid(a)
78#define setgid(a)
79#define SHELL "cmd.exe"
80#define XINITRC "xinitrc.cmd"
81#define XSERVERRC "xservrc.cmd"
82char **envsave;	/* to circumvent an UNIXOS2 problem */
83#define environ envsave
84#endif
85
86#include <stdlib.h>
87extern char **environ;
88char **newenviron = NULL;
89char **newenvironlast = NULL;
90
91#ifndef SHELL
92#define SHELL "sh"
93#endif
94
95#ifndef HAVE_WORKING_VFORK
96# ifndef vfork
97#  define vfork() fork()
98# endif
99#else
100# ifdef HAVE_VFORK_H
101#  include <vfork.h>
102# endif
103#endif
104
105/* A/UX setpgid incorrectly removes the controlling terminal.
106   Per Posix, only setsid should do that. */
107#ifdef macII
108#define setpgid setpgrp
109#endif
110
111#ifdef __UNIXOS2__
112#define HAS_EXECVPE
113#endif
114
115#ifdef HAS_EXECVPE
116#define Execvpe(path, argv, envp) execvpe(path, argv, envp)
117#else
118#define Execvpe(path, argv, envp) execvp(path, argv)
119#endif
120
121const char *bindir = BINDIR;
122const char * const server_names[] = {
123#if defined(ultrix) && defined(mips)
124    "Xdec        Digital color display on DECstation",
125#endif
126#if defined(sun) && !defined(XORG)	/* Sun */
127    "Xsun        Sun BW2, CG2, CG3, CG4, or CG6 on Sun 2, 3, 4, or 386i",
128    "Xsunmono    Sun BW2 on Sun 2, 3, 4, or 386i ",
129    "Xsun24      Sun BW2, CG2, CG3, CG4, CG6, or CG8 on Sun 4",
130#endif
131#ifdef hpux				/* HP */
132    "Xhp         HP monochrome and colors displays on 9000/300 series",
133#endif
134#ifdef ibm				/* IBM */
135    "Xibm        IBM AED, APA, 8514a, megapel, VGA displays on PC/RT",
136#endif
137#ifdef macII				/* MacII */
138    "XmacII      Apple monochrome display on Macintosh II",
139#endif
140#ifdef XFREE86
141    "XFree86     XFree86 displays",
142#endif
143#ifdef XORG
144    "Xorg        Common X server for most displays",
145#endif
146#ifdef __APPLE__
147    "Xquartz     Mac OSX Quartz displays.",
148#endif
149    "Xvfb        Virtual frame buffer",
150    "Xfake       kdrive-based virtual frame buffer",
151    "Xnest       X server nested in a window on another X server",
152    "Xephyr      kdrive-based nested X server",
153    NULL};
154
155#ifndef XINITRC
156#define XINITRC ".xinitrc"
157#endif
158char xinitrcbuf[256];
159
160#ifndef XSERVERRC
161#define XSERVERRC ".xserverrc"
162#endif
163char xserverrcbuf[256];
164
165#define	TRUE		1
166#define	FALSE		0
167#define	OK_EXIT		0
168#define	ERR_EXIT	1
169
170static char *default_server = "X";
171static char *default_display = ":0";		/* choose most efficient */
172static char *default_client[] = {"xterm", "-geometry", "+1+1", "-n", "login", NULL};
173static char *serverargv[100];
174static char *clientargv[100];
175static char **server = serverargv + 2;		/* make sure room for sh .xserverrc args */
176static char **client = clientargv + 2;		/* make sure room for sh .xinitrc args */
177static char *displayNum = NULL;
178static char *program = NULL;
179static Display *xd = NULL;			/* server connection */
180#ifndef SYSV
181#if defined(__CYGWIN__) || defined(SVR4) || defined(_POSIX_SOURCE) || defined(CSRG_BASED) || defined(__UNIXOS2__) || defined(Lynx) || defined(__APPLE__)
182int status;
183#else
184union wait	status;
185#endif
186#endif /* SYSV */
187int serverpid = -1;
188int clientpid = -1;
189volatile int gotSignal = 0;
190
191static void Execute ( char **vec, char **envp );
192static Bool waitforserver ( void );
193static Bool processTimeout ( int timeout, char *string );
194static int startServer ( char *server[] );
195static int startClient ( char *client[] );
196static int ignorexio ( Display *dpy );
197static void shutdown ( void );
198static void set_environment ( void );
199static void Fatal(char *msg);
200static void Error ( char *fmt, ... );
201
202#ifdef RETSIGTYPE /* autoconf AC_TYPE_SIGNAL */
203# define SIGVAL RETSIGTYPE
204#endif /* RETSIGTYPE */
205
206static SIGVAL
207sigCatch(int sig)
208{
209	/* On system with POSIX signals, just interrupt the system call */
210	gotSignal = sig;
211}
212
213static SIGVAL
214sigAlarm(int sig)
215{
216#if defined(SYSV) || defined(SVR4) || defined(linux) || defined(__UNIXOS2__) || defined(__APPLE__)
217	signal (sig, sigAlarm);
218#endif
219}
220
221static SIGVAL
222sigUsr1(int sig)
223{
224#if defined(SYSV) || defined(SVR4) || defined(linux) || defined(__UNIXOS2__) || defined(__APPLE__)
225	signal (sig, sigUsr1);
226#endif
227}
228
229static void
230Execute(char **vec,		/* has room from up above */
231	char **envp)
232{
233    Execvpe (vec[0], vec, envp);
234#ifndef __UNIXOS2__
235    if (access (vec[0], R_OK) == 0) {
236	vec--;				/* back it up to stuff shell in */
237	vec[0] = SHELL;
238	Execvpe (vec[0], vec, envp);
239    }
240#endif
241    return;
242}
243
244#ifndef __UNIXOS2__
245int
246main(int argc, char *argv[])
247#else
248int
249main(int argc, char *argv[], char *envp[])
250#endif
251{
252	register char **sptr = server;
253	register char **cptr = client;
254	register char **ptr;
255	int pid;
256	int client_given = 0, server_given = 0;
257	int client_args_given = 0, server_args_given = 0;
258	int start_of_client_args, start_of_server_args;
259	struct sigaction sa;
260#ifdef __APPLE__
261#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1060
262	vproc_transaction_t vt;
263#endif
264#endif
265
266#ifdef __UNIXOS2__
267	envsave = envp;	/* circumvent an EMX problem */
268
269	/* Check whether the system will run at all */
270	if (_emx_rev < 50) {
271		APIRET rc;
272		HMODULE hmod;
273		char name[CCHMAXPATH];
274		char fail[9];
275		fputs ("This program requires emx.dll revision 50 (0.9c) "
276			"or later.\n", stderr);
277		rc = DosLoadModule (fail, sizeof (fail), "emx", &hmod);
278		if (rc == 0) {
279			rc = DosQueryModuleName (hmod, sizeof (name), name);
280			if (rc == 0)
281				fprintf (stderr, "Please delete or update `%s'.\n", name);
282			DosFreeModule (hmod);
283		}
284		exit (2);
285	}
286#endif
287	program = *argv++;
288	argc--;
289	/*
290	 * copy the client args.
291	 */
292	if (argc == 0 ||
293#ifndef __UNIXOS2__
294	    (**argv != '/' && **argv != '.')) {
295#else
296	    (**argv != '/' && **argv != '\\' && **argv != '.' &&
297	     !(isalpha(**argv) && (*argv)[1]==':'))) {
298#endif
299		for (ptr = default_client; *ptr; )
300			*cptr++ = *ptr++;
301#ifdef sun
302		/*
303		 * If running on a sun, and if WINDOW_PARENT isn't defined,
304		 * that means SunWindows isn't running, so we should pass
305		 * the -C flag to xterm so that it sets up a console.
306		 */
307		if ( getenv("WINDOW_PARENT") == NULL )
308		    *cptr++ = "-C";
309#endif /* sun */
310	} else {
311		client_given = 1;
312	}
313	start_of_client_args = (cptr - client);
314	while (argc && strcmp(*argv, "--")) {
315		client_args_given++;
316		*cptr++ = *argv++;
317		argc--;
318	}
319	*cptr = NULL;
320	if (argc) {
321		argv++;
322		argc--;
323	}
324
325	/*
326	 * Copy the server args.
327	 */
328	if (argc == 0 ||
329#ifndef __UNIXOS2__
330	    (**argv != '/' && **argv != '.')) {
331		*sptr++ = default_server;
332#else
333	    (**argv != '/' && **argv != '\\' && **argv != '.' &&
334	     !(isalpha(**argv) && (*argv)[1]==':'))) {
335		*sptr = getenv("XSERVER");
336		if (!*sptr) {
337			Error("No XSERVER environment variable set");
338			exit(1);
339		}
340		*sptr++;
341#endif
342	} else {
343		server_given = 1;
344		*sptr++ = *argv++;
345		argc--;
346	}
347	if (argc > 0 && (argv[0][0] == ':' && isdigit(argv[0][1])))
348		displayNum = *argv;
349	else
350		displayNum = *sptr++ = default_display;
351
352	start_of_server_args = (sptr - server);
353	while (--argc >= 0) {
354		server_args_given++;
355		*sptr++ = *argv++;
356	}
357	*sptr = NULL;
358
359	/*
360	 * if no client arguments given, check for a startup file and copy
361	 * that into the argument list
362	 */
363	if (!client_given) {
364	    char *cp;
365	    Bool required = False;
366
367	    xinitrcbuf[0] = '\0';
368	    if ((cp = getenv ("XINITRC")) != NULL) {
369		(void) snprintf (xinitrcbuf, sizeof(xinitrcbuf), "%s", cp);
370		required = True;
371	    } else if ((cp = getenv ("HOME")) != NULL) {
372		(void) snprintf (xinitrcbuf, sizeof(xinitrcbuf),
373				 "%s/%s", cp, XINITRC);
374	    }
375	    if (xinitrcbuf[0]) {
376		if (access (xinitrcbuf, F_OK) == 0) {
377		    client += start_of_client_args - 1;
378		    client[0] = xinitrcbuf;
379		} else if (required) {
380		    fprintf (stderr,
381			     "%s:  warning, no client init file \"%s\"\n",
382			     program, xinitrcbuf);
383		}
384	    }
385	}
386
387	/*
388	 * if no server arguments given, check for a startup file and copy
389	 * that into the argument list
390	 */
391	if (!server_given) {
392	    char *cp;
393	    Bool required = False;
394
395	    xserverrcbuf[0] = '\0';
396	    if ((cp = getenv ("XSERVERRC")) != NULL) {
397		(void) snprintf (xserverrcbuf, sizeof(xserverrcbuf), "%s", cp);
398		required = True;
399	    } else if ((cp = getenv ("HOME")) != NULL) {
400		(void) snprintf (xserverrcbuf, sizeof(xserverrcbuf),
401				 "%s/%s", cp, XSERVERRC);
402	    }
403	    if (xserverrcbuf[0]) {
404		if (access (xserverrcbuf, F_OK) == 0) {
405		    server += start_of_server_args - 1;
406		    server[0] = xserverrcbuf;
407		} else if (required) {
408		    fprintf (stderr,
409			     "%s:  warning, no server init file \"%s\"\n",
410			     program, xserverrcbuf);
411		}
412	    }
413	}
414
415	/*
416	 * put the display name into the environment
417	 */
418	set_environment ();
419
420	/*
421	 * Start the server and client.
422	 */
423#ifdef SIGCHLD
424	signal(SIGCHLD, SIG_DFL);	/* Insurance */
425#endif
426
427	/* Let those signal interrupt the wait() call in the main loop */
428	memset(&sa, 0, sizeof sa);
429	sa.sa_handler = sigCatch;
430	sigemptyset(&sa.sa_mask);
431	sa.sa_flags = 0;	/* do not set SA_RESTART */
432
433	sigaction(SIGTERM, &sa, NULL);
434	sigaction(SIGQUIT, &sa, NULL);
435	sigaction(SIGINT, &sa, NULL);
436	sigaction(SIGHUP, &sa, NULL);
437	sigaction(SIGPIPE, &sa, NULL);
438
439	signal(SIGALRM, sigAlarm);
440	signal(SIGUSR1, sigUsr1);
441
442#ifdef __APPLE__
443#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1060
444	vt = vproc_transaction_begin(NULL);
445#endif
446#endif
447
448	if (startServer(server) > 0
449	 && startClient(client) > 0) {
450		pid = -1;
451		while (pid != clientpid && pid != serverpid
452		       && gotSignal == 0
453			)
454			pid = wait(NULL);
455	}
456
457#ifdef __APPLE__
458#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1060
459	vproc_transaction_end(NULL, vt);
460#endif
461#endif
462
463	signal(SIGTERM, SIG_IGN);
464	signal(SIGQUIT, SIG_IGN);
465	signal(SIGINT, SIG_IGN);
466	signal(SIGHUP, SIG_IGN);
467	signal(SIGPIPE, SIG_IGN);
468
469	shutdown();
470
471	if (gotSignal != 0) {
472		Error("unexpected signal %d.\n", gotSignal);
473		exit(ERR_EXIT);
474	}
475
476	if (serverpid < 0 )
477		Fatal("Server error.\n");
478	if (clientpid < 0)
479		Fatal("Client error.\n");
480	exit(OK_EXIT);
481}
482
483
484/*
485 *	waitforserver - wait for X server to start up
486 */
487static Bool
488waitforserver(void)
489{
490	int	ncycles	 = 120;		/* # of cycles to wait */
491	int	cycles;			/* Wait cycle count */
492
493#ifdef __APPLE__
494	/* For Apple, we don't get signaled by the server when it's ready, so we just
495	 * want to sleep now since we're going to sleep later anyways and this allows us
496	 * to avoid the awkard, "why is there an error message in the log" questions
497	 * from users.
498         */
499
500	sleep(2);
501#endif
502
503	for (cycles = 0; cycles < ncycles; cycles++) {
504		if ((xd = XOpenDisplay(displayNum))) {
505			return(TRUE);
506		}
507		else {
508		    if (!processTimeout (1, "X server to begin accepting connections"))
509		      break;
510		}
511	}
512
513	fprintf (stderr, "giving up.\r\n");
514	return(FALSE);
515}
516
517/*
518 * return TRUE if we timeout waiting for pid to exit, FALSE otherwise.
519 */
520static Bool
521processTimeout(int timeout, char *string)
522{
523	int	i = 0, pidfound = -1;
524	static char	*laststring;
525
526	for (;;) {
527#if defined(SYSV) || defined(__UNIXOS2__)
528		alarm(1);
529		if ((pidfound = wait(NULL)) == serverpid)
530			break;
531		alarm(0);
532#else /* SYSV */
533#if defined(SVR4) || defined(_POSIX_SOURCE) || defined(Lynx) || defined(__APPLE__)
534		if ((pidfound = waitpid(serverpid, &status, WNOHANG)) == serverpid)
535			break;
536#else
537		if ((pidfound = wait3(&status, WNOHANG, NULL)) == serverpid)
538			break;
539#endif
540#endif /* SYSV */
541		if (timeout) {
542			if (i == 0 && string != laststring)
543				fprintf(stderr, "\r\nwaiting for %s ", string);
544			else
545				fprintf(stderr, ".");
546			fflush(stderr);
547		}
548		if (timeout)
549			sleep (1);
550		if (++i > timeout)
551			break;
552	}
553	if ( i > 0 ) fputc( '\n', stderr );     /* tidy up after message */
554	laststring = string;
555	return( serverpid != pidfound );
556}
557
558static int
559startServer(char *server[])
560{
561	sigset_t mask, old;
562#ifdef __UNIXOS2__
563	sigset_t pendings;
564#endif
565
566	sigemptyset(&mask);
567	sigaddset(&mask, SIGUSR1);
568	sigprocmask(SIG_BLOCK, &mask, &old);
569
570	serverpid = fork();
571
572	switch(serverpid) {
573	case 0:
574		/* Unblock */
575		sigprocmask(SIG_SETMASK, &old, NULL);
576
577		/*
578		 * don't hang on read/write to control tty
579		 */
580#ifdef SIGTTIN
581		(void) signal(SIGTTIN, SIG_IGN);
582#endif
583#ifdef SIGTTOU
584		(void) signal(SIGTTOU, SIG_IGN);
585#endif
586		/*
587		 * ignore SIGUSR1 in child.  The server
588		 * will notice this and send SIGUSR1 back
589		 * at xinit when ready to accept connections
590		 */
591		(void) signal(SIGUSR1, SIG_IGN);
592		/*
593		 * prevent server from getting sighup from vhangup()
594		 * if client is xterm -L
595		 */
596#ifndef __UNIXOS2__
597		setpgid(0,getpid());
598#endif
599		Execute (server, environ);
600		Error ("no server \"%s\" in PATH\n", server[0]);
601		{
602		    const char * const *cpp;
603
604		    fprintf (stderr,
605"\nUse the -- option, or make sure that %s is in your path and\n",
606			     bindir);
607		    fprintf (stderr,
608"that \"%s\" is a program or a link to the right type of server\n",
609			     server[0]);
610		    fprintf (stderr,
611"for your display.  Possible server names include:\n\n");
612		    for (cpp = server_names; *cpp; cpp++) {
613			fprintf (stderr, "    %s\n", *cpp);
614		    }
615		    fprintf (stderr, "\n");
616		}
617		exit (ERR_EXIT);
618
619		break;
620	case -1:
621		break;
622	default:
623		/*
624		 * don't nice server
625		 */
626#ifdef PRIO_PROCESS
627		setpriority( PRIO_PROCESS, serverpid, -1 );
628#endif
629
630		errno = 0;
631		if (! processTimeout(0, "")) {
632			serverpid = -1;
633			break;
634		}
635		/*
636		 * kludge to avoid race with TCP, giving server time to
637		 * set his socket options before we try to open it,
638		 * either use the 15 second timeout, or await SIGUSR1.
639		 *
640		 * If your machine is substantially slower than 15 seconds,
641		 * you can easily adjust this value.
642		 */
643		alarm (15);
644
645#ifdef __UNIXOS2__
646		/*
647		 * fg2003/05/06: work around a problem in EMX: sigsuspend()
648		 * does not deliver pending signals when called but when
649		 * returning; so if SIGUSR1 has already been sent by the
650		 * server, we would still have to await SIGALRM
651		 */
652		sigemptyset(&pendings);
653		sigpending(&pendings);
654		if (!sigismember(&pendings, SIGUSR1))
655#endif /* __UNIXOS2__ */
656		sigsuspend(&old);
657		alarm (0);
658		sigprocmask(SIG_SETMASK, &old, NULL);
659
660		if (waitforserver() == 0) {
661			Error("unable to connect to X server\r\n");
662			shutdown();
663			serverpid = -1;
664		}
665		break;
666	}
667
668	return(serverpid);
669}
670
671static void
672setWindowPath(void)
673{
674	/* setting WINDOWPATH for clients */
675	Atom prop;
676	Atom actualtype;
677	int actualformat;
678	unsigned long nitems;
679	unsigned long bytes_after;
680	unsigned char *buf;
681	const char *windowpath;
682	char *newwindowpath;
683	unsigned long num;
684	char nums[10];
685	int numn;
686	size_t len;
687	prop = XInternAtom(xd, "XFree86_VT", False);
688	if (prop == None) {
689#ifdef DEBUG
690		fprintf(stderr, "no XFree86_VT atom\n");
691#endif
692		return;
693	}
694	if (XGetWindowProperty(xd, DefaultRootWindow(xd), prop, 0, 1,
695		False, AnyPropertyType, &actualtype, &actualformat,
696		&nitems, &bytes_after, &buf)) {
697#ifdef DEBUG
698		fprintf(stderr, "no XFree86_VT property\n");
699#endif
700		return;
701	}
702	if (nitems != 1) {
703#ifdef DEBUG
704		fprintf(stderr, "%lu items in XFree86_VT property!\n", nitems);
705#endif
706		XFree(buf);
707		return;
708	}
709	switch (actualtype) {
710	case XA_CARDINAL:
711	case XA_INTEGER:
712	case XA_WINDOW:
713		switch (actualformat) {
714		case  8:
715			num = (*(uint8_t  *)(void *)buf);
716			break;
717		case 16:
718			num = (*(uint16_t *)(void *)buf);
719			break;
720		case 32:
721			num = (*(uint32_t *)(void *)buf);
722			break;
723		default:
724#ifdef DEBUG
725			fprintf(stderr, "format %d in XFree86_VT property!\n", actualformat);
726#endif
727			XFree(buf);
728			return;
729		}
730		break;
731	default:
732#ifdef DEBUG
733		fprintf(stderr, "type %lx in XFree86_VT property!\n", actualtype);
734#endif
735		XFree(buf);
736		return;
737	}
738	XFree(buf);
739	windowpath = getenv("WINDOWPATH");
740	numn = snprintf(nums, sizeof(nums), "%lu", num);
741	if (!windowpath) {
742		len = 10 + 1 + numn + 1;
743		newwindowpath = malloc(len);
744		if (newwindowpath == NULL)
745		    return;
746		snprintf(newwindowpath, len, "WINDOWPATH=%s", nums);
747	} else {
748		len = 10 + 1 + strlen(windowpath) + 1 + numn + 1;
749		newwindowpath = malloc(len);
750		if (newwindowpath == NULL)
751		    return;
752		snprintf(newwindowpath, len, "WINDOWPATH=%s:%s",
753			 windowpath, nums);
754	}
755	*newenvironlast++ = newwindowpath;
756	*newenvironlast = NULL;
757}
758
759static int
760startClient(char *client[])
761{
762	setWindowPath();
763	if ((clientpid = vfork()) == 0) {
764		if (setuid(getuid()) == -1) {
765			Error("cannot change uid: %s\n", strerror(errno));
766			_exit(ERR_EXIT);
767		}
768		setpgid(0, getpid());
769		environ = newenviron;
770#ifdef __UNIXOS2__
771#undef environ
772		environ = newenviron;
773		client[0] = (char*)__XOS2RedirRoot(client[0]);
774#endif
775		Execute (client,newenviron);
776		Error ("no program named \"%s\" in PATH\r\n", client[0]);
777		fprintf (stderr,
778"\nSpecify a program on the command line or make sure that %s\r\n", bindir);
779		fprintf (stderr,
780"is in your path.\r\n");
781		fprintf (stderr, "\n");
782		_exit (ERR_EXIT);
783	}
784	return (clientpid);
785}
786
787#ifndef HAVE_KILLPG
788#define killpg(pgrp, sig) kill(-(pgrp), sig)
789#endif
790
791static jmp_buf close_env;
792
793static int
794ignorexio(Display *dpy)
795{
796    fprintf (stderr, "%s:  connection to X server lost.\r\n", program);
797    longjmp (close_env, 1);
798    /*NOTREACHED*/
799    return 0;
800}
801
802static void
803shutdown(void)
804{
805	/* have kept display opened, so close it now */
806	if (clientpid > 0) {
807		XSetIOErrorHandler (ignorexio);
808		if (! setjmp(close_env)) {
809		    XCloseDisplay(xd);
810		}
811
812		/* HUP all local clients to allow them to clean up */
813		errno = 0;
814		if ((killpg(clientpid, SIGHUP) != 0) &&
815		    (errno != ESRCH))
816			Error("can't send HUP to process group %d\r\n",
817				clientpid);
818	}
819
820	if (serverpid < 0)
821		return;
822	errno = 0;
823	if (killpg(serverpid, SIGTERM) < 0) {
824		if (errno == EPERM)
825			Fatal("Can't kill X server\r\n");
826		if (errno == ESRCH)
827			return;
828	}
829	if (! processTimeout(10, "X server to shut down")) {
830	    fprintf (stderr, "\r\n");
831	    return;
832	}
833
834	fprintf(stderr,
835	"\r\n%s:  X server slow to shut down, sending KILL signal.\r\n",
836		program);
837	fflush(stderr);
838	errno = 0;
839	if (killpg(serverpid, SIGKILL) < 0) {
840		if (errno == ESRCH)
841			return;
842	}
843	if (processTimeout(3, "server to die")) {
844		fprintf (stderr, "\r\n");
845		Fatal("Can't kill server\r\n");
846	}
847	fprintf (stderr, "\r\n");
848	return;
849}
850
851
852/*
853 * make a new copy of environment that has room for DISPLAY
854 */
855
856static void
857set_environment(void)
858{
859    int nenvvars;
860    char **newPtr, **oldPtr;
861    static char displaybuf[512];
862
863    /* count number of environment variables */
864    for (oldPtr = environ; *oldPtr; oldPtr++) ;
865
866    nenvvars = (oldPtr - environ);
867    newenviron = (char **) malloc ((nenvvars + 3) * sizeof(char **));
868    if (!newenviron) {
869	fprintf (stderr,
870		 "%s:  unable to allocate %d pointers for environment\n",
871		 program, nenvvars + 3);
872	exit (1);
873    }
874
875    /* put DISPLAY=displayname as first element */
876    snprintf (displaybuf, sizeof(displaybuf), "DISPLAY=%s", displayNum);
877    newPtr = newenviron;
878    *newPtr++ = displaybuf;
879
880    /* copy pointers to other variables */
881    for (oldPtr = environ; *oldPtr; oldPtr++) {
882	if (strncmp (*oldPtr, "DISPLAY=", 8) != 0
883	 && strncmp (*oldPtr, "WINDOWPATH=", 11) != 0) {
884	    *newPtr++ = *oldPtr;
885	}
886    }
887    *newPtr = NULL;
888    newenvironlast=newPtr;
889    return;
890}
891
892static void
893Fatal(char *msg)
894{
895	Error(msg);
896	exit(ERR_EXIT);
897}
898
899static void
900Error(char *fmt, ...)
901{
902        va_list ap;
903
904	va_start(ap, fmt);
905	fprintf(stderr, "%s:  ", program);
906	if (errno > 0)
907	  fprintf (stderr, "%s (errno %d):  ", strerror(errno), errno);
908	vfprintf(stderr, fmt, ap);
909	va_end(ap);
910}
911