/*
Copyright 1990-2001 Sun Microsystems, Inc. All Rights Reserved.

Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions: The above copyright notice and this
permission notice shall be included in all copies or substantial
portions of the Software.


THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE OPEN GROUP OR SUN MICROSYSTEMS, INC. BE LIABLE
FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH
THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE EVEN IF
ADVISED IN ADVANCE OF THE POSSIBILITY OF SUCH DAMAGES.


Except as contained in this notice, the names of The Open Group and/or
Sun Microsystems, Inc. shall not be used in advertising or otherwise to
promote the sale, use or other dealings in this Software without prior
written authorization from The Open Group and/or Sun Microsystems,
Inc., as applicable.


X Window System is a trademark of The Open Group

OSF/1, OSF/Motif and Motif are registered trademarks, and OSF, the OSF
logo, LBX, X Window System, and Xinerama are trademarks of the Open
Group. All other trademarks and registered trademarks mentioned herein
are the property of their respective owners. No right, title or
interest in or to any trademark, service mark, logo or trade name of
Sun Microsystems, Inc. or its licensors is granted.

*/

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pwd.h>
#include <signal.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/utsname.h>
#include <locale.h>
#include <nl_types.h>
#include <X11/Intrinsic.h>
#include <X11/Xlibint.h>
#include <X11/StringDefs.h>
#include <X11/Xatom.h>

#include "watchdog.h"

#define	HTT_CLASS_NAME "Htt"

#if 0
#include "watchdog_msg.h"
#include "nls_message.h"
#include "htt_server/main.h"
/* For debugging purpose */
#include "htt_debug.h"
#endif

#ifndef PATH_MAX
#define PATH_MAX 1024
#endif

#ifndef OPENWINHOME
#define OPENWINHOME "/usr/openwin"
#endif
#define OPENWIN_MOTIF_PRELOAD_ENV "LD_PRELOAD=/usr/dt/lib/libXm.so.3"

#define IMDIR "/usr/lib/im"

static void     start_htt_server(int *, char *argv[]);
static void     start_htt_props(int *, char *argv[]);
static void     proc_exit(int);
static void     clean_up(int);
static void     parse_command(int, char **);
static char    *build_cmdline_db(Widget toplevel, char *argv[], int argc, Bool replace_mode);

static char    *htt_props_path = "htt_props";

#define  NL_HTT_SETD  3

typedef enum {
  RmServerActive = 1,
  RmServerReset = 2,
  RmServerKilled = 3
} ServerStatus;

static ServerStatus Server_Status = RmServerActive;

#if 0
static HttMessage htt_message = (HttMessage) 0;
#endif

#if 0
#define nl_msg(msg_num, msg) GetNLSMessage(htt_message, msg_num, msg)
#endif
#define nl_msg(msg_num, msg) (msg)

static int      htt_server_pid, htt_props_pid;
static Atom     htt_atom, server_atom, props_atom, resource_atom;
static Atom     cmdline_atom, htt_server_reset_atom, htt_watchdog_atom,
  class_atom;
static Display *display;
static Window   httw_id;

static Bool     spawn_props = True;	/* htt_props spawned by htt */
static char   **htt_server_arg;
static char   **htt_props_arg;
static char    *get_home_dir(char *buf);
static char    *get_host(char *buf, int max_len);
static int      htt_server_killed = 0;

typedef struct _RmDatabase {
  char *htt_server_name;
  Bool start_props;	/* This bool is used to sense
				 * htt_props running or not */
  Bool respond_to_sm;
} RmDatabase;

static RmDatabase my_rdb;

static XtResource rdb_items[] = {
  {"inputMethod", "InputMethod", XtRString, sizeof(char *),
   offsetof(RmDatabase, htt_server_name), XtRString, (XPointer)"htt_xbe"},
  {"startProps", "StartProps", XtRBool, sizeof(Bool),
   offsetof(RmDatabase, start_props), XtRString, (XPointer) "false"},
  {"respondToSM", "RespondToSM", XtRBool, sizeof(Bool),
   offsetof(RmDatabase, respond_to_sm), XtRString, (XPointer) "false"},
};

static XrmOptionDescRec rdb_options[] = {
  {"-xim", "*inputMethod", XrmoptionSepArg, NULL},
  {"-sp", "*startProps", XrmoptionNoArg, (XPointer) "true"},
  {"-so", "*startProps", XrmoptionNoArg, (XPointer) "false"},
  {"-rsm", "*respondToSM", XrmoptionNoArg, (XPointer) "true"}
};

static int      NUM_RDB_OPTIONS = XtNumber(rdb_options);

void
main(argc, argv)
     int             argc;
     char           *argv[];
{
  pid_t           grpid;
  Atom            ret_type;
  int             ret_format;
  unsigned long   nitems, bytes_left;
  char            htt_name[MAXNAMELEN];
  int             screen_no;
  XEvent          ev;
  unsigned long  *tmp;
  int             i;
  char           *locale;
  char           *cmdline_prop;
  XtAppContext    app;
  Widget          toplevel;
  Atom            htt_save_atom[3];
  int             pid = getpid();
  Atom            host_atom;
  char            hostname_prop[1024];

  setlocale(LC_ALL, "");
#if 0
  htt_message = CreateNLSMessage((char *) 0, NL_HTT_SETD);
#endif

#if 0
  LOG0("watchdog.c:starting htt");
#endif
  /*
   * Setup the signal handlers to monitor htt_server, htt_props
   * abnormal termination
   */
#ifdef SETPGRP_VOID
  grpid = setpgrp();
#else
  grpid = setpgrp(0, 0);
#endif

#ifdef SunOS
  sigset(SIGTERM, clean_up);
  sigset(SIGINT, clean_up);
  sigset(SIGCHLD, proc_exit);
#else
  signal(SIGTERM, clean_up);
  signal(SIGINT, clean_up);
  signal(SIGCHLD, proc_exit);
#endif

#if 0
  LOG0("watchdog.c:Signal handlers set");
#endif
  toplevel = XtVaAppInitialize(&app, HTT_CLASS_NAME,
			       rdb_options, NUM_RDB_OPTIONS,
			       &argc, argv, NULL,
			       NULL);
  XtGetApplicationResources(toplevel, (char *) &my_rdb,
			    rdb_items, XtNumber(rdb_items),
			    0, 0);

  my_rdb.start_props = False;
  my_rdb.respond_to_sm = False;

  /* duplicate again this time to build command line db */
  cmdline_prop = build_cmdline_db(toplevel, argv, argc, False);

  parse_command(argc, argv);

  strcpy(htt_name, HTT_WINDOW_NAME);
  if (locale = setlocale(LC_CTYPE, NULL))
    strcat(htt_name, locale);
  else {
    fprintf(stderr,
	    nl_msg(NL_HTT_LOCALE_FAIL,
		   "htt : Error - locale not supported\n")
	    );
    exit(1);
  }

  htt_server_arg = (char **) malloc(sizeof(char *) * (argc + 1));

  htt_props_arg = (char **) malloc(sizeof(char *) * (argc + 3));

  {
    /* htt_server */
    char **pargv = htt_server_arg;
    *pargv++ = my_rdb.htt_server_name;
    for (i = 1; i < argc; i++) {
      *pargv++ = argv[i];
    }
    *pargv = NULL;

    /* htt_props */
    pargv = htt_props_arg;
    *pargv++ = htt_props_path;
    for (i = 1; i < argc; i++) {
      *pargv++ = argv[i];
    }
    *pargv++ = "-lc_basiclocale";
    *pargv++ = locale;
    *pargv = NULL;
  }
  /* Obtain display */
  display = XtDisplay(toplevel);
  if (display == NULL) {
    fprintf(stderr,
	    nl_msg(NL_HTT_XOPEN_DISPLAY_FAIL,
		   "htt : Error - Unable to connect with X server\n"));
    exit(-1);
  }
  /* Create the properties in the display */
  htt_atom = XInternAtom(display, htt_name, False);
  htt_watchdog_atom = XInternAtom(display, HTT_WATCHDOG_PID, False);
  server_atom = XInternAtom(display, HTT_SERVER_PID, False);
  props_atom = XInternAtom(display, HTT_PROPS_PID, False);
  resource_atom = XInternAtom(display, HTT_RESOURCES, False);
  cmdline_atom = XInternAtom(display, HTT_CMDLINE_RESOURCES, False);
  htt_server_reset_atom = XInternAtom(display, HTT_SERVER_RESET, False);
  if (htt_atom == BadAtom)
    fprintf(stderr,
	    nl_msg(NL_ATOM_CREATE_FAIL,
		   "htt : Warning - Unable to create atom")
	    );

  /*
   * If already a version of htt is running under same locale , detect
   * it by checking SelectionOwner of HTT_ATOM
   */
  screen_no = DefaultScreen(display);
  httw_id = XGetSelectionOwner(display, htt_atom);
  if (httw_id != None) {
    fprintf(stderr,
	    nl_msg(NL_HTT_ALREADY_RUNNING,
		   "htt : Error - htt is already running...\n")
	    );
    exit(-1);
  }
  /* Create the window & make this window as owner of propery htt_atom */
  httw_id = XCreateSimpleWindow(display, RootWindow(display, screen_no),
				0, 0, 10, 10, 4, 1, 1);

  /* Register WM Protocol */
#if 0
  LOG1("watchdog.c:htt_window_id %x\n", httw_id);
#endif

  htt_save_atom[0] = XInternAtom(display, "_MOTIF_WM_MESSAGES", True);
  htt_save_atom[1] = XInternAtom(display, "WM_DELETE_WINDOW", True);
  htt_save_atom[2] = XInternAtom(display, "WM_SAVE_YOURSELF", True);

  host_atom = XInternAtom(display, "WM_CLIENT_MACHINE", True);
  get_host(hostname_prop, sizeof(hostname_prop));
  XChangeProperty(display, httw_id, host_atom, XA_STRING, 8,
		  PropModeReplace, hostname_prop, strlen(hostname_prop));

  class_atom = XInternAtom(display, "WM_CLASS", True);
  XChangeProperty(display, httw_id, class_atom, XA_STRING, 8,
		  PropModeReplace, "htt", strlen("htt"));

  XSetWMProtocols(display, httw_id, &htt_save_atom, 3);

  XSelectInput(display, httw_id, PropertyChangeMask | StructureNotifyMask);
  XSetSelectionOwner(display, htt_atom, httw_id, CurrentTime);

  XChangeProperty(display, httw_id, cmdline_atom, XA_STRING, 8,
		  PropModeReplace, cmdline_prop, strlen(cmdline_prop));
  XFlush(display);	/* I am not sure it will reach before
			 * htt_server starts so flush explicitly */

  /*
   * Set the PID property and used by htt_props to verify if it is
   * forked from htt
   */
  XChangeProperty(display, httw_id, htt_watchdog_atom, XA_WINDOW, 32,
		  PropModeReplace, &pid, sizeof(pid));

  /* fork htt_server */
  start_htt_server(&htt_server_pid, htt_server_arg);

  XChangeProperty(display, httw_id, server_atom, XA_WINDOW, 32,
		  PropModeReplace,
		  &htt_server_pid, sizeof(htt_server_pid));
  XFlush(display);


  /* fork htt_props *//* TO DO: conditional fork */
    if (my_rdb.start_props) {
      start_htt_props(&htt_props_pid, htt_props_arg);
      XChangeProperty(display, httw_id, props_atom, XA_WINDOW, 32,
		      PropModeReplace, &htt_props_pid,
		      sizeof(htt_props_pid));
      XFlush(display);
    }
    for (;;) {
      XNextEvent(display, &ev);
      switch (ev.type) {
      case PropertyNotify:
	if (ev.xproperty.window == httw_id)
	  if (ev.xproperty.atom == props_atom) {
#if 0
	    LOG2("watchdog.c:  property notify event wid = %x atom = %s\n",
		 ev.xproperty.window, XGetAtomName(display, ev.xproperty.atom));
#endif
	    XGetWindowProperty(display, httw_id,
			       props_atom, 0, 4, False, XA_WINDOW,
			       &ret_type, &ret_format,
			       &nitems, &bytes_left, &tmp);
	    if (ret_type == XA_WINDOW) {
	      if (*tmp) {
		htt_props_pid = *tmp;
		my_rdb.start_props = 1;
#if 0
		LOG1("watchdog.c:htt_props started pid=%d\n", htt_props_pid);
#endif
	      } else {
		my_rdb.start_props = 0;
		htt_props_pid = -1;
		spawn_props = False;	/* htt_props spawned by
					 * htt is killed */
#if 0
		LOG0("watchdog.c:htt_props is killed");
#endif
	      }
	    }
	  } else if (ev.xproperty.atom == server_atom) {
	    XGetWindowProperty(display, httw_id,
			       server_atom, 0, 4, False, XA_WINDOW,
			       &ret_type, &ret_format,
			       &nitems, &bytes_left, &tmp);
	    if (ret_type == XA_WINDOW)
	      if (*tmp) {
		if (htt_server_pid == (*tmp));
		else
		  fprintf(stderr,
			  nl_msg(NL_HTT_DUPLAICTED,
				 "htt : Warning - Duplicate htt_server %d\n"),
			  *tmp);
	      }
	  } else if (ev.xproperty.atom == resource_atom) {
	    cmdline_prop = build_cmdline_db(toplevel, argv, argc, False);
	    XChangeProperty(display, httw_id, cmdline_atom, XA_STRING,
			    8, PropModeReplace, cmdline_prop,
			    strlen(cmdline_prop) + 1);
	    XFlush(display);
	    XFree(cmdline_prop);
	  } else if (ev.xproperty.atom == htt_server_reset_atom) {
	    XGetWindowProperty(display, httw_id, htt_server_reset_atom,
			       0, 4,
			       False, XA_WINDOW, &ret_type, &ret_format,
			       &nitems, &bytes_left, &tmp);
	    if (*tmp) {
	      htt_server_killed = *tmp;
	      /* Kill & start the server */
	      Server_Status = RmServerReset;
	      kill(htt_server_pid, SIGTERM);
	      cmdline_prop = build_cmdline_db(toplevel, argv, argc, True);
	      XChangeProperty(display, httw_id, cmdline_atom,
			      XA_STRING, 8,
			      PropModeReplace, cmdline_prop,
			      strlen(cmdline_prop) + 1);
	      XFlush(display);
	      XFree(cmdline_prop);
	      start_htt_server(&htt_server_pid, htt_server_arg);
	      XChangeProperty(display, httw_id, server_atom,
			      XA_WINDOW,
			      32, PropModeReplace, &htt_server_pid,
			      sizeof(htt_server_pid));
	    } else {
	      /* Kill the server */
	      Server_Status = RmServerKilled;
	      kill(htt_server_pid, SIGTERM);
	    }

	  } else
#ifdef HTT_DEBUG
	    fprintf(stderr,
		    "htt:Unknown property notify event wid = %x atom = %s\n",
		    ev.xproperty.window,
		    XGetAtomName(display, ev.xproperty.atom))
#endif
	    ;
	break;
      case DestroyNotify:
	if (ev.xdestroywindow.window == httw_id) {
	  Server_Status = RmServerKilled;
	  kill(htt_server_pid, SIGTERM);
	  if (my_rdb.start_props)
	    kill(htt_props_pid, SIGTERM);
	  exit(0);
	} else {
				/* don't know what to do.... */
	}
      case ClientMessage:{

#if 0
	LOG0("watchdog.c:Client Message");
	LOG1("watchdog.c:Message type %s\n",
	     XGetAtomName(display, ev.xclient.message_type));
	LOG1("watchdog.c:format %d\n", ev.xclient.format);
	LOG1("watchdog.c:Client message data %s \n",
	     XGetAtomName(display, ev.xclient.data.l[0]));
#endif
				/* Need to tell htt_server to save its state */
	if ((ev.xclient.format == 32) && (!strncmp(XGetAtomName(display, ev.xclient.message_type), "WM_PROTOCOLS", 12))) {
	  if (ev.xclient.data.l[0] == htt_save_atom[1]) {
	    char           *res = strdup("DeleteWindow:True");
	    XChangeProperty(display, httw_id,
			    resource_atom, XA_STRING,
			    8, PropModeAppend,
			    (unsigned char *) res, strlen(res) + 1);
	    free(res);
	  }
	  if (ev.xclient.data.l[0] == htt_save_atom[2]) {
	    char           *res = strdup("SaveState:True");
	    XChangeProperty(display, httw_id,
			    resource_atom, XA_STRING,
			    8, PropModeAppend,
			    (unsigned char *) res, strlen(res) + 1);
	    free(res);
	    if (my_rdb.respond_to_sm)
	       if (!spawn_props) {
	         /* spawn_props = False means  */
	         char          **tmp_argv = (char **) XtMalloc((argc + 1) *
							  sizeof(char *));
	         int             tmp_argc;
	         for (tmp_argc = 0; tmp_argc < argc; tmp_argc++)
	           tmp_argv[tmp_argc] = argv[tmp_argc];
	         tmp_argv[tmp_argc] = (char *) XtMalloc(strlen("-so") + 1);
	         strcpy(tmp_argv[tmp_argc++], "-so");
	         XSetCommand(display, httw_id, tmp_argv, tmp_argc);
	         XtFree(tmp_argv[tmp_argc - 1]);
	         XtFree(tmp_argv);
	       } else
	         XSetCommand(display, httw_id, argv, argc);
	     else
	       XSetCommand(display, httw_id, (char **)0, 0);
	       /* Ignore WM_SAVEYOURSELF  msg */
            }
           }
	break;
      }
      }
    }
}

void 
clean_up(int unused)
{

  if (my_rdb.start_props) {
    fprintf(stderr,
	    nl_msg(NL_HTT_PROPS_STILL_RUNNING,
		   "htt : Warning - htt_props is still running use htt_props to kill this server ...\n")
	    );
#ifdef	SunOS
    sigset(SIGINT, (void (*) (int)) clean_up);
#else
    signal(SIGINT, (void (*) (int)) clean_up);
#endif
    return;
  }
  /*
   * Change server_pid to zero, signal handler sense it that is handled
   * by htt
   */
  htt_server_killed = 1;
  Server_Status = RmServerKilled;
  kill(htt_server_pid, SIGTERM);
  if (my_rdb.start_props)
    kill(htt_props_pid, SIGTERM);
  exit(1);
}

void 
proc_exit(int unused)
{
  int             pid;
  int             wstat;

  for (;;) {
    pid = wait3(&wstat, WNOHANG, (struct rusage *) NULL);
    if (pid == 0)
      return;
    else if (pid == -1)
      return;
    else if (pid == htt_server_pid) {
      if (WIFSTOPPED(wstat)) {
	fprintf(stderr,
		nl_msg(NL_HTT_SUSPENDED,
		       "htt_server has been suspended \n")
		);
      } else if (WIFEXITED(wstat)) {
	switch (Server_Status) {
	case RmServerKilled:
	  if (my_rdb.start_props)
	    kill(htt_props_pid, SIGTERM);
	  exit(1);
	  break;	/* Not so useful */
	case RmServerReset:
	  /*
	   * Don't worry abt it. Watchdog wants
	   * to reset htt_server
	   */
	  break;
	case RmServerActive:
	  fprintf(stderr, nl_msg(NL_HTT_SERVER_EXITED_UNKNOWN,
				 "htt : Warning - htt_server has been terminated without knowledge of htt , restart htt again\n"));
	  if (my_rdb.start_props)
	    kill(htt_props_pid, SIGTERM);
	  exit(0);
	  break;	/* Not so usefule */
	}
#if 0
	fprintf(stderr,
		nl_msg(NL_HTT_SERVER_DIED,
		       "htt : Warning - htt_server died and recovered\n"));
	start_htt_server(&htt_server_pid, htt_server_arg);
#endif
      } else if (WIFSIGNALED(wstat)) {
	switch (WTERMSIG(wstat)) {
	case SIGTERM:
	case SIGKILL:	/* there must be a reason */
	  if (!htt_server_killed) {
	    /*
	     * 4049423: In zh_TW.BIG5 and
	     * ja_JP.PCK, htt prints
	     * message in garbage when it
	     * exits
	     * 
	     * fprintf(stderr,
	     * nl_msg(NL_HTT_SERVER_EXITED
	     * , "htt : htt_server has
	     * been terminated\n") );
	     */
	    if (my_rdb.start_props)
	      kill(htt_props_pid, SIGTERM);
	    exit(0);
	  }
	  htt_server_killed = 0;
	  break;
	case SIGQUIT:
	case SIGILL:
#ifndef	linux
	case SIGEMT:
#endif
	case SIGSEGV:
	case SIGFPE:
	case SIGTRAP:
	case SIGBUS:
#ifndef	linux
	case SIGSYS:
#endif
	case SIGIOT:
	  fprintf(stderr,
		  nl_msg(NL_HTT_SERVER_DIED,
			 "htt : Warning - htt_server died and recovered\n")
		  );
	  start_htt_server(&htt_server_pid, htt_server_arg);
	  break;
	default:
	  fprintf(stderr,
		  nl_msg(NL_HTT_SERVER_DIED_UNKNOWN,
			 "htt : Error - htt_server died. Don't know how to recover\n")
		  );
	  if (my_rdb.start_props)
	    kill(htt_props_pid, SIGTERM);
	  exit(1);
	}
      }
    } else if ((my_rdb.start_props) && (pid == htt_props_pid)) {
      /*
       * printf("watchdog: htt_props has died, do
       * something?\n");
       */
    }
  }
}

int
check_openwin_path(char *prg, char *oppath, char *result)
{
  static char suffix[]="/bin/";
  int req;

  if (!oppath) return 0;

  req = strlen(prg) + sizeof(suffix) + 1;

  if (oppath
      && ((strlen(oppath) + sizeof (suffix) + req) < MAXNAMELEN))
    {
      strcpy(result, oppath);
      strcat(result, suffix);
      strcat(result, prg);
      if (access(result, X_OK) == 0) return 1;
    }
  return 0;
}

/* If the path in OPENWIN, return 1. */
#define OPENWIN_PATH 1
int
search_program(char *prg, char *path)
{
  char *basepath;

  if (check_openwin_path(prg, getenv("OPENWINHOME"), path))
    return OPENWIN_PATH;
  if (check_openwin_path(prg, OPENWINHOME, path))
    return OPENWIN_PATH;
    
  basepath = IMDIR;
  if (basepath
      && ((strlen(basepath) + strlen(prg) + 2) < MAXNAMELEN))
    {
      strcpy(path, basepath);
      strcat(path, "/");
      strcat(path, prg);
      if (access(path, X_OK) == 0) return 0;
    }
  return -1;
}

void 
start_htt_server(htt_server_pid, argv)
     int            *htt_server_pid;
     char           *argv[];
{
  char            pathname[MAXNAMELEN];
  extern int      errno;
  int             pid;
  int             flag;

  flag = search_program(my_rdb.htt_server_name, pathname);

  if (flag < 0)
    {
      perror("htt_server not found\n");
      return;
    }

  pid = (*htt_server_pid) = fork();
  switch (*htt_server_pid) {
  case -1:
    perror("watchdog:fork\n");
    exit(errno);
  case 0:
    /*
     * Note: Forked process sleeps for one second (roughly) in
     * order to allow htt to update PID property. It should work
     * most of the cases
     */
    sleep(1);

#ifdef SETPGRP_VOID
    setpgrp();
#else
    setpgrp(0, 0);
#endif

#ifdef SunOS
    if (flag == OPENWIN_PATH) {
      putenv(OPENWIN_MOTIF_PRELOAD_ENV);
    }
#endif
    execv(pathname, argv);
    perror("execv htt_server failed\n");
  }
  Server_Status = RmServerActive;
}

void 
start_htt_props(htt_props_pid, argv)
     int            *htt_props_pid;
     char           *argv[];
{
  char            pathname[MAXNAMELEN];
  extern int      errno;

  if (search_program(htt_props_path, pathname) < 0)
    {
      perror("htt_props not found\n");
      return;
    }
  *htt_props_pid = fork();
  switch (*htt_props_pid) {
  case -1:
    perror("watchdog:fork\n");
    exit(errno);
  case 0:
    setpgrp();
    if (!my_rdb.respond_to_sm)
      sleep(10);
    execv(pathname, argv);
    perror("execv htt_props failed\n");
  }
}


static void
usage(void) {
  printf(
	 nl_msg(NL_HTT_USAGE_1,
		"usage:\n       htt [-options ...]\n\nwhere options include:\n")
	 );
  printf(
	 nl_msg(NL_HTT_USAGE_2,
		"    -help                         print out this message\n")
	 );
  printf(
	 nl_msg(NL_HTT_USAGE_3,
		"    -display displayname          X server to contact\n")
	 );
  printf(
	 nl_msg(NL_HTT_USAGE_4,
		"    -xrm file                     additional resource file\n")
	 );
#if 0
  printf(
	 nl_msg(NL_HTT_USAGE_5,
		"    -so                           server only\n")
	 );
  printf(
	 nl_msg(NL_HTT_USAGE_5,
		"    -sp                           start htt_props as well\n")
	 );
  printf(
	 nl_msg(NL_HTT_USAGE_6,
		"    -nosm                         do not respond to session manager\n")
	 );
#endif
  printf(
	 nl_msg(NL_HTT_USAGE_7,
		"    -xim                          xim server name\n")
	 );
}

static void
parse_command(int argc, char **argv)
{
  int             i;
  for (i = 1; i < argc; i++) {
    if (!strcmp(argv[i], "-help")) {
      usage();
      exit(0);
    }
  }
}

static char*
build_cmdline_db(Widget toplevel, char *argv[], int argc, Bool replace_mode)
{
  char            filenamebuf[PATH_MAX], *filename = filenamebuf;
  static XrmDatabase cmd_db;
  FILE           *fd;
  char           *type_return[20];
  XrmValue        val_return;
  char           *lang;
  int             count;
  int             file_size;
  char           *str_return;
  static XrmQuark RmQString;

  static XrmDatabase db;
  char          **pargv;
  int             pargc;
  Bool            res_status;

  pargc = argc;
  pargv = (char **) Xmalloc(sizeof(char *) * pargc);
  for (count = 0; count < pargc; count++) {
    pargv[count] = Xmalloc(strlen(argv[count]) + 1);
    strcpy(pargv[count], argv[count]);
  }


  RmQString = XrmPermStringToQuark(XtRString);
  db = XtDatabase(XtDisplay(toplevel));
  /* Create db from command line options */
  XrmParseCommand(&cmd_db, rdb_options, NUM_RDB_OPTIONS,
		  HTT_CLASS_NAME, &pargc, pargv);

  for (count = 0; count < pargc; count++)
    XFree(pargv[count]);
  Xfree(pargv);

  /* try to get locale of the program */
  res_status = XrmGetResource(db, "htt*language",
			      "Htt*Language", type_return, &val_return);

  if (res_status && (char *) val_return.addr) {
    int             len = strlen((char *) val_return.addr);
    if (len) {
      char            lang_env[256];
      lang = Xmalloc(len + 1);
      strcpy(lang, (char *) val_return.addr);
      setlocale(LC_CTYPE, lang);
      strcpy(lang_env, "LC_CTYPE=");
      strcat(lang_env, lang);
      putenv(lang_env);
    }
  } else
    lang = setlocale(LC_CTYPE, "");

  /* Add options from ~/.httdefaults */
  get_home_dir(filename);
  strcat(filename, ".httdefaults");
  XrmCombineFileDatabase(filename, &cmd_db, False);

  /* Add options from ~/.Xlocale/<lang>/app-defaults/<HTT_CLASS_NAME> */
  get_home_dir(filename);
  strcat(filename, ".Xlocale/");
  strcat(filename, lang);
  strcat(filename, "/app-defaults/");
  strcat(filename, HTT_CLASS_NAME);
  XrmCombineFileDatabase(filename, &cmd_db, replace_mode);

  /* Generate an uniq file name */
  strcpy(filename, "/tmp/HTTXXXXXX");
  mktemp(filename);
#if 0
  LOG1("resouce file name is %s\n", filename);
#endif

  /* Store the database */
  XrmPutFileDatabase(cmd_db, filename);

  /* If there is no resource the file is empty, or it is not created */
  if (fd = fopen(filename, "r")) {
    /* File exists */
    fseek(fd, 0, SEEK_END);
    file_size = ftell(fd);

    str_return = Xmalloc(file_size + 1);
    fseek(fd, 0, SEEK_SET);
    fread(str_return, file_size, 1, fd);
    str_return[file_size] = '\0';
    fclose(fd);
    unlink(filename);
  } else {
    /* File doesn't exist so send empty resource */
    str_return = Xmalloc(1);
    *str_return = '\0';
  }
  return str_return;
}

static char*
get_home_dir(char *buf) {
  static char *ptr = NULL;
  int len;

  if (ptr == NULL) {
    uid_t uid;
    struct passwd  *pw;

    if (!(ptr = getenv("HOME"))) {
      if (ptr = getenv("USER")) {
	pw = getpwnam(ptr);
      } else {
	uid = getuid();
	pw = getpwuid(uid);
      }
      if (pw) {
	ptr = pw->pw_dir;
      } else {
	ptr = NULL;
	*buf = '\0';
      }
    }
  }
  if (ptr) {
    strcpy(buf, ptr);
  }
  len = strlen(buf);

  if (buf[len - 1] != '/') {
    buf[len] = '/';
    buf[len + 1] = '\0';
  }
  return buf;
}

static char    *
get_host(buf, max_len)
     char           *buf;
     int             max_len;
{
  int             len;
  struct utsname  name;

  uname(&name);

  len = strlen(name.nodename);

  if (len >= max_len) {
    len = max_len - 1;
  }
  strncpy(buf, name.nodename, len);
  buf[len] = '\0';

  return buf;
}
