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
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 */
46 struct dual_list *next;
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)
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;
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 ||
98 FD_SET(dual_daemon_pipe, fds);
99 if (dual_daemon_pipe > maxfd) {
100 maxfd = dual_daemon_pipe;
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)
114 if (dual_daemon_pipe == -1 ||
116 !FD_ISSET(dual_daemon_pipe, fds)) {
120 n = sys_write(dual_daemon_pipe,
121 &dual_list->data[dual_list->offset],
122 dual_list->length - dual_list->offset);
125 /* the pipe is dead! fall back to normal operation */
126 dual_daemon_pipe = -1;
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);
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);
158 list->data = memdup(&state->request, sizeof(state->request));
159 list->length = sizeof(state->request);
162 if (!dual_list_end) {
164 dual_list_end = list;
166 dual_list_end->next = list;
167 dual_list_end = list;
170 background_process = False;
177 void do_dual_daemon(void)
180 struct winbindd_cli_state state;
182 if (pipe(fdpair) != 0) {
187 state.pid = getpid();
189 dual_daemon_pipe = fdpair[1];
190 state.sock = fdpair[0];
192 if (sys_fork() != 0) {
198 /* tdb needs special fork handling */
199 if (tdb_reopen_all() == -1) {
200 DEBUG(0,("tdb_reopen_all failed.\n"));
204 dual_daemon_pipe = -1;
205 opt_dual_daemon = False;
208 /* free up any talloc memory */
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 */
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);
230 case WINBINDD_GETGRENT:
231 case WINBINDD_GETGRLST:
232 winbindd_setgrent(&state);
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;
259 struct winbindd_child *child;
260 struct winbindd_request *request;
261 struct winbindd_response *response;
262 void (*continuation)(void *private, BOOL success);
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),
276 struct winbindd_async_request *state, *tmp;
278 SMB_ASSERT(continuation != NULL);
280 state = TALLOC_P(mem_ctx, struct winbindd_async_request);
283 DEBUG(0, ("talloc failed\n"));
284 continuation(private, False);
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);
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);
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);
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);
333 DEBUG(5, ("Could not receive async reply\n"));
334 state->response->result = WINBINDD_ERROR;
338 if (state->response->result == WINBINDD_OK)
339 SMB_ASSERT(cache_retrieve_response(child->pid,
342 DLIST_REMOVE(child->requests, state);
344 schedule_async_request(child);
346 state->continuation(state->private, 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) {
359 if (child->event.flags != 0) {
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, False);
375 setup_async_write(&child->event, request->request,
376 sizeof(*request->request),
377 async_request_sent, request);
381 struct domain_request_state {
383 struct winbindd_domain *domain;
384 struct winbindd_request *request;
385 struct winbindd_response *response;
386 void (*continuation)(void *private, BOOL success);
390 static void domain_init_recv(void *private, 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, BOOL success),
399 struct domain_request_state *state;
401 if (domain->initialized) {
402 async_request(mem_ctx, &domain->child, request, response,
403 continuation, private);
407 state = TALLOC_P(mem_ctx, struct domain_request_state);
409 DEBUG(0, ("talloc failed\n"));
410 continuation(private, False);
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 = private;
421 init_child_connection(domain, domain_init_recv, state);
424 static void recvfrom_child(void *private, BOOL success)
426 struct winbindd_cli_state *state =
427 talloc_get_type_abort(private, 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);
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, BOOL success)
461 struct domain_request_state *state =
462 talloc_get_type_abort(private, struct domain_request_state);
465 DEBUG(5, ("Domain init returned an error\n"));
466 state->continuation(state->private, False);
470 async_request(state->mem_ctx, &state->domain->child,
471 state->request, state->response,
472 state->continuation, state->private);
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,
488 { WINBINDD_INIT_CONNECTION, winbindd_dual_init_connection,
490 { WINBINDD_GETDCNAME, winbindd_dual_getdcname, "GETDCNAME" },
491 { WINBINDD_SHOW_SEQUENCE, winbindd_dual_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,
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,
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)
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);
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);
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) {
583 DEBUG(0, ("Unknown child %d died!\n", pid));
587 remove_fd_event(&child->event);
588 close(child->event.fd);
590 child->event.flags = 0;
593 schedule_async_request(child);
596 static BOOL fork_domain_child(struct winbindd_child *child)
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",
609 state.pid = getpid();
611 child->pid = sys_fork();
613 if (child->pid == -1) {
614 DEBUG(0, ("Could not fork: %s\n", strerror(errno)));
618 if (child->pid != 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);
632 state.sock = fdpair[0];
635 /* tdb needs special fork handling */
636 if (tdb_reopen_all() == -1) {
637 DEBUG(0,("tdb_reopen_all failed.\n"));
641 close_conns_after_fork();
643 if (!override_logfile) {
644 lp_set_logfile(child->logfilename);
648 dual_daemon_pipe = -1;
649 opt_dual_daemon = False;
652 /* free up any talloc memory */
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 */
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(),
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"));
690 state.read_buf_len = 0;