#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 __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[] = {
#ifdef __APPLE__
"Xquartz Mac OSX Quartz displays.",
#else
# ifdef __CYGWIN__
"XWin X Server for the Cygwin environment on Microsoft Windows",
# else
"Xorg Common X server for most displays",
# endif
#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",
"Xvnc X server accessed over VNC's RFB protocol",
"Xdmx Distributed Multi-head 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++;
} 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);
}