/* -*- mode: C; c-file-style: "bsd" -*- */ #include "errors.h" #include "interfaces.h" #include "server.h" #include "types.h" typedef struct _PORBitParam PORBitParam; struct _PORBitParam { CORBA_TypeCode type; CORBA_ParameterMode mode; }; /* Hackaround for breakage in some unstable versions of perl */ #if (PERL_VERSION == 5) && (PERL_SUBVERSION > 57) && defined (USE_THREADS) # undef ERRSV # define ERRSV (aTHX->errsv) #else # ifndef ERRSV # define ERRSV GvSV(errgv) # endif #endif #define INSTVARS_MAGIC 0x18981972 /* Magic (adopted from DBI) to attach InstVars invisibly to perlobj */ PORBitInstVars * porbit_instvars_add (SV *perlobj) { SV *iv_sv = newSV (sizeof(PORBitInstVars)); PORBitInstVars *iv = (PORBitInstVars *)SvPVX(iv_sv); SV *rv = newRV(iv_sv); /* just needed for sv_bless */ sv_bless (rv, gv_stashpv("CORBA::ORBit::InstVars", TRUE)); sv_free (rv); iv->magic = INSTVARS_MAGIC; iv->servant = NULL; if (SvROK(perlobj)) perlobj = SvRV(perlobj); sv_magic (perlobj, iv_sv, '~' , Nullch, 0); SvREFCNT_dec (iv_sv); /* sv_magic() incremented it */ /* It looks from sv.c like this is now unecessary, but DBI does it * and it shouldn't do any harm */ SvRMAGICAL_on (perlobj); return iv; } PORBitInstVars * porbit_instvars_get (SV *perlobj) { PORBitInstVars *iv = NULL; if (SvROK(perlobj)) perlobj = SvRV(perlobj); if (SvMAGICAL (perlobj)) { MAGIC *mg = mg_find (perlobj, '~'); if (mg) iv = (PORBitInstVars *)SvPVX(mg->mg_obj); } if (iv && (iv->magic == INSTVARS_MAGIC)) return iv; else return NULL; } void porbit_instvars_destroy (PORBitInstVars *instvars) { CORBA_Environment ev; CORBA_exception_init (&ev); assert (instvars->magic == INSTVARS_MAGIC); porbit_servant_destroy (instvars->servant, &ev); if (ev._major != CORBA_NO_EXCEPTION) { warn ("error while destroying servant"); } /* We don't free instvars itself here, because we have stuck * it inside an SV * */ } /* Find a Perl object for this CORBA object. */ SV * porbit_servant_to_sv (PortableServer_Servant servant) { if (servant) { PORBitServant *porbit_servant = (PORBitServant *)servant; return newRV_inc(porbit_servant->perlobj); } /* FIXME: memory leaks? */ return newSVsv(&PL_sv_undef); } static gchar * porbit_get_repoid (SV *perlobj) { char *result; int count; dSP; PUSHMARK(sp); XPUSHs(perlobj); PUTBACK; count = perl_call_method("_repoid", G_SCALAR); SPAGAIN; if (count != 1) /* sanity check */ croak("object->_repoid didn't return 1 argument"); result = g_strdup (POPp); PUTBACK; return result; } static gboolean is_a_recurse (CORBA_InterfaceDef_FullInterfaceDescription *desc, const char *repo_id) { CORBA_unsigned_long i; if (strcmp (desc->id, repo_id) == 0) return TRUE; for (i = 0; i < desc->base_interfaces._length; i++) { PORBitIfaceInfo *info = porbit_find_interface_description(desc->base_interfaces._buffer[i]); if (info) { if (is_a_recurse(info->desc, repo_id)) return TRUE; } } return FALSE; } gboolean porbit_servant_is_a (SV *perlobj, const char *repo_id) { PORBitInstVars *iv; PORBitServant *servant; iv = porbit_instvars_get (perlobj); servant = (PORBitServant *)iv->servant; return is_a_recurse (servant->desc, repo_id); } static PortableServer_Servant porbit_get_orbit_servant (SV *perlobj) { PortableServer_Servant result; int count; dSP; PUSHMARK(sp); XPUSHs(perlobj); PUTBACK; count = perl_call_method("_porbit_servant", G_SCALAR); SPAGAIN; if (count != 1) /* sanity check */ croak("object->_porbit_servant didn't return 1 argument"); result = (PortableServer_Servant) POPi; PUTBACK; return result; } PortableServer_Servant porbit_sv_to_servant (SV *perlobj) { PORBitInstVars *iv; if (!SvOK(perlobj)) return NULL; iv = porbit_instvars_get (perlobj); if (!iv && !sv_derived_from (perlobj, "PortableServer::ServantBase")) croak ("Argument is not a PortableServer::ServantBase"); if (!iv) { iv = porbit_instvars_add (perlobj); iv->servant = porbit_get_orbit_servant (perlobj); } return iv->servant; } void porbit_servant_ref (PortableServer_Servant servant) { PORBitServant *porbit_servant = (PORBitServant *)servant; SvREFCNT_inc (porbit_servant->perlobj); } void porbit_servant_unref (PortableServer_Servant servant) { PORBitServant *porbit_servant = (PORBitServant *)servant; SvREFCNT_dec (porbit_servant->perlobj); } PortableServer_ObjectId * porbit_sv_to_objectid (SV *sv) { STRLEN len; char *str; PortableServer_ObjectId *result; str = SvPV(sv, len); result = (PortableServer_ObjectId *)CORBA_sequence_octet__alloc(); result->_length = len + 1; result->_buffer = CORBA_octet_allocbuf (result->_length); result->_release = CORBA_TRUE; memcpy (result->_buffer, str, len); result->_buffer[len] = '\0'; return result; } SV * porbit_objectid_to_sv (PortableServer_ObjectId *oid) { SV *sv; char *strbuf; sv = newSV(oid->_length); SvCUR_set(sv, oid->_length-1); SvPOK_on (sv); strbuf = SvPVX(sv); memcpy (strbuf, oid->_buffer, oid->_length - 1); strbuf[oid->_length - 1] = '\0'; return sv; } /********************* * Stub calling code * *********************/ /* Utility function used for error reporting */ static char * servant_classname (PORBitServant *servant) { return HvNAME(SvSTASH(servant->perlobj)); } static SV * porbit_call_method (PORBitServant *servant, const char *name, int return_items) { int return_count; dSP; GV *throwngv = gv_fetchpv("Error::THROWN", TRUE, SVt_PV); save_scalar (throwngv); /* assume enclosing scope */ sv_setsv (GvSV(throwngv), &PL_sv_undef); return_count = perl_call_method ((char *)name, G_EVAL | ((return_items == 0) ? G_VOID : ((return_items == 1) ? G_SCALAR : G_ARRAY))); SPAGAIN; if (SvOK(ERRSV) && (SvROK(ERRSV) || SvTRUE(ERRSV))) { /* an error or exception occurred */ while (return_count--) /* empty stack */ (void)POPs; PUTBACK; if (SvOK(GvSV(throwngv))) { /* exception */ return newSVsv(GvSV(throwngv)); } else { warn ("Error occured in implementation '%s::%s': %s", servant_classname (servant), name, SvPV(ERRSV, PL_na)); return porbit_system_except("IDL:omg.org/CORBA/UNKNOWN:1.0", 0, CORBA_COMPLETED_MAYBE); } } if (return_count != return_items) { if (return_items && (PL_dowarn & G_WARN_ON)) warn("Implementation '%s::%s' returned %d items and should return %d items", servant_classname (servant), name, return_count, return_items); if (return_count > return_items) { while (return_count > return_items) { (void)POPs; return_count--; } PUTBACK; } else { EXTEND(SP, return_items - return_count); while (return_count < return_items) { (void)PUSHs(&PL_sv_undef); return_count++; } PUTBACK; } } return NULL; } static void call_implementation (PORBitServant *servant, GIOPRecvBuffer *recv_buffer, CORBA_Environment *ev, const char *name, PORBitParam *params, CORBA_unsigned_long nparams, CORBA_ExcDescriptionSeq *exceptions) { dSP; GIOPSendBuffer *send_buffer = NULL; CORBA_unsigned_long i; AV *inout_args = NULL; SV *error_sv; int exception_level = 0; int stack_index; int inout_index; int return_items = 0; ENTER; SAVETMPS; PUSHMARK(sp); XPUSHs(sv_2mortal(newRV_inc(servant->perlobj))); for (i=0; i < nparams; i++) { if (params[i].mode == CORBA_PARAM_IN || params[i].mode == CORBA_PARAM_INOUT) { SV *arg; /* We need the PUTBACK/SPAGAIN here, since the call to * porbit_get_sv might want the stack */ PUTBACK; arg = porbit_get_sv (recv_buffer, params[i].type); SPAGAIN; if (!arg) { error_sv = porbit_system_except("IDL:omg.org/CORBA/BAD_PARAM:1.0", 0, CORBA_COMPLETED_NO); goto cleanup; } if (params[i].mode == CORBA_PARAM_INOUT) { if (inout_args == NULL) inout_args = newAV(); av_push(inout_args,arg); XPUSHs(sv_2mortal(newRV_inc(arg))); } else { XPUSHs(sv_2mortal(arg)); } } else { return_items++; } } PUTBACK; error_sv = porbit_call_method (servant, name, return_items); if (error_sv) goto clean2; /* The call succeeded -- decode the results */ SPAGAIN; if (!recv_buffer->message.u.request.response_expected) goto cleanup; send_buffer = giop_send_reply_buffer_use(GIOP_MESSAGE_BUFFER(recv_buffer)->connection, NULL, recv_buffer->message.u.request.request_id, CORBA_NO_EXCEPTION); if (!send_buffer) { warn ("Lost connection to client while sending result of call to %s::%s", servant_classname (servant), name); goto cleanup; } stack_index = 1; inout_index = 0; for (i = 0; i < nparams; i++) { CORBA_boolean success; switch (params[i].mode) { case CORBA_PARAM_IN: continue; case CORBA_PARAM_OUT: success = porbit_put_sv (send_buffer, params[i].type, *(sp-return_items+stack_index++)); break; case CORBA_PARAM_INOUT: success = porbit_put_sv (send_buffer, params[i].type, *av_fetch(inout_args, inout_index++, 0)); break; } if (!success) { warn ("Error marshalling result of call to %s::%s", servant_classname (servant), name); error_sv = porbit_system_except("IDL:omg.org/CORBA/MARSHAL:1.0", 0, CORBA_COMPLETED_YES); goto cleanup; } } giop_send_buffer_write (send_buffer); cleanup: sp -= return_items; PUTBACK; clean2: if (inout_args) { av_undef (inout_args); inout_args = NULL; } if (send_buffer) { giop_send_buffer_unuse (send_buffer); send_buffer = NULL; } if (error_sv) { SV *new_error; CORBA_exception_type type; exception_level++; if (exception_level > 2) { warn ("Panic: recursion marshalling error from %s::%s", servant_classname (servant), name); SvREFCNT_dec (error_sv); goto out; } if (sv_derived_from(error_sv, "CORBA::UserException")) type = CORBA_USER_EXCEPTION; else if (sv_derived_from(error_sv, "CORBA::SystemException")) type = CORBA_SYSTEM_EXCEPTION; else { warn ("Exception thrown from %s::%s must derive from CORBA::UserException or CORBA::SystemException", servant_classname (servant), name); SvREFCNT_dec (error_sv); error_sv = porbit_system_except("IDL:omg.org/CORBA/UNKNOWN:1.0", 0, CORBA_COMPLETED_MAYBE); goto cleanup; } send_buffer = giop_send_reply_buffer_use(GIOP_MESSAGE_BUFFER(recv_buffer)->connection, NULL, recv_buffer->message.u.request.request_id, type); if (!send_buffer) { warn ("Lost connection to client while sending exception from call to %s::%s.\n %s", servant_classname (servant), name, SvPV (error_sv, PL_na)); SvREFCNT_dec (error_sv); goto out; } new_error = porbit_put_exception (send_buffer, NULL, error_sv, exceptions); SvREFCNT_dec (error_sv); if (new_error) { error_sv = new_error; goto cleanup; } giop_send_buffer_write (send_buffer); giop_send_buffer_unuse (send_buffer); } out: FREETMPS; LEAVE; } static void porbit_attr_set_skel (PORBitServant *servant, GIOPRecvBuffer *recv_buffer, CORBA_Environment *ev, gpointer implementation) { PORBitParam param; gchar *name; CORBA_AttributeDescription *attr = implementation; param.type = attr->type; param.mode = CORBA_PARAM_IN; name = g_strconcat ("_set_", attr->name, NULL); call_implementation (servant, recv_buffer, ev, name, ¶m, 1, NULL); g_free (name); } static void porbit_attr_get_skel (PORBitServant *servant, GIOPRecvBuffer *recv_buffer, CORBA_Environment *ev, gpointer implementation) { gchar *name; PORBitParam param; CORBA_AttributeDescription *attr = implementation; param.type = attr->type; param.mode = CORBA_PARAM_OUT; name = g_strconcat ("_get_", attr->name, NULL); call_implementation (servant, recv_buffer, ev, name, ¶m, 1, NULL); g_free (name); } static void porbit_operation_skel (PORBitServant *servant, GIOPRecvBuffer *recv_buffer, CORBA_Environment *ev, gpointer implementation) { CORBA_OperationDescription *opr = implementation; PORBitParam *params; CORBA_unsigned_long nparams = opr->parameters._length; CORBA_unsigned_long i,j; if (opr->result->kind != CORBA_tk_void) nparams++; params = g_new (PORBitParam, nparams); i = 0; if (opr->result->kind != CORBA_tk_void) { params[0].type = opr->result; params[i].mode = CORBA_PARAM_OUT; i++; } for (j=0; jparameters._length; j++) { params[i].type = opr->parameters._buffer[j].type; params[i].mode = opr->parameters._buffer[j].mode; i++; } call_implementation (servant, recv_buffer, ev, opr->name, params, nparams, &opr->exceptions); g_free (params); } static CORBA_OperationDescription * find_operation (CORBA_InterfaceDef_FullInterfaceDescription *d, const char *name) { CORBA_unsigned_long i; for (i=0; i < d->operations._length; i++) { if (strcmp (name, d->operations._buffer[i].name) == 0) return &d->operations._buffer[i]; } for (i = 0; i < d->base_interfaces._length; i++) { PORBitIfaceInfo *info = porbit_find_interface_description(d->base_interfaces._buffer[i]); if (info) { CORBA_OperationDescription *res = find_operation(info->desc, name); if (res) return res; } } return NULL; } static CORBA_AttributeDescription * find_attribute (CORBA_InterfaceDef_FullInterfaceDescription *d, const char *name, bool set) { CORBA_unsigned_long i; for (i=0; i < d->attributes._length; i++) { if (!strcmp (name, d->attributes._buffer[i].name)) { if (!set || d->attributes._buffer[i].mode != CORBA_ATTR_READONLY) return &d->attributes._buffer[i]; } } for (i = 0; i < d->base_interfaces._length; i++) { PORBitIfaceInfo *info = porbit_find_interface_description(d->base_interfaces._buffer[i]); if (info) { CORBA_AttributeDescription *res = find_attribute(info->desc, name, set); if (res) return res; } } return NULL; } static ORBitSkeleton porbit_get_skel (PORBitServant *servant, GIOPRecvBuffer *recv_buffer, gpointer *impl) { gchar *opname = recv_buffer->message.u.request.operation; if (strncmp(opname, "_set_", 5) == 0) { CORBA_AttributeDescription *attr_desc = find_attribute(servant->desc, opname+5, TRUE); if (attr_desc) { *impl = attr_desc; return (ORBitSkeleton)porbit_attr_set_skel; } } else if (strncmp(opname, "_get_", 5) == 0) { CORBA_AttributeDescription *attr_desc = find_attribute(servant->desc, opname+5, FALSE); if (attr_desc) { *impl = attr_desc; return (ORBitSkeleton)porbit_attr_get_skel; } } else { CORBA_OperationDescription *op_desc = find_operation(servant->desc, opname); if (op_desc) { *impl = op_desc; return (ORBitSkeleton)porbit_operation_skel; } } return (ORBitSkeleton)NULL; } PORBitServant * porbit_servant_create (SV *perlobj, CORBA_Environment *ev) { char *repoid; PORBitIfaceInfo *info; PORBitServant *servant; assert (SvROK(perlobj)); repoid = porbit_get_repoid(perlobj); info = porbit_find_interface_description (repoid); if (!info) { info = porbit_load_contained (NULL, repoid, ev); if (ev->_major != CORBA_NO_EXCEPTION) { g_free (repoid); return NULL; } } g_free (repoid); servant = g_new (PORBitServant, 1); servant->_private = NULL; servant->vepv = NULL; servant->perlobj = SvRV(perlobj); servant->desc = info->desc; PortableServer_ServantBase__init((PortableServer_ServantBase *)servant, ev); if (ev->_major != CORBA_NO_EXCEPTION) goto exception; if (!info->class_id) { info->class_info.relay_call = (ORBit_impl_finder)porbit_get_skel; info->class_info.class_name = info->desc->id; info->class_info.init_local_objref = NULL; info->class_id = ORBit_register_class(&info->class_info); } ORBIT_OBJECT_KEY(servant->_private)->class_info = &info->class_info; exception: if (ev->_major != CORBA_NO_EXCEPTION) { g_free (servant); servant = NULL; } return servant; } void porbit_servant_destroy (PORBitServant *servant, CORBA_Environment *ev) { PortableServer_ServantBase__fini((PortableServer_Servant *)servant, ev); g_free (servant); }