r25446: Merge some changes I made on the way home from SFO:
[nivanova/samba-autobuild/.git] / source4 / scripting / ejs / smbcalls_rpc.c
index e1d9c93be67a141a235fe1a215f6a6603113a9b6..5d7c6742089de69c744be9cbdbc505f52e0eb5f5 100644 (file)
@@ -7,7 +7,7 @@
    
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
-   the Free Software Foundation; either version 2 of the License, or
+   the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.
    
    This program is distributed in the hope that it will be useful,
    GNU General Public License for more details.
    
    You should have received a copy of the GNU General Public License
-   along with this program; if not, write to the Free Software
-   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 
 #include "includes.h"
-#include "lib/ejs/ejs.h"
-#include "librpc/gen_ndr/ndr_echo.h"
+#include "scripting/ejs/smbcalls.h"
+#include "lib/appweb/ejs/ejs.h"
+#include "librpc/gen_ndr/echo.h"
 #include "lib/cmdline/popt_common.h"
+#include "lib/messaging/irpc.h"
 #include "scripting/ejs/ejsrpc.h"
+#include "lib/util/dlinklist.h"
+#include "lib/events/events.h"
+#include "librpc/ndr/ndr_table.h"
+#include "auth/credentials/credentials.h"
+#include "librpc/rpc/dcerpc.h"
+#include "cluster/cluster.h"
 
 /*
-  connect to an rpc server
+  state of a irpc 'connection'
+*/
+struct ejs_irpc_connection {
+       const char *server_name;
+       struct server_id *dest_ids;
+       struct messaging_context *msg_ctx;
+};
+
+/*
+  messaging clients need server IDs as well ...
+ */
+#define EJS_ID_BASE 0x30000000
+
+/*
+  setup a context for talking to a irpc server
      example: 
-        var conn = new Object();
-        status = rpc_connect(conn, "ncacn_ip_tcp:localhost", "rpcecho");
+        status = irpc.connect("smb_server");
 */
-static int ejs_rpc_connect(MprVarHandle eid, int argc, struct MprVar **argv)
+static int ejs_irpc_connect(MprVarHandle eid, int argc, char **argv)
+{
+       NTSTATUS status;
+       int i;
+       struct event_context *ev;
+       struct ejs_irpc_connection *p;
+       struct MprVar *this = mprGetProperty(ejsGetLocalObject(eid), "this", 0);
+
+       /* validate arguments */
+       if (argc != 1) {
+               ejsSetErrorMsg(eid, "rpc_connect invalid arguments");
+               return -1;
+       }
+
+       p = talloc(this, struct ejs_irpc_connection);
+       if (p == NULL) {
+               return -1;
+       }
+
+       p->server_name = argv[0];
+
+       ev = event_context_find(p);
+
+       /* create a messaging context, looping as we have no way to
+          allocate temporary server ids automatically */
+       for (i=0;i<10000;i++) {
+               p->msg_ctx = messaging_init(p, 
+                                           lp_messaging_path(p, global_loadparm),
+                                           cluster_id(EJS_ID_BASE + i), ev);
+               if (p->msg_ctx) break;
+       }
+       if (p->msg_ctx == NULL) {
+               ejsSetErrorMsg(eid, "irpc_connect unable to create a messaging context");
+               talloc_free(p);
+               return -1;
+       }
+
+       p->dest_ids = irpc_servers_byname(p->msg_ctx, p, p->server_name);
+       if (p->dest_ids == NULL || p->dest_ids[0].id == 0) {
+               talloc_free(p);
+               status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
+       } else {
+               mprSetPtrChild(this, "irpc", p);
+               status = NT_STATUS_OK;
+       }
+
+       mpr_Return(eid, mprNTSTATUS(status));
+       return 0;
+}
+
+
+/*
+  connect to an rpc server
+     examples: 
+        status = rpc.connect("ncacn_ip_tcp:localhost");
+        status = rpc.connect("ncacn_ip_tcp:localhost", "pipe_name");
+*/
+static int ejs_rpc_connect(MprVarHandle eid, int argc, char **argv)
 {
        const char *binding, *pipe_name;
-       const struct dcerpc_interface_table *iface;
+       const struct ndr_interface_table *iface;
        NTSTATUS status;
        struct dcerpc_pipe *p;
-       struct MprVar *conn;
+       struct cli_credentials *creds;
+       struct event_context *ev;
+       struct MprVar *this = mprGetProperty(ejsGetLocalObject(eid), "this", 0);
+       struct MprVar *credentials;
 
        /* validate arguments */
-       if (argc != 3 ||
-           argv[0]->type != MPR_TYPE_OBJECT ||
-           argv[1]->type != MPR_TYPE_STRING ||
-           argv[2]->type != MPR_TYPE_STRING) {
+       if (argc < 1) {
                ejsSetErrorMsg(eid, "rpc_connect invalid arguments");
                return -1;
        }
 
-       conn       = argv[0];
-       binding    = mprToString(argv[1]);
-       pipe_name  = mprToString(argv[2]);
+       binding    = argv[0];
+       if (strchr(binding, ':') == NULL) {
+               /* its an irpc connect */
+               return ejs_irpc_connect(eid, argc, argv);
+       }
+
+       if (argc > 1) {
+               pipe_name = argv[1];
+       } else {
+               pipe_name = mprToString(mprGetProperty(this, "pipe_name", NULL));
+       }
 
-       iface = idl_iface_by_name(pipe_name);
+       iface = ndr_table_by_name(pipe_name);
        if (iface == NULL) {
                status = NT_STATUS_OBJECT_NAME_INVALID;
                goto done;
        }
 
-       status = dcerpc_pipe_connect(mprMemCtx(), &p, binding, 
-                                    iface->uuid, iface->if_version, 
-                                    cmdline_credentials, NULL);
+       credentials = mprGetProperty(this, "credentials", NULL);
+       if (credentials) {
+               creds = (struct cli_credentials *)
+                               mprGetPtr(credentials, "creds");
+       } else {
+               creds = cmdline_credentials;
+       }
+       if (creds == NULL) {
+               creds = cli_credentials_init(mprMemCtx());
+               cli_credentials_guess(creds);
+               cli_credentials_set_anonymous(creds);
+       }
+
+       ev = event_context_find(mprMemCtx());
+
+       status = dcerpc_pipe_connect(this, &p, binding, iface, creds, ev);
        if (!NT_STATUS_IS_OK(status)) goto done;
 
        /* callers don't allocate ref vars in the ejs interface */
        p->conn->flags |= DCERPC_NDR_REF_ALLOC;
 
-       mprSetPtr(conn, "pipe", p);
-       mprSetPtr(conn, "iface", iface);
+       /* by making the pipe a child of the connection variable, it will
+          auto close when it goes out of scope in the script */
+       mprSetPtrChild(this, "pipe", p);
+
+done:
+       mpr_Return(eid, mprNTSTATUS(status));
+       return 0;
+}
+
+
+/*
+  make an irpc call - called via the same interface as rpc
+*/
+static int ejs_irpc_call(int eid, struct MprVar *io, 
+                        const struct ndr_interface_table *iface, int callnum,
+                        ejs_pull_function_t ejs_pull, ejs_push_function_t ejs_push)
+{
+       NTSTATUS status;
+       void *ptr;
+       struct ejs_rpc *ejs;
+       const struct ndr_interface_call *call;
+       struct ejs_irpc_connection *p;
+       struct irpc_request **reqs;
+       int i, count;
+       struct MprVar *results;
+
+       p = (struct ejs_irpc_connection *)mprGetThisPtr(eid, "irpc");
+
+       ejs = talloc(mprMemCtx(), struct ejs_rpc);
+       if (ejs == NULL) {
+               status = NT_STATUS_NO_MEMORY;
+               goto done;
+       }
+
+       call = &iface->calls[callnum];
+
+       ejs->eid = eid;
+       ejs->callname = call->name;
+
+       /* allocate the C structure */
+       ptr = talloc_zero_size(ejs, call->struct_size);
+       if (ptr == NULL) {
+               status = NT_STATUS_NO_MEMORY;
+               goto done;
+       }
+
+       /* convert the mpr object into a C structure */
+       status = ejs_pull(ejs, io, ptr);
+       if (!NT_STATUS_IS_OK(status)) {
+               goto done;
+       }
+
+       for (count=0;p->dest_ids[count].id;count++) /* noop */ ;
+
+       /* we need to make a call per server */
+       reqs = talloc_array(ejs, struct irpc_request *, count);
+       if (reqs == NULL) {
+               status = NT_STATUS_NO_MEMORY;
+               goto done;
+       }
+
+       /* make the actual calls */
+       for (i=0;i<count;i++) {
+               reqs[i] = irpc_call_send(p->msg_ctx, p->dest_ids[i], 
+                                        iface, callnum, ptr, ptr);
+               if (reqs[i] == NULL) {
+                       status = NT_STATUS_NO_MEMORY;
+                       goto done;
+               }
+               talloc_steal(reqs, reqs[i]);
+       }
+       
+       mprSetVar(io, "results", mprObject("results"));
+       results = mprGetProperty(io, "results", NULL);
+
+       /* and receive the results, placing them in io.results[i] */
+       for (i=0;i<count;i++) {
+               struct MprVar *output;
+
+               status = irpc_call_recv(reqs[i]);
+               if (!NT_STATUS_IS_OK(status)) {
+                       goto done;
+               }
+               status = ejs_push(ejs, io, ptr);
+               if (!NT_STATUS_IS_OK(status)) {
+                       goto done;
+               }
+
+               /* add to the results array */
+               output = mprGetProperty(io, "output", NULL);
+               if (output) {
+                       char idx[16];
+                       mprItoa(i, idx, sizeof(idx));
+                       mprSetProperty(results, idx, output);
+                       mprDeleteProperty(io, "output");
+               }
+       }
+       mprSetVar(results, "length", mprCreateIntegerVar(i));
 
 done:
-       ejsSetReturnValue(eid, mprNTSTATUS(status));
+       talloc_free(ejs);
+       mpr_Return(eid, mprNTSTATUS(status));
+       if (NT_STATUS_EQUAL(status, NT_STATUS_INTERNAL_ERROR)) {
+               return -1;
+       }
        return 0;
 }
 
 
 /*
-  make an rpc call
-     example:
-            status = rpc_call(conn, "echo_AddOne", io);
+  backend code for making an rpc call - this is called from the pidl generated ejs
+  code
 */
  int ejs_rpc_call(int eid, int argc, struct MprVar **argv,
-                 const char *callname,
+                 const struct ndr_interface_table *iface, int callnum,
                  ejs_pull_function_t ejs_pull, ejs_push_function_t ejs_push)
 {
-       struct MprVar *conn, *io;
-       const struct dcerpc_interface_table *iface;
+       struct MprVar *io;
        struct dcerpc_pipe *p;
-       const struct dcerpc_interface_call *call;
        NTSTATUS status;
        void *ptr;
        struct rpc_request *req;
-       int callnum;
+       struct ejs_rpc *ejs;
+       const struct ndr_interface_call *call;
 
-       if (argc != 2 ||
-           argv[0]->type != MPR_TYPE_OBJECT ||
-           argv[1]->type != MPR_TYPE_OBJECT) {
+       if (argc != 1 || argv[0]->type != MPR_TYPE_OBJECT) {
                ejsSetErrorMsg(eid, "rpc_call invalid arguments");
                return -1;
        }
            
-       conn     = argv[0];
-       io       = argv[1];
+       io       = argv[0];
+
+       if (mprGetThisPtr(eid, "irpc")) {
+               /* its an irpc call */
+               return ejs_irpc_call(eid, io, iface, callnum, ejs_pull, ejs_push);
+       }
 
        /* get the pipe info */
-       p = mprGetPtr(conn, "pipe");
-       iface = mprGetPtr(conn, "iface");
-       if (p == NULL || iface == NULL) {
+       p = mprGetThisPtr(eid, "pipe");
+       if (p == NULL) {
                ejsSetErrorMsg(eid, "rpc_call invalid pipe");
                return -1;
        }
 
-       /* find the call by name */
-       call = dcerpc_iface_find_call(iface, callname);
-       if (call == NULL) {
-               status = NT_STATUS_OBJECT_NAME_INVALID;
+       ejs = talloc(mprMemCtx(), struct ejs_rpc);
+       if (ejs == NULL) {
+               status = NT_STATUS_NO_MEMORY;
                goto done;
        }
-       callnum = call - iface->calls;
+
+       call = &iface->calls[callnum];
+
+       ejs->eid = eid;
+       ejs->callname = call->name;
 
        /* allocate the C structure */
-       ptr = talloc_zero_size(mprMemCtx(), call->struct_size);
+       ptr = talloc_zero_size(ejs, call->struct_size);
        if (ptr == NULL) {
                status = NT_STATUS_NO_MEMORY;
                goto done;
        }
 
        /* convert the mpr object into a C structure */
-       status = ejs_pull_rpc(eid, callname, io, ptr, ejs_pull);
+       status = ejs_pull(ejs, io, ptr);
        if (!NT_STATUS_IS_OK(status)) {
                goto done;
        }
 
+       /* make the actual call */
+       req = dcerpc_ndr_request_send(p, NULL, iface, callnum, ptr, ptr);
+
        /* if requested, print the structure */
        if (p->conn->flags & DCERPC_DEBUG_PRINT_IN) {
                ndr_print_function_debug(call->ndr_print, call->name, NDR_IN, ptr);
        }
 
-       /* make the actual call */
-       req = dcerpc_ndr_request_send(p, NULL, iface, callnum, ptr, ptr);
        if (req == NULL) {
                status = NT_STATUS_NO_MEMORY;
-               talloc_free(ptr);
                goto done;
        }
+
        status = dcerpc_ndr_request_recv(req);
+       if (!NT_STATUS_IS_OK(status)) {
+               goto done;
+       }
 
        /* print the 'out' structure, if needed */
        if (p->conn->flags & DCERPC_DEBUG_PRINT_OUT) {
                ndr_print_function_debug(call->ndr_print, call->name, NDR_OUT, ptr);
        }
 
-       status = ejs_push_rpc(eid, callname, io, ptr, ejs_push);
+       status = ejs_push(ejs, io, ptr);
 
-       talloc_free(ptr);
 done:
-       ejsSetReturnValue(eid, mprNTSTATUS(status));
+       talloc_free(ejs);
+       mpr_Return(eid, mprNTSTATUS(status));
        if (NT_STATUS_EQUAL(status, NT_STATUS_INTERNAL_ERROR)) {
                return -1;
        }
        return 0;
 }
 
-
 /*
-  setup C functions that be called from ejs
+  hook called by generated RPC interfaces at the end of their init routines
+  used to add generic operations on the pipe
 */
-void smb_setup_ejs_rpc(void)
+int ejs_rpc_init(struct MprVar *obj, const char *name)
 {
-       void setup_ejs_rpcecho(void);
-       void setup_ejs_samr(void);
-       void setup_ejs_misc(void);
-       void setup_ejs_security(void);
+       ndr_table_init();
 
-       ejsDefineCFunction(-1, "rpc_connect", ejs_rpc_connect, NULL, MPR_VAR_SCRIPT_HANDLE);
-
-       setup_ejs_rpcecho();
-       setup_ejs_samr();
-       setup_ejs_misc();
-       setup_ejs_security();
-}
-
-/*
-  setup constants for rpc calls
-*/
-void smb_setup_ejs_rpc_constants(int eid)
-{
-       struct MprVar v;
-
-       void setup_ejs_constants_rpcecho(int);
-       void setup_ejs_constants_samr(int);
-       void setup_ejs_constants_misc(int);
-       void setup_ejs_constants_security(int);
-
-       setup_ejs_constants_rpcecho(eid);
-       setup_ejs_constants_samr(eid);
-       setup_ejs_constants_misc(eid);
-       setup_ejs_constants_security(eid);
-       
-       v = mprCreatePtrVar(NULL, "NULL");
-       mprSetProperty(ejsGetGlobalObject(eid), "NULL", &v);
+       mprSetStringCFunction(obj, "connect", ejs_rpc_connect);
+       if (mprGetProperty(obj, "pipe_name", NULL) == NULL) {
+               mprSetVar(obj, "pipe_name", mprString(name));
+       }
+       return 0;
 }