r7703: Fix the problem with MAP_PRIVATE not updating the file.
[Samba.git] / source3 / nsswitch / winbindd_dual.c
blobe1ed2d67633869dc069e7bf496611919cc7ad458
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 extern BOOL opt_dual_daemon;
40 BOOL background_process = False;
41 int dual_daemon_pipe = -1;
44 /* a list of requests ready to be sent to the dual daemon */
45 struct dual_list {
46 struct dual_list *next;
47 char *data;
48 int length;
49 int offset;
52 static struct dual_list *dual_list;
53 static struct dual_list *dual_list_end;
55 /* Read some data from a client connection */
57 static void dual_client_read(struct winbindd_cli_state *state)
59 int n;
61 /* Read data */
63 n = sys_read(state->sock, state->read_buf_len +
64 (char *)&state->request,
65 sizeof(state->request) - state->read_buf_len);
67 DEBUG(10,("client_read: read %d bytes. Need %ld more for a full "
68 "request.\n", n, (unsigned long)(sizeof(state->request) - n -
69 state->read_buf_len) ));
71 /* Read failed, kill client */
73 if (n == -1 || n == 0) {
74 DEBUG(5,("read failed on sock %d, pid %lu: %s\n",
75 state->sock, (unsigned long)state->pid,
76 (n == -1) ? strerror(errno) : "EOF"));
78 state->finished = True;
79 return;
82 /* Update client state */
84 state->read_buf_len += n;
85 state->last_access = time(NULL);
89 setup a select() including the dual daemon pipe
91 int dual_select_setup(fd_set *fds, int maxfd)
93 if (dual_daemon_pipe == -1 ||
94 !dual_list) {
95 return maxfd;
98 FD_SET(dual_daemon_pipe, fds);
99 if (dual_daemon_pipe > maxfd) {
100 maxfd = dual_daemon_pipe;
102 return maxfd;
107 a hook called from the main winbindd select() loop to handle writes
108 to the dual daemon pipe
110 void dual_select(fd_set *fds)
112 int n;
114 if (dual_daemon_pipe == -1 ||
115 !dual_list ||
116 !FD_ISSET(dual_daemon_pipe, fds)) {
117 return;
120 n = sys_write(dual_daemon_pipe,
121 &dual_list->data[dual_list->offset],
122 dual_list->length - dual_list->offset);
124 if (n <= 0) {
125 /* the pipe is dead! fall back to normal operation */
126 dual_daemon_pipe = -1;
127 return;
130 dual_list->offset += n;
132 if (dual_list->offset == dual_list->length) {
133 struct dual_list *next;
134 next = dual_list->next;
135 free(dual_list->data);
136 free(dual_list);
137 dual_list = next;
138 if (!dual_list) {
139 dual_list_end = NULL;
145 send a request to the background daemon
146 this is called for stale cached entries
148 void dual_send_request(struct winbindd_cli_state *state)
150 struct dual_list *list;
152 if (!background_process) return;
154 list = SMB_MALLOC_P(struct dual_list);
155 if (!list) return;
157 list->next = NULL;
158 list->data = memdup(&state->request, sizeof(state->request));
159 list->length = sizeof(state->request);
160 list->offset = 0;
162 if (!dual_list_end) {
163 dual_list = list;
164 dual_list_end = list;
165 } else {
166 dual_list_end->next = list;
167 dual_list_end = list;
170 background_process = False;
175 the main dual daemon
177 void do_dual_daemon(void)
179 int fdpair[2];
180 struct winbindd_cli_state state;
182 if (pipe(fdpair) != 0) {
183 return;
186 ZERO_STRUCT(state);
187 state.pid = getpid();
189 dual_daemon_pipe = fdpair[1];
190 state.sock = fdpair[0];
192 if (sys_fork() != 0) {
193 close(fdpair[0]);
194 return;
196 close(fdpair[1]);
198 /* tdb needs special fork handling */
199 if (tdb_reopen_all() == -1) {
200 DEBUG(0,("tdb_reopen_all failed.\n"));
201 _exit(0);
204 dual_daemon_pipe = -1;
205 opt_dual_daemon = False;
207 while (1) {
208 /* free up any talloc memory */
209 lp_talloc_free();
210 main_loop_talloc_free();
212 /* fetch a request from the main daemon */
213 dual_client_read(&state);
215 if (state.finished) {
216 /* we lost contact with our parent */
217 exit(0);
220 /* process full rquests */
221 if (state.read_buf_len == sizeof(state.request)) {
222 DEBUG(4,("dual daemon request %d\n", (int)state.request.cmd));
224 /* special handling for the stateful requests */
225 switch (state.request.cmd) {
226 case WINBINDD_GETPWENT:
227 winbindd_setpwent(&state);
228 break;
230 case WINBINDD_GETGRENT:
231 case WINBINDD_GETGRLST:
232 winbindd_setgrent(&state);
233 break;
234 default:
235 break;
238 winbind_process_packet(&state);
239 SAFE_FREE(state.response.extra_data);
241 free_getent_state(state.getpwent_state);
242 free_getent_state(state.getgrent_state);
243 state.getpwent_state = NULL;
244 state.getgrent_state = NULL;
250 * Machinery for async requests sent to children. You set up a
251 * winbindd_request, select a child to query, and issue a async_request
252 * call. When the request is completed, the callback function you specified is
253 * called back with the private pointer you gave to async_request.
256 struct winbindd_async_request {
257 struct winbindd_async_request *next, *prev;
258 TALLOC_CTX *mem_ctx;
259 struct winbindd_child *child;
260 struct winbindd_request *request;
261 struct winbindd_response *response;
262 void (*continuation)(void *private, BOOL success);
263 void *private;
266 static void async_request_sent(void *private, BOOL success);
267 static void async_reply_recv(void *private, BOOL success);
268 static void schedule_async_request(struct winbindd_child *child);
270 void async_request(TALLOC_CTX *mem_ctx, struct winbindd_child *child,
271 struct winbindd_request *request,
272 struct winbindd_response *response,
273 void (*continuation)(void *private, BOOL success),
274 void *private)
276 struct winbindd_async_request *state, *tmp;
278 SMB_ASSERT(continuation != NULL);
280 state = TALLOC_P(mem_ctx, struct winbindd_async_request);
282 if (state == NULL) {
283 DEBUG(0, ("talloc failed\n"));
284 continuation(private, False);
285 return;
288 state->mem_ctx = mem_ctx;
289 state->child = child;
290 state->request = request;
291 state->response = response;
292 state->continuation = continuation;
293 state->private = private;
295 DLIST_ADD_END(child->requests, state, tmp);
297 schedule_async_request(child);
299 return;
302 static void async_request_sent(void *private, BOOL success)
304 struct winbindd_async_request *state =
305 talloc_get_type_abort(private, struct winbindd_async_request);
307 if (!success) {
308 DEBUG(5, ("Could not send async request\n"));
310 state->response->length = sizeof(struct winbindd_response);
311 state->response->result = WINBINDD_ERROR;
312 state->continuation(state->private, False);
313 return;
316 /* Request successfully sent to the child, setup the wait for reply */
318 setup_async_read(&state->child->event,
319 &state->response->result,
320 sizeof(state->response->result),
321 async_reply_recv, state);
324 static void async_reply_recv(void *private, BOOL success)
326 struct winbindd_async_request *state =
327 talloc_get_type_abort(private, struct winbindd_async_request);
328 struct winbindd_child *child = state->child;
330 state->response->length = sizeof(struct winbindd_response);
332 if (!success) {
333 DEBUG(5, ("Could not receive async reply\n"));
334 state->response->result = WINBINDD_ERROR;
337 if (state->response->result == WINBINDD_OK)
338 SMB_ASSERT(cache_retrieve_response(child->pid,
339 state->response));
341 DLIST_REMOVE(child->requests, state);
343 schedule_async_request(child);
345 state->continuation(state->private, True);
348 static BOOL fork_domain_child(struct winbindd_child *child);
350 static void schedule_async_request(struct winbindd_child *child)
352 struct winbindd_async_request *request = child->requests;
354 if (request == NULL) {
355 return;
358 if (child->event.flags != 0) {
359 return; /* Busy */
362 if ((child->pid == 0) && (!fork_domain_child(child))) {
363 /* Cancel all outstanding requests */
365 while (request != NULL) {
366 /* request might be free'd in the continuation */
367 struct winbindd_async_request *next = request->next;
368 request->continuation(request->private, False);
369 request = next;
371 return;
374 setup_async_write(&child->event, request->request,
375 sizeof(*request->request),
376 async_request_sent, request);
377 return;
380 struct domain_request_state {
381 TALLOC_CTX *mem_ctx;
382 struct winbindd_domain *domain;
383 struct winbindd_request *request;
384 struct winbindd_response *response;
385 void (*continuation)(void *private, BOOL success);
386 void *private;
389 static void domain_init_recv(void *private, BOOL success);
391 void async_domain_request(TALLOC_CTX *mem_ctx,
392 struct winbindd_domain *domain,
393 struct winbindd_request *request,
394 struct winbindd_response *response,
395 void (*continuation)(void *private, BOOL success),
396 void *private)
398 struct domain_request_state *state;
400 if (domain->initialized) {
401 async_request(mem_ctx, &domain->child, request, response,
402 continuation, private);
403 return;
406 state = TALLOC_P(mem_ctx, struct domain_request_state);
407 if (state == NULL) {
408 DEBUG(0, ("talloc failed\n"));
409 continuation(private, False);
410 return;
413 state->mem_ctx = mem_ctx;
414 state->domain = domain;
415 state->request = request;
416 state->response = response;
417 state->continuation = continuation;
418 state->private = private;
420 init_child_connection(domain, domain_init_recv, state);
423 static void domain_init_recv(void *private, BOOL success)
425 struct domain_request_state *state =
426 talloc_get_type_abort(private, struct domain_request_state);
428 if (!success) {
429 DEBUG(5, ("Domain init returned an error\n"));
430 state->continuation(state->private, False);
431 return;
434 async_request(state->mem_ctx, &state->domain->child,
435 state->request, state->response,
436 state->continuation, state->private);
439 struct winbindd_child_dispatch_table {
440 enum winbindd_cmd cmd;
441 enum winbindd_result (*fn)(struct winbindd_domain *domain,
442 struct winbindd_cli_state *state);
443 const char *winbindd_cmd_name;
446 static struct winbindd_child_dispatch_table child_dispatch_table[] = {
448 { WINBINDD_LOOKUPSID, winbindd_dual_lookupsid, "LOOKUPSID" },
449 { WINBINDD_LOOKUPNAME, winbindd_dual_lookupname, "LOOKUPNAME" },
450 { WINBINDD_LIST_TRUSTDOM, winbindd_dual_list_trusted_domains,
451 "LIST_TRUSTDOM" },
452 { WINBINDD_INIT_CONNECTION, winbindd_dual_init_connection,
453 "INIT_CONNECTION" },
454 { WINBINDD_GETDCNAME, winbindd_dual_getdcname, "GETDCNAME" },
455 { WINBINDD_SHOW_SEQUENCE, winbindd_dual_show_sequence,
456 "SHOW_SEQUENCE" },
457 { WINBINDD_PAM_AUTH, winbindd_dual_pam_auth, "PAM_AUTH" },
458 { WINBINDD_PAM_AUTH_CRAP, winbindd_dual_pam_auth_crap, "AUTH_CRAP" },
459 { WINBINDD_CHECK_MACHACC, winbindd_dual_check_machine_acct,
460 "CHECK_MACHACC" },
461 { WINBINDD_DUAL_SID2UID, winbindd_dual_sid2uid, "DUAL_SID2UID" },
462 { WINBINDD_DUAL_SID2GID, winbindd_dual_sid2gid, "DUAL_SID2GID" },
463 { WINBINDD_DUAL_UID2NAME, winbindd_dual_uid2name, "DUAL_UID2NAME" },
464 { WINBINDD_DUAL_NAME2UID, winbindd_dual_name2uid, "DUAL_NAME2UID" },
465 { WINBINDD_DUAL_GID2NAME, winbindd_dual_gid2name, "DUAL_GID2NAME" },
466 { WINBINDD_DUAL_NAME2GID, winbindd_dual_name2gid, "DUAL_NAME2GID" },
467 { WINBINDD_DUAL_IDMAPSET, winbindd_dual_idmapset, "DUAL_IDMAPSET" },
468 { WINBINDD_DUAL_USERINFO, winbindd_dual_userinfo, "DUAL_USERINFO" },
469 { WINBINDD_ALLOCATE_RID, winbindd_dual_allocate_rid, "ALLOCATE_RID" },
470 { WINBINDD_ALLOCATE_RID_AND_GID, winbindd_dual_allocate_rid_and_gid,
471 "ALLOCATE_RID_AND_GID" },
472 { WINBINDD_GETUSERDOMGROUPS, winbindd_dual_getuserdomgroups,
473 "GETUSERDOMGROUPS" },
474 { WINBINDD_DUAL_GETSIDALIASES, winbindd_dual_getsidaliases,
475 "GETSIDALIASES" },
476 /* End of list */
478 { WINBINDD_NUM_CMDS, NULL, "NONE" }
481 static void child_process_request(struct winbindd_domain *domain,
482 struct winbindd_cli_state *state)
484 struct winbindd_child_dispatch_table *table;
486 /* Free response data - we may be interrupted and receive another
487 command before being able to send this data off. */
489 state->response.result = WINBINDD_ERROR;
490 state->response.length = sizeof(struct winbindd_response);
492 state->mem_ctx = talloc_init("winbind request");
493 if (state->mem_ctx == NULL)
494 return;
496 /* Process command */
498 for (table = child_dispatch_table; table->fn; table++) {
499 if (state->request.cmd == table->cmd) {
500 DEBUG(10,("process_request: request fn %s\n",
501 table->winbindd_cmd_name ));
502 state->response.result = table->fn(domain, state);
503 break;
507 if (!table->fn) {
508 DEBUG(10,("process_request: unknown request fn number %d\n",
509 (int)state->request.cmd ));
510 state->response.result = WINBINDD_ERROR;
513 talloc_destroy(state->mem_ctx);
516 void setup_domain_child(struct winbindd_domain *domain,
517 struct winbindd_child *child,
518 const char *explicit_logfile)
520 if (explicit_logfile != NULL) {
521 pstr_sprintf(child->logfilename, "%s/log.winbindd-%s",
522 dyn_LOGFILEBASE, explicit_logfile);
523 } else if (domain != NULL) {
524 pstr_sprintf(child->logfilename, "%s/log.wb-%s",
525 dyn_LOGFILEBASE, domain->name);
526 } else {
527 smb_panic("Internal error: domain == NULL && "
528 "explicit_logfile == NULL");
531 child->domain = domain;
534 struct winbindd_child *children = NULL;
536 void winbind_child_died(pid_t pid)
538 struct winbindd_child *child;
540 for (child = children; child != NULL; child = child->next) {
541 if (child->pid == pid) {
542 break;
546 if (child == NULL) {
547 DEBUG(0, ("Unknown child %d died!\n", pid));
548 return;
551 remove_fd_event(&child->event);
552 close(child->event.fd);
553 child->event.fd = 0;
554 child->event.flags = 0;
555 child->pid = 0;
557 schedule_async_request(child);
560 static BOOL fork_domain_child(struct winbindd_child *child)
562 int fdpair[2];
563 struct winbindd_cli_state state;
564 extern BOOL override_logfile;
566 if (socketpair(AF_UNIX, SOCK_STREAM, 0, fdpair) != 0) {
567 DEBUG(0, ("Could not open child pipe: %s\n",
568 strerror(errno)));
569 return False;
572 ZERO_STRUCT(state);
573 state.pid = getpid();
575 child->pid = sys_fork();
577 if (child->pid == -1) {
578 DEBUG(0, ("Could not fork: %s\n", strerror(errno)));
579 return False;
582 if (child->pid != 0) {
583 /* Parent */
584 close(fdpair[0]);
585 child->next = child->prev = NULL;
586 DLIST_ADD(children, child);
587 child->event.fd = fdpair[1];
588 child->event.flags = 0;
589 child->requests = NULL;
590 add_fd_event(&child->event);
591 return True;
594 /* Child */
596 state.sock = fdpair[0];
597 close(fdpair[1]);
599 /* tdb needs special fork handling */
600 if (tdb_reopen_all() == -1) {
601 DEBUG(0,("tdb_reopen_all failed.\n"));
602 _exit(0);
605 close_conns_after_fork();
607 if (!override_logfile) {
608 lp_set_logfile(child->logfilename);
609 reopen_logs();
612 dual_daemon_pipe = -1;
613 opt_dual_daemon = False;
615 while (1) {
616 /* free up any talloc memory */
617 lp_talloc_free();
618 main_loop_talloc_free();
620 /* fetch a request from the main daemon */
621 dual_client_read(&state);
623 if (state.finished) {
624 /* we lost contact with our parent */
625 exit(0);
628 /* process full rquests */
629 if (state.read_buf_len == sizeof(state.request)) {
630 DEBUG(4,("child daemon request %d\n",
631 (int)state.request.cmd));
633 state.request.null_term = '\0';
634 child_process_request(child->domain, &state);
636 if (state.response.result == WINBINDD_OK)
637 cache_store_response(sys_getpid(),
638 &state.response);
640 SAFE_FREE(state.response.extra_data);
642 /* We just send the result code back, the result
643 * structure needs to be fetched via the
644 * winbindd_cache. Hmm. That needs fixing... */
646 if (write_data(state.sock,
647 (void *)&state.response.result,
648 sizeof(state.response.result)) !=
649 sizeof(state.response.result)) {
650 DEBUG(0, ("Could not write result\n"));
651 exit(1);
654 state.read_buf_len = 0;