#include "EXTERN.h" #include "perl.h" #include "XSUB.h" #define MAX_PARA 64 #define RFCIMPORT 0 #define RFCEXPORT 1 #define RFCTABLE 2 #define BUF_SIZE 8192 #define RFC_WAIT_TIME 10 #include #include #include #include /* SAP flag for Windows NT or 95 */ #ifdef _WIN32 # ifndef SAPonNT # define SAPonNT # endif #endif #include "saprfc.h" #include "sapitab.h" #if defined(SAPonNT) #include "windows.h" #endif /* name of installed function for global callback in tRFC */ char name_user_global_server[31] = "%%USER_GLOBAL_SERVER"; /* global hash of interfaces */ HV* p_iface_hash; /* global pointers to saprfc object */ SV* global_saprfc; HV* p_saprfc; /* global reference to main loop callback for registered RFC */ SV* sv_callback; /* * local prototypes & declarations */ static RFC_RC DLL_CALL_BACK_FUNCTION handle_request( RFC_HANDLE handle, SV* sv_iface ); static RFC_RC DLL_CALL_BACK_FUNCTION do_docu( RFC_HANDLE handle ); static SV* call_handler(SV* sv_callback_handler, SV* sv_iface, SV* sv_data); RFC_RC loop_callback(SV* sv_callback_handler, SV* sv_self); void get_attributes(RFC_HANDLE rfc_handle, HV* hv_sysinfo); static int DLL_CALL_BACK_FUNCTION TID_check(RFC_TID tid); static void DLL_CALL_BACK_FUNCTION TID_commit(RFC_TID tid); static void DLL_CALL_BACK_FUNCTION TID_confirm(RFC_TID tid); static void DLL_CALL_BACK_FUNCTION TID_rollback(RFC_TID tid); static RFC_RC DLL_CALL_BACK_FUNCTION user_global_server(RFC_HANDLE rfc_handle); static char *user_global_server_docu(void); static RFC_RC install_docu ( RFC_HANDLE handle ); static char * do_docu_docu( void ); /* store a reference to the documentation array ref */ SV* sv_store_docu; /* standard error call back handler - installed into connnection object */ static void DLL_CALL_BACK_FUNCTION rfc_error( char * operation ){ RFC_ERROR_INFO_EX error_info; RfcLastErrorEx(&error_info); croak( "RFC Call/Exception: %s \tError group: %d \tKey: %s \tMessage: %s", operation, error_info.group, error_info.key, error_info.message ); /* RFC_ERROR_INFO error_info; RfcLastError(&error_info); croak( "RFC Call/Key: %s \tStatus: %s \tMessage: %s\tInternal State:%s", error_info.key, error_info.status, error_info.message, error_info.intstat ); */ } /* build a connection to an SAP system */ SV* MyConnect(SV* connectstring){ RFC_ENV new_env; RFC_HANDLE handle; RFC_ERROR_INFO_EX error_info; /* RFC_ERROR_INFO error_info; */ new_env.allocate = NULL; new_env.errorhandler = rfc_error; RfcEnvironment( &new_env ); /* fprintf(stderr, "%s\n", SvPV(connectstring, SvCUR(connectstring))); */ handle = RfcOpenEx(SvPV(connectstring, SvCUR(connectstring)), &error_info); if (handle == RFC_HANDLE_NULL){ RfcLastErrorEx(&error_info); croak( "RFC Call/Exception: Connection Failed \tError group: %d \tKey: %s \tMessage: %s", error_info.group, error_info.key, error_info.message ); /* RfcLastError(&error_info); croak( "RFC Call/Key: %s \tStatus: %s \tMessage: %s\tInternal State:%s", error_info.key, error_info.status, error_info.message, error_info.intstat ); */ }; return newSViv( ( int ) handle ); } /* Disconnect from an SAP system */ SV* MyDisconnect(SV* sv_handle){ RFC_HANDLE handle = SvIV( sv_handle ); RfcClose( handle ); return newSViv(1); } /* create a parameter space and zero it */ static void * make_space( SV* length ){ char * ptr; int len = SvIV( length ); ptr = malloc( len + 1 ); if ( ptr == NULL ) return 0; memset(ptr, 0, len + 1); return ptr; } /* copy the value of a parameter to a new pointer variable to be passed back onto the parameter pointer argument */ static void * make_copy( SV* value, SV* length ){ char * ptr; int len = SvIV( length ); ptr = malloc( len + 1 ); if ( ptr == NULL ) return 0; memset(ptr, 0, len + 1); Copy(SvPV( value, len ), ptr, len, char); return ptr; } /* copy the value of a parameter to a new pointer variable to be passed back onto the parameter pointer argument without the length supplied */ static void * make_strdup( SV* value ){ char * ptr; int len = strlen(SvPV(value, PL_na)); ptr = malloc( len + 1 ); if ( ptr == NULL ) return 0; memset(ptr, 0, len + 1); Copy(SvPV( value, len ), ptr, len, char); return ptr; } /* build the RFC call interface, do the RFC call, and then build a complex hash structure of the results to pass back into perl */ SV* MyRfcCallReceive(SV* sv_handle, SV* sv_function, SV* iface){ RFC_PARAMETER myexports[MAX_PARA]; RFC_PARAMETER myimports[MAX_PARA]; RFC_TABLE mytables[MAX_PARA]; RFC_RC rc; RFC_HANDLE handle; char * function; char * exception; RFC_ERROR_INFO_EX error_info; /* RFC_ERROR_INFO error_info; */ int tab_cnt, imp_cnt, exp_cnt, irow, h_index, a_index, i, j; AV* array; HV* h_parms; HV* p_hash; HE* h_entry; SV* h_key; SV* sv_type; HV* hash = newHV(); tab_cnt = 0; exp_cnt = 0; imp_cnt = 0; handle = SvIV( sv_handle ); function = SvPV( sv_function, PL_na ); /* get the RFC interface definition hash and iterate */ h_parms = (HV*)SvRV( iface ); h_index = hv_iterinit( h_parms ); for (i = 0; i < h_index; i++) { /* grab each parameter hash */ h_entry = hv_iternext( h_parms ); h_key = hv_iterkeysv( h_entry ); p_hash = (HV*)SvRV( hv_iterval(h_parms, h_entry) ); sv_type = *hv_fetch( p_hash, (char *) "TYPE", 4, FALSE ); /* determine the interface parameter type and build a definition */ switch ( SvIV(sv_type) ){ case RFCIMPORT: /* build an import parameter and allocate space for it to be returned into */ myimports[imp_cnt].name = make_strdup( h_key ); if ( myimports[imp_cnt].name == NULL ) return 0; myimports[imp_cnt].nlen = strlen( SvPV(h_key, PL_na)); myimports[imp_cnt].addr = make_space( *hv_fetch(p_hash, (char *) "LEN", 3, FALSE) ); myimports[imp_cnt].leng = SvIV( *hv_fetch( p_hash, (char *) "LEN", 3, FALSE ) ); myimports[imp_cnt].type = SvIV( *hv_fetch( p_hash, (char *) "INTYPE", 6, FALSE ) ); ++imp_cnt; break; case RFCEXPORT: /* build an export parameter and pass the value onto the structure */ myexports[exp_cnt].name = make_strdup( h_key ); if ( myexports[exp_cnt].name == NULL ) return 0; myexports[exp_cnt].nlen = strlen( SvPV(h_key, PL_na)); myexports[exp_cnt].addr = make_copy( *hv_fetch(p_hash, (char *) "VALUE", 5, FALSE), *hv_fetch(p_hash, (char *) "LEN", 3, FALSE) ); myexports[exp_cnt].leng = SvIV( *hv_fetch( p_hash, (char *) "LEN", 3, FALSE ) ); myexports[exp_cnt].type = SvIV( *hv_fetch( p_hash, (char *) "INTYPE", 6, FALSE ) ); ++exp_cnt; break; case RFCTABLE: /* construct a table parameter and copy the table rows on to the table handle */ mytables[tab_cnt].name = make_strdup( h_key ); if ( mytables[tab_cnt].name == NULL ) return 0; mytables[tab_cnt].nlen = strlen( SvPV(h_key, PL_na)); mytables[tab_cnt].leng = SvIV( *hv_fetch( p_hash, (char *) "LEN", 3, FALSE ) ); mytables[tab_cnt].itmode = RFC_ITMODE_BYREFERENCE; mytables[tab_cnt].type = RFCTYPE_CHAR; /* maybe should be RFCTYPE_BYTE */ mytables[tab_cnt].ithandle = ItCreate( mytables[tab_cnt].name, SvIV( *hv_fetch( p_hash, (char *) "LEN", 3, FALSE ) ), 0 , 0 ); if ( mytables[tab_cnt].ithandle == NULL ) return 0; array = (AV*) SvRV( *hv_fetch( p_hash, (char *) "VALUE", 5, FALSE ) ); a_index = av_len( array ); for (j = 0; j <= a_index; j++) { Copy( SvPV( *av_fetch( array, j, FALSE ), PL_na ), ItAppLine( mytables[tab_cnt].ithandle ), mytables[tab_cnt].leng, char ); }; tab_cnt++; break; default: fprintf(stderr, " I DONT KNOW WHAT THIS PARAMETER IS: %s \n", SvPV(h_key, PL_na)); exit(0); break; }; }; /* tack on a NULL value parameter to each type to signify that there are no more */ myexports[exp_cnt].name = NULL; myexports[exp_cnt].nlen = 0; myexports[exp_cnt].leng = 0; myexports[exp_cnt].addr = NULL; myexports[exp_cnt].type = 0; myimports[imp_cnt].name = NULL; myimports[imp_cnt].nlen = 0; myimports[imp_cnt].leng = 0; myimports[imp_cnt].addr = NULL; myimports[imp_cnt].type = 0; mytables[tab_cnt].name = NULL; mytables[tab_cnt].ithandle = NULL; mytables[tab_cnt].nlen = 0; mytables[tab_cnt].leng = 0; mytables[tab_cnt].type = 0; /* do the actual RFC call to SAP */ rc = RfcCallReceive( handle, function, myexports, myimports, mytables, &exception ); /* check the return code - if necessary construct an error message */ if ( rc != RFC_OK ){ RfcLastErrorEx( &error_info ); if (( rc == RFC_EXCEPTION ) || ( rc == RFC_SYS_EXCEPTION )) { hv_store( hash, (char *) "__RETURN_CODE__", 15, newSVpvf( "EXCEPT\t%s\tGROUP\t%d\tKEY\t%s\tMESSAGE\t%s", exception, error_info.group, error_info.key, error_info.message ), 0 ); } else { hv_store( hash, (char *) "__RETURN_CODE__", 15, newSVpvf( "EXCEPT\t%s\tGROUP\t%d\tKEY\t%s\tMESSAGE\t%s","RfcCallReceive", error_info.group, error_info.key, error_info.message ), 0 ); }; } else { hv_store( hash, (char *) "__RETURN_CODE__", 15, newSVpvf( "%d", RFC_OK ), 0 ); }; /* if ( rc != RFC_OK ){ RfcLastError( &error_info ); if (( rc == RFC_EXCEPTION ) || ( rc == RFC_SYS_EXCEPTION )) { hv_store( hash, (char *) "__RETURN_CODE__", 15, newSVpvf( "EXCEPT\t%s\tKEY\t%s\tSTATUS\t%s\tMESSAGE\t%sINTSTAT\t%s", exception, error_info.key, error_info.status, error_info.message, error_info.intstat ), 0 ); } else { hv_store( hash, (char *) "__RETURN_CODE__", 15, newSVpvf( "EXCEPT\t%s\tKEY\t%s\tSTATUS\t%s\tMESSAGE\t%sINTSTAT\t%s", exception, error_info.key, error_info.status, error_info.message, error_info.intstat ), 0 ); }; } else { hv_store( hash, (char *) "__RETURN_CODE__", 15, newSVpvf( "%d", RFC_OK ), 0 ); }; */ /* free up the used memory for export parameters */ for (exp_cnt = 0; exp_cnt < MAX_PARA; exp_cnt++){ if ( myexports[exp_cnt].name == NULL ){ break; } else { free(myexports[exp_cnt].name); }; myexports[exp_cnt].name = NULL; myexports[exp_cnt].nlen = 0; myexports[exp_cnt].leng = 0; myexports[exp_cnt].type = 0; if ( myexports[exp_cnt].addr != NULL ){ free(myexports[exp_cnt].addr); }; myexports[exp_cnt].addr = NULL; }; /* retrieve the values of the import parameters and free up the memory */ for (imp_cnt = 0; imp_cnt < MAX_PARA; imp_cnt++){ if ( myimports[imp_cnt].name == NULL ){ break; }; if ( myimports[imp_cnt].name != NULL ){ switch ( myimports[imp_cnt].type ){ /* case RFCTYPE_INT: case RFCTYPE_FLOAT: */ default: /* All the other SAP internal data types case RFCTYPE_CHAR: case RFCTYPE_BYTE: case RFCTYPE_NUM: case RFCTYPE_BCD: case RFCTYPE_DATE: case RFCTYPE_TIME: */ hv_store( hash, myimports[imp_cnt].name, myimports[imp_cnt].nlen, newSVpv( myimports[imp_cnt].addr, myimports[imp_cnt].leng ), 0 ); break; }; free(myimports[imp_cnt].name); }; myimports[imp_cnt].name = NULL; myimports[imp_cnt].nlen = 0; myimports[imp_cnt].leng = 0; myimports[imp_cnt].type = 0; if ( myimports[imp_cnt].addr != NULL ){ free(myimports[imp_cnt].addr); }; myimports[imp_cnt].addr = NULL; }; /* retrieve the values of the table parameters and free up the memory */ for (tab_cnt = 0; tab_cnt < MAX_PARA; tab_cnt++){ if ( mytables[tab_cnt].name == NULL ){ break; }; if ( mytables[tab_cnt].name != NULL ){ #ifdef DOIBMWKRND hv_store( hash, mytables[tab_cnt].name, mytables[tab_cnt].nlen, newRV_noinc( array = newAV() ), 0); #else hv_store( hash, mytables[tab_cnt].name, mytables[tab_cnt].nlen, newRV_noinc( (SV*) array = newAV() ), 0); #endif /* grab each table row and push onto an array */ for (irow = 1; irow <= ItFill(mytables[tab_cnt].ithandle); irow++){ av_push( array, newSVpv( ItGetLine( mytables[tab_cnt].ithandle, irow ), mytables[tab_cnt].leng ) ); }; free(mytables[tab_cnt].name); }; mytables[tab_cnt].name = NULL; if ( mytables[tab_cnt].ithandle != NULL ){ ItFree( mytables[tab_cnt].ithandle ); }; mytables[tab_cnt].ithandle = NULL; mytables[tab_cnt].nlen = 0; mytables[tab_cnt].leng = 0; mytables[tab_cnt].type = 0; }; return newRV_noinc( (SV*) hash); } RFC_RC loop_callback(SV* sv_callback_handler, SV* sv_self) { int result; SV* sv_rvalue; dSP; /* if there is no handler then get out of here */ if (! SvTRUE(sv_callback_handler)) return RFC_OK; /* initialising the argument stack */ ENTER; SAVETMPS; PUSHMARK(SP); /* push the pkt onto the stack */ XPUSHs( sv_self ); /* stash the stack point */ PUTBACK; result = perl_call_sv(sv_callback_handler, G_EVAL | G_SCALAR ); /* disassemble the results off the argument stack */ if(SvTRUE(ERRSV)) fprintf(stderr, "RFC callback - perl call errored: %s\n", SvPV(ERRSV,PL_na)); SPAGAIN; /* was this handled or passed? */ /* fprintf(stderr, "results are: %d \n", result); */ if (result > 0){ sv_rvalue = newSVsv(POPs); } else { sv_rvalue = newSViv(0); } PUTBACK; FREETMPS; LEAVE; if (SvTRUE(sv_rvalue)){ return RFC_OK; } else { return RFC_SYS_EXCEPTION; } } /*--------------------------------------------------------------------*/ /* TID_CHECK-Function for transactional RFC */ /*--------------------------------------------------------------------*/ static int DLL_CALL_BACK_FUNCTION TID_check(RFC_TID tid) { /* fprintf(stderr, "\n\nStart Function TID_CHECK TID = %s\n", tid); */ int result; SV* sv_callback_handler; SV* sv_rvalue; dSP; sv_callback_handler = (SV*) *hv_fetch(p_saprfc, (char *) "TRFC_CHECK", 10, FALSE); /* if there is no handler then get out of here */ if (! SvTRUE(sv_callback_handler)) return 0; /* initialising the argument stack */ ENTER; SAVETMPS; PUSHMARK(SP); /* push the tid onto the stack */ XPUSHs( newSVpvf("%s",tid) ); /* stash the stack point */ PUTBACK; result = perl_call_sv(sv_callback_handler, G_EVAL | G_SCALAR ); /* disassemble the results off the argument stack */ if(SvTRUE(ERRSV)) fprintf(stderr, "RFC TID_check callback - perl call errored: %s\n", SvPV(ERRSV,PL_na)); SPAGAIN; /* was this handled or passed? */ /* fprintf(stderr, "results are: %d \n", result); */ if (result > 0){ sv_rvalue = newSVsv(POPs); } else { sv_rvalue = newSViv(0); } PUTBACK; FREETMPS; LEAVE; if (SvTRUE(sv_rvalue)){ return 1; } else { return 0; } } /*--------------------------------------------------------------------*/ /* TID_COMMIT-Function for transactional RFC */ /*--------------------------------------------------------------------*/ static void DLL_CALL_BACK_FUNCTION TID_commit(RFC_TID tid) { /* fprintf(stderr, "\n\nStart Function TID_COMMIT TID = %s\n", tid); */ int result; SV* sv_callback_handler; dSP; sv_callback_handler = (SV*) *hv_fetch(p_saprfc, (char *) "TRFC_COMMIT", 11, FALSE); /* if there is no handler then get out of here */ if (! SvTRUE(sv_callback_handler)) return; /* initialising the argument stack */ ENTER; SAVETMPS; PUSHMARK(SP); /* push the tid onto the stack */ XPUSHs( newSVpvf("%s",tid) ); /* stash the stack point */ PUTBACK; result = perl_call_sv(sv_callback_handler, G_EVAL | G_DISCARD ); /* disassemble the results off the argument stack */ if(SvTRUE(ERRSV)) fprintf(stderr, "RFC TID_commit callback - perl call errored: %s\n", SvPV(ERRSV,PL_na)); SPAGAIN; PUTBACK; FREETMPS; LEAVE; return; } /*--------------------------------------------------------------------*/ /* CONFIRM-Function for transactional RFC */ /*--------------------------------------------------------------------*/ static void DLL_CALL_BACK_FUNCTION TID_confirm(RFC_TID tid) { /* fprintf(stderr, "\n\nStart Function TID_CONFIRM TID = %s\n", tid); */ int result; SV* sv_callback_handler; dSP; sv_callback_handler = (SV*) *hv_fetch(p_saprfc, (char *) "TRFC_CONFIRM", 12, FALSE); /* if there is no handler then get out of here */ if (! SvTRUE(sv_callback_handler)) return; /* initialising the argument stack */ ENTER; SAVETMPS; PUSHMARK(SP); /* push the tid onto the stack */ XPUSHs( newSVpvf("%s",tid) ); /* stash the stack point */ PUTBACK; result = perl_call_sv(sv_callback_handler, G_EVAL | G_DISCARD ); /* disassemble the results off the argument stack */ if(SvTRUE(ERRSV)) fprintf(stderr, "RFC TID_confirm callback - perl call errored: %s\n", SvPV(ERRSV,PL_na)); SPAGAIN; PUTBACK; FREETMPS; LEAVE; return; } /*--------------------------------------------------------------------*/ /* TID_ROLLBACK-Function for transactional RFC */ /*--------------------------------------------------------------------*/ static void DLL_CALL_BACK_FUNCTION TID_rollback(RFC_TID tid) { /* fprintf(stderr, "\n\nStart Function TID_ROLLBACK TID = %s\n", tid); */ int result; SV* sv_callback_handler; dSP; sv_callback_handler = (SV*) *hv_fetch(p_saprfc, (char *) "TRFC_ROLLBACK", 13, FALSE); /* if there is no handler then get out of here */ if (! SvTRUE(sv_callback_handler)) return; /* initialising the argument stack */ ENTER; SAVETMPS; PUSHMARK(SP); /* push the tid onto the stack */ XPUSHs( newSVpvf("%s",tid) ); /* stash the stack point */ PUTBACK; result = perl_call_sv(sv_callback_handler, G_EVAL | G_DISCARD ); /* disassemble the results off the argument stack */ if(SvTRUE(ERRSV)) fprintf(stderr, "RFC TID_rollback callback - perl call errored: %s\n", SvPV(ERRSV,PL_na)); SPAGAIN; PUTBACK; FREETMPS; LEAVE; return; } static RFC_RC DLL_CALL_BACK_FUNCTION user_global_server(RFC_HANDLE handle) { RFC_RC rc; RFC_FUNCTIONNAME funcname; SV* sv_iface; fprintf(stderr, "\n\nStart Function %s\n", name_user_global_server); fprintf(stderr, "\n<== RfcGetName rfc_rc = "); rc = RfcGetName(handle, funcname); fprintf(stderr, "%d\n", rc); if (rc == RFC_OK) { fprintf(stderr, " Function Name: '%s'\n", funcname); } if (rc != RFC_OK){ /* fprintf(stderr, "RFC connection failure code: %d \n", rc); */ /* hv_store(p_saprfc, (char *) "ERROR", 5, newSVpvf("RFC connection failure code: %d", rc), 0); */ return rc; } /* check at this point for registered functions */ if ( ! hv_exists(p_iface_hash, funcname, strlen(funcname)) ){ fprintf(stderr, "the MISSING Function Name is: %s\n", funcname); RfcRaise( handle, "FUNCTION_MISSING" ); /* do event callback to intertwine other events */ /* rc = loop_callback(sv_callback, sv_saprfc); */ /* XXX */ return RFC_NOT_FOUND; } /* pass in the interface to be handled */ sv_iface = *hv_fetch(p_iface_hash, funcname, strlen(funcname), FALSE); handle_request(handle, sv_iface); rc = loop_callback(sv_callback, global_saprfc); return rc; } #undef NL #define NL "\n" static char *user_global_server_docu(void) { static char docu[] = "The RFC library will call this function if any unknown" NL "RFC function should be executed in this RFC server program." NL ; return docu; } SV* my_accept( SV* sv_conn, SV* sv_docu, SV* sv_ifaces, SV* sv_saprfc ) { /* initialized data */ static RFC_ENV env; RFC_ERROR_INFO_EX error_info; RFC_HANDLE handle; RFC_RC rc; RFC_FUNCTIONNAME funcname; SV* sv_iface; SV* sv_wait; SV* sv_is_trfc; RFC_INT wtime = 0; RFC_INT ntotal, ninit, nready, nbusy; char gwserv[8]; /* * install error handler */ global_saprfc = sv_saprfc; p_saprfc = (HV*)SvRV( sv_saprfc ); env.errorhandler = rfc_error; RfcEnvironment( &env ); /* * accept connection * * (command line argv must be passed to RfcAccept) */ /* fprintf(stderr, "The connection string is: %s\n", SvPV(sv_conn, SvCUR(sv_conn))); */ /* get the tRFC indicator */ sv_is_trfc = (SV*) *hv_fetch(p_saprfc, (char *) "TRFC", 4, FALSE); handle = RfcAcceptExt( SvPV(sv_conn, SvCUR(sv_conn)) ); /* fprintf(stderr, "what is my handle: %d\n", handle); */ sprintf(gwserv, "%d", SvIV((SV*) *hv_fetch(p_saprfc, (char *) "GWSERV", 6, FALSE))); rc = RfcCheckRegisterServer( SvPV((SV*) *hv_fetch(p_saprfc, (char *) "TPNAME", 6, FALSE), PL_na), SvPV((SV*) *hv_fetch(p_saprfc, (char *) "GWHOST", 6, FALSE), PL_na), gwserv, &ntotal, &ninit, &nready, &nbusy, &error_info); if (rc != RFC_OK) { /* fprintf(stderr, "\nGroup Error group %d\n", error_info.group); fprintf(stderr, "Key %s\n", error_info.key); fprintf(stderr, "Message %s\n\n", error_info.message); */ hv_store(p_saprfc, (char *) "ERROR", 5, newSVpvf("\nGroup Error group %d\nKey %s\nMessage %s\n\n", error_info.group, error_info.key, error_info.message), 0); return newSViv(-1); } /* obscure error when SAP is starting up but still get RFC_OK above */ if (ntotal == 0) { hv_store(p_saprfc, (char *) "ERROR", 5, newSVpvf("\nGroup Error group 102\nKey RFC_ERROR_COMMUNICATION\nMessage Error connecting to the gateway - no registered servers found\n\n"), 0); return newSViv(-1); } /* fprintf(stderr, "\nNo. registered servers : %d", ntotal); fprintf(stderr, "\nNo. registered servers in INIT-state : %d", ninit); fprintf(stderr, "\nNo. registered servers in READY-state: %d", nready); fprintf(stderr, "\nNo. registered servers in BUSY-state : %d", nbusy); */ if (SvTRUE(sv_is_trfc)) { /* Install transaction control */ RfcInstallTransactionControl((RFC_ON_CHECK_TID) TID_check, (RFC_ON_COMMIT) TID_commit, (RFC_ON_ROLLBACK) TID_rollback, (RFC_ON_CONFIRM_TID) TID_confirm); } /* * static function to install offered function modules - RFC_DOCU */ sv_store_docu = sv_docu; rc = install_docu(handle); if( rc != RFC_OK ) { RfcAbort( handle, "Initialisation error" ); hv_store(p_saprfc, (char *) "ERROR", 5, newSVpvf("Initialisation error in the gateway"), 0); return newSViv(-1); } p_iface_hash = (HV*)SvRV( sv_ifaces ); #ifdef SAPonNT /* if one uses rfcexec as a bootstrap to start the * RFC COM support features, one need to initialize * Win32's COM routines * we discared the return value since there are few * users for this scenario. If this call fails the * follwing COM calls will break anyway, so that users * which do need this call will not go far. * for users, which do not need this call, * it would be unfortunate to stop here */ /* Remove this temporarily to fix compiler problems for WIN32 (void)CoInitialize(NULL); */ #endif /* * Setup the wait value * */ sv_callback = *hv_fetch(p_saprfc, (char *) "CALLBACK", 8, FALSE); sv_wait = *hv_fetch(p_saprfc, (char *) "WAIT", 4, FALSE); if (SvTRUE(sv_wait)) wtime = SvIV(sv_wait); else wtime = RFC_WAIT_TIME; /* global handler for tRFC */ if (SvTRUE(sv_is_trfc)) { rc = RfcInstallFunction(name_user_global_server, (RFC_ONCALL) user_global_server, user_global_server_docu()); if( rc != RFC_OK ) { fprintf(stderr, "\nERROR: Install %s rfc_rc = %d", name_user_global_server, rc); RfcAbort( handle, "Cant install global tRFC handler" ); return newSViv(-1); } } /* fprintf(stderr, "The Wait time is: %d \n", wtime); */ /* * enter main loop */ do { /* fprintf(stderr, "going to wait ...\n"); */ rc = RfcWaitForRequest(handle, wtime); /* fprintf(stderr, "done the wait: %d \n", rc); */ /* needs to get an RFC_OK or RFC_RETRY */ if (rc == RFC_RETRY){ /* do event loop callback here for interloop breakout */ /* fprintf(stderr, "got into the retry...\n"); */ rc = loop_callback(sv_callback, sv_saprfc); continue; } /* short circuit here for tRFC */ if (SvTRUE(sv_is_trfc)) { rc = RfcDispatch(handle); /* fprintf(stderr, "done the dispatch: %d \n", rc); */ continue; } /* this will block until a straight RFC call is made */ if (rc == RFC_OK) rc = RfcGetName(handle, funcname); if (rc != RFC_OK){ /* fprintf(stderr, "RFC connection failure code: %d \n", rc); */ hv_store(p_saprfc, (char *) "ERROR", 5, newSVpvf("RFC connection failure code: %d", rc), 0); continue; } /* check at this point for registered functions */ if ( ! hv_exists(p_iface_hash, funcname, strlen(funcname)) ){ fprintf(stderr, "the MISSING Function Name is: %s\n", funcname); RfcRaise( handle, "FUNCTION_MISSING" ); /* do event callback to intertwine other events */ rc = loop_callback(sv_callback, sv_saprfc); continue; } /* pass in the interface to be handled */ sv_iface = *hv_fetch(p_iface_hash, funcname, strlen(funcname), FALSE); handle_request(handle, sv_iface); /* fprintf(stderr, "round the loop ...\n"); */ /* do event callback to intertwine other events */ rc = loop_callback(sv_callback, sv_saprfc); } while( rc == RFC_OK || rc == RFC_RETRY ); /* * connection was closed by the client : * also close connection and terminate */ RfcClose( handle ); #ifdef SAPonNT /* Remove this temporarily to fix compiler problems for WIN32 (void)CoUninitialize(); */ #endif return newSViv(rc); } /* main */ static RFC_RC install_docu( RFC_HANDLE handle ) { RFC_RC rc; /* * install the function modules offered * * the documentation texts are placed in static memory * within some static functions to keep things readable. */ /* * RFC_DOCU interface */ rc = RfcInstallFunction("RFC_DOCU", do_docu, do_docu_docu() ); if( rc != RFC_OK ) return rc; return RFC_OK; } /* install_docu */ /*====================================================================*/ /* */ /* Get specific info about an RFC connection */ /* */ /*====================================================================*/ void get_attributes(RFC_HANDLE rfc_handle, HV* hv_sysinfo) { RFC_ATTRIBUTES rfc_attributes; RFC_RC rc; hv_clear(hv_sysinfo); rc = RfcGetAttributes(rfc_handle, &rfc_attributes); if (rc != RFC_OK) return; hv_store(hv_sysinfo, "dest", 4, newSVpv(rfc_attributes.dest, strlen(rfc_attributes.dest)), 0); hv_store(hv_sysinfo, "localhost", 9, newSVpv(rfc_attributes.own_host, strlen(rfc_attributes.own_host)), 0); if (rfc_attributes.rfc_role == RFC_ROLE_CLIENT) { if (rfc_attributes.partner_type == RFC_SERVER_EXT) hv_store(hv_sysinfo, "servprogname", 12, newSVpv(rfc_attributes.partner_host, strlen(rfc_attributes.partner_host)), 0); else if (rfc_attributes.partner_type == RFC_SERVER_EXT_REG) hv_store(hv_sysinfo, "servprogid", 10, newSVpv(rfc_attributes.partner_host, strlen(rfc_attributes.partner_host)), 0); else hv_store(hv_sysinfo, "partnerhost", 11, newSVpv(rfc_attributes.partner_host, strlen(rfc_attributes.partner_host)), 0); } else hv_store(hv_sysinfo, "partnerhost", 11, newSVpv(rfc_attributes.partner_host, strlen(rfc_attributes.partner_host)), 0); hv_store(hv_sysinfo, "sysnr", 5, newSVpv(rfc_attributes.systnr, strlen(rfc_attributes.systnr)), 0); hv_store(hv_sysinfo, "sysid", 5, newSVpv(rfc_attributes.sysid, strlen(rfc_attributes.sysid)), 0); hv_store(hv_sysinfo, "mandt", 5, newSVpv(rfc_attributes.client, strlen(rfc_attributes.client)), 0); hv_store(hv_sysinfo, "user", 4, newSVpv(rfc_attributes.user, strlen(rfc_attributes.user)), 0); hv_store(hv_sysinfo, "lang", 4, newSVpv(rfc_attributes.language, strlen(rfc_attributes.language)), 0); hv_store(hv_sysinfo, "isolang", 7, newSVpv(rfc_attributes.ISO_language, strlen(rfc_attributes.ISO_language)), 0); if (rfc_attributes.trace == 'X') hv_store(hv_sysinfo, "trace", 5, newSVpv("ON", 2), 0); else hv_store(hv_sysinfo, "trace", 5, newSVpv("OFF", 3), 0); hv_store(hv_sysinfo, "localcodepage", 13, newSVpv(rfc_attributes.own_codepage, strlen(rfc_attributes.own_codepage)), 0); hv_store(hv_sysinfo, "partnercodepage", 15, newSVpv(rfc_attributes.partner_codepage, strlen(rfc_attributes.partner_codepage)), 0); if (rfc_attributes.rfc_role == RFC_ROLE_CLIENT) hv_store(hv_sysinfo, "rfcrole", 7, newSVpv("External RFC Client", strlen("External RFC Client")), 0); else if (rfc_attributes.own_type == RFC_SERVER_EXT) hv_store(hv_sysinfo, "rfcrole", 7, newSVpv("External RFC Server, started by SAP gateway", strlen("External RFC Server, started by SAP gateway")), 0); else hv_store(hv_sysinfo, "rfcrole", 7, newSVpv("External RFC Server, registered at SAP gateway", strlen("External RFC Server, registered at SAP gateway")), 0); hv_store(hv_sysinfo, "rel", 3, newSVpv(rfc_attributes.own_rel, strlen(rfc_attributes.own_rel)), 0); if (rfc_attributes.partner_type == RFC_SERVER_R3) hv_store(hv_sysinfo, "rfcpartner", 10, newSVpv("R3", strlen("R3")), 0); else if (rfc_attributes.partner_type == RFC_SERVER_R2) hv_store(hv_sysinfo, "rfcpartner", 10, newSVpv("R2", strlen("R2")), 0); else if (rfc_attributes.rfc_role == RFC_ROLE_CLIENT) { if (rfc_attributes.partner_type == RFC_SERVER_EXT) hv_store(hv_sysinfo, "rfcpartner", 10, newSVpv("External RFC Server, started by SAP gateway", strlen("External RFC Server, started by SAP gateway")), 0); else hv_store(hv_sysinfo, "rfcpartner", 10, newSVpv("External RFC Server, registered at SAP gateway", strlen("External RFC Server, registered at SAP gateway")), 0); } else hv_store(hv_sysinfo, "rfcpartner", 10, newSVpv("External RFC Client", strlen("External RFC Client")), 0); hv_store(hv_sysinfo, "partnerrel", 10, newSVpv(rfc_attributes.partner_rel, strlen(rfc_attributes.partner_rel)), 0); hv_store(hv_sysinfo, "kernelrel", 9, newSVpv(rfc_attributes.kernel_rel, strlen(rfc_attributes.kernel_rel)), 0); hv_store(hv_sysinfo, "convid", 6, newSVpv(rfc_attributes.CPIC_convid, strlen(rfc_attributes.CPIC_convid)), 0); return; } /* * Generic Inbound RFC Request Handler * */ static RFC_RC DLL_CALL_BACK_FUNCTION handle_request( RFC_HANDLE handle, SV* sv_iface ) { char command[256]; RFC_PARAMETER parameter[MAX_PARA]; RFC_TABLE table[MAX_PARA]; RFC_RC rc; RFC_CHAR read_flag = 0; int mode; char * p; char ** exception; int tab_cnt, imp_cnt, exp_cnt, irow, h_index, a_index, i, j; AV* array; HV* h_parms; HV* p_hash; HV* hv_sysinfo; HE* h_entry; SV* h_key; SV* sv_type; SV* sv_result; SV* sv_callback_handler; SV* sv_self; HV* hash = newHV(); tab_cnt = 0; exp_cnt = 0; imp_cnt = 0; /* get the RFC interface definition hash and iterate */ h_parms = (HV*)SvRV( sv_iface ); h_index = hv_iterinit( h_parms ); for (i = 0; i < h_index; i++) { /* grab each parameter hash */ h_entry = hv_iternext( h_parms ); h_key = hv_iterkeysv( h_entry ); /* fprintf(stderr, "processing parameter: %s\n", SvPV(h_key, PL_na)); */ if (strncmp("__HANDLER__", SvPV(h_key, PL_na),11) == 0 || strncmp("__SELF__", SvPV(h_key, PL_na),8) == 0){ continue; } /* fprintf(stderr, "ok want this parameter ...\n"); */ p_hash = (HV*)SvRV( hv_iterval(h_parms, h_entry) ); sv_type = *hv_fetch( p_hash, (char *) "TYPE", 4, FALSE ); /* determine the interface parameter type and build a definition */ switch ( SvIV(sv_type) ){ case RFCIMPORT: /* build an import parameter and allocate space for it to be returned into */ /* fprintf(stderr, "adding import parameter name is: %s\n", SvPV(h_key, PL_na)); */ parameter[imp_cnt].name = make_strdup( h_key ); if ( parameter[imp_cnt].name == NULL ) return 0; parameter[imp_cnt].nlen = strlen( SvPV(h_key, PL_na)); parameter[imp_cnt].addr = make_space( *hv_fetch(p_hash, (char *) "LEN", 3, FALSE) ); parameter[imp_cnt].leng = SvIV( *hv_fetch( p_hash, (char *) "LEN", 3, FALSE ) ); parameter[imp_cnt].type = SvIV( *hv_fetch( p_hash, (char *) "INTYPE", 6, FALSE ) ); ++imp_cnt; break; case RFCTABLE: /* construct a table parameter and copy the table rows on to the table handle */ /* fprintf(stderr, "adding table parameter name is: %s\n", SvPV(h_key, PL_na)); */ table[tab_cnt].name = make_strdup( h_key ); if ( table[tab_cnt].name == NULL ) return 0; table[tab_cnt].nlen = strlen( SvPV(h_key, PL_na)); table[tab_cnt].leng = SvIV( *hv_fetch( p_hash, (char *) "LEN", 3, FALSE ) ); table[tab_cnt].itmode = RFC_ITMODE_BYREFERENCE; table[tab_cnt].type = RFCTYPE_CHAR; /* maybe should be RFCTYPE_BYTE */ tab_cnt++; break; default: /* ignore export parameters */ break; }; }; /* tack on a NULL value parameter to each type to signify that there are no more */ parameter[imp_cnt].name = NULL; parameter[imp_cnt].nlen = 0; parameter[imp_cnt].leng = 0; parameter[imp_cnt].addr = NULL; parameter[imp_cnt].type = 0; table[tab_cnt].name = NULL; table[tab_cnt].ithandle = NULL; table[tab_cnt].nlen = 0; table[tab_cnt].leng = 0; table[tab_cnt].type = 0; /* fprintf(stderr, "going to import parameter data...\n"); */ rc = RfcGetData( handle, parameter, table ); /* fprintf(stderr, "return code: %d\n", rc); */ if( rc != RFC_OK ) return rc; for (imp_cnt = 0; imp_cnt < MAX_PARA; imp_cnt++){ if ( parameter[imp_cnt].name == NULL ){ break; }; /* fprintf(stderr, "getting import parameter: %s \n", parameter[imp_cnt].name); */ if ( parameter[imp_cnt].name != NULL ){ hv_store( hash, parameter[imp_cnt].name, parameter[imp_cnt].nlen, newSVpv( parameter[imp_cnt].addr, parameter[imp_cnt].leng ), 0 ); free(parameter[imp_cnt].name); }; /* fprintf(stderr, " parameter value: %s \n", parameter[imp_cnt].addr); */ parameter[imp_cnt].name = NULL; parameter[imp_cnt].nlen = 0; parameter[imp_cnt].leng = 0; parameter[imp_cnt].type = 0; if ( parameter[imp_cnt].addr != NULL ){ free(parameter[imp_cnt].addr); }; parameter[imp_cnt].addr = NULL; }; /* retrieve the values of the table parameters and free up the memory */ for (tab_cnt = 0; tab_cnt < MAX_PARA; tab_cnt++){ if ( table[tab_cnt].name == NULL ){ break; }; /* fprintf(stderr, "getting table parameter: %s \n", table[tab_cnt].name); */ if ( table[tab_cnt].name != NULL ){ #ifdef DOIBMWKRND hv_store( hash, table[tab_cnt].name, table[tab_cnt].nlen, newRV_noinc( array = newAV() ), 0); #else hv_store( hash, table[tab_cnt].name, table[tab_cnt].nlen, newRV_noinc( (SV*) array = newAV() ), 0); #endif /* grab each table row and push onto an array */ if (table[tab_cnt].ithandle != NULL){ /* fprintf(stderr, "going to check count\n"); fprintf(stderr, "the table count is: %d \n", ItFill(table[tab_cnt].ithandle)); */ for (irow = 1; irow <= ItFill(table[tab_cnt].ithandle); irow++){ av_push( array, newSVpv( ItGetLine( table[tab_cnt].ithandle, irow ), table[tab_cnt].leng ) ); }; }; free(table[tab_cnt].name); }; table[tab_cnt].name = NULL; if ( table[tab_cnt].ithandle != NULL ){ ItFree( table[tab_cnt].ithandle ); }; table[tab_cnt].ithandle = NULL; table[tab_cnt].nlen = 0; table[tab_cnt].leng = 0; table[tab_cnt].type = 0; }; /* fprintf(stderr, "got data - now do callback\n"); */ sv_callback_handler = *hv_fetch(h_parms, (char *) "__HANDLER__", 11, FALSE); sv_self = *hv_fetch(h_parms, (char *) "__SELF__", 8, FALSE); /* get the systeminfo of the current connection */ hv_sysinfo = (HV*)SvRV(*hv_fetch((HV*)SvRV(sv_self), (char *) "SYSINFO", 7, FALSE)); get_attributes(handle, hv_sysinfo); sv_result = call_handler( sv_callback_handler, sv_self, newRV_noinc( (SV*) hash) ); /* if( rc != RFC_OK ) return rc; */ /* fprintf(stderr, "Result is: %s \n", SvPV(sv_result, PL_na)); */ /* get the RFC interface definition hash and iterate */ h_parms = (HV*)SvRV( sv_result ); h_index = hv_iterinit( h_parms ); /* fprintf(stderr, "processing parameters: %d ...\n", h_index); */ exp_cnt = 0; tab_cnt = 0; for (i = 0; i < h_index; i++) { /* grab each parameter hash */ h_entry = hv_iternext( h_parms ); h_key = hv_iterkeysv( h_entry ); /* Check for a serious error */ if (strncmp("__EXCEPTION__", SvPV(h_key, PL_na),13) == 0){ sv_type = (SV*) hv_iterval(h_parms, h_entry); /* fprintf(stderr, "Got an exception: %s \n", SvPV(sv_type, PL_na)); */ RfcRaise( handle, SvPV(sv_type, PL_na) ); return 0; } /* fprintf(stderr, "processing parameter: %s\n", SvPV(h_key, PL_na)); */ if (strncmp("__HANDLER__", SvPV(h_key, PL_na),11) == 0 || strncmp("__SELF__", SvPV(h_key, PL_na),8) == 0){ /* fprintf(stderr, "dont want the handler...\n"); */ continue; } /* fprintf(stderr, "ok want this parameter ...\n"); */ p_hash = (HV*)SvRV( hv_iterval(h_parms, h_entry) ); sv_type = *hv_fetch( p_hash, (char *) "TYPE", 4, FALSE ); /* determine the interface parameter type and build a definition */ switch ( SvIV(sv_type) ){ case RFCEXPORT: /* build an export parameter and pass the value onto the structure */ parameter[exp_cnt].name = make_strdup( h_key ); if ( parameter[exp_cnt].name == NULL ) return 0; parameter[exp_cnt].nlen = strlen( SvPV(h_key, PL_na)); parameter[exp_cnt].addr = make_copy( *hv_fetch(p_hash, (char *) "VALUE", 5, FALSE), *hv_fetch(p_hash, (char *) "LEN", 3, FALSE) ); parameter[exp_cnt].leng = SvIV( *hv_fetch( p_hash, (char *) "LEN", 3, FALSE ) ); parameter[exp_cnt].type = SvIV( *hv_fetch( p_hash, (char *) "INTYPE", 6, FALSE ) ); ++exp_cnt; break; case RFCTABLE: /* construct a table parameter and copy the table rows on to the table handle */ /* fprintf(stderr, "adding table parameter name is: %s\n", SvPV(h_key, PL_na)); */ table[tab_cnt].name = make_strdup( h_key ); if ( table[tab_cnt].name == NULL ) return 0; table[tab_cnt].nlen = strlen( SvPV(h_key, PL_na)); table[tab_cnt].leng = SvIV( *hv_fetch( p_hash, (char *) "LEN", 3, FALSE ) ); table[tab_cnt].itmode = RFC_ITMODE_BYREFERENCE; table[tab_cnt].type = RFCTYPE_CHAR; /* maybe should be RFCTYPE_BYTE */ table[tab_cnt].ithandle = ItCreate( table[tab_cnt].name, SvIV( *hv_fetch( p_hash, (char *) "LEN", 3, FALSE ) ), 0 , 0 ); if ( table[tab_cnt].ithandle == NULL ) return 0; array = (AV*) SvRV( *hv_fetch( p_hash, (char *) "VALUE", 5, FALSE ) ); a_index = av_len( array ); /* fprintf(stderr, "the array contains: %d \n", a_index); */ /* fprintf(stderr, "the array id is: %d \n", tab_cnt); */ for (j = 0; j <= a_index; j++) { Copy( SvPV( *av_fetch( array, j, FALSE ), PL_na ), ItAppLine( table[tab_cnt].ithandle ), table[tab_cnt].leng, char ); }; tab_cnt++; break; default: /* ignore import parameters */ break; }; }; /* tack on a NULL value parameter to each type to signify that there are no more */ parameter[exp_cnt].name = NULL; parameter[exp_cnt].nlen = 0; parameter[exp_cnt].leng = 0; parameter[exp_cnt].addr = NULL; parameter[exp_cnt].type = 0; table[tab_cnt].name = NULL; table[tab_cnt].ithandle = NULL; table[tab_cnt].nlen = 0; table[tab_cnt].leng = 0; table[tab_cnt].type = 0; /* fprintf(stderr, "sending\n"); */ rc = RfcSendData( handle, parameter, table ); /* fprintf(stderr, "after send\n"); */ return rc; } SV* call_handler(SV* sv_callback_handler, SV* sv_iface, SV* sv_data) { int result; SV* sv_rvalue; dSP; /* initialising the argument stack */ ENTER; SAVETMPS; PUSHMARK(SP); /* push the pkt onto the stack */ XPUSHs( sv_callback_handler ); XPUSHs( sv_iface ); XPUSHs( sv_2mortal( sv_data ) ); /* stash the stack point */ PUTBACK; /*result = perl_call_sv(sv_callback_handler, G_EVAL | G_SCALAR ); */ result = perl_call_pv("SAP::Rfc::Handler", G_EVAL | G_SCALAR ); /* disassemble the results off the argument stack */ if(SvTRUE(ERRSV)) fprintf(stderr, "RFC callback - perl call errored: %s", SvPV(ERRSV,PL_na)); SPAGAIN; /* was this handled or passed? */ /* fprintf(stderr, "results are: %d \n", result); */ if (result > 0){ sv_rvalue = newSVsv(POPs); } else { sv_rvalue = newSViv(0); } PUTBACK; FREETMPS; LEAVE; return sv_rvalue; } static RFC_RC DLL_CALL_BACK_FUNCTION do_docu( RFC_HANDLE handle ) { RFC_PARAMETER parameter[1]; RFC_TABLE table[2]; RFC_RC rc; RFC_CHAR read_flag = 0; AV* array; int a_index; int mode; char *p; int i; parameter[0].name = NULL; parameter[0].nlen = 0; parameter[0].leng = 0; parameter[0].addr = NULL; parameter[0].type = 0; table[0].name = malloc( 5 ); memset(table[0].name, 0, 5); Copy("DOCU", table[0].name, 4, char); table[0].nlen = 4; table[0].type = RFCTYPE_CHAR; table[0].leng = 80; table[0].itmode = RFC_ITMODE_BYREFERENCE; table[1].name = NULL; table[1].ithandle = NULL; table[1].nlen = 0; table[1].leng = 0; table[1].type = 0; rc = RfcGetData( handle, parameter, table ); if( rc != RFC_OK ) return rc; parameter[0].name = NULL; parameter[0].nlen = 0; parameter[0].leng = 0; parameter[0].addr = NULL; parameter[0].type = 0; table[0].name = malloc( 5 ); memset(table[0].name, 0, 5); Copy("DOCU", table[0].name, 4, char); table[0].nlen = 4; table[0].type = RFCTYPE_CHAR; table[0].leng = 80; table[0].itmode = RFC_ITMODE_BYREFERENCE; table[1].name = NULL; table[1].ithandle = NULL; table[1].nlen = 0; table[1].leng = 0; table[1].type = 0; table[0].ithandle = ItCreate( table[0].name, 80, 0 , 0 ); /* get the documentation out of the array */ array = (AV*) SvRV( sv_store_docu ); a_index = av_len( array ); for (i = 0; i <= a_index; i++) { Copy( SvPV( *av_fetch( array, i, FALSE ), PL_na ), ItAppLine( table[0].ithandle ), table[0].leng, char ); }; rc = RfcSendData( handle, parameter, table ); return rc; } /* * * function module documentation * */ /* * insert newline characters to start a new line */ #undef NL #define NL "\n" static char * do_docu_docu( void ) { static char docu[] = "This is the override function for the standard self " NL "discovery documentation function. " NL "" NL "IMPORTING" NL "TABLES" NL " DOCU C(80)" NL " internal table contains the documentaiton data. " NL ; return docu; } MODULE = SAP::Rfc PACKAGE = SAP::Rfc PROTOTYPES: DISABLE SV * MyConnect (sv_handle) SV * sv_handle SV * MyDisconnect (sv_handle) SV * sv_handle SV * MyRfcCallReceive (sv_handle, sv_function, iface) SV * sv_handle SV * sv_function SV * iface SV * my_accept (sv_conn, sv_docu, sv_ifaces, sv_saprfc) SV * sv_conn SV * sv_docu SV * sv_ifaces SV * sv_saprfc