#include "normPostProcess.h"
#include "protoDebug.h"
#include <errno.h>
#include <string.h>
#include <signal.h>
#include <ctype.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <stdio.h>
#ifndef sighandler_t
#ifndef sig_t
typedef void (*sighandler_t)(int);
#else
typedef sig_t sighandler_t;
#endif #endif
#ifdef NETSCAPE_SUPPORT
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <X11/Xmu/WinUtil.h>
#include "vroot.h"
#define MOZILLA_VERSION_PROP "_MOZILLA_VERSION"
static Atom XA_MOZILLA_VERSION = 0;
bool CheckForNetscape(const char* cmd);
bool NetscapeIsRunning(Window *result);
bool NetscapeCheckWindow(Window window);
#endif
#define MAX_SUBPROCESSES 32
static void OnSIGCHLD(int signum);
class UnixPostProcessor : public NormPostProcessor
{
public:
~UnixPostProcessor();
bool IsActive();
bool ProcessFile(const char* path);
void Kill();
void ClearPid(int pid);
private:
friend class NormPostProcessor;
UnixPostProcessor();
bool IsActive(unsigned int array_element);
unsigned int GetOpenProcessIdSlot();
void Kill(unsigned int array_element);
int process_id[MAX_SUBPROCESSES];
#ifdef NETSCAPE_SUPPORT
Window window_id;
#endif };
UnixPostProcessor* theprocessor;
UnixPostProcessor::UnixPostProcessor()
: process_id()
#ifdef NETSCAPE_SUPPORT
,window_id(0)
#endif {
signal(SIGCHLD, OnSIGCHLD);
}
NormPostProcessor* NormPostProcessor::Create()
{
theprocessor = new UnixPostProcessor;
return static_cast<NormPostProcessor*>(theprocessor);
}
UnixPostProcessor::~UnixPostProcessor()
{
if (IsActive()) Kill();
}
bool UnixPostProcessor::IsActive()
{
for (unsigned int i=0;i<MAX_SUBPROCESSES;i++)
{
if (0 != process_id[i]) return true;
}
return false;
}
bool UnixPostProcessor::IsActive(unsigned int array_element)
{
return (0 != process_id[array_element]);
}
unsigned int UnixPostProcessor::GetOpenProcessIdSlot()
{
for (unsigned int i=0;i<MAX_SUBPROCESSES;i++)
{
if (0 == process_id[i]) return i;
}
Kill(0);
return 0;
}
bool UnixPostProcessor::ProcessFile(const char* path)
{
const char** argv = (const char**)process_argv;
int argc = process_argc;
#ifdef NETSCAPE_SUPPORT
#define USE_ACTIVE_WINDOW 0xffffffff
const char* myArgs[32];
char wid_text[32], url_text[PATH_MAX+64];
if (CheckForNetscape(argv[0]))
{
int i = 0;
myArgs[i++] = process_argv[0];
Window w = 0;
if (NetscapeIsRunning(&w))
{
if (window_id)
{
if (NetscapeCheckWindow(window_id))
{
myArgs[i++] = "-id";
sprintf(wid_text, "0x%lx", window_id);
myArgs[i++] = wid_text;
myArgs[i++] = "-noraise";
myArgs[i++] = "-remote";
sprintf(url_text, "openURL(file://%s)", path);
myArgs[i++] = url_text;
}
else if (USE_ACTIVE_WINDOW == window_id)
{
myArgs[i++] = "-id";
sprintf(wid_text, "0x%lx", w);
myArgs[i++] = wid_text;
window_id = w;
myArgs[i++] = "-noraise";
myArgs[i++] = "-remote";
sprintf(url_text, "openURL(file://%s)", path);
myArgs[i++] = url_text;
}
else {
window_id = USE_ACTIVE_WINDOW;
myArgs[i++] = "-remote";
sprintf(url_text, "openURL(file://%s,new-window)", path);
myArgs[i++] = url_text;
}
}
else
{
window_id = USE_ACTIVE_WINDOW;
myArgs[i++] = "-remote";
sprintf(url_text, "openURL(file://%s,new-window)", path);
myArgs[i++] = url_text;
}
argv = myArgs;
}
else
{
window_id = USE_ACTIVE_WINDOW;
myArgs[i++] = path;
} myArgs[i] = NULL;
argc = i - 1;
argv = myArgs;
}
else
#endif {
argv[argc] = path;
}
sighandler_t sigtermHandler = signal(SIGTERM, SIG_DFL);
sighandler_t sigintHandler = signal(SIGINT, SIG_DFL);
sighandler_t sigchldHandler = signal(SIGCHLD, SIG_DFL);
unsigned int idnum = GetOpenProcessIdSlot();
switch((process_id[idnum] = fork()))
{
case -1: DMSG(0, "UnixPostProcessor::ProcessFile fork() error: %s\n", strerror(errno));
process_id[idnum] = 0;
process_argv[process_argc] = NULL;
return false;
case 0: if (execvp((char*)argv[0], (char**)argv) < 0)
{
DMSG(0, "UnixPostProcessor::ProcessFile execvp() error: %s\n", strerror(errno));
exit(-1);
}
break;
default: process_argv[process_argc] = NULL;
signal(SIGTERM, sigtermHandler);
signal(SIGINT, sigintHandler);
signal(SIGCHLD, sigchldHandler);
break;
}
return true;
}
void UnixPostProcessor::Kill()
{
for (unsigned int i=0;i<MAX_SUBPROCESSES;i++)
{
if (IsActive(i)) Kill(i);
}
}
void UnixPostProcessor::Kill(unsigned int array_element)
{
if (!IsActive(array_element)) return;
int count = 0;
while((kill(process_id[array_element], SIGTERM) != 0) && count < 10)
{
if (errno == ESRCH) break;
count++;
DMSG(0, "UnixPostProcessor::Kill kill() error: %s\n", strerror(errno));
}
count = 0;
int status;
while((waitpid(process_id[array_element], &status, 0) != process_id[array_element]) && count < 10)
{
if (errno == ECHILD) break;
count++;
DMSG(0, "UnixPostProcessor::Kill waitpid() error: %s\n", strerror(errno));
}
process_id[array_element] = 0;
}
void UnixPostProcessor::ClearPid(int pid)
{
for (unsigned int i=0;i<MAX_SUBPROCESSES;i++)
{
if (pid == process_id[i]) process_id[i] = 0;
}
}
static void OnSIGCHLD(int signum)
{
int status;
pid_t p;
while (true)
{
p = waitpid(-1, &status, WNOHANG);
if (p == -1)
{
if (errno == EINTR) continue;
break;
}
else if (p == 0) break;
theprocessor->ClearPid(p);
}
}
#ifdef NETSCAPE_SUPPORT
bool CheckForNetscape(const char* cmd)
{
char txt[8];
const char *ptr = strrchr(cmd, PROTO_PATH_DELIMITER);
if (ptr)
ptr++;
else
ptr = cmd;
strncpy(txt, ptr, 8);
for (int i=0; i<8; i++) txt[i] = toupper(txt[i]);
if (!strncmp(txt, "NETSCAPE", 8))
return true;
else
return false;
}
bool NetscapeIsRunning(Window *result)
{
int i;
Window root2, parent, *kids;
unsigned int nkids;
*result = 0;
Display *dpy = XOpenDisplay(NULL);
if (!dpy)
{
fprintf(stderr, "mdp: XOpenDisplay() error\n");
return false;
}
if (!XA_MOZILLA_VERSION)
XA_MOZILLA_VERSION = XInternAtom(dpy, MOZILLA_VERSION_PROP, False);
Window root = RootWindowOfScreen(DefaultScreenOfDisplay(dpy));
if (!XQueryTree (dpy, root, &root2, &parent, &kids, &nkids))
{
fprintf(stderr, "mdp: XQueryTree failed on display %s\n", DisplayString (dpy));
XCloseDisplay(dpy);
return false;
}
if (!(kids && nkids))
{
fprintf(stderr, "mdp: root window has no children on display %s\n",
DisplayString (dpy));
XCloseDisplay(dpy);
return false;
}
for (i = nkids-1; i >= 0; i--)
{
Atom type;
int format;
unsigned long nitems, bytesafter;
unsigned char *version = 0;
Window w = XmuClientWindow (dpy, kids[i]);
int status = XGetWindowProperty (dpy, w, XA_MOZILLA_VERSION,
0, (65536 / sizeof (long)),
False, XA_STRING,
&type, &format, &nitems, &bytesafter,
&version);
if (! version) continue;
XFree (version);
if (status == Success && type != None)
{
*result = w;
break;
}
}
XCloseDisplay(dpy);
return (*result ? true : false);
}
bool NetscapeCheckWindow(Window window)
{
int i;
Window root2, parent, *kids;
unsigned int nkids;
Display *dpy = XOpenDisplay(NULL);
if (!dpy)
{
fprintf(stderr, "mdp: XOpenDisplay() error\n");
return false;
}
if (!XA_MOZILLA_VERSION)
XA_MOZILLA_VERSION = XInternAtom(dpy, MOZILLA_VERSION_PROP, False);
Window root = RootWindowOfScreen(DefaultScreenOfDisplay (dpy));
if (!XQueryTree (dpy, root, &root2, &parent, &kids, &nkids))
{
fprintf(stderr, "mdp: XQueryTree failed on display %s\n", DisplayString (dpy));
XCloseDisplay(dpy);
return false;
}
if (!(kids && nkids)) return false;
for (i = nkids-1; i >= 0; i--)
{
Atom type;
int format;
unsigned long nitems, bytesafter;
unsigned char *version = 0;
Window w = XmuClientWindow (dpy, kids[i]);
int status = XGetWindowProperty (dpy, w, XA_MOZILLA_VERSION,
0, (65536 / sizeof (long)),
False, XA_STRING,
&type, &format, &nitems, &bytesafter,
&version);
if (!version) continue;
XFree (version);
if (status == Success && type != None)
{
if (w == window)
{
XCloseDisplay(dpy);
return true;
}
else
{
continue;
}
}
}
XCloseDisplay(dpy);
return false;
} #endif