rpc_client: retry open on STATUS_PIPE_NOT_AVAILABLE
authorDavid Disseldorp <ddiss@samba.org>
Mon, 3 Mar 2014 18:49:35 +0000 (19:49 +0100)
committerKarolin Seeger <kseeger@samba.org>
Mon, 10 Mar 2014 15:20:47 +0000 (16:20 +0100)
Windows Server starts some named pipe services on demand, and responds
to initial open requests with STATUS_PIPE_NOT_AVAILABLE. The FssagentRpc
named pipe on Windows Server 2012 exhibits this behaviour.

This change sees rpcclient retry named pipe open requests when the
server responds with STATUS_PIPE_NOT_AVAILABLE. The retry logic is
contained in an asynchronous tevent_timer callback, to allow for
non-blocking callers.

Signed-off-by: David Disseldorp <ddiss@samba.org>
Reviewed-by: Jeremy Allison <jra@samba.org>
(cherry picked from commit ebe6627c1f0e6b488a0c456860a055fd5701e84d)
[ddiss@samba.org: rebasead for 4.1 without 46d29d46bc065d51e3f7ca6892]

Bug: https://bugzilla.samba.org/show_bug.cgi?id=10484
Initial FSRVP rpcclient requests fail with NT_STATUS_PIPE_NOT_AVAILABLE

source3/rpc_client/rpc_transport_np.c

index 0be07eba75c7917c9d0bd9d9dcb2b4ac35530a45..1fd5ca92916ae84d63ca5e2f4613139439c1b65c 100644 (file)
 #include "../lib/util/tevent_ntstatus.h"
 #include "rpc_client/rpc_transport.h"
 #include "libsmb/cli_np_tstream.h"
+#include "client.h"
 
 #undef DBGC_CLASS
 #define DBGC_CLASS DBGC_RPC_CLI
 
 struct rpc_transport_np_init_state {
        struct rpc_cli_transport *transport;
+       int retries;
+       struct tevent_context *ev;
+       struct cli_state *cli;
+       struct timeval abs_timeout;
+       const char *pipe_name;
 };
 
 static void rpc_transport_np_init_pipe_open(struct tevent_req *subreq);
@@ -38,7 +44,6 @@ struct tevent_req *rpc_transport_np_init_send(TALLOC_CTX *mem_ctx,
 {
        struct tevent_req *req;
        struct rpc_transport_np_init_state *state;
-       const char *pipe_name;
        struct tevent_req *subreq;
 
        req = tevent_req_create(mem_ctx, &state,
@@ -47,16 +52,19 @@ struct tevent_req *rpc_transport_np_init_send(TALLOC_CTX *mem_ctx,
                return NULL;
        }
 
-       pipe_name = get_pipe_name_from_syntax(state, abstract_syntax);
-       if (tevent_req_nomem(pipe_name, req)) {
+       state->ev = ev;
+       state->cli = cli;
+       state->abs_timeout = timeval_current_ofs_msec(cli->timeout);
+       state->pipe_name = get_pipe_name_from_syntax(state, abstract_syntax);
+       if (tevent_req_nomem(state->pipe_name, req)) {
                return tevent_req_post(req, ev);
        }
 
-       while (pipe_name[0] == '\\') {
-               pipe_name++;
+       while (state->pipe_name[0] == '\\') {
+               state->pipe_name++;
        }
 
-       subreq = tstream_cli_np_open_send(state, ev, cli, pipe_name);
+       subreq = tstream_cli_np_open_send(state, ev, cli, state->pipe_name);
        if (tevent_req_nomem(subreq, req)) {
                return tevent_req_post(req, ev);
        }
@@ -65,6 +73,25 @@ struct tevent_req *rpc_transport_np_init_send(TALLOC_CTX *mem_ctx,
        return req;
 }
 
+static void rpc_transport_np_init_pipe_open_retry(struct tevent_context *ev,
+                                                 struct tevent_timer *te,
+                                                 struct timeval t,
+                                                 void *priv_data)
+{
+       struct tevent_req *subreq;
+       struct tevent_req *req = talloc_get_type(priv_data, struct tevent_req);
+       struct rpc_transport_np_init_state *state = tevent_req_data(
+               req, struct rpc_transport_np_init_state);
+
+       subreq = tstream_cli_np_open_send(state, ev, state->cli,
+                                         state->pipe_name);
+       if (tevent_req_nomem(subreq, req)) {
+               return;
+       }
+       tevent_req_set_callback(subreq, rpc_transport_np_init_pipe_open, req);
+       state->retries++;
+}
+
 static void rpc_transport_np_init_pipe_open(struct tevent_req *subreq)
 {
        struct tevent_req *req = tevent_req_callback_data(
@@ -76,7 +103,23 @@ static void rpc_transport_np_init_pipe_open(struct tevent_req *subreq)
 
        status = tstream_cli_np_open_recv(subreq, state, &stream);
        TALLOC_FREE(subreq);
-       if (!NT_STATUS_IS_OK(status)) {
+       if (NT_STATUS_EQUAL(status, NT_STATUS_PIPE_NOT_AVAILABLE)
+                               && (!timeval_expired(&state->abs_timeout))) {
+               struct tevent_timer *te;
+               /*
+                * Retry on STATUS_PIPE_NOT_AVAILABLE, Windows starts some
+                * servers (FssagentRpc) on demand.
+                */
+               DEBUG(2, ("RPC pipe %s not available, retry %d\n",
+                         state->pipe_name, state->retries));
+               te = tevent_add_timer(state->ev, state,
+                                timeval_current_ofs_msec(100 * state->retries),
+                                rpc_transport_np_init_pipe_open_retry, req);
+               if (tevent_req_nomem(te, req)) {
+                       return;
+               }
+               return;
+       } else if (!NT_STATUS_IS_OK(status)) {
                tevent_req_nterror(req, status);
                return;
        }