/* -*- Mode:C; tab-width: 8 -*- * Checks whether there is a netscape running, and responsive. * Exits with 0 if yes; non-zero if no. * Created: 4-Aug-01 by jwz, for Netscape 4.x. * Modified: 21-May-02 by jwz, for Mozilla 0.x. * * Mostly lifted from: * remote.c --- remote control of Netscape Navigator for Unix. * version 1.1.3, for Netscape Navigator 1.1 and newer. * * Copyright © 1996 Netscape Communications Corporation, all rights reserved. * Created: Jamie Zawinski , 24-Dec-94. * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation. No representations are made about the suitability of this * software for any purpose. It is provided "as is" without express or * implied warranty. * * Documentation for the protocol which this code implements may be found at: * * http://home.netscape.com/newsref/std/x-remote.html */ #include #include #include #include #include #include #include /* for XmuClientWindow() */ #include /* vroot.h is a header file which lets a client get along with `virtual root' window managers like swm, tvtwm, olvwm, etc. If you don't have this header file, you can find it at "http://home.netscape.com/newsref/std/vroot.h". If you don't care about supporting virtual root window managers, you can comment this line out. */ /*#include "vroot.h"*/ #define STANDALONE #define DEBUG_PROPS #ifdef STANDALONE static const char *progname = 0; static const char *expected_mozilla_version = "4.77"; #else /* !STANDALONE */ extern const char *progname; extern const char *expected_mozilla_version; #endif /* !STANDALONE */ #define MOZILLA_VERSION_PROP "_MOZILLA_VERSION" #define MOZILLA_LOCK_PROP "_MOZILLA_LOCK" #define MOZILLA_COMMAND_PROP "_MOZILLA_COMMAND" #define MOZILLA_RESPONSE_PROP "_MOZILLA_RESPONSE" static Atom XA_MOZILLA_VERSION = 0; static Atom XA_MOZILLA_LOCK = 0; static Atom XA_MOZILLA_COMMAND = 0; static Atom XA_MOZILLA_RESPONSE = 0; static int verbose_p; static int timeout; static void mozilla_remote_init_atoms (Display *dpy) { if (! XA_MOZILLA_VERSION) XA_MOZILLA_VERSION = XInternAtom (dpy, MOZILLA_VERSION_PROP, False); if (! XA_MOZILLA_LOCK) XA_MOZILLA_LOCK = XInternAtom (dpy, MOZILLA_LOCK_PROP, False); if (! XA_MOZILLA_COMMAND) XA_MOZILLA_COMMAND = XInternAtom (dpy, MOZILLA_COMMAND_PROP, False); if (! XA_MOZILLA_RESPONSE) XA_MOZILLA_RESPONSE = XInternAtom (dpy, MOZILLA_RESPONSE_PROP, False); } static Window mozilla_remote_find_window (Display *dpy) { int i; Window root = RootWindowOfScreen (DefaultScreenOfDisplay (dpy)); Window root2, parent, *kids; unsigned int nkids; Window result = 0; Window tenative = 0; unsigned char *tenative_version = 0; unsigned char *result_version = 0; if (! XQueryTree (dpy, root, &root2, &parent, &kids, &nkids)) { if (verbose_p) fprintf (stderr, "%s: XQueryTree failed on display %s\n", progname, DisplayString (dpy)); exit (2); } /* root != root2 is possible with virtual root WMs. */ if (! (kids && nkids)) { if (verbose_p) fprintf (stderr, "%s: root window has no children on display %s\n", progname, DisplayString (dpy)); exit (2); } 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; if (strcmp ((char *) version, expected_mozilla_version) && !tenative) { tenative = w; tenative_version = version; continue; } if (status == Success && type != None && (!tenative || strcmp ((char *) version, tenative_version))) { result = w; result_version = version; break; } XFree (version); } if (result && tenative) { if (verbose_p) fprintf (stderr, "%s: warning: both version %s (0x%x) and version\n" "\t%s (0x%x) are running. Using version %s.\n", progname, tenative_version, (unsigned int) tenative, result_version, (unsigned int) result, result_version); XFree (tenative_version); return result; } else if (tenative) { if (verbose_p > 1) fprintf (stderr, "%s: warning: expected version %s but found version\n" "\t%s (0x%x) instead.\n", progname, expected_mozilla_version, tenative_version, (unsigned int) tenative); XFree (tenative_version); return tenative; } else if (result) { return result; } else { if (verbose_p) fprintf (stderr, "%s: not running on display %s\n", progname, DisplayString (dpy)); exit (1); } } static void mozilla_remote_check_window (Display *dpy, Window window) { Atom type; int format; unsigned long nitems, bytesafter; unsigned char *version = 0; int status = XGetWindowProperty (dpy, window, XA_MOZILLA_VERSION, 0, (65536 / sizeof (long)), False, XA_STRING, &type, &format, &nitems, &bytesafter, &version); if (status != Success || !version) { if (verbose_p) fprintf (stderr, "%s: window 0x%x is not a Netscape window.\n", progname, (unsigned int) window); exit (6); } else if (strcmp ((char *) version, expected_mozilla_version)) { if (verbose_p > 1) fprintf (stderr, "%s: warning: window 0x%x is Netscape version %s;\n" "\texpected version %s.\n", progname, (unsigned int) window, version, expected_mozilla_version); } XFree (version); } static char *lock_data = 0; static void mozilla_remote_obtain_lock (Display *dpy, Window window) { Bool locked = False; Bool waited = False; if (! lock_data) { lock_data = (char *) malloc (255); sprintf (lock_data, "pid%d@", getpid ()); if (gethostname (lock_data + strlen (lock_data), 100)) { perror ("gethostname"); exit (-1); } } do { int result; Atom actual_type; int actual_format; unsigned long nitems, bytes_after; unsigned char *data = 0; XGrabServer (dpy); /* ################################# DANGER! */ result = XGetWindowProperty (dpy, window, XA_MOZILLA_LOCK, 0, (65536 / sizeof (long)), False, /* don't delete */ XA_STRING, &actual_type, &actual_format, &nitems, &bytes_after, &data); if (result != Success || actual_type == None) { /* It's not now locked - lock it. */ #ifdef DEBUG_PROPS if (verbose_p > 1) fprintf (stderr, "%s: (writing " MOZILLA_LOCK_PROP " \"%s\" to 0x%x)\n", progname, lock_data, (unsigned int) window); #endif XChangeProperty (dpy, window, XA_MOZILLA_LOCK, XA_STRING, 8, PropModeReplace, (unsigned char *) lock_data, strlen (lock_data)); locked = True; } XUngrabServer (dpy); /* ################################# danger over */ XSync (dpy, False); if (! locked) { /* We tried to grab the lock this time, and failed because someone else is holding it already. So, wait for a PropertyDelete event to come in, and try again. */ if (verbose_p) fprintf (stderr, "%s: window 0x%x is locked by %s; waiting...\n", progname, (unsigned int) window, data); waited = True; while (1) { XEvent event; XNextEvent (dpy, &event); if (event.xany.type == DestroyNotify && event.xdestroywindow.window == window) { if (verbose_p) fprintf (stderr, "%s: window 0x%x unexpectedly destroyed.\n", progname, (unsigned int) window); exit (6); } else if (event.xany.type == PropertyNotify && event.xproperty.state == PropertyDelete && event.xproperty.window == window && event.xproperty.atom == XA_MOZILLA_LOCK) { /* Ok! Someone deleted their lock, so now we can try again. */ #ifdef DEBUG_PROPS if (verbose_p > 1) fprintf (stderr, "%s: (0x%x unlocked, trying again...)\n", progname, (unsigned int) window); #endif break; } } } if (data) XFree (data); } while (! locked); if (waited && verbose_p) fprintf (stderr, "%s: obtained lock.\n", progname); } static void mozilla_remote_free_lock (Display *dpy, Window window) { int result; Atom actual_type; int actual_format; unsigned long nitems, bytes_after; unsigned char *data = 0; #ifdef DEBUG_PROPS if (verbose_p > 1) fprintf (stderr, "%s: (deleting " MOZILLA_LOCK_PROP " \"%s\" from 0x%x)\n", progname, lock_data, (unsigned int) window); #endif result = XGetWindowProperty (dpy, window, XA_MOZILLA_LOCK, 0, (65536 / sizeof (long)), True, /* atomic delete after */ XA_STRING, &actual_type, &actual_format, &nitems, &bytes_after, &data); if (result != Success) { if (verbose_p) fprintf (stderr, "%s: unable to read and delete " MOZILLA_LOCK_PROP " property\n", progname); return; } else if (!data || !*data) { if (verbose_p) fprintf (stderr, "%s: invalid data on " MOZILLA_LOCK_PROP " of window 0x%x.\n", progname, (unsigned int) window); return; } else if (strcmp ((char *) data, lock_data)) { if (verbose_p) fprintf (stderr, "%s: " MOZILLA_LOCK_PROP " was stolen! Expected \"%s\", saw \"%s\"!\n", progname, lock_data, data); return; } if (data) XFree (data); } static int mozilla_remote_command (Display *dpy, Window window, const char *command, Bool raise_p) { int result = -1; Bool done = False; char *new_command = 0; /* The -noraise option is implemented by passing a "noraise" argument to each command to which it should apply. */ if (! raise_p) { char *close; new_command = (char *) malloc (strlen (command) + 20); strcpy (new_command, command); close = strrchr (new_command, ')'); if (close) strcpy (close, ", noraise)"); else strcat (new_command, "(noraise)"); command = new_command; } #ifdef DEBUG_PROPS if (verbose_p > 1) fprintf (stderr, "%s: (writing " MOZILLA_COMMAND_PROP " \"%s\" to 0x%x)\n", progname, command, (unsigned int) window); #endif XChangeProperty (dpy, window, XA_MOZILLA_COMMAND, XA_STRING, 8, PropModeReplace, (unsigned char *) command, strlen (command)); while (!done) { XEvent event; XNextEvent (dpy, &event); if (event.xany.type == DestroyNotify && event.xdestroywindow.window == window) { /* Print to warn user...*/ if (verbose_p) fprintf (stderr, "%s: window 0x%x was destroyed.\n", progname, (unsigned int) window); result = 6; goto DONE; } else if (event.xany.type == PropertyNotify && event.xproperty.state == PropertyNewValue && event.xproperty.window == window && event.xproperty.atom == XA_MOZILLA_RESPONSE) { Atom actual_type; int actual_format; unsigned long nitems, bytes_after; unsigned char *data = 0; result = XGetWindowProperty (dpy, window, XA_MOZILLA_RESPONSE, 0, (65536 / sizeof (long)), True, /* atomic delete after */ XA_STRING, &actual_type, &actual_format, &nitems, &bytes_after, &data); #ifdef DEBUG_PROPS if (result == Success && data && *data) { if (verbose_p > 1) fprintf (stderr, "%s: (server sent " MOZILLA_RESPONSE_PROP " \"%s\" to 0x%x.)\n", progname, data, (unsigned int) window); } #endif if (result != Success) { if (verbose_p > 1) fprintf (stderr, "%s: failed reading " MOZILLA_RESPONSE_PROP " from window 0x%0x.\n", progname, (unsigned int) window); result = 6; done = True; } else if (!data || strlen((char *) data) < 5) { if (verbose_p > 1) fprintf (stderr, "%s: invalid data on " MOZILLA_RESPONSE_PROP " property of window 0x%0x.\n", progname, (unsigned int) window); result = 6; done = True; } else if (*data == '1') /* positive preliminary reply */ { if (verbose_p > 1) fprintf (stderr, "%s: %s\n", progname, data + 4); /* keep going */ done = False; } #if 1 else if (!strncmp ((char *)data, "200", 3)) /* positive completion */ { result = 0; done = True; } #endif else if (*data == '2') /* positive completion */ { if (verbose_p > 1) fprintf (stderr, "%s: %s\n", progname, data + 4); result = 0; done = True; } else if (*data == '3') /* positive intermediate reply */ { if (verbose_p > 1) fprintf (stderr, "%s: internal error: " "server wants more information? (%s)\n", progname, data); result = 3; done = True; } else if (*data == '4' || /* transient negative completion */ *data == '5') /* permanent negative completion */ { if (verbose_p > 1) fprintf (stderr, "%s: %s\n", progname, data + 4); result = (*data - '0'); done = True; } else { if (verbose_p > 1) fprintf (stderr, "%s: unrecognised " MOZILLA_RESPONSE_PROP " from window 0x%x: %s\n", progname, (unsigned int) window, data); result = 6; done = True; } if (data) XFree (data); } #ifdef DEBUG_PROPS else if (event.xany.type == PropertyNotify && event.xproperty.window == window && event.xproperty.state == PropertyDelete && event.xproperty.atom == XA_MOZILLA_COMMAND) { if (verbose_p > 1) fprintf (stderr, "%s: (server 0x%x has accepted " MOZILLA_COMMAND_PROP ".)\n", progname, (unsigned int) window); } #endif /* DEBUG_PROPS */ } DONE: if (new_command) free (new_command); return result; } int mozilla_ping (Display *dpy) { int status = 0; Window window; int raise_p = 0; const char *command = "dance_like_a_monkey"; mozilla_remote_init_atoms (dpy); window = mozilla_remote_find_window (dpy); if (verbose_p) mozilla_remote_check_window (dpy, window); XSelectInput (dpy, window, (PropertyChangeMask|StructureNotifyMask)); mozilla_remote_obtain_lock (dpy, window); status = mozilla_remote_command (dpy, window, command, raise_p); /* #### for the purposes of mozilla-ping, treat "501 unrecognized command" #### as "ok". If Mozilla got as far as telling us that there was no #### such command, that's just fine. */ if (status == 5) status = 0; /* When status = 6, it means the window has been destroyed */ /* It is invalid to free the lock when window is destroyed. */ if ( status != 6 ) mozilla_remote_free_lock (dpy, window); if (verbose_p) fprintf (stderr, "%s: %s (status %d)\n", progname, (status == 0 ? "pingable" : "unpingable"), status); return status; } static void sigalarm_handler (int sig) { if (verbose_p) fprintf (stderr, "%s: unpingable (%d second timeout!)\n", progname, timeout); exit (666); } #ifdef STANDALONE static void usage (char *badopt) { if (badopt) fprintf (stderr, "unknown option: %s\n", badopt); fprintf (stderr, "usage: %s [-dpy dpy] [-verbose] [-timeout secs]\n" "Checks whether there is a netscape running, and responsive.\n" "Exits with 0 if yes; non-zero if no.\n", progname); exit (-1); } int main (int argc, char **argv) { int i; Display *dpy; char *dpyn = getenv("DISPLAY"); progname = strrchr (argv[0], '/'); if (progname) progname++; else progname = argv[0]; verbose_p = 0; timeout = 5; for (i = 1; i < argc; i++) { if (argv[i][0] == '-' && argv[i][1] == '-') argv[i]++; if (!strcmp(argv[i], "-v") || !strcmp(argv[i], "-verbose")) verbose_p++; else if (!strcmp(argv[i], "-vv")) verbose_p += 2; else if (!strcmp(argv[i], "-vvv")) verbose_p += 3; else if (!strcmp(argv[i], "-timeout") && i < argc-1) { char c; if (1 != sscanf(argv[++i], " %d %c", &timeout, &c) || timeout < 1 || timeout > 60) usage(0); } else if ((!strcmp(argv[i], "-d") || !strcmp(argv[i], "-dpy") || !strcmp(argv[i], "-disp") || !strcmp(argv[i], "-display")) && i < argc-1) dpyn = argv[++i]; else usage(argv[i]); } dpy = XOpenDisplay (dpyn); if (! dpy) { fprintf (stderr, "%s: unable to open display \"%s\"\n", progname, dpyn); exit (-1); } signal (SIGALRM, sigalarm_handler); alarm (timeout); exit (mozilla_ping (dpy)); } #endif /* STANDALONE */