r8821: continue the trend to move to a more OO style of interface for our js
[nivanova/samba-autobuild/.git] / source4 / scripting / ejs / smbcalls_rpc.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    provide interfaces to rpc calls from ejs scripts
5
6    Copyright (C) Andrew Tridgell 2005
7    
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 2 of the License, or
11    (at your option) any later version.
12    
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.
17    
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 */
22
23 #include "includes.h"
24 #include "scripting/ejs/smbcalls.h"
25 #include "lib/appweb/ejs/ejs.h"
26 #include "librpc/gen_ndr/ndr_echo.h"
27 #include "lib/cmdline/popt_common.h"
28 #include "lib/messaging/irpc.h"
29 #include "scripting/ejs/ejsrpc.h"
30 #include "dlinklist.h"
31
32 /*
33   state of a irpc 'connection'
34 */
35 struct ejs_irpc_connection {
36         const char *server_name;
37         uint32_t *dest_ids;
38         struct messaging_context *msg_ctx;
39 };
40
41 /*
42   messaging clients need server IDs as well ...
43  */
44 #define EJS_ID_BASE 0x30000000
45
46 /*
47   setup a context for talking to a irpc server
48      example: 
49         status = irpc.connect("smb_server");
50 */
51 static int ejs_irpc_connect(MprVarHandle eid, int argc, char **argv)
52 {
53         NTSTATUS status;
54         int i;
55         struct event_context *ev;
56         struct ejs_irpc_connection *p;
57         struct MprVar *this = mprGetProperty(ejsGetLocalObject(eid), "this", 0);
58
59         /* validate arguments */
60         if (argc != 1) {
61                 ejsSetErrorMsg(eid, "rpc_connect invalid arguments");
62                 return -1;
63         }
64
65         p = talloc(this, struct ejs_irpc_connection);
66         if (p == NULL) {
67                 return -1;
68         }
69
70         p->server_name = argv[0];
71
72         ev = talloc_find_parent_bytype(mprMemCtx(), struct event_context);
73
74         /* create a messaging context, looping as we have no way to
75            allocate temporary server ids automatically */
76         for (i=0;i<10000;i++) {
77                 p->msg_ctx = messaging_init(p, EJS_ID_BASE + i, ev);
78                 if (p->msg_ctx) break;
79         }
80         if (p->msg_ctx == NULL) {
81                 ejsSetErrorMsg(eid, "irpc_connect unable to create a messaging context");
82                 talloc_free(p);
83                 return -1;
84         }
85
86         p->dest_ids = irpc_servers_byname(p->msg_ctx, p->server_name);
87         if (p->dest_ids == NULL || p->dest_ids[0] == 0) {
88                 talloc_free(p);
89                 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
90         } else {
91                 mprSetPtrChild(this, "irpc", p);
92                 status = NT_STATUS_OK;
93         }
94
95         mpr_Return(eid, mprNTSTATUS(status));
96         return 0;
97 }
98
99
100 /*
101   connect to an rpc server
102      examples: 
103         status = rpc.connect("ncacn_ip_tcp:localhost");
104         status = rpc.connect("ncacn_ip_tcp:localhost", "pipe_name");
105 */
106 static int ejs_rpc_connect(MprVarHandle eid, int argc, char **argv)
107 {
108         const char *binding, *pipe_name;
109         const struct dcerpc_interface_table *iface;
110         NTSTATUS status;
111         struct dcerpc_pipe *p;
112         struct cli_credentials *creds = cmdline_credentials;
113         struct event_context *ev;
114         struct MprVar *this = mprGetProperty(ejsGetLocalObject(eid), "this", 0);
115
116         /* validate arguments */
117         if (argc < 1) {
118                 ejsSetErrorMsg(eid, "rpc_connect invalid arguments");
119                 return -1;
120         }
121
122         binding    = argv[0];
123         if (strchr(binding, ':') == NULL) {
124                 /* its an irpc connect */
125                 return ejs_irpc_connect(eid, argc, argv);
126         }
127
128         if (argc > 1) {
129                 pipe_name = argv[1];
130         } else {
131                 pipe_name = mprToString(mprGetProperty(this, "pipe_name", NULL));
132         }
133
134         iface = idl_iface_by_name(pipe_name);
135         if (iface == NULL) {
136                 status = NT_STATUS_OBJECT_NAME_INVALID;
137                 goto done;
138         }
139
140         if (creds == NULL) {
141                 creds = cli_credentials_init(mprMemCtx());
142                 cli_credentials_guess(creds);
143                 cli_credentials_set_username(creds, "", CRED_GUESSED);
144                 cli_credentials_set_password(creds, "", CRED_GUESSED);
145         }
146
147         ev = talloc_find_parent_bytype(mprMemCtx(), struct event_context);
148
149         status = dcerpc_pipe_connect(this, &p, binding, 
150                                      iface->uuid, iface->if_version, 
151                                      creds, ev);
152         if (!NT_STATUS_IS_OK(status)) goto done;
153
154         /* callers don't allocate ref vars in the ejs interface */
155         p->conn->flags |= DCERPC_NDR_REF_ALLOC;
156
157         /* by making the pipe a child of the connection variable, it will
158            auto close when it goes out of scope in the script */
159         mprSetPtrChild(this, "pipe", p);
160         mprSetPtr(this, "iface", iface);
161
162 done:
163         mpr_Return(eid, mprNTSTATUS(status));
164         return 0;
165 }
166
167
168 /*
169   make an irpc call - called via the same interface as rpc
170 */
171 static int ejs_irpc_call(int eid, struct MprVar *io, 
172                          const struct dcerpc_interface_table *iface, int callnum,
173                          ejs_pull_function_t ejs_pull, ejs_push_function_t ejs_push)
174 {
175         NTSTATUS status;
176         void *ptr;
177         struct ejs_rpc *ejs;
178         const struct dcerpc_interface_call *call;
179         struct ejs_irpc_connection *p;
180         struct irpc_request **reqs;
181         int i, count;
182         struct MprVar *results;
183
184         p = mprGetThisPtr(eid, "irpc");
185
186         ejs = talloc(mprMemCtx(), struct ejs_rpc);
187         if (ejs == NULL) {
188                 status = NT_STATUS_NO_MEMORY;
189                 goto done;
190         }
191
192         call = &iface->calls[callnum];
193
194         ejs->eid = eid;
195         ejs->callname = call->name;
196
197         /* allocate the C structure */
198         ptr = talloc_zero_size(ejs, call->struct_size);
199         if (ptr == NULL) {
200                 status = NT_STATUS_NO_MEMORY;
201                 goto done;
202         }
203
204         /* convert the mpr object into a C structure */
205         status = ejs_pull(ejs, io, ptr);
206         if (!NT_STATUS_IS_OK(status)) {
207                 goto done;
208         }
209
210         for (count=0;p->dest_ids[count];count++) /* noop */ ;
211
212         /* we need to make a call per server */
213         reqs = talloc_array(ejs, struct irpc_request *, count);
214         if (reqs == NULL) {
215                 status = NT_STATUS_NO_MEMORY;
216                 goto done;
217         }
218
219         /* make the actual calls */
220         for (i=0;i<count;i++) {
221                 reqs[i] = irpc_call_send(p->msg_ctx, p->dest_ids[i], 
222                                          iface, callnum, ptr);
223                 if (reqs[i] == NULL) {
224                         status = NT_STATUS_NO_MEMORY;
225                         goto done;
226                 }
227                 talloc_steal(reqs, reqs[i]);
228         }
229         
230         mprSetVar(io, "results", mprObject("results"));
231         results = mprGetProperty(io, "results", NULL);
232
233         /* and receive the results, placing them in io.results[i] */
234         for (i=0;i<count;i++) {
235                 struct MprVar *output;
236
237                 status = irpc_call_recv(reqs[i]);
238                 if (!NT_STATUS_IS_OK(status)) {
239                         goto done;
240                 }
241                 status = ejs_push(ejs, io, ptr);
242                 if (!NT_STATUS_IS_OK(status)) {
243                         goto done;
244                 }
245                 talloc_free(reqs[i]);
246
247                 /* add to the results array */
248                 output = mprGetProperty(io, "output", NULL);
249                 if (output) {
250                         char idx[16];
251                         mprItoa(i, idx, sizeof(idx));
252                         mprSetProperty(results, idx, output);
253                         mprDeleteProperty(io, "output");
254                 }
255         }
256         mprSetVar(results, "length", mprCreateIntegerVar(i));
257
258 done:
259         talloc_free(ejs);
260         mpr_Return(eid, mprNTSTATUS(status));
261         if (NT_STATUS_EQUAL(status, NT_STATUS_INTERNAL_ERROR)) {
262                 return -1;
263         }
264         return 0;
265 }
266
267
268 /*
269   backend code for making an rpc call - this is called from the pidl generated ejs
270   code
271 */
272  int ejs_rpc_call(int eid, int argc, struct MprVar **argv,
273                   const struct dcerpc_interface_table *iface, int callnum,
274                   ejs_pull_function_t ejs_pull, ejs_push_function_t ejs_push)
275 {
276         struct MprVar *io;
277         struct dcerpc_pipe *p;
278         NTSTATUS status;
279         void *ptr;
280         struct rpc_request *req;
281         struct ejs_rpc *ejs;
282         const struct dcerpc_interface_call *call;
283
284         if (argc != 1 || argv[0]->type != MPR_TYPE_OBJECT) {
285                 ejsSetErrorMsg(eid, "rpc_call invalid arguments");
286                 return -1;
287         }
288             
289         io       = argv[0];
290
291         if (mprGetThisPtr(eid, "irpc")) {
292                 /* its an irpc call */
293                 return ejs_irpc_call(eid, io, iface, callnum, ejs_pull, ejs_push);
294         }
295
296         /* get the pipe info */
297         p = mprGetThisPtr(eid, "pipe");
298         if (p == NULL) {
299                 ejsSetErrorMsg(eid, "rpc_call invalid pipe");
300                 return -1;
301         }
302
303         ejs = talloc(mprMemCtx(), struct ejs_rpc);
304         if (ejs == NULL) {
305                 status = NT_STATUS_NO_MEMORY;
306                 goto done;
307         }
308
309         call = &iface->calls[callnum];
310
311         ejs->eid = eid;
312         ejs->callname = call->name;
313
314         /* allocate the C structure */
315         ptr = talloc_zero_size(ejs, call->struct_size);
316         if (ptr == NULL) {
317                 status = NT_STATUS_NO_MEMORY;
318                 goto done;
319         }
320
321         /* convert the mpr object into a C structure */
322         status = ejs_pull(ejs, io, ptr);
323         if (!NT_STATUS_IS_OK(status)) {
324                 goto done;
325         }
326
327         /* if requested, print the structure */
328         if (p->conn->flags & DCERPC_DEBUG_PRINT_IN) {
329                 ndr_print_function_debug(call->ndr_print, call->name, NDR_IN, ptr);
330         }
331
332         /* make the actual call */
333         req = dcerpc_ndr_request_send(p, NULL, iface, callnum, ptr, ptr);
334         if (req == NULL) {
335                 status = NT_STATUS_NO_MEMORY;
336                 goto done;
337         }
338         status = dcerpc_ndr_request_recv(req);
339
340         /* print the 'out' structure, if needed */
341         if (p->conn->flags & DCERPC_DEBUG_PRINT_OUT) {
342                 ndr_print_function_debug(call->ndr_print, call->name, NDR_OUT, ptr);
343         }
344
345         status = ejs_push(ejs, io, ptr);
346
347 done:
348         talloc_free(ejs);
349         mpr_Return(eid, mprNTSTATUS(status));
350         if (NT_STATUS_EQUAL(status, NT_STATUS_INTERNAL_ERROR)) {
351                 return -1;
352         }
353         return 0;
354 }
355
356
357 /* a list of registered ejs rpc modules */
358 static struct ejs_register {
359         struct ejs_register *next, *prev;
360         const char *name;
361         MprCFunction fn;
362 } *ejs_registered;
363
364 /*
365   register a generated ejs module
366 */
367  NTSTATUS smbcalls_register_ejs(const char *name, MprCFunction fn)
368 {
369         struct ejs_register *r;
370         void *ctx = ejs_registered;
371         if (ctx == NULL) {
372                 ctx = talloc_autofree_context();
373         }
374         r = talloc(ctx, struct ejs_register);
375         NT_STATUS_HAVE_NO_MEMORY(r);
376         r->name = name;
377         r->fn = fn;
378         DLIST_ADD(ejs_registered, r);
379         return NT_STATUS_OK;
380 }
381
382 /*
383   setup C functions that be called from ejs
384 */
385 void smb_setup_ejs_rpc(void)
386 {
387         struct ejs_register *r;
388
389         for (r=ejs_registered;r;r=r->next) {
390                 ejsDefineCFunction(-1, r->name, r->fn, NULL, MPR_VAR_SCRIPT_HANDLE);
391         }
392 }
393
394 /*
395   hook called by generated RPC interfaces at the end of their init routines
396   used to add generic operations on the pipe
397 */
398 int ejs_rpc_init(struct MprVar *obj, const char *name)
399 {
400         mprSetStringCFunction(obj, "connect", ejs_rpc_connect);
401         if (mprGetProperty(obj, "pipe_name", NULL) == NULL) {
402                 mprSetVar(obj, "pipe_name", mprString(name));
403         }
404         return 0;
405 }