#define	    IDENT	"1A08"
/*
** +--------------------------------------------------------------+
** |                  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]SinqHM_srv_server.c
**
** Author   . . . . . . . . . . : D. Maden
** Date of creation . . . . . . : Oct 1996
**
** Updates:
**  1A01  2-Oct-1996 DM	    Initial version.
**
**  SinqHM_srv_server.c forms a child process of the SINQ Histogram Memory
**  process. It does the communication with the client.
**
**  To compile this program for VxWorks on PSS123, use:
**
	    ccvx SinqHM_srv_server.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
**
**====================================================================
*/
#include    <vxWorks.h>
#include    <taskLib.h>
#include    <msgQLib.h>
#include    <sockLib.h>

#include    <stdlib.h>
#include    <stdio.h>
#include    <setjmp.h>

#include    <unistd.h>	/* Get the POSIX environment defined ..
			** .. see DEC OSF/1 Guide to Realtime Programming */
#include    <errno.h>
#include    <netinet/in.h>
#include    <sys/types.h>
#include    <sys/socket.h>

#include    <sys/mman.h>	/* Get POSIX mem mgmnt definitions. */
#include    <signal.h>		/* Get POSIX signal definitions. */
#include    <timers.h>
#include    <sys/wait.h>	/* Get POSIX wait definitions. */
/*
**====================	Global Definitions =====================================
*/
#include    "SinqHM_def.h"
#include    "SinqHM_gbl.h"
/*==============================================================================
		Local Routines for SRV
**					None
*/
/*==============================================================================
		Global Routines for SRV
**			
*/
  void	SinqHM_server_setup_id () {
/*	======================
**		Simply copy some ident information to global variables
**		for use by the SQHM_IDENT request.
*/
    StrJoin (Sqhm_serv_ident, sizeof (Sqhm_serv_ident), IDENT, "");
    StrJoin (Sqhm_serv_date,  sizeof (Sqhm_serv_date),  __DATE__, ", ");
    StrJoin (Sqhm_serv_date,  sizeof (Sqhm_serv_date),  Sqhm_serv_date, __TIME__);
  }
/*==============================================================================
		Here beginneth the main code for SinqHM_server ...
**
**		    Note: There must be NO return from this function.
*/
  int SinqHM_server (int suspend_flag, int index, int pkt_size) {
/*    =============
**
**		Set up TCP/IP port for operations with client, accept his
**		connection and perform services for him.
*/
    int			status, status0, status1;
    int			i, is, nbytes, bytes_to_come;
    char		recd[80];
    char		*p_nxt_byte;
    int			cnct_skt, rw_skt;
    struct sockaddr_in	my_lcl_sockname;
    struct sockaddr_in	my_rmt_sockname;
    int			my_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;

    port = Port_base + index;
/*============================================================================
*/
    StrJoin (Tsk_name[index], sizeof (Tsk_name[0]), taskName (get_pid ()), "");

    if (suspend_flag != 0) {
      printf ("\n%s: Suspending ..\n", Tsk_name[index]);
      taskSuspend (0);	    /* We've been told to suspend ourself, ..
			    **  .. presumably for debug reasons.
			    */
      printf ("\n%s: Suspension ended!\n", Tsk_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 = catch_int_signal;
    sig_action.sa_flags   = 0;
    if (sigaction (SIGINT, &sig_action, NULL) == -1) {
      getErrno (&My_errno);
      perror ("sigaction error:");
      Child_exit_status[index] = -1;	/* Tell our parent we've given up */
      is = semGive (Sem_Server);	/* Let our parent continue */
      exit (KER__BAD_VALUE);
    }
/*============================================================================
**	Create a TCP/IP socket for communication, bind it and listen.
*/
    Child_cnct_skt[index] = 0;
    cnct_skt = socket (AF_INET, SOCK_STREAM, 0);
    if (cnct_skt == -1) {
      perror (StrJoin (recd, sizeof (recd),
		Tsk_name[index], " -- cnct_skt socket error"));
      Child_exit_status[index] = -2;	/* Tell our parent we've given up */
      is = semGive (Sem_Server);	/* Let our parent continue */
      exit (KER__BAD_VALUE);
    }
    status = 1;
    setsockopt(cnct_skt, SOL_SOCKET, SO_REUSEADDR, &status, sizeof(int));

    my_lcl_sockname.sin_family = AF_INET;
    my_lcl_sockname.sin_port   = htons (port);
    my_lcl_sockname.sin_addr.s_addr = 0;
    status = bind (cnct_skt, (struct sockaddr *) &my_lcl_sockname,
					  sizeof (my_lcl_sockname));
    if (status != 0) {
      close (cnct_skt);
      perror (StrJoin (recd, sizeof (recd),
			Tsk_name[index], " -- cnct_skt bind error"));
      Child_exit_status[index] = -2;	/* Tell our parent we've given up */
      is = semGive (Sem_Server);	/* Let our parent continue */
      exit (KER__BAD_VALUE);
    }

    status = listen (cnct_skt, 1);
    if (status != 0) {
      close (cnct_skt);
      perror (StrJoin (recd, sizeof (recd),
			Tsk_name[index], " -- cnct_skt listen error"));
      Child_exit_status[index] = -3;	/* Tell our parent we've given up */
      is = semGive (Sem_Server);	/* Let our parent continue */
      exit (KER__BAD_VALUE);
    }
/*============================================================================
**	    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.
*/
    is = semGive (Sem_Server);
    if (is != OK) {
      kill (Parent_pid, SIGUSR1);   /* Inform parent that we are in trouble */
      printf ("%s -- sem release error\n", Tsk_name[index]);
      close (cnct_skt);
      exit (KER__BAD_VALUE);
    }
    Child_cnct_skt[index] = cnct_skt;
/*============================================================================
**	    Ensure that our bit in SinqHM_Dsbl_Mask is clear.
*/
    do_daq (&my_rply_bf, index, DAQ__CLR);
/*============================================================================
**	    Wait for the client to connect to our port
*/
    my_rmt_sockname_len = sizeof (my_rmt_sockname);
    rw_skt = accept (cnct_skt, (struct sockaddr *) &my_rmt_sockname,
						&my_rmt_sockname_len);
    if (rw_skt == -1) {
      perror (StrJoin (recd, sizeof (recd),
			Tsk_name[index], " -- cnct_skt accept error"));
      close (cnct_skt);
      Child_cnct_skt[index] = 0;
      kill (Parent_pid, SIGUSR1);   /* Inform parent that we are in trouble */
      exit (KER__BAD_VALUE);
    }
    Child_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 (rw_skt, p_nxt_byte, bytes_to_come, 0);
      if (nbytes <= 0) break;
      Rw_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 = do_command (index, rw_skt, pkt_size, &my_req_bf, &my_rply_bf);
      if (is == OK) 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.
**	Anything else is a fatal error.
*/
    do_daq (&my_rply_bf, index, DAQ__CLR); /* Ensure our inhibit bit is clear */

    close (rw_skt);	Child_rw_skt[index] = 0;
    close (cnct_skt);	Child_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),
			    Tsk_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", Tsk_name[index]);
	status = KER__SUCCESS;
      }else {
	printf ("\n%s -- rw_skt recv wrong number of bytes: %d\n",
				    Tsk_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", Tsk_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", Tsk_name[index]);
      status = KER__SUCCESS;
    }else {
					/* Unrecognised request received! */
      printf ("\n%s -- unrecognised command received: 0x%x\n",
			Tsk_name[index], ntohl (my_req_bf.cmnd));
      status = KER__EXIT_SIGNAL;
    }
    kill (Parent_pid, SIGUSR1);	    /* Inform parent that we are exiting */
    exit (status);
  }
/*======================================= End of SinqHM_srv_server.c ========*/
