r8322: * get RegSetValue() working for printer subkey values
[Samba/gbeck.git] / source / nsswitch / winbindd_dual.c
blob46b3ce2258bf4d7658e96ae181adf3129b91147b
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_data, BOOL success);
263 void *private_data;
266 static void async_request_sent(void *private_data, BOOL success);
267 static void async_reply_recv(void *private_data, 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_data, BOOL success),
274 void *private_data)
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_data, 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_data = private_data;
295 DLIST_ADD_END(child->requests, state, tmp);
297 schedule_async_request(child);
299 return;
302 static void async_request_sent(void *private_data, BOOL success)
304 struct winbindd_async_request *state =
305 talloc_get_type_abort(private_data, 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_data, 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_data, BOOL success)
326 struct winbindd_async_request *state =
327 talloc_get_type_abort(private_data, 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;
335 return;
338 if (state->response->result == WINBINDD_OK)
339 SMB_ASSERT(cache_retrieve_response(child->pid,
340 state->response));
342 DLIST_REMOVE(child->requests, state);
344 schedule_async_request(child);
346 state->continuation(state->private_data, True);
349 static BOOL fork_domain_child(struct winbindd_child *child);
351 static void schedule_async_request(struct winbindd_child *child)
353 struct winbindd_async_request *request = child->requests;
355 if (request == NULL) {
356 return;
359 if (child->event.flags != 0) {
360 return; /* Busy */
363 if ((child->pid == 0) && (!fork_domain_child(child))) {
364 /* Cancel all outstanding requests */
366 while (request != NULL) {
367 /* request might be free'd in the continuation */
368 struct winbindd_async_request *next = request->next;
369 request->continuation(request->private_data, False);
370 request = next;
372 return;
375 setup_async_write(&child->event, request->request,
376 sizeof(*request->request),
377 async_request_sent, request);
378 return;
381 struct domain_request_state {
382 TALLOC_CTX *mem_ctx;
383 struct winbindd_domain *domain;
384 struct winbindd_request *request;
385 struct winbindd_response *response;
386 void (*continuation)(void *private_data, BOOL success);
387 void *private_data;
390 static void domain_init_recv(void *private_data, BOOL success);
392 void async_domain_request(TALLOC_CTX *mem_ctx,
393 struct winbindd_domain *domain,
394 struct winbindd_request *request,
395 struct winbindd_response *response,
396 void (*continuation)(void *private_data, BOOL success),
397 void *private_data)
399 struct domain_request_state *state;
401 if (domain->initialized) {
402 async_request(mem_ctx, &domain->child, request, response,
403 continuation, private_data);
404 return;
407 state = TALLOC_P(mem_ctx, struct domain_request_state);
408 if (state == NULL) {
409 DEBUG(0, ("talloc failed\n"));
410 continuation(private_data, False);
411 return;
414 state->mem_ctx = mem_ctx;
415 state->domain = domain;
416 state->request = request;
417 state->response = response;
418 state->continuation = continuation;
419 state->private_data = private_data;
421 init_child_connection(domain, domain_init_recv, state);
424 static void recvfrom_child(void *private_data, BOOL success)
426 struct winbindd_cli_state *state =
427 talloc_get_type_abort(private_data, struct winbindd_cli_state);
428 enum winbindd_result result = state->response.result;
430 /* This is an optimization: The child has written directly to the
431 * response buffer. The request itself is still in pending state,
432 * state that in the result code. */
434 state->response.result = WINBINDD_PENDING;
436 if ((!success) || (result != WINBINDD_OK)) {
437 request_error(state);
438 return;
441 request_ok(state);
444 void sendto_child(struct winbindd_cli_state *state,
445 struct winbindd_child *child)
447 async_request(state->mem_ctx, child, &state->request,
448 &state->response, recvfrom_child, state);
451 void sendto_domain(struct winbindd_cli_state *state,
452 struct winbindd_domain *domain)
454 async_domain_request(state->mem_ctx, domain,
455 &state->request, &state->response,
456 recvfrom_child, state);
459 static void domain_init_recv(void *private_data, BOOL success)
461 struct domain_request_state *state =
462 talloc_get_type_abort(private_data, struct domain_request_state);
464 if (!success) {
465 DEBUG(5, ("Domain init returned an error\n"));
466 state->continuation(state->private_data, False);
467 return;
470 async_request(state->mem_ctx, &state->domain->child,
471 state->request, state->response,
472 state->continuation, state->private_data);
475 struct winbindd_child_dispatch_table {
476 enum winbindd_cmd cmd;
477 enum winbindd_result (*fn)(struct winbindd_domain *domain,
478 struct winbindd_cli_state *state);
479 const char *winbindd_cmd_name;
482 static struct winbindd_child_dispatch_table child_dispatch_table[] = {
484 { WINBINDD_LOOKUPSID, winbindd_dual_lookupsid, "LOOKUPSID" },
485 { WINBINDD_LOOKUPNAME, winbindd_dual_lookupname, "LOOKUPNAME" },
486 { WINBINDD_LIST_TRUSTDOM, winbindd_dual_list_trusted_domains,
487 "LIST_TRUSTDOM" },
488 { WINBINDD_INIT_CONNECTION, winbindd_dual_init_connection,
489 "INIT_CONNECTION" },
490 { WINBINDD_GETDCNAME, winbindd_dual_getdcname, "GETDCNAME" },
491 { WINBINDD_SHOW_SEQUENCE, winbindd_dual_show_sequence,
492 "SHOW_SEQUENCE" },
493 { WINBINDD_PAM_AUTH, winbindd_dual_pam_auth, "PAM_AUTH" },
494 { WINBINDD_PAM_AUTH_CRAP, winbindd_dual_pam_auth_crap, "AUTH_CRAP" },
495 { WINBINDD_CHECK_MACHACC, winbindd_dual_check_machine_acct,
496 "CHECK_MACHACC" },
497 { WINBINDD_DUAL_SID2UID, winbindd_dual_sid2uid, "DUAL_SID2UID" },
498 { WINBINDD_DUAL_SID2GID, winbindd_dual_sid2gid, "DUAL_SID2GID" },
499 { WINBINDD_DUAL_UID2NAME, winbindd_dual_uid2name, "DUAL_UID2NAME" },
500 { WINBINDD_DUAL_NAME2UID, winbindd_dual_name2uid, "DUAL_NAME2UID" },
501 { WINBINDD_DUAL_GID2NAME, winbindd_dual_gid2name, "DUAL_GID2NAME" },
502 { WINBINDD_DUAL_NAME2GID, winbindd_dual_name2gid, "DUAL_NAME2GID" },
503 { WINBINDD_DUAL_IDMAPSET, winbindd_dual_idmapset, "DUAL_IDMAPSET" },
504 { WINBINDD_DUAL_USERINFO, winbindd_dual_userinfo, "DUAL_USERINFO" },
505 { WINBINDD_ALLOCATE_RID, winbindd_dual_allocate_rid, "ALLOCATE_RID" },
506 { WINBINDD_ALLOCATE_RID_AND_GID, winbindd_dual_allocate_rid_and_gid,
507 "ALLOCATE_RID_AND_GID" },
508 { WINBINDD_GETUSERDOMGROUPS, winbindd_dual_getuserdomgroups,
509 "GETUSERDOMGROUPS" },
510 { WINBINDD_DUAL_GETSIDALIASES, winbindd_dual_getsidaliases,
511 "GETSIDALIASES" },
512 /* End of list */
514 { WINBINDD_NUM_CMDS, NULL, "NONE" }
517 static void child_process_request(struct winbindd_domain *domain,
518 struct winbindd_cli_state *state)
520 struct winbindd_child_dispatch_table *table;
522 /* Free response data - we may be interrupted and receive another
523 command before being able to send this data off. */
525 state->response.result = WINBINDD_ERROR;
526 state->response.length = sizeof(struct winbindd_response);
528 state->mem_ctx = talloc_init("winbind request");
529 if (state->mem_ctx == NULL)
530 return;
532 /* Process command */
534 for (table = child_dispatch_table; table->fn; table++) {
535 if (state->request.cmd == table->cmd) {
536 DEBUG(10,("process_request: request fn %s\n",
537 table->winbindd_cmd_name ));
538 state->response.result = table->fn(domain, state);
539 break;
543 if (!table->fn) {
544 DEBUG(10,("process_request: unknown request fn number %d\n",
545 (int)state->request.cmd ));
546 state->response.result = WINBINDD_ERROR;
549 talloc_destroy(state->mem_ctx);
552 void setup_domain_child(struct winbindd_domain *domain,
553 struct winbindd_child *child,
554 const char *explicit_logfile)
556 if (explicit_logfile != NULL) {
557 pstr_sprintf(child->logfilename, "%s/log.winbindd-%s",
558 dyn_LOGFILEBASE, explicit_logfile);
559 } else if (domain != NULL) {
560 pstr_sprintf(child->logfilename, "%s/log.wb-%s",
561 dyn_LOGFILEBASE, domain->name);
562 } else {
563 smb_panic("Internal error: domain == NULL && "
564 "explicit_logfile == NULL");
567 child->domain = domain;
570 struct winbindd_child *children = NULL;
572 void winbind_child_died(pid_t pid)
574 struct winbindd_child *child;
576 for (child = children; child != NULL; child = child->next) {
577 if (child->pid == pid) {
578 break;
582 if (child == NULL) {
583 DEBUG(0, ("Unknown child %d died!\n", pid));
584 return;
587 remove_fd_event(&child->event);
588 close(child->event.fd);
589 child->event.fd = 0;
590 child->event.flags = 0;
591 child->pid = 0;
593 schedule_async_request(child);
596 static BOOL fork_domain_child(struct winbindd_child *child)
598 int fdpair[2];
599 struct winbindd_cli_state state;
600 extern BOOL override_logfile;
602 if (socketpair(AF_UNIX, SOCK_STREAM, 0, fdpair) != 0) {
603 DEBUG(0, ("Could not open child pipe: %s\n",
604 strerror(errno)));
605 return False;
608 ZERO_STRUCT(state);
609 state.pid = getpid();
611 child->pid = sys_fork();
613 if (child->pid == -1) {
614 DEBUG(0, ("Could not fork: %s\n", strerror(errno)));
615 return False;
618 if (child->pid != 0) {
619 /* Parent */
620 close(fdpair[0]);
621 child->next = child->prev = NULL;
622 DLIST_ADD(children, child);
623 child->event.fd = fdpair[1];
624 child->event.flags = 0;
625 child->requests = NULL;
626 add_fd_event(&child->event);
627 return True;
630 /* Child */
632 state.sock = fdpair[0];
633 close(fdpair[1]);
635 /* tdb needs special fork handling */
636 if (tdb_reopen_all() == -1) {
637 DEBUG(0,("tdb_reopen_all failed.\n"));
638 _exit(0);
641 close_conns_after_fork();
643 if (!override_logfile) {
644 lp_set_logfile(child->logfilename);
645 reopen_logs();
648 dual_daemon_pipe = -1;
649 opt_dual_daemon = False;
651 while (1) {
652 /* free up any talloc memory */
653 lp_talloc_free();
654 main_loop_talloc_free();
656 /* fetch a request from the main daemon */
657 dual_client_read(&state);
659 if (state.finished) {
660 /* we lost contact with our parent */
661 exit(0);
664 /* process full rquests */
665 if (state.read_buf_len == sizeof(state.request)) {
666 DEBUG(4,("child daemon request %d\n",
667 (int)state.request.cmd));
669 state.request.null_term = '\0';
670 child_process_request(child->domain, &state);
672 if (state.response.result == WINBINDD_OK)
673 cache_store_response(sys_getpid(),
674 &state.response);
676 SAFE_FREE(state.response.extra_data);
678 /* We just send the result code back, the result
679 * structure needs to be fetched via the
680 * winbindd_cache. Hmm. That needs fixing... */
682 if (write_data(state.sock,
683 (void *)&state.response.result,
684 sizeof(state.response.result)) !=
685 sizeof(state.response.result)) {
686 DEBUG(0, ("Could not write result\n"));
687 exit(1);
690 state.read_buf_len = 0;