lib/util: Standardize use of st_[acm]time ns
[samba.git] / source3 / lib / server_prefork.c
index 71441c3303bbea0eaef1577ec2e33a675718d1b2..d3fb8d1a8bcfe27f173d58685fc9b070266d8c6e 100644 (file)
 */
 
 #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;
@@ -55,7 +56,7 @@ static int prefork_pool_destructor(struct prefork_pool *pfp)
 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)
@@ -74,13 +75,16 @@ bool prefork_create_pool(TALLOC_CTX *mem_ctx,
                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;
@@ -88,7 +92,8 @@ bool prefork_create_pool(TALLOC_CTX *mem_ctx,
        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);
@@ -101,7 +106,7 @@ bool prefork_create_pool(TALLOC_CTX *mem_ctx,
                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));
@@ -154,7 +159,8 @@ int prefork_expand_pool(struct prefork_pool *pfp, int new_max)
        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",
@@ -188,7 +194,7 @@ int prefork_add_children(struct tevent_context *ev_ctx,
                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));
@@ -238,9 +244,11 @@ static int prefork_sort_oldest(const void *ap, const void *bp)
        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;
@@ -265,13 +273,15 @@ int prefork_retire_children(struct prefork_pool *pfp,
                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++;
                }
        }
@@ -279,7 +289,7 @@ int prefork_retire_children(struct prefork_pool *pfp,
        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;
 
@@ -292,15 +302,18 @@ int prefork_count_active_children(struct prefork_pool *pfp, int *total)
 
                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)
@@ -316,7 +329,7 @@ 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) {
@@ -420,6 +433,23 @@ void prefork_send_signal_to_all(struct prefork_pool *pfp, int signal_num)
        }
 }
 
+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,
@@ -468,9 +498,9 @@ struct pf_listen_state {
        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;
@@ -482,6 +512,7 @@ struct pf_listen_ctx {
        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,
@@ -492,7 +523,7 @@ struct tevent_req *prefork_listen_send(TALLOC_CTX *mem_ctx,
                                        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;
@@ -510,7 +541,8 @@ struct tevent_req *prefork_listen_send(TALLOC_CTX *mem_ctx,
        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);
@@ -526,7 +558,8 @@ struct tevent_req *prefork_listen_send(TALLOC_CTX *mem_ctx,
                }
                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,
@@ -550,7 +583,8 @@ static void prefork_listen_accept_handler(struct tevent_context *ev,
        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;
 
@@ -558,7 +592,8 @@ static void prefork_listen_accept_handler(struct tevent_context *ev,
        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;
@@ -566,23 +601,32 @@ static void prefork_listen_accept_handler(struct tevent_context *ev,
                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,
@@ -609,11 +653,14 @@ static void prefork_listen_accept_handler(struct tevent_context *ev,
        }
 
 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)
 {
@@ -625,15 +672,20 @@ int prefork_listen_recv(struct tevent_req *req,
        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++;