r12620: Get rid of automatically generated lists of init functions of subsystems.
[abartlet/samba.git/.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 #include "lib/events/events.h"
32
33 /*
34   state of a irpc 'connection'
35 */
36 struct ejs_irpc_connection {
37         const char *server_name;
38         uint32_t *dest_ids;
39         struct messaging_context *msg_ctx;
40 };
41
42 /*
43   messaging clients need server IDs as well ...
44  */
45 #define EJS_ID_BASE 0x30000000
46
47 /*
48   setup a context for talking to a irpc server
49      example: 
50         status = irpc.connect("smb_server");
51 */
52 static int ejs_irpc_connect(MprVarHandle eid, int argc, char **argv)
53 {
54         NTSTATUS status;
55         int i;
56         struct event_context *ev;
57         struct ejs_irpc_connection *p;
58         struct MprVar *this = mprGetProperty(ejsGetLocalObject(eid), "this", 0);
59
60         /* validate arguments */
61         if (argc != 1) {
62                 ejsSetErrorMsg(eid, "rpc_connect invalid arguments");
63                 return -1;
64         }
65
66         p = talloc(this, struct ejs_irpc_connection);
67         if (p == NULL) {
68                 return -1;
69         }
70
71         p->server_name = argv[0];
72
73         ev = event_context_find(p);
74
75         /* create a messaging context, looping as we have no way to
76            allocate temporary server ids automatically */
77         for (i=0;i<10000;i++) {
78                 p->msg_ctx = messaging_init(p, EJS_ID_BASE + i, ev);
79                 if (p->msg_ctx) break;
80         }
81         if (p->msg_ctx == NULL) {
82                 ejsSetErrorMsg(eid, "irpc_connect unable to create a messaging context");
83                 talloc_free(p);
84                 return -1;
85         }
86
87         p->dest_ids = irpc_servers_byname(p->msg_ctx, p->server_name);
88         if (p->dest_ids == NULL || p->dest_ids[0] == 0) {
89                 talloc_free(p);
90                 status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
91         } else {
92                 mprSetPtrChild(this, "irpc", p);
93                 status = NT_STATUS_OK;
94         }
95
96         mpr_Return(eid, mprNTSTATUS(status));
97         return 0;
98 }
99
100
101 /*
102   connect to an rpc server
103      examples: 
104         status = rpc.connect("ncacn_ip_tcp:localhost");
105         status = rpc.connect("ncacn_ip_tcp:localhost", "pipe_name");
106 */
107 static int ejs_rpc_connect(MprVarHandle eid, int argc, char **argv)
108 {
109         const char *binding, *pipe_name;
110         const struct dcerpc_interface_table *iface;
111         NTSTATUS status;
112         struct dcerpc_pipe *p;
113         struct cli_credentials *creds;
114         struct event_context *ev;
115         struct MprVar *this = mprGetProperty(ejsGetLocalObject(eid), "this", 0);
116         struct MprVar *credentials;
117
118         /* validate arguments */
119         if (argc < 1) {
120                 ejsSetErrorMsg(eid, "rpc_connect invalid arguments");
121                 return -1;
122         }
123
124         binding    = argv[0];
125         if (strchr(binding, ':') == NULL) {
126                 /* its an irpc connect */
127                 return ejs_irpc_connect(eid, argc, argv);
128         }
129
130         if (argc > 1) {
131                 pipe_name = argv[1];
132         } else {
133                 pipe_name = mprToString(mprGetProperty(this, "pipe_name", NULL));
134         }
135
136         iface = idl_iface_by_name(pipe_name);
137         if (iface == NULL) {
138                 status = NT_STATUS_OBJECT_NAME_INVALID;
139                 goto done;
140         }
141
142         credentials = mprGetProperty(this, "credentials", NULL);
143         if (credentials) {
144                 creds = mprGetPtr(credentials, "creds");
145         } else {
146                 creds = cmdline_credentials;
147         }
148         if (creds == NULL) {
149                 creds = cli_credentials_init(mprMemCtx());
150                 cli_credentials_guess(creds);
151                 cli_credentials_set_anonymous(creds);
152         }
153
154         ev = event_context_find(mprMemCtx());
155
156         status = dcerpc_pipe_connect(this, &p, binding, iface, creds, ev);
157         if (!NT_STATUS_IS_OK(status)) goto done;
158
159         /* callers don't allocate ref vars in the ejs interface */
160         p->conn->flags |= DCERPC_NDR_REF_ALLOC;
161
162         /* by making the pipe a child of the connection variable, it will
163            auto close when it goes out of scope in the script */
164         mprSetPtrChild(this, "pipe", p);
165
166 done:
167         mpr_Return(eid, mprNTSTATUS(status));
168         return 0;
169 }
170
171
172 /*
173   make an irpc call - called via the same interface as rpc
174 */
175 static int ejs_irpc_call(int eid, struct MprVar *io, 
176                          const struct dcerpc_interface_table *iface, int callnum,
177                          ejs_pull_function_t ejs_pull, ejs_push_function_t ejs_push)
178 {
179         NTSTATUS status;
180         void *ptr;
181         struct ejs_rpc *ejs;
182         const struct dcerpc_interface_call *call;
183         struct ejs_irpc_connection *p;
184         struct irpc_request **reqs;
185         int i, count;
186         struct MprVar *results;
187
188         p = mprGetThisPtr(eid, "irpc");
189
190         ejs = talloc(mprMemCtx(), struct ejs_rpc);
191         if (ejs == NULL) {
192                 status = NT_STATUS_NO_MEMORY;
193                 goto done;
194         }
195
196         call = &iface->calls[callnum];
197
198         ejs->eid = eid;
199         ejs->callname = call->name;
200
201         /* allocate the C structure */
202         ptr = talloc_zero_size(ejs, call->struct_size);
203         if (ptr == NULL) {
204                 status = NT_STATUS_NO_MEMORY;
205                 goto done;
206         }
207
208         /* convert the mpr object into a C structure */
209         status = ejs_pull(ejs, io, ptr);
210         if (!NT_STATUS_IS_OK(status)) {
211                 goto done;
212         }
213
214         for (count=0;p->dest_ids[count];count++) /* noop */ ;
215
216         /* we need to make a call per server */
217         reqs = talloc_array(ejs, struct irpc_request *, count);
218         if (reqs == NULL) {
219                 status = NT_STATUS_NO_MEMORY;
220                 goto done;
221         }
222
223         /* make the actual calls */
224         for (i=0;i<count;i++) {
225                 reqs[i] = irpc_call_send(p->msg_ctx, p->dest_ids[i], 
226                                          iface, callnum, ptr, ptr);
227                 if (reqs[i] == NULL) {
228                         status = NT_STATUS_NO_MEMORY;
229                         goto done;
230                 }
231                 talloc_steal(reqs, reqs[i]);
232         }
233         
234         mprSetVar(io, "results", mprObject("results"));
235         results = mprGetProperty(io, "results", NULL);
236
237         /* and receive the results, placing them in io.results[i] */
238         for (i=0;i<count;i++) {
239                 struct MprVar *output;
240
241                 status = irpc_call_recv(reqs[i]);
242                 if (!NT_STATUS_IS_OK(status)) {
243                         goto done;
244                 }
245                 status = ejs_push(ejs, io, ptr);
246                 if (!NT_STATUS_IS_OK(status)) {
247                         goto done;
248                 }
249
250                 /* add to the results array */
251                 output = mprGetProperty(io, "output", NULL);
252                 if (output) {
253                         char idx[16];
254                         mprItoa(i, idx, sizeof(idx));
255                         mprSetProperty(results, idx, output);
256                         mprDeleteProperty(io, "output");
257                 }
258         }
259         mprSetVar(results, "length", mprCreateIntegerVar(i));
260
261 done:
262         talloc_free(ejs);
263         mpr_Return(eid, mprNTSTATUS(status));
264         if (NT_STATUS_EQUAL(status, NT_STATUS_INTERNAL_ERROR)) {
265                 return -1;
266         }
267         return 0;
268 }
269
270
271 /*
272   backend code for making an rpc call - this is called from the pidl generated ejs
273   code
274 */
275  int ejs_rpc_call(int eid, int argc, struct MprVar **argv,
276                   const struct dcerpc_interface_table *iface, int callnum,
277                   ejs_pull_function_t ejs_pull, ejs_push_function_t ejs_push)
278 {
279         struct MprVar *io;
280         struct dcerpc_pipe *p;
281         NTSTATUS status;
282         void *ptr;
283         struct rpc_request *req;
284         struct ejs_rpc *ejs;
285         const struct dcerpc_interface_call *call;
286
287         if (argc != 1 || argv[0]->type != MPR_TYPE_OBJECT) {
288                 ejsSetErrorMsg(eid, "rpc_call invalid arguments");
289                 return -1;
290         }
291             
292         io       = argv[0];
293
294         if (mprGetThisPtr(eid, "irpc")) {
295                 /* its an irpc call */
296                 return ejs_irpc_call(eid, io, iface, callnum, ejs_pull, ejs_push);
297         }
298
299         /* get the pipe info */
300         p = mprGetThisPtr(eid, "pipe");
301         if (p == NULL) {
302                 ejsSetErrorMsg(eid, "rpc_call invalid pipe");
303                 return -1;
304         }
305
306         ejs = talloc(mprMemCtx(), struct ejs_rpc);
307         if (ejs == NULL) {
308                 status = NT_STATUS_NO_MEMORY;
309                 goto done;
310         }
311
312         call = &iface->calls[callnum];
313
314         ejs->eid = eid;
315         ejs->callname = call->name;
316
317         /* allocate the C structure */
318         ptr = talloc_zero_size(ejs, call->struct_size);
319         if (ptr == NULL) {
320                 status = NT_STATUS_NO_MEMORY;
321                 goto done;
322         }
323
324         /* convert the mpr object into a C structure */
325         status = ejs_pull(ejs, io, ptr);
326         if (!NT_STATUS_IS_OK(status)) {
327                 goto done;
328         }
329
330         /* make the actual call */
331         req = dcerpc_ndr_request_send(p, NULL, iface, callnum, ptr, ptr);
332
333         /* if requested, print the structure */
334         if (p->conn->flags & DCERPC_DEBUG_PRINT_IN) {
335                 ndr_print_function_debug(call->ndr_print, call->name, NDR_IN, ptr);
336         }
337
338         if (req == NULL) {
339                 status = NT_STATUS_NO_MEMORY;
340                 goto done;
341         }
342
343         status = dcerpc_ndr_request_recv(req);
344         if (!NT_STATUS_IS_OK(status)) {
345                 goto done;
346         }
347
348         /* print the 'out' structure, if needed */
349         if (p->conn->flags & DCERPC_DEBUG_PRINT_OUT) {
350                 ndr_print_function_debug(call->ndr_print, call->name, NDR_OUT, ptr);
351         }
352
353         status = ejs_push(ejs, io, ptr);
354
355 done:
356         talloc_free(ejs);
357         mpr_Return(eid, mprNTSTATUS(status));
358         if (NT_STATUS_EQUAL(status, NT_STATUS_INTERNAL_ERROR)) {
359                 return -1;
360         }
361         return 0;
362 }
363
364 /*
365   hook called by generated RPC interfaces at the end of their init routines
366   used to add generic operations on the pipe
367 */
368 int ejs_rpc_init(struct MprVar *obj, const char *name)
369 {
370         dcerpc_table_init();
371
372         mprSetStringCFunction(obj, "connect", ejs_rpc_connect);
373         if (mprGetProperty(obj, "pipe_name", NULL) == NULL) {
374                 mprSetVar(obj, "pipe_name", mprString(name));
375         }
376         return 0;
377 }