r23424: Thanks to Jerry, we finally tracked down the :
[ira/wip.git] / source3 / nsswitch / winbindd_dual.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    Winbind child daemons
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  * We fork a child per domain to be able to act non-blocking in the main
26  * winbind daemon. A domain controller thousands of miles away being being
27  * slow replying with a 10.000 user list should not hold up netlogon calls
28  * that can be handled locally.
29  */
30
31 #include "includes.h"
32 #include "winbindd.h"
33
34 #undef DBGC_CLASS
35 #define DBGC_CLASS DBGC_WINBIND
36
37 extern BOOL override_logfile;
38
39 /* Read some data from a client connection */
40
41 static void child_read_request(struct winbindd_cli_state *state)
42 {
43         ssize_t len;
44
45         /* Read data */
46
47         len = read_data(state->sock, (char *)&state->request,
48                         sizeof(state->request));
49
50         if (len != sizeof(state->request)) {
51                 DEBUG(len > 0 ? 0 : 3, ("Got invalid request length: %d\n", (int)len));
52                 state->finished = True;
53                 return;
54         }
55
56         if (state->request.extra_len == 0) {
57                 state->request.extra_data.data = NULL;
58                 return;
59         }
60
61         DEBUG(10, ("Need to read %d extra bytes\n", (int)state->request.extra_len));
62
63         state->request.extra_data.data =
64                 SMB_MALLOC_ARRAY(char, state->request.extra_len + 1);
65
66         if (state->request.extra_data.data == NULL) {
67                 DEBUG(0, ("malloc failed\n"));
68                 state->finished = True;
69                 return;
70         }
71
72         /* Ensure null termination */
73         state->request.extra_data.data[state->request.extra_len] = '\0';
74
75         len = read_data(state->sock, state->request.extra_data.data,
76                         state->request.extra_len);
77
78         if (len != state->request.extra_len) {
79                 DEBUG(0, ("Could not read extra data\n"));
80                 state->finished = True;
81                 return;
82         }
83 }
84
85 /*
86  * Machinery for async requests sent to children. You set up a
87  * winbindd_request, select a child to query, and issue a async_request
88  * call. When the request is completed, the callback function you specified is
89  * called back with the private pointer you gave to async_request.
90  */
91
92 struct winbindd_async_request {
93         struct winbindd_async_request *next, *prev;
94         TALLOC_CTX *mem_ctx;
95         struct winbindd_child *child;
96         struct winbindd_request *request;
97         struct winbindd_response *response;
98         void (*continuation)(void *private_data, BOOL success);
99         struct timed_event *reply_timeout_event;
100         void *private_data;
101 };
102
103 static void async_main_request_sent(void *private_data, BOOL success);
104 static void async_request_sent(void *private_data, BOOL success);
105 static void async_reply_recv(void *private_data, BOOL success);
106 static void schedule_async_request(struct winbindd_child *child);
107
108 void async_request(TALLOC_CTX *mem_ctx, struct winbindd_child *child,
109                    struct winbindd_request *request,
110                    struct winbindd_response *response,
111                    void (*continuation)(void *private_data, BOOL success),
112                    void *private_data)
113 {
114         struct winbindd_async_request *state;
115
116         SMB_ASSERT(continuation != NULL);
117
118         state = TALLOC_P(mem_ctx, struct winbindd_async_request);
119
120         if (state == NULL) {
121                 DEBUG(0, ("talloc failed\n"));
122                 continuation(private_data, False);
123                 return;
124         }
125
126         state->mem_ctx = mem_ctx;
127         state->child = child;
128         state->request = request;
129         state->response = response;
130         state->continuation = continuation;
131         state->private_data = private_data;
132
133         DLIST_ADD_END(child->requests, state, struct winbindd_async_request *);
134
135         schedule_async_request(child);
136
137         return;
138 }
139
140 static void async_main_request_sent(void *private_data, BOOL success)
141 {
142         struct winbindd_async_request *state =
143                 talloc_get_type_abort(private_data, struct winbindd_async_request);
144
145         if (!success) {
146                 DEBUG(5, ("Could not send async request\n"));
147
148                 state->response->length = sizeof(struct winbindd_response);
149                 state->response->result = WINBINDD_ERROR;
150                 state->continuation(state->private_data, False);
151                 return;
152         }
153
154         if (state->request->extra_len == 0) {
155                 async_request_sent(private_data, True);
156                 return;
157         }
158
159         setup_async_write(&state->child->event, state->request->extra_data.data,
160                           state->request->extra_len,
161                           async_request_sent, state);
162 }
163
164 /****************************************************************
165  Handler triggered if the child winbindd doesn't respond within
166  a given timeout.
167 ****************************************************************/
168
169 static void async_request_timeout_handler(struct event_context *ctx,
170                                         struct timed_event *te,
171                                         const struct timeval *now,
172                                         void *private_data)
173 {
174         struct winbindd_async_request *state =
175                 talloc_get_type_abort(private_data, struct winbindd_async_request);
176
177         /* Deal with the reply - set to error. */
178
179         async_reply_recv(private_data, False);
180
181         /* 
182          * Close the socket to the child. Should cause the
183          * child to exit.
184          */
185
186         DEBUG(0,("async_request_timeout_handler: child pid %u is not responding. "
187                 "Closing connection to it.\n",
188                 state->child->pid ));
189
190         winbind_child_died(state->child->pid);
191 }
192
193 static void async_request_sent(void *private_data_data, BOOL success)
194 {
195         uint32_t timeout = 30;
196         struct winbindd_async_request *state =
197                 talloc_get_type_abort(private_data_data, struct winbindd_async_request);
198
199         if (!success) {
200                 DEBUG(5, ("Could not send async request\n"));
201
202                 state->response->length = sizeof(struct winbindd_response);
203                 state->response->result = WINBINDD_ERROR;
204                 state->continuation(state->private_data, False);
205                 return;
206         }
207
208         /* Request successfully sent to the child, setup the wait for reply */
209
210         setup_async_read(&state->child->event,
211                          &state->response->result,
212                          sizeof(state->response->result),
213                          async_reply_recv, state);
214
215         /* 
216          * Normal timeouts are 30s, but auth requests may take a long
217          * time to timeout.
218          */
219
220         if (state->request->cmd == WINBINDD_PAM_AUTH ||
221                         state->request->cmd == WINBINDD_PAM_AUTH_CRAP ) {
222
223                 timeout = 300;
224         }
225
226         /* 
227          * Set up a timeout of 1 minute for the response.
228          * If we don't get it close the child socket and
229          * report failure.
230          */
231
232         state->reply_timeout_event = event_add_timed(winbind_event_context(),
233                                                         NULL,
234                                                         timeval_current_ofs(timeout,0),
235                                                         "async_request_timeout",
236                                                         async_request_timeout_handler,
237                                                         state);
238         if (!state->reply_timeout_event) {
239                 smb_panic("async_request_sent: failed to add timeout handler.\n");
240         }
241 }
242
243 static void async_reply_recv(void *private_data, BOOL success)
244 {
245         struct winbindd_async_request *state =
246                 talloc_get_type_abort(private_data, struct winbindd_async_request);
247         struct winbindd_child *child = state->child;
248
249         if (state->reply_timeout_event) {
250                 TALLOC_FREE(state->reply_timeout_event);
251         }
252
253         state->response->length = sizeof(struct winbindd_response);
254
255         if (!success) {
256                 DEBUG(5, ("Could not receive async reply\n"));
257
258                 cache_cleanup_response(child->pid);
259                 DLIST_REMOVE(child->requests, state);
260
261                 state->response->result = WINBINDD_ERROR;
262                 state->continuation(state->private_data, False);
263                 return;
264         }
265
266         SMB_ASSERT(cache_retrieve_response(child->pid,
267                                            state->response));
268
269         cache_cleanup_response(child->pid);
270         
271         DLIST_REMOVE(child->requests, state);
272
273         schedule_async_request(child);
274
275         state->continuation(state->private_data, True);
276 }
277
278 static BOOL fork_domain_child(struct winbindd_child *child);
279
280 static void schedule_async_request(struct winbindd_child *child)
281 {
282         struct winbindd_async_request *request = child->requests;
283
284         if (request == NULL) {
285                 return;
286         }
287
288         if (child->event.flags != 0) {
289                 return;         /* Busy */
290         }
291
292         if ((child->pid == 0) && (!fork_domain_child(child))) {
293                 /* Cancel all outstanding requests */
294
295                 while (request != NULL) {
296                         /* request might be free'd in the continuation */
297                         struct winbindd_async_request *next = request->next;
298                         request->continuation(request->private_data, False);
299                         request = next;
300                 }
301                 return;
302         }
303
304         setup_async_write(&child->event, request->request,
305                           sizeof(*request->request),
306                           async_main_request_sent, request);
307
308         return;
309 }
310
311 struct domain_request_state {
312         TALLOC_CTX *mem_ctx;
313         struct winbindd_domain *domain;
314         struct winbindd_request *request;
315         struct winbindd_response *response;
316         void (*continuation)(void *private_data_data, BOOL success);
317         void *private_data_data;
318 };
319
320 static void domain_init_recv(void *private_data_data, BOOL success);
321
322 void async_domain_request(TALLOC_CTX *mem_ctx,
323                           struct winbindd_domain *domain,
324                           struct winbindd_request *request,
325                           struct winbindd_response *response,
326                           void (*continuation)(void *private_data_data, BOOL success),
327                           void *private_data_data)
328 {
329         struct domain_request_state *state;
330
331         if (domain->initialized) {
332                 async_request(mem_ctx, &domain->child, request, response,
333                               continuation, private_data_data);
334                 return;
335         }
336
337         state = TALLOC_P(mem_ctx, struct domain_request_state);
338         if (state == NULL) {
339                 DEBUG(0, ("talloc failed\n"));
340                 continuation(private_data_data, False);
341                 return;
342         }
343
344         state->mem_ctx = mem_ctx;
345         state->domain = domain;
346         state->request = request;
347         state->response = response;
348         state->continuation = continuation;
349         state->private_data_data = private_data_data;
350
351         init_child_connection(domain, domain_init_recv, state);
352 }
353
354 static void recvfrom_child(void *private_data_data, BOOL success)
355 {
356         struct winbindd_cli_state *state =
357                 talloc_get_type_abort(private_data_data, struct winbindd_cli_state);
358         enum winbindd_result result = state->response.result;
359
360         /* This is an optimization: The child has written directly to the
361          * response buffer. The request itself is still in pending state,
362          * state that in the result code. */
363
364         state->response.result = WINBINDD_PENDING;
365
366         if ((!success) || (result != WINBINDD_OK)) {
367                 request_error(state);
368                 return;
369         }
370
371         request_ok(state);
372 }
373
374 void sendto_child(struct winbindd_cli_state *state,
375                   struct winbindd_child *child)
376 {
377         async_request(state->mem_ctx, child, &state->request,
378                       &state->response, recvfrom_child, state);
379 }
380
381 void sendto_domain(struct winbindd_cli_state *state,
382                    struct winbindd_domain *domain)
383 {
384         async_domain_request(state->mem_ctx, domain,
385                              &state->request, &state->response,
386                              recvfrom_child, state);
387 }
388
389 static void domain_init_recv(void *private_data_data, BOOL success)
390 {
391         struct domain_request_state *state =
392                 talloc_get_type_abort(private_data_data, struct domain_request_state);
393
394         if (!success) {
395                 DEBUG(5, ("Domain init returned an error\n"));
396                 state->continuation(state->private_data_data, False);
397                 return;
398         }
399
400         async_request(state->mem_ctx, &state->domain->child,
401                       state->request, state->response,
402                       state->continuation, state->private_data_data);
403 }
404
405 struct winbindd_child_dispatch_table {
406         enum winbindd_cmd cmd;
407         enum winbindd_result (*fn)(struct winbindd_domain *domain,
408                                    struct winbindd_cli_state *state);
409         const char *winbindd_cmd_name;
410 };
411
412 static struct winbindd_child_dispatch_table child_dispatch_table[] = {
413         
414         { WINBINDD_LOOKUPSID,            winbindd_dual_lookupsid,             "LOOKUPSID" },
415         { WINBINDD_LOOKUPNAME,           winbindd_dual_lookupname,            "LOOKUPNAME" },
416         { WINBINDD_LOOKUPRIDS,           winbindd_dual_lookuprids,            "LOOKUPRIDS" },
417         { WINBINDD_LIST_TRUSTDOM,        winbindd_dual_list_trusted_domains,  "LIST_TRUSTDOM" },
418         { WINBINDD_INIT_CONNECTION,      winbindd_dual_init_connection,       "INIT_CONNECTION" },
419         { WINBINDD_GETDCNAME,            winbindd_dual_getdcname,             "GETDCNAME" },
420         { WINBINDD_SHOW_SEQUENCE,        winbindd_dual_show_sequence,         "SHOW_SEQUENCE" },
421         { WINBINDD_PAM_AUTH,             winbindd_dual_pam_auth,              "PAM_AUTH" },
422         { WINBINDD_PAM_AUTH_CRAP,        winbindd_dual_pam_auth_crap,         "AUTH_CRAP" },
423         { WINBINDD_PAM_LOGOFF,           winbindd_dual_pam_logoff,            "PAM_LOGOFF" },
424         { WINBINDD_PAM_CHNG_PSWD_AUTH_CRAP,winbindd_dual_pam_chng_pswd_auth_crap,"CHNG_PSWD_AUTH_CRAP" },
425         { WINBINDD_PAM_CHAUTHTOK,        winbindd_dual_pam_chauthtok,         "PAM_CHAUTHTOK" },
426         { WINBINDD_CHECK_MACHACC,        winbindd_dual_check_machine_acct,    "CHECK_MACHACC" },
427         { WINBINDD_DUAL_SID2UID,         winbindd_dual_sid2uid,               "DUAL_SID2UID" },
428         { WINBINDD_DUAL_SID2GID,         winbindd_dual_sid2gid,               "DUAL_SID2GID" },
429 #if 0   /* DISABLED until we fix the interface in Samba 3.0.26 --jerry */
430         { WINBINDD_DUAL_SIDS2XIDS,       winbindd_dual_sids2xids,             "DUAL_SIDS2XIDS" },
431 #endif  /* end DISABLED */
432         { WINBINDD_DUAL_UID2SID,         winbindd_dual_uid2sid,               "DUAL_UID2SID" },
433         { WINBINDD_DUAL_GID2SID,         winbindd_dual_gid2sid,               "DUAL_GID2SID" },
434         { WINBINDD_DUAL_UID2NAME,        winbindd_dual_uid2name,              "DUAL_UID2NAME" },
435         { WINBINDD_DUAL_NAME2UID,        winbindd_dual_name2uid,              "DUAL_NAME2UID" },
436         { WINBINDD_DUAL_GID2NAME,        winbindd_dual_gid2name,              "DUAL_GID2NAME" },
437         { WINBINDD_DUAL_NAME2GID,        winbindd_dual_name2gid,              "DUAL_NAME2GID" },
438         { WINBINDD_DUAL_SET_MAPPING,     winbindd_dual_set_mapping,           "DUAL_SET_MAPPING" },
439         { WINBINDD_DUAL_SET_HWM,         winbindd_dual_set_hwm,               "DUAL_SET_HWMS" },
440         { WINBINDD_DUAL_DUMP_MAPS,       winbindd_dual_dump_maps,             "DUAL_DUMP_MAPS" },
441         { WINBINDD_DUAL_USERINFO,        winbindd_dual_userinfo,              "DUAL_USERINFO" },
442         { WINBINDD_ALLOCATE_UID,         winbindd_dual_allocate_uid,          "ALLOCATE_UID" },
443         { WINBINDD_ALLOCATE_GID,         winbindd_dual_allocate_gid,          "ALLOCATE_GID" },
444         { WINBINDD_GETUSERDOMGROUPS,     winbindd_dual_getuserdomgroups,      "GETUSERDOMGROUPS" },
445         { WINBINDD_DUAL_GETSIDALIASES,   winbindd_dual_getsidaliases,         "GETSIDALIASES" },
446         { WINBINDD_CCACHE_NTLMAUTH,      winbindd_dual_ccache_ntlm_auth,      "CCACHE_NTLM_AUTH" },
447         /* End of list */
448
449         { WINBINDD_NUM_CMDS, NULL, "NONE" }
450 };
451
452 static void child_process_request(struct winbindd_domain *domain,
453                                   struct winbindd_cli_state *state)
454 {
455         struct winbindd_child_dispatch_table *table;
456
457         /* Free response data - we may be interrupted and receive another
458            command before being able to send this data off. */
459
460         state->response.result = WINBINDD_ERROR;
461         state->response.length = sizeof(struct winbindd_response);
462
463         state->mem_ctx = talloc_init("winbind request");
464         if (state->mem_ctx == NULL)
465                 return;
466
467         /* Process command */
468
469         for (table = child_dispatch_table; table->fn; table++) {
470                 if (state->request.cmd == table->cmd) {
471                         DEBUG(10,("process_request: request fn %s\n",
472                                   table->winbindd_cmd_name ));
473                         state->response.result = table->fn(domain, state);
474                         break;
475                 }
476         }
477
478         if (!table->fn) {
479                 DEBUG(10,("process_request: unknown request fn number %d\n",
480                           (int)state->request.cmd ));
481                 state->response.result = WINBINDD_ERROR;
482         }
483
484         talloc_destroy(state->mem_ctx);
485 }
486
487 void setup_domain_child(struct winbindd_domain *domain,
488                         struct winbindd_child *child,
489                         const char *explicit_logfile)
490 {
491         if (explicit_logfile != NULL) {
492                 pstr_sprintf(child->logfilename, "%s/log.winbindd-%s",
493                              dyn_LOGFILEBASE, explicit_logfile);
494         } else if (domain != NULL) {
495                 pstr_sprintf(child->logfilename, "%s/log.wb-%s",
496                              dyn_LOGFILEBASE, domain->name);
497         } else {
498                 smb_panic("Internal error: domain == NULL && "
499                           "explicit_logfile == NULL");
500         }
501
502         child->domain = domain;
503 }
504
505 struct winbindd_child *children = NULL;
506
507 void winbind_child_died(pid_t pid)
508 {
509         struct winbindd_child *child;
510
511         for (child = children; child != NULL; child = child->next) {
512                 if (child->pid == pid) {
513                         break;
514                 }
515         }
516
517         if (child == NULL) {
518                 DEBUG(0, ("Unknown child %d died!\n", pid));
519                 return;
520         }
521
522         remove_fd_event(&child->event);
523         close(child->event.fd);
524         child->event.fd = 0;
525         child->event.flags = 0;
526         child->pid = 0;
527
528         schedule_async_request(child);
529 }
530
531 /* Ensure any negative cache entries with the netbios or realm names are removed. */
532
533 void winbindd_flush_negative_conn_cache(struct winbindd_domain *domain)
534 {
535         flush_negative_conn_cache_for_domain(domain->name);
536         if (*domain->alt_name) {
537                 flush_negative_conn_cache_for_domain(domain->alt_name);
538         }
539 }
540
541 /* Set our domains as offline and forward the offline message to our children. */
542
543 void winbind_msg_offline(struct messaging_context *msg_ctx,
544                          void *private_data,
545                          uint32_t msg_type,
546                          struct server_id server_id,
547                          DATA_BLOB *data)
548 {
549         struct winbindd_child *child;
550         struct winbindd_domain *domain;
551
552         DEBUG(10,("winbind_msg_offline: got offline message.\n"));
553
554         if (!lp_winbind_offline_logon()) {
555                 DEBUG(10,("winbind_msg_offline: rejecting offline message.\n"));
556                 return;
557         }
558
559         /* Set our global state as offline. */
560         if (!set_global_winbindd_state_offline()) {
561                 DEBUG(10,("winbind_msg_offline: offline request failed.\n"));
562                 return;
563         }
564
565         /* Set all our domains as offline. */
566         for (domain = domain_list(); domain; domain = domain->next) {
567                 if (domain->internal) {
568                         continue;
569                 }
570                 DEBUG(5,("winbind_msg_offline: marking %s offline.\n", domain->name));
571                 set_domain_offline(domain);
572         }
573
574         for (child = children; child != NULL; child = child->next) {
575                 /* Don't send message to idmap child.  We've already
576                    done so above. */
577                 if (!child->domain || (child == idmap_child())) {
578                         continue;
579                 }
580
581                 /* Or internal domains (this should not be possible....) */
582                 if (child->domain->internal) {
583                         continue;
584                 }
585
586                 /* Each winbindd child should only process requests for one domain - make sure
587                    we only set it online / offline for that domain. */
588
589                 DEBUG(10,("winbind_msg_offline: sending message to pid %u for domain %s.\n",
590                         (unsigned int)child->pid, domain->name ));
591
592                 messaging_send_buf(msg_ctx, pid_to_procid(child->pid),
593                                    MSG_WINBIND_OFFLINE,
594                                    (uint8 *)child->domain->name,
595                                    strlen(child->domain->name)+1);
596         }
597 }
598
599 /* Set our domains as online and forward the online message to our children. */
600
601 void winbind_msg_online(struct messaging_context *msg_ctx,
602                         void *private_data,
603                         uint32_t msg_type,
604                         struct server_id server_id,
605                         DATA_BLOB *data)
606 {
607         struct winbindd_child *child;
608         struct winbindd_domain *domain;
609
610         DEBUG(10,("winbind_msg_online: got online message.\n"));
611
612         if (!lp_winbind_offline_logon()) {
613                 DEBUG(10,("winbind_msg_online: rejecting online message.\n"));
614                 return;
615         }
616
617         /* Set our global state as online. */
618         set_global_winbindd_state_online();
619
620         smb_nscd_flush_user_cache();
621         smb_nscd_flush_group_cache();
622
623         /* Set all our domains as online. */
624         for (domain = domain_list(); domain; domain = domain->next) {
625                 if (domain->internal) {
626                         continue;
627                 }
628                 DEBUG(5,("winbind_msg_online: requesting %s to go online.\n", domain->name));
629
630                 winbindd_flush_negative_conn_cache(domain);
631                 set_domain_online_request(domain);
632
633                 /* Send an online message to the idmap child when our
634                    primary domain comes back online */
635
636                 if ( domain->primary ) {
637                         struct winbindd_child *idmap = idmap_child();
638                         
639                         if ( idmap->pid != 0 ) {
640                                 messaging_send_buf(msg_ctx,
641                                                    pid_to_procid(idmap->pid), 
642                                                    MSG_WINBIND_ONLINE,
643                                                    (uint8 *)domain->name,
644                                                    strlen(domain->name)+1);
645                         }
646                         
647                 }
648         }
649
650         for (child = children; child != NULL; child = child->next) {
651                 /* Don't send message to idmap child. */
652                 if (!child->domain || (child == idmap_child())) {
653                         continue;
654                 }
655
656                 /* Or internal domains (this should not be possible....) */
657                 if (child->domain->internal) {
658                         continue;
659                 }
660
661                 /* Each winbindd child should only process requests for one domain - make sure
662                    we only set it online / offline for that domain. */
663
664                 DEBUG(10,("winbind_msg_online: sending message to pid %u for domain %s.\n",
665                         (unsigned int)child->pid, child->domain->name ));
666
667                 messaging_send_buf(msg_ctx, pid_to_procid(child->pid),
668                                    MSG_WINBIND_ONLINE,
669                                    (uint8 *)child->domain->name,
670                                    strlen(child->domain->name)+1);
671         }
672 }
673
674 /* Forward the online/offline messages to our children. */
675 void winbind_msg_onlinestatus(struct messaging_context *msg_ctx,
676                               void *private_data,
677                               uint32_t msg_type,
678                               struct server_id server_id,
679                               DATA_BLOB *data)
680 {
681         struct winbindd_child *child;
682
683         DEBUG(10,("winbind_msg_onlinestatus: got onlinestatus message.\n"));
684
685         for (child = children; child != NULL; child = child->next) {
686                 if (child->domain && child->domain->primary) {
687                         DEBUG(10,("winbind_msg_onlinestatus: "
688                                   "sending message to pid %u of primary domain.\n",
689                                   (unsigned int)child->pid));
690                         messaging_send_buf(msg_ctx, pid_to_procid(child->pid), 
691                                            MSG_WINBIND_ONLINESTATUS,
692                                            (uint8 *)data->data,
693                                            data->length);
694                         break;
695                 }
696         }
697 }
698
699
700 static void account_lockout_policy_handler(struct event_context *ctx,
701                                            struct timed_event *te,
702                                            const struct timeval *now,
703                                            void *private_data)
704 {
705         struct winbindd_child *child =
706                 (struct winbindd_child *)private_data;
707         TALLOC_CTX *mem_ctx = NULL;
708         struct winbindd_methods *methods;
709         SAM_UNK_INFO_12 lockout_policy;
710         NTSTATUS result;
711
712         DEBUG(10,("account_lockout_policy_handler called\n"));
713
714         if (child->lockout_policy_event) {
715                 TALLOC_FREE(child->lockout_policy_event);
716         }
717
718         if ( !winbindd_can_contact_domain( child->domain ) ) {
719                 DEBUG(10,("account_lockout_policy_handler: Removing myself since I "
720                           "do not have an incoming trust to domain %s\n", 
721                           child->domain->name));
722
723                 return;         
724         }
725
726         methods = child->domain->methods;
727
728         mem_ctx = talloc_init("account_lockout_policy_handler ctx");
729         if (!mem_ctx) {
730                 result = NT_STATUS_NO_MEMORY;
731         } else {
732                 result = methods->lockout_policy(child->domain, mem_ctx, &lockout_policy);
733         }
734
735         talloc_destroy(mem_ctx);
736
737         if (!NT_STATUS_IS_OK(result)) {
738                 DEBUG(10,("account_lockout_policy_handler: lockout_policy failed error %s\n",
739                          nt_errstr(result)));
740         }
741
742         child->lockout_policy_event = event_add_timed(winbind_event_context(), NULL,
743                                                       timeval_current_ofs(3600, 0),
744                                                       "account_lockout_policy_handler",
745                                                       account_lockout_policy_handler,
746                                                       child);
747 }
748
749 /* Deal with a request to go offline. */
750
751 static void child_msg_offline(struct messaging_context *msg,
752                               void *private_data,
753                               uint32_t msg_type,
754                               struct server_id server_id,
755                               DATA_BLOB *data)
756 {
757         struct winbindd_domain *domain;
758         const char *domainname = (const char *)data->data;
759
760         if (data->data == NULL || data->length == 0) {
761                 return;
762         }
763
764         DEBUG(5,("child_msg_offline received for domain %s.\n", domainname));
765
766         if (!lp_winbind_offline_logon()) {
767                 DEBUG(10,("child_msg_offline: rejecting offline message.\n"));
768                 return;
769         }
770
771         /* Mark the requested domain offline. */
772
773         for (domain = domain_list(); domain; domain = domain->next) {
774                 if (domain->internal) {
775                         continue;
776                 }
777                 if (strequal(domain->name, domainname)) {
778                         DEBUG(5,("child_msg_offline: marking %s offline.\n", domain->name));
779                         set_domain_offline(domain);
780                 }
781         }
782 }
783
784 /* Deal with a request to go online. */
785
786 static void child_msg_online(struct messaging_context *msg,
787                              void *private_data,
788                              uint32_t msg_type,
789                              struct server_id server_id,
790                              DATA_BLOB *data)
791 {
792         struct winbindd_domain *domain;
793         const char *domainname = (const char *)data->data;
794
795         if (data->data == NULL || data->length == 0) {
796                 return;
797         }
798
799         DEBUG(5,("child_msg_online received for domain %s.\n", domainname));
800
801         if (!lp_winbind_offline_logon()) {
802                 DEBUG(10,("child_msg_online: rejecting online message.\n"));
803                 return;
804         }
805
806         /* Set our global state as online. */
807         set_global_winbindd_state_online();
808
809         /* Try and mark everything online - delete any negative cache entries
810            to force a reconnect now. */
811
812         for (domain = domain_list(); domain; domain = domain->next) {
813                 if (domain->internal) {
814                         continue;
815                 }
816                 if (strequal(domain->name, domainname)) {
817                         DEBUG(5,("child_msg_online: requesting %s to go online.\n", domain->name));
818                         winbindd_flush_negative_conn_cache(domain);
819                         set_domain_online_request(domain);
820                 }
821         }
822 }
823
824 static const char *collect_onlinestatus(TALLOC_CTX *mem_ctx)
825 {
826         struct winbindd_domain *domain;
827         char *buf = NULL;
828
829         if ((buf = talloc_asprintf(mem_ctx, "global:%s ", 
830                                    get_global_winbindd_state_offline() ? 
831                                    "Offline":"Online")) == NULL) {
832                 return NULL;
833         }
834
835         for (domain = domain_list(); domain; domain = domain->next) {
836                 if ((buf = talloc_asprintf_append(buf, "%s:%s ", 
837                                                   domain->name, 
838                                                   domain->online ?
839                                                   "Online":"Offline")) == NULL) {
840                         return NULL;
841                 }
842         }
843
844         buf = talloc_asprintf_append(buf, "\n");
845
846         DEBUG(5,("collect_onlinestatus: %s", buf));
847
848         return buf;
849 }
850
851 static void child_msg_onlinestatus(struct messaging_context *msg_ctx,
852                                    void *private_data,
853                                    uint32_t msg_type,
854                                    struct server_id server_id,
855                                    DATA_BLOB *data)
856 {
857         TALLOC_CTX *mem_ctx;
858         const char *message;
859         struct server_id *sender;
860         
861         DEBUG(5,("winbind_msg_onlinestatus received.\n"));
862
863         if (!data->data) {
864                 return;
865         }
866
867         sender = (struct server_id *)data->data;
868
869         mem_ctx = talloc_init("winbind_msg_onlinestatus");
870         if (mem_ctx == NULL) {
871                 return;
872         }
873         
874         message = collect_onlinestatus(mem_ctx);
875         if (message == NULL) {
876                 talloc_destroy(mem_ctx);
877                 return;
878         }
879
880         messaging_send_buf(msg_ctx, *sender, MSG_WINBIND_ONLINESTATUS, 
881                            (uint8 *)message, strlen(message) + 1);
882
883         talloc_destroy(mem_ctx);
884 }
885
886 static BOOL fork_domain_child(struct winbindd_child *child)
887 {
888         int fdpair[2];
889         struct winbindd_cli_state state;
890         struct winbindd_domain *domain;
891
892         if (socketpair(AF_UNIX, SOCK_STREAM, 0, fdpair) != 0) {
893                 DEBUG(0, ("Could not open child pipe: %s\n",
894                           strerror(errno)));
895                 return False;
896         }
897
898         ZERO_STRUCT(state);
899         state.pid = sys_getpid();
900
901         /* Stop zombies */
902         CatchChild();
903
904         child->pid = sys_fork();
905
906         if (child->pid == -1) {
907                 DEBUG(0, ("Could not fork: %s\n", strerror(errno)));
908                 return False;
909         }
910
911         if (child->pid != 0) {
912                 /* Parent */
913                 close(fdpair[0]);
914                 child->next = child->prev = NULL;
915                 DLIST_ADD(children, child);
916                 child->event.fd = fdpair[1];
917                 child->event.flags = 0;
918                 child->requests = NULL;
919                 add_fd_event(&child->event);
920                 return True;
921         }
922
923         /* Child */
924
925         state.sock = fdpair[0];
926         close(fdpair[1]);
927
928         /* tdb needs special fork handling */
929         if (tdb_reopen_all(1) == -1) {
930                 DEBUG(0,("tdb_reopen_all failed.\n"));
931                 _exit(0);
932         }
933
934         close_conns_after_fork();
935
936         if (!override_logfile) {
937                 lp_set_logfile(child->logfilename);
938                 reopen_logs();
939         }
940
941         /*
942          * For clustering, we need to re-init our ctdbd connection after the
943          * fork
944          */
945         if (!NT_STATUS_IS_OK(messaging_reinit(winbind_messaging_context())))
946                 exit(1);
947
948         /* Don't handle the same messages as our parent. */
949         messaging_deregister(winbind_messaging_context(),
950                              MSG_SMB_CONF_UPDATED, NULL);
951         messaging_deregister(winbind_messaging_context(),
952                              MSG_SHUTDOWN, NULL);
953         messaging_deregister(winbind_messaging_context(),
954                              MSG_WINBIND_OFFLINE, NULL);
955         messaging_deregister(winbind_messaging_context(),
956                              MSG_WINBIND_ONLINE, NULL);
957         messaging_deregister(winbind_messaging_context(),
958                              MSG_WINBIND_ONLINESTATUS, NULL);
959
960         /* Handle online/offline messages. */
961         messaging_register(winbind_messaging_context(), NULL,
962                            MSG_WINBIND_OFFLINE, child_msg_offline);
963         messaging_register(winbind_messaging_context(), NULL,
964                            MSG_WINBIND_ONLINE, child_msg_online);
965         messaging_register(winbind_messaging_context(), NULL,
966                            MSG_WINBIND_ONLINESTATUS, child_msg_onlinestatus);
967
968         if ( child->domain ) {
969                 child->domain->startup = True;
970                 child->domain->startup_time = time(NULL);
971         }
972
973         /* Ensure we have no pending check_online events other
974            than one for this domain. */
975
976         for (domain = domain_list(); domain; domain = domain->next) {
977                 if (domain != child->domain) {
978                         if (domain->check_online_event) {
979                                 TALLOC_FREE(domain->check_online_event);
980                         }
981                 }
982         }
983
984         /* Ensure we're not handling an event inherited from
985            our parent. */
986
987         cancel_named_event(winbind_event_context(),
988                            "krb5_ticket_refresh_handler");
989
990         /* We might be in the idmap child...*/
991         if (child->domain && !(child->domain->internal) &&
992             lp_winbind_offline_logon()) {
993
994                 set_domain_online_request(child->domain);
995
996                 child->lockout_policy_event = event_add_timed(
997                         winbind_event_context(), NULL, timeval_zero(),
998                         "account_lockout_policy_handler",
999                         account_lockout_policy_handler,
1000                         child);
1001         }
1002
1003         while (1) {
1004
1005                 int ret;
1006                 fd_set read_fds;
1007                 struct timeval t;
1008                 struct timeval *tp;
1009                 struct timeval now;
1010
1011                 /* free up any talloc memory */
1012                 lp_TALLOC_FREE();
1013                 main_loop_TALLOC_FREE();
1014
1015                 run_events(winbind_event_context(), 0, NULL, NULL);
1016
1017                 GetTimeOfDay(&now);
1018
1019                 if (child->domain && child->domain->startup &&
1020                                 (now.tv_sec > child->domain->startup_time + 30)) {
1021                         /* No longer in "startup" mode. */
1022                         DEBUG(10,("fork_domain_child: domain %s no longer in 'startup' mode.\n",
1023                                 child->domain->name ));
1024                         child->domain->startup = False;
1025                 }
1026
1027                 tp = get_timed_events_timeout(winbind_event_context(), &t);
1028                 if (tp) {
1029                         DEBUG(11,("select will use timeout of %u.%u seconds\n",
1030                                 (unsigned int)tp->tv_sec, (unsigned int)tp->tv_usec ));
1031                 }
1032
1033                 /* Handle messages */
1034
1035                 message_dispatch(winbind_messaging_context());
1036
1037                 FD_ZERO(&read_fds);
1038                 FD_SET(state.sock, &read_fds);
1039
1040                 ret = sys_select(state.sock + 1, &read_fds, NULL, NULL, tp);
1041
1042                 if (ret == 0) {
1043                         DEBUG(11,("nothing is ready yet, continue\n"));
1044                         continue;
1045                 }
1046
1047                 if (ret == -1 && errno == EINTR) {
1048                         /* We got a signal - continue. */
1049                         continue;
1050                 }
1051
1052                 if (ret == -1 && errno != EINTR) {
1053                         DEBUG(0,("select error occured\n"));
1054                         perror("select");
1055                         return False;
1056                 }
1057
1058                 /* fetch a request from the main daemon */
1059                 child_read_request(&state);
1060
1061                 if (state.finished) {
1062                         /* we lost contact with our parent */
1063                         exit(0);
1064                 }
1065
1066                 DEBUG(4,("child daemon request %d\n", (int)state.request.cmd));
1067
1068                 ZERO_STRUCT(state.response);
1069                 state.request.null_term = '\0';
1070                 child_process_request(child->domain, &state);
1071
1072                 SAFE_FREE(state.request.extra_data.data);
1073
1074                 cache_store_response(sys_getpid(), &state.response);
1075
1076                 SAFE_FREE(state.response.extra_data.data);
1077
1078                 /* We just send the result code back, the result
1079                  * structure needs to be fetched via the
1080                  * winbindd_cache. Hmm. That needs fixing... */
1081
1082                 if (write_data(state.sock,
1083                                (const char *)&state.response.result,
1084                                sizeof(state.response.result)) !=
1085                     sizeof(state.response.result)) {
1086                         DEBUG(0, ("Could not write result\n"));
1087                         exit(1);
1088                 }
1089         }
1090 }