summaryrefslogtreecommitdiff
path: root/source3/winbindd/winbindd_dual.c
diff options
context:
space:
mode:
Diffstat (limited to 'source3/winbindd/winbindd_dual.c')
-rw-r--r--source3/winbindd/winbindd_dual.c386
1 files changed, 156 insertions, 230 deletions
diff --git a/source3/winbindd/winbindd_dual.c b/source3/winbindd/winbindd_dual.c
index 9a5098f661..53a5bc47a4 100644
--- a/source3/winbindd/winbindd_dual.c
+++ b/source3/winbindd/winbindd_dual.c
@@ -29,6 +29,7 @@
#include "includes.h"
#include "winbindd.h"
+#include "../../nsswitch/libwbclient/wbc_async.h"
#undef DBGC_CLASS
#define DBGC_CLASS DBGC_WINBIND
@@ -85,254 +86,188 @@ static void child_read_request(struct winbindd_cli_state *state)
}
/*
- * Machinery for async requests sent to children. You set up a
- * winbindd_request, select a child to query, and issue a async_request
- * call. When the request is completed, the callback function you specified is
- * called back with the private pointer you gave to async_request.
+ * Do winbind child async request. This is not simply wb_simple_trans. We have
+ * to do the queueing ourselves because while a request is queued, the child
+ * might have crashed, and we have to re-fork it in the _trigger function.
*/
-struct winbindd_async_request {
- struct winbindd_async_request *next, *prev;
- TALLOC_CTX *mem_ctx;
+struct wb_child_request_state {
+ struct tevent_context *ev;
struct winbindd_child *child;
struct winbindd_request *request;
struct winbindd_response *response;
- void (*continuation)(void *private_data, bool success);
- struct timed_event *reply_timeout_event;
- pid_t child_pid; /* pid of the child we're waiting on. Used to detect
- a restart of the child (child->pid != child_pid). */
- void *private_data;
};
-static void async_request_fail(struct winbindd_async_request *state);
-static void async_main_request_sent(void *private_data, bool success);
-static void async_request_sent(void *private_data, bool success);
-static void async_reply_recv(void *private_data, bool success);
-static void schedule_async_request(struct winbindd_child *child);
-
-void async_request(TALLOC_CTX *mem_ctx, struct winbindd_child *child,
- struct winbindd_request *request,
- struct winbindd_response *response,
- void (*continuation)(void *private_data, bool success),
- void *private_data)
-{
- struct winbindd_async_request *state;
-
- SMB_ASSERT(continuation != NULL);
+static bool fork_domain_child(struct winbindd_child *child);
- DEBUG(10, ("Sending request to child pid %d (domain=%s)\n",
- (int)child->pid,
- (child->domain != NULL) ? child->domain->name : "''"));
+static void wb_child_request_trigger(struct tevent_req *req,
+ void *private_data);
+static void wb_child_request_done(struct tevent_req *subreq);
- state = TALLOC_P(mem_ctx, struct winbindd_async_request);
+struct tevent_req *wb_child_request_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct winbindd_child *child,
+ struct winbindd_request *request)
+{
+ struct tevent_req *req;
+ struct wb_child_request_state *state;
- if (state == NULL) {
- DEBUG(0, ("talloc failed\n"));
- continuation(private_data, False);
- return;
+ req = tevent_req_create(mem_ctx, &state,
+ struct wb_child_request_state);
+ if (req == NULL) {
+ return NULL;
}
- state->mem_ctx = mem_ctx;
+ state->ev = ev;
state->child = child;
- state->reply_timeout_event = NULL;
state->request = request;
- state->response = response;
- state->continuation = continuation;
- state->private_data = private_data;
-
- DLIST_ADD_END(child->requests, state, struct winbindd_async_request *);
- schedule_async_request(child);
-
- return;
+ if (!tevent_queue_add(child->queue, ev, req,
+ wb_child_request_trigger, NULL)) {
+ tevent_req_nomem(NULL, req);
+ return tevent_req_post(req, ev);
+ }
+ return req;
}
-static void async_main_request_sent(void *private_data, bool success)
+static void wb_child_request_trigger(struct tevent_req *req,
+ void *private_data)
{
- struct winbindd_async_request *state =
- talloc_get_type_abort(private_data, struct winbindd_async_request);
+ struct wb_child_request_state *state = tevent_req_data(
+ req, struct wb_child_request_state);
+ struct tevent_req *subreq;
- if (!success) {
- DEBUG(5, ("Could not send async request\n"));
- async_request_fail(state);
+ if ((state->child->pid == 0) && (!fork_domain_child(state->child))) {
+ tevent_req_error(req, errno);
return;
}
- if (state->request->extra_len == 0) {
- async_request_sent(private_data, True);
+ subreq = wb_simple_trans_send(state, winbind_event_context(), NULL,
+ state->child->sock, state->request);
+ if (tevent_req_nomem(subreq, req)) {
return;
}
+ tevent_req_set_callback(subreq, wb_child_request_done, req);
- setup_async_write(&state->child->event, state->request->extra_data.data,
- state->request->extra_len,
- async_request_sent, state);
-}
-
-/****************************************************************
- Handler triggered if the child winbindd doesn't respond within
- a given timeout.
-****************************************************************/
-
-static void async_request_timeout_handler(struct event_context *ctx,
- struct timed_event *te,
- struct timeval now,
- void *private_data)
-{
- struct winbindd_async_request *state =
- talloc_get_type_abort(private_data, struct winbindd_async_request);
-
- DEBUG(0,("async_request_timeout_handler: child pid %u is not responding. "
- "Closing connection to it.\n",
- (unsigned int)state->child_pid ));
-
- /* Deal with the reply - set to error. */
- async_reply_recv(private_data, False);
+ if (!tevent_req_set_endtime(req, state->ev,
+ timeval_current_ofs(300, 0))) {
+ tevent_req_nomem(NULL, req);
+ return;
+ }
}
-/**************************************************************
- Common function called on both async send and recv fail.
- Cleans up the child and schedules the next request.
-**************************************************************/
-
-static void async_request_fail(struct winbindd_async_request *state)
+static void wb_child_request_done(struct tevent_req *subreq)
{
- DLIST_REMOVE(state->child->requests, state);
-
- TALLOC_FREE(state->reply_timeout_event);
-
- /* If child exists and is not already reaped,
- send kill signal to child. */
-
- if ((state->child->pid != (pid_t)0) &&
- (state->child->pid != (pid_t)-1) &&
- (state->child->pid == state->child_pid)) {
- kill(state->child_pid, SIGTERM);
-
- /*
- * Close the socket to the child.
- */
- winbind_child_died(state->child_pid);
+ struct tevent_req *req = tevent_req_callback_data(
+ subreq, struct tevent_req);
+ struct wb_child_request_state *state = tevent_req_data(
+ req, struct wb_child_request_state);
+ int ret, err;
+
+ ret = wb_simple_trans_recv(subreq, state, &state->response, &err);
+ TALLOC_FREE(subreq);
+ if (ret == -1) {
+ tevent_req_error(req, err);
+ return;
}
-
- state->response->length = sizeof(struct winbindd_response);
- state->response->result = WINBINDD_ERROR;
- state->continuation(state->private_data, False);
+ tevent_req_done(req);
}
-static void async_request_sent(void *private_data_data, bool success)
+int wb_child_request_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
+ struct winbindd_response **presponse, int *err)
{
- struct winbindd_async_request *state =
- talloc_get_type_abort(private_data_data, struct winbindd_async_request);
+ struct wb_child_request_state *state = tevent_req_data(
+ req, struct wb_child_request_state);
- if (!success) {
- DEBUG(5, ("Could not send async request to child pid %u\n",
- (unsigned int)state->child_pid ));
- async_request_fail(state);
- return;
+ if (tevent_req_is_unix_error(req, err)) {
+ return -1;
}
-
- /* Request successfully sent to the child, setup the wait for reply */
-
- setup_async_read(&state->child->event,
- &state->response->result,
- sizeof(state->response->result),
- async_reply_recv, state);
-
- /*
- * Set up a timeout of 300 seconds for the response.
- * If we don't get it close the child socket and
- * report failure.
- */
-
- state->reply_timeout_event = event_add_timed(winbind_event_context(),
- NULL,
- timeval_current_ofs(300,0),
- async_request_timeout_handler,
- state);
- if (!state->reply_timeout_event) {
- smb_panic("async_request_sent: failed to add timeout handler.\n");
+ if (state->response->result != WINBINDD_OK) {
+ *err = EIO; /* EIO doesn't fit, but what would be better? */
+ return -1;
}
+ *presponse = talloc_move(mem_ctx, &state->response);
+ return 0;
}
-static void async_reply_recv(void *private_data, bool success)
-{
- struct winbindd_async_request *state =
- talloc_get_type_abort(private_data, struct winbindd_async_request);
- struct winbindd_child *child = state->child;
-
- TALLOC_FREE(state->reply_timeout_event);
-
- state->response->length = sizeof(struct winbindd_response);
-
- if (!success) {
- DEBUG(5, ("Could not receive async reply from child pid %u\n",
- (unsigned int)state->child_pid ));
-
- cache_cleanup_response(state->child_pid);
- async_request_fail(state);
- return;
- }
-
- SMB_ASSERT(cache_retrieve_response(state->child_pid,
- state->response));
-
- cache_cleanup_response(state->child_pid);
-
- DLIST_REMOVE(child->requests, state);
-
- schedule_async_request(child);
+/*
+ * Machinery for async requests sent to children. You set up a
+ * winbindd_request, select a child to query, and issue a async_request
+ * call. When the request is completed, the callback function you specified is
+ * called back with the private pointer you gave to async_request.
+ */
- state->continuation(state->private_data, True);
-}
+struct winbindd_async_request {
+ struct winbindd_async_request *next, *prev;
+ TALLOC_CTX *mem_ctx;
+ struct winbindd_child *child;
+ struct winbindd_response *response;
+ void (*continuation)(void *private_data, bool success);
+ struct timed_event *reply_timeout_event;
+ pid_t child_pid; /* pid of the child we're waiting on. Used to detect
+ a restart of the child (child->pid != child_pid). */
+ void *private_data;
+};
static bool fork_domain_child(struct winbindd_child *child);
+static void async_request_done(struct tevent_req *req);
-static void schedule_async_request(struct winbindd_child *child)
+void async_request(TALLOC_CTX *mem_ctx, struct winbindd_child *child,
+ struct winbindd_request *request,
+ struct winbindd_response *response,
+ void (*continuation)(void *private_data, bool success),
+ void *private_data)
{
- struct winbindd_async_request *request = child->requests;
+ struct winbindd_async_request *state;
+ struct tevent_req *req;
- if (request == NULL) {
- return;
- }
+ DEBUG(10, ("Sending request to child pid %d (domain=%s)\n",
+ (int)child->pid,
+ (child->domain != NULL) ? child->domain->name : "''"));
- if (child->event.flags != 0) {
- return; /* Busy */
+ state = talloc(mem_ctx, struct winbindd_async_request);
+ if (state == NULL) {
+ DEBUG(0, ("talloc failed\n"));
+ continuation(private_data, False);
+ return;
}
- /*
- * This may be a reschedule, so we might
- * have an existing timeout event pending on
- * the first entry in the child->requests list
- * (we only send one request at a time).
- * Ensure we free it before we reschedule.
- * Bug #5814, from hargagan <shargagan@novell.com>.
- * JRA.
- */
-
- TALLOC_FREE(request->reply_timeout_event);
+ state->mem_ctx = mem_ctx;
+ state->child = child;
+ state->reply_timeout_event = NULL;
+ state->response = response;
+ state->continuation = continuation;
+ state->private_data = private_data;
- if ((child->pid == 0) && (!fork_domain_child(child))) {
- /* fork_domain_child failed.
- Cancel all outstanding requests */
+ request->pid = child->pid;
- while (request != NULL) {
- /* request might be free'd in the continuation */
- struct winbindd_async_request *next = request->next;
+ req = wb_child_request_send(state, winbind_event_context(),
+ child, request);
+ if (req == NULL) {
+ DEBUG(0, ("wb_child_request_send failed\n"));
+ continuation(private_data, false);
+ return;
+ }
+ tevent_req_set_callback(req, async_request_done, state);
+}
- async_request_fail(request);
- request = next;
- }
+static void async_request_done(struct tevent_req *req)
+{
+ struct winbindd_async_request *state = tevent_req_callback_data(
+ req, struct winbindd_async_request);
+ struct winbindd_response *response;
+ int ret, err;
+
+ ret = wb_child_request_recv(req, state, &response, &err);
+ TALLOC_FREE(req);
+ if (ret == -1) {
+ DEBUG(2, ("wb_child_request_recv failed: %s\n",
+ strerror(err)));
+ state->continuation(state->private_data, false);
return;
}
-
- /* Now we know who we're sending to - remember the pid. */
- request->child_pid = child->pid;
-
- setup_async_write(&child->event, request->request,
- sizeof(*request->request),
- async_main_request_sent, request);
-
- return;
+ *state->response = *response;
+ state->continuation(state->private_data, true);
}
struct domain_request_state {
@@ -477,6 +412,8 @@ void setup_child(struct winbindd_child *child,
child->domain = NULL;
child->table = table;
+ child->queue = tevent_queue_create(NULL, "winbind_child");
+ SMB_ASSERT(child->queue != NULL);
}
struct winbindd_child *children = NULL;
@@ -500,24 +437,9 @@ void winbind_child_died(pid_t pid)
DLIST_REMOVE(children, child);
- remove_fd_event(&child->event);
- close(child->event.fd);
- child->event.fd = 0;
- child->event.flags = 0;
+ close(child->sock);
+ child->sock = -1;
child->pid = 0;
-
- if (child->requests) {
- /*
- * schedule_async_request() will also
- * clear this event but the call is
- * idempotent so it doesn't hurt to
- * cover all possible future code
- * paths. JRA.
- */
- TALLOC_FREE(child->requests->reply_timeout_event);
- }
-
- schedule_async_request(child);
}
/* Ensure any negative cache entries with the netbios or realm names are removed. */
@@ -1176,11 +1098,6 @@ bool winbindd_reinit_after_fork(const char *logfilename)
/* Destroy all possible events in child list. */
for (cl = children; cl != NULL; cl = cl->next) {
- struct winbindd_async_request *request;
-
- for (request = cl->requests; request; request = request->next) {
- TALLOC_FREE(request->reply_timeout_event);
- }
TALLOC_FREE(cl->lockout_policy_event);
TALLOC_FREE(cl->machine_password_change_event);
@@ -1247,9 +1164,7 @@ static bool fork_domain_child(struct winbindd_child *child)
close(fdpair[0]);
child->next = child->prev = NULL;
DLIST_ADD(children, child);
- child->event.fd = fdpair[1];
- child->event.flags = 0;
- add_fd_event(&child->event);
+ child->sock = fdpair[1];
return True;
}
@@ -1358,6 +1273,8 @@ static bool fork_domain_child(struct winbindd_child *child)
struct timeval *tp;
struct timeval now;
TALLOC_CTX *frame = talloc_stackframe();
+ struct iovec iov[2];
+ int iov_count;
if (run_events(winbind_event_context(), 0, NULL, NULL)) {
TALLOC_FREE(frame);
@@ -1428,18 +1345,27 @@ static bool fork_domain_child(struct winbindd_child *child)
state.request->null_term = '\0';
child_process_request(child, &state);
+ DEBUG(4, ("Finished processing child request %d\n",
+ (int)state.request->cmd));
+
SAFE_FREE(state.request->extra_data.data);
- cache_store_response(sys_getpid(), &state.response);
+ iov[0].iov_base = (void *)&state.response;
+ iov[0].iov_len = sizeof(struct winbindd_response);
+ iov_count = 1;
+
+ if (state.response.length > sizeof(struct winbindd_response)) {
+ iov[1].iov_base =
+ (void *)state.response.extra_data.data;
+ iov[1].iov_len = state.response.length-iov[0].iov_len;
+ iov_count = 2;
+ }
- /* We just send the result code back, the result
- * structure needs to be fetched via the
- * winbindd_cache. Hmm. That needs fixing... */
+ DEBUG(10, ("Writing %d bytes to parent\n",
+ (int)state.response.length));
- if (write_data(state.sock,
- (const char *)&state.response.result,
- sizeof(state.response.result)) !=
- sizeof(state.response.result)) {
+ if (write_data_iov(state.sock, iov, iov_count) !=
+ state.response.length) {
DEBUG(0, ("Could not write result\n"));
exit(1);
}