Don't limit the number of retries in wb_trans.
[Samba/gebeck_regimport.git] / source3 / lib / wbclient.c
blob164cfc96911b45f9cfb329f06621edab1b984766
1 /*
2 Unix SMB/CIFS implementation.
3 Infrastructure for async winbind requests
4 Copyright (C) Volker Lendecke 2008
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>.
20 #include "includes.h"
21 #include "wbc_async.h"
23 wbcErr map_wbc_err_from_errno(int error)
25 switch(error) {
26 case EPERM:
27 case EACCES:
28 return WBC_ERR_AUTH_ERROR;
29 case ENOMEM:
30 return WBC_ERR_NO_MEMORY;
31 case EIO:
32 default:
33 return WBC_ERR_UNKNOWN_FAILURE;
37 bool tevent_req_is_wbcerr(struct tevent_req *req, wbcErr *pwbc_err)
39 enum tevent_req_state state;
40 uint64_t error;
41 if (!tevent_req_is_error(req, &state, &error)) {
42 *pwbc_err = WBC_ERR_SUCCESS;
43 return false;
46 switch (state) {
47 case TEVENT_REQ_USER_ERROR:
48 *pwbc_err = error;
49 break;
50 case TEVENT_REQ_TIMED_OUT:
51 *pwbc_err = WBC_ERR_UNKNOWN_FAILURE;
52 break;
53 case TEVENT_REQ_NO_MEMORY:
54 *pwbc_err = WBC_ERR_NO_MEMORY;
55 break;
56 default:
57 *pwbc_err = WBC_ERR_UNKNOWN_FAILURE;
58 break;
60 return true;
63 wbcErr tevent_req_simple_recv_wbcerr(struct tevent_req *req)
65 wbcErr wbc_err;
67 if (tevent_req_is_wbcerr(req, &wbc_err)) {
68 return wbc_err;
71 return WBC_ERR_SUCCESS;
74 struct wb_context {
75 struct tevent_queue *queue;
76 int fd;
77 bool is_priv;
80 static int make_nonstd_fd(int fd)
82 int i;
83 int sys_errno = 0;
84 int fds[3];
85 int num_fds = 0;
87 if (fd == -1) {
88 return -1;
90 while (fd < 3) {
91 fds[num_fds++] = fd;
92 fd = dup(fd);
93 if (fd == -1) {
94 sys_errno = errno;
95 break;
98 for (i=0; i<num_fds; i++) {
99 close(fds[i]);
101 if (fd == -1) {
102 errno = sys_errno;
104 return fd;
107 /****************************************************************************
108 Set a fd into blocking/nonblocking mode. Uses POSIX O_NONBLOCK if available,
109 else
110 if SYSV use O_NDELAY
111 if BSD use FNDELAY
112 Set close on exec also.
113 ****************************************************************************/
115 static int make_safe_fd(int fd)
117 int result, flags;
118 int new_fd = make_nonstd_fd(fd);
120 if (new_fd == -1) {
121 goto fail;
124 /* Socket should be nonblocking. */
126 #ifdef O_NONBLOCK
127 #define FLAG_TO_SET O_NONBLOCK
128 #else
129 #ifdef SYSV
130 #define FLAG_TO_SET O_NDELAY
131 #else /* BSD */
132 #define FLAG_TO_SET FNDELAY
133 #endif
134 #endif
136 if ((flags = fcntl(new_fd, F_GETFL)) == -1) {
137 goto fail;
140 flags |= FLAG_TO_SET;
141 if (fcntl(new_fd, F_SETFL, flags) == -1) {
142 goto fail;
145 #undef FLAG_TO_SET
147 /* Socket should be closed on exec() */
148 #ifdef FD_CLOEXEC
149 result = flags = fcntl(new_fd, F_GETFD, 0);
150 if (flags >= 0) {
151 flags |= FD_CLOEXEC;
152 result = fcntl( new_fd, F_SETFD, flags );
154 if (result < 0) {
155 goto fail;
157 #endif
158 return new_fd;
160 fail:
161 if (new_fd != -1) {
162 int sys_errno = errno;
163 close(new_fd);
164 errno = sys_errno;
166 return -1;
169 struct wb_context *wb_context_init(TALLOC_CTX *mem_ctx)
171 struct wb_context *result;
173 result = talloc(mem_ctx, struct wb_context);
174 if (result == NULL) {
175 return NULL;
177 result->queue = tevent_queue_create(result, "wb_trans");
178 if (result->queue == NULL) {
179 TALLOC_FREE(result);
180 return NULL;
182 result->fd = -1;
183 result->is_priv = false;
184 return result;
187 struct wb_connect_state {
188 int dummy;
191 static void wbc_connect_connected(struct tevent_req *subreq);
193 static struct tevent_req *wb_connect_send(TALLOC_CTX *mem_ctx,
194 struct tevent_context *ev,
195 struct wb_context *wb_ctx,
196 const char *dir)
198 struct tevent_req *result, *subreq;
199 struct wb_connect_state *state;
200 struct sockaddr_un sunaddr;
201 struct stat st;
202 char *path = NULL;
203 wbcErr wbc_err;
205 result = tevent_req_create(mem_ctx, &state, struct wb_connect_state);
206 if (result == NULL) {
207 return NULL;
210 if (wb_ctx->fd != -1) {
211 close(wb_ctx->fd);
212 wb_ctx->fd = -1;
215 /* Check permissions on unix socket directory */
217 if (lstat(dir, &st) == -1) {
218 wbc_err = WBC_ERR_WINBIND_NOT_AVAILABLE;
219 goto post_status;
222 if (!S_ISDIR(st.st_mode) ||
223 (st.st_uid != 0 && st.st_uid != geteuid())) {
224 wbc_err = WBC_ERR_WINBIND_NOT_AVAILABLE;
225 goto post_status;
228 /* Connect to socket */
230 path = talloc_asprintf(talloc_tos(), "%s/%s", dir,
231 WINBINDD_SOCKET_NAME);
232 if (path == NULL) {
233 goto nomem;
236 sunaddr.sun_family = AF_UNIX;
237 strlcpy(sunaddr.sun_path, path, sizeof(sunaddr.sun_path));
238 TALLOC_FREE(path);
240 /* If socket file doesn't exist, don't bother trying to connect
241 with retry. This is an attempt to make the system usable when
242 the winbindd daemon is not running. */
244 if ((lstat(sunaddr.sun_path, &st) == -1)
245 || !S_ISSOCK(st.st_mode)
246 || (st.st_uid != 0 && st.st_uid != geteuid())) {
247 wbc_err = WBC_ERR_WINBIND_NOT_AVAILABLE;
248 goto post_status;
251 wb_ctx->fd = make_safe_fd(socket(AF_UNIX, SOCK_STREAM, 0));
252 if (wb_ctx->fd == -1) {
253 wbc_err = map_wbc_err_from_errno(errno);
254 goto post_status;
257 subreq = async_connect_send(mem_ctx, ev, wb_ctx->fd,
258 (struct sockaddr *)(void *)&sunaddr,
259 sizeof(sunaddr));
260 if (subreq == NULL) {
261 goto nomem;
263 tevent_req_set_callback(subreq, wbc_connect_connected, result);
264 return result;
266 post_status:
267 tevent_req_error(result, wbc_err);
268 return tevent_req_post(result, ev);
269 nomem:
270 TALLOC_FREE(result);
271 return NULL;
274 static void wbc_connect_connected(struct tevent_req *subreq)
276 struct tevent_req *req = tevent_req_callback_data(
277 subreq, struct tevent_req);
278 int res, err;
280 res = async_connect_recv(subreq, &err);
281 TALLOC_FREE(subreq);
282 if (res == -1) {
283 tevent_req_error(req, map_wbc_err_from_errno(err));
284 return;
286 tevent_req_done(req);
289 static wbcErr wb_connect_recv(struct tevent_req *req)
291 return tevent_req_simple_recv_wbcerr(req);
294 static const char *winbindd_socket_dir(void)
296 #ifdef SOCKET_WRAPPER
297 const char *env_dir;
299 env_dir = getenv(WINBINDD_SOCKET_DIR_ENVVAR);
300 if (env_dir) {
301 return env_dir;
303 #endif
305 return WINBINDD_SOCKET_DIR;
308 struct wb_open_pipe_state {
309 struct wb_context *wb_ctx;
310 struct tevent_context *ev;
311 bool need_priv;
312 struct winbindd_request wb_req;
315 static void wb_open_pipe_connect_nonpriv_done(struct tevent_req *subreq);
316 static void wb_open_pipe_ping_done(struct tevent_req *subreq);
317 static void wb_open_pipe_getpriv_done(struct tevent_req *subreq);
318 static void wb_open_pipe_connect_priv_done(struct tevent_req *subreq);
320 static struct tevent_req *wb_open_pipe_send(TALLOC_CTX *mem_ctx,
321 struct tevent_context *ev,
322 struct wb_context *wb_ctx,
323 bool need_priv)
325 struct tevent_req *result, *subreq;
326 struct wb_open_pipe_state *state;
328 result = tevent_req_create(mem_ctx, &state, struct wb_open_pipe_state);
329 if (result == NULL) {
330 return NULL;
332 state->wb_ctx = wb_ctx;
333 state->ev = ev;
334 state->need_priv = need_priv;
336 if (wb_ctx->fd != -1) {
337 close(wb_ctx->fd);
338 wb_ctx->fd = -1;
341 subreq = wb_connect_send(state, ev, wb_ctx, winbindd_socket_dir());
342 if (subreq == NULL) {
343 goto fail;
345 tevent_req_set_callback(subreq, wb_open_pipe_connect_nonpriv_done,
346 result);
347 return result;
349 fail:
350 TALLOC_FREE(result);
351 return NULL;
354 static void wb_open_pipe_connect_nonpriv_done(struct tevent_req *subreq)
356 struct tevent_req *req = tevent_req_callback_data(
357 subreq, struct tevent_req);
358 struct wb_open_pipe_state *state = tevent_req_data(
359 req, struct wb_open_pipe_state);
360 wbcErr wbc_err;
362 wbc_err = wb_connect_recv(subreq);
363 TALLOC_FREE(subreq);
364 if (!WBC_ERROR_IS_OK(wbc_err)) {
365 state->wb_ctx->is_priv = true;
366 tevent_req_error(req, wbc_err);
367 return;
370 ZERO_STRUCT(state->wb_req);
371 state->wb_req.cmd = WINBINDD_INTERFACE_VERSION;
372 state->wb_req.pid = getpid();
374 subreq = wb_simple_trans_send(state, state->ev, NULL,
375 state->wb_ctx->fd, &state->wb_req);
376 if (tevent_req_nomem(subreq, req)) {
377 return;
379 tevent_req_set_callback(subreq, wb_open_pipe_ping_done, req);
382 static void wb_open_pipe_ping_done(struct tevent_req *subreq)
384 struct tevent_req *req = tevent_req_callback_data(
385 subreq, struct tevent_req);
386 struct wb_open_pipe_state *state = tevent_req_data(
387 req, struct wb_open_pipe_state);
388 struct winbindd_response *wb_resp;
389 int ret, err;
391 ret = wb_simple_trans_recv(subreq, state, &wb_resp, &err);
392 TALLOC_FREE(subreq);
393 if (ret == -1) {
394 tevent_req_error(req, map_wbc_err_from_errno(err));
395 return;
398 if (!state->need_priv) {
399 tevent_req_done(req);
400 return;
403 state->wb_req.cmd = WINBINDD_PRIV_PIPE_DIR;
404 state->wb_req.pid = getpid();
406 subreq = wb_simple_trans_send(state, state->ev, NULL,
407 state->wb_ctx->fd, &state->wb_req);
408 if (tevent_req_nomem(subreq, req)) {
409 return;
411 tevent_req_set_callback(subreq, wb_open_pipe_getpriv_done, req);
414 static void wb_open_pipe_getpriv_done(struct tevent_req *subreq)
416 struct tevent_req *req = tevent_req_callback_data(
417 subreq, struct tevent_req);
418 struct wb_open_pipe_state *state = tevent_req_data(
419 req, struct wb_open_pipe_state);
420 struct winbindd_response *wb_resp = NULL;
421 int ret, err;
423 ret = wb_simple_trans_recv(subreq, state, &wb_resp, &err);
424 TALLOC_FREE(subreq);
425 if (ret == -1) {
426 tevent_req_error(req, map_wbc_err_from_errno(err));
427 return;
430 close(state->wb_ctx->fd);
431 state->wb_ctx->fd = -1;
433 subreq = wb_connect_send(state, state->ev, state->wb_ctx,
434 (char *)wb_resp->extra_data.data);
435 TALLOC_FREE(wb_resp);
436 if (tevent_req_nomem(subreq, req)) {
437 return;
439 tevent_req_set_callback(subreq, wb_open_pipe_connect_priv_done, req);
442 static void wb_open_pipe_connect_priv_done(struct tevent_req *subreq)
444 struct tevent_req *req = tevent_req_callback_data(
445 subreq, struct tevent_req);
446 struct wb_open_pipe_state *state = tevent_req_data(
447 req, struct wb_open_pipe_state);
448 wbcErr wbc_err;
450 wbc_err = wb_connect_recv(subreq);
451 TALLOC_FREE(subreq);
452 if (!WBC_ERROR_IS_OK(wbc_err)) {
453 tevent_req_error(req, wbc_err);
454 return;
456 state->wb_ctx->is_priv = true;
457 tevent_req_done(req);
460 static wbcErr wb_open_pipe_recv(struct tevent_req *req)
462 return tevent_req_simple_recv_wbcerr(req);
465 struct wb_trans_state {
466 struct wb_trans_state *prev, *next;
467 struct wb_context *wb_ctx;
468 struct tevent_context *ev;
469 struct winbindd_request *wb_req;
470 struct winbindd_response *wb_resp;
471 bool need_priv;
474 static bool closed_fd(int fd)
476 struct timeval tv;
477 fd_set r_fds;
478 int selret;
480 if (fd == -1) {
481 return true;
484 FD_ZERO(&r_fds);
485 FD_SET(fd, &r_fds);
486 ZERO_STRUCT(tv);
488 selret = select(fd+1, &r_fds, NULL, NULL, &tv);
489 if (selret == -1) {
490 return true;
492 if (selret == 0) {
493 return false;
495 return (FD_ISSET(fd, &r_fds));
498 static void wb_trans_trigger(struct tevent_req *req, void *private_data);
499 static void wb_trans_connect_done(struct tevent_req *subreq);
500 static void wb_trans_done(struct tevent_req *subreq);
501 static void wb_trans_retry_wait_done(struct tevent_req *subreq);
503 struct tevent_req *wb_trans_send(TALLOC_CTX *mem_ctx,
504 struct tevent_context *ev,
505 struct wb_context *wb_ctx, bool need_priv,
506 struct winbindd_request *wb_req)
508 struct tevent_req *req;
509 struct wb_trans_state *state;
511 req = tevent_req_create(mem_ctx, &state, struct wb_trans_state);
512 if (req == NULL) {
513 return NULL;
515 state->wb_ctx = wb_ctx;
516 state->ev = ev;
517 state->wb_req = wb_req;
518 state->need_priv = need_priv;
520 if (!tevent_queue_add(wb_ctx->queue, ev, req, wb_trans_trigger,
521 NULL)) {
522 tevent_req_nomem(NULL, req);
523 return tevent_req_post(req, ev);
525 return req;
528 static void wb_trans_trigger(struct tevent_req *req, void *private_data)
530 struct wb_trans_state *state = tevent_req_data(
531 req, struct wb_trans_state);
532 struct tevent_req *subreq;
534 if ((state->wb_ctx->fd != -1) && closed_fd(state->wb_ctx->fd)) {
535 close(state->wb_ctx->fd);
536 state->wb_ctx->fd = -1;
539 if ((state->wb_ctx->fd == -1)
540 || (state->need_priv && !state->wb_ctx->is_priv)) {
541 subreq = wb_open_pipe_send(state, state->ev, state->wb_ctx,
542 state->need_priv);
543 if (tevent_req_nomem(subreq, req)) {
544 return;
546 tevent_req_set_callback(subreq, wb_trans_connect_done, req);
547 return;
550 state->wb_req->pid = getpid();
552 subreq = wb_simple_trans_send(state, state->ev, NULL,
553 state->wb_ctx->fd, state->wb_req);
554 if (tevent_req_nomem(subreq, req)) {
555 return;
557 tevent_req_set_callback(subreq, wb_trans_done, req);
560 static bool wb_trans_retry(struct tevent_req *req,
561 struct wb_trans_state *state,
562 wbcErr wbc_err)
564 struct tevent_req *subreq;
566 if (WBC_ERROR_IS_OK(wbc_err)) {
567 return false;
570 if (wbc_err == WBC_ERR_WINBIND_NOT_AVAILABLE) {
572 * Winbind not around or we can't connect to the pipe. Fail
573 * immediately.
575 tevent_req_error(req, wbc_err);
576 return true;
580 * The transfer as such failed, retry after one second
583 if (state->wb_ctx->fd != -1) {
584 close(state->wb_ctx->fd);
585 state->wb_ctx->fd = -1;
588 subreq = tevent_wakeup_send(state, state->ev,
589 timeval_current_ofs(1, 0));
590 if (tevent_req_nomem(subreq, req)) {
591 return true;
593 tevent_req_set_callback(subreq, wb_trans_retry_wait_done, req);
594 return true;
597 static void wb_trans_retry_wait_done(struct tevent_req *subreq)
599 struct tevent_req *req = tevent_req_callback_data(
600 subreq, struct tevent_req);
601 struct wb_trans_state *state = tevent_req_data(
602 req, struct wb_trans_state);
603 bool ret;
605 ret = tevent_wakeup_recv(subreq);
606 TALLOC_FREE(subreq);
607 if (!ret) {
608 tevent_req_error(req, WBC_ERR_UNKNOWN_FAILURE);
609 return;
612 subreq = wb_open_pipe_send(state, state->ev, state->wb_ctx,
613 state->need_priv);
614 if (tevent_req_nomem(subreq, req)) {
615 return;
617 tevent_req_set_callback(subreq, wb_trans_connect_done, req);
620 static void wb_trans_connect_done(struct tevent_req *subreq)
622 struct tevent_req *req = tevent_req_callback_data(
623 subreq, struct tevent_req);
624 struct wb_trans_state *state = tevent_req_data(
625 req, struct wb_trans_state);
626 wbcErr wbc_err;
628 wbc_err = wb_open_pipe_recv(subreq);
629 TALLOC_FREE(subreq);
631 if (wb_trans_retry(req, state, wbc_err)) {
632 return;
635 subreq = wb_simple_trans_send(state, state->ev, NULL,
636 state->wb_ctx->fd, state->wb_req);
637 if (tevent_req_nomem(subreq, req)) {
638 return;
640 tevent_req_set_callback(subreq, wb_trans_done, req);
643 static void wb_trans_done(struct tevent_req *subreq)
645 struct tevent_req *req = tevent_req_callback_data(
646 subreq, struct tevent_req);
647 struct wb_trans_state *state = tevent_req_data(
648 req, struct wb_trans_state);
649 int ret, err;
651 ret = wb_simple_trans_recv(subreq, state, &state->wb_resp, &err);
652 TALLOC_FREE(subreq);
653 if ((ret == -1)
654 && wb_trans_retry(req, state, map_wbc_err_from_errno(err))) {
655 return;
658 tevent_req_done(req);
661 wbcErr wb_trans_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
662 struct winbindd_response **presponse)
664 struct wb_trans_state *state = tevent_req_data(
665 req, struct wb_trans_state);
666 wbcErr wbc_err;
668 if (tevent_req_is_wbcerr(req, &wbc_err)) {
669 return wbc_err;
672 *presponse = talloc_move(mem_ctx, &state->wb_resp);
673 return WBC_ERR_SUCCESS;