#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <X11/Xlib.h>
#include <X11/Xos.h>
#include <X11/Xatom.h>
#include <stdio.h>
#include <ctype.h>
#include <stdint.h>
#ifdef X_POSIX_C_SOURCE
#define _POSIX_C_SOURCE X_POSIX_C_SOURCE
#include <signal.h>
#undef _POSIX_C_SOURCE
#else
#if defined(X_NOT_POSIX) || defined(_POSIX_SOURCE)
#include <signal.h>
#else
#define _POSIX_SOURCE
#include <signal.h>
#undef _POSIX_SOURCE
#endif
#endif
#ifndef SYSV
#include <sys/wait.h>
#endif
#include <errno.h>
#include <setjmp.h>
#include <stdarg.h>
#ifdef __APPLE__
#include <AvailabilityMacros.h>
#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1060
#include <vproc.h>
#endif
#endif
#if !defined(SIGCHLD) && defined(SIGCLD)
#define SIGCHLD SIGCLD
#endif
#ifdef __UNIXOS2__
#define INCL_DOSMODULEMGR
#include <os2.h>
#define setpgid(a,b)
#define setuid(a)
#define setgid(a)
#define SHELL "cmd.exe"
#define XINITRC "xinitrc.cmd"
#define XSERVERRC "xservrc.cmd"
char **envsave;
#define environ envsave
#endif
#include <stdlib.h>
extern char **environ;
char **newenviron = NULL;
char **newenvironlast = NULL;
#ifndef SHELL
#define SHELL "sh"
#endif
#ifndef HAVE_WORKING_VFORK
# ifndef vfork
# define vfork() fork()
# endif
#else
# ifdef HAVE_VFORK_H
# include <vfork.h>
# endif
#endif
#ifdef macII
#define setpgid setpgrp
#endif
#ifdef __UNIXOS2__
#define HAS_EXECVPE
#endif
#ifdef HAS_EXECVPE
#define Execvpe(path, argv, envp) execvpe(path, argv, envp)
#else
#define Execvpe(path, argv, envp) execvp(path, argv)
#endif
const char *bindir = BINDIR;
const char * const server_names[] = {
#if defined(ultrix) && defined(mips)
"Xdec Digital color display on DECstation",
#endif
#if defined(sun) && !defined(XORG)
"Xsun Sun BW2, CG2, CG3, CG4, or CG6 on Sun 2, 3, 4, or 386i",
"Xsunmono Sun BW2 on Sun 2, 3, 4, or 386i ",
"Xsun24 Sun BW2, CG2, CG3, CG4, CG6, or CG8 on Sun 4",
#endif
#ifdef hpux
"Xhp HP monochrome and colors displays on 9000/300 series",
#endif
#ifdef ibm
"Xibm IBM AED, APA, 8514a, megapel, VGA displays on PC/RT",
#endif
#ifdef macII
"XmacII Apple monochrome display on Macintosh II",
#endif
#ifdef XFREE86
"XFree86 XFree86 displays",
#endif
#ifdef XORG
"Xorg Common X server for most displays",
#endif
#ifdef __APPLE__
"Xquartz Mac OSX Quartz displays.",
#endif
"Xvfb Virtual frame buffer",
"Xfake kdrive-based virtual frame buffer",
"Xnest X server nested in a window on another X server",
"Xephyr kdrive-based nested X server",
NULL};
#ifndef XINITRC
#define XINITRC ".xinitrc"
#endif
char xinitrcbuf[256];
#ifndef XSERVERRC
#define XSERVERRC ".xserverrc"
#endif
char xserverrcbuf[256];
#define TRUE 1
#define FALSE 0
#define OK_EXIT 0
#define ERR_EXIT 1
static char *default_server = "X";
static char *default_display = ":0";
static char *default_client[] = {"xterm", "-geometry", "+1+1", "-n", "login", NULL};
static char *serverargv[100];
static char *clientargv[100];
static char **server = serverargv + 2;
static char **client = clientargv + 2;
static char *displayNum = NULL;
static char *program = NULL;
static Display *xd = NULL;
#ifndef SYSV
#if defined(__CYGWIN__) || defined(SVR4) || defined(_POSIX_SOURCE) || defined(CSRG_BASED) || defined(__UNIXOS2__) || defined(Lynx) || defined(__APPLE__)
int status;
#else
union wait status;
#endif
#endif
int serverpid = -1;
int clientpid = -1;
volatile int gotSignal = 0;
static void Execute ( char **vec, char **envp );
static Bool waitforserver ( void );
static Bool processTimeout ( int timeout, char *string );
static int startServer ( char *server[] );
static int startClient ( char *client[] );
static int ignorexio ( Display *dpy );
static void shutdown ( void );
static void set_environment ( void );
static void Fatal(char *msg);
static void Error ( char *fmt, ... );
#ifdef RETSIGTYPE
# define SIGVAL RETSIGTYPE
#endif
static SIGVAL
sigCatch(int sig)
{
gotSignal = sig;
}
static SIGVAL
sigAlarm(int sig)
{
#if defined(SYSV) || defined(SVR4) || defined(linux) || defined(__UNIXOS2__) || defined(__APPLE__)
signal (sig, sigAlarm);
#endif
}
static SIGVAL
sigUsr1(int sig)
{
#if defined(SYSV) || defined(SVR4) || defined(linux) || defined(__UNIXOS2__) || defined(__APPLE__)
signal (sig, sigUsr1);
#endif
}
static void
Execute(char **vec,
char **envp)
{
Execvpe (vec[0], vec, envp);
#ifndef __UNIXOS2__
if (access (vec[0], R_OK) == 0) {
vec--;
vec[0] = SHELL;
Execvpe (vec[0], vec, envp);
}
#endif
return;
}
#ifndef __UNIXOS2__
int
main(int argc, char *argv[])
#else
int
main(int argc, char *argv[], char *envp[])
#endif
{
register char **sptr = server;
register char **cptr = client;
register char **ptr;
int pid;
int client_given = 0, server_given = 0;
int client_args_given = 0, server_args_given = 0;
int start_of_client_args, start_of_server_args;
struct sigaction sa;
#ifdef __APPLE__
#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1060
vproc_transaction_t vt;
#endif
#endif
#ifdef __UNIXOS2__
envsave = envp;
if (_emx_rev < 50) {
APIRET rc;
HMODULE hmod;
char name[CCHMAXPATH];
char fail[9];
fputs ("This program requires emx.dll revision 50 (0.9c) "
"or later.\n", stderr);
rc = DosLoadModule (fail, sizeof (fail), "emx", &hmod);
if (rc == 0) {
rc = DosQueryModuleName (hmod, sizeof (name), name);
if (rc == 0)
fprintf (stderr, "Please delete or update `%s'.\n", name);
DosFreeModule (hmod);
}
exit (2);
}
#endif
program = *argv++;
argc--;
if (argc == 0 ||
#ifndef __UNIXOS2__
(**argv != '/' && **argv != '.')) {
#else
(**argv != '/' && **argv != '\\' && **argv != '.' &&
!(isalpha(**argv) && (*argv)[1]==':'))) {
#endif
for (ptr = default_client; *ptr; )
*cptr++ = *ptr++;
#ifdef sun
if ( getenv("WINDOW_PARENT") == NULL )
*cptr++ = "-C";
#endif
} else {
client_given = 1;
}
start_of_client_args = (cptr - client);
while (argc && strcmp(*argv, "--")) {
client_args_given++;
*cptr++ = *argv++;
argc--;
}
*cptr = NULL;
if (argc) {
argv++;
argc--;
}
if (argc == 0 ||
#ifndef __UNIXOS2__
(**argv != '/' && **argv != '.')) {
*sptr++ = default_server;
#else
(**argv != '/' && **argv != '\\' && **argv != '.' &&
!(isalpha(**argv) && (*argv)[1]==':'))) {
*sptr = getenv("XSERVER");
if (!*sptr) {
Error("No XSERVER environment variable set");
exit(1);
}
*sptr++;
#endif
} else {
server_given = 1;
*sptr++ = *argv++;
argc--;
}
if (argc > 0 && (argv[0][0] == ':' && isdigit(argv[0][1])))
displayNum = *argv;
else
displayNum = *sptr++ = default_display;
start_of_server_args = (sptr - server);
while (--argc >= 0) {
server_args_given++;
*sptr++ = *argv++;
}
*sptr = NULL;
if (!client_given) {
char *cp;
Bool required = False;
xinitrcbuf[0] = '\0';
if ((cp = getenv ("XINITRC")) != NULL) {
(void) snprintf (xinitrcbuf, sizeof(xinitrcbuf), "%s", cp);
required = True;
} else if ((cp = getenv ("HOME")) != NULL) {
(void) snprintf (xinitrcbuf, sizeof(xinitrcbuf),
"%s/%s", cp, XINITRC);
}
if (xinitrcbuf[0]) {
if (access (xinitrcbuf, F_OK) == 0) {
client += start_of_client_args - 1;
client[0] = xinitrcbuf;
} else if (required) {
fprintf (stderr,
"%s: warning, no client init file \"%s\"\n",
program, xinitrcbuf);
}
}
}
if (!server_given) {
char *cp;
Bool required = False;
xserverrcbuf[0] = '\0';
if ((cp = getenv ("XSERVERRC")) != NULL) {
(void) snprintf (xserverrcbuf, sizeof(xserverrcbuf), "%s", cp);
required = True;
} else if ((cp = getenv ("HOME")) != NULL) {
(void) snprintf (xserverrcbuf, sizeof(xserverrcbuf),
"%s/%s", cp, XSERVERRC);
}
if (xserverrcbuf[0]) {
if (access (xserverrcbuf, F_OK) == 0) {
server += start_of_server_args - 1;
server[0] = xserverrcbuf;
} else if (required) {
fprintf (stderr,
"%s: warning, no server init file \"%s\"\n",
program, xserverrcbuf);
}
}
}
set_environment ();
#ifdef SIGCHLD
signal(SIGCHLD, SIG_DFL);
#endif
memset(&sa, 0, sizeof sa);
sa.sa_handler = sigCatch;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
sigaction(SIGTERM, &sa, NULL);
sigaction(SIGQUIT, &sa, NULL);
sigaction(SIGINT, &sa, NULL);
sigaction(SIGHUP, &sa, NULL);
sigaction(SIGPIPE, &sa, NULL);
signal(SIGALRM, sigAlarm);
signal(SIGUSR1, sigUsr1);
#ifdef __APPLE__
#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1060
vt = vproc_transaction_begin(NULL);
#endif
#endif
if (startServer(server) > 0
&& startClient(client) > 0) {
pid = -1;
while (pid != clientpid && pid != serverpid
&& gotSignal == 0
)
pid = wait(NULL);
}
#ifdef __APPLE__
#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1060
vproc_transaction_end(NULL, vt);
#endif
#endif
signal(SIGTERM, SIG_IGN);
signal(SIGQUIT, SIG_IGN);
signal(SIGINT, SIG_IGN);
signal(SIGHUP, SIG_IGN);
signal(SIGPIPE, SIG_IGN);
shutdown();
if (gotSignal != 0) {
Error("unexpected signal %d.\n", gotSignal);
exit(ERR_EXIT);
}
if (serverpid < 0 )
Fatal("Server error.\n");
if (clientpid < 0)
Fatal("Client error.\n");
exit(OK_EXIT);
}
static Bool
waitforserver(void)
{
int ncycles = 120;
int cycles;
#ifdef __APPLE__
sleep(2);
#endif
for (cycles = 0; cycles < ncycles; cycles++) {
if ((xd = XOpenDisplay(displayNum))) {
return(TRUE);
}
else {
if (!processTimeout (1, "X server to begin accepting connections"))
break;
}
}
fprintf (stderr, "giving up.\r\n");
return(FALSE);
}
static Bool
processTimeout(int timeout, char *string)
{
int i = 0, pidfound = -1;
static char *laststring;
for (;;) {
#if defined(SYSV) || defined(__UNIXOS2__)
alarm(1);
if ((pidfound = wait(NULL)) == serverpid)
break;
alarm(0);
#else
#if defined(SVR4) || defined(_POSIX_SOURCE) || defined(Lynx) || defined(__APPLE__)
if ((pidfound = waitpid(serverpid, &status, WNOHANG)) == serverpid)
break;
#else
if ((pidfound = wait3(&status, WNOHANG, NULL)) == serverpid)
break;
#endif
#endif
if (timeout) {
if (i == 0 && string != laststring)
fprintf(stderr, "\r\nwaiting for %s ", string);
else
fprintf(stderr, ".");
fflush(stderr);
}
if (timeout)
sleep (1);
if (++i > timeout)
break;
}
if ( i > 0 ) fputc( '\n', stderr );
laststring = string;
return( serverpid != pidfound );
}
static int
startServer(char *server[])
{
sigset_t mask, old;
#ifdef __UNIXOS2__
sigset_t pendings;
#endif
sigemptyset(&mask);
sigaddset(&mask, SIGUSR1);
sigprocmask(SIG_BLOCK, &mask, &old);
serverpid = fork();
switch(serverpid) {
case 0:
sigprocmask(SIG_SETMASK, &old, NULL);
#ifdef SIGTTIN
(void) signal(SIGTTIN, SIG_IGN);
#endif
#ifdef SIGTTOU
(void) signal(SIGTTOU, SIG_IGN);
#endif
(void) signal(SIGUSR1, SIG_IGN);
#ifndef __UNIXOS2__
setpgid(0,getpid());
#endif
Execute (server, environ);
Error ("no server \"%s\" in PATH\n", server[0]);
{
const char * const *cpp;
fprintf (stderr,
"\nUse the -- option, or make sure that %s is in your path and\n",
bindir);
fprintf (stderr,
"that \"%s\" is a program or a link to the right type of server\n",
server[0]);
fprintf (stderr,
"for your display. Possible server names include:\n\n");
for (cpp = server_names; *cpp; cpp++) {
fprintf (stderr, " %s\n", *cpp);
}
fprintf (stderr, "\n");
}
exit (ERR_EXIT);
break;
case -1:
break;
default:
#ifdef PRIO_PROCESS
setpriority( PRIO_PROCESS, serverpid, -1 );
#endif
errno = 0;
if (! processTimeout(0, "")) {
serverpid = -1;
break;
}
alarm (15);
#ifdef __UNIXOS2__
sigemptyset(&pendings);
sigpending(&pendings);
if (!sigismember(&pendings, SIGUSR1))
#endif
sigsuspend(&old);
alarm (0);
sigprocmask(SIG_SETMASK, &old, NULL);
if (waitforserver() == 0) {
Error("unable to connect to X server\r\n");
shutdown();
serverpid = -1;
}
break;
}
return(serverpid);
}
static void
setWindowPath(void)
{
Atom prop;
Atom actualtype;
int actualformat;
unsigned long nitems;
unsigned long bytes_after;
unsigned char *buf;
const char *windowpath;
char *newwindowpath;
unsigned long num;
char nums[10];
int numn;
size_t len;
prop = XInternAtom(xd, "XFree86_VT", False);
if (prop == None) {
#ifdef DEBUG
fprintf(stderr, "no XFree86_VT atom\n");
#endif
return;
}
if (XGetWindowProperty(xd, DefaultRootWindow(xd), prop, 0, 1,
False, AnyPropertyType, &actualtype, &actualformat,
&nitems, &bytes_after, &buf)) {
#ifdef DEBUG
fprintf(stderr, "no XFree86_VT property\n");
#endif
return;
}
if (nitems != 1) {
#ifdef DEBUG
fprintf(stderr, "%lu items in XFree86_VT property!\n", nitems);
#endif
XFree(buf);
return;
}
switch (actualtype) {
case XA_CARDINAL:
case XA_INTEGER:
case XA_WINDOW:
switch (actualformat) {
case 8:
num = (*(uint8_t *)(void *)buf);
break;
case 16:
num = (*(uint16_t *)(void *)buf);
break;
case 32:
num = (*(uint32_t *)(void *)buf);
break;
default:
#ifdef DEBUG
fprintf(stderr, "format %d in XFree86_VT property!\n", actualformat);
#endif
XFree(buf);
return;
}
break;
default:
#ifdef DEBUG
fprintf(stderr, "type %lx in XFree86_VT property!\n", actualtype);
#endif
XFree(buf);
return;
}
XFree(buf);
windowpath = getenv("WINDOWPATH");
numn = snprintf(nums, sizeof(nums), "%lu", num);
if (!windowpath) {
len = 10 + 1 + numn + 1;
newwindowpath = malloc(len);
if (newwindowpath == NULL)
return;
snprintf(newwindowpath, len, "WINDOWPATH=%s", nums);
} else {
len = 10 + 1 + strlen(windowpath) + 1 + numn + 1;
newwindowpath = malloc(len);
if (newwindowpath == NULL)
return;
snprintf(newwindowpath, len, "WINDOWPATH=%s:%s",
windowpath, nums);
}
*newenvironlast++ = newwindowpath;
*newenvironlast = NULL;
}
static int
startClient(char *client[])
{
setWindowPath();
if ((clientpid = vfork()) == 0) {
if (setuid(getuid()) == -1) {
Error("cannot change uid: %s\n", strerror(errno));
_exit(ERR_EXIT);
}
setpgid(0, getpid());
environ = newenviron;
#ifdef __UNIXOS2__
#undef environ
environ = newenviron;
client[0] = (char*)__XOS2RedirRoot(client[0]);
#endif
Execute (client,newenviron);
Error ("no program named \"%s\" in PATH\r\n", client[0]);
fprintf (stderr,
"\nSpecify a program on the command line or make sure that %s\r\n", bindir);
fprintf (stderr,
"is in your path.\r\n");
fprintf (stderr, "\n");
_exit (ERR_EXIT);
}
return (clientpid);
}
#ifndef HAVE_KILLPG
#define killpg(pgrp, sig) kill(-(pgrp), sig)
#endif
static jmp_buf close_env;
static int
ignorexio(Display *dpy)
{
fprintf (stderr, "%s: connection to X server lost.\r\n", program);
longjmp (close_env, 1);
return 0;
}
static void
shutdown(void)
{
if (clientpid > 0) {
XSetIOErrorHandler (ignorexio);
if (! setjmp(close_env)) {
XCloseDisplay(xd);
}
errno = 0;
if ((killpg(clientpid, SIGHUP) != 0) &&
(errno != ESRCH))
Error("can't send HUP to process group %d\r\n",
clientpid);
}
if (serverpid < 0)
return;
errno = 0;
if (killpg(serverpid, SIGTERM) < 0) {
if (errno == EPERM)
Fatal("Can't kill X server\r\n");
if (errno == ESRCH)
return;
}
if (! processTimeout(10, "X server to shut down")) {
fprintf (stderr, "\r\n");
return;
}
fprintf(stderr,
"\r\n%s: X server slow to shut down, sending KILL signal.\r\n",
program);
fflush(stderr);
errno = 0;
if (killpg(serverpid, SIGKILL) < 0) {
if (errno == ESRCH)
return;
}
if (processTimeout(3, "server to die")) {
fprintf (stderr, "\r\n");
Fatal("Can't kill server\r\n");
}
fprintf (stderr, "\r\n");
return;
}
static void
set_environment(void)
{
int nenvvars;
char **newPtr, **oldPtr;
static char displaybuf[512];
for (oldPtr = environ; *oldPtr; oldPtr++) ;
nenvvars = (oldPtr - environ);
newenviron = (char **) malloc ((nenvvars + 3) * sizeof(char **));
if (!newenviron) {
fprintf (stderr,
"%s: unable to allocate %d pointers for environment\n",
program, nenvvars + 3);
exit (1);
}
snprintf (displaybuf, sizeof(displaybuf), "DISPLAY=%s", displayNum);
newPtr = newenviron;
*newPtr++ = displaybuf;
for (oldPtr = environ; *oldPtr; oldPtr++) {
if (strncmp (*oldPtr, "DISPLAY=", 8) != 0
&& strncmp (*oldPtr, "WINDOWPATH=", 11) != 0) {
*newPtr++ = *oldPtr;
}
}
*newPtr = NULL;
newenvironlast=newPtr;
return;
}
static void
Fatal(char *msg)
{
Error(msg);
exit(ERR_EXIT);
}
static void
Error(char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
fprintf(stderr, "%s: ", program);
if (errno > 0)
fprintf (stderr, "%s (errno %d): ", strerror(errno), errno);
vfprintf(stderr, fmt, ap);
va_end(ap);
}