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