1
0
mirror of https://github.com/samba-team/samba.git synced 2025-02-24 13:57:43 +03:00

s3-prefork: add asynchronous functions

To get a client connection it is now possible to use asynchronous functions.

Signed-off-by: Andreas Schneider <asn@samba.org>
This commit is contained in:
Simo Sorce 2011-05-03 18:55:25 -04:00 committed by Andreas Schneider
parent 4fef4fe498
commit b9354f7229
2 changed files with 267 additions and 1 deletions

View File

@ -24,6 +24,7 @@
#include "system/filesys.h" #include "system/filesys.h"
#include "server_prefork.h" #include "server_prefork.h"
#include "../lib/util/util.h" #include "../lib/util/util.h"
#include "../lib/util/tevent_unix.h"
struct prefork_pool { struct prefork_pool {
@ -94,7 +95,6 @@ bool prefork_create_pool(struct tevent_context *ev_ctx,
case 0: /* THE CHILD */ case 0: /* THE CHILD */
pfp->pool[i].status = PF_WORKER_IDLE; pfp->pool[i].status = PF_WORKER_IDLE;
ret = pfp->main_fn(ev_ctx, &pfp->pool[i], ret = pfp->main_fn(ev_ctx, &pfp->pool[i],
pfp->listen_fd, pfp->lock_fd, pfp->listen_fd, pfp->lock_fd,
pfp->private_data); pfp->private_data);
@ -500,3 +500,261 @@ int prefork_wait_for_client(struct pf_worker_data *pf,
*fd = sd; *fd = sd;
return 0; return 0;
} }
/* ==== async code ==== */
#define PF_ASYNC_LOCK_GRAB 0x01
#define PF_ASYNC_LOCK_RELEASE 0x02
#define PF_ASYNC_ACTION_MASK 0x03
#define PF_ASYNC_LOCK_DONE 0x04
struct pf_lock_state {
struct pf_worker_data *pf;
int lock_fd;
int flags;
};
static void prefork_lock_handler(struct tevent_context *ev,
struct tevent_timer *te,
struct timeval curtime, void *pvt);
static struct tevent_req *prefork_lock_send(TALLOC_CTX *mem_ctx,
struct tevent_context *ev,
struct pf_worker_data *pf,
int lock_fd, int action)
{
struct tevent_req *req;
struct pf_lock_state *state;
req = tevent_req_create(mem_ctx, &state, struct pf_lock_state);
if (!req) {
return NULL;
}
state->pf = pf;
state->lock_fd = lock_fd;
state->flags = action;
/* try once immediately */
prefork_lock_handler(ev, NULL, tevent_timeval_zero(), req);
if (state->flags & PF_ASYNC_LOCK_DONE) {
tevent_req_post(req, ev);
}
return req;
}
static void prefork_lock_handler(struct tevent_context *ev,
struct tevent_timer *te,
struct timeval curtime, void *pvt)
{
struct tevent_req *req;
struct pf_lock_state *state;
int ret;
req = talloc_get_type_abort(pvt, struct tevent_req);
state = tevent_req_data(req, struct pf_lock_state);
switch (state->flags & PF_ASYNC_ACTION_MASK) {
case PF_ASYNC_LOCK_GRAB:
ret = prefork_grab_lock(state->pf, state->lock_fd, 0);
break;
case PF_ASYNC_LOCK_RELEASE:
ret = prefork_release_lock(state->pf, state->lock_fd, 0);
break;
default:
ret = EINVAL;
break;
}
switch (ret) {
case 0:
state->flags |= PF_ASYNC_LOCK_DONE;
tevent_req_done(req);
return;
case -1:
te = tevent_add_timer(ev, state,
tevent_timeval_current_ofs(1, 0),
prefork_lock_handler, req);
tevent_req_nomem(te, req);
return;
case -2:
/* server tells us to stop */
state->flags |= PF_ASYNC_LOCK_DONE;
tevent_req_error(req, -2);
return;
default:
state->flags |= PF_ASYNC_LOCK_DONE;
tevent_req_error(req, ret);
return;
}
}
static int prefork_lock_recv(struct tevent_req *req)
{
int ret;
if (!tevent_req_is_unix_error(req, &ret)) {
ret = 0;
}
tevent_req_received(req);
return ret;
}
struct pf_listen_state {
struct tevent_context *ev;
struct pf_worker_data *pf;
int lock_fd;
int listen_fd;
struct sockaddr *addr;
socklen_t *addrlen;
int accept_fd;
int error;
};
static void prefork_listen_lock_done(struct tevent_req *subreq);
static void prefork_listen_accept_handler(struct tevent_context *ev,
struct tevent_fd *fde,
uint16_t flags, void *pvt);
static void prefork_listen_release_done(struct tevent_req *subreq);
struct tevent_req *prefork_listen_send(TALLOC_CTX *mem_ctx,
struct tevent_context *ev,
struct pf_worker_data *pf,
int lock_fd, int listen_fd,
struct sockaddr *addr,
socklen_t *addrlen)
{
struct tevent_req *req, *subreq;
struct pf_listen_state *state;
req = tevent_req_create(mem_ctx, &state, struct pf_listen_state);
if (!req) {
return NULL;
}
state->ev = ev;
state->pf = pf;
state->lock_fd = lock_fd;
state->listen_fd = listen_fd;
state->addr = addr;
state->addrlen = addrlen;
state->accept_fd = -1;
state->error = 0;
subreq = prefork_lock_send(state, state->ev, state->pf,
state->lock_fd, PF_ASYNC_LOCK_GRAB);
if (tevent_req_nomem(subreq, req)) {
return tevent_req_post(req, ev);
}
tevent_req_set_callback(subreq, prefork_listen_lock_done, req);
return req;
}
static void prefork_listen_lock_done(struct tevent_req *subreq)
{
struct tevent_req *req;
struct pf_listen_state *state;
struct tevent_fd *fde;
int ret;
req = tevent_req_callback_data(subreq, struct tevent_req);
state = tevent_req_data(req, struct pf_listen_state);
ret = prefork_lock_recv(subreq);
if (ret != 0) {
tevent_req_error(req, ret);
return;
}
/* next step, accept */
fde = tevent_add_fd(state->ev, state,
state->listen_fd, TEVENT_FD_READ,
prefork_listen_accept_handler, req);
tevent_req_nomem(fde, req);
}
static void prefork_listen_accept_handler(struct tevent_context *ev,
struct tevent_fd *fde,
uint16_t flags, void *pvt)
{
struct pf_listen_state *state;
struct tevent_req *req, *subreq;
int err = 0;
int sd = -1;
req = talloc_get_type_abort(pvt, struct tevent_req);
state = tevent_req_data(req, struct pf_listen_state);
sd = accept(state->listen_fd, state->addr, state->addrlen);
if (sd == -1) {
if (errno == EINTR) {
/* keep trying */
return;
}
err = errno;
DEBUG(6, ("Accept failed! (%d, %s)\n", err, strerror(err)));
}
/* do not track the listen fd anymore */
talloc_free(fde);
if (err) {
tevent_req_error(req, err);
return;
}
state->accept_fd = sd;
/* release lock now */
subreq = prefork_lock_send(state, state->ev, state->pf,
state->lock_fd, PF_ASYNC_LOCK_RELEASE);
if (tevent_req_nomem(subreq, req)) {
return;
}
tevent_req_set_callback(subreq, prefork_listen_release_done, req);
}
static void prefork_listen_release_done(struct tevent_req *subreq)
{
struct tevent_req *req;
int ret;
req = tevent_req_callback_data(subreq, struct tevent_req);
ret = prefork_lock_recv(subreq);
if (ret != 0) {
tevent_req_error(req, ret);
return;
}
tevent_req_done(req);
}
int prefork_listen_recv(struct tevent_req *req, int *fd)
{
struct pf_listen_state *state;
int ret;
state = tevent_req_data(req, struct pf_listen_state);
if (tevent_req_is_unix_error(req, &ret)) {
if (state->accept_fd != -1) {
close(state->accept_fd);
}
} else {
*fd = state->accept_fd;
ret = 0;
state->pf->status = PF_WORKER_BUSY;
state->pf->num_clients++;
}
tevent_req_received(req);
return ret;
}

View File

@ -74,3 +74,11 @@ int prefork_wait_for_client(struct pf_worker_data *pf,
int lock_fd, int listen_fd, int lock_fd, int listen_fd,
struct sockaddr *addr, struct sockaddr *addr,
socklen_t *addrlen, int *fd); socklen_t *addrlen, int *fd);
struct tevent_req *prefork_listen_send(TALLOC_CTX *mem_ctx,
struct tevent_context *ev,
struct pf_worker_data *pf,
int lock_fd, int listen_fd,
struct sockaddr *addr,
socklen_t *addrlen);
int prefork_listen_recv(struct tevent_req *req, int *fd);