2 Unix SMB/CIFS implementation.
4 provide interfaces to rpc calls from ejs scripts
6 Copyright (C) Andrew Tridgell 2005
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>.
23 #include "scripting/ejs/smbcalls.h"
24 #include "lib/appweb/ejs/ejs.h"
25 #include "librpc/gen_ndr/echo.h"
26 #include "lib/cmdline/popt_common.h"
27 #include "lib/messaging/irpc.h"
28 #include "scripting/ejs/ejsrpc.h"
29 #include "lib/util/dlinklist.h"
30 #include "lib/events/events.h"
31 #include "librpc/ndr/ndr_table.h"
32 #include "auth/credentials/credentials.h"
33 #include "librpc/rpc/dcerpc.h"
34 #include "cluster/cluster.h"
37 state of a irpc 'connection'
39 struct ejs_irpc_connection {
40 const char *server_name;
41 struct server_id *dest_ids;
42 struct messaging_context *msg_ctx;
46 messaging clients need server IDs as well ...
48 #define EJS_ID_BASE 0x30000000
51 setup a context for talking to a irpc server
53 status = irpc.connect("smb_server");
55 static int ejs_irpc_connect(MprVarHandle eid, int argc, char **argv)
59 struct event_context *ev;
60 struct ejs_irpc_connection *p;
61 struct MprVar *this = mprGetProperty(ejsGetLocalObject(eid), "this", 0);
63 /* validate arguments */
65 ejsSetErrorMsg(eid, "rpc_connect invalid arguments");
69 p = talloc(this, struct ejs_irpc_connection);
74 p->server_name = argv[0];
76 ev = event_context_find(p);
78 /* create a messaging context, looping as we have no way to
79 allocate temporary server ids automatically */
80 for (i=0;i<10000;i++) {
81 p->msg_ctx = messaging_init(p,
82 lp_messaging_path(p, global_loadparm),
83 cluster_id(EJS_ID_BASE + i), ev);
84 if (p->msg_ctx) break;
86 if (p->msg_ctx == NULL) {
87 ejsSetErrorMsg(eid, "irpc_connect unable to create a messaging context");
92 p->dest_ids = irpc_servers_byname(p->msg_ctx, p, p->server_name);
93 if (p->dest_ids == NULL || p->dest_ids[0].id == 0) {
95 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
97 mprSetPtrChild(this, "irpc", p);
98 status = NT_STATUS_OK;
101 mpr_Return(eid, mprNTSTATUS(status));
107 connect to an rpc server
109 status = rpc.connect("ncacn_ip_tcp:localhost");
110 status = rpc.connect("ncacn_ip_tcp:localhost", "pipe_name");
112 static int ejs_rpc_connect(MprVarHandle eid, int argc, char **argv)
114 const char *binding, *pipe_name;
115 const struct ndr_interface_table *iface;
117 struct dcerpc_pipe *p;
118 struct cli_credentials *creds;
119 struct event_context *ev;
120 struct MprVar *this = mprGetProperty(ejsGetLocalObject(eid), "this", 0);
121 struct MprVar *credentials;
123 /* validate arguments */
125 ejsSetErrorMsg(eid, "rpc_connect invalid arguments");
130 if (strchr(binding, ':') == NULL) {
131 /* its an irpc connect */
132 return ejs_irpc_connect(eid, argc, argv);
138 pipe_name = mprToString(mprGetProperty(this, "pipe_name", NULL));
141 iface = ndr_table_by_name(pipe_name);
143 status = NT_STATUS_OBJECT_NAME_INVALID;
147 credentials = mprGetProperty(this, "credentials", NULL);
149 creds = (struct cli_credentials *)
150 mprGetPtr(credentials, "creds");
152 creds = cmdline_credentials;
155 creds = cli_credentials_init(mprMemCtx());
156 cli_credentials_guess(creds);
157 cli_credentials_set_anonymous(creds);
160 ev = event_context_find(mprMemCtx());
162 status = dcerpc_pipe_connect(this, &p, binding, iface, creds, ev);
163 if (!NT_STATUS_IS_OK(status)) goto done;
165 /* callers don't allocate ref vars in the ejs interface */
166 p->conn->flags |= DCERPC_NDR_REF_ALLOC;
168 /* by making the pipe a child of the connection variable, it will
169 auto close when it goes out of scope in the script */
170 mprSetPtrChild(this, "pipe", p);
173 mpr_Return(eid, mprNTSTATUS(status));
179 make an irpc call - called via the same interface as rpc
181 static int ejs_irpc_call(int eid, struct MprVar *io,
182 const struct ndr_interface_table *iface, int callnum,
183 ejs_pull_function_t ejs_pull, ejs_push_function_t ejs_push)
188 const struct ndr_interface_call *call;
189 struct ejs_irpc_connection *p;
190 struct irpc_request **reqs;
192 struct MprVar *results;
194 p = (struct ejs_irpc_connection *)mprGetThisPtr(eid, "irpc");
196 ejs = talloc(mprMemCtx(), struct ejs_rpc);
198 status = NT_STATUS_NO_MEMORY;
202 call = &iface->calls[callnum];
205 ejs->callname = call->name;
207 /* allocate the C structure */
208 ptr = talloc_zero_size(ejs, call->struct_size);
210 status = NT_STATUS_NO_MEMORY;
214 /* convert the mpr object into a C structure */
215 status = ejs_pull(ejs, io, ptr);
216 if (!NT_STATUS_IS_OK(status)) {
220 for (count=0;p->dest_ids[count].id;count++) /* noop */ ;
222 /* we need to make a call per server */
223 reqs = talloc_array(ejs, struct irpc_request *, count);
225 status = NT_STATUS_NO_MEMORY;
229 /* make the actual calls */
230 for (i=0;i<count;i++) {
231 reqs[i] = irpc_call_send(p->msg_ctx, p->dest_ids[i],
232 iface, callnum, ptr, ptr);
233 if (reqs[i] == NULL) {
234 status = NT_STATUS_NO_MEMORY;
237 talloc_steal(reqs, reqs[i]);
240 mprSetVar(io, "results", mprObject("results"));
241 results = mprGetProperty(io, "results", NULL);
243 /* and receive the results, placing them in io.results[i] */
244 for (i=0;i<count;i++) {
245 struct MprVar *output;
247 status = irpc_call_recv(reqs[i]);
248 if (!NT_STATUS_IS_OK(status)) {
251 status = ejs_push(ejs, io, ptr);
252 if (!NT_STATUS_IS_OK(status)) {
256 /* add to the results array */
257 output = mprGetProperty(io, "output", NULL);
260 mprItoa(i, idx, sizeof(idx));
261 mprSetProperty(results, idx, output);
262 mprDeleteProperty(io, "output");
265 mprSetVar(results, "length", mprCreateIntegerVar(i));
269 mpr_Return(eid, mprNTSTATUS(status));
270 if (NT_STATUS_EQUAL(status, NT_STATUS_INTERNAL_ERROR)) {
278 backend code for making an rpc call - this is called from the pidl generated ejs
281 int ejs_rpc_call(int eid, int argc, struct MprVar **argv,
282 const struct ndr_interface_table *iface, int callnum,
283 ejs_pull_function_t ejs_pull, ejs_push_function_t ejs_push)
286 struct dcerpc_pipe *p;
289 struct rpc_request *req;
291 const struct ndr_interface_call *call;
293 if (argc != 1 || argv[0]->type != MPR_TYPE_OBJECT) {
294 ejsSetErrorMsg(eid, "rpc_call invalid arguments");
300 if (mprGetThisPtr(eid, "irpc")) {
301 /* its an irpc call */
302 return ejs_irpc_call(eid, io, iface, callnum, ejs_pull, ejs_push);
305 /* get the pipe info */
306 p = mprGetThisPtr(eid, "pipe");
308 ejsSetErrorMsg(eid, "rpc_call invalid pipe");
312 ejs = talloc(mprMemCtx(), struct ejs_rpc);
314 status = NT_STATUS_NO_MEMORY;
318 call = &iface->calls[callnum];
321 ejs->callname = call->name;
323 /* allocate the C structure */
324 ptr = talloc_zero_size(ejs, call->struct_size);
326 status = NT_STATUS_NO_MEMORY;
330 /* convert the mpr object into a C structure */
331 status = ejs_pull(ejs, io, ptr);
332 if (!NT_STATUS_IS_OK(status)) {
336 /* make the actual call */
337 req = dcerpc_ndr_request_send(p, NULL, iface, callnum, ptr, ptr);
339 /* if requested, print the structure */
340 if (p->conn->flags & DCERPC_DEBUG_PRINT_IN) {
341 ndr_print_function_debug(call->ndr_print, call->name, NDR_IN, ptr);
345 status = NT_STATUS_NO_MEMORY;
349 status = dcerpc_ndr_request_recv(req);
350 if (!NT_STATUS_IS_OK(status)) {
354 /* print the 'out' structure, if needed */
355 if (p->conn->flags & DCERPC_DEBUG_PRINT_OUT) {
356 ndr_print_function_debug(call->ndr_print, call->name, NDR_OUT, ptr);
359 status = ejs_push(ejs, io, ptr);
363 mpr_Return(eid, mprNTSTATUS(status));
364 if (NT_STATUS_EQUAL(status, NT_STATUS_INTERNAL_ERROR)) {
371 hook called by generated RPC interfaces at the end of their init routines
372 used to add generic operations on the pipe
374 int ejs_rpc_init(struct MprVar *obj, const char *name)
378 mprSetStringCFunction(obj, "connect", ejs_rpc_connect);
379 if (mprGetProperty(obj, "pipe_name", NULL) == NULL) {
380 mprSetVar(obj, "pipe_name", mprString(name));