From 1d56c74b565fa02592ab48d8b82fcf03ad78daf3 Mon Sep 17 00:00:00 2001 From: David Disseldorp Date: Mon, 3 Mar 2014 19:49:35 +0100 Subject: [PATCH] rpc_client: retry open on STATUS_PIPE_NOT_AVAILABLE 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 Reviewed-by: Jeremy Allison (cherry picked from commit ebe6627c1f0e6b488a0c456860a055fd5701e84d) [ddiss@samba.org: rebasead for 4.1 without 46d29d46bc065d51e3f7ca6892] --- source3/rpc_client/rpc_transport_np.c | 57 ++++++++++++++++++++++++++++++----- 1 file changed, 50 insertions(+), 7 deletions(-) diff --git a/source3/rpc_client/rpc_transport_np.c b/source3/rpc_client/rpc_transport_np.c index 0be07eb..1fd5ca9 100644 --- a/source3/rpc_client/rpc_transport_np.c +++ b/source3/rpc_client/rpc_transport_np.c @@ -21,12 +21,18 @@ #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; } -- 1.8.4.5