From 79c5d73a71c35f5b16232072a7b52033cb9364cb Mon Sep 17 00:00:00 2001 From: Jelmer Vernooij Date: Fri, 12 Nov 2004 00:48:24 +0000 Subject: r3689: Large number of COM updates: - Work on server side and local COM support (should work, just no example classes yet) - Use vtables so that local and remote calls can be used transparently - Generate 'proxies and stubs' rather then heavily modified code in client.pm and server.pm. proxies (client side code) are generated in proxy.pm, stubs (server side dispatchers) are generated in stubs.pm - Support registering classes and interfaces - DCOM interfaces no longer have to be in the same IDL file as their base interface, which will allow us to split up dcom.idl (This used to be commit 7466947a23985f9bb15209b67880f7b94dc515c8) --- source4/build/pidl/README | 2 + source4/build/pidl/client.pm | 79 ++-------------- source4/build/pidl/header.pm | 45 ++++++--- source4/build/pidl/parser.pm | 9 +- source4/build/pidl/pidl.pl | 13 ++- source4/build/pidl/proxy.pm | 166 ++++++++++++++++++++++++++++++++ source4/build/pidl/server.pm | 13 +-- source4/build/pidl/stub.pm | 221 +++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 455 insertions(+), 93 deletions(-) create mode 100644 source4/build/pidl/proxy.pm create mode 100644 source4/build/pidl/stub.pm (limited to 'source4/build') diff --git a/source4/build/pidl/README b/source4/build/pidl/README index 17a214cd58..c6432a8c0e 100644 --- a/source4/build/pidl/README +++ b/source4/build/pidl/README @@ -21,6 +21,8 @@ parser.pm - Generates pull/push functions for parsing server.pm - Generates server side implementation in C template.pm - Generates stubs in C for server implementation validator.pm - Validates the parse tree +proxy.pm - Generates proxy object for DCOM (client-side) +stub.pm - Generates stub call handler for DCOM (server-side) Other files in this directory are: tables.pl - Generates a table of available interfaces from a list of IDL files diff --git a/source4/build/pidl/client.pm b/source4/build/pidl/client.pm index 549a5d0dd3..015ac05223 100644 --- a/source4/build/pidl/client.pm +++ b/source4/build/pidl/client.pm @@ -11,82 +11,33 @@ my($res); ##################################################################### # parse a function -sub ParseFunction($) +sub ParseFunction($$) { + my $interface = shift; my $fn = shift; my $name = $fn->{NAME}; my $uname = uc $name; - return if (util::has_property($fn, "local")); - - my $objarg; - if (util::has_property($fn, "object")) { - $objarg = "&d->objref->u_objref.u_standard.std.ipid"; - # FIXME: Support custom marshalling - - $res .= " -struct rpc_request *dcerpc_$name\_send(struct dcom_interface *d, TALLOC_CTX *mem_ctx, struct $name *r) -{ - struct dcerpc_pipe *p; - NTSTATUS status = dcom_get_pipe(d, &p); - - if (NT_STATUS_IS_ERR(status)) { - return NULL; - } - - ZERO_STRUCT(r->in.ORPCthis); - r->in.ORPCthis.version.MajorVersion = COM_MAJOR_VERSION; - r->in.ORPCthis.version.MinorVersion = COM_MINOR_VERSION; - -"; - } else { - $objarg = "NULL"; - $res .= " + $res .= " struct rpc_request *dcerpc_$name\_send(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, struct $name *r) -{"; - } +{ - $res.=" if (p->flags & DCERPC_DEBUG_PRINT_IN) { NDR_PRINT_IN_DEBUG($name, r); } - return dcerpc_ndr_request_send(p, $objarg, DCERPC_$uname, mem_ctx, + return dcerpc_ndr_request_send(p, NULL, DCERPC_$uname, mem_ctx, (ndr_push_flags_fn_t) ndr_push_$name, (ndr_pull_flags_fn_t) ndr_pull_$name, r, sizeof(*r)); } -"; - - if (util::has_property($fn, "object")) { - $res .= -" -NTSTATUS dcerpc_$name(struct dcom_interface *d, TALLOC_CTX *mem_ctx, struct $name *r) -{ - struct dcerpc_pipe *p; - NTSTATUS status = dcom_get_pipe(d, &p); - struct rpc_request *req; - - if (NT_STATUS_IS_ERR(status)) { - return status; - } - - "; - $objarg = "d"; - } else { - $res .= -" NTSTATUS dcerpc_$name(struct dcerpc_pipe *p, TALLOC_CTX *mem_ctx, struct $name *r) { struct rpc_request *req; NTSTATUS status; - "; - $objarg = "p"; - } - - $res .= " - req = dcerpc_$name\_send($objarg, mem_ctx, r); + + req = dcerpc_$name\_send(p, mem_ctx, r); if (req == NULL) return NT_STATUS_NO_MEMORY; status = dcerpc_ndr_request_recv(req); @@ -112,22 +63,10 @@ sub ParseInterface($) { my($interface) = shift; my($data) = $interface->{DATA}; + $res = "/* Client functions generated by pidl */\n\n"; foreach my $d (@{$data}) { ($d->{TYPE} eq "FUNCTION") && - ParseFunction($d); - } -} - - -##################################################################### -# parse a parsed IDL structure back into an IDL file -sub Parse($) -{ - my($idl) = shift; - $res = "/* dcerpc client calls generated by pidl */\n\n"; - foreach my $x (@{$idl}) { - ($x->{TYPE} eq "INTERFACE") && - ParseInterface($x); + ParseFunction($interface, $d); } return $res; } diff --git a/source4/build/pidl/header.pm b/source4/build/pidl/header.pm index b981200781..188fdd4f72 100644 --- a/source4/build/pidl/header.pm +++ b/source4/build/pidl/header.pm @@ -298,22 +298,42 @@ sub HeaderFunction($) ##################################################################### # output prototypes for a IDL function -sub HeaderFnProto($) +sub HeaderFnProto($$) { + my $interface = shift; my $fn = shift; my $name = $fn->{NAME}; - my $firstarg = "dcerpc_pipe"; - if (util::has_property($fn, "object")) { - $firstarg = "dcom_interface"; - } - $res .= "void ndr_print_$name(struct ndr_print *, const char *, int, struct $name *);\n"; - $res .= "struct rpc_request *dcerpc_$name\_send(struct $firstarg *, TALLOC_CTX *, struct $name *);\n"; - $res .= "NTSTATUS dcerpc_$name(struct $firstarg *, TALLOC_CTX *, struct $name *);\n"; + + if (util::has_property($interface, "object")) { + $res .= "NTSTATUS dcom_$interface->{NAME}_$name (struct dcom_interface_p *, TALLOC_CTX *mem_ctx, struct $name *);\n"; + } else { + $res .= "NTSTATUS dcerpc_$name(struct dcerpc_pipe *, TALLOC_CTX *, struct $name *);\n"; + $res .= "struct rpc_request *dcerpc_$name\_send(struct dcerpc_pipe *, TALLOC_CTX *, struct $name *);\n"; + } $res .= "\n"; } + +##################################################################### +# generate vtable structure for DCOM interface +sub HeaderVTable($) +{ + my $interface = shift; + $res .= "struct dcom_$interface->{NAME}_vtable {\n"; + if (defined($interface->{BASE})) { + $res .= "\tstruct dcom_$interface->{BASE}\_vtable base;\n"; + } + + my $data = $interface->{DATA}; + foreach my $d (@{$data}) { + $res .= "\tNTSTATUS (*$d->{NAME}) (struct dcom_interface_p *, TALLOC_CTX *mem_ctx, struct $d->{NAME} *);\n" if ($d->{TYPE} eq "FUNCTION"); + } + $res .= "};\n\n"; +} + + ##################################################################### # parse the interface definitions sub HeaderInterface($) @@ -379,11 +399,14 @@ sub HeaderInterface($) HeaderTypedef($d); ($d->{TYPE} eq "TYPEDEF") && HeaderTypedefProto($d); - ($d->{TYPE} eq "FUNCTION") && + ($d->{TYPE} eq "FUNCTION") && HeaderFunction($d); - ($d->{TYPE} eq "FUNCTION") && - HeaderFnProto($d); + ($d->{TYPE} eq "FUNCTION") && + HeaderFnProto($interface, $d); } + + (util::has_property($interface, "object")) && + HeaderVTable($interface); $res .= "#endif /* _HEADER_NDR_$interface->{NAME} */\n"; } diff --git a/source4/build/pidl/parser.pm b/source4/build/pidl/parser.pm index fd95f91665..e55e5ecd9c 100644 --- a/source4/build/pidl/parser.pm +++ b/source4/build/pidl/parser.pm @@ -9,6 +9,7 @@ package IdlParser; use strict; use client; +use proxy; use needed; # the list of needed functions @@ -1594,11 +1595,15 @@ sub Parse($$) if ($x->{TYPE} eq "INTERFACE") { needed::BuildNeeded($x); ParseInterface($x); + + if (util::has_property($x, "object")) { + pidl IdlProxy::ParseInterface($x); + } else { + pidl IdlClient::ParseInterface($x); + } } } - pidl IdlClient::Parse($idl); - close(OUT); } diff --git a/source4/build/pidl/pidl.pl b/source4/build/pidl/pidl.pl index fcd189d8cd..bb7f1f8ed6 100755 --- a/source4/build/pidl/pidl.pl +++ b/source4/build/pidl/pidl.pl @@ -17,6 +17,7 @@ use idl; use dump; use header; use server; +use stub; use parser; use eparser; use validator; @@ -139,7 +140,17 @@ sub process_file($) if ($opt_server) { my($server) = util::ChangeExtension($output, "_s.c"); - util::FileSave($server, IdlServer::Parse($pidl)); + my $res = ""; + foreach my $x (@{$pidl}) { + next if ($x->{TYPE} ne "INTERFACE"); + + if (util::has_property($x, "object")) { + $res .= IdlStub::ParseInterface($x); + } else { + $res .= IdlServer::ParseInterface($x); + } + } + util::FileSave($server, $res); } if ($opt_parser) { diff --git a/source4/build/pidl/proxy.pm b/source4/build/pidl/proxy.pm new file mode 100644 index 0000000000..b6d9733419 --- /dev/null +++ b/source4/build/pidl/proxy.pm @@ -0,0 +1,166 @@ +################################################### +# DCOM proxy generator +# Copyright jelmer@samba.org 2003 +# released under the GNU GPL + +package IdlProxy; + +use strict; + +my($res); + +sub ParseVTable($$) +{ + my $interface = shift; + my $name = shift; + + # Generate the vtable + $res .="\tstruct dcom_$interface->{NAME}_vtable $name = {"; + + if (defined($interface->{BASE})) { + $res .= "\n\t\t{},"; + } + + my $data = $interface->{DATA}; + + foreach my $d (@{$data}) { + if ($d->{TYPE} eq "FUNCTION") { + $res .= "\n\t\tdcom_proxy_$interface->{NAME}_$d->{NAME}"; + $res .= ","; + } + } + + $res .= "\n\t};\n\n"; +} + +sub ParseRegFunc($) +{ + my $interface = shift; + + $res .= "NTSTATUS dcom_$interface->{NAME}_init(void) +{ + struct dcom_interface iface; +"; + + ParseVTable($interface, "proxy"); + + if (defined($interface->{BASE})) { + $res.= " + const void *base_vtable; + + GUID_from_string(DCERPC_" . (uc $interface->{BASE}) . "_UUID, &iface.base_iid); + + base_vtable = dcom_proxy_vtable_by_iid(&iface.base_iid); + if (base_vtable == NULL) { + return NT_STATUS_FOOBAR; + } + + proxy.base = *((const struct dcom_$interface->{BASE}_vtable *)base_vtable); + "; + } else { + $res .= "\tZERO_STRUCT(iface.base_iid);\n"; + } + + $res.= " + iface.num_methods = DCERPC_" . (uc $interface->{NAME}) . "_CALL_COUNT; + GUID_from_string(DCERPC_" . (uc $interface->{NAME}) . "_UUID, &iface.iid); + iface.proxy_vtable = talloc_memdup(NULL, &proxy, sizeof(struct dcom_$interface->{NAME}_vtable)); + + return register_backend(\"dcom_interface\", &iface); +}\n\n"; +} + +##################################################################### +# parse a function +sub ParseFunction($$) +{ + my $interface = shift; + my $fn = shift; + my $name = $fn->{NAME}; + my $uname = uc $name; + + if (util::has_property($fn, "local")) { + $res .= " +static NTSTATUS dcom_proxy_$interface->{NAME}_$name(struct dcom_interface_p *d, TALLOC_CTX *mem_ctx, struct $name *r) +{ + /* FIXME */ + return NT_STATUS_NOT_SUPPORTED; +}\n"; + } else { + $res .= " +static struct rpc_request *dcom_proxy_$interface->{NAME}_$name\_send(struct dcom_interface_p *d, TALLOC_CTX *mem_ctx, struct $name *r) +{ + struct dcerpc_pipe *p; + NTSTATUS status = dcom_get_pipe(d, &p); + + if (NT_STATUS_IS_ERR(status)) { + return NULL; + } + + ZERO_STRUCT(r->in.ORPCthis); + r->in.ORPCthis.version.MajorVersion = COM_MAJOR_VERSION; + r->in.ORPCthis.version.MinorVersion = COM_MINOR_VERSION; + + if (p->flags & DCERPC_DEBUG_PRINT_IN) { + NDR_PRINT_IN_DEBUG($name, r); + } + + return dcerpc_ndr_request_send(p, &d->ipid, DCERPC_$uname, mem_ctx, + (ndr_push_flags_fn_t) ndr_push_$name, + (ndr_pull_flags_fn_t) ndr_pull_$name, + r, sizeof(*r)); +} + +static NTSTATUS dcom_proxy_$interface->{NAME}_$name(struct dcom_interface_p *d, TALLOC_CTX *mem_ctx, struct $name *r) +{ + struct dcerpc_pipe *p; + NTSTATUS status = dcom_get_pipe(d, &p); + struct rpc_request *req; + + if (NT_STATUS_IS_ERR(status)) { + return status; + } + + req = dcom_proxy_$interface->{NAME}_$name\_send(d, mem_ctx, r); + if (req == NULL) return NT_STATUS_NO_MEMORY; + + status = dcerpc_ndr_request_recv(req); + + if (NT_STATUS_IS_OK(status) && (p->flags & DCERPC_DEBUG_PRINT_OUT)) { + NDR_PRINT_OUT_DEBUG($name, r); + } + "; + if ($fn->{RETURN_TYPE} eq "NTSTATUS") { + $res .= "\tif (NT_STATUS_IS_OK(status)) status = r->out.result;\n"; + } + $res .= + " + return status; +}"; + } + + $res .=" +NTSTATUS dcom_$interface->{NAME}_$name (struct dcom_interface_p *d, TALLOC_CTX *mem_ctx, struct $name *r) +{ + return ((const struct dcom_$interface->{NAME}_vtable *)d->vtable)->$name (d, mem_ctx, r); +} +"; +} + + +##################################################################### +# parse the interface definitions +sub ParseInterface($) +{ + my($interface) = shift; + my($data) = $interface->{DATA}; + $res = "/* DCOM stubs generated by pidl */\n\n"; + foreach my $d (@{$data}) { + ($d->{TYPE} eq "FUNCTION") && + ParseFunction($interface, $d); + } + + ParseRegFunc($interface); +} + +1; diff --git a/source4/build/pidl/server.pm b/source4/build/pidl/server.pm index 01c5adacb4..6a9ea157a9 100644 --- a/source4/build/pidl/server.pm +++ b/source4/build/pidl/server.pm @@ -203,19 +203,14 @@ NTSTATUS dcerpc_server_$name\_init(void) "; } - ##################################################################### # parse a parsed IDL structure back into an IDL file -sub Parse($) +sub ParseInterface($) { - my($idl) = shift; + my($interface) = shift; $res = "/* dcerpc server boilerplate generated by pidl */\n\n"; - foreach my $x (@{$idl}) { - if ($x->{TYPE} eq "INTERFACE") { - Boilerplate_Iface($x); - Boilerplate_Ep_Server($x); - } - } + Boilerplate_Iface($interface); + Boilerplate_Ep_Server($interface); return $res; } diff --git a/source4/build/pidl/stub.pm b/source4/build/pidl/stub.pm new file mode 100644 index 0000000000..1cfb3cb11e --- /dev/null +++ b/source4/build/pidl/stub.pm @@ -0,0 +1,221 @@ +################################################### +# stub boilerplate generator +# Copyright jelmer@samba.org 2004 +# Copyright tridge@samba.org 2003 +# released under the GNU GPL + +package IdlStub; + +use strict; + +my($res); + +sub pidl($) +{ + $res .= shift; +} + + +##################################################### +# generate the switch statement for function dispatch +sub gen_dispatch_switch($) +{ + my $data = shift; + + my $count = 0; + foreach my $d (@{$data}) { + next if ($d->{TYPE} ne "FUNCTION"); + + pidl "\tcase $count: {\n"; + pidl "\t\tstruct $d->{NAME} *r2 = r;\n"; + pidl "\t\tif (DEBUGLEVEL > 10) {\n"; + pidl "\t\t\tNDR_PRINT_FUNCTION_DEBUG($d->{NAME}, NDR_IN, r2);\n"; + pidl "\t\t}\n"; + if ($d->{RETURN_TYPE} && $d->{RETURN_TYPE} ne "void") { + pidl "\t\tr2->out.result = vtable->$d->{NAME}(iface, mem_ctx, r2);\n"; + } else { + pidl "\t\tvtable->$d->{NAME}(iface, mem_ctx, r2);\n"; + } + pidl "\t\tif (DEBUGLEVEL > 10 && dce_call->fault_code == 0) {\n"; + pidl "\t\t\tNDR_PRINT_FUNCTION_DEBUG($d->{NAME}, NDR_OUT | NDR_SET_VALUES, r2);\n"; + pidl "\t\t}\n"; + pidl "\t\tif (dce_call->fault_code != 0) {\n"; + pidl "\t\t\tDEBUG(2,(\"dcerpc_fault 0x%x in $d->{NAME}\\n\", dce_call->fault_code));\n"; + pidl "\t\t}\n"; + pidl "\t\tbreak;\n\t}\n"; + $count++; + } +} + + +##################################################################### +# produce boilerplate code for a interface +sub Boilerplate_Iface($) +{ + my($interface) = shift; + my($data) = $interface->{DATA}; + my $count = 0; + my $name = $interface->{NAME}; + my $uname = uc $name; + + foreach my $d (@{$data}) { + if ($d->{TYPE} eq "FUNCTION") { $count++; } + } + + if ($count == 0) { + return; + } + + pidl " +static NTSTATUS $name\__op_bind(struct dcesrv_call_state *dce_call, const struct dcesrv_interface *iface) +{ +#ifdef DCESRV_INTERFACE_$uname\_BIND + return DCESRV_INTERFACE_$uname\_BIND(dce_call,iface); +#else + return NT_STATUS_OK; +#endif +} + +static void $name\__op_unbind(struct dcesrv_connection *dce_conn, const struct dcesrv_interface *iface) +{ +#ifdef DCESRV_INTERFACE_$uname\_UNBIND + DCESRV_INTERFACE_$uname\_UNBIND(dce_conn,iface); +#else + return; +#endif +} + +static NTSTATUS $name\__op_dispatch(struct dcesrv_call_state *dce_call, TALLOC_CTX *mem_ctx, void *r) +{ + uint16 opnum = dce_call->pkt.u.request.opnum; + struct GUID ipid = dce_call->pkt.u.request.object.object; + struct dcom_interface_p *iface = dcoms_get_ifacep(&ipid); + struct dcom_$name\_vtable *vtable = if->vtable; + + dce_call->fault_code = 0; + + switch (opnum) { +"; + gen_dispatch_switch($data); + +pidl " + default: + dce_call->fault_code = DCERPC_FAULT_OP_RNG_ERROR; + break; + } + + if (dce_call->fault_code != 0) { + return NT_STATUS_NET_WRITE_FAULT; + } + return NT_STATUS_OK; +} + +static const struct dcesrv_interface $name\_interface = { + &dcerpc_table_$name, + $name\__op_bind, + $name\__op_unbind, + $name\__op_dispatch +}; + +"; +} + +##################################################################### +# produce boilerplate code for an endpoint server +sub Boilerplate_Ep_Server($) +{ + my($interface) = shift; + my($data) = $interface->{DATA}; + my $count = 0; + my $name = $interface->{NAME}; + my $uname = uc $name; + + foreach my $d (@{$data}) { + if ($d->{TYPE} eq "FUNCTION") { $count++; } + } + + if ($count == 0) { + return; + } + + pidl " +static NTSTATUS $name\__op_init_server(struct dcesrv_context *dce_ctx, const struct dcesrv_endpoint_server *ep_server) +{ + int i; + + for (i=0;i<$name\_interface.ndr->endpoints->count;i++) { + NTSTATUS ret; + const char *name = $name\_interface.ndr->endpoints->names[i]; + + ret = dcesrv_interface_register(dce_ctx, name, &$name\_interface, NULL); + if (!NT_STATUS_IS_OK(ret)) { + DEBUG(1,(\"$name\_op_init_server: failed to register endpoint \'%s\'\\n\",name)); + return ret; + } + } + + return NT_STATUS_OK; +} + +static BOOL $name\__op_interface_by_uuid(struct dcesrv_interface *iface, const char *uuid, uint32 if_version) +{ + if ($name\_interface.ndr->if_version == if_version && + strcmp($name\_interface.ndr->uuid, uuid)==0) { + memcpy(iface,&$name\_interface, sizeof(*iface)); + return True; + } + + return False; +} + +static BOOL $name\__op_interface_by_name(struct dcesrv_interface *iface, const char *name) +{ + if (strcmp($name\_interface.ndr->name, name)==0) { + memcpy(iface,&$name\_interface, sizeof(*iface)); + return True; + } + + return False; +} + +NTSTATUS dcerpc_server_$name\_init(void) +{ + NTSTATUS ret; + struct dcesrv_endpoint_server ep_server; + + /* fill in our name */ + ep_server.name = \"$name\"; + + /* fill in all the operations */ + ep_server.init_server = $name\__op_init_server; + + ep_server.interface_by_uuid = $name\__op_interface_by_uuid; + ep_server.interface_by_name = $name\__op_interface_by_name; + + /* register ourselves with the DCERPC subsystem. */ + ret = register_backend(\"dcerpc\", &ep_server); + + if (!NT_STATUS_IS_OK(ret)) { + DEBUG(0,(\"Failed to register \'$name\' endpoint server!\\n\")); + return ret; + } + + return ret; +} + +"; +} + +sub ParseInterface($) +{ + my($interface) = shift; + $res = "/* dcerpc server boilerplate generated by pidl */\n\n"; + + Boilerplate_Iface($interface); + Boilerplate_Ep_Server($interface); + + return $res; +} + +1; + -- cgit