*/
#include "includes.h"
+#include "serverid.h"
+#include "messages.h"
#include "system/time.h"
#include "system/shmem.h"
#include "system/filesys.h"
#include "server_prefork.h"
-#include "../lib/util/util.h"
+#include "../lib/util/samba_util.h"
#include "../lib/util/tevent_unix.h"
struct prefork_pool {
-
int listen_fd_size;
- int *listen_fds;
+ struct pf_listen_fd *listen_fds;
prefork_main_fn_t *main_fn;
void *private_data;
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 listen_fd_size, struct pf_listen_fd *listen_fds,
int min_children, int max_children,
prefork_main_fn_t *main_fn, void *private_data,
struct prefork_pool **pf_pool)
return false;
}
pfp->listen_fd_size = listen_fd_size;
- pfp->listen_fds = talloc_array(pfp, int, listen_fd_size);
+ pfp->listen_fds = talloc_array(pfp, struct pf_listen_fd,
+ 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];
+ /* force sockets in non-blocking mode */
+ set_blocking(listen_fds[i].fd, false);
}
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 = anonymous_shared_allocate(data_size);
+ pfp->pool = (struct pf_worker_data *)anonymous_shared_allocate(
+ data_size);
if (pfp->pool == NULL) {
DEBUG(1, ("Failed to mmap memory for prefork pool!\n"));
talloc_free(pfp);
pfp->pool[i].allowed_clients = 1;
pfp->pool[i].started = now;
- pid = sys_fork();
+ pid = fork();
switch (pid) {
case -1:
DEBUG(1, ("Failed to prefork child n. %d !\n", i));
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);
+ pool = (struct prefork_pool *)anonymous_shared_resize(
+ &pfp->pool, new_size, false);
if (pool == NULL) {
ret = errno;
DEBUG(3, ("Failed to mremap memory (%d: %s)!\n",
pfp->pool[i].allowed_clients = 1;
pfp->pool[i].started = now;
- pid = sys_fork();
+ pid = fork();
switch (pid) {
case -1:
DEBUG(1, ("Failed to prefork child n. %d !\n", j));
return -1;
}
-int prefork_retire_children(struct prefork_pool *pfp,
+int prefork_retire_children(struct messaging_context *msg_ctx,
+ struct prefork_pool *pfp,
int num_children, time_t age_limit)
{
+ const DATA_BLOB ping = data_blob_null;
time_t now = time(NULL);
struct prefork_oldest *oldest;
int i, j;
prefork_sort_oldest);
for (i = 0, j = 0; i < pfp->pool_size && j < num_children; i++) {
- if ((pfp->pool[i].status == PF_WORKER_ALIVE ||
- pfp->pool[i].status == PF_WORKER_ACCEPTING) &&
- pfp->pool[i].started <= age_limit) {
+ if (((pfp->pool[i].status == PF_WORKER_ALIVE) &&
+ (pfp->pool[i].num_clients < 1)) &&
+ (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));
+ DEBUG(5, ("Retiring pid %u!\n", (unsigned int)pfp->pool[i].pid));
pfp->pool[i].cmds = PF_SRV_MSG_EXIT;
- kill(pfp->pool[i].pid, SIGHUP);
+ messaging_send(msg_ctx,
+ pid_to_procid(pfp->pool[i].pid),
+ MSG_PREFORK_PARENT_EVENT, &ping);
j++;
}
}
return j;
}
-int prefork_count_active_children(struct prefork_pool *pfp, int *total)
+int prefork_count_children(struct prefork_pool *pfp, int *active)
{
int i, a, t;
t++;
- if (pfp->pool[i].num_clients <= 0) {
+ if ((pfp->pool[i].status == PF_WORKER_EXITING) ||
+ (pfp->pool[i].num_clients <= 0)) {
continue;
}
a++;
}
- *total = t;
- return a;
+ if (active) {
+ *active = a;
+ }
+ return t;
}
static void prefork_cleanup_loop(struct prefork_pool *pfp)
continue;
}
- pid = sys_waitpid(pfp->pool[i].pid, &status, WNOHANG);
+ pid = waitpid(pfp->pool[i].pid, &status, WNOHANG);
if (pid > 0) {
if (pfp->pool[i].status != PF_WORKER_EXITING) {
}
}
+void prefork_warn_active_children(struct messaging_context *msg_ctx,
+ struct prefork_pool *pfp)
+{
+ const DATA_BLOB ping = data_blob_null;
+ int i;
+
+ for (i = 0; i < pfp->pool_size; i++) {
+ if (pfp->pool[i].status == PF_WORKER_NONE) {
+ continue;
+ }
+
+ messaging_send(msg_ctx,
+ pid_to_procid(pfp->pool[i].pid),
+ MSG_PREFORK_PARENT_EVENT, &ping);
+ }
+}
+
static void prefork_sigchld_handler(struct tevent_context *ev_ctx,
struct tevent_signal *se,
int signum, int count,
struct pf_worker_data *pf;
int listen_fd_size;
- int *listen_fds;
+ struct pf_listen_fd *listen_fds;
- int accept_fd;
+ struct pf_listen_fd accept;
struct tsocket_address *srv_addr;
struct tsocket_address *cli_addr;
TALLOC_CTX *fde_ctx;
struct tevent_req *req;
int listen_fd;
+ void *listen_fd_data;
};
static void prefork_listen_accept_handler(struct tevent_context *ev,
struct tevent_context *ev,
struct pf_worker_data *pf,
int listen_fd_size,
- int *listen_fds)
+ struct pf_listen_fd *listen_fds)
{
struct tevent_req *req;
struct pf_listen_state *state;
state->pf = pf;
state->listen_fd_size = listen_fd_size;
state->listen_fds = listen_fds;
- state->accept_fd = -1;
+ state->accept.fd = -1;
+ state->accept.fd_data = NULL;
state->error = 0;
fde_ctx = talloc_new(state);
}
ctx->fde_ctx = fde_ctx;
ctx->req = req;
- ctx->listen_fd = state->listen_fds[i];
+ ctx->listen_fd = state->listen_fds[i].fd;
+ ctx->listen_fd_data = state->listen_fds[i].fd_data;
fde = tevent_add_fd(state->ev, fde_ctx,
ctx->listen_fd, TEVENT_FD_READ,
struct pf_listen_ctx *ctx;
struct sockaddr_storage addr;
socklen_t addrlen;
- int err = 0;
+ int soerr = 0;
+ socklen_t solen = sizeof(soerr);
int sd = -1;
int ret;
req = ctx->req;
state = tevent_req_data(ctx->req, struct pf_listen_state);
- if (state->pf->cmds == PF_SRV_MSG_EXIT) {
+ if ((state->pf->cmds == PF_SRV_MSG_EXIT) &&
+ (state->pf->num_clients <= 0)) {
/* We have been asked to exit, so drop here and the next
* child will pick it up */
state->pf->status = PF_WORKER_EXITING;
goto done;
}
+ /* before proceeding check that the listening fd is ok */
+ ret = getsockopt(ctx->listen_fd, SOL_SOCKET, SO_ERROR, &soerr, &solen);
+ if (ret == -1) {
+ /* this is a fatal error, we cannot continue listening */
+ state->error = EBADF;
+ goto done;
+ }
+ if (soerr != 0) {
+ /* this is a fatal error, we cannot continue listening */
+ state->error = soerr;
+ goto done;
+ }
+
ZERO_STRUCT(addr);
addrlen = sizeof(addr);
sd = accept(ctx->listen_fd, (struct sockaddr *)&addr, &addrlen);
if (sd == -1) {
- err = errno;
- DEBUG(6, ("Accept failed! (%d, %s)\n", err, strerror(err)));
- }
-
- /* do not track the listen fds anymore */
- talloc_free(ctx->fde_ctx);
- ctx = NULL;
- if (err) {
- state->error = err;
+ state->error = errno;
+ DEBUG(6, ("Accept failed! (%d, %s)\n",
+ state->error, strerror(state->error)));
goto done;
}
+ smb_set_close_on_exec(sd);
- state->accept_fd = sd;
+ state->accept.fd = sd;
+ state->accept.fd_data = ctx->listen_fd_data;
ret = tsocket_address_bsd_from_sockaddr(state,
(struct sockaddr *)(void *)&addr,
}
done:
+ /* do not track the listen fds anymore */
+ talloc_free(ctx->fde_ctx);
tevent_req_done(req);
}
int prefork_listen_recv(struct tevent_req *req,
TALLOC_CTX *mem_ctx, int *fd,
+ void **fd_data,
struct tsocket_address **srv_addr,
struct tsocket_address **cli_addr)
{
if (state->error) {
ret = state->error;
} else {
- tevent_req_is_unix_error(req, &ret);
+ if (!tevent_req_is_unix_error(req, &ret)) {
+ ret = 0;
+ }
}
if (ret) {
- if (state->accept_fd != -1) {
- close(state->accept_fd);
+ if (state->accept.fd != -1) {
+ close(state->accept.fd);
}
} else {
- *fd = state->accept_fd;
+ *fd = state->accept.fd;
+ if (fd_data != NULL) {
+ *fd_data = state->accept.fd_data;
+ }
*srv_addr = talloc_move(mem_ctx, &state->srv_addr);
*cli_addr = talloc_move(mem_ctx, &state->cli_addr);
state->pf->num_clients++;