struct prefork_pool {
- int listen_fd;
- int lock_fd;
+ int listen_fd_size;
+ int *listen_fds;
prefork_main_fn_t *main_fn;
void *private_data;
int pool_size;
struct pf_worker_data *pool;
+
+ int allowed_clients;
+
+ prefork_sigchld_fn_t *sigchld_fn;
+ void *sigchld_data;
};
-int prefork_pool_destructor(struct prefork_pool *pfp)
+static bool prefork_setup_sigchld_handler(struct tevent_context *ev_ctx,
+ struct prefork_pool *pfp);
+
+static int prefork_pool_destructor(struct prefork_pool *pfp)
{
- munmap(pfp->pool, pfp->pool_size * sizeof(struct pf_worker_data));
+ anonymous_shared_free(pfp->pool);
return 0;
}
-bool prefork_create_pool(struct tevent_context *ev_ctx,
- TALLOC_CTX *mem_ctx, int listen_fd,
+bool prefork_create_pool(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev_ctx,
+ struct messaging_context *msg_ctx,
+ int listen_fd_size, int *listen_fds,
int min_children, int max_children,
prefork_main_fn_t *main_fn, void *private_data,
struct prefork_pool **pf_pool)
size_t data_size;
int ret;
int i;
+ bool ok;
- pfp = talloc(mem_ctx, struct prefork_pool);
+ pfp = talloc_zero(mem_ctx, struct prefork_pool);
if (!pfp) {
DEBUG(1, ("Out of memory!\n"));
return false;
}
- pfp->listen_fd = listen_fd;
- pfp->main_fn = main_fn;
- pfp->private_data = private_data;
-
- pfp->lock_fd = create_unlink_tmp(NULL);
- if (pfp->lock_fd == -1) {
- DEBUG(1, ("Failed to create prefork lock fd!\n"));
- talloc_free(pfp);
+ pfp->listen_fd_size = listen_fd_size;
+ pfp->listen_fds = talloc_array(pfp, int, listen_fd_size);
+ if (!pfp->listen_fds) {
+ DEBUG(1, ("Out of memory!\n"));
return false;
}
+ for (i = 0; i < listen_fd_size; i++) {
+ pfp->listen_fds[i] = listen_fds[i];
+ }
+ pfp->main_fn = main_fn;
+ pfp->private_data = private_data;
pfp->pool_size = max_children;
data_size = sizeof(struct pf_worker_data) * max_children;
- pfp->pool = mmap(NULL, data_size, PROT_READ|PROT_WRITE,
- MAP_SHARED|MAP_ANONYMOUS, -1, 0);
- if (pfp->pool == MAP_FAILED) {
+ pfp->pool = anonymous_shared_allocate(data_size);
+ if (pfp->pool == NULL) {
DEBUG(1, ("Failed to mmap memory for prefork pool!\n"));
talloc_free(pfp);
return false;
talloc_set_destructor(pfp, prefork_pool_destructor);
for (i = 0; i < min_children; i++) {
+
+ pfp->pool[i].allowed_clients = 1;
+ pfp->pool[i].started = now;
+
pid = sys_fork();
switch (pid) {
case -1:
case 0: /* THE CHILD */
- pfp->pool[i].status = PF_WORKER_IDLE;
- ret = pfp->main_fn(ev_ctx, &pfp->pool[i],
- pfp->listen_fd, pfp->lock_fd,
+ pfp->pool[i].status = PF_WORKER_ALIVE;
+ ret = pfp->main_fn(ev_ctx, msg_ctx,
+ &pfp->pool[i], i + 1,
+ pfp->listen_fd_size,
+ pfp->listen_fds,
pfp->private_data);
exit(ret);
default: /* THE PARENT */
pfp->pool[i].pid = pid;
- pfp->pool[i].started = now;
break;
}
}
+ ok = prefork_setup_sigchld_handler(ev_ctx, pfp);
+ if (!ok) {
+ DEBUG(1, ("Failed to setup SIGCHLD Handler!\n"));
+ talloc_free(pfp);
+ return false;
+ }
+
*pf_pool = pfp;
return true;
}
+/* Provide the new max children number in new_max
+ * (must be larger than current max).
+ * Returns: 0 if all fine
+ * ENOSPC if mremap fails to expand
+ * EINVAL if new_max is invalid
+ */
+int prefork_expand_pool(struct prefork_pool *pfp, int new_max)
+{
+ struct prefork_pool *pool;
+ size_t old_size;
+ size_t new_size;
+ int ret;
+
+ if (new_max <= pfp->pool_size) {
+ return EINVAL;
+ }
+
+ old_size = sizeof(struct pf_worker_data) * pfp->pool_size;
+ new_size = sizeof(struct pf_worker_data) * new_max;
+
+ pool = anonymous_shared_resize(&pfp->pool, new_size, false);
+ if (pool == NULL) {
+ ret = errno;
+ DEBUG(3, ("Failed to mremap memory (%d: %s)!\n",
+ ret, strerror(ret)));
+ return ret;
+ }
+
+ memset(&pool[pfp->pool_size], 0, new_size - old_size);
+
+ pfp->pool_size = new_max;
+
+ return 0;
+}
+
int prefork_add_children(struct tevent_context *ev_ctx,
+ struct messaging_context *msg_ctx,
struct prefork_pool *pfp,
int num_children)
{
continue;
}
+ pfp->pool[i].allowed_clients = 1;
+ pfp->pool[i].started = now;
+
pid = sys_fork();
switch (pid) {
case -1:
case 0: /* THE CHILD */
- pfp->pool[i].status = PF_WORKER_IDLE;
- ret = pfp->main_fn(ev_ctx, &pfp->pool[i],
- pfp->listen_fd, pfp->lock_fd,
+ pfp->pool[i].status = PF_WORKER_ALIVE;
+ ret = pfp->main_fn(ev_ctx, msg_ctx,
+ &pfp->pool[i], i + 1,
+ pfp->listen_fd_size,
+ pfp->listen_fds,
pfp->private_data);
pfp->pool[i].status = PF_WORKER_EXITING;
default: /* THE PARENT */
pfp->pool[i].pid = pid;
- pfp->pool[i].started = now;
j++;
break;
}
/* sort in inverse order */
static int prefork_sort_oldest(const void *ap, const void *bp)
{
- struct prefork_oldest *a = (struct prefork_oldest *)ap;
- struct prefork_oldest *b = (struct prefork_oldest *)bp;
+ const struct prefork_oldest *a = (const struct prefork_oldest *)ap;
+ const struct prefork_oldest *b = (const struct prefork_oldest *)bp;
if (a->started == b->started) {
return 0;
for (i = 0; i < pfp->pool_size; i++) {
oldest[i].num = i;
- if (pfp->pool[i].status == PF_WORKER_IDLE) {
+ if (pfp->pool[i].status == PF_WORKER_ALIVE ||
+ pfp->pool[i].status == PF_WORKER_ACCEPTING) {
oldest[i].started = pfp->pool[i].started;
} else {
oldest[i].started = now;
prefork_sort_oldest);
for (i = 0, j = 0; i < pfp->pool_size && j < num_children; i++) {
- if (pfp->pool[i].status == PF_WORKER_IDLE &&
+ if ((pfp->pool[i].status == PF_WORKER_ALIVE ||
+ pfp->pool[i].status == PF_WORKER_ACCEPTING) &&
pfp->pool[i].started <= age_limit) {
/* tell the child it's time to give up */
DEBUG(5, ("Retiring pid %d!\n", pfp->pool[i].pid));
t++;
- if (pfp->pool[i].num_clients == 0) {
+ if (pfp->pool[i].num_clients <= 0) {
continue;
}
return a;
}
-/* to be used to finally mark a children as dead, so that it's slot can
- * be reused */
-bool prefork_mark_pid_dead(struct prefork_pool *pfp, pid_t pid)
+static void prefork_cleanup_loop(struct prefork_pool *pfp)
{
+ int status;
+ pid_t pid;
int i;
+ /* TODO: should we use a process group id wait instead of looping ? */
for (i = 0; i < pfp->pool_size; i++) {
- if (pfp->pool[i].pid == pid) {
+ if (pfp->pool[i].status == PF_WORKER_NONE ||
+ pfp->pool[i].pid == 0) {
+ continue;
+ }
+
+ pid = sys_waitpid(pfp->pool[i].pid, &status, WNOHANG);
+ if (pid > 0) {
+
if (pfp->pool[i].status != PF_WORKER_EXITING) {
- DEBUG(2, ("pid %d terminated abnormally!\n",
- (int)pid));
+ DEBUG(3, ("Child (%d) terminated abnormally:"
+ " %d\n", (int)pid, status));
+ } else {
+ DEBUG(10, ("Child (%d) terminated with status:"
+ " %d\n", (int)pid, status));
}
/* reset all fields,
* this makes status = PF_WORK_NONE */
memset(&pfp->pool[i], 0,
sizeof(struct pf_worker_data));
-
- return true;
}
}
- return false;
-}
-
-/* ==== Functions used by children ==== */
-
-static SIG_ATOMIC_T pf_alarm;
-
-static void pf_alarm_cb(int signum)
-{
- pf_alarm = 1;
}
-
-/*
- * Parameters:
- * pf - the worker shared data structure
- * lock_fd - the file descriptor used for locking
- * timeout - expressed in seconds:
- * -1 never timeouts,
- * 0 timeouts immediately
- * N seconds before timing out
- *
- * Returns values:
- * negative errno on fatal error
- * 0 on success to acquire lock
- * -1 on timeout/lock held by other
- * -2 on server msg to terminate
- * ERRNO on other errors
- */
-
-static int prefork_grab_lock(struct pf_worker_data *pf,
- int lock_fd, int timeout)
+int prefork_count_allowed_connections(struct prefork_pool *pfp)
{
- struct flock lock;
- int op;
- int ret;
-
- if (pf->cmds == PF_SRV_MSG_EXIT) {
- return -2;
- }
+ int c;
+ int i;
- pf_alarm = 0;
+ c = 0;
+ for (i = 0; i < pfp->pool_size; i++) {
+ if (pfp->pool[i].status == PF_WORKER_NONE ||
+ pfp->pool[i].status == PF_WORKER_EXITING) {
+ continue;
+ }
- if (timeout > 0) {
- CatchSignal(SIGALRM, pf_alarm_cb);
- alarm(timeout);
- }
+ if (pfp->pool[i].num_clients < 0) {
+ continue;
+ }
- if (timeout == 0) {
- op = F_SETLK;
- } else {
- op = F_SETLKW;
+ c += pfp->pool[i].allowed_clients - pfp->pool[i].num_clients;
}
- ret = 0;
- do {
- ZERO_STRUCT(lock);
- lock.l_type = F_WRLCK;
- lock.l_whence = SEEK_SET;
-
- ret = fcntl(lock_fd, op, &lock);
- if (ret == 0) break;
+ return c;
+}
- ret = errno;
+void prefork_increase_allowed_clients(struct prefork_pool *pfp, int max)
+{
+ int i;
- if (pf->cmds == PF_SRV_MSG_EXIT) {
- ret = -2;
- goto done;
+ for (i = 0; i < pfp->pool_size; i++) {
+ if (pfp->pool[i].status == PF_WORKER_NONE ||
+ pfp->pool[i].status == PF_WORKER_EXITING) {
+ continue;
}
- switch (ret) {
- case EINTR:
- break;
-
- case EACCES:
- case EAGAIN:
- /* lock held by other proc */
- ret = -1;
- goto done;
- default:
- goto done;
+ if (pfp->pool[i].num_clients < 0) {
+ continue;
}
- if (pf_alarm == 1) {
- /* timed out */
- ret = -1;
- goto done;
+ if (pfp->pool[i].allowed_clients < max) {
+ pfp->pool[i].allowed_clients++;
}
- } while (timeout != 0);
-
- if (ret != 0) {
- /* We have the Lock */
- pf->status = PF_WORKER_ACCEPTING;
- }
-
-done:
- if (timeout > 0) {
- alarm(0);
- CatchSignal(SIGALRM, SIG_IGN);
}
-
- if (ret > 0) {
- DEBUG(1, ("Failed to get lock (%d, %s)!\n",
- ret, strerror(ret)));
- }
- return ret;
}
-/*
- * Parameters:
- * pf - the worker shared data structure
- * lock_fd - the file descriptor used for locking
- * timeout - expressed in seconds:
- * -1 never timeouts,
- * 0 timeouts immediately
- * N seconds before timing out
- *
- * Returns values:
- * negative errno on fatal error
- * 0 on success to release lock
- * -1 on timeout
- * ERRNO on error
- */
-
-static int prefork_release_lock(struct pf_worker_data *pf,
- int lock_fd, int timeout)
+void prefork_decrease_allowed_clients(struct prefork_pool *pfp)
{
- struct flock lock;
- int op;
- int ret;
-
- pf_alarm = 0;
-
- if (timeout > 0) {
- CatchSignal(SIGALRM, pf_alarm_cb);
- alarm(timeout);
- }
-
- if (timeout == 0) {
- op = F_SETLK;
- } else {
- op = F_SETLKW;
- }
-
- do {
- ZERO_STRUCT(lock);
- lock.l_type = F_UNLCK;
- lock.l_whence = SEEK_SET;
-
- ret = fcntl(lock_fd, op, &lock);
- if (ret == 0) break;
-
- ret = errno;
+ int i;
- if (ret != EINTR) {
- goto done;
+ for (i = 0; i < pfp->pool_size; i++) {
+ if (pfp->pool[i].status == PF_WORKER_NONE ||
+ pfp->pool[i].status == PF_WORKER_EXITING) {
+ continue;
}
- if (pf_alarm == 1) {
- /* timed out */
- ret = -1;
- goto done;
+ if (pfp->pool[i].num_clients < 0) {
+ continue;
}
- } while (timeout != 0);
-done:
- if (timeout > 0) {
- alarm(0);
- CatchSignal(SIGALRM, SIG_IGN);
- }
-
- if (ret > 0) {
- DEBUG(1, ("Failed to release lock (%d, %s)!\n",
- ret, strerror(ret)));
+ if (pfp->pool[i].allowed_clients > 1) {
+ pfp->pool[i].allowed_clients--;
+ }
}
- return ret;
}
-/* returns:
- * negative errno on error
- * -2 if server commands to terminate
- * 0 if all ok
- * ERRNO on other errors
- */
-
-int prefork_wait_for_client(struct pf_worker_data *pf,
- int lock_fd, int listen_fd,
- struct sockaddr *addr,
- socklen_t *addrlen, int *fd)
+void prefork_reset_allowed_clients(struct prefork_pool *pfp)
{
- int ret;
- int sd = -1;
- int err;
+ int i;
- ret = prefork_grab_lock(pf, lock_fd, -1);
- if (ret != 0) {
- return ret;
+ for (i = 0; i < pfp->pool_size; i++) {
+ pfp->pool[i].allowed_clients = 1;
}
+}
- err = 0;
- do {
- sd = accept(listen_fd, addr, addrlen);
-
- if (sd != -1) break;
+void prefork_send_signal_to_all(struct prefork_pool *pfp, int signal_num)
+{
+ int i;
- if (errno == EINTR) {
- if (pf->cmds == PF_SRV_MSG_EXIT) {
- err = -2;
- }
- } else {
- err = errno;
+ for (i = 0; i < pfp->pool_size; i++) {
+ if (pfp->pool[i].status == PF_WORKER_NONE) {
+ continue;
}
- } while ((sd == -1) && (err == 0));
-
- /* return lock now, even if the accept failed.
- * if it takes more than 10 seconds we are in deep trouble */
- ret = prefork_release_lock(pf, lock_fd, 2);
- if (ret != 0) {
- /* we were unable to release the lock!! */
- DEBUG(0, ("Terminating due to fatal failure!\n"));
-
- /* Just exit we cannot hold the whole server, better to error
- * on this one client and hope it was a transiet problem */
- err = -2;
- }
-
- if (err != 0) {
- if (sd != -1) {
- close(sd);
- sd = -1;
- }
- return err;
+ kill(pfp->pool[i].pid, signal_num);
}
-
- pf->status = PF_WORKER_BUSY;
- pf->num_clients++;
- *fd = sd;
- 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)
+static void prefork_sigchld_handler(struct tevent_context *ev_ctx,
+ struct tevent_signal *se,
+ int signum, int count,
+ void *siginfo, void *pvt)
{
- struct tevent_req *req;
- struct pf_lock_state *state;
+ struct prefork_pool *pfp;
- req = tevent_req_create(mem_ctx, &state, struct pf_lock_state);
- if (!req) {
- return NULL;
- }
+ pfp = talloc_get_type_abort(pvt, struct prefork_pool);
- state->pf = pf;
- state->lock_fd = lock_fd;
- state->flags = action;
+ /* run the cleanup function to make sure all dead children are
+ * properly and timely retired. */
+ prefork_cleanup_loop(pfp);
- /* try once immediately */
- prefork_lock_handler(ev, NULL, tevent_timeval_zero(), req);
- if (state->flags & PF_ASYNC_LOCK_DONE) {
- tevent_req_post(req, ev);
+ if (pfp->sigchld_fn) {
+ pfp->sigchld_fn(ev_ctx, pfp, pfp->sigchld_data);
}
-
- return req;
}
-static void prefork_lock_handler(struct tevent_context *ev,
- struct tevent_timer *te,
- struct timeval curtime, void *pvt)
+static bool prefork_setup_sigchld_handler(struct tevent_context *ev_ctx,
+ struct prefork_pool *pfp)
{
- struct tevent_req *req;
- struct pf_lock_state *state;
- int ret;
+ struct tevent_signal *se;
- 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;
+ se = tevent_add_signal(ev_ctx, pfp, SIGCHLD, 0,
+ prefork_sigchld_handler, pfp);
+ if (!se) {
+ DEBUG(0, ("Failed to setup SIGCHLD handler!\n"));
+ return false;
}
+
+ return true;
}
-static int prefork_lock_recv(struct tevent_req *req)
+void prefork_set_sigchld_callback(struct prefork_pool *pfp,
+ prefork_sigchld_fn_t *sigchld_fn,
+ void *private_data)
{
- int ret;
-
- if (!tevent_req_is_unix_error(req, &ret)) {
- ret = 0;
- }
-
- tevent_req_received(req);
- return ret;
+ pfp->sigchld_fn = sigchld_fn;
+ pfp->sigchld_data = private_data;
}
+/* ==== Functions used by children ==== */
+
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 listen_fd_size;
+ int *listen_fds;
int accept_fd;
+ struct tsocket_address *srv_addr;
+ struct tsocket_address *cli_addr;
+
int error;
};
-static void prefork_listen_lock_done(struct tevent_req *subreq);
+struct pf_listen_ctx {
+ TALLOC_CTX *fde_ctx;
+ struct tevent_req *req;
+ int listen_fd;
+};
+
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)
+ int listen_fd_size,
+ int *listen_fds)
{
- struct tevent_req *req, *subreq;
+ struct tevent_req *req;
struct pf_listen_state *state;
+ struct pf_listen_ctx *ctx;
+ struct tevent_fd *fde;
+ TALLOC_CTX *fde_ctx;
+ int i;
req = tevent_req_create(mem_ctx, &state, struct pf_listen_state);
if (!req) {
state->ev = ev;
state->pf = pf;
- state->lock_fd = lock_fd;
- state->listen_fd = listen_fd;
- state->addr = addr;
- state->addrlen = addrlen;
+ state->listen_fd_size = listen_fd_size;
+ state->listen_fds = listen_fds;
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)) {
+ fde_ctx = talloc_new(state);
+ if (tevent_req_nomem(fde_ctx, 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;
+ /* race on accept */
+ for (i = 0; i < state->listen_fd_size; i++) {
+ ctx = talloc(fde_ctx, struct pf_listen_ctx);
+ if (tevent_req_nomem(ctx, req)) {
+ return tevent_req_post(req, ev);
+ }
+ ctx->fde_ctx = fde_ctx;
+ ctx->req = req;
+ ctx->listen_fd = state->listen_fds[i];
+
+ fde = tevent_add_fd(state->ev, fde_ctx,
+ ctx->listen_fd, TEVENT_FD_READ,
+ prefork_listen_accept_handler, ctx);
+ if (tevent_req_nomem(fde, req)) {
+ return tevent_req_post(req, ev);
+ }
}
- /* 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);
+ pf->status = PF_WORKER_ACCEPTING;
+
+ return req;
}
static void prefork_listen_accept_handler(struct tevent_context *ev,
uint16_t flags, void *pvt)
{
struct pf_listen_state *state;
- struct tevent_req *req, *subreq;
+ struct tevent_req *req;
+ struct pf_listen_ctx *ctx;
+ struct sockaddr_storage addr;
+ socklen_t addrlen;
int err = 0;
int sd = -1;
+ int ret;
- req = talloc_get_type_abort(pvt, struct tevent_req);
- state = tevent_req_data(req, struct pf_listen_state);
+ ctx = talloc_get_type_abort(pvt, struct pf_listen_ctx);
+ req = ctx->req;
+ state = tevent_req_data(ctx->req, struct pf_listen_state);
+
+ if (state->pf->cmds == PF_SRV_MSG_EXIT) {
+ /* We have been asked to exit, so drop here and the next
+ * child will pick it up */
+ state->pf->status = PF_WORKER_EXITING;
+ state->error = EINTR;
+ goto done;
+ }
- sd = accept(state->listen_fd, state->addr, state->addrlen);
+ ZERO_STRUCT(addr);
+ addrlen = sizeof(addr);
+ sd = accept(ctx->listen_fd, (struct sockaddr *)&addr, &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);
+ /* do not track the listen fds anymore */
+ talloc_free(ctx->fde_ctx);
+ ctx = NULL;
if (err) {
- tevent_req_error(req, err);
- return;
+ state->error = err;
+ goto done;
}
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;
+ ret = tsocket_address_bsd_from_sockaddr(state,
+ (struct sockaddr *)(void *)&addr,
+ addrlen, &state->cli_addr);
+ if (ret < 0) {
+ state->error = errno;
+ goto done;
}
- 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);
+ ZERO_STRUCT(addr);
+ addrlen = sizeof(addr);
+ ret = getsockname(sd, (struct sockaddr *)(void *)&addr, &addrlen);
+ if (ret < 0) {
+ state->error = errno;
+ goto done;
+ }
- ret = prefork_lock_recv(subreq);
- if (ret != 0) {
- tevent_req_error(req, ret);
- return;
+ ret = tsocket_address_bsd_from_sockaddr(state,
+ (struct sockaddr *)(void *)&addr,
+ addrlen, &state->srv_addr);
+ if (ret < 0) {
+ state->error = errno;
+ goto done;
}
+done:
tevent_req_done(req);
}
-int prefork_listen_recv(struct tevent_req *req, int *fd)
+int prefork_listen_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx, int *fd,
+ struct tsocket_address **srv_addr,
+ struct tsocket_address **cli_addr)
{
struct pf_listen_state *state;
- int ret;
+ int ret = 0;
state = tevent_req_data(req, struct pf_listen_state);
- if (tevent_req_is_unix_error(req, &ret)) {
+ if (state->error) {
+ ret = state->error;
+ } else {
+ tevent_req_is_unix_error(req, &ret);
+ }
+
+ if (ret) {
if (state->accept_fd != -1) {
close(state->accept_fd);
}
} else {
*fd = state->accept_fd;
- ret = 0;
- state->pf->status = PF_WORKER_BUSY;
+ *srv_addr = talloc_move(mem_ctx, &state->srv_addr);
+ *cli_addr = talloc_move(mem_ctx, &state->cli_addr);
state->pf->num_clients++;
}
+ if (state->pf->status == PF_WORKER_ACCEPTING) {
+ state->pf->status = PF_WORKER_ALIVE;
+ }
tevent_req_received(req);
return ret;