r10400: commit merge patch from jra
[Samba/gbeck.git] / source / nsswitch / winbindd_dual.c
blobec0b7a36e2fb976fb192efa503c49cb763e024e2
1 /*
2 Unix SMB/CIFS implementation.
4 Winbind background daemon
6 Copyright (C) Andrew Tridgell 2002
7 Copyright (C) Volker Lendecke 2004,2005
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 the idea of the optional dual daemon mode is ot prevent slow domain
26 responses from clagging up the rest of the system. When in dual
27 daemon mode winbindd always responds to requests from cache if the
28 request is in cache, and if the cached answer is stale then it asks
29 the "dual daemon" to update the cache for that request
33 #include "includes.h"
34 #include "winbindd.h"
36 #undef DBGC_CLASS
37 #define DBGC_CLASS DBGC_WINBIND
39 /* Read some data from a client connection */
41 static void dual_client_read(struct winbindd_cli_state *state)
43 int n;
45 /* Read data */
47 n = sys_read(state->sock, state->read_buf_len +
48 (char *)&state->request,
49 sizeof(state->request) - state->read_buf_len);
51 DEBUG(10,("client_read: read %d bytes. Need %ld more for a full "
52 "request.\n", n, (unsigned long)(sizeof(state->request) - n -
53 state->read_buf_len) ));
55 /* Read failed, kill client */
57 if (n == -1 || n == 0) {
58 DEBUG(5,("read failed on sock %d, pid %lu: %s\n",
59 state->sock, (unsigned long)state->pid,
60 (n == -1) ? strerror(errno) : "EOF"));
62 state->finished = True;
63 return;
66 /* Update client state */
68 state->read_buf_len += n;
69 state->last_access = time(NULL);
73 * Machinery for async requests sent to children. You set up a
74 * winbindd_request, select a child to query, and issue a async_request
75 * call. When the request is completed, the callback function you specified is
76 * called back with the private pointer you gave to async_request.
79 struct winbindd_async_request {
80 struct winbindd_async_request *next, *prev;
81 TALLOC_CTX *mem_ctx;
82 struct winbindd_child *child;
83 struct winbindd_request *request;
84 struct winbindd_response *response;
85 void (*continuation)(void *private_data, BOOL success);
86 void *private_data;
89 static void async_request_sent(void *private_data, BOOL success);
90 static void async_reply_recv(void *private_data, BOOL success);
91 static void schedule_async_request(struct winbindd_child *child);
93 void async_request(TALLOC_CTX *mem_ctx, struct winbindd_child *child,
94 struct winbindd_request *request,
95 struct winbindd_response *response,
96 void (*continuation)(void *private_data, BOOL success),
97 void *private_data)
99 struct winbindd_async_request *state, *tmp;
101 SMB_ASSERT(continuation != NULL);
103 state = TALLOC_P(mem_ctx, struct winbindd_async_request);
105 if (state == NULL) {
106 DEBUG(0, ("talloc failed\n"));
107 continuation(private_data, False);
108 return;
111 state->mem_ctx = mem_ctx;
112 state->child = child;
113 state->request = request;
114 state->response = response;
115 state->continuation = continuation;
116 state->private_data = private_data;
118 DLIST_ADD_END(child->requests, state, tmp);
120 schedule_async_request(child);
122 return;
125 static void async_request_sent(void *private_data, BOOL success)
127 struct winbindd_async_request *state =
128 talloc_get_type_abort(private_data, struct winbindd_async_request);
130 if (!success) {
131 DEBUG(5, ("Could not send async request\n"));
133 state->response->length = sizeof(struct winbindd_response);
134 state->response->result = WINBINDD_ERROR;
135 state->continuation(state->private_data, False);
136 return;
139 /* Request successfully sent to the child, setup the wait for reply */
141 setup_async_read(&state->child->event,
142 &state->response->result,
143 sizeof(state->response->result),
144 async_reply_recv, state);
147 static void async_reply_recv(void *private_data, BOOL success)
149 struct winbindd_async_request *state =
150 talloc_get_type_abort(private_data, struct winbindd_async_request);
151 struct winbindd_child *child = state->child;
153 state->response->length = sizeof(struct winbindd_response);
155 if (!success) {
156 DEBUG(5, ("Could not receive async reply\n"));
157 state->response->result = WINBINDD_ERROR;
158 return;
161 SMB_ASSERT(cache_retrieve_response(child->pid,
162 state->response));
164 DLIST_REMOVE(child->requests, state);
166 schedule_async_request(child);
168 state->continuation(state->private_data, True);
171 static BOOL fork_domain_child(struct winbindd_child *child);
173 static void schedule_async_request(struct winbindd_child *child)
175 struct winbindd_async_request *request = child->requests;
177 if (request == NULL) {
178 return;
181 if (child->event.flags != 0) {
182 return; /* Busy */
185 if ((child->pid == 0) && (!fork_domain_child(child))) {
186 /* Cancel all outstanding requests */
188 while (request != NULL) {
189 /* request might be free'd in the continuation */
190 struct winbindd_async_request *next = request->next;
191 request->continuation(request->private_data, False);
192 request = next;
194 return;
197 setup_async_write(&child->event, request->request,
198 sizeof(*request->request),
199 async_request_sent, request);
200 return;
203 struct domain_request_state {
204 TALLOC_CTX *mem_ctx;
205 struct winbindd_domain *domain;
206 struct winbindd_request *request;
207 struct winbindd_response *response;
208 void (*continuation)(void *private_data, BOOL success);
209 void *private_data;
212 static void domain_init_recv(void *private_data, BOOL success);
214 void async_domain_request(TALLOC_CTX *mem_ctx,
215 struct winbindd_domain *domain,
216 struct winbindd_request *request,
217 struct winbindd_response *response,
218 void (*continuation)(void *private_data, BOOL success),
219 void *private_data)
221 struct domain_request_state *state;
223 if (domain->initialized) {
224 async_request(mem_ctx, &domain->child, request, response,
225 continuation, private_data);
226 return;
229 state = TALLOC_P(mem_ctx, struct domain_request_state);
230 if (state == NULL) {
231 DEBUG(0, ("talloc failed\n"));
232 continuation(private_data, False);
233 return;
236 state->mem_ctx = mem_ctx;
237 state->domain = domain;
238 state->request = request;
239 state->response = response;
240 state->continuation = continuation;
241 state->private_data = private_data;
243 init_child_connection(domain, domain_init_recv, state);
246 static void recvfrom_child(void *private_data, BOOL success)
248 struct winbindd_cli_state *state =
249 talloc_get_type_abort(private_data, struct winbindd_cli_state);
250 enum winbindd_result result = state->response.result;
252 /* This is an optimization: The child has written directly to the
253 * response buffer. The request itself is still in pending state,
254 * state that in the result code. */
256 state->response.result = WINBINDD_PENDING;
258 if ((!success) || (result != WINBINDD_OK)) {
259 request_error(state);
260 return;
263 request_ok(state);
266 void sendto_child(struct winbindd_cli_state *state,
267 struct winbindd_child *child)
269 async_request(state->mem_ctx, child, &state->request,
270 &state->response, recvfrom_child, state);
273 void sendto_domain(struct winbindd_cli_state *state,
274 struct winbindd_domain *domain)
276 async_domain_request(state->mem_ctx, domain,
277 &state->request, &state->response,
278 recvfrom_child, state);
281 static void domain_init_recv(void *private_data, BOOL success)
283 struct domain_request_state *state =
284 talloc_get_type_abort(private_data, struct domain_request_state);
286 if (!success) {
287 DEBUG(5, ("Domain init returned an error\n"));
288 state->continuation(state->private_data, False);
289 return;
292 async_request(state->mem_ctx, &state->domain->child,
293 state->request, state->response,
294 state->continuation, state->private_data);
297 struct winbindd_child_dispatch_table {
298 enum winbindd_cmd cmd;
299 enum winbindd_result (*fn)(struct winbindd_domain *domain,
300 struct winbindd_cli_state *state);
301 const char *winbindd_cmd_name;
304 static struct winbindd_child_dispatch_table child_dispatch_table[] = {
306 { WINBINDD_LOOKUPSID, winbindd_dual_lookupsid, "LOOKUPSID" },
307 { WINBINDD_LOOKUPNAME, winbindd_dual_lookupname, "LOOKUPNAME" },
308 { WINBINDD_LIST_TRUSTDOM, winbindd_dual_list_trusted_domains, "LIST_TRUSTDOM" },
309 { WINBINDD_INIT_CONNECTION, winbindd_dual_init_connection, "INIT_CONNECTION" },
310 { WINBINDD_GETDCNAME, winbindd_dual_getdcname, "GETDCNAME" },
311 { WINBINDD_SHOW_SEQUENCE, winbindd_dual_show_sequence, "SHOW_SEQUENCE" },
312 { WINBINDD_PAM_AUTH, winbindd_dual_pam_auth, "PAM_AUTH" },
313 { WINBINDD_PAM_AUTH_CRAP, winbindd_dual_pam_auth_crap, "AUTH_CRAP" },
314 { WINBINDD_CHECK_MACHACC, winbindd_dual_check_machine_acct, "CHECK_MACHACC" },
315 { WINBINDD_DUAL_SID2UID, winbindd_dual_sid2uid, "DUAL_SID2UID" },
316 { WINBINDD_DUAL_SID2GID, winbindd_dual_sid2gid, "DUAL_SID2GID" },
317 { WINBINDD_DUAL_UID2NAME, winbindd_dual_uid2name, "DUAL_UID2NAME" },
318 { WINBINDD_DUAL_NAME2UID, winbindd_dual_name2uid, "DUAL_NAME2UID" },
319 { WINBINDD_DUAL_GID2NAME, winbindd_dual_gid2name, "DUAL_GID2NAME" },
320 { WINBINDD_DUAL_NAME2GID, winbindd_dual_name2gid, "DUAL_NAME2GID" },
321 { WINBINDD_DUAL_IDMAPSET, winbindd_dual_idmapset, "DUAL_IDMAPSET" },
322 { WINBINDD_DUAL_USERINFO, winbindd_dual_userinfo, "DUAL_USERINFO" },
323 { WINBINDD_ALLOCATE_RID, winbindd_dual_allocate_rid, "ALLOCATE_RID" },
324 { WINBINDD_ALLOCATE_RID_AND_GID, winbindd_dual_allocate_rid_and_gid, "ALLOCATE_RID_AND_GID" },
325 { WINBINDD_GETUSERDOMGROUPS, winbindd_dual_getuserdomgroups, "GETUSERDOMGROUPS" },
326 { WINBINDD_DUAL_GETSIDALIASES, winbindd_dual_getsidaliases, "GETSIDALIASES" },
327 /* End of list */
329 { WINBINDD_NUM_CMDS, NULL, "NONE" }
332 static void child_process_request(struct winbindd_domain *domain,
333 struct winbindd_cli_state *state)
335 struct winbindd_child_dispatch_table *table;
337 /* Free response data - we may be interrupted and receive another
338 command before being able to send this data off. */
340 state->response.result = WINBINDD_ERROR;
341 state->response.length = sizeof(struct winbindd_response);
343 state->mem_ctx = talloc_init("winbind request");
344 if (state->mem_ctx == NULL)
345 return;
347 /* Process command */
349 for (table = child_dispatch_table; table->fn; table++) {
350 if (state->request.cmd == table->cmd) {
351 DEBUG(10,("process_request: request fn %s\n",
352 table->winbindd_cmd_name ));
353 state->response.result = table->fn(domain, state);
354 break;
358 if (!table->fn) {
359 DEBUG(10,("process_request: unknown request fn number %d\n",
360 (int)state->request.cmd ));
361 state->response.result = WINBINDD_ERROR;
364 talloc_destroy(state->mem_ctx);
367 void setup_domain_child(struct winbindd_domain *domain,
368 struct winbindd_child *child,
369 const char *explicit_logfile)
371 if (explicit_logfile != NULL) {
372 pstr_sprintf(child->logfilename, "%s/log.winbindd-%s",
373 dyn_LOGFILEBASE, explicit_logfile);
374 } else if (domain != NULL) {
375 pstr_sprintf(child->logfilename, "%s/log.wb-%s",
376 dyn_LOGFILEBASE, domain->name);
377 } else {
378 smb_panic("Internal error: domain == NULL && "
379 "explicit_logfile == NULL");
382 child->domain = domain;
385 struct winbindd_child *children = NULL;
387 void winbind_child_died(pid_t pid)
389 struct winbindd_child *child;
391 for (child = children; child != NULL; child = child->next) {
392 if (child->pid == pid) {
393 break;
397 if (child == NULL) {
398 DEBUG(0, ("Unknown child %d died!\n", pid));
399 return;
402 remove_fd_event(&child->event);
403 close(child->event.fd);
404 child->event.fd = 0;
405 child->event.flags = 0;
406 child->pid = 0;
408 schedule_async_request(child);
411 static BOOL fork_domain_child(struct winbindd_child *child)
413 int fdpair[2];
414 struct winbindd_cli_state state;
415 extern BOOL override_logfile;
417 if (socketpair(AF_UNIX, SOCK_STREAM, 0, fdpair) != 0) {
418 DEBUG(0, ("Could not open child pipe: %s\n",
419 strerror(errno)));
420 return False;
423 ZERO_STRUCT(state);
424 state.pid = getpid();
426 child->pid = sys_fork();
428 if (child->pid == -1) {
429 DEBUG(0, ("Could not fork: %s\n", strerror(errno)));
430 return False;
433 if (child->pid != 0) {
434 /* Parent */
435 close(fdpair[0]);
436 child->next = child->prev = NULL;
437 DLIST_ADD(children, child);
438 child->event.fd = fdpair[1];
439 child->event.flags = 0;
440 child->requests = NULL;
441 add_fd_event(&child->event);
442 return True;
445 /* Child */
447 state.sock = fdpair[0];
448 close(fdpair[1]);
450 /* tdb needs special fork handling */
451 if (tdb_reopen_all() == -1) {
452 DEBUG(0,("tdb_reopen_all failed.\n"));
453 _exit(0);
456 close_conns_after_fork();
458 if (!override_logfile) {
459 lp_set_logfile(child->logfilename);
460 reopen_logs();
463 while (1) {
464 /* free up any talloc memory */
465 lp_talloc_free();
466 main_loop_talloc_free();
468 /* fetch a request from the main daemon */
469 dual_client_read(&state);
471 if (state.finished) {
472 /* we lost contact with our parent */
473 exit(0);
476 /* process full rquests */
477 if (state.read_buf_len == sizeof(state.request)) {
478 DEBUG(4,("child daemon request %d\n",
479 (int)state.request.cmd));
481 ZERO_STRUCT(state.response);
482 state.request.null_term = '\0';
483 child_process_request(child->domain, &state);
485 cache_store_response(sys_getpid(), &state.response);
487 SAFE_FREE(state.response.extra_data);
489 /* We just send the result code back, the result
490 * structure needs to be fetched via the
491 * winbindd_cache. Hmm. That needs fixing... */
493 if (write_data(state.sock,
494 (void *)&state.response.result,
495 sizeof(state.response.result)) !=
496 sizeof(state.response.result)) {
497 DEBUG(0, ("Could not write result\n"));
498 exit(1);
501 state.read_buf_len = 0;