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
;
337 if (state
->response
->result
== WINBINDD_OK
)
338 SMB_ASSERT(cache_retrieve_response(child
->pid
,
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
) {
358 if (child
->event
.flags
!= 0) {
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
);
374 setup_async_write(&child
->event
, request
->request
,
375 sizeof(*request
->request
),
376 async_request_sent
, request
);
380 struct domain_request_state
{
382 struct winbindd_domain
*domain
;
383 struct winbindd_request
*request
;
384 struct winbindd_response
*response
;
385 void (*continuation
)(void *private, BOOL success
);
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
),
398 struct domain_request_state
*state
;
400 if (domain
->initialized
) {
401 async_request(mem_ctx
, &domain
->child
, request
, response
,
402 continuation
, private);
406 state
= TALLOC_P(mem_ctx
, struct domain_request_state
);
408 DEBUG(0, ("talloc failed\n"));
409 continuation(private, False
);
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
);
429 DEBUG(5, ("Domain init returned an error\n"));
430 state
->continuation(state
->private, False
);
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
,
452 { WINBINDD_INIT_CONNECTION
, winbindd_dual_init_connection
,
454 { WINBINDD_GETDCNAME
, winbindd_dual_getdcname
, "GETDCNAME" },
455 { WINBINDD_SHOW_SEQUENCE
, winbindd_dual_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
,
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
,
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
)
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
);
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
);
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
) {
547 DEBUG(0, ("Unknown child %d died!\n", pid
));
551 remove_fd_event(&child
->event
);
552 close(child
->event
.fd
);
554 child
->event
.flags
= 0;
557 schedule_async_request(child
);
560 static BOOL
fork_domain_child(struct winbindd_child
*child
)
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",
573 state
.pid
= getpid();
575 child
->pid
= sys_fork();
577 if (child
->pid
== -1) {
578 DEBUG(0, ("Could not fork: %s\n", strerror(errno
)));
582 if (child
->pid
!= 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
);
596 state
.sock
= fdpair
[0];
599 /* tdb needs special fork handling */
600 if (tdb_reopen_all() == -1) {
601 DEBUG(0,("tdb_reopen_all failed.\n"));
605 close_conns_after_fork();
607 if (!override_logfile
) {
608 lp_set_logfile(child
->logfilename
);
612 dual_daemon_pipe
= -1;
613 opt_dual_daemon
= False
;
616 /* free up any talloc memory */
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 */
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(),
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"));
654 state
.read_buf_len
= 0;