1/*
2   Unix SMB/CIFS implementation.
3
4   Winbind background daemon
5
6   Copyright (C) Andrew Tridgell 2002
7
8   This program is free software; you can redistribute it and/or modify
9   it under the terms of the GNU General Public License as published by
10   the Free Software Foundation; either version 2 of the License, or
11   (at your option) any later version.
12
13   This program is distributed in the hope that it will be useful,
14   but WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16   GNU General Public License for more details.
17
18   You should have received a copy of the GNU General Public License
19   along with this program; if not, write to the Free Software
20   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21*/
22
23/*
24  the idea of the optional dual daemon mode is ot prevent slow domain
25  responses from clagging up the rest of the system. When in dual
26  daemon mode winbindd always responds to requests from cache if the
27  request is in cache, and if the cached answer is stale then it asks
28  the "dual daemon" to update the cache for that request
29
30 */
31
32#include "includes.h"
33#include "winbindd.h"
34
35#undef DBGC_CLASS
36#define DBGC_CLASS DBGC_WINBIND
37
38extern BOOL opt_dual_daemon;
39BOOL background_process = False;
40int dual_daemon_pipe = -1;
41
42
43/* a list of requests ready to be sent to the dual daemon */
44struct dual_list {
45	struct dual_list *next;
46	char *data;
47	int length;
48	int offset;
49};
50
51static struct dual_list *dual_list;
52static struct dual_list *dual_list_end;
53
54/*
55  setup a select() including the dual daemon pipe
56 */
57int dual_select_setup(fd_set *fds, int maxfd)
58{
59	if (dual_daemon_pipe == -1 ||
60	    !dual_list) {
61		return maxfd;
62	}
63
64	FD_SET(dual_daemon_pipe, fds);
65	if (dual_daemon_pipe > maxfd) {
66		maxfd = dual_daemon_pipe;
67	}
68	return maxfd;
69}
70
71
72/*
73  a hook called from the main winbindd select() loop to handle writes
74  to the dual daemon pipe
75*/
76void dual_select(fd_set *fds)
77{
78	int n;
79
80	if (dual_daemon_pipe == -1 ||
81	    !dual_list ||
82	    !FD_ISSET(dual_daemon_pipe, fds)) {
83		return;
84	}
85
86	n = sys_write(dual_daemon_pipe,
87		  &dual_list->data[dual_list->offset],
88		  dual_list->length - dual_list->offset);
89
90	if (n <= 0) {
91		/* the pipe is dead! fall back to normal operation */
92		dual_daemon_pipe = -1;
93		return;
94	}
95
96	dual_list->offset += n;
97
98	if (dual_list->offset == dual_list->length) {
99		struct dual_list *next;
100		next = dual_list->next;
101		free(dual_list->data);
102		free(dual_list);
103		dual_list = next;
104		if (!dual_list) {
105			dual_list_end = NULL;
106		}
107	}
108}
109
110/*
111   send a request to the background daemon
112   this is called for stale cached entries
113*/
114void dual_send_request(struct winbindd_cli_state *state)
115{
116	struct dual_list *list;
117
118	if (!background_process) return;
119
120	list = SMB_MALLOC_P(struct dual_list);
121	if (!list) return;
122
123	list->next = NULL;
124	list->data = memdup(&state->request, sizeof(state->request));
125	list->length = sizeof(state->request);
126	list->offset = 0;
127
128	if (!dual_list_end) {
129		dual_list = list;
130		dual_list_end = list;
131	} else {
132		dual_list_end->next = list;
133		dual_list_end = list;
134	}
135
136	background_process = False;
137}
138
139
140/*
141the main dual daemon
142*/
143void do_dual_daemon(void)
144{
145	int fdpair[2];
146	struct winbindd_cli_state state;
147
148	if (pipe(fdpair) != 0) {
149		return;
150	}
151
152	ZERO_STRUCT(state);
153	state.pid = getpid();
154
155	dual_daemon_pipe = fdpair[1];
156	state.sock = fdpair[0];
157
158	if (sys_fork() != 0) {
159		close(fdpair[0]);
160		return;
161	}
162	close(fdpair[1]);
163
164	/* tdb needs special fork handling */
165	if (tdb_reopen_all() == -1) {
166		DEBUG(0,("tdb_reopen_all failed.\n"));
167		_exit(0);
168	}
169
170	dual_daemon_pipe = -1;
171	opt_dual_daemon = False;
172
173	while (1) {
174		/* free up any talloc memory */
175		lp_talloc_free();
176		main_loop_talloc_free();
177
178		/* fetch a request from the main daemon */
179		winbind_client_read(&state);
180
181		if (state.finished) {
182			/* we lost contact with our parent */
183			exit(0);
184		}
185
186		/* process full rquests */
187		if (state.read_buf_len == sizeof(state.request)) {
188			DEBUG(4,("dual daemon request %d\n", (int)state.request.cmd));
189
190			/* special handling for the stateful requests */
191			switch (state.request.cmd) {
192			case WINBINDD_GETPWENT:
193				winbindd_setpwent(&state);
194				break;
195
196			case WINBINDD_GETGRENT:
197			case WINBINDD_GETGRLST:
198				winbindd_setgrent(&state);
199				break;
200			default:
201				break;
202			}
203
204			winbind_process_packet(&state);
205			SAFE_FREE(state.response.extra_data);
206
207			free_getent_state(state.getpwent_state);
208			free_getent_state(state.getgrent_state);
209			state.getpwent_state = NULL;
210			state.getgrent_state = NULL;
211		}
212	}
213}
214
215