r9330: Remove the classic dual daemon since it was not being used.
[samba.git] / source / nsswitch / winbindd_dual.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    Winbind background daemon
5
6    Copyright (C) Andrew Tridgell 2002
7    Copyright (C) Volker Lendecke 2004,2005
8    
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.
13    
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.
18    
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.
22 */
23
24 /*
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
30
31  */
32
33 #include "includes.h"
34 #include "winbindd.h"
35
36 #undef DBGC_CLASS
37 #define DBGC_CLASS DBGC_WINBIND
38
39 /* Read some data from a client connection */
40
41 static void dual_client_read(struct winbindd_cli_state *state)
42 {
43         int n;
44     
45         /* Read data */
46
47         n = sys_read(state->sock, state->read_buf_len + 
48                  (char *)&state->request, 
49                  sizeof(state->request) - state->read_buf_len);
50         
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) ));
54
55         /* Read failed, kill client */
56         
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"));
61                 
62                 state->finished = True;
63                 return;
64         }
65         
66         /* Update client state */
67         
68         state->read_buf_len += n;
69         state->last_access = time(NULL);
70 }
71
72 /*
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.
77  */
78
79 struct winbindd_async_request {
80         struct winbindd_async_request *next, *prev;
81         TALLOC_CTX *mem_ctx;
82         struct winbindd_child *child;
83         struct winbindd_request *request;
84         struct winbindd_response *response;
85         void (*continuation)(void *private_data, BOOL success);
86         void *private_data;
87 };
88
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);
92
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),
97                    void *private_data)
98 {
99         struct winbindd_async_request *state, *tmp;
100
101         SMB_ASSERT(continuation != NULL);
102
103         state = TALLOC_P(mem_ctx, struct winbindd_async_request);
104
105         if (state == NULL) {
106                 DEBUG(0, ("talloc failed\n"));
107                 continuation(private_data, False);
108                 return;
109         }
110
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;
117
118         DLIST_ADD_END(child->requests, state, tmp);
119
120         schedule_async_request(child);
121
122         return;
123 }
124
125 static void async_request_sent(void *private_data, BOOL success)
126 {
127         struct winbindd_async_request *state =
128                 talloc_get_type_abort(private_data, struct winbindd_async_request);
129
130         if (!success) {
131                 DEBUG(5, ("Could not send async request\n"));
132
133                 state->response->length = sizeof(struct winbindd_response);
134                 state->response->result = WINBINDD_ERROR;
135                 state->continuation(state->private_data, False);
136                 return;
137         }
138
139         /* Request successfully sent to the child, setup the wait for reply */
140
141         setup_async_read(&state->child->event,
142                          &state->response->result,
143                          sizeof(state->response->result),
144                          async_reply_recv, state);
145 }
146
147 static void async_reply_recv(void *private_data, BOOL success)
148 {
149         struct winbindd_async_request *state =
150                 talloc_get_type_abort(private_data, struct winbindd_async_request);
151         struct winbindd_child *child = state->child;
152
153         state->response->length = sizeof(struct winbindd_response);
154
155         if (!success) {
156                 DEBUG(5, ("Could not receive async reply\n"));
157                 state->response->result = WINBINDD_ERROR;
158                 return;
159         }
160
161         if (state->response->result == WINBINDD_OK)
162                 SMB_ASSERT(cache_retrieve_response(child->pid,
163                                                    state->response));
164
165         DLIST_REMOVE(child->requests, state);
166
167         schedule_async_request(child);
168
169         state->continuation(state->private_data, True);
170 }
171
172 static BOOL fork_domain_child(struct winbindd_child *child);
173
174 static void schedule_async_request(struct winbindd_child *child)
175 {
176         struct winbindd_async_request *request = child->requests;
177
178         if (request == NULL) {
179                 return;
180         }
181
182         if (child->event.flags != 0) {
183                 return;         /* Busy */
184         }
185
186         if ((child->pid == 0) && (!fork_domain_child(child))) {
187                 /* Cancel all outstanding requests */
188
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);
193                         request = next;
194                 }
195                 return;
196         }
197
198         setup_async_write(&child->event, request->request,
199                           sizeof(*request->request),
200                           async_request_sent, request);
201         return;
202 }
203
204 struct domain_request_state {
205         TALLOC_CTX *mem_ctx;
206         struct winbindd_domain *domain;
207         struct winbindd_request *request;
208         struct winbindd_response *response;
209         void (*continuation)(void *private_data, BOOL success);
210         void *private_data;
211 };
212
213 static void domain_init_recv(void *private_data, BOOL success);
214
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),
220                           void *private_data)
221 {
222         struct domain_request_state *state;
223
224         if (domain->initialized) {
225                 async_request(mem_ctx, &domain->child, request, response,
226                               continuation, private_data);
227                 return;
228         }
229
230         state = TALLOC_P(mem_ctx, struct domain_request_state);
231         if (state == NULL) {
232                 DEBUG(0, ("talloc failed\n"));
233                 continuation(private_data, False);
234                 return;
235         }
236
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;
243
244         init_child_connection(domain, domain_init_recv, state);
245 }
246
247 static void recvfrom_child(void *private_data, BOOL success)
248 {
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;
252
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. */
256
257         state->response.result = WINBINDD_PENDING;
258
259         if ((!success) || (result != WINBINDD_OK)) {
260                 request_error(state);
261                 return;
262         }
263
264         request_ok(state);
265 }
266
267 void sendto_child(struct winbindd_cli_state *state,
268                   struct winbindd_child *child)
269 {
270         async_request(state->mem_ctx, child, &state->request,
271                       &state->response, recvfrom_child, state);
272 }
273
274 void sendto_domain(struct winbindd_cli_state *state,
275                    struct winbindd_domain *domain)
276 {
277         async_domain_request(state->mem_ctx, domain,
278                              &state->request, &state->response,
279                              recvfrom_child, state);
280 }
281
282 static void domain_init_recv(void *private_data, BOOL success)
283 {
284         struct domain_request_state *state =
285                 talloc_get_type_abort(private_data, struct domain_request_state);
286
287         if (!success) {
288                 DEBUG(5, ("Domain init returned an error\n"));
289                 state->continuation(state->private_data, False);
290                 return;
291         }
292
293         async_request(state->mem_ctx, &state->domain->child,
294                       state->request, state->response,
295                       state->continuation, state->private_data);
296 }
297
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;
303 };
304
305 static struct winbindd_child_dispatch_table child_dispatch_table[] = {
306         
307         { WINBINDD_LOOKUPSID, winbindd_dual_lookupsid, "LOOKUPSID" },
308         { WINBINDD_LOOKUPNAME, winbindd_dual_lookupname, "LOOKUPNAME" },
309         { WINBINDD_LIST_TRUSTDOM, winbindd_dual_list_trusted_domains,
310           "LIST_TRUSTDOM" },
311         { WINBINDD_INIT_CONNECTION, winbindd_dual_init_connection,
312           "INIT_CONNECTION" },
313         { WINBINDD_GETDCNAME, winbindd_dual_getdcname, "GETDCNAME" },
314         { WINBINDD_SHOW_SEQUENCE, winbindd_dual_show_sequence,
315           "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,
319           "CHECK_MACHACC" },
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,
334           "GETSIDALIASES" },
335         /* End of list */
336
337         { WINBINDD_NUM_CMDS, NULL, "NONE" }
338 };
339
340 static void child_process_request(struct winbindd_domain *domain,
341                                   struct winbindd_cli_state *state)
342 {
343         struct winbindd_child_dispatch_table *table;
344
345         /* Free response data - we may be interrupted and receive another
346            command before being able to send this data off. */
347
348         state->response.result = WINBINDD_ERROR;
349         state->response.length = sizeof(struct winbindd_response);
350
351         state->mem_ctx = talloc_init("winbind request");
352         if (state->mem_ctx == NULL)
353                 return;
354
355         /* Process command */
356
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);
362                         break;
363                 }
364         }
365
366         if (!table->fn) {
367                 DEBUG(10,("process_request: unknown request fn number %d\n",
368                           (int)state->request.cmd ));
369                 state->response.result = WINBINDD_ERROR;
370         }
371
372         talloc_destroy(state->mem_ctx);
373 }
374
375 void setup_domain_child(struct winbindd_domain *domain,
376                         struct winbindd_child *child,
377                         const char *explicit_logfile)
378 {
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);
385         } else {
386                 smb_panic("Internal error: domain == NULL && "
387                           "explicit_logfile == NULL");
388         }
389
390         child->domain = domain;
391 }
392
393 struct winbindd_child *children = NULL;
394
395 void winbind_child_died(pid_t pid)
396 {
397         struct winbindd_child *child;
398
399         for (child = children; child != NULL; child = child->next) {
400                 if (child->pid == pid) {
401                         break;
402                 }
403         }
404
405         if (child == NULL) {
406                 DEBUG(0, ("Unknown child %d died!\n", pid));
407                 return;
408         }
409
410         remove_fd_event(&child->event);
411         close(child->event.fd);
412         child->event.fd = 0;
413         child->event.flags = 0;
414         child->pid = 0;
415
416         schedule_async_request(child);
417 }
418
419 static BOOL fork_domain_child(struct winbindd_child *child)
420 {
421         int fdpair[2];
422         struct winbindd_cli_state state;
423         extern BOOL override_logfile;
424
425         if (socketpair(AF_UNIX, SOCK_STREAM, 0, fdpair) != 0) {
426                 DEBUG(0, ("Could not open child pipe: %s\n",
427                           strerror(errno)));
428                 return False;
429         }
430
431         ZERO_STRUCT(state);
432         state.pid = getpid();
433
434         child->pid = sys_fork();
435
436         if (child->pid == -1) {
437                 DEBUG(0, ("Could not fork: %s\n", strerror(errno)));
438                 return False;
439         }
440
441         if (child->pid != 0) {
442                 /* Parent */
443                 close(fdpair[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);
450                 return True;
451         }
452
453         /* Child */
454
455         state.sock = fdpair[0];
456         close(fdpair[1]);
457
458         /* tdb needs special fork handling */
459         if (tdb_reopen_all() == -1) {
460                 DEBUG(0,("tdb_reopen_all failed.\n"));
461                 _exit(0);
462         }
463
464         close_conns_after_fork();
465
466         if (!override_logfile) {
467                 lp_set_logfile(child->logfilename);
468                 reopen_logs();
469         }
470         
471         while (1) {
472                 /* free up any talloc memory */
473                 lp_talloc_free();
474                 main_loop_talloc_free();
475
476                 /* fetch a request from the main daemon */
477                 dual_client_read(&state);
478
479                 if (state.finished) {
480                         /* we lost contact with our parent */
481                         exit(0);
482                 }
483
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));
488
489                         state.request.null_term = '\0';
490                         child_process_request(child->domain, &state);
491
492                         if (state.response.result == WINBINDD_OK)
493                                 cache_store_response(sys_getpid(),
494                                                      &state.response);
495
496                         SAFE_FREE(state.response.extra_data);
497
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... */
501
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"));
507                                 exit(1);
508                         }
509
510                         state.read_buf_len = 0;
511                 }
512         }
513 }