s3compat-only s3:winbindd Move more parts of winbindd.c into winbindd_event.c
[abartlet/samba.git/.git] / source3 / winbindd / winbindd_event.c
1 /*
2    Unix SMB/CIFS implementation.
3
4    Winbind daemon for ntdom nss module
5
6    Copyright (C) by Tim Potter 2000-2002
7    Copyright (C) Andrew Tridgell 2002
8    Copyright (C) Jelmer Vernooij 2003
9    Copyright (C) Volker Lendecke 2004
10    Copyright (C) Andrew Bartlett 2010
11
12    This program is free software; you can redistribute it and/or modify
13    it under the terms of the GNU General Public License as published by
14    the Free Software Foundation; either version 3 of the License, or
15    (at your option) any later version.
16
17    This program is distributed in the hope that it will be useful,
18    but WITHOUT ANY WARRANTY; without even the implied warranty of
19    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20    GNU General Public License for more details.
21
22    You should have received a copy of the GNU General Public License
23    along with this program.  If not, see <http://www.gnu.org/licenses/>.
24 */
25
26 #include "includes.h"
27 #include "winbindd.h"
28 #include "../../nsswitch/libwbclient/wbc_async.h"
29
30 /*
31  * This is the main event loop of winbind requests. It goes through a
32  * state-machine of 3 read/write requests, 4 if you have extra data to send.
33  *
34  * An idle winbind client has a read request of 4 bytes outstanding,
35  * finalizing function is request_len_recv, checking the length. request_recv
36  * then processes the packet. The processing function then at some point has
37  * to call request_finished which schedules sending the response.
38  */
39
40 static void winbind_client_request_read(struct tevent_req *req);
41 static void winbind_client_response_written(struct tevent_req *req);
42
43 void request_finished(struct winbindd_cli_state *state)
44 {
45         struct tevent_req *req;
46
47         TALLOC_FREE(state->request);
48
49         req = wb_resp_write_send(state, winbind_event_context(),
50                                  state->out_queue, state->sock,
51                                  state->response);
52         if (req == NULL) {
53                 DEBUG(10,("request_finished[%d:%s]: wb_resp_write_send() failed\n",
54                           (int)state->pid, state->cmd_name));
55                 winbindd_remove_client(state);
56                 return;
57         }
58         tevent_req_set_callback(req, winbind_client_response_written, state);
59 }
60
61 static void winbind_client_response_written(struct tevent_req *req)
62 {
63         struct winbindd_cli_state *state = tevent_req_callback_data(
64                 req, struct winbindd_cli_state);
65         ssize_t ret;
66         int err;
67
68         ret = wb_resp_write_recv(req, &err);
69         TALLOC_FREE(req);
70         if (ret == -1) {
71                 close(state->sock);
72                 state->sock = -1;
73                 DEBUG(2, ("Could not write response[%d:%s] to client: %s\n",
74                           (int)state->pid, state->cmd_name, strerror(err)));
75                 winbindd_remove_client(state);
76                 return;
77         }
78
79         DEBUG(10,("winbind_client_response_written[%d:%s]: delivered response "
80                   "to client\n", (int)state->pid, state->cmd_name));
81
82         TALLOC_FREE(state->mem_ctx);
83         state->response = NULL;
84         state->cmd_name = "no request";
85         state->recv_fn = NULL;
86
87         req = wb_req_read_send(state, winbind_event_context(), state->sock,
88                                WINBINDD_MAX_EXTRA_DATA);
89         if (req == NULL) {
90                 winbindd_remove_client(state);
91                 return;
92         }
93         tevent_req_set_callback(req, winbind_client_request_read, state);
94 }
95
96 /* Process a new connection by adding it to the client connection list */
97
98 static void new_connection(int listen_sock, bool privileged)
99 {
100         struct sockaddr_un sunaddr;
101         struct winbindd_cli_state *state;
102         struct tevent_req *req;
103         socklen_t len;
104         int sock;
105
106         /* Accept connection */
107
108         len = sizeof(sunaddr);
109
110         do {
111                 sock = accept(listen_sock, (struct sockaddr *)(void *)&sunaddr,
112                               &len);
113         } while (sock == -1 && errno == EINTR);
114
115         if (sock == -1)
116                 return;
117
118         DEBUG(6,("accepted socket %d\n", sock));
119
120         /* Create new connection structure */
121
122         if ((state = TALLOC_ZERO_P(NULL, struct winbindd_cli_state)) == NULL) {
123                 close(sock);
124                 return;
125         }
126
127         state->sock = sock;
128
129         state->out_queue = tevent_queue_create(state, "winbind client reply");
130         if (state->out_queue == NULL) {
131                 close(sock);
132                 TALLOC_FREE(state);
133                 return;
134         }
135
136         state->last_access = time(NULL);        
137
138         state->privileged = privileged;
139
140         req = wb_req_read_send(state, winbind_event_context(), state->sock,
141                                WINBINDD_MAX_EXTRA_DATA);
142         if (req == NULL) {
143                 TALLOC_FREE(state);
144                 close(sock);
145                 return;
146         }
147         tevent_req_set_callback(req, winbind_client_request_read, state);
148
149         /* Add to connection list */
150
151         winbindd_add_client(state);
152 }
153
154 static void winbind_client_request_read(struct tevent_req *req)
155 {
156         struct winbindd_cli_state *state = tevent_req_callback_data(
157                 req, struct winbindd_cli_state);
158         ssize_t ret;
159         int err;
160
161         ret = wb_req_read_recv(req, state, &state->request, &err);
162         TALLOC_FREE(req);
163         if (ret == -1) {
164                 if (err == EPIPE) {
165                         DEBUG(6, ("closing socket %d, client exited\n",
166                                   state->sock));
167                 } else {
168                         DEBUG(2, ("Could not read client request from fd %d: "
169                                   "%s\n", state->sock, strerror(err)));
170                 }
171                 close(state->sock);
172                 state->sock = -1;
173                 winbindd_remove_client(state);
174                 return;
175         }
176         wb_process_request(state);
177 }
178
179 /* Remove a client connection from client connection list */
180
181 void winbindd_remove_client(struct winbindd_cli_state *state)
182 {
183         char c = 0;
184         int nwritten;
185
186         /* It's a dead client - hold a funeral */
187
188         if (state == NULL) {
189                 return;
190         }
191
192         if (state->sock != -1) {
193                 /* tell client, we are closing ... */
194                 nwritten = write(state->sock, &c, sizeof(c));
195                 if (nwritten == -1) {
196                         DEBUG(2, ("final write to client failed: %s\n",
197                                 strerror(errno)));
198                 }
199
200                 /* Close socket */
201
202                 close(state->sock);
203                 state->sock = -1;
204         }
205
206         TALLOC_FREE(state->mem_ctx);
207
208         /* Remove from list and free */
209
210         winbindd_remove_client_from_list(state);
211         TALLOC_FREE(state);
212 }
213
214 /* Is a client idle? */
215
216 bool client_is_idle(struct winbindd_cli_state *state) {
217   return (state->response == NULL &&
218           !state->pwent_state && !state->grent_state);
219 }
220
221 /* Shutdown client connection which has been idle for the longest time */
222
223 static bool remove_idle_client(void)
224 {
225         struct winbindd_cli_state *state, *remove_state = NULL;
226         time_t last_access = 0;
227         int nidle = 0;
228
229         for (state = winbindd_client_list(); state; state = state->next) {
230                 if (client_is_idle(state)) {
231                         nidle++;
232                         if (!last_access || state->last_access < last_access) {
233                                 last_access = state->last_access;
234                                 remove_state = state;
235                         }
236                 }
237         }
238
239         if (remove_state) {
240                 DEBUG(5,("Found %d idle client connections, shutting down sock %d, pid %u\n",
241                         nidle, remove_state->sock, (unsigned int)remove_state->pid));
242                 winbindd_remove_client(remove_state);
243                 return True;
244         }
245
246         return False;
247 }
248
249 struct winbindd_listen_state {
250         bool privileged;
251         int fd;
252 };
253
254 static void winbindd_listen_fde_handler(struct tevent_context *ev,
255                                         struct tevent_fd *fde,
256                                         uint16_t flags,
257                                         void *private_data)
258 {
259         struct winbindd_listen_state *s = talloc_get_type_abort(private_data,
260                                           struct winbindd_listen_state);
261
262         while (winbindd_num_clients() > lp_winbind_max_clients() - 1) {
263                 DEBUG(5,("winbindd: Exceeding %d client "
264                          "connections, removing idle "
265                          "connection.\n", lp_winbind_max_clients()));
266                 if (!remove_idle_client()) {
267                         DEBUG(0,("winbindd: Exceeding %d "
268                                  "client connections, no idle "
269                                  "connection found\n",
270                                  lp_winbind_max_clients()));
271                         break;
272                 }
273         }
274         new_connection(s->fd, s->privileged);
275 }
276
277 /*
278  * Winbindd socket accessor functions
279  */
280
281 const char *get_winbind_pipe_dir(void)
282 {
283         return lp_parm_const_string(-1, "winbindd", "socket dir", WINBINDD_SOCKET_DIR);
284 }
285
286 char *get_winbind_priv_pipe_dir(void)
287 {
288         return lock_path(WINBINDD_PRIV_SOCKET_SUBDIR);
289 }
290
291 bool winbindd_setup_listeners(void)
292 {
293         struct winbindd_listen_state *pub_state = NULL;
294         struct winbindd_listen_state *priv_state = NULL;
295         struct tevent_fd *fde;
296
297         pub_state = talloc(winbind_event_context(),
298                            struct winbindd_listen_state);
299         if (!pub_state) {
300                 goto failed;
301         }
302
303         pub_state->privileged = false;
304         pub_state->fd = create_pipe_sock(
305                 get_winbind_pipe_dir(), WINBINDD_SOCKET_NAME, 0755);
306         if (pub_state->fd == -1) {
307                 goto failed;
308         }
309
310         fde = tevent_add_fd(winbind_event_context(), pub_state, pub_state->fd,
311                             TEVENT_FD_READ, winbindd_listen_fde_handler,
312                             pub_state);
313         if (fde == NULL) {
314                 close(pub_state->fd);
315                 goto failed;
316         }
317         tevent_fd_set_auto_close(fde);
318
319         priv_state = talloc(winbind_event_context(),
320                             struct winbindd_listen_state);
321         if (!priv_state) {
322                 goto failed;
323         }
324
325         priv_state->privileged = true;
326         priv_state->fd = create_pipe_sock(
327                 get_winbind_priv_pipe_dir(), WINBINDD_SOCKET_NAME, 0750);
328         if (priv_state->fd == -1) {
329                 goto failed;
330         }
331
332         fde = tevent_add_fd(winbind_event_context(), priv_state,
333                             priv_state->fd, TEVENT_FD_READ,
334                             winbindd_listen_fde_handler, priv_state);
335         if (fde == NULL) {
336                 close(priv_state->fd);
337                 goto failed;
338         }
339         tevent_fd_set_auto_close(fde);
340
341         return true;
342 failed:
343         TALLOC_FREE(pub_state);
344         TALLOC_FREE(priv_state);
345         return false;
346 }
347