#define	    IDENT	"1A03"
/*
** +--------------------------------------------------------------+
** |                  Paul Scherrer Institute                     |
** |                     SINQ Division				  |
** |                                                              |
** | This software may be used freely by non-profit organizations.|
** | It may be copied provided that the name of P.S.I. and of the |
** | author is included. Neither P.S.I. nor the author assume any |
** | responsibility for the use of this software outside of P.S.I.|
** +--------------------------------------------------------------+
**
** Module Name  . . . . . . . . : [...SinqHM]FOCUS_srv_main.c
**
** Author   . . . . . . . . . . : D. Maden
** Date of creation . . . . . . : Mar 2000
**	    Well, start a motor. But not a3 in powder mode in order to

** Updates:
**  1A01  7-Mar-2000 DM	    Initial version.
**---------------------------------------------------------------------------
**  FOCUS_srv_main.c is the main program of a SINQ Histogram Memory process
**  for the FOCUS TOF spectrometer at SINQ. It acts as an intermediary between
**  host software, which believes that it is talking to a single version of
**  SinqHM_srv, and 3 copies of SinqHM_srv on 3 different front-end processors,
**  each of which is histogramming data from one of the 3 detector banks on
**  FOCUS.
**
**  Communication between FOCUS_srv and the client is via TCP/IP protocol.
**
**  To compile and link this program for VxWorks on PSS123, use:
**
	    ccvx '-DINST="<instr>"' FOCUS_srv_main.c
	    ccvx '-DINST="<instr>"' FOCUS_srv_server.c
	    ccvx '-DINST="<instr>"' FOCUS_srv_routines.c
** where
	    ccvx = ccppc -O0			\
		    -mcpu=603			\
		    -I${WIND_BASE}/target/h	\
		    -fno-builtin		\
		    -fno-for-scope		\
		    -nostdinc			\
		    -DCPU=PPC603		\
		    -D_GNU_TOOL			\
		    -gdwarf -c -Wimplicit
** and
**	    <instr> = FOCUS or ...

    ldppc -o FOCUS_srv.o -r FOCUS_srv_main.o	    \
			     FOCUS_srv_routines.o  \
			     FOCUS_srv_server.o

**  and, on the target,
**
**   -> ld < /home/pss123/aco/maden/wind/SinqHM/FOCUS/NEW/FOCUS_srv.o
**   -> sp FOCUS_main, \
**	      <hostM>, <ncM>, <hostO>, <ncO>, <hostU>, <ncU>, <c_port>, <s_port>
**	         ^       ^       ^       ^       ^       ^       ^         ^
** Name of Host  |       |       |       |       |       |       |         |
** for Mittel-Bnk+       |       |       |       |       |       |         |
** # Counters in Mittel- +       |       |       |       |       |         |
**    Bank                       |       |       |       |       |         |
** Name of Host for Ober-Bank ---+       |       |       |       |         |
** # Counters in Ober-Bank --------------+       |       |       |         |
** Name of Host for Unter-Bank ------------------+       |       |         |
** # Counters in Unter-Bank -----------------------------+       |         |
** Port number of Bank Servers (dflt = 2500) --------------------+         |
** Our Port number as a Server (dflt = 2400) ------------------------------+
** e.g.
**     sp  FOCUS_main, "localhost", 150, "lnse05.vme", 117, "lnse06.vme", 116
**  or sps FOCUS_main, "localhost", 150, "lnse05.vme", 117, "lnse06.vme", 116
**====================================================================
*/
#include    "vxWorks.h"
#include    "taskLib.h"
#include    "msgQLib.h"
#include    "sockLib.h"
#include    "inetLib.h"
#include    "sysLib.h"

#include    <stdlib.h>
#include    <stdio.h>
#include    <ctype.h>
#include    <string.h>
#include    <unistd.h>
#include    <errno.h>
#include    <netinet/in.h>
#include    <arpa/inet.h>
#include    <netdb.h>
#include    <sys/types.h>
#include    <sys/socket.h>
#include    <setjmp.h>
#include    <time.h>

#include    <signal.h>		/* Get POSIX signal definitions. */
#include    <timers.h>
#include    <sys/mman.h>	/* Get POSIX mem mgmnt definitions. */
#include    <sys/wait.h>	/* Get POSIX wait definitions. */
/*
**====================	Global Definitions =====================================
*/
#include    "SinqHM_def.h"
#include    "FOCUS_gbl.h"

#define READBUFSIZE 66560
  extern    char    *vxWorksVersion;	/* Version string of vxWorks */
  extern    char    *creationDate;	/* Creation Date string of vxWorks */
  static    uchar   readBuffer[READBUFSIZE];

/*
**=============================================================================
**  Local routines.
**--------------------------------------------------------------------------
**	    FOCUS_server:  This is the Child Process Code for FOCUS_srv.
**
**		Set up TCP/IP port for operations with client, accept his
**		connection and perform services for him.
**
**		Note:	There must be NO return from this function.
*/
  int	FOCUS_server (
/*	============
*/		pid_t	 parent_pid,
		SEM_ID	 sem,
		int	 index,
		int	 pkt_size,
		int	 suspend_flag,
		int	*iret) {    /* Return a status to parent here:
				    **	0 = all OK
				    ** -1 = sigaction error.
				    ** -2 = socket or bind error with cnct_skt.
				    ** -3 = listen error with cnct_skt.
				    ** -4 = socket or bind error with clnt_skt.
				    ** -5 = failed to connect to a slave mstr.
				    ** -6 = SQHM_CNCT error response from slave.
				    ** -7 = socket or bind error with child.
				    ** -8 = failed to connect to a slave srvr.
				    */
    int			status;
    int			i, is, nbytes, bytes_to_come;
    char		recd[80];
    char		*p_nxt_byte;
    int			cnct_skt, rw_skt, clnt_skt, serve_port;
    struct sockaddr_in	rmt_sockname;
    int			rmt_sockname_len;
    char		*buff_pntr;
    int			port;

    sigset_t		proc_sigmask;
    struct sigaction	sig_action;

    struct req_buff_struct  my_req_bf;
    struct rply_buff_struct my_rply_bf;

/*============================================================================
*/
    *iret = 0;	    /* Assume initialisation will be OK */

    port = FS_port + index;

    StrJoin (FS_name[index], sizeof (FS_name[0]), taskName (get_pid ()), "");

    if (suspend_flag != 0) {
      printf ("\n%s: Suspending ..\n", FS_name[index]);
      taskSuspend (0);	    /* We've been told to suspend ourself, ..
			    **  .. presumably for debug reasons.
			    */
      printf ("\n%s: Suspension ended!\n", FS_name[index]);
    }
/*============================================================================
**	Define our signal mask first and define an action routine to catch
**	the SIGINT signal our parent will send us when he wants us to exit.
*/
    status = sigemptyset (&proc_sigmask);
    status = sigprocmask (SIG_SETMASK, &proc_sigmask, NULL);

    status = sigemptyset (&sig_action.sa_mask);
    sig_action.sa_handler = FS_catch_int_signal;
    sig_action.sa_flags   = 0;
    if (sigaction (SIGINT, &sig_action, NULL) == -1) {
      getErrno (&FS_errno);
      perror ("sigaction error:");
      *iret = -1;			/* Tell our parent we've given up */
      is = semGive (sem);		/* Let our parent continue */
      exit (KER__BAD_VALUE);
    }
/*============================================================================
**	Create a TCP/IP socket for communication, bind it and listen.
*/
    FS_chld_cnct_skt[index] = 0;
    cnct_skt = socket_bind (port, FS_name[index]);
    if (cnct_skt == -1) {
      *iret = -2;			/* Tell our parent we've given up */
      is = semGive (sem);		/* Let our parent continue */
      exit (KER__BAD_VALUE);
    }

    status = listen (cnct_skt, 1);
    if (status != 0) {
      close (cnct_skt);
      perror (StrJoin (recd, sizeof (recd),
			FS_name[index], " -- cnct_skt listen error"));
      *iret = -3;		/* Tell our parent we've given up */
      is = semGive (sem);	/* Let our parent continue */
      exit (KER__BAD_VALUE);
    }
/*============================================================================
**	    Now open client connections to each of the Slaves
*/
    my_req_bf.bigend = htonl (0x12345678);
    my_req_bf.cmnd   = htonl (SQHM_CNCT);
    my_req_bf.u.cnct.max_pkt   = htonl (pkt_size);
    my_req_bf.u.cnct.strt_mode = 0;
    for (i = 0; i < FS_n_slaves; i++) {
      if (FS_slv_active[i]) {
		/*
		** Create a socket and bind it
		*/
	clnt_skt = socket_bind (0, FS_name[index]);
	if (clnt_skt == -1) {
	  *iret = -4;		 /* Tell our parent we've given up */
	  FS_close_all (index);	 /* Shut down any client cnct'ns we've made */
	  close (cnct_skt);
	  is = semGive (sem);	 /* Let our parent continue */
	  exit (KER__BAD_VALUE);
	}
		/*
		** Make a connection to Master-server in slave
		*/
	rmt_sockname_len = sizeof (FS_rmt_socknam[0]);
	status = connect (clnt_skt, (struct sockaddr *) &FS_rmt_socknam[i],
					rmt_sockname_len);
	if (status == -1) {
	  StrJoin (recd, sizeof (recd),
			": error connecting to master in ", FS_slaves[i]);
	  perror (StrJoin (recd, sizeof (recd), FS_name[index], recd));
	  *iret = -5;		/* Tell our parent we've given up */
	  close (clnt_skt);
	  FS_close_all (index);	/* Shut down any client cnct'ns we've made */
	  close (cnct_skt);
	  is = semGive (sem);	/* Let our parent continue */
	  exit (KER__BAD_VALUE);
	}
		/*
		** Send SQHM_CNCT command to Master-server and get response
		*/
	status = rqst_send_get (clnt_skt, &my_req_bf, sizeof (my_req_bf),
			 &my_rply_bf, sizeof (my_rply_bf),
			 &FS_bytes_got, &FS_bytes_put, FS_name[index]);
	if (!status || (ntohl (my_rply_bf.status) != KER__SUCCESS)) {
	  if (ntohl (my_rply_bf.status) != KER__SUCCESS) {
	    printf (
             "\n%s: %s to %s.\n, status = %d, substatus = %d ",
                        "error response to SINQHM_CNCT",
			FS_name[index], FS_slaves[i], 
                        ntohl(my_rply_bf.status), 
                        ntohl(my_rply_bf.sub_status));
	  }
	  *iret = -6;		/* Tell our parent we've given up */
	  close (clnt_skt);
	  FS_close_all (index);	/* Shut down any client cnct'ns we've made */
	  close (cnct_skt);
	  is = semGive (sem);	/* Let our parent continue */
	  exit (KER__BAD_VALUE);
	}
	close (clnt_skt);
	serve_port = ntohl (my_rply_bf.u.cnct.port);
	FS_pkt_siz[index][i] = ntohl (my_rply_bf.u.cnct.pkt_size);
		/*
		** Create a socket for talking to our server in slave and bind
		*/
	clnt_skt = socket_bind (0, FS_name[index]);
	if (clnt_skt == -1) {
	  *iret = -7;		 /* Tell our parent we've given up */
	  FS_close_all (index);	 /* Shut down any client cnct'ns we've made */
	  close (cnct_skt);
	  is = semGive (sem);	 /* Let our parent continue */
	  exit (KER__BAD_VALUE);
	}
		/*
		** Make a connection to our server in slave
		*/
	rmt_sockname = FS_rmt_socknam[i];
	rmt_sockname.sin_port = htons (serve_port);
	rmt_sockname_len = sizeof (rmt_sockname);
	status = connect (clnt_skt, (struct sockaddr *) &rmt_sockname,
					rmt_sockname_len);
	if (status == -1) {
	  StrJoin (recd, sizeof (recd),
			": error connecting to server in ", FS_slaves[i]);
	  perror (StrJoin (recd, sizeof (recd), FS_name[index], recd));
	  *iret = -8;		/* Tell our parent we've given up */
	  close (clnt_skt);
	  FS_close_all (index);	/* Shut down any client cnct'ns we've made */
	  close (cnct_skt);
	  is = semGive (sem);	/* Let our parent continue */
	  exit (KER__BAD_VALUE);
	}
	FS_skt[index][i] = clnt_skt;
      }
    }
/*============================================================================
**	    Tell parent our cnct port is set up and waiting by releasing
**	    semaphore so that our parent can continue. The parent will then
**	    send a message to the client telling him which port he is to use.
*/
    FS_chld_cnct_skt[index] = cnct_skt;
    is = semGive (sem);
    if (is != OK) {
      kill (parent_pid, SIGUSR1);   /* Inform parent that we are in trouble */
      printf ("\n%s -- sem release error\n", FS_name[index]);
      close (FS_chld_cnct_skt[index]); FS_chld_cnct_skt[index] = 0;
      FS_close_all (index);	    /* Close connections to our servers */
      exit (KER__BAD_VALUE);
    }
/*============================================================================
**	    Wait for the client to connect to our port
*/
    rmt_sockname_len = sizeof (rmt_sockname);
    rw_skt = accept (FS_chld_cnct_skt[index],
		    (struct sockaddr *) &rmt_sockname, &rmt_sockname_len);
    if (rw_skt == -1) {
      perror (StrJoin (recd, sizeof (recd),
			FS_name[index], " -- cnct_skt accept error"));
      close (FS_chld_cnct_skt[index]); FS_chld_cnct_skt[index] = 0;
      FS_close_all (index);	    /* Close connections to our servers */
      kill (parent_pid, SIGUSR1);   /* Inform parent that we are in trouble */
      exit (KER__BAD_VALUE);
    }
    FS_chld_rw_skt[index] = rw_skt;
/*
**-------------------------------------------------------------------------
**			    Here is the main recv loop
*/
  main_loop:
/*=========
*/  bytes_to_come = sizeof (my_req_bf);	    /* Read a command */
    p_nxt_byte = (char *) &my_req_bf;
    while (bytes_to_come > 0) {
      nbytes = recv (FS_chld_rw_skt[index], p_nxt_byte, bytes_to_come, 0);
      if (nbytes <= 0) break;
      FS_bytes_got += nbytes;
      bytes_to_come -= nbytes;
      p_nxt_byte += nbytes;
    }
    if ((bytes_to_come == 0) &&
	(ntohl (my_req_bf.bigend) == 0x12345678) &&
	(ntohl (my_req_bf.cmnd) != SQHM_CLOSE)) {
      is = FS_do_command (index, FS_chld_rw_skt[index], pkt_size,
					&my_req_bf, &my_rply_bf);
      if (is) goto main_loop;	/* Break out of loop on error */
    }
/*
**			    End of the main recv loop
**-------------------------------------------------------------------------
**	Get here if we drop out of the "recv" loop.
**	This should only happen on getting a SQHM_CLOSE message or if our
**	client closes his socket without sending a SQHM_CLOSE message.
**	Anything else is a fatal error.
*/
    close (FS_chld_rw_skt[index]); FS_chld_rw_skt[index] = 0;
    close (FS_chld_cnct_skt[index]); FS_chld_cnct_skt[index] = 0;
		/*
		** Check the various error conditions
		*/
    if (bytes_to_come != 0) {	/* Was a complete request read? */
				/* No, it may be a network error, in which case
				** nbytes is -1, or nothing was read, which
				** indicates that our client simply closed
				** his connection (forgetting to send a
				** SQHM_CLOSE message), or the client has
				** screwed up the protocol! */
      if (nbytes == -1) {
						/* It's a network error */
	perror (StrJoin (recd, sizeof (recd),
			    FS_name[index], " -- rw_skt recv error"));
	status = KER__EXIT_SIGNAL;
      }else if (bytes_to_come == sizeof (my_req_bf)) {
				/* Our client simply closed his connection.
				** Treat this as normal. */
	printf ("\n%s exiting.\n", FS_name[index]);
	status = KER__SUCCESS;
      }else {
	printf ("\n%s -- rw_skt recv wrong number of bytes: %d\n",
				    FS_name[index], nbytes);
	status = KER__EXIT_SIGNAL;
      }
    }else if (ntohl (my_req_bf.bigend) != 0x12345678) {
					    /* Network byte-order wrong! */
      printf ("\n%s -- big-endian/little-endian problem!\n"
	    "    Buffer received in non-network byte order!\n", FS_name[index]);
      status = KER__EXIT_SIGNAL;
    }else if (ntohl (my_req_bf.cmnd) == SQHM_CLOSE) {
			       /* SQHM_CLOSE request received. This is normal */
      printf ("\n%s exiting.\n", FS_name[index]);
      status = KER__SUCCESS;
    }else {
					/* Unrecognised request received! */
      printf ("\n%s -- unrecognised command received: 0x%x\n",
			FS_name[index], ntohl (my_req_bf.cmnd));
      status = KER__EXIT_SIGNAL;
    }

    FS_close_all (index);	    /* Close connections to our servers */
    kill (parent_pid, SIGUSR1);	    /* Inform parent that we are exiting */
    exit (status);
  }
/*
**--------------------------------------------------------------------------
*/
  void FS_catch_signal (int signo) {
/*     ===============
**		    Action Routine for catching signals from Child Processes.
**
**		This routine is specific to FOCUS_srv.
*/
    pid_t   my_pid;

    my_pid = get_pid ();
    if (FS_dbg_lev0) printf ("\nFS_catch_signal: "
			"Signal %d received by PID 0x%x.\n", signo, my_pid);
    switch (signo) {
     case SIGUSR1:	    /* A child is about to exit. */
       FS_cnt_SIGUSR1++;
       sleep (1);	    /* Give it a chance to disappear! */
       break;
     default:
       printf ("\nFS_catch_signal: Unexpected signal received "
			    "by PID 0x%x: %d.\n", my_pid, signo);
    }
  }
/*
**--------------------------------------------------------------------------*/
  void FS_catch_int_signal (int signo) {
/*     ===================
**	    Action Routine for catching SIGINT signals. This is a signal
**	    from the parent to get its child to exit. An exit will be performed.
**
**		This routine is specific to FOCUS_srv.
*/
    int	    i, j;
    pid_t   my_pid;

    my_pid = get_pid ();
			    /*
			    ** Try to find our index by searching the PID list
			    */
    if (my_pid == FS_pids[0]) {
      ;				    /* Should not get here - there's no Filler
				    ** for FOCUS_srv! */
    }else {
      for (i = MAX_CLIENTS; i > 0; i--) if (my_pid == FS_pids[i]) break;
      if (i > 0) {
	if (FS_chld_rw_skt[i] != 0) {
	  printf ("\nClosing r/w socket of task %s\n", FS_name[i]);
	  close (FS_chld_rw_skt[i]); FS_chld_rw_skt[i] = 0;
	}
	if (FS_chld_cnct_skt[i] != 0) {
	  printf ("\nClosing connect socket of task %s\n", FS_name[i]);
	  close (FS_chld_cnct_skt[i]); FS_chld_cnct_skt[i] = 0;
	}
	for (j = 0; j < FS_n_slaves; j++) {
	  if (FS_skt[i][j] != 0) {
	    printf ("\nClosing client socket of task %s to %s\n",
			    FS_name[i], FS_slaves[i]);
	    close (FS_skt[i][j]); FS_skt[i][j] = 0;
	  }
	}
      }
    }

    if (FS_dbg_lev0) printf ("\nFS_catch_int_signal: "
			"Signal %d received by PID 0x%x.\n", signo, my_pid);
    switch (signo) {
     case SIGINT:	    /* A SIGINT signal has been received. Exit. */
       exit (KER__SUCCESS);
       break;
     default:
       printf ("\nFS_catch_int_signal: "
		 "Unexpected signal %d received by PID 0x%x.\n", signo, my_pid);
    }
  }
/*
**--------------------------------------------------------------------------
**	    FS_cleanup_children:    Find out which child(ren) have exited and
**			    update the list of task PID's so the associated
**			    task slot becomes free.
**
**		    If FILLER should be present but has fallen off, then any
**		    active children will be told to exit and we shall set our
**		    state to "deconfig".
*/
  void	FS_cleanup_children (
/*	===================
*/		int	  max_chld,	/* The size of the pids array */
		pid_t	 *pids,		/* Array of pids. Index 0 is FILLER */
		int	 *pcnt_SIGUSR1,	/* Flag set on SIGUSR1 signals whenever
					** a child exits. */
		int	  verbose,	/* If true, report what's going on */
		char	**tsk_names,
		char	 *filler_name) {

    int		i, status, n_active, do_newline = False;
    char       *t_name;
    pid_t	pid;

    if (pids[0] != 0) {
      if (verbose) printf ("\n  ... checking state of %s ...", filler_name);
      t_name = taskName (pids[0]);
      if (t_name == NULL) {
        if (verbose) printf (" it has exited.");
        pids[0] = 0;
      }else {
        if (verbose) printf (" it is still active.");
      }
      if (verbose) do_newline = True;
    }

    n_active = 0;
    for (i = 1; i <= max_chld; i++) {
      if (pids[i] != 0) {
	if (verbose) printf ("\n  ... checking state of %s ...", tsk_names[i]);
	t_name = taskName (pids[i]);
	if (t_name == NULL) {
	  if (verbose) printf (" it has exited.");
	  pids[i] = 0;
	}else {
	  if (verbose) printf (" it is still active.");
	  n_active += 1;
	}
	if (verbose) do_newline = True;
      }
    }
    if (do_newline) printf ("\n");
    if (pcnt_SIGUSR1 != NULL) *pcnt_SIGUSR1 = 0;

    if ((filler_name[0] != NIL) &&	/* Must we force a de-config? */
	(pids[0] == 0) &&
	(n_active > 0)) {
      for (;;) {			/* Loop for ever till programmed! */
	printf ("\nThe filler process, %s, has terminated abnormally.\n"
		"A de-configure must be forced\n"
		"This has not yet been programmed.\n"
		"Re-boot the histogram memory!!\n", filler_name);
	sleep (5);
      }
    }
  }
/*
**--------------------------------------------------------------------------
**	    FS_close_all:	Shut down any active client cnct'ns
*/
  int	FS_close_all (
/*	============
*/		int	  index) {

    int			    i, status;
    char		    recd[80];
    struct req_buff_struct  my_req_bf;
    struct rply_buff_struct my_rply_bf;

    my_req_bf.bigend = htonl (0x12345678);
    my_req_bf.cmnd   = htonl (SQHM_CLOSE);
    for (i = 0; i < FS_n_slaves; i++) {
      if (FS_skt[index][i] != 0) {
	status = send (FS_skt[index][i],
			(char *) &my_req_bf, sizeof (my_req_bf), 0);
	if (status == -1) {
	  perror (StrJoin (recd, sizeof (recd),
		FS_name[index], "/rqst_send_get: send error"));
	}
	close (FS_skt[index][i]);
	FS_skt[index][i] = 0;
      }
    }
  }
/*
**--------------------------------------------------------------------------
**	    FS_close_down_offspring:	Close down FILLER and, if directed,
**					other children too.
*/
  int	FS_close_down_offspring (
/*	=======================
*/		int	  harshness,
		int	  max_chld,
		pid_t	 *pids,
		int	 *exit_stat,
		int	 *pcnt_SIGUSR1,	/* Flag set on SIGUSR1 signals
					** whenever a child exits. */
		int	  verbose,	/* If true, report what's going on */
		char	**tsk_names,
		char	 *filler_name) {

    int		i, j;
    int		n_children, new_c = 0;
    char	buff[132];

    n_children = 0;		/* See if any server children are active */
    for (i = 1; i <= max_chld; i++) {
      if (pids[i] != 0) n_children += 1;
    }
    if (n_children > 0) {   /* If so, only proceed if asked to be ruthless */
      if (harshness == 0) {
	printf ("\nDe-configure error. ");
        if (n_children == 1) sprintf (buff, "1 server is");
        if (n_children != 1) sprintf (buff, "%d servers are", n_children);
	printf ("%s still active.\n", buff);
	return False;
      }else {
        if (n_children == 1) sprintf (buff, " -- 1 server is still active. It");
        if (n_children != 1) sprintf (buff, " -- %d servers are still active. "
						"They", n_children);
	printf ("\n%s will be terminated.\n", buff);
      }
    }

    FS_kill_children (max_chld, pids,
	    tsk_names, filler_name);   /* Kill FILLER and any server children */
    sleep (1);			       /* Give them chance to exit */
    FS_cleanup_children (max_chld, pids,    /* Update the PIDs */
			    pcnt_SIGUSR1, verbose, tsk_names, filler_name);

    for (j = 0; j < n_children; j++) {	    /* Check to see if all gone */
      if (pids[0] == 0) new_c = 0; else new_c = 1;  /* Count remainder */
      for (i = max_chld; i > 0; i--) {
	if (pids[i] != 0) new_c += 1;
      }
      if (new_c == 0) break;
      sleep (1);
      FS_cleanup_children (max_chld, pids,
			    pcnt_SIGUSR1, verbose, tsk_names, filler_name);
    }
    if (new_c != 0) {
      printf ("     ... failed to tidy up. %d children still active.\n", new_c);
      return False;
    }else {
      return True;
    }
  }
/*
**--------------------------------------------------------------------------
**	    FS_deconfig_all:  Deconfigure any active slaves.
**
**		This routine is specific to FOCUS_srv.
*/
  int	FS_deconfig_all (int harshness) {
/*	===============
*/
    int				i, iret, all_ok = True;
    struct req_buff_struct	rqst;
    struct rply_buff_struct	reply;

    rqst.bigend	    = htonl (0x12345678);     /* Set up the deconfig request */
    rqst.cmnd	    = htonl (SQHM_DECONFIG);
    rqst.u.decnfg.sub_code = htonl (harshness);
					/*
					** Loop sending it to active slaves
					*/
    for (i = 0; i < FS_n_slaves; i++) {
      if (FS_slv_active[i]) {
	iret = FS_send_cmnd_to_mstr (&FS_rmt_socknam[i],
		    &rqst, sizeof (rqst), &reply,
		    &FS_bytes_got, &FS_bytes_put);
	if (iret && (ntohl (reply.status) == KER__SUCCESS)) {
	  FS_slv_active[i] = False;
	}else {
	  all_ok = False;
	}
      }
    }
    return all_ok;
  }
/*
**--------------------------------------------------------------------------
**	    FS_do_command:  Action routine for an SQHM_??? command.
**			    The command is processed (including returning a
**			    status response message).
**
**		This routine is specific to FOCUS_srv.
*/
  int	FS_do_command (
/*	=============
*/		int			 index,
		int			 skt,
		int			 pkt_size,
		struct req_buff_struct	*rqst,
		struct rply_buff_struct *reply) {

    int		status, indx, is, suspendMode, i, j, sub_cmnd;
    int		txt_len, txt_offset, bytes_to_go, harshness;
    int		my_first_bin, my_nbins, my_bytes_per_bin, my_hist_no;
    int		my_sub_code, my_x_lo, my_nx, my_y_lo, my_ny, my_xdim, my_h_slct;
    time_t	secs;
    char       *msge, *p_buff, *p_txt;

    reply->bigend = htonl (0x12345678);  /* Put in the "endian" flag. */
    rply_status_setup (reply, KER__SUCCESS, 0, "All OK");

    switch (ntohl (rqst->cmnd)) {

     case SQHM_CLOSE:		     /* Close the connection. */
      return False;		     /* Do nothing. */

     case SQHM_CNCT:		     /* Make a new connection. */
      printf ("\n%s: connecting ...", FS_name[index]);

      time (&secs);
      secs = secs - FS_start_time;
      reply->u.cnct.up_time = htonl (secs);

      if (!FS_cfgn_done) {	     /* Check we've been configured */
	printf (" but not yet configured!\n");
	is = FS_rply_status_setup_and_send (skt, reply, KER__BAD_STATE, 0,
					    "Not configured", &FS_bytes_put);
      }else if (index != 0) {
	printf (" but not issued to SinqHM-master!\n");
	is = FS_rply_status_setup_and_send (skt, reply, KER__BAD_STATE, 0,
					    "Not SinqHM-master", &FS_bytes_put);
      }else {
	pkt_size = ntohl (rqst->u.cnct.max_pkt);
	if ((pkt_size < 1024) || (pkt_size > 8192)) pkt_size = 8192;
	pkt_size = pkt_size & ~3;     /* Ensure pkt_size is multiple of 4 */
	indx = FS_make_child (FOCUS_server, 100,
			ntohl (rqst->u.cnct.strt_mode), pkt_size,
			MAX_CLIENTS, FS_pids, FS_chld_exit_status,
			&FS_cnt_SIGUSR1, "FSserv");
	if (indx <= 0) {	          /* Child created and ready? */
	  printf ("\n%s: creation of child failed: %d.\n",
					FS_name[0], indx);
	  switch (indx) {
	   case -1: msge = "Spawn failed"; break;
	   case -2: msge = "No more ports"; break;
	   case -3: msge = "Time-out of child start-up"; break;
	   case -4: msge = "Child could not get started"; break;
	   default: msge = "Bad status from make_child";
	  }
	  is = FS_rply_status_setup_and_send (skt, reply, KER__BAD_CREATE,
						    indx, msge, &FS_bytes_put);
	}else {			    /* Yes, child is up and ready. */
	  reply->u.cnct.port	      = htonl (FS_port + indx);
	  reply->u.cnct.pkt_size      = htonl (pkt_size);
	  reply->u.cnct.hm_mode	      = htonl (FS_mode_ALL);
	  reply->u.cnct.n_hists	      = htonl (FS_hi_cntr - FS_lo_cntr + 1);
	  reply->u.cnct.num_bins      = htonl (FS_hi_bin - FS_lo_bin + 1);
	  reply->u.cnct.bytes_per_bin = htonl (FS_bpb);
	  reply->u.cnct.curr_hist     = htonl (0);
	  reply->u.cnct.max_block = htonl (memFindMax ());  /* Max free mem */
	  reply->u.cnct.total_bytes = htonl (FS_totl_bytes);
	  reply->u.cnct.lo_cntr = htonl (FS_lo_cntr);
	  reply->u.cnct.lo_bin = htonl (FS_lo_bin);
	  reply->u.cnct.compress = htonl (FS_compress);
	  printf (" %s started. Packet size = %d.\n", FS_name[indx], pkt_size);
	  is = FS_rply_status_send (skt, reply, &FS_bytes_put);
	}
      }
      if (!is)
	printf ("\n%s -- send of status reply failed.\n", FS_name[index]);
      return is;

     case SQHM_CONFIG:
      printf ("\n%s: Configure ..", FS_name[index]);
      if (FS_cfgn_done) {
	is = FS_rply_status_setup_and_send (skt, reply, KER__BAD_STATE, 0,
					  "Already configured", &FS_bytes_put);
	printf (" already configured!\n");
	return False;
      }

      FS_free_memory (NULL);	/* There should be no memory allocated but
				** go and release it anyway!
				*/
      is = FS_do_config (skt, rqst, reply);	/* No, do the config */
      if (!is) {
	FS_rply_status_send (skt, reply, &FS_bytes_put);
	return False;
      }else {
	FS_cfgn_done = True;
	is = FS_rply_status_send (skt, reply, &FS_bytes_put);
	if (!is)
	    printf ("\n%s -- send of status reply failed.\n", FS_name[index]);
	return is;
      }

     case SQHM_DAQ:		    /* Start or stop data acquisition.
				    ** Simply forward to our slaves. */
      printf ("\n%s: SQHM_DAQ", FS_name[index]);
      if (!FS_cfgn_done) {		      /* Configured yet? */
	FS_rply_status_setup_and_send (skt, reply, KER__BAD_STATE, 0,  /* No */
					    "Not configured", &FS_bytes_put);
	printf (" -- not configured!\n");
	return False;
      }
      sub_cmnd = ntohl (rqst->u.daq.sub_cmnd);
      if (sub_cmnd == DAQ__CLR)  printf ("/DAQ__CLR:");
      if (sub_cmnd == DAQ__GO)   printf ("/DAQ__GO:");
      if (sub_cmnd == DAQ__INH)  printf ("/DAQ__INH:");
      if (sub_cmnd == DAQ__STOP) printf ("/DAQ__STOP:");
      if (sub_cmnd == DAQ__TST)  printf ("/DAQ__TST:");

      is = FS_do_daq (index, sub_cmnd, rqst, reply);

      if (FS_dsbl_mask == 0) {
	printf (" data acquisition is active.\n");
      }else {
	printf (" data acquisition is inhibited.\n");
      }

      is = FS_rply_status_send (skt, reply, &FS_bytes_put);
      if (!is)
	    printf ("\n%s -- send of status reply failed.\n", FS_name[index]);
      return is;

     case SQHM_DBG:			     /* Set debug parameters.
					     ** Just do this locally. Do not
					     ** pass on to slaves. */
      FS_dbg_mask = ntohl (rqst->u.dbg.mask);
      FS_dbg_lev0 = ((FS_dbg_mask & 1) != 0) ? 1 : 0;
      FS_dbg_lev1 = ((FS_dbg_mask & 2) != 0) ? 1 : 0;
      FS_dbg_lev2 = ((FS_dbg_mask & 4) != 0) ? 1 : 0;
      FS_dbg_lev3 = ((FS_dbg_mask & 8) != 0) ? 1 : 0;
      printf ("\n%s: Debug mask = 0x%x\n", FS_name[index], FS_dbg_mask);
      is = FS_rply_status_setup_and_send (skt, reply, KER__SUCCESS, 0,
						NULL, &FS_bytes_put);
      if (!is)
	    printf ("\n%s -- send of status reply failed.\n", FS_name[index]);
      return is;

     case SQHM_DECONFIG:	/* Close down any servers and then deconfigure
				** our slaves */
      printf ("\n%s: De-configure", FS_name[index]);
      if (!FS_cfgn_done) {		    /* Anything to do? */
	printf (" -- already de-configured!\n");
	is = FS_rply_status_setup_and_send (skt, reply, KER__SUCCESS, 0,
						NULL, &FS_bytes_put);
      }else if (index != 0) {
	printf (" -- not issued to Master Server!\n");
	is = FS_rply_status_setup_and_send (skt, reply, KER__BAD_STATE, 0,
					    "Not Master Server", &FS_bytes_put);
      }else {
	harshness = ntohl (rqst->u.decnfg.sub_code);  /* Pick out "harshness" */
	is = FS_close_down_offspring (harshness,
		    MAX_CLIENTS, FS_pids, FS_chld_exit_status,
		    &FS_cnt_SIGUSR1, FS_dbg_lev0, FS_p_names, "");
	if (is) {
	  is = FS_deconfig_all (harshness);
	  if (is) {
	    printf (" done.\n");
	    FS_cfgn_done = False;
	    is = FS_rply_status_setup_and_send (skt, reply, KER__SUCCESS, 0,
						    NULL, &FS_bytes_put);
	  }else {
	    printf ("\nFailed! Problem deconfiguring slaves.\n");
	    is = FS_rply_status_setup_and_send (skt, reply, KER__BAD_STATE, is,
			    "Problem deconfiguring slaves", &FS_bytes_put);
	  }
	}else {
	  printf ("\nFailed! Problem terminating children.\n");
	  is = FS_rply_status_setup_and_send (skt, reply, KER__BAD_STATE, is,
			    "Problem terminating children", &FS_bytes_put);
	}
	FS_free_memory (NULL);	    /* Release all reserved memory */
      }
      if (!is)
	    printf ("\n%s -- send of status reply failed.\n", FS_name[index]);
      return is;

     case SQHM_EXIT:				    /* Rundown */
      printf ("\n%s: Run-down ...", FS_name[index]);
      if (index != 0) {
	printf (" -- not issued to Master Server!\n");
	is = FS_rply_status_setup_and_send (skt, reply, KER__BAD_STATE, 0,
				    "Not SinqHM-master", &FS_bytes_put);
      }else if (FS_cfgn_done) {
	printf (" -- not issued to SinqHM-master!\n");
	is = FS_rply_status_setup_and_send (skt, reply, KER__BAD_STATE, 0,
				    "Not SinqHM-master", &FS_bytes_put);
      }else {
	rqst->u.decnfg.sub_code = htonl (1);	    /* Set "harshness" = 1 */
	is = FS_close_down_offspring (
		    ntohl (rqst->u.decnfg.sub_code),  /* Pick out "harshness" */
		    MAX_CLIENTS, FS_pids, FS_chld_exit_status,
		    &FS_cnt_SIGUSR1, FS_dbg_lev0, FS_p_names, "");
	if (is) {
	  if (FS_cfgn_done) {
	    printf (" deconfiguring ...");
	    is = FS_deconfig_all (1);
	    if (is) {
	      printf (" done.\n");
	      FS_cfgn_done = False;
	      is = FS_rply_status_setup_and_send (skt, reply, KER__SUCCESS, 0,
						    NULL, &FS_bytes_put);
	    }else {
	      printf (" WARNING: deconfig failed.\n");
	      is = FS_rply_status_setup_and_send (skt, reply, KER__BAD_STATE,
			is, "Problem deconfiguring", &FS_bytes_put);
	    }
	  }
	}else {
	  printf (" WARNING: problem terminating children.\n");
	  is = FS_rply_status_setup_and_send (skt, reply, KER__BAD_STATE, is,
			    "Problem terminating children", &FS_bytes_put);
	}
	FS_free_memory (NULL);	    /* Release all reserved memory */
      }
      if (!is)
	    printf ("\n%s -- send of status reply failed.\n", FS_name[index]);
      return True;

     case SQHM_IDENT:		     /* Give info on software identification. */
      txt_len = IDENT_MSGE_LEN;
      txt_offset = 0;
      p_buff = calloc (txt_len, 1);	    /* Get a buffer for text info */
      if (p_buff == NULL) {
	printf ("\n%s -- SQHM_IDENT: failed to get buffer!\n",
	    FS_name[index]);
	FS_rply_status_setup_and_send (skt, reply, KER__BAD_ALLOC, 0,
	    "Failed to get buffer for composing Ident", &FS_bytes_put);
	return False;
      }
      time (&secs);
      secs = secs - FS_start_time;
      reply->u.ident.up_time = htonl (secs);
      reply->u.ident.n_extra_bytes = htonl (IDENT_MSGE_LEN);
      p_txt = p_buff;

      reply->u.ident.offset_vxWorks_ident = htons (txt_offset);
      StrJoin (p_txt, txt_len, vxWorksVersion, "");
      i = strlen (p_txt) + 1;
      txt_offset += i; p_txt += i; txt_len -= i;
      
      reply->u.ident.offset_vxWorks_date = htons (txt_offset);
      StrJoin (p_txt, txt_len, creationDate, "");
      i = strlen (p_txt) + 1;
      txt_offset += i; p_txt += i; txt_len -= i;

      reply->u.ident.offset_instr = htons (txt_offset);
      StrJoin (p_txt, txt_len, FS_inst, "");
      i = strlen (p_txt) + 1;
      txt_offset += i; p_txt += i; txt_len -= i;

      reply->u.ident.offset_def_ident = htons (txt_offset);
      StrJoin (p_txt, txt_len, FS_def_ident, "");
      i = strlen (p_txt) + 1;
      txt_offset += i; p_txt += i; txt_len -= i;

      reply->u.ident.offset_sinqhm_main_ident = htons (txt_offset);
      StrJoin (p_txt, txt_len, FS_main_ident, "");
      i = strlen (p_txt) + 1;
      txt_offset += i; p_txt += i; txt_len -= i;

      reply->u.ident.offset_sinqhm_main_date = htons (txt_offset);
      StrJoin (p_txt, txt_len, FS_main_date, "");
      i = strlen (p_txt) + 1;
      txt_offset += i; p_txt += i; txt_len -= i;

      reply->u.ident.offset_sinqhm_server_ident = htons (txt_offset);
      StrJoin (p_txt, txt_len, FS_serv_ident, "");
      i = strlen (p_txt) + 1;
      txt_offset += i; p_txt += i; txt_len -= i;

      reply->u.ident.offset_sinqhm_server_date = htons (txt_offset);
      StrJoin (p_txt, txt_len, FS_serv_date, "");
      i = strlen (p_txt) + 1;
      txt_offset += i; p_txt += i; txt_len -= i;

      reply->u.ident.offset_sinqhm_filler_ident = htons (txt_offset);
      StrJoin (p_txt, txt_len, "", "");
      i = strlen (p_txt) + 1;
      txt_offset += i; p_txt += i; txt_len -= i;

      reply->u.ident.offset_sinqhm_filler_date = htons (txt_offset);
      StrJoin (p_txt, txt_len, "", "");
      i = strlen (p_txt) + 1;
      txt_offset += i; p_txt += i; txt_len -= i;

      reply->u.ident.offset_sinqhm_routines_ident = htons (txt_offset);
      StrJoin (p_txt, txt_len, FS_rout_ident, "");
      i = strlen (p_txt) + 1;
      txt_offset += i; p_txt += i; txt_len -= i;

      reply->u.ident.offset_sinqhm_routines_date = htons (txt_offset);
      StrJoin (p_txt, txt_len, FS_rout_date, "");
      i = strlen (p_txt) + 1;
      txt_offset += i; p_txt += i; txt_len -= i;

      is = FS_rply_status_send (skt, reply, &FS_bytes_put);
		    /*
		    ** Now send the data
		    */
      p_txt = p_buff;
      bytes_to_go = IDENT_MSGE_LEN;
      while (bytes_to_go > 0) {
	i = (bytes_to_go > pkt_size) ? pkt_size : bytes_to_go;
	is = send (skt, p_txt, i, 0);
	if (is > 0) {
	  FS_bytes_put += is;
	  bytes_to_go  -= is;
	  p_txt        += is;
	}else {
	  free (p_buff);
	  printf ("\n%s -- SQHM_IDENT: failed to send extra data!\n",
				    FS_name[index]);
	  return False;		    /* Fatal error on send */
	}
      }
      free (p_buff);
      return True;

     case SQHM_PROJECT:	    /* Read a projected "rectangle" of the HM. */
      if (!FS_cfgn_done) {	     /* Check we've been configured */
	printf ("\n%s -- projecting but not yet configured!\n", FS_name[index]);
	is = FS_rply_status_setup_and_send (skt, reply, KER__BAD_STATE, 0,
					    "Not configured", &FS_bytes_put);
	return False;
      }else {
	if (FS_dbg_lev0) printf ("\n%s -- projecting ...", FS_name[index]);
	my_sub_code = ntohl (rqst->u.project.sub_code);
	my_x_lo     = ntohl (rqst->u.project.x_lo);
	my_nx       = ntohl (rqst->u.project.nx);
	my_y_lo     = ntohl (rqst->u.project.y_lo);
	my_ny       = ntohl (rqst->u.project.ny);
	my_xdim     = ntohl (rqst->u.project.xdim);
	my_h_slct   = ntohl (rqst->u.project.nhist);
	is = FS_do_project (index, reply, skt, pkt_size,
	       my_sub_code, my_x_lo, my_nx, my_y_lo, my_ny, my_xdim, my_h_slct);
	if (!is) {
	  printf ("\n%s -- SQHM_PROJECT fatal error sending status reply or "
			"histo data.\n", FS_name[index]);
	}else {
	  if (FS_dbg_lev0) printf (" done.\n");
	}
	return is;
      }

     case SQHM_READ:	/* Read a section of the HM. */
      if (!FS_cfgn_done) {	     /* Check we've been configured */
	printf ("\n%s -- reading but not yet configured!\n", FS_name[index]);
	is = FS_rply_status_setup_and_send (skt, reply, KER__BAD_STATE, 0,
					    "Not configured", &FS_bytes_put);
	return False;
      }else {
	if (FS_dbg_lev0) printf ("\n%s -- reading ...", FS_name[index]);
	my_hist_no   = ntohl (rqst->u.read.hist_no);
	my_first_bin = ntohl (rqst->u.read.first_bin);
	my_nbins     = ntohl (rqst->u.read.n_bins);
	is = FS_do_read (index, reply, skt, pkt_size,
		    my_nbins, my_first_bin, my_hist_no);
	if (!is) {
	  printf ("\n%s -- SQHM_READ fatal error sending status reply or "
			"histo data.\n", FS_name[index]);
	}else {
	  if (FS_dbg_lev0) printf (" done.\n");
	}
	return is;
      }

     case SQHM_SELECT:	    /* SQHM_SELECT still needs to be programmed. */
      printf ("\n%s: Select ...", FS_name[index]);
      is = FS_rply_status_setup_and_send (skt, reply, KER__BAD_VALUE, 0,
				    "Not yet available!", &FS_bytes_put);
      if (!is) {
	printf ("\n%s -- send of status reply failed.\n", FS_name[index]);
      }else {
	printf (" not yet implemented!\n");
      }
      return is;

     case SQHM_STATUS:			/* Get-Status request */
      if (FS_dbg_lev0) printf ("\n%s: Status ...", FS_name[index]);
		    /*
		    ** Build up info
		    */
      reply->status = htonl (KER__SUCCESS);	/* Indicate success */
      strcpy ((char *) &reply->sub_status,	/* Set up the Ident */
			    SINQHM_DEF_ID);	/* This does not need htonl! */
      reply->u.status.max_n_hists  = htonl (N_HISTS_MAX);
      reply->u.status.max_num_bins = htonl (N_BINS_MAX + 1);
      reply->u.status.max_srvrs    = MAX_CLIENTS;
      reply->u.status.max_block    = htonl (memFindMax ());  /* Max free mem */
      if (!FS_cfgn_done) {
	reply->u.status.cfg_state     = 0;
	reply->u.status.n_hists	      = 0;
	reply->u.status.curr_hist     = 0;
	reply->u.status.num_bins      = 0;
	reply->u.status.act_srvrs     = 0;
	reply->u.status.bytes_per_bin = 0;
	reply->u.status.compress      = 0;
	reply->u.status.daq_now	      = 0;
	reply->u.status.filler_mask   = 0;
	reply->u.status.tsi_status    = 0;
	reply->u.status.flags	      = 0;
	reply->u.status.dt_or_dts.both = 0;
	reply->u.status.num_bad_events = 0;
      }else {
	reply->u.status.cfg_state    = htonl (FS_mode_ALL);
	reply->u.status.n_hists      = htonl (FS_hi_cntr - FS_lo_cntr + 1);
	reply->u.status.curr_hist    = htons (0);
	reply->u.status.num_bins     = htonl (FS_hi_bin - FS_lo_bin + 1);

	for (j = 0, i = 1; i <= MAX_CLIENTS; i++) if (FS_pids[i] != 0) j++;
	reply->u.status.act_srvrs  = j;

	reply->u.status.bytes_per_bin = FS_bpb;
	reply->u.status.compress      = FS_compress;
	reply->u.status.daq_now	      = htons (FS_dsbl_mask);
	reply->u.status.filler_mask   = htons (FS_srv_masks[0]);
	reply->u.status.tsi_status    = htonl (0);
	reply->u.status.flags	      = htonl (0);
	reply->u.status.dt_or_dts.both = htonl (0);
	reply->u.status.num_bad_events = htonl (0);
      }
      time (&secs);
      secs = secs - FS_start_time;
      reply->u.status.up_time = htonl (secs);

      is = FS_rply_status_send (skt, reply, &FS_bytes_put);
      if (FS_dbg_lev0) {
	if (!is) {
	  printf ("\n%s -- send of status reply failed.\n", FS_name[index]);
	}else {
	  printf (" has been sent.\n");
	}
      }
      return is;

     case SQHM_WRITE:	/* Write a section of the HM.
			**
			** NOTE: always call FS_do_write, even if not configured
			** ====  since network data has always to be read.
			*/
      printf ("\n%s: writing ...", FS_name[index]);
      my_hist_no       = ntohl (rqst->u.write.hist_no);
      my_first_bin     = ntohl (rqst->u.write.first_bin);
      my_nbins	       = ntohl (rqst->u.write.n_bins);
      my_bytes_per_bin = ntohl (rqst->u.write.bytes_per_bin);
      is = FS_do_write (index, rqst, reply, skt, pkt_size,
			my_nbins, my_first_bin, my_bytes_per_bin, my_hist_no);
      if (!is) {
	printf ("\n%s -- send of status reply failed.\n", FS_name[index]);
      }else {
	printf (" done.\n");
      }
      return is;

     case SQHM_ZERO:
      if (!FS_cfgn_done) {	     /* Check we've been configured */
	printf ("\n%s -- zeroing but not yet configured!\n", FS_name[index]);
	is = FS_rply_status_setup_and_send (skt, reply, KER__BAD_STATE, 0,
					    "Not configured", &FS_bytes_put);
	return False;
      }else {
        printf ("\n%s -- zeroing ...", FS_name[index]);

	my_hist_no   = ntohl (rqst->u.zero.hist_no);
	my_first_bin = ntohl (rqst->u.zero.first_bin);
        my_nbins     = ntohl (rqst->u.zero.n_bins);

	FS_do_zero (reply, index, my_nbins, my_first_bin, my_hist_no);
	is = FS_rply_status_send (skt, reply, &FS_bytes_put);
	if (!is) {
	  printf ("\n%s -- send of status reply failed.\n", FS_name[index]);
	}else {
	  printf (" done.\n");
	}
	return is;
      }

     default:
      printf ("\n%s: Unrecognised command received ...\n", FS_name[index]);
      is = FS_rply_status_setup_and_send (skt, reply, KER__BAD_VALUE, 0,
				"Unrecognised command!", &FS_bytes_put);
      if (!is) {
	printf ("\n%s -- send of status reply failed.\n", FS_name[index]);
      }else {
	printf (" it has been acknowledged.\n");
      }
      return is;
    }
    printf ("\nSoftware error in do_command - switch statement completed!\n");
    return False;
  }
/*
**--------------------------------------------------------------------------
**	    FS_do_config:  ...
*/
  int	FS_do_config (
/*	============
*/		int			 skt,
		struct req_buff_struct  *request,
		struct rply_buff_struct *reply) {

    int			i, n_bytes_got, iret, return_status;
    uint		bytes_needed, n_extra, n_edges, n_banks;
    uint		sizeof_edge_arr, n_bins, flag;
    uint		lo_cntr, hi_cntr, n_cntrs, slv_lo_cntr, slv_hi_cntr;
    char		    *p_addr;
    struct tof_bank	    *p_bank;
    struct req_buff_struct  *my_rqu;
    struct rply_buff_struct  my_rply;

    FS_mode_ALL = ntohl (request->u.cnfg.mode);	/* The histogramming mode */

    FS_mode	    = FS_mode_ALL & (~SQHM__SUB_MODE_MSK);
    FS_mode_DEBUG   = FS_mode_ALL & SQHM__DEBUG;
    FS_mode_UD	    = FS_mode_ALL & SQHM__UD;
    FS_mode_BO	    = FS_mode_ALL & SQHM__BO_MSK;
    FS_mode_STROBO  = FS_mode_ALL & SQHM__STROBO;
    FS_mode_REFLECT = FS_mode_ALL & SQHM__REFLECT;
    FS_mode_NO_STAT = FS_mode_ALL & SQHM__NO_STAT;

    if (FS_mode != SQHM__TOF) {	    /* The mode must be TOF */
      printf ("\nFS_do_config: mode is not TOF\n");
      return False;
    }
	    /*
	    ** For complicated configurations, there may be more config
	    ** data to be read. If so n_extra_bytes will be non-zero.
	    */
    n_extra = ntohl (request->u.cnfg.u.tof.n_extra_bytes);
    if (n_extra != 0) {
	    /*
	    ** There is extra data to be read. Get buffer space for the
	    ** extra stuff plus space to copy over what has already been
	    ** read so that everything is in a contiguous buffer.
	    */
      bytes_needed = n_extra + sizeof (*request);
      bytes_needed = (bytes_needed + 3) & (~3);	/* Round up to multiple of 4 */
      p_addr = malloc (bytes_needed);
      if (p_addr == NULL) {
	printf ("\nFS_do_config:"
	    " cannot allocate memory for extra config data!\n");
	net_flush (skt, n_extra);
	rply_status_setup (reply, KER__BAD_ALLOC, 0,
	    "Failed to get buffer for extra config data");
	return False;
      }
      my_rqu = (struct req_buff_struct *) p_addr;
      memcpy (p_addr, request, sizeof (*request));  /* Copy in what has already
						    ** been read. */
      bytes_needed = n_extra;		/* And read the rest */
      p_addr += sizeof (*request);
      while (bytes_needed > 0) {
	n_bytes_got = recv (skt, p_addr, bytes_needed, 0);
	if (n_bytes_got <= 0) break;
	FS_bytes_got += n_bytes_got;
	bytes_needed -= n_bytes_got;
	p_addr += n_bytes_got;
      }
      if (n_bytes_got < 0) {
	printf ("\n"
	    "%s -- R/W-Socket recv error\n"
	    "%s -- Connection will be closed.\n", FS_name[0], FS_name[0]);
	rply_status_setup (reply, KER__BAD_VALUE, 0, "recv failed");
	free (my_rqu);
	return False;
      }
    }else {
      my_rqu = request;
    }

    n_bins = ntohl (my_rqu->u.cnfg.u.tof.edge_0.n_bins);
    flag   = ntohl (my_rqu->u.cnfg.u.tof.edge_0.flag);
    if ((flag & FLAG__VAR_BIN) != 0) {
      sizeof_edge_arr = OffsetOf (struct tof_edge_arr, edges) +
			((n_bins + 1) * sizeof (uint));
      FS_compress = 0;
    }else {
      sizeof_edge_arr = sizeof (struct tof_edge_arr);
      FS_compress = ntohl (my_rqu->u.cnfg.u.tof.edge_0.edges[1]) -
			ntohl (my_rqu->u.cnfg.u.tof.edge_0.edges[0]);
    }
    p_bank = (struct tof_bank *) (
		(char *) &my_rqu->u.cnfg.u.tof.edge_0 + sizeof_edge_arr);


    n_edges = ntohs (my_rqu->u.cnfg.u.tof.n_edges);
    n_banks = ntohs (my_rqu->u.cnfg.u.tof.n_banks);
    if ((n_edges != 1) || (n_banks != 1)) {
      printf ("\nFS_do_config: configuration is too complex.\n");
      return False;
    }
    lo_cntr = ntohl (p_bank->first);
    n_cntrs = ntohl (p_bank->n_cntrs);
    hi_cntr = lo_cntr + n_cntrs -1;
		    /*
		    ** Now loop sending the configuration request to
		    ** all slaves.
		    */
    return_status = True;		    /* Assume all will be OK */
    for (i = 0; i < FS_n_slaves; i++) {
      if (FS_slaves[i] != NULL) {
	slv_lo_cntr = (FS_lo_det[i] > lo_cntr) ? FS_lo_det[i] : lo_cntr;
	slv_hi_cntr = (FS_hi_det[i] < hi_cntr) ? FS_hi_det[i] : hi_cntr;
	if (slv_hi_cntr >= slv_lo_cntr) {
	  FS_slv_active[i] = True;
	  p_bank->first	  = htonl (slv_lo_cntr - FS_lo_det[i]);
	  p_bank->n_cntrs = htonl (slv_hi_cntr - slv_lo_cntr + 1);
	  iret = FS_send_cmnd_to_mstr (&FS_rmt_socknam[i],
		my_rqu, (sizeof (*request) + n_extra),
		&my_rply, &FS_bytes_got, &FS_bytes_put);
	  if (iret && (ntohl (my_rply.status) != KER__SUCCESS)) {
	    reply->status     = my_rply.status;
	    reply->sub_status = my_rply.sub_status;
	    StrJoin (reply->u.message, sizeof (reply->u.message),
				FS_slaves[i], ": ");
	    StrJoin (reply->u.message, sizeof (reply->u.message),
				reply->u.message, my_rply.u.message);
	    FS_slv_active[i] = False;
	    return_status = False;
	    break;
	  }
	}else {
	  FS_slv_active[i] = False;
	}
      }
    }

    if (!return_status) {   /* If any error, force deconfig */
      FS_deconfig_all (1);
      return return_status;
    }

    FS_lo_cntr = lo_cntr;
    FS_hi_cntr = hi_cntr;
    FS_lo_bin = 0;
    FS_hi_bin = n_bins - 1;
    FS_bpb = ntohl (p_bank->bytes_per_bin);
    FS_totl_bytes = FS_bpb * (FS_hi_cntr - FS_lo_cntr + 1) *
				(FS_hi_bin - FS_lo_bin + 1);

    if (n_extra != 0) free (my_rqu);
    return return_status;
  }
/*
**--------------------------------------------------------------------------
**	    FS_do_daq:  Handle an SQHM_DAQ command.
*/
  int	FS_do_daq (
/*	=========
*/		int			 indx,
		int			 sub_cmnd,
		struct req_buff_struct  *request,
		struct rply_buff_struct *reply) {

    int		i, iret, all_ok = True;

    if (sub_cmnd == DAQ__TST) {	    /* DAQ__TST is special - we just give the ..
				    ** .. caller a snapshot of our status ..
				    ** .. without invoking our slaves. */
      reply->status = htonl (KER__SUCCESS);
      reply->u.daq.daq_was = htons (FS_dsbl_mask);
      reply->u.daq.filler_mask = htons (FS_srv_masks[0]);
      reply->u.daq.server_mask = htons (FS_srv_masks[indx]);
      reply->u.daq.daq_now = reply->u.daq.daq_was;
    }else {
		/*
		** Claim the sem to avoid race conditions with other threads
		*/
      if (FS_use_sem_tmo) {
	iret = semTake (FS_sem_daq, FS_sem_tmo);
      }else {
	iret = semTake (FS_sem_daq, WAIT_FOREVER);
      }
			/*
			** Loop sending the SQHM_DAQ command to active slaves
			*/
      request->bigend	      = htonl (0x12345678);
      request->cmnd	      = htonl (SQHM_DAQ);
      request->u.daq.sub_cmnd = htonl (sub_cmnd);
      for (i = 0; i < FS_n_slaves; i++) {
	if (FS_slv_active[i]) {
	  if (indx == 0) {	/* If we are the master-server, send it to the
				** master-servers in the slaves. */
	    iret = FS_send_cmnd_to_mstr (&FS_rmt_socknam[i],
		    request, sizeof (*request), reply,
		    &FS_bytes_got, &FS_bytes_put);
	    if (!iret || (ntohl (reply->status) != KER__SUCCESS)) all_ok=False;
	  }else {
	    iret = rqst_send_get (FS_skt[indx][i], request, sizeof (*request),
			 reply, sizeof (*reply),
			 &FS_bytes_got, &FS_bytes_put, FS_name[indx]);
	    if (!iret || (ntohl (reply->status) != KER__SUCCESS)) all_ok=False;
	  }
	}
      }
      if (!all_ok) {		/* If error, go no further! */
	semGive (FS_sem_daq);
	return all_ok;
      }
      reply->status = htonl (KER__SUCCESS);
      reply->u.daq.daq_was = htons (FS_dsbl_mask);
      reply->u.daq.filler_mask = htons (FS_srv_masks[0]);
      reply->u.daq.server_mask = htons (FS_srv_masks[indx]);
      switch (sub_cmnd) {
       case DAQ__CLR:
	FS_dsbl_mask &= (~FS_srv_masks[indx]);  /* Clear user's mask bit. */
	break;
       case DAQ__GO:
	FS_dsbl_mask &= (~FS_srv_masks[0]);	/* Clear "filler's" mask bit. */
	break;
       case DAQ__INH:
	FS_dsbl_mask |= FS_srv_masks[indx];	    /* Set user's mask bit */
	break;
       case DAQ__STOP:
	FS_dsbl_mask |= FS_srv_masks[0];	/* Set "filler's" mask bit. */
      }
      reply->u.daq.daq_now = htons (FS_dsbl_mask);  /* Complete the status
						    ** response message */
      iret = semGive (FS_sem_daq);	    /* Release the semaphore */
    }
    return all_ok;
  }
/*
**--------------------------------------------------------------------------
**	    FS_do_project:  Read a "rectangular" region of Hist Mem and send it
**			    to the client.
*/
  int	FS_do_project (
/*	=============
*/		int			index,
		struct rply_buff_struct *rply_bf,
		int			rw_skt,
		int			pkt_size,
		uint			sub_code,
		uint			x_lo,
		uint			nx,
		uint			y_lo,
		uint			ny,
		uint			xdim,
		uint			h_slct) {
/*
**                  Read a "rectangular" region of Hist Mem and send it
**		    to the client.
*/
    uint		    my_bin_lo,  my_bin_hi;
    uint		    my_cntr_lo, my_cntr_hi;
    int			    i, is, nput, my_daq_save;
    uint		    my_nbins, bins_to_go, bytes_in_buff, bytes_to_go;
    uchar		   *buff_pntr;
    char		    recd[132], *nxt;
    struct req_buff_struct  my_rqst;
    struct rply_buff_struct my_rply;

    register union {
      void	    *base;
      uchar	    *ch;
      usint	    *i2;
      uint	    *i4;
    } my_pntr0, my_pntr1;

    rply_status_setup (rply_bf, KER__BAD_VALUE, 0, "Bad argument"); /* Assume ..
						** .. we shall find a bad arg */
/*--------------------------------------------------------------------
**	    There's no need to test the hist mode; it must be SQHM_TOF because
**	    SQHM_CONFIG rejects anything else. So, start by checking arguments.
*/
    if ((sub_code & PROJECT__1_DIM) != 0) {
      printf ("\n%s -- SQHM_PROJECT: SQHM__TOF+PROJECT__1_DIM not yet "
			"implemented!\n", FS_name[index]);
      is = FS_rply_status_setup_and_send (rw_skt, rply_bf, KER__BAD_VALUE, 0,
			    "Not yet implemented", &FS_bytes_put);
      return OK;
    }
				/* Check the arguments */
				/* Check that counter numbers are in range */
    my_cntr_lo = y_lo;
    if ((my_cntr_lo < FS_lo_cntr) ||
	(my_cntr_lo > FS_hi_cntr)) {
      printf ("\n%s -- SQHM_PROJECT: Bad y_lo!\n", FS_name[index]);
      is = FS_rply_status_send (rw_skt, rply_bf, &FS_bytes_put);
      return OK;
    }
    my_cntr_hi = (ny != 0) ? (y_lo + ny - 1) : FS_hi_cntr;
    if ((my_cntr_hi < my_cntr_lo) || (my_cntr_hi > FS_hi_cntr)) {
      printf ("\n%s -- SQHM_PROJECT: Bad ny!\n", FS_name[index]);
      is = FS_rply_status_send (rw_skt, rply_bf, &FS_bytes_put);
      return OK;
    }
				/* Check that bin numbers are in range */
    my_bin_lo = x_lo;
    if ((my_bin_lo < FS_lo_bin) ||
	(my_bin_lo > FS_hi_bin)) {
      printf ("\n%s -- SQHM_PROJECT: Bad x_lo!\n", FS_name[index]);
      is = FS_rply_status_send (rw_skt, rply_bf, &FS_bytes_put);
      return OK;
    }
    my_bin_hi = (nx != 0) ?
		    (x_lo + nx - 1) : FS_hi_bin;
    if ((my_bin_hi < my_bin_lo) ||
	(my_bin_hi > FS_hi_bin)) {
      printf ("\n%s -- SQHM_PROJECT: Bad nx!\n", FS_name[index]);
      is = FS_rply_status_send (rw_skt, rply_bf, &FS_bytes_put);
      return OK;
    }
		    /*
		    ** All the arguments have been checked. Reserve some space
		    ** for projecting the data. The amount of space that we
		    ** need for the projection depends on whether the
		    ** projection is onto the x-axis or y-axis.
		    */
    my_nbins = ((sub_code & PROJECT__ON_Y) == 0) ?
		    (my_bin_hi - my_bin_lo + 1) : (my_cntr_hi - my_cntr_lo + 1);

    my_pntr0.base = calloc (my_nbins, sizeof (uint));
    my_pntr1.base = calloc (my_nbins, sizeof (uint));
    if ((my_pntr0.base == NULL) || (my_pntr1.base == NULL)) {
      printf ("\n%s -- SQHM_PROJECT: failed to get buffer!\n",
				FS_name[index]);
      FS_rply_status_setup_and_send (rw_skt, rply_bf, KER__BAD_ALLOC, 0,
	    "Failed to get buffer for projecting data", &FS_bytes_put);
      return OK;
      free (my_pntr1.base);
    }
		    /*-----------------------------------------------------
		    ** We got some buffer space so inhibit data acqu ...
		    */
    my_daq_save = FS_dsbl_mask & FS_srv_masks[index]; /* Remember the ..
			** .. state of our inhibit bit and disable data acq
			** .. if not already disabled by us. This is necessary
			** .. to prevent data "skewing" as we read out our
			** .. 3 slaves. */
    if (my_daq_save == 0) FS_do_daq (index, DAQ__INH, &my_rqst, &my_rply);
		    /*-----------------------------------------------------
		    ** ... then do the projection ...
		    */
    bins_to_go = FS_project_hm (index, pkt_size,
		    my_pntr0.base, my_pntr1.base, my_nbins,
		    sub_code, my_cntr_lo, my_cntr_hi, my_bin_lo, my_bin_hi);
    if (bins_to_go <= 0) {
      if (my_daq_save == 0) FS_do_daq (index, DAQ__CLR, &my_rqst, &my_rply);
      perror (StrJoin (recd, sizeof (recd),
		    FS_name[index], "/FS_do_project: FS_project_hm error"));
      free (my_pntr0.base);
      free (my_pntr1.base);
      return False;
    }
		    /*-----------------------------------------------------
		    ** ... projection complete so let Filler continue and
		    **     send data back to client.
		    */
    if (my_daq_save == 0) FS_do_daq (index, DAQ__CLR, &my_rqst, &my_rply);

    rply_bf->u.project.n_bins	     = htonl (my_nbins);
    rply_bf->u.project.bytes_per_bin = htonl (sizeof (uint));
    rply_bf->u.project.cnts_lo	     = htonl (0);
    rply_bf->u.project.cnts_hi	     = htonl (0);

    if (rply_bf->bigend != 0x12345678) {    /* If byte swapping needed, ...
					    ** .. then swap them! */
      for (i = 0; i < my_nbins; i++) my_pntr0.i4[i] = htonl (my_pntr0.i4[i]);
    }

    is = FS_rply_status_setup_and_send (rw_skt, rply_bf, KER__SUCCESS, 0,
		    NULL, &FS_bytes_put);

    bytes_to_go = my_nbins * sizeof (uint);
    buff_pntr = (uchar *) my_pntr0.base;

    while (bytes_to_go > 0) {
      nput = (bytes_to_go > pkt_size) ? pkt_size : bytes_to_go;
      is = send (rw_skt, buff_pntr, nput, 0);
      if (is > 0) {
	FS_bytes_put += is;
	bytes_to_go  -= is;
	buff_pntr    += is;
      }else {
	free (my_pntr0.base);
        free (my_pntr1.base);
	return ERROR;	    /* Fatal error on send */
      }
    }
    free (my_pntr0.base);	    /* Release our bit of memory */
    free (my_pntr1.base);
    return True;
  }
/*
**--------------------------------------------------------------------------
**	    FS_do_read:	    Read a consecutive region of 'nbins' bins of the
**			    Hist Mem starting with bin 'first' and send it to
**			    the client.
*/
  int	FS_do_read (
/*	==========
*/		int			index,
		struct rply_buff_struct *rply_bf,
		int			rw_skt,
		int			pkt_size,
		uint			nbins,
		uint			first,
		uint			hist_no) {

    register int	    i, my_fstbin, my_topbin;
    int			    is, status, bytes_to_go, nput, nxtbin, nb;
    int			    my_daq_save;
    uint		    maxbins, my_nbins, bins_in_buff;
    uint		    nxtbin_to_send, nbins_to_send, bytes_in_buff;
    uint		    bins_to_go, lastbin;
    struct req_buff_struct  my_rqst;
    struct rply_buff_struct my_rply;
    uchar		    *buff_pntr, *last_byte, *nxt;
    uchar		    buff[pkt_size];

    register union {
      void	    *base;
      uchar	    *ch;
      usint	    *i2;
      uint	    *i4;
    } my_pntr, hm_pntr;
/*--------------------------------------------------------------------
**	    There's no need to test the hist mode; it must be SQHM_TOF because
**	    SQHM_CONFIG rejects anything else. So, start by checking arguments.
*/
    maxbins = (FS_hi_cntr - FS_lo_cntr + 1) * (FS_hi_bin - FS_lo_bin + 1);
/***************************************************************/
/***************************************************************/
    if ((hist_no == -1) || (hist_no == 0)) {
		/* Regard the histograms as 1 long array */
      if ((first == -1) && (nbins == -1)) {
		/* The user just wants to read everything */
	my_fstbin = 0;
	my_nbins  = maxbins;
      }else {
		/* The user wants a part of the histogram data */
	if (first >= maxbins) {
	  if (FS_dbg_lev0) printf (
	       "\n%s -- SQHM_READ: bad first_bin %d!\n", FS_name[index], first);
	  is = FS_rply_status_setup_and_send (rw_skt, rply_bf, KER__BAD_VALUE,
			    0, "\"first_bin\" too big", &FS_bytes_put);
	  return is;
	}else if ((nbins > maxbins) || ((first + nbins) > maxbins)) {
	  if (FS_dbg_lev0) printf (
		"\n%s -- SQHM_READ: bad n_bins %d!\n", FS_name[index], nbins);
	  is = FS_rply_status_setup_and_send (rw_skt, rply_bf, KER__BAD_VALUE,
			    0, "\"n_bins\" too big", &FS_bytes_put);
	  return is;
	}
	my_fstbin = first;
	my_nbins  = nbins;
      }
    }else {
		/* The user wants a part of the histogram of a single counter */
      if ((hist_no < FS_lo_cntr) || (hist_no > FS_hi_cntr)) {
	if (FS_dbg_lev0) printf (
		"\n%s -- SQHM_READ: bad hist_no!\n", FS_name[index]);
	is = FS_rply_status_setup_and_send (rw_skt, rply_bf, KER__BAD_VALUE,
			    0, "\"hist_no\" out of range", &FS_bytes_put);
	return is;
      }else if ((first < FS_lo_bin) || (first > FS_hi_bin)) {
	if (FS_dbg_lev0) printf (
		"\n%s -- SQHM_READ: bad first_bin!\n", FS_name[index]);
	is = FS_rply_status_setup_and_send (rw_skt, rply_bf, KER__BAD_VALUE,
			    0, "\"first_bin\" out of range", &FS_bytes_put);
	return is;
      }else if ((nbins > (FS_hi_bin - FS_lo_bin + 1)) ||
		((first + nbins) > (FS_hi_bin + 1))) {
	if (FS_dbg_lev0) printf (
		"\n%s -- SQHM_READ: bad n_bins!\n", FS_name[index]);
	is = FS_rply_status_setup_and_send (rw_skt, rply_bf, KER__BAD_VALUE,
			    0, "\"n_bins\" out of range", &FS_bytes_put);
	return is;
      }
      my_fstbin = (hist_no - FS_lo_cntr) * (FS_hi_bin - FS_lo_bin + 1) + first;
      my_nbins  = nbins;
    }
/***************************************************************/
		    /*
		    ** The args are OK.
		    ** Set up fixed buffer to do transfer
		    */
    my_pntr.base = NULL;
    my_pntr.base = readBuffer;
    bins_in_buff = READBUFSIZE/FS_bpb;

    if (my_pntr.base == NULL ) {
      if (FS_dbg_lev0) printf (
		"\n%s -- SQHM_READ: no buffer space available!\n",
					    FS_name[index]);
      is = FS_rply_status_setup_and_send (rw_skt, rply_bf, KER__BAD_VALUE,
			    0, "No buffer space", &FS_bytes_put);
      return is;
    }
		    /*
		    ** The args are OK and we have some buffer space.
		    ** Tell client he's about to get some data!
		    */
    rply_bf->u.read.first_bin	    = htonl (my_fstbin);
    rply_bf->u.read.n_bins	    = htonl (my_nbins);
    rply_bf->u.read.bytes_per_bin   = htonl (FS_bpb);
    rply_bf->u.read.cnts_lo	    = 0;    /* \  cnts_lo and cnts_hi not */
    rply_bf->u.read.cnts_hi	    = 0;    /* /  yet implemented	  */
    is = FS_rply_status_setup_and_send (rw_skt, rply_bf, KER__SUCCESS,
				    0, NULL, &FS_bytes_put);

    my_daq_save = FS_dsbl_mask & FS_srv_masks[index]; /* Remember the ..
			** .. state of our inhibit bit and disable data acq
			** .. if not already disabled by us. This is necessary
			** .. to prevent data "skewing" as we read out our
			** .. 3 slaves. */
    if (my_daq_save == 0) FS_do_daq (index, DAQ__INH, &my_rqst, &my_rply);
    nxtbin_to_send = my_fstbin;
    nbins_to_send = my_nbins;
    bytes_in_buff = 0;
    nxt = (char *) my_pntr.base;

    while (nbins_to_send > 0) {
				    /* Read some histogram data into buffer */
      lastbin = nxtbin_to_send + bins_in_buff;
      if(lastbin > my_nbins-1){
	lastbin = my_nbins-1;
      }
      bins_to_go = FS_read_hm (index, pkt_size,
				my_pntr.base, bins_in_buff,
				nxtbin_to_send, lastbin);
      if (bins_to_go <= 0) {
	perror (StrJoin (buff, pkt_size,
		    FS_name[index], "/FS_do_read: FS_read_hm error"));
	free (my_pntr.base);
	if (my_daq_save == 0) FS_do_daq (index, DAQ__CLR, &my_rqst, &my_rply);
	return False;
      }
      nxt = (char *) my_pntr.base;
      bytes_in_buff = bins_to_go * FS_bpb;
      while (bytes_in_buff > 0) {	    /* Send data to user */
	bytes_to_go = (bytes_in_buff > pkt_size) ? pkt_size : bytes_in_buff;
	is = send (rw_skt, nxt, bytes_to_go, 0);
	if (is <= 0) {
	  perror (StrJoin (buff, pkt_size,
		    FS_name[index], "/FS_do_read: send error"));
	  free (my_pntr.base);
	  if (my_daq_save == 0) FS_do_daq (index, DAQ__CLR, &my_rqst, &my_rply);
	  return False;
	}
	FS_bytes_put += bytes_to_go;
	bytes_in_buff -= bytes_to_go;
	nxt += bytes_to_go;
      }
      nxtbin_to_send += bins_to_go;
      nbins_to_send  -= bins_to_go;
    }

		    /*
		    ** Read-out complete so let DAQ continue.
		    */
    if (my_daq_save == 0) FS_do_daq (index, DAQ__CLR, &my_rqst, &my_rply);
    return True;
  }
/*
**--------------------------------------------------------------------------
**	    FS_do_write:    Preset a consecutive region of the Hist Mem. The
**			    number of bins to preset is 'nbins' starting with
**			    bin 'first'. The data for the bins is read from
**			    the client.
*/
  int	FS_do_write (
/*	===========
*/		int			index,
		struct req_buff_struct	*req_bf,
		struct rply_buff_struct *rply_bf,
		int			rw_skt,
		int			pkt_size,
		int			nbins,
		int			first,
		int			bytes_per_bin,
		int			hist_no) {
    return True;
  }
/*
**--------------------------------------------------------------------------
**	    FS_do_zero:	    Action routine for SQHM_ZERO command.
 *
 * This is only partially functional. The definition for SQHM_ZERO states
 * that it is possible to zero only parts of the histogram. The version
 * below just forwards a full zero request to all slaves and thus fits
 * the bill for FOCUS. Probably for any other instrument as well.
 *  
 * Mark Koennecke, November 2000 
*/
  int	FS_do_zero (
/*	===========
*/		struct rply_buff_struct *reply,
		int			indx,
		int			nbins,
		int			first_bin,
		int			hist_no) {

    int		i, iret, all_ok = True;
    struct req_buff_struct request;

    /*
      set up the request structure which zeros the whole histogram
    */
    request.bigend = htonl(0x12345678);
    request.cmnd   = htonl(SQHM_ZERO);
    request.u.zero.hist_no = htonl(-1);
    request.u.zero.first_bin = htonl(-1);
    request.u.zero.n_bins = htonl(-1);

		/*
		** Claim the sem to avoid race conditions with other threads
		*/
    if (FS_use_sem_tmo) {
	iret = semTake (FS_sem_daq, FS_sem_tmo);
    }else {
	iret = semTake (FS_sem_daq, WAIT_FOREVER);
    }

    /*
      send the request to all known slaves 
    */
      for (i = 0; i < FS_n_slaves; i++) {
	if (FS_slv_active[i]) {
	  if (indx == 0) {	/* If we are the master-server, send it to the
				** master-servers in the slaves. */
	    iret = FS_send_cmnd_to_mstr (&FS_rmt_socknam[i],
		    &request, sizeof (request), reply,
		    &FS_bytes_got, &FS_bytes_put);
	    if (!iret || (ntohl (reply->status) != KER__SUCCESS)) all_ok=False;
	  }else {
	    iret = rqst_send_get (FS_skt[indx][i], &request, sizeof (request),
			 reply, sizeof (*reply),
			 &FS_bytes_got, &FS_bytes_put, FS_name[indx]);
	    if (!iret || (ntohl (reply->status) != KER__SUCCESS)) all_ok=False;
	  }
	}
      }
      if (!all_ok) {		/* If error, go no further! */
        printf("Error zeroing histogram no %d\n", i);
	semGive (FS_sem_daq);
	return all_ok;
      }

      /*
       tell the world about our success 
      */
      reply->status = htonl (KER__SUCCESS);

      /* release semaphore */
      semGive (FS_sem_daq);


    return True;
  }
/*
**--------------------------------------------------------------------------
**	    FS_free_memory: Free any memory allocated for FOCUS_srv
*/
  void	FS_free_memory (
/*	==============
*/		    void *extras) {

    int	    i;
			/*
			** Release a possible pending buffer
			*/
    free (extras);
			/*
			** Release any allocated histogram buffer
			*/
    free (FS_base_addr);
    FS_base_addr = NULL;
  }
/*
**--------------------------------------------------------------------------
**	    FS_kill_children:	Send signals to all our offspring to cause
**				them to terminate.
*/
  void	FS_kill_children (
/*	================
*/		    int	     max_chld,
		    pid_t   *pids,
		    char   **tsk_names,
		    char    *filler_name) {

    int	    i, is;

    if (pids[0] != 0) {
      printf (" .. closing down %s ..", filler_name);
      is = kill (pids[0], SIGINT);
      if (!is) printf ("\nBad status killing %s.\n", filler_name);
    }
    for (i = 1; i <= max_chld; i++) {
      if (pids[i] != 0) {
        printf ("\n    Terminating %s ..", tsk_names[i]);
        is = kill (pids[i], SIGINT);
	if (is == ERROR) printf ("\nBad status killing %s.\n", tsk_names[i]);
      }
    }
  }
/*
**--------------------------------------------------------------------------
**	    FS_make_child:  Find a free child index, create a child process and
**		    invoke procedure proc in the child procedure.
**		    Return value >= 0 if child successfully created;
**				 = -1 if error returned by fork;
**				 = -2 if there are too many children already;
**				 = -3 if time-out waiting for child to signal.
**				 < -3 the child indicated a start-up error. The
**					child's error code is the return
**					value + 3.
*/
  int	FS_make_child (
/*	=============
*/		int   (* proc) (),  /* The procedure to be started */
		int	 prty,	    /* The child's priority */
		int	 spnd,	    /* "Suspend" flag to help debug the child */
		int	 pkt_size,  /* TCP/IP packet size to be used */
		int	 max_chld,  /* The size of the following arrays */
		pid_t	*pids,	    /* Array of PIDs of children */
		int	*exit_stat, /* Array of children's exit stati */
		int	*pcnt_SIGUSR1,	/* Flag set on SIGUSR1 signals whenever
					** a child exits. */
		char	*name) {    /* Prefix for child's name */
		
    int	    i, status, tmo;
    pid_t   created_pid;
    char    tsknam[20];
    SEM_ID  my_sem;
/*
**	    Find a free PID index. Zero is not a permitted value.
*/
    for (i = 1; i <= max_chld; i++) if (pids[i] == 0) break;

    if (i > max_chld) {		/* Free index found? */
      return -2;		/* No, there are already too many children. */
    }else {
      if (pcnt_SIGUSR1 != NULL) *pcnt_SIGUSR1 = 0;
      my_sem = semBCreate (SEM_Q_FIFO, SEM_EMPTY);  /* Create a claimed sem-
				** -aphore which the child can release when
				** it has finished its initialisation. */
      exit_stat[i] = 0;		/* Zero child's status indicator */
      sprintf (tsknam, "%.*s%02d", (sizeof (tsknam) - 3),
			name, i);	    /* Generate the child's name */
      created_pid = taskSpawn (		    /* Make a child */
			tsknam,		    /* Name */
			prty,		    /* Priority */
			VX_FP_TASK,	    /* Options - fl.pt. usage assumed */
			20000,		    /* Stack size */
			proc,		    /* The entry point */
			FS_pid, (int) my_sem, i, pkt_size, spnd,
			(int) &exit_stat[i],
			0, 0, 0, 0);
      if (created_pid == ERROR) {	    /* Error? */
        return -1;			    /* Yes. */
      }else {				    /* Parent */
	pids[i] = created_pid;
	tmo = 1000;		    /* Normally give child 1000 ticks to ..
				    ** .. get going */
	if (spnd != 0) {
	  tmo = WAIT_FOREVER;	    /* But in debug mode, wait forever! */
	}
	status = semTake (my_sem, tmo);	    /* Wait for child to get going */
	semDelete (my_sem);
	if (status == ERROR) {		    /* Timeout? */
	  taskDelete (pids[i]);		    /* Yes, kill child. */
	  pids[i] = 0;
	  return -3;			    /* Return with error. */
	}else {				/* No timeout so child is ready. */
	  if (exit_stat[i] >= 0) {	/* Did the child have a startup ..
					** .. problem */
	    return i;			/* No, so return its index */
	  }else {
	    return (exit_stat[i] - 3);	/* Yes, so return error */
	  }
	}
      }
    }
  }
/*
**--------------------------------------------------------------------------
**	    FS_project_hm: Project a block of histogram data.
**
**		Return value = # of bins in buffer, each of size uint.
**		Notes:
**		  This is an internal routine so all args are assumed to be OK.
*/
  int	FS_project_hm (
/*	=============
*/		int	     index,
		int	     pkt_size,
		uint	    *buff,	    /* Buffer to receive the data */
		uint	    *w_buff,	    /* Workspace */
		uint	     blen_in_bins,  /* Size of buff and w_buff in bins */
		uint	     sub_code,	    /* 0 or PROJECT__ON_Y */
		uint	     lo_cntr,	    /* First counter wanted */
		uint	     hi_cntr,	    /* Last counter wanted */
		uint	     fst_bin,	    /* First bin wanted */
		uint	     lst_bin) {	    /* Last bin wanted */

    int			    i, j, k, nx, ny, iret;
    uint		    s_lo_cntr, s_hi_cntr, bytes_to_come;
    uint		    f_cntr, l_cntr, n_cntr_got;
    uint		    f_bin,  l_bin,  n_bins_got;
    uchar		   *b_pntr;
    struct req_buff_struct  my_rq;
    struct rply_buff_struct my_rp;
			/*
			** There are 2 cases: project on x and project on y.
			** Project on x is expected to be the more common!
			*/
    if (sub_code != PROJECT__ON_Y) {
      n_cntr_got = 0;			/* Project on X */
      memset (buff, 0, (blen_in_bins * sizeof (uint)));	/* Zero the buffer */
      for (i = 0; i < FS_n_slaves; i++) { /* Find which slave(s) has the wanted data */
	s_lo_cntr = FS_lo_det[i];
	s_hi_cntr = FS_hi_det[i];
	if (((s_lo_cntr >= lo_cntr) && (s_hi_cntr <= hi_cntr)) ||
	    ((s_lo_cntr <  lo_cntr) && (s_hi_cntr >  hi_cntr)) ||
	    ((s_lo_cntr <  lo_cntr) && (s_hi_cntr >= lo_cntr)) ||
	    ((s_lo_cntr <= hi_cntr) && (s_hi_cntr >  hi_cntr))) {
	  f_cntr = (s_lo_cntr >= lo_cntr) ? 0 : (lo_cntr - s_lo_cntr);
	  l_cntr = (s_hi_cntr <= hi_cntr) ? (s_hi_cntr - s_lo_cntr) :
					    (hi_cntr - s_lo_cntr);
	  my_rq.bigend             = htonl (0x12345678);
	  my_rq.cmnd               = htonl (SQHM_PROJECT);
	  my_rq.u.project.sub_code = htonl (0);
	  my_rq.u.project.x_lo     = htonl (fst_bin);
	  my_rq.u.project.nx       = htonl (lst_bin - fst_bin + 1);
	  my_rq.u.project.y_lo     = htonl (f_cntr);
	  my_rq.u.project.ny       = htonl (l_cntr - f_cntr + 1);
	  my_rq.u.project.xdim     = htonl (0);
	  my_rq.u.project.nhist    = htonl (0);
	  iret = rqst_send_get (FS_skt[index][i], &my_rq, sizeof (my_rq),
				&my_rp, sizeof (my_rp),
				&FS_bytes_put, &FS_bytes_got, FS_name[index]);
	  if (!iret) {return 0;};
	  if (ntohl (my_rp.status) != KER__SUCCESS) {return 0;};
	  nx = ntohl (my_rp.u.project.n_bins);	/* # of bins to be read */
	  bytes_to_come = nx * ntohl (my_rp.u.project.bytes_per_bin);
	  b_pntr = (uchar *) w_buff;
	  while (bytes_to_come > 0) {
	    j = (bytes_to_come > pkt_size) ? pkt_size : bytes_to_come;
	    k = recv (FS_skt[index][i], b_pntr, j, 0);
	    if (k <= 0) {perror ("FS_project_hm"); return 0;}
	    bytes_to_come -= k;
	    b_pntr += k;
	  }
	  k = l_cntr - f_cntr + 1;
	  n_cntr_got += k;
	  lo_cntr += k;
	  for (k = 0; k < nx; k++) buff[k] += ntohl (w_buff[k]);
	}
      }
      return nx;
    }else {
      n_cntr_got = 0;			/* Project on Y */
      for (i = 0; i < 3; i++) {	/* Find which slave(s) has the wanted data */
	s_lo_cntr = FS_lo_det[i];
	s_hi_cntr = FS_hi_det[i];
	if (((s_lo_cntr >= lo_cntr) && (s_hi_cntr <= hi_cntr)) ||
	    ((s_lo_cntr <  lo_cntr) && (s_hi_cntr >  hi_cntr)) ||
	    ((s_lo_cntr <  lo_cntr) && (s_hi_cntr >= lo_cntr)) ||
	    ((s_lo_cntr <= hi_cntr) && (s_hi_cntr >  hi_cntr))) {
	  f_cntr = (s_lo_cntr >= lo_cntr) ? 0 : (lo_cntr - s_lo_cntr);
	  l_cntr = (s_hi_cntr <= hi_cntr) ? (s_hi_cntr - s_lo_cntr) :
					    (hi_cntr - s_lo_cntr);
	  my_rq.bigend             = htonl (0x12345678);
	  my_rq.cmnd               = htonl (SQHM_PROJECT);
	  my_rq.u.project.sub_code = htonl (PROJECT__ON_Y);
	  my_rq.u.project.x_lo     = htonl (fst_bin);
	  my_rq.u.project.nx       = htonl (lst_bin - fst_bin + 1);
	  my_rq.u.project.y_lo     = htonl (f_cntr);
	  my_rq.u.project.ny       = htonl (l_cntr - f_cntr + 1);
	  my_rq.u.project.xdim     = htonl (0);
	  my_rq.u.project.nhist    = htonl (0);
	  iret = rqst_send_get (FS_skt[index][i], &my_rq, sizeof (my_rq),
				&my_rp, sizeof (my_rp),
				&FS_bytes_put, &FS_bytes_got, FS_name[index]);
	  if (!iret) {return 0;};
	  if (ntohl (my_rp.status) != KER__SUCCESS) {return 0;};
	  ny = ntohl (my_rp.u.project.n_bins);	/* # of elements to be read */
	  bytes_to_come = ny * ntohl (my_rp.u.project.bytes_per_bin);
	  b_pntr = (uchar *) w_buff;
	  while (bytes_to_come > 0) {
	    j = (bytes_to_come > pkt_size) ? pkt_size : bytes_to_come;
	    k = recv (FS_skt[index][i], b_pntr, j, 0);
	    if (k <= 0) {perror ("FS_project_hm"); return 0;}
	    bytes_to_come -= k;
	    b_pntr += k;
	  }
	  for (k = 0; k < ny; k++) buff[n_cntr_got+k] = ntohl (w_buff[k]);
	  n_cntr_got += ny;
	  lo_cntr += ny;
	}
      }
      return n_cntr_got;
    }
  }
/*
**--------------------------------------------------------------------------
**	    FS_read_hm: Read a block of histogram data.
**
**		Return value = # of bins read.
*/
  int	FS_read_hm (
/*	==========
*/		int	     index,
		int	     pkt_size,
		uchar	    *buff,	    /* Buffer to receive the data */
		uint	     blen_in_bins,  /* Size of buff in bins */
		uint	     fst_bin,	    /* First bin wanted */
		uint	     lst_bin) {	    /* Last bin wanted */

    int			    i, j, k, iret;
    uint		    s_fst_bin, s_lst_bin, bins_per_hist;
    uint		    f_bin, l_bin, bytes_to_come, n_bins_got;
    struct req_buff_struct  my_rq;
    struct rply_buff_struct my_rp;

    bins_per_hist = FS_hi_bin - FS_lo_bin + 1;
    n_bins_got = 0;

    for (i = 0; i < 3; i++) {	/* Find which slave(s) has the wanted data */
      s_fst_bin = 0;
      for (j = 0; j < i; j++) {
	s_fst_bin += (FS_hi_det[j] - FS_lo_det[j] + 1) * bins_per_hist;
      }
      s_lst_bin = s_fst_bin +
	    (FS_hi_det[i] - FS_lo_det[i] + 1) * bins_per_hist - 1;
      if (((s_fst_bin >= fst_bin) && (s_lst_bin <= lst_bin)) ||
	  ((s_fst_bin <  fst_bin) && (s_lst_bin >  lst_bin)) ||
	  ((s_fst_bin <  fst_bin) && (s_lst_bin >= fst_bin)) ||
	  ((s_fst_bin <= lst_bin) && (s_lst_bin >  lst_bin))) {
	f_bin = (s_fst_bin >= fst_bin) ? 0 : (fst_bin - s_fst_bin);
	l_bin = (s_lst_bin <= lst_bin) ? (s_lst_bin - s_fst_bin) :
					    (lst_bin - s_fst_bin);
	my_rq.bigend           = htonl (0x12345678);
	my_rq.cmnd             = htonl (SQHM_READ);
	my_rq.u.read.hist_no   = htonl (-1);
	my_rq.u.read.first_bin = htonl (f_bin);
	my_rq.u.read.n_bins    = htonl (l_bin - f_bin + 1);
	iret = rqst_send_get (FS_skt[index][i], &my_rq, sizeof (my_rq),
				&my_rp, sizeof (my_rp),
				&FS_bytes_put, &FS_bytes_got, FS_name[index]);
	if (!iret) {return 0;};
	if (ntohl (my_rp.status) != KER__SUCCESS) {return 0;};
	k = ntohl (my_rp.u.read.n_bins);	/* # of bins to be read */
	bytes_to_come = k * FS_bpb;
	while (bytes_to_come > 0) {
	  j = (bytes_to_come > pkt_size) ? pkt_size : bytes_to_come;
	  k = recv (FS_skt[index][i], buff, j, 0);
	  if (k <= 0) {perror ("FS_read_hm"); return 0;}
	  bytes_to_come -= k;
	  buff += k;
	}
	k = l_bin - f_bin + 1;
	n_bins_got += k;
	fst_bin += k;
      }
    }
    return n_bins_got;
  }
/*
**--------------------------------------------------------------------------*/
  int	FS_rply_status_send (
/*	===================
*/		int			skt,
		struct rply_buff_struct	*rply_bf,
		uint			*cntr_out) {

    int	    is;

    is = send (skt, (char *) rply_bf, sizeof (*rply_bf), 0);
    if (is == sizeof (*rply_bf)) {
      *cntr_out += is;
      return True;
    }else {
      return False;
    }
  }
/*
**--------------------------------------------------------------------------*/
  int	FS_rply_status_setup_and_send (
/*	=============================
*/		int			skt,
		struct rply_buff_struct	*rply_bf,
		int			status,
		int			sub_status,
		char			*msg,
		uint			*cntr_out) {

    int	    is;

    rply_status_setup (rply_bf, status, sub_status, msg);
    is = FS_rply_status_send (skt, rply_bf, cntr_out);

    return is;
  }
/*
**--------------------------------------------------------------------------
**	    FS_send_cmnd_to_mstr:	Send command to Sinqm-Master process
**					in a slave.
*/
  int	FS_send_cmnd_to_mstr (
/*	====================
*/		    struct sockaddr_in	    *rmt_sockname,
		    struct req_buff_struct  *request,
		    int			     requ_len,
		    struct rply_buff_struct *reply,
		    uint		    *in_cntr,
		    uint		    *out_cntr) {

    int			 status, cnct_skt, rmt_sockname_len, i;
    int			 bytes_to_come, nbytes;
    char		*p_nxt_byte;
    struct sockaddr_in	 lcl_sockname;
/*
**  	   Create a TCP/IP socket for connecting to SINQHM_SRV root and bind it.
*/
    cnct_skt = socket (AF_INET, SOCK_STREAM, 0);
    i = 1;
    setsockopt(cnct_skt,SOL_SOCKET, SO_REUSEADDR, &i,sizeof(int));
    if (cnct_skt == -1) {
      close(cnct_skt);
      failInet ("\nFS_send_cmnd_to_mstr: socket error.");
    }

    setup_host (NULL, &lcl_sockname, 0, False);
    status = bind (cnct_skt, (struct sockaddr *) &lcl_sockname,
					sizeof (lcl_sockname));
    if (status == -1){
      close(cnct_skt);
      failInet ("\nFS_send_cmnd_to_mstr: bind error.");
    }
/*
**		Connect to SINQHM_SRV root.
*/
    rmt_sockname_len = sizeof (*rmt_sockname);
    status = connect (cnct_skt, (struct sockaddr *) rmt_sockname,
				    rmt_sockname_len);
    if (status == -1) {
      close(cnct_skt);
      getErrno (&FS_errno);
      failInet ("\nFS_send_cmnd_to_mstr: connect error");
    }
	    /*
	    ** Send the request to the server
	    */
    status = send (cnct_skt, (char *) request, requ_len, 0);
    if (status == -1) {
      close(cnct_skt);
      failInet ("\nFS_send_cmnd_to_mstr -- send error");
    }
    if (status != requ_len) {
      printf ("\nFS_send_cmnd_to_mstr -- wrong number of bytes sent: %d %d\n",
			status, requ_len);
      close(cnct_skt);
      return False;
    }
    *out_cntr += requ_len;
/*
**		    Wait for the status response.
*/
    bytes_to_come = sizeof (*reply);
    p_nxt_byte = (char *) reply;
    while (bytes_to_come > 0) {
      nbytes = recv (cnct_skt, p_nxt_byte, bytes_to_come, 0);
      if (nbytes <= 0) break;
      *in_cntr += nbytes;
      bytes_to_come -= nbytes;
      p_nxt_byte += nbytes;
    }
    if (nbytes == -1) {
      close(cnct_skt);
      failInet ("\nFS_send_cmnd_to_mstr -- recv error");
      return False;
    }else if (nbytes == 0) {
      printf ("\nFS_send_cmnd_to_mstr -- server has closed connection!\n");
      close(cnct_skt);
      return False;
    }else if (ntohl (reply->bigend) != 0x12345678) {
					    /* Network byte-order wrong! */
      printf ("\nFS_send_cmnd_to_mstr -- big-endian/little-endian problem!\n"
		"    Buffer received in non-network byte order!\n");
      close(cnct_skt);
      return False;
    }
    close(cnct_skt);
    return True;
  }
/*
**--------------------------------------------------------------------------
**	    rqst_send_get:  Send a network request and get reply
*/
  int	rqst_send_get (
/*	=============
*/		int			 skt,
		struct req_buff_struct	*rqst_bf,
		int			 rq_len,
		struct rply_buff_struct	*rply_bf,
		int			 rp_size,
		int			*out_cntr,
		int			*in_cntr,
		char			*tsk_name) {

    int	     status, bytes_to_come, nbytes, n_bytes_got;
    char     recd[80], *p_addr;
		/*
		** Send the request to the server
		*/
    status = send (skt, (char *) rqst_bf, rq_len, 0);
    if (status == -1) {
      perror (StrJoin (recd, sizeof (recd),
		tsk_name, "/rqst_send_get: send error"));
      return False;
    }
    if (status != rq_len) {
      printf ("\n%s/rqst_send_get: wrong number of bytes sent: %d %d\n",
			tsk_name, status, rq_len);
      return False;
    }
    *out_cntr += rq_len;
/*
**		    Wait for the response.
*/
    bytes_to_come = rp_size;
    p_addr = (char *) rply_bf;
    while (bytes_to_come > 0) {
      n_bytes_got = recv (skt, p_addr, bytes_to_come, 0);
      if (n_bytes_got <= 0) break;
      *in_cntr += n_bytes_got;
      bytes_to_come -= n_bytes_got;
      p_addr += n_bytes_got;
    }
    if (n_bytes_got < 0) {
      perror (StrJoin (recd, sizeof (recd),
		tsk_name, "/rqst_send_get: recv error"));
      return False;
    }else if (n_bytes_got == 0) {
      printf ("\n%s/rqst_send_get: zero bytes received!\n", tsk_name);
      return False;
    }else if (ntohl (rply_bf->bigend) != 0x12345678) {
      printf ("\n%s/rqst_send_get: big-endian/little-endian problem!\n",
			    tsk_name);
      return False;
    }
    return True;
  }
/*
**--------------------------------------------------------------------------
**	    setup_host:	Setup Internet structures for a named host
*/
  int	setup_host (
/*	==========
*/		    char		*host,
		    struct sockaddr_in	*sockname,
		    int			 port,
		    int			 verbose) {

    int		     i, j, k, l, m;
    char	     my_host[64], my_addr[32];
    struct in_addr   my_inet_addr;

    if (host == NULL) {
      my_inet_addr.s_addr = 0;
    }else {
      if (strlen (host) >= sizeof (my_host)) return False;
      for (i = 0; i <= strlen (host); i++) my_host[i] = tolower (host[i]);
		/*
		** Try to convert the Internet address. First assume it is dot
		** notation. If this fails, see if we can recognise the name.
		*/
      my_inet_addr.s_addr = inet_addr (my_host);
      if (my_inet_addr.s_addr == ERROR) {
	if (strcmp (my_host, "localhost") == 0) {
	  my_inet_addr.s_addr = inet_addr ("127.0.0.1");
	}else {
	  i = sscanf (my_host, "lnse%d%n", &j, &k);
	  if ((i == 1) && (k == 6)) {
	    if (strcmp (&my_host[k], ".vme") == 0) {
	      sprintf (my_addr, "192.168.11.%d", j);	    /* "lnse??.vme" */
	    }else {
	      sprintf (my_addr, "129.129.62.%d", (100+j));    /* "lnse??" */
	    }
	    my_inet_addr.s_addr = inet_addr (my_addr);
	  }else {
	    i = sscanf (my_host, "psds%d%n", &j, &k);
	    if ((i == 1) && (k == 6) && (my_host[k] == NIL)) {
	      sprintf (my_addr, "129.129.62.%d", j);    /* "psds??" */
	      my_inet_addr.s_addr = inet_addr (my_addr);
	    }
	  }
	}
      }
    }
    if (my_inet_addr.s_addr == ERROR) {
      printf ("\nsetup_host: Node \"%s\" is unknown.\n", host);
      return False;
    }
    if (verbose) {
      printf ("Host is %s ", host);
      inet_ntoa_b (my_inet_addr, my_addr);
      printf ("(%s),", my_addr);
      printf (" Port %d.\n", port);
    }
    sockname->sin_family      = AF_INET;
    sockname->sin_port	      = htons (port);
    sockname->sin_addr.s_addr = my_inet_addr.s_addr;
  }
/*
**--------------------------------------------------------------------------
**	    socket_bind:    Setup an Internet socket and bind it to a port.
*/
  int	socket_bind (
/*	===========
*/		    int	     port,
		    char    *tsk_name) {

    int			status, skt;
    struct sockaddr_in	sockname;
    char		recd[80];

    skt = socket (AF_INET, SOCK_STREAM, 0);
    if (skt == -1) {
      perror (StrJoin (recd, sizeof (recd), tsk_name,
			" -- socket error in socket_bind"));
      return skt;
    }
    status = 1;
    setsockopt(skt,SOL_SOCKET, SO_REUSEADDR, &status, sizeof(int));

    setup_host (NULL, &sockname, port, False);
    status = bind (skt, (struct sockaddr *) &sockname, sizeof (sockname));
    if (status != 0) {
      close (skt);
      perror (StrJoin (recd, sizeof (recd), tsk_name,
			" -- bind error in socket_bind"));
      return -1;
    }
    return skt;
  }
/*
**==============================================================================
**  Main line program
**  ------------------
**============================================================================*/
  int	FOCUS_main (
/*	==========
*/	    char *hostM, int ndetM,
	    char *hostO, int ndetO,
	    char *hostU, int ndetU,
	    int c_port,				/* Our port # as client */
	    int s_port,				/* Our port # for serving */
	    int arg9, int suspend) {
/*============================================================================*/
    struct req_buff_struct	    req_buff;
    struct rply_buff_struct	    rply_buff;

    int			i, j, is, status, cntr;
    int			keep_going = True;
    int			bytes_to_come, n_bytes_got;
    char		*p_addr;
    int			cl_port;
    struct sockaddr_in	lcl_sockname;
    struct sockaddr_in	rmt_sockname;
    int			rmt_sockname_len;
    int			pkt_size;
    time_t		timer;
    char		buff[132];

    sigset_t		proc_sigmask;
    struct sigaction	sig_action;

/*============================================================================
*/
    printf ("\n");
    printf ("hostM = \"%s\" %d cntrs\n", hostM, ndetM);
    printf ("hostO = \"%s\" %d cntrs\n", hostO, ndetO);
    printf ("hostU = \"%s\" %d cntrs\n", hostU, ndetU);
    printf ("Client port = %d\n", c_port);
    printf ("Server port = %d\n", s_port);

    if (suspend != 0) {
      printf ("\nFOCUS_srv: Suspending ...\n");
      taskSuspend (0);	    /* We've been told to suspend ourself, ..
			    **  .. presumably for debug reasons.
			    */
      printf ("\nFOCUS_srv: Suspension ended!\n");
    }
/*============================================================================
**	Perform the initialisation of global variables ...
*/
    StrJoin (FS_inst, sizeof (FS_inst), INST, "");     /* Save the instr.
							     ** name in a global
							     */
    StrJoin (FS_def_ident, sizeof (FS_def_ident), SINQHM_DEF_ID, "");
    StrJoin (FS_main_ident, sizeof (FS_main_ident), IDENT, "");
    StrJoin (FS_main_date,  sizeof (FS_main_date),  __DATE__, ", ");
    StrJoin (FS_main_date,  sizeof (FS_main_date),  FS_main_date, __TIME__);
    time (&FS_start_time);	/* Initialise the start time */
    FS_serv_ident[0] = NIL;
    FS_serv_date[0]  = NIL;
    FS_rout_ident[0] = NIL;
    FS_rout_date[0]  = NIL;

    FS_cfgn_done = False; /* Force a configuration before we can do anything. */

    FS_bytes_got = 0; FS_bytes_put = 0;
    FS_dbg_mask = FS_dbg_lev0 = FS_dbg_lev1 = FS_dbg_lev2 = FS_dbg_lev3 = 0;

    FS_free_memory (NULL);  /* Release memory (possibly) in case ..
			    ** .. of a restart of the program */
    if (FS_cnct_skt != 0) close (FS_cnct_skt);
    if (FS_rw_skt   != 0) close (FS_rw_skt);
    FS_cnct_skt = FS_rw_skt = 0;
    
    for (i = 0; i <= MAX_CLIENTS; i++) {
      FS_pids[i] = 0;
      FS_chld_exit_status[i] = 0;
      if (FS_chld_cnct_skt[i] != 0) close (FS_chld_cnct_skt[i]);
      if (FS_chld_rw_skt[i]   != 0) close (FS_chld_rw_skt[i]);
      FS_chld_cnct_skt[i] = FS_chld_rw_skt[i]   = 0;
      FS_p_names[i] = &FS_name[i][0]; FS_name[i][0] = NIL;
      for (j = 0; j < MAX_SLV; j++) {
	if (FS_skt[i][j] != 0) {close (FS_skt[i][j]); FS_skt[i][j] = 0;}
      }
      FS_srv_masks[i] = (i == 0) ? 0x8000 : 1 << (i - 1);
    }

    if (FS_sem_daq == NULL) {
      FS_sem_daq = semBCreate (SEM_Q_FIFO, SEM_FULL);  /* Create a released sem-
					    ** aphore for guarding access to
					    ** FS_dsbl_mask */
    }
    FS_dsbl_mask = 0;		/* Start up with DAQ enabled */

    FS_pid = get_pid ();			/* Store our pid */
    StrJoin (FS_name[0], sizeof (FS_name[0]),	/* And our task name */
		taskName (FS_pid), "");
/*
**	Debug hint - set FS_use_sem_tmo to False (== 0) to disable sem time-outs
**		     during debug sessions. This will stop things falling off.
*/
    FS_sem_tmo = 5 * sysClkRateGet ();	/* 5 second time-out by default */
    FS_use_sem_tmo = True;
/*
**	    Do all the necessary network orientated setup for our slaves to
**	    make it easier to broadcast commands to them.
*/
/*
**	    Get Internet address of the front-end.
*/
    FS_n_slaves = 3;
    if ((c_port < 128) || (c_port > 32767)) c_port = 2500;

    status = setup_host (hostM, &FS_rmt_socknam[0], c_port, True);
    if (status) FS_slaves[0] = hostM; else FS_slaves[0] = NULL;
    status = setup_host (hostO, &FS_rmt_socknam[1], c_port, True);
    if (status) FS_slaves[1] = hostO; else FS_slaves[1] = NULL;
    status = setup_host (hostU, &FS_rmt_socknam[2], c_port, True);
    if (status) FS_slaves[2] = hostU; else FS_slaves[2] = NULL;

    FS_lo_det[0] = 0;
    FS_hi_det[0] = FS_lo_det[0] + ndetM - 1;
    FS_lo_det[1] = FS_hi_det[0] + 1;
    FS_hi_det[1] = FS_lo_det[1] + ndetO - 1;
    FS_lo_det[2] = FS_hi_det[1] + 1;
    FS_hi_det[2] = FS_lo_det[2] + ndetU - 1;
    if (ndetM == 0) FS_slaves[0] = NULL;
    if (ndetO == 0) FS_slaves[1] = NULL;
    if (ndetU == 0) FS_slaves[2] = NULL;

    for (i = 0; i <= FS_n_slaves; i++) {
      FS_slv_active[i] = False;
    }
/*============================================================================
**	Clear the process signal mask to let in all signals (actually, on
**	PPC VxWorks this seems to cause all signals to get ignored!) and then
**	declare action routine for SIGUSR1. Our children signal with SIGUSR1
**	just before they exit in a more or less controlled manner. If the
**	child gets deleted from the shell, it will not signal.
*/
    FS_cnt_SIGUSR1 = 0;

    status = sigemptyset (&proc_sigmask);
    status = sigprocmask (SIG_SETMASK, &proc_sigmask, NULL);
    status = sigemptyset (&sig_action.sa_mask);
    sig_action.sa_handler = FS_catch_signal;
    sig_action.sa_flags   = 0;

    if (sigaction (SIGUSR1, &sig_action, NULL) == -1) {
      getErrno (&FS_errno);
      perror ("sigaction error:");
      exit (0);
    }
/*============================================================================
**		Get a port number to use. If the argument "port" is not
**		a valid port number (not in range 128 to 32767), use PORT_BASE.
*/
    FS_port = PORT_BASE;
    if ((s_port >= 128) && (s_port < 32768)) FS_port = s_port;
/*============================================================================
**	Create a TCP/IP socket for receiving connection requests, bind it and
**	set it to listen.
*/
    FS_cnct_skt = socket (AF_INET, SOCK_STREAM, 0);
    if (FS_cnct_skt == -1) {
      sprintf (buff, "\n%s -- Cnct-Socket socket error", FS_name[0]);
      failInet (buff);
    }
    status = 1;
    setsockopt(FS_cnct_skt, SOL_SOCKET, SO_REUSEADDR, &status, sizeof(int));
    setup_host (NULL, &lcl_sockname, FS_port, False);
    status = bind (FS_cnct_skt, (struct sockaddr *) &lcl_sockname,
					  sizeof (lcl_sockname));
    if (status != 0) {
      sprintf (buff, "\n%s -- Cnct-Socket bind error", FS_name[0]);
      failInet (buff);
    }
    status = listen (FS_cnct_skt, 5);
    if (status != 0) {
      sprintf (buff, "\n%s -- Cnct-Socket listen error", FS_name[0]);
      failInet (buff);
    }
/*
**	    Clients must now connect to the Connect-port and send either a
**	    connect, configure, deconfigure, status or exit request. A
**	    successful configure request must precede any connection request.
**
**	    When a configure request arrives, the histogram data structures are
**	    initialised, the histogram filler process (FILLER) is spawned and
**	    connection requests are enabled. A status reply is sent to the
**	    client and the connection closed.
**
**	    When a connection request arrives, a child will be spawned to
**	    handle the connection. The child will be told which port to use
**	    and he will release a semaphore when the port is ready. The parent
**	    then sends the client a reply to tell him which port to use. The
**	    connection is then closed to await further connections.
*/
    setup_inet_info (FS_host, sizeof (FS_host),	    /* Find out who we are  */
		     FS_addr, sizeof (FS_addr));
    printf ("\n%s: Program  Ident = \"%s\". Started on %s (%s).\n",
		    FS_name[0], IDENT, FS_host, FS_addr);
    printf ("%s: Protocol Ident = \"%s\"\n", FS_name[0], FS_def_ident);

#ifndef INST
    #error  Define the target instrument via the symbol "INST"!
#endif

    if (strcmp (INST, "FOCUS") == 0) {
      printf ("%s: Instrument = \"%s\"\n", FS_name[0], "FOCUS");
    }else {
      printf ("%s: Instrument is not defined\n", FS_name[0]);
    }
/*============================================================================*/
    while (keep_going) {
      rmt_sockname_len = sizeof (rmt_sockname);
      FS_rw_skt = accept (FS_cnct_skt, (struct sockaddr *) &rmt_sockname,
						&rmt_sockname_len);
      if (FS_rw_skt == -1) {
        getErrno (&FS_errno);
	sprintf (buff, "\n%s -- Cnct-Socket accept error", FS_name[0]);
	failInet (buff);	/* No return */
      }

      p_addr = inet_ntoa (rmt_sockname.sin_addr);
      cl_port = ntohs (rmt_sockname.sin_port);
      printf ("\n%s: Connected to Node %s, Port %d.\n",
				FS_name[0], p_addr, cl_port);

      bytes_to_come = sizeof (req_buff);   /* Read a command */
      p_addr = (char *) &req_buff;
      while (bytes_to_come > 0) {
	n_bytes_got = recv (FS_rw_skt, p_addr, bytes_to_come, 0);
	if (n_bytes_got <= 0) break;
	FS_bytes_got += n_bytes_got;
	bytes_to_come -= n_bytes_got;
	p_addr += n_bytes_got;
      }

      if (FS_cnt_SIGUSR1 > 0) {	    /* Have any children exited? */
	if (FS_dbg_lev0) printf ("\n%s: Cleaning up children ...", FS_name[0]);
	FS_cleanup_children (MAX_CLIENTS,   /* Yes, update our database. */
		FS_pids, &FS_cnt_SIGUSR1, FS_dbg_lev0, FS_p_names, "");
	if (FS_dbg_lev0) printf ("\n  ... cleanup of children done.\n");
      }

      if (n_bytes_got < 0) {
	printf ("\n%s -- R/W-Socket recv error\n", FS_name[0]);
	printf ("%s -- Connection to %s will be closed.\n", FS_name[0],
			    inet_ntoa (rmt_sockname.sin_addr));
      }else if (n_bytes_got == 0) {
	printf ("\n%s -- no command received!\n", FS_name[0]);
	printf ("%s -- Connection to %s will be closed.\n", FS_name[0],
			    inet_ntoa (rmt_sockname.sin_addr));
      }else if (bytes_to_come != 0) {
	printf ("\n%s -- command packet has wrong number of bytes:"
		" %d instead of %d\n", FS_name[0],
		n_bytes_got, sizeof (req_buff));
	printf ("%s -- Connection to %s will be closed.\n", FS_name[0],
			    inet_ntoa (rmt_sockname.sin_addr));
      }else if (ntohl (req_buff.bigend) != 0x12345678) {
	printf ("\n%s -- big-endian/little-endian problem!\n"
		"   Buffer received in non-network byte order!\n", FS_name[0]);
      }else {
	FS_do_command (0, FS_rw_skt, 4096, &req_buff, &rply_buff);
      }
      close (FS_rw_skt); FS_rw_skt = 0;
      if (ntohl (req_buff.cmnd) == SQHM_EXIT) keep_going = False;
    }
/*============================================================================*/
    close (FS_cnct_skt); FS_cnct_skt = 0;
    exit (KER__SUCCESS);
  }
/*========================================= End of FOCUS_srv_main.c ========*/
