/* * * $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 * * */