Main Page | Modules | Data Structures | File List | Globals | Examples

ts.c

The following is a program which, when run as a server, establishes a simple RPC handler, and when run as client sets up and calls that RPC. Note the use of CMlisten_specific on the server-side to force CM to bind to a particular IP port.

/*
 *
 *  $Id: ts.c,v 1.2 2004/10/14 21:16:25 pmw Exp $
 *
 */

#include "config.h"
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_WINDOWS_H
#include <windows.h>
#include <process.h>
#endif
#include <sys/types.h>
#include <signal.h>
#include <string.h>

#include "cmrpc.h"

CManager cm;

static int quiet = 0;
static int regression_master = 1;
static int keep_server_going = 1;
static int forked;
static int status = 0;
static int fork_client = 1;

typedef struct call1_struct
{
  int i1;
  int i2;
  char* s1;
} call1, *call1_ptr;


typedef struct resp1_struct
{
  int result;
  char* res_str;
} resp1, *resp1_ptr;


IOField call1_flds[] =
{
  { "i1", "integer", sizeof (int), IOOffset (call1_ptr, i1) },
  { "i2", "integer", sizeof (int), IOOffset (call1_ptr, i2) },
  { "s1", "string", sizeof (char*), IOOffset (call1_ptr, s1) },
  { (char*)0, (char*)0, 0, 0 }
};

IOField resp1_flds[] = 
{
  { "result", "integer", sizeof (int), IOOffset (resp1_ptr, result) },
  { "res_str", "string", sizeof (char*), IOOffset (resp1_ptr, res_str) },
  { (char*)0, (char*)0, 0, 0 }
};

static
pid_t
run_subprocess(args)
char **args;
{
#ifdef HAVE_WINDOWS_H
    int child;
    child = _spawnv(_P_NOWAIT, "./ts.exe", args);
    if (child == -1) {
        perror("spawnv");
    }
    return child;
#else
    pid_t child = fork();
    if (child == 0) {
        /* I'm the child */
        execv("./ts", args);
    }
    return child;
#endif
}


void
handler (void* in_msg, void* out_msg, CMrpc_options options)
{
  call1* pc;
  resp1* pr;

  pc = (call1*)in_msg;
  pr = (resp1*)out_msg;

  /*
    if (pc->i1 == -1000 
    && pc->i2 == -1000
    && strcmp (pc->s1, "shutdown") == 0)
    {
    keep_server_going = 0;
    }
  */
  pr->result = pc->i1 + pc->i2;
  pr->res_str = strdup (pc->s1);
  pr->res_str[0] = toupper (pr->res_str[0]);
  CMrpc_clear_option(options, CMrpc_call_cleanup_function);
}

void 
oneway_handler (void* in_msg, CMrpc_options options)
{
  call1* pc;

  pc = (call1*)in_msg;

  if (pc->i1 == -1000 
      && pc->i2 == -1000
      && strcmp (pc->s1, "shutdown") == 0)
    {
      keep_server_going = 0;
      if (!quiet)
        fprintf (stderr, "oneway shutdown msg received...\n");
      CMrpc_shutdown();
    }

}

void
cleanup_handler (CManager cm, void* msg)
{
  resp1* pr;


  pr = (resp1*)msg;
  if (!quiet) fprintf (stderr, "In cleanup handler, about to free memory...\n");
  free (pr->res_str);
  if (!quiet) fprintf (stderr, "DONE\n");
}


int
check_result (call1_ptr pc, resp1_ptr pr)
{
  /*
   * returns 0 if everything is OK, shell status semantics instead
   * of C boolean semantics
   */
  int r = 0;
  char *t;
  
  t = strdup (pc->s1);

  *t = (unsigned char) toupper (*t);

  r = ((pc->i1 + pc->i2 != pr->result 
        || strcmp (t, pr->res_str) != 0));
  if (!quiet)
    {
      fprintf (stderr, "result is %d:%s, should be %d:%s\n",
               pr->result, pr->res_str,
               pc->i1 + pc->i2, t);
      if (r)
        fprintf (stderr, "test error\n");
    }
  
  free (t);
  return r;
}
          

static pid_t client_proc = 0;

static void
fail_and_die(signal)
int signal;
{
    fprintf(stderr, "Test failed to complete in reasonable time\n");
    if (client_proc != 0) {
        kill(client_proc, 9);
    }
    exit(1);
}


int
main (int argc, char** argv)
{
  int reps;
  attr_list attrs;
  char* test_str = "snuggle!";
  char* shutdown_str = "shutdown";
  char* test_rpc_name = "addemup";
  char* test_server = "localhost";

#ifdef HAVE_WINDOWS_H
    SetTimer(NULL, 5, 1000, (TIMERPROC) fail_and_die);
#else
    struct sigaction sigact;
    sigact.sa_flags = 0;
    sigact.sa_handler = fail_and_die;
    sigemptyset(&sigact.sa_mask);
    sigaddset(&sigact.sa_mask, SIGALRM);
    sigaction(SIGALRM, &sigact, NULL);
    alarm(300);  
#endif

    while (argv[1] && (argv[1][0] == '-')) 
      {
        if (argv[1][1] == 'c') 
          {
            regression_master = 0;
          } 
        else if (argv[1][1] == 's') 
          {
            regression_master = 1;
          } 
        else if (argv[1][1] == 'q') 
          {
            quiet = 1;
          } 
        else if (argv[1][1] == 'v') 
          {
            quiet = 0;
          } 
        else if (argv[1][1] == 'n') 
          {
            fork_client = 0;
          }
        else if (argv[1][1] == 'h')
          {
            test_server = argv[2];
            argv++;
          }
        argv++;
      }

    
  gen_pthread_init();
  cm = CManager_create();
  /*  forked = CMfork_comm_thread (cm);  */

  attrs = create_attr_list();
  set_attr (attrs,
            CM_IP_HOSTNAME,
            Attr_String,
            test_server);
  set_attr (attrs,
            CM_IP_PORT,
            Attr_Int4,
            (attr_value*)6969);

  if (!regression_master)
    {
      /*
       * run as client
       */
      call1 call_struct;
      resp1 response_struct;
      CMrpc_ticket t;
      IOContext ic, oc;
      
      call_struct.i1 = 5;
      call_struct.i2 = 5;
      call_struct.s1 = test_str;
      response_struct.result = 0;
      
      CMrpc_register_rpc_format (cm, test_rpc_name,
                                  call1_flds, NULL,
                                  resp1_flds, NULL);
      CMrpc_register_rpc_contact_info (cm, test_rpc_name, attrs);

      CMrpc_call_rpc (cm, test_rpc_name, NULL, &call_struct, &response_struct, 0);

      status += check_result (&call_struct, &response_struct);

      if (!quiet) printf ("first call complete\n");

      call_struct.i1 = 12;
      call_struct.i2 = 30;
      
      t = CMrpc_call_rpc (cm, test_rpc_name, NULL, &call_struct, &response_struct, CMrpc_async_call);

      CMrpc_redeem_ticket (cm, t, &response_struct, 1);

      status += check_result (&call_struct, &response_struct);

      if (!quiet) printf ("second call complete\n");

      call_struct.i1 = 100;
      call_struct.i2 = 25;
      
      /*
       *  do async and anonymous at the same time?  You Must Be Joking!
       */

      ic = CMget_user_type_context (cm);
      oc = CMget_user_type_context (cm);
      CMregister_user_format (cm, ic, "addemup", call1_flds, NULL);
      CMregister_user_format (cm, oc, "addemup", resp1_flds, NULL);

      t = CMrpc_call_anon_rpc (cm, "addemup", attrs,
                                ic, &call_struct,
                                oc, &response_struct,
                                CMrpc_async_call);
      /*      t = CMrpc_call_rpc (cm, test_rpc_name, &call_struct, &response_struct, 1);*/

      reps = 0;
      while (reps++ < 5 && CMrpc_redeem_ticket (cm, t, &response_struct, 0) != 1)
        {
          if (!quiet) {
            fprintf (stderr, "Async/no-wait result wasn't available, sleeping....\n");
            /* fprintf (stderr, "error is %s\n", strerror(CMrpc_errno));*/
          }
          CMusleep (cm,1000);
        }

      if (reps >= 5)
        {
          if (!quiet) fprintf (stderr, "Async/no-wait result never arrived!\n");
        }
      else
        {
          status += check_result (&call_struct, &response_struct);
        }

      CMfree_user_type_context (cm, ic);
      CMfree_user_type_context (cm, oc);

      if (!quiet) printf ("third call complete\n");

      call_struct.i1 = call_struct.i2 = -1000;
      call_struct.s1 = shutdown_str;
      CMrpc_call_rpc_oneway (cm, test_rpc_name, attrs, &call_struct, 0);

      if (!quiet) printf ("shutdown call complete\n");

      CManager_close (cm);
      free_attr_list (attrs);
    }
  else
    {
      char *args[] = { "ts", "-q", "-c", NULL };
      /*
       * run as server
       */
      CMlisten_specific (cm, attrs);
      
      CMrpc_register_rpc_format (cm, test_rpc_name,
                                  call1_flds, NULL, 
                                  resp1_flds, NULL);

      CMrpc_register_rpc_handler (cm, test_rpc_name, &handler, &oneway_handler, &cleanup_handler);

      if (fork_client)
        {
          /*
           * fork the client
           */
          client_proc = run_subprocess (args);
          /*   if (!quiet) fprintf (stderr, "client proc pid is %lu\n", client_proc);  */
        }

      while (keep_server_going)
        {
            if (!forked) 
              {
                CMsleep(cm, 1);
              }
            else
              {
                sleep(1);
              }
        }
      
      CManager_close (cm);
      /* free_attr_list (attrs);
       -- causes weird segmentation fault on Linux */
    }

  return status;
}
  

/*
 *
 *  $Log: ts.c,v $
 *  Revision 1.2  2004/10/14 21:16:25  pmw
 *  Added doxygen documentation.
 *  Also changed prefix of everything from CM_RPC to CMrpc.  It looks better.
 *
 *  Revision 1.1  2004/09/24 15:40:08  pmw
 *  Restructuring to add tests and config subdirectories
 *
 *  Revision 1.32  2004/05/06 17:55:04  eisen
 *  Tweak CMRPC so that it works with CMSelfFormats set.  This required some
 *  extensions to CM, and a bit of rearranging.
 *
 *  Revision 1.31  2002/08/16 17:56:36  pmw
 *  uncommented alarm call in test program, I'd commented it during
 *  debugging...
 *
 *  Revision 1.30  2002/08/15 03:55:13  pmw
 *  fixed MT problem where async calls had their connection whacked out
 *  from underneath them, causing their condition to be marked failed
 *  before any tickets corresponding to them were redeemed.
 *
 *  also cleaned up undeclared function warnings in tclHash.c.
 *
 *  also changed handling of attr_lists in request.c to eliminate
 *  attr_add_list call.
 *
 *  Revision 1.29  2002/08/07 16:35:56  eisen
 *  Change the way that atoms are handled.  Start using the
 *  set_attr_atom_and_string() to assign a 32-bit value to the string instead of
 *  letting the atom server assign it.
 *
 *  Revision 1.28  2002/07/29 15:51:53  eisen
 *  Odd hangups happening on Linux.  The server process is hanging in exit(0),
 *  presumably at the end of normal processing.  Stack dump is
 *  exit()->IO_cleanup()->IO_flush_all()->flockfile()->pthread_mutex_lock().
 *  Later the alarm() signal comes in and fail_and_die() is called which itself
 *  then deadlocks on the fprintf(stderr) waiting to lock the output stream.  My
 *  suspicion is that this is caused by some memory abuse and CM maybe being too
 *  aggressive in cleaning up in-use memory and locks.  Not quite sure.  Rather
 *  than debug fully at the moment I thought I'd try moving the CManager_close()
 *  call from oneway_handler() to the end of the main program.  Maybe it'll
 *  help.
 *
 *  BTW, the hanging doesn't seem to happen if the program has a controlling
 *  tty, making this very difficult to test.  I'll leave this for tomorrow's
 *  build.  Eventually we need to go through the ts example with purify and see
 *  if CM's memory free plan needs to be refined (I'm sure it does.)
 *
 *  g
 *
 *  Revision 1.27  2002/07/09 21:17:15  pmw
 *  changes to quiet the nightly build process
 *
 *  Revision 1.26  2002/06/18 16:00:10  eisen
 *  ts.c
 *
 *  Revision 1.25  2002/06/16 21:59:16  pmw
 *  more and more fixes.  This should at least shut up the messages from
 *  the nightly chaos builds.
 *
 *  Revision 1.24  2001/10/11 16:20:11  pmw
 *  fixed problem preventing test from completing correctly - changed hostname back
 *  to localhost.  also added -no-install flag for ts_LDFLAGS in Makefile.am.
 *
 *  Revision 1.23  2001/10/09 15:11:32  pmw
 *  now links against gen_thread .so instead of .a
 *
 *  Revision 1.22  2001/07/13 20:46:21  eisen
 *  compile under windows
 *
 *  Revision 1.21  2000/11/21 14:55:46  eisen
 *  Make sure we never do a kill(0,9), as this kills
 *
 *  Revision 1.20  2000/10/23 20:52:10  pmw
 *  Added contact_info parameter to call_rpc calls.  This is to be used
 *  if, for a specific invocation of an RPC, you wish to override the
 *  registered contact information.
 *
 *  Revision 1.19  2000/10/16 03:45:37  pmw
 *  Added oneway RPC calls.  This has been tested somewhat.
 *
 *  Revision 1.18  2000/10/16 02:31:24  pmw
 *  1 - fixed gotcha-in-waiting concerning IOcontext management and anonymous
 *  calls.  The IOcontext allocated for anon calls was getting freed before the
 *  user ever saw the result structure from the call, and the underlying PBIO
 *  buffer was suspect.  This meant revising the anon-call interface back to
 *  where the user supplies his own IOcontext values.
 *  2 - Added rpc-with-channel functionality, not yet tested.
 *
 *  Revision 1.17  2000/09/29 20:52:08  pmw
 *  fixed lingering problems from subcontext changes.  hopefully this closes it.
 *  make check works correctly, so I'll believe it for the moment.
 *
 *  Revision 1.16  2000/09/27 23:41:41  pmw
 *  Hopefully, this fixes the looming format problem Greg pointed out.
 *  Subcontexts are now used to hold the input and output top-level
 *  formats of each RPC format pair, and appropriate conversion setting
 *  should be being done based on matching the format names on the incoming
 *  message with those registered "locally" and setting the conversion with
 *  the local field list.
 *
 *  Revision 1.15  2000/09/19 20:54:18  pmw
 *  changed syntax of handlers to eliminate explicit passing of CManager value - now contained in options structure
 *
 *  Revision 1.14  2000/05/30 14:53:09  eisen
 *  Eliminate warning by using variable "forked".  (In practice, this probably
 *  fixed a bug.  The result of CMfork_comm_thread() shows whether or not a
 *  separate kernel-level communications-handling thread was forked.  If it was,
 *  *no* other thread should call CMpoll_network() or else something bad might
 *  happen.  The likeliest result would be deadlock, but other race conditions
 *  are possible.
 *
 *  Revision 1.13  2000/05/14 17:33:14  pmw
 *  removed C++ comment that caused compilations to fail on some architectures
 *
 *  Revision 1.12  2000/05/12 20:54:18  pmw
 *  Re-did anonymous call syntax to avoid PBIO memory-handling issues
 *
 *  Revision 1.11  2000/05/12 19:45:07  pmw
 *  stupid typo fix and CVS headers added
 *
 *
 */


Generated on Thu Oct 14 18:34:49 2004 for CMrpc by  doxygen 1.3.9.1