diff options
author | Gerald Carter <jerry@samba.org> | 2005-06-08 22:10:34 +0000 |
---|---|---|
committer | Gerald (Jerry) Carter <jerry@samba.org> | 2007-10-10 10:57:08 -0500 |
commit | fed660877c16562265327c6093ea645cf4176b5c (patch) | |
tree | e92ae1356542ba095d806bbe1093fa56fbc8ddcc /source3/nsswitch/winbindd_dual.c | |
parent | 66bb4f03c3466205488f72e4878e8801c5bbb295 (diff) | |
download | samba-fed660877c16562265327c6093ea645cf4176b5c.tar.gz samba-fed660877c16562265327c6093ea645cf4176b5c.tar.bz2 samba-fed660877c16562265327c6093ea645cf4176b5c.zip |
r7415: * big change -- volker's new async winbindd from trunk
(This used to be commit a0ac9a8ffd4af31a0ebc423b4acbb2f043d865b8)
Diffstat (limited to 'source3/nsswitch/winbindd_dual.c')
-rw-r--r-- | source3/nsswitch/winbindd_dual.c | 445 |
1 files changed, 444 insertions, 1 deletions
diff --git a/source3/nsswitch/winbindd_dual.c b/source3/nsswitch/winbindd_dual.c index 587507ee29..f8b802dafa 100644 --- a/source3/nsswitch/winbindd_dual.c +++ b/source3/nsswitch/winbindd_dual.c @@ -4,6 +4,7 @@ Winbind background daemon Copyright (C) Andrew Tridgell 2002 + Copyright (C) Volker Lendecke 2004,2005 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 @@ -51,6 +52,39 @@ struct dual_list { static struct dual_list *dual_list; static struct dual_list *dual_list_end; +/* Read some data from a client connection */ + +static void dual_client_read(struct winbindd_cli_state *state) +{ + int n; + + /* Read data */ + + n = sys_read(state->sock, state->read_buf_len + + (char *)&state->request, + sizeof(state->request) - state->read_buf_len); + + DEBUG(10,("client_read: read %d bytes. Need %ld more for a full " + "request.\n", n, (unsigned long)(sizeof(state->request) - n - + state->read_buf_len) )); + + /* Read failed, kill client */ + + if (n == -1 || n == 0) { + DEBUG(5,("read failed on sock %d, pid %lu: %s\n", + state->sock, (unsigned long)state->pid, + (n == -1) ? strerror(errno) : "EOF")); + + state->finished = True; + return; + } + + /* Update client state */ + + state->read_buf_len += n; + state->last_access = time(NULL); +} + /* setup a select() including the dual daemon pipe */ @@ -176,7 +210,7 @@ void do_dual_daemon(void) main_loop_talloc_free(); /* fetch a request from the main daemon */ - winbind_client_read(&state); + dual_client_read(&state); if (state.finished) { /* we lost contact with our parent */ @@ -212,3 +246,412 @@ void do_dual_daemon(void) } } +/* + * 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. + */ + +struct winbindd_async_request { + struct winbindd_async_request *next, *prev; + TALLOC_CTX *mem_ctx; + struct winbindd_child *child; + struct winbindd_request *request; + struct winbindd_response *response; + void (*continuation)(void *private, BOOL success); + void *private; +}; + +static void async_request_sent(void *private, BOOL success); +static void async_reply_recv(void *private, 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, BOOL success), + void *private) +{ + struct winbindd_async_request *state, *tmp; + + SMB_ASSERT(continuation != NULL); + + state = TALLOC_P(mem_ctx, struct winbindd_async_request); + + if (state == NULL) { + DEBUG(0, ("talloc failed\n")); + continuation(private, False); + return; + } + + state->mem_ctx = mem_ctx; + state->child = child; + state->request = request; + state->response = response; + state->continuation = continuation; + state->private = private; + + DLIST_ADD_END(child->requests, state, tmp); + + schedule_async_request(child); + + return; +} + +static void async_request_sent(void *private, BOOL success) +{ + struct winbindd_async_request *state = + talloc_get_type_abort(private, struct winbindd_async_request); + + if (!success) { + DEBUG(5, ("Could not send async request\n")); + + state->response->length = sizeof(struct winbindd_response); + state->response->result = WINBINDD_ERROR; + state->continuation(state->private, False); + return; + } + + /* 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); +} + +static void async_reply_recv(void *private, BOOL success) +{ + struct winbindd_async_request *state = + talloc_get_type_abort(private, struct winbindd_async_request); + struct winbindd_child *child = state->child; + + state->response->length = sizeof(struct winbindd_response); + + if (!success) { + DEBUG(5, ("Could not receive async reply\n")); + state->response->result = WINBINDD_ERROR; + } + + if (state->response->result == WINBINDD_OK) + SMB_ASSERT(cache_retrieve_response(child->pid, + state->response)); + + DLIST_REMOVE(child->requests, state); + + schedule_async_request(child); + + state->continuation(state->private, True); +} + +static BOOL fork_domain_child(struct winbindd_child *child); + +static void schedule_async_request(struct winbindd_child *child) +{ + struct winbindd_async_request *request = child->requests; + + if (request == NULL) { + return; + } + + if (child->event.flags != 0) { + return; /* Busy */ + } + + if ((child->pid == 0) && (!fork_domain_child(child))) { + /* Cancel all outstanding requests */ + + while (request != NULL) { + /* request might be free'd in the continuation */ + struct winbindd_async_request *next = request->next; + request->continuation(request->private, False); + request = next; + } + return; + } + + setup_async_write(&child->event, request->request, + sizeof(*request->request), + async_request_sent, request); + return; +} + +struct domain_request_state { + TALLOC_CTX *mem_ctx; + struct winbindd_domain *domain; + struct winbindd_request *request; + struct winbindd_response *response; + void (*continuation)(void *private, BOOL success); + void *private; +}; + +static void domain_init_recv(void *private, BOOL success); + +void async_domain_request(TALLOC_CTX *mem_ctx, + struct winbindd_domain *domain, + struct winbindd_request *request, + struct winbindd_response *response, + void (*continuation)(void *private, BOOL success), + void *private) +{ + struct domain_request_state *state; + + if (domain->initialized) { + async_request(mem_ctx, &domain->child, request, response, + continuation, private); + return; + } + + state = TALLOC_P(mem_ctx, struct domain_request_state); + if (state == NULL) { + DEBUG(0, ("talloc failed\n")); + continuation(private, False); + return; + } + + state->mem_ctx = mem_ctx; + state->domain = domain; + state->request = request; + state->response = response; + state->continuation = continuation; + state->private = private; + + init_child_connection(domain, domain_init_recv, state); +} + +static void domain_init_recv(void *private, BOOL success) +{ + struct domain_request_state *state = + talloc_get_type_abort(private, struct domain_request_state); + + if (!success) { + DEBUG(5, ("Domain init returned an error\n")); + state->continuation(state->private, False); + return; + } + + async_request(state->mem_ctx, &state->domain->child, + state->request, state->response, + state->continuation, state->private); +} + +struct winbindd_child_dispatch_table { + enum winbindd_cmd cmd; + enum winbindd_result (*fn)(struct winbindd_domain *domain, + struct winbindd_cli_state *state); + const char *winbindd_cmd_name; +}; + +static struct winbindd_child_dispatch_table child_dispatch_table[] = { + + { WINBINDD_LOOKUPSID, winbindd_dual_lookupsid, "LOOKUPSID" }, + { WINBINDD_LOOKUPNAME, winbindd_dual_lookupname, "LOOKUPNAME" }, + { WINBINDD_LIST_TRUSTDOM, winbindd_dual_list_trusted_domains, + "LIST_TRUSTDOM" }, + { WINBINDD_INIT_CONNECTION, winbindd_dual_init_connection, + "INIT_CONNECTION" }, + { WINBINDD_GETDCNAME, winbindd_dual_getdcname, "GETDCNAME" }, + { WINBINDD_SHOW_SEQUENCE, winbindd_dual_show_sequence, + "SHOW_SEQUENCE" }, + { WINBINDD_PAM_AUTH, winbindd_dual_pam_auth, "PAM_AUTH" }, + { WINBINDD_PAM_AUTH_CRAP, winbindd_dual_pam_auth_crap, "AUTH_CRAP" }, + { WINBINDD_CHECK_MACHACC, winbindd_dual_check_machine_acct, + "CHECK_MACHACC" }, + { WINBINDD_DUAL_SID2UID, winbindd_dual_sid2uid, "DUAL_SID2UID" }, + { WINBINDD_DUAL_SID2GID, winbindd_dual_sid2gid, "DUAL_SID2GID" }, + { WINBINDD_DUAL_UID2NAME, winbindd_dual_uid2name, "DUAL_UID2NAME" }, + { WINBINDD_DUAL_NAME2UID, winbindd_dual_name2uid, "DUAL_NAME2UID" }, + { WINBINDD_DUAL_GID2NAME, winbindd_dual_gid2name, "DUAL_GID2NAME" }, + { WINBINDD_DUAL_NAME2GID, winbindd_dual_name2gid, "DUAL_NAME2GID" }, + { WINBINDD_DUAL_IDMAPSET, winbindd_dual_idmapset, "DUAL_IDMAPSET" }, + { WINBINDD_DUAL_USERINFO, winbindd_dual_userinfo, "DUAL_USERINFO" }, + { WINBINDD_ALLOCATE_RID, winbindd_dual_allocate_rid, "ALLOCATE_RID" }, + { WINBINDD_ALLOCATE_RID_AND_GID, winbindd_dual_allocate_rid_and_gid, + "ALLOCATE_RID_AND_GID" }, + { WINBINDD_GETUSERDOMGROUPS, winbindd_dual_getuserdomgroups, + "GETUSERDOMGROUPS" }, + { WINBINDD_DUAL_GETSIDALIASES, winbindd_dual_getsidaliases, + "GETSIDALIASES" }, + /* End of list */ + + { WINBINDD_NUM_CMDS, NULL, "NONE" } +}; + +static void child_process_request(struct winbindd_domain *domain, + struct winbindd_cli_state *state) +{ + struct winbindd_child_dispatch_table *table; + + /* Free response data - we may be interrupted and receive another + command before being able to send this data off. */ + + state->response.result = WINBINDD_ERROR; + state->response.length = sizeof(struct winbindd_response); + + state->mem_ctx = talloc_init("winbind request"); + if (state->mem_ctx == NULL) + return; + + /* Process command */ + + for (table = child_dispatch_table; table->fn; table++) { + if (state->request.cmd == table->cmd) { + DEBUG(10,("process_request: request fn %s\n", + table->winbindd_cmd_name )); + state->response.result = table->fn(domain, state); + break; + } + } + + if (!table->fn) { + DEBUG(10,("process_request: unknown request fn number %d\n", + (int)state->request.cmd )); + state->response.result = WINBINDD_ERROR; + } + + talloc_destroy(state->mem_ctx); +} + +void setup_domain_child(struct winbindd_domain *domain, + struct winbindd_child *child, + const char *explicit_logfile) +{ + if (explicit_logfile != NULL) { + pstr_sprintf(child->logfilename, "%s/log.winbindd-%s", + dyn_LOGFILEBASE, explicit_logfile); + } else if (domain != NULL) { + pstr_sprintf(child->logfilename, "%s/log.wb-%s", + dyn_LOGFILEBASE, domain->name); + } else { + smb_panic("Internal error: domain == NULL && " + "explicit_logfile == NULL"); + } + + child->domain = domain; +} + +struct winbindd_child *children = NULL; + +void winbind_child_died(pid_t pid) +{ + struct winbindd_child *child; + + for (child = children; child != NULL; child = child->next) { + if (child->pid == pid) { + break; + } + } + + if (child == NULL) { + DEBUG(0, ("Unknown child %d died!\n", pid)); + return; + } + + remove_fd_event(&child->event); + close(child->event.fd); + child->event.fd = 0; + child->event.flags = 0; + child->pid = 0; + + schedule_async_request(child); +} + +static BOOL fork_domain_child(struct winbindd_child *child) +{ + int fdpair[2]; + struct winbindd_cli_state state; + extern BOOL override_logfile; + + if (socketpair(AF_LOCAL, SOCK_STREAM, 0, fdpair) != 0) { + DEBUG(0, ("Could not open child pipe: %s\n", + strerror(errno))); + return False; + } + + ZERO_STRUCT(state); + state.pid = getpid(); + + child->pid = sys_fork(); + + if (child->pid == -1) { + DEBUG(0, ("Could not fork: %s\n", strerror(errno))); + return False; + } + + if (child->pid != 0) { + /* Parent */ + close(fdpair[0]); + child->next = child->prev = NULL; + DLIST_ADD(children, child); + child->event.fd = fdpair[1]; + child->event.flags = 0; + child->requests = NULL; + add_fd_event(&child->event); + return True; + } + + /* Child */ + + state.sock = fdpair[0]; + close(fdpair[1]); + + /* tdb needs special fork handling */ + if (tdb_reopen_all() == -1) { + DEBUG(0,("tdb_reopen_all failed.\n")); + _exit(0); + } + + close_conns_after_fork(); + + if (!override_logfile) { + lp_set_logfile(child->logfilename); + reopen_logs(); + } + + dual_daemon_pipe = -1; + opt_dual_daemon = False; + + while (1) { + /* free up any talloc memory */ + lp_talloc_free(); + main_loop_talloc_free(); + + /* fetch a request from the main daemon */ + dual_client_read(&state); + + if (state.finished) { + /* we lost contact with our parent */ + exit(0); + } + + /* process full rquests */ + if (state.read_buf_len == sizeof(state.request)) { + DEBUG(4,("child daemon request %d\n", + (int)state.request.cmd)); + + state.request.null_term = '\0'; + child_process_request(child->domain, &state); + + if (state.response.result == WINBINDD_OK) + cache_store_response(sys_getpid(), + &state.response); + + SAFE_FREE(state.response.extra_data); + + /* We just send the result code back, the result + * structure needs to be fetched via the + * winbindd_cache. Hmm. That needs fixing... */ + + if (write_data(state.sock, + (void *)&state.response.result, + sizeof(state.response.result)) != + sizeof(state.response.result)) { + DEBUG(0, ("Could not write result\n")); + exit(1); + } + + state.read_buf_len = 0; + } + } +} |