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 /* Read some data from a client connection */
41 static void dual_client_read(struct winbindd_cli_state *state)
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;
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;
82 struct winbindd_child *child;
83 struct winbindd_request *request;
84 struct winbindd_response *response;
85 void (*continuation)(void *private_data, BOOL success);
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),
99 struct winbindd_async_request *state, *tmp;
101 SMB_ASSERT(continuation != NULL);
103 state = TALLOC_P(mem_ctx, struct winbindd_async_request);
106 DEBUG(0, ("talloc failed\n"));
107 continuation(private_data, False);
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);
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);
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);
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);
156 DEBUG(5, ("Could not receive async reply\n"));
157 state->response->result = WINBINDD_ERROR;
161 if (state->response->result == WINBINDD_OK)
162 SMB_ASSERT(cache_retrieve_response(child->pid,
165 DLIST_REMOVE(child->requests, state);
167 schedule_async_request(child);
169 state->continuation(state->private_data, True);
172 static BOOL fork_domain_child(struct winbindd_child *child);
174 static void schedule_async_request(struct winbindd_child *child)
176 struct winbindd_async_request *request = child->requests;
178 if (request == NULL) {
182 if (child->event.flags != 0) {
186 if ((child->pid == 0) && (!fork_domain_child(child))) {
187 /* Cancel all outstanding requests */
189 while (request != NULL) {
190 /* request might be free'd in the continuation */
191 struct winbindd_async_request *next = request->next;
192 request->continuation(request->private_data, False);
198 setup_async_write(&child->event, request->request,
199 sizeof(*request->request),
200 async_request_sent, request);
204 struct domain_request_state {
206 struct winbindd_domain *domain;
207 struct winbindd_request *request;
208 struct winbindd_response *response;
209 void (*continuation)(void *private_data, BOOL success);
213 static void domain_init_recv(void *private_data, BOOL success);
215 void async_domain_request(TALLOC_CTX *mem_ctx,
216 struct winbindd_domain *domain,
217 struct winbindd_request *request,
218 struct winbindd_response *response,
219 void (*continuation)(void *private_data, BOOL success),
222 struct domain_request_state *state;
224 if (domain->initialized) {
225 async_request(mem_ctx, &domain->child, request, response,
226 continuation, private_data);
230 state = TALLOC_P(mem_ctx, struct domain_request_state);
232 DEBUG(0, ("talloc failed\n"));
233 continuation(private_data, False);
237 state->mem_ctx = mem_ctx;
238 state->domain = domain;
239 state->request = request;
240 state->response = response;
241 state->continuation = continuation;
242 state->private_data = private_data;
244 init_child_connection(domain, domain_init_recv, state);
247 static void recvfrom_child(void *private_data, BOOL success)
249 struct winbindd_cli_state *state =
250 talloc_get_type_abort(private_data, struct winbindd_cli_state);
251 enum winbindd_result result = state->response.result;
253 /* This is an optimization: The child has written directly to the
254 * response buffer. The request itself is still in pending state,
255 * state that in the result code. */
257 state->response.result = WINBINDD_PENDING;
259 if ((!success) || (result != WINBINDD_OK)) {
260 request_error(state);
267 void sendto_child(struct winbindd_cli_state *state,
268 struct winbindd_child *child)
270 async_request(state->mem_ctx, child, &state->request,
271 &state->response, recvfrom_child, state);
274 void sendto_domain(struct winbindd_cli_state *state,
275 struct winbindd_domain *domain)
277 async_domain_request(state->mem_ctx, domain,
278 &state->request, &state->response,
279 recvfrom_child, state);
282 static void domain_init_recv(void *private_data, BOOL success)
284 struct domain_request_state *state =
285 talloc_get_type_abort(private_data, struct domain_request_state);
288 DEBUG(5, ("Domain init returned an error\n"));
289 state->continuation(state->private_data, False);
293 async_request(state->mem_ctx, &state->domain->child,
294 state->request, state->response,
295 state->continuation, state->private_data);
298 struct winbindd_child_dispatch_table {
299 enum winbindd_cmd cmd;
300 enum winbindd_result (*fn)(struct winbindd_domain *domain,
301 struct winbindd_cli_state *state);
302 const char *winbindd_cmd_name;
305 static struct winbindd_child_dispatch_table child_dispatch_table[] = {
307 { WINBINDD_LOOKUPSID, winbindd_dual_lookupsid, "LOOKUPSID" },
308 { WINBINDD_LOOKUPNAME, winbindd_dual_lookupname, "LOOKUPNAME" },
309 { WINBINDD_LIST_TRUSTDOM, winbindd_dual_list_trusted_domains,
311 { WINBINDD_INIT_CONNECTION, winbindd_dual_init_connection,
313 { WINBINDD_GETDCNAME, winbindd_dual_getdcname, "GETDCNAME" },
314 { WINBINDD_SHOW_SEQUENCE, winbindd_dual_show_sequence,
316 { WINBINDD_PAM_AUTH, winbindd_dual_pam_auth, "PAM_AUTH" },
317 { WINBINDD_PAM_AUTH_CRAP, winbindd_dual_pam_auth_crap, "AUTH_CRAP" },
318 { WINBINDD_CHECK_MACHACC, winbindd_dual_check_machine_acct,
320 { WINBINDD_DUAL_SID2UID, winbindd_dual_sid2uid, "DUAL_SID2UID" },
321 { WINBINDD_DUAL_SID2GID, winbindd_dual_sid2gid, "DUAL_SID2GID" },
322 { WINBINDD_DUAL_UID2NAME, winbindd_dual_uid2name, "DUAL_UID2NAME" },
323 { WINBINDD_DUAL_NAME2UID, winbindd_dual_name2uid, "DUAL_NAME2UID" },
324 { WINBINDD_DUAL_GID2NAME, winbindd_dual_gid2name, "DUAL_GID2NAME" },
325 { WINBINDD_DUAL_NAME2GID, winbindd_dual_name2gid, "DUAL_NAME2GID" },
326 { WINBINDD_DUAL_IDMAPSET, winbindd_dual_idmapset, "DUAL_IDMAPSET" },
327 { WINBINDD_DUAL_USERINFO, winbindd_dual_userinfo, "DUAL_USERINFO" },
328 { WINBINDD_ALLOCATE_RID, winbindd_dual_allocate_rid, "ALLOCATE_RID" },
329 { WINBINDD_ALLOCATE_RID_AND_GID, winbindd_dual_allocate_rid_and_gid,
330 "ALLOCATE_RID_AND_GID" },
331 { WINBINDD_GETUSERDOMGROUPS, winbindd_dual_getuserdomgroups,
332 "GETUSERDOMGROUPS" },
333 { WINBINDD_DUAL_GETSIDALIASES, winbindd_dual_getsidaliases,
337 { WINBINDD_NUM_CMDS, NULL, "NONE" }
340 static void child_process_request(struct winbindd_domain *domain,
341 struct winbindd_cli_state *state)
343 struct winbindd_child_dispatch_table *table;
345 /* Free response data - we may be interrupted and receive another
346 command before being able to send this data off. */
348 state->response.result = WINBINDD_ERROR;
349 state->response.length = sizeof(struct winbindd_response);
351 state->mem_ctx = talloc_init("winbind request");
352 if (state->mem_ctx == NULL)
355 /* Process command */
357 for (table = child_dispatch_table; table->fn; table++) {
358 if (state->request.cmd == table->cmd) {
359 DEBUG(10,("process_request: request fn %s\n",
360 table->winbindd_cmd_name ));
361 state->response.result = table->fn(domain, state);
367 DEBUG(10,("process_request: unknown request fn number %d\n",
368 (int)state->request.cmd ));
369 state->response.result = WINBINDD_ERROR;
372 talloc_destroy(state->mem_ctx);
375 void setup_domain_child(struct winbindd_domain *domain,
376 struct winbindd_child *child,
377 const char *explicit_logfile)
379 if (explicit_logfile != NULL) {
380 pstr_sprintf(child->logfilename, "%s/log.winbindd-%s",
381 dyn_LOGFILEBASE, explicit_logfile);
382 } else if (domain != NULL) {
383 pstr_sprintf(child->logfilename, "%s/log.wb-%s",
384 dyn_LOGFILEBASE, domain->name);
386 smb_panic("Internal error: domain == NULL && "
387 "explicit_logfile == NULL");
390 child->domain = domain;
393 struct winbindd_child *children = NULL;
395 void winbind_child_died(pid_t pid)
397 struct winbindd_child *child;
399 for (child = children; child != NULL; child = child->next) {
400 if (child->pid == pid) {
406 DEBUG(0, ("Unknown child %d died!\n", pid));
410 remove_fd_event(&child->event);
411 close(child->event.fd);
413 child->event.flags = 0;
416 schedule_async_request(child);
419 static BOOL fork_domain_child(struct winbindd_child *child)
422 struct winbindd_cli_state state;
423 extern BOOL override_logfile;
425 if (socketpair(AF_UNIX, SOCK_STREAM, 0, fdpair) != 0) {
426 DEBUG(0, ("Could not open child pipe: %s\n",
432 state.pid = getpid();
434 child->pid = sys_fork();
436 if (child->pid == -1) {
437 DEBUG(0, ("Could not fork: %s\n", strerror(errno)));
441 if (child->pid != 0) {
444 child->next = child->prev = NULL;
445 DLIST_ADD(children, child);
446 child->event.fd = fdpair[1];
447 child->event.flags = 0;
448 child->requests = NULL;
449 add_fd_event(&child->event);
455 state.sock = fdpair[0];
458 /* tdb needs special fork handling */
459 if (tdb_reopen_all() == -1) {
460 DEBUG(0,("tdb_reopen_all failed.\n"));
464 close_conns_after_fork();
466 if (!override_logfile) {
467 lp_set_logfile(child->logfilename);
472 /* free up any talloc memory */
474 main_loop_talloc_free();
476 /* fetch a request from the main daemon */
477 dual_client_read(&state);
479 if (state.finished) {
480 /* we lost contact with our parent */
484 /* process full rquests */
485 if (state.read_buf_len == sizeof(state.request)) {
486 DEBUG(4,("child daemon request %d\n",
487 (int)state.request.cmd));
489 state.request.null_term = '\0';
490 child_process_request(child->domain, &state);
492 if (state.response.result == WINBINDD_OK)
493 cache_store_response(sys_getpid(),
496 SAFE_FREE(state.response.extra_data);
498 /* We just send the result code back, the result
499 * structure needs to be fetched via the
500 * winbindd_cache. Hmm. That needs fixing... */
502 if (write_data(state.sock,
503 (void *)&state.response.result,
504 sizeof(state.response.result)) !=
505 sizeof(state.response.result)) {
506 DEBUG(0, ("Could not write result\n"));
510 state.read_buf_len = 0;