/*
 * srpc_test_server.c
 *
 * Server for the testing of the SRPC calls in NASD
 *
 * Author: Shelby Davis
 */
/*
 * Copyright (c) of Carnegie Mellon University, 1999.
 *
 * Permission to reproduce, use, and prepare derivative works of
 * this software for internal use is granted provided the copyright
 * and "No Warranty" statements are included with all reproductions
 * and derivative works. This software may also be redistributed
 * without charge provided that the copyright and "No Warranty"
 * statements are included in all redistributions.
 *
 * NO WARRANTY. THIS SOFTWARE IS FURNISHED ON AN "AS IS" BASIS.
 * CARNEGIE MELLON UNIVERSITY MAKES NO WARRANTIES OF ANY KIND, EITHER
 * EXPRESSED OR IMPLIED AS TO THE MATTER INCLUDING, BUT NOT LIMITED
 * TO: WARRANTY OF FITNESS FOR PURPOSE OR MERCHANTABILITY, EXCLUSIVITY
 * OF RESULTS OR RESULTS OBTAINED FROM USE OF THIS SOFTWARE. CARNEGIE
 * MELLON UNIVERSITY DOES NOT MAKE ANY WARRANTY OF ANY KIND WITH RESPECT
 * TO FREEDOM FROM PATENT, TRADEMARK, OR COPYRIGHT INFRINGEMENT.
 */
#include <nasd/nasd_options.h>

#if NASD_RPC_PACKAGE == NASD_RPC_PACKAGE_SRPC

#include <nasd/nasd_threadstuff.h>
#include <nasd/nasd_shutdown.h>
#include <nasd/nasd_types.h>
#include <nasd/nasd_freelist.h>
#include <nasd/nasd_common.h>
#include <nasd/nasd_getopt.h>
#include <nasd/nasd_general.h>
#include <nasd/nasd_srpc.h>
#include <nasd/nasd_timeout.h>
#include <nasd/nasd_rpcgen_glob_param.h>
#include <nasd/nasd_marshall.h>
#include <nasd/nasd_timer.h>
#include <nasd/srpc_test.h>
#include <nasd/srpc_test_sstub.h>
#include <nasd/srpc_test_marshall.h>

#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#define DEFAULT_SRPC_TEST_SERVER_PORT 5500

/* zee functions that we use. */
nasd_status_t srpc_test_rpc_init();
void nasd_od_srpc_kill_listener_group(void  *ignored);
nasd_status_t srpc_test_rpc_specific_init();
nasd_status_t srpc_test_really_shutdown();
void srpc_test_shutdown_timeoutsys(void *ignored);
void srpc_test_shutdown_rpc(void *ignored);
void srpc_test_timeout_shutdown_rpc(nasd_timeout_handle_t   tmh,
				    void                   *arg1,
				    void                   *arg2);
nasd_status_t srpc_test_rpc_listen(int          service_threads,
				   nasd_uint16  ipport);
extern nasd_status_t srpc_test_demux(nasd_srpc_listener_t     *listener,
			      nasd_srpc_conn_t         *conn,
			      nasd_srpc_server_call_t  *call);

/* zee random global variables */
nasd_shutdown_list_t *srpc_test_shutdown = NULL;
int srpc_test_rpc_qdepth = 0;
nasd_edrfs_opstats_t srpc_test_opstats;
nasd_uint64 srpc_test_threads_in_rpc = 0;
int srpc_test_wait_shutdown = 0;
NASD_DECLARE_COND(srpc_test_shutdown_cond)
NASD_DECLARE_MUTEX(srpc_test_rpc_cnt_lock)
NASD_DECLARE_MUTEX(srpc_test_rpc_qdepth_lock)
char *progname;

int signal_pipe[2];
#define signal_pipe_rd  signal_pipe[0]
#define signal_pipe_wr  signal_pipe[1]

#define CLOSEUP_PIPES() { \
   close(signal_pipe[0]); \
   close(signal_pipe[1]); \
 }

nasd_srpc_listener_t *srpc_test_fm_listener;
int srpc_test_fm_srpc_started = 0;
nasd_threadgroup_t srpc_test_fm_listening_group;
int svc_threads = 8;
int stack_size = 32768;
int srpc_test_active = 0;
nasd_timeout_handle_t srpc_test_shutdown_timeout_handle;
nasd_timespec_t srpc_test_shutdown_timeout_interval = {1, 0};
int alldone = 0;
pid_t srpc_test_fm_signal_pid;
int srpc_test_fm_stopped = 0;
nasd_uint64 srpc_test_shutdown_timeouts = 0;

void
nasd_od_srpc_kill_listener_group(void  *ignored)
{
  nasd_status_t rc;
  
  rc = nasd_destroy_threadgroup(&srpc_test_fm_listening_group);
  if (rc) {
    nasd_printf("WARNING: got 0x%x (%s) destroying srpc_test_fm_listening_group\n",
		rc, nasd_error_string(rc));
  }
}

nasd_status_t
srpc_test_rpc_init()
{
  nasd_status_t rc;
  
  srpc_test_rpc_qdepth = 0;
  
  bzero((char *)&srpc_test_opstats, sizeof(srpc_test_opstats));
  
  if (srpc_test_threads_in_rpc) {
    /* already/still running */
    return(NASD_FAIL);
  }
  
  srpc_test_wait_shutdown = 0;
  srpc_test_threads_in_rpc = 0;
  
  rc = nasd_cond_init(&srpc_test_shutdown_cond);
  if (rc)
    return(rc);
  rc = nasd_shutdown_cond(srpc_test_shutdown, &srpc_test_shutdown_cond);
  if (rc) {
    return(rc);
  }
  
  rc = nasd_mutex_init(&srpc_test_rpc_cnt_lock);
  if (rc)
    return(rc);
  rc = nasd_shutdown_mutex(srpc_test_shutdown, &srpc_test_rpc_cnt_lock);
  if (rc) {
    return(rc);
  }
  
  rc = nasd_mutex_init(&srpc_test_rpc_qdepth_lock);
  if (rc)
    return(rc);
  rc = nasd_shutdown_mutex(srpc_test_shutdown, &srpc_test_rpc_qdepth_lock);
  if (rc) {
    return(rc);
  }
  
  rc = srpc_test_rpc_specific_init();
  if (rc) {
    return(rc);
  }
  
  return(NASD_SUCCESS);
}

nasd_status_t
srpc_test_rpc_specific_init()
{
  nasd_status_t rc;
  
  rc = nasd_init_threadgroup(&srpc_test_fm_listening_group);
  if (rc)
    return(rc);
  rc = nasd_shutdown_proc(srpc_test_shutdown,
			  nasd_od_srpc_kill_listener_group, NULL);
  if (rc) {
    nasd_od_srpc_kill_listener_group(NULL);
    return(rc);
  }
  
  srpc_test_fm_listener = NULL;
  rc = nasd_srpc_init();
  
  if (rc == NASD_SUCCESS) {
    srpc_test_fm_srpc_started = 1;
  }
  
  return(rc);
}

void
srpc_test_shutdown_timeoutsys(void  *ignored)
{
  nasd_timeout_shutdown();
}

nasd_status_t
srpc_test_really_shutdown()
{
  nasd_status_t rc;
  int e;
  
  nasd_printf("SRPC_TEST: really shutting down\n");
  
  e = 0;
  rc = nasd_shutdown_list_shutdown(srpc_test_shutdown,
				   NASD_SHUTDOWN_ANNOUNCE_NONE);
  if (rc) {
    nasd_printf("ERROR: got 0x%x (%s) from nasd_shutdown_list_shutdown()\n",
		rc, nasd_error_string(rc));
    e = 1;
  }
  
  nasd_printf("SRPC_TEST: automatic shutdown complete\n");
  
  nasd_shutdown_cleanup();
  
  nasd_printf("SRPC_TEST: shutdown complete\n");
  
  return(NASD_SUCCESS);
}

nasd_status_t
srpc_test_startup_rpc()
{
  nasd_status_t rc;
  
#ifdef MQ
  rc = srpc_test_mq_specific_startup();
  if (rc)
    return rc;
#endif /* !KERNEL */
  
  rc = nasd_shutdown_proc(srpc_test_shutdown, srpc_test_shutdown_rpc, NULL);
  if (rc) {
    srpc_test_shutdown_rpc(NULL);
    return(rc);
  }
  
  srpc_test_active = 1;
  
  return(NASD_SUCCESS);
}

void
srpc_test_shutdown_rpc(void *ignored)
{
  nasd_status_t rc;
  
  srpc_test_wait_shutdown = 1;
  
  rc = NASD_FAIL;
  
  NASD_LOCK_MUTEX(srpc_test_rpc_cnt_lock);
  while(srpc_test_threads_in_rpc) {
    if (rc) {
      rc = nasd_timeout_add(&srpc_test_shutdown_timeout_handle,
			    srpc_test_timeout_shutdown_rpc, NULL, NULL,
			    srpc_test_shutdown_timeout_interval,
			    srpc_test_shutdown_timeout_interval,
			    NASD_TIMEOUT_F_PERIODIC);
    }
    if (rc) {
      nasd_printf("EDRFS: got 0x%x (%s) adding timeout for shutdown-quiesce\n",
		  rc, nasd_error_string(rc));
    }
    NASD_WAIT_COND(srpc_test_shutdown_cond,srpc_test_rpc_cnt_lock);
  }
  NASD_UNLOCK_MUTEX(srpc_test_rpc_cnt_lock);
}

void
srpc_test_timeout_shutdown_rpc( nasd_timeout_handle_t   tmh,
                                void                   *arg1,
                                void                   *arg2)
{
  nasd_timespec_t ts;
  char str[128];
  
  if (srpc_test_threads_in_rpc == 0) {
    NASD_BROADCAST_COND(srpc_test_shutdown_cond);
  }
  srpc_test_shutdown_timeouts++;
}

void
srpc_test_stop_rpc()
{
  nasd_status_t rc;
  
  if (srpc_test_fm_listener) {
    rc = nasd_srpc_listener_destroy(srpc_test_fm_listener);
    if (rc) {
      nasd_printf("SRPC_TEST ERROR: got 0x%x (%s) from "
		  "nasd_srpc_listener_destroy()\n",
		  rc, nasd_error_string(rc));
    }
    srpc_test_fm_listener = NULL;
  }
  
  if (srpc_test_fm_srpc_started) {
    /* Go go gadget linux */
    NASD_THREADGROUP_WAIT_START(&srpc_test_fm_listening_group);
    NASD_THREADGROUP_WAIT_STOP(&srpc_test_fm_listening_group);
    srpc_test_fm_srpc_started = 0;
    nasd_srpc_shutdown();
  }  

#ifdef MQ
  srpc_test_mq_specific_stop();
#endif /* !KERNEL */

}

nasd_status_t
srpc_test_rpc_set_stacksize(int  stacksize)
{
  return(NASD_OP_NOT_SUPPORTED);
}

nasd_status_t
srpc_test_rpc_listen(int          service_threads,
                              nasd_uint16  ipport)
{
  nasd_status_t rc;
  
  rc = nasd_srpc_listener_start(&srpc_test_fm_listener, 
                                service_threads, ipport,
                                srpc_test_demux);
  if (rc)
    return(rc);
  
  /*
   * Subtle race condition: if we shut down asynchronously between the start
   * and this wait, we lose the memory. If this seems like a problem, we
   * could resolve it by having _start() hand the serial number to us,
   * and waiting only on the serial number.
   */
  
  NASD_THREADGROUP_STARTED(&srpc_test_fm_listening_group);
  NASD_THREADGROUP_RUNNING(&srpc_test_fm_listening_group);
  
  rc = nasd_srpc_listener_wait(srpc_test_fm_listener);
  
  NASD_THREADGROUP_DONE(&srpc_test_fm_listening_group);
  
  return(rc);
}

nasd_srpc_status_t
srpc_test_null_dr_server(
  nasd_srpc_listener_t     *listener,
  nasd_srpc_conn_t         *conn,
  nasd_srpc_server_call_t  *call,
  nasd_res_otw_t out_res) 
{
  nasd_res_t res;
  nasd_res_t_unmarshall(out_res, &res);
  nasd_printf("SRPC_TEST: null op sent, id:%d\n",res.nasd_status);

  return (NASD_SUCCESS);
}

nasd_srpc_status_t
srpc_test_read_server(
  nasd_srpc_listener_t     *listener,
  nasd_srpc_conn_t         *conn,
  nasd_srpc_server_call_t  *call,
  srpc_test_seed_otw_t in_seed,
  nasd_res_otw_t out_stat,
  nasd_srpc_server_pipestate_t *out_pipe) 
{
  srpc_test_seed_t seed;
  srpc_test_seed_t_unmarshall(in_seed, &seed);
  nasd_printf("SRPC_TEST: read server sent\n");
  
  return (NASD_SUCCESS);
}

nasd_srpc_status_t
srpc_test_write_server(
  nasd_srpc_listener_t     *listener,
  nasd_srpc_conn_t         *conn,
  nasd_srpc_server_call_t  *call,
  srpc_test_seed_otw_t in_seed,
  nasd_res_otw_t out_stat,
  nasd_srpc_server_pipestate_t *in_pipe) 
{
  srpc_test_seed_t seed;
  srpc_test_seed_t_unmarshall(in_seed, &seed);
  nasd_printf("SRPC_TEST: write server sent\n");
  return (NASD_SUCCESS);
}


void
usage()
{
  fprintf(stderr, "USAGE: %s [options]\n", progname);
  fprintf(stderr, "Options:\n");
  fprintf(stderr, "  -t service threads [default %d]\n", svc_threads);
  fprintf(stderr, "  -p specify ip port [default %d]\n",
	  DEFAULT_SRPC_TEST_SERVER_PORT);
  fflush(stderr);
  exit(1);
}

void
handle_sigint(int  sig)
{
  int ret;
  char c;
  
  srpc_test_fm_stopped = 1;
  c = 'a';
  ret = write(signal_pipe_wr, &c, 1);
}

int main(int argc, char **argv) 
{
  nasd_uint16 ipport = DEFAULT_SRPC_TEST_SERVER_PORT;
  nasd_status_t rc;
  nasd_srpc_listener_t  *listen;
  int ret,arg_count,temp;
  char c;
  struct timeval tv;

  progname = argv[0];

  if(argc > 1) {
    arg_count = 1;
    while(arg_count < argc) {
      if(argv[arg_count][0] != '-') usage();
      if(argv[arg_count][1] != 'p' ||
         argv[arg_count][1] != 't') usage();
      if(argv[arg_count][1] == (char)(0)) usage();
      if(argv[arg_count][2] == (char)(0)) {
        arg_count++;
        if(arg_count == argc) usage();
        if(!sscanf(argv[arg_count],"%d",temp)) usage();
        if(argv[arg_count-1][1]=='p') ipport = temp;
        else svc_threads = temp;
      } else {
        if(!sscanf(&(argv[arg_count][2]),"%d",temp)) usage();
        if(argv[arg_count][1]=='p') ipport = temp;
        else svc_threads = temp;
      }
      arg_count++;
    }
  }                                   

  if(ipport < 1 || svc_threads < 1) usage();
  printf("Using port %d, %d service threads\n", ipport, svc_threads);

  ret = pipe(signal_pipe);
  if(ret) {
    fprintf(stderr,"ERROR: cannot create signal pipe\n");
    fflush(stderr);
    exit(1);
  }
  
  rc = nasd_mem_init();
  if(rc) { 
    fprintf(stderr,
	    "SRPC_TEST ERROR: got 0x%x (%s) from nasd_mem_init()\n",
	    rc, nasd_error_string(rc));
    fflush(stderr);
    exit(1);
  }    

  rc = nasd_shutdown_sys_init();
  if(rc) { 
    fprintf(stderr,
	    "SRPC_TEST ERROR: got 0x%x (%s) from nasd_shutdown_sys_init()\n",
	    rc, nasd_error_string(rc));
    fflush(stderr);
    exit(1);
  }    
  
  rc = nasd_shutdown_list_init(&srpc_test_shutdown);
  if(rc) { 
    fprintf(stderr,
	    "SRPC_TEST ERROR: got 0x%x (%s) from nasd_shutdown_list_init()\n",
	    rc, nasd_error_string(rc));
    fflush(stderr);
    exit(1);
  }    

  rc = nasd_timeout_init();
  if (rc) {
    fprintf(stderr,
	    "SRPC_TEST ERROR: got 0x%x (%s) from nasd_timeout_init()\n",
	    rc, nasd_error_string(rc));
    fflush(stderr);
    srpc_test_really_shutdown();
    exit(1);
  }
  
  rc = nasd_shutdown_proc(srpc_test_shutdown, srpc_test_shutdown_timeoutsys,
			  NULL);
  if (rc) {
    fprintf(stderr,
	    "SRPC_TEST ERROR: got 0x%x (%s) from nasd_shutdown_proc()\n",
	    rc, nasd_error_string(rc));
    fflush(stderr);
    srpc_test_shutdown_timeoutsys(NULL);
    srpc_test_really_shutdown();
    exit(1);
  }
  
  rc = srpc_test_rpc_init();
  if (rc) {
    fprintf(stderr,
	    "SRPC_TEST ERROR: got 0x%x (%s) from srpc_test_rpc_init()\n",
	    rc, nasd_error_string(rc));
    fflush(stderr);
    srpc_test_really_shutdown();
    exit(1);
  }

  rc = srpc_test_startup_rpc();
  if (rc) {
    fprintf(stderr,
	    "SRPC_TEST ERROR: got 0x%x (%s) from srpc_test_startup_rpc()\n",
	    rc, nasd_error_string(rc));
    fflush(stderr);
    CLOSEUP_PIPES();
    exit(1);
  }
 
#ifndef LINUX
  signal(SIGUSR1, handle_sigint);
#endif /* LINUX */
  
#ifdef LINUX
  /* Linux's pthreads library uses SIGUSR1 and SIGUSR2, so we can't. */
  signal(SIGPWR, handle_sigint);
#endif /* LINUX */

  signal(SIGINT, handle_sigint);
  
  rc = srpc_test_rpc_set_stacksize(stack_size);
  if (rc == NASD_OP_NOT_SUPPORTED) {
    printf("SRPC_TEST: setting stack size not supported\n");
  }
  else if (rc) {
    printf("SRPC_TEST: got 0x%x (%s) setting stack size to %d\n",
	   rc, nasd_error_string(rc), stack_size);
  }
  else {
    printf("SRPC_TEST: stack size set to %d\n", stack_size);
  }
  
  printf("Running with pid %d\n", srpc_test_fm_signal_pid);
  fflush(stdout);

  rc = srpc_test_rpc_listen(svc_threads, ipport);
  if (rc) {
    fprintf(stderr, 
	    "SRPC_TEST ERROR: got 0x%x (%s) from srpc_test_rpc_listen()\n",
	    rc, nasd_error_string(rc));
    fflush(stderr);
  }

 if (!srpc_test_fm_stopped) {
   srpc_test_stop_rpc();
   
   rc = srpc_test_really_shutdown();
   if (rc) {
     fprintf(stderr, "ERROR: got %s (%lx) from srpc_test_really_shutdown()\n",
	     nasd_error_string(rc), (u_long)rc);
     fflush(stderr);
     CLOSEUP_PIPES();
     exit(1);
   }
 }
 else {
   /*
    * Someone else is stopping. They'll exit for us.
    */
   do {
     tv.tv_sec = 1;
     tv.tv_usec = 0;
     
     ret = select(1, NULL, NULL, NULL, &tv);
     if (ret) {
       break;
     }
   } while(alldone == 0);
 }
 
 CLOSEUP_PIPES();
 exit(2);
}
#endif /* NASD_RPC_PACKAGE == NASD_RPC_PACKAGE_SRPC */
/* Local Variables:  */
/* indent-tabs-mode: nil */
/* tab-width: 2 */
/* End: */
