rpc_server3: Use fdopen_keepfd()
[samba.git] / source3 / rpc_server / rpc_worker.c
index c41e72381fbf70888cc19d74341e2dd60fadf134..81c7a90972180391a53fd6e2b3412e80c79ecfce 100644 (file)
@@ -24,6 +24,7 @@
 #include "source3/librpc/gen_ndr/ndr_rpc_host.h"
 #include "lib/util/debug.h"
 #include "lib/util/fault.h"
+#include "lib/util/util_file.h"
 #include "rpc_server.h"
 #include "rpc_pipes.h"
 #include "source3/smbd/proto.h"
@@ -95,11 +96,13 @@ static void rpc_worker_print_interface(
 
 static NTSTATUS rpc_worker_report_status(struct rpc_worker *worker)
 {
-       uint8_t buf[12];
+       uint8_t buf[16];
        DATA_BLOB blob = { .data = buf, .length = sizeof(buf), };
        enum ndr_err_code ndr_err;
        NTSTATUS status;
 
+       worker->status.num_association_groups = worker->dce_ctx->assoc_groups_num;
+
        if (DEBUGLEVEL >= 10) {
                NDR_PRINT_DEBUG(rpc_worker_status, &worker->status);
        }
@@ -129,7 +132,19 @@ static void rpc_worker_connection_terminated(
        NTSTATUS status;
        bool found = false;
 
-       SMB_ASSERT(worker->status.num_clients > 0);
+       /*
+        * We need to drop the association group reference
+        * explicitly here in order to avoid the order given
+        * by the destructors. rpc_worker_report_status() below,
+        * expects worker->dce_ctx->assoc_groups_num to be updated
+        * already.
+        */
+       if (conn->assoc_group != NULL) {
+               talloc_unlink(conn, conn->assoc_group);
+               conn->assoc_group = NULL;
+       }
+
+       SMB_ASSERT(worker->status.num_connections > 0);
 
        for (w = worker->conns; w != NULL; w = w->next) {
                if (w == ncacn_conn) {
@@ -141,7 +156,7 @@ static void rpc_worker_connection_terminated(
 
        DLIST_REMOVE(worker->conns, ncacn_conn);
 
-       worker->status.num_clients -= 1;
+       worker->status.num_connections -= 1;
 
        status = rpc_worker_report_status(worker);
        if (!NT_STATUS_IS_OK(status)) {
@@ -376,6 +391,8 @@ static void rpc_worker_new_client(
                                  strerror(errno));
                        goto fail;
                }
+               /* as server we want to fail early */
+               tstream_bsd_fail_readv_first_error(tstream, true);
        }
        sock = -1;
 
@@ -467,7 +484,7 @@ static void rpc_worker_new_client(
        TALLOC_FREE(client);
 
        DLIST_ADD(worker->conns, ncacn_conn);
-       worker->status.num_clients += 1;
+       worker->status.num_connections += 1;
 
        dcesrv_loop_next_packet(dcesrv_conn, pkt, buffer);
 
@@ -553,7 +570,6 @@ static bool rpc_worker_status_filter(
                private_data, struct rpc_worker);
        struct dcerpc_ncacn_conn *conn = NULL;
        FILE *f = NULL;
-       int fd;
 
        if (rec->msg_type != MSG_RPC_DUMP_STATUS) {
                return false;
@@ -564,18 +580,9 @@ static bool rpc_worker_status_filter(
                return false;
        }
 
-       fd = dup(rec->fds[0]);
-       if (fd == -1) {
-               DBG_DEBUG("dup(%"PRIi64") failed: %s\n",
-                         rec->fds[0],
-                         strerror(errno));
-               return false;
-       }
-
-       f = fdopen(fd, "w");
+       f = fdopen_keepfd(rec->fds[0], "w");
        if (f == NULL) {
-               DBG_DEBUG("fdopen failed: %s\n", strerror(errno));
-               close(fd);
+               DBG_DEBUG("fdopen_keepfd failed: %s\n", strerror(errno));
                return false;
        }
 
@@ -612,7 +619,7 @@ static struct dcesrv_assoc_group *rpc_worker_assoc_group_reference(
        void *id_ptr = NULL;
 
        /* find an association group given a assoc_group_id */
-       id_ptr = idr_find(conn->dce_ctx->assoc_groups_idr, id & 0xffffff);
+       id_ptr = idr_find(conn->dce_ctx->assoc_groups_idr, id & UINT16_MAX);
        if (id_ptr == NULL) {
                DBG_NOTICE("Failed to find assoc_group 0x%08x\n", id);
                return NULL;
@@ -647,11 +654,14 @@ static int rpc_worker_assoc_group_destructor(
 
        ret = idr_remove(
                assoc_group->dce_ctx->assoc_groups_idr,
-               assoc_group->id & 0xffffff);
+               assoc_group->id & UINT16_MAX);
        if (ret != 0) {
                DBG_WARNING("Failed to remove assoc_group 0x%08x\n",
                            assoc_group->id);
        }
+
+       SMB_ASSERT(assoc_group->dce_ctx->assoc_groups_num > 0);
+       assoc_group->dce_ctx->assoc_groups_num -= 1;
        return 0;
 }
 
@@ -659,7 +669,7 @@ static int rpc_worker_assoc_group_destructor(
   allocate a new association group
  */
 static struct dcesrv_assoc_group *rpc_worker_assoc_group_new(
-       struct dcesrv_connection *conn, uint8_t worker_index)
+       struct dcesrv_connection *conn, uint16_t worker_index)
 {
        struct dcesrv_context *dce_ctx = conn->dce_ctx;
        const struct dcesrv_endpoint *endpoint = conn->endpoint;
@@ -673,6 +683,11 @@ static struct dcesrv_assoc_group *rpc_worker_assoc_group_new(
                return NULL;
        }
 
+       /*
+        * We use 16-bit to encode the worker index,
+        * have 16-bits left within the worker to form a
+        * 32-bit association group id.
+        */
        id = idr_get_new_random(
                dce_ctx->assoc_groups_idr, assoc_group, 1, UINT16_MAX);
        if (id == -1) {
@@ -680,12 +695,15 @@ static struct dcesrv_assoc_group *rpc_worker_assoc_group_new(
                DBG_WARNING("Out of association groups!\n");
                return NULL;
        }
-       assoc_group->id = (worker_index << 24) + id;
+       assoc_group->id = (((uint32_t)worker_index) << 16) | id;
        assoc_group->transport = transport;
        assoc_group->dce_ctx = dce_ctx;
 
        talloc_set_destructor(assoc_group, rpc_worker_assoc_group_destructor);
 
+       SMB_ASSERT(dce_ctx->assoc_groups_num < UINT16_MAX);
+       dce_ctx->assoc_groups_num += 1;
+
        return assoc_group;
 }
 
@@ -698,10 +716,10 @@ static NTSTATUS rpc_worker_assoc_group_find(
        uint32_t assoc_group_id = call->pkt.u.bind.assoc_group_id;
 
        if (assoc_group_id != 0) {
-               uint8_t worker_index = (assoc_group_id & 0xff000000) >> 24;
+               uint16_t worker_index = (assoc_group_id & 0xffff0000) >> 16;
                if (worker_index != w->status.worker_index) {
-                       DBG_DEBUG("Wrong worker id %"PRIu8", "
-                                 "expected %"PRIu8"\n",
+                       DBG_DEBUG("Wrong worker id %"PRIu16", "
+                                 "expected %"PRIu32"\n",
                                  worker_index,
                                  w->status.worker_index);
                        return NT_STATUS_NOT_FOUND;
@@ -801,7 +819,7 @@ static struct tevent_req *rpc_worker_send(
                tevent_req_error(req, EINVAL);
                return tevent_req_post(req, ev);
        }
-       if ((worker_index < 0) || ((unsigned)worker_index > UINT32_MAX)) {
+       if ((worker_index < 0) || ((unsigned)worker_index > UINT16_MAX)) {
                DBG_ERR("Invalid worker index %d\n", worker_index);
                tevent_req_error(req, EINVAL);
                return tevent_req_post(req, ev);
@@ -1125,10 +1143,10 @@ int rpc_worker_main(
 
        reopen_logs();
 
-       DEBUG(0, ("%s version %s started.\n",
-                 progname,
-                 samba_version_string()));
-       DEBUGADD(0,("%s\n", COPYRIGHT_STARTUP_MESSAGE));
+       DBG_STARTUP_NOTICE("%s version %s started.\n%s\n",
+                          progname,
+                          samba_version_string(),
+                          samba_copyright_string());
 
        msg_ctx = global_messaging_context();
        if (msg_ctx == NULL) {