2 Unix SMB/CIFS implementation.
5 Copyright (C) Simo Sorce <idra@samba.org> 2011
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>.
24 #include "system/time.h"
25 #include "system/shmem.h"
26 #include "system/filesys.h"
27 #include "server_prefork.h"
28 #include "../lib/util/util.h"
29 #include "../lib/util/tevent_unix.h"
36 prefork_main_fn_t *main_fn;
40 struct pf_worker_data *pool;
44 prefork_sigchld_fn_t *sigchld_fn;
48 static bool prefork_setup_sigchld_handler(struct tevent_context *ev_ctx,
49 struct prefork_pool *pfp);
51 static int prefork_pool_destructor(struct prefork_pool *pfp)
53 anonymous_shared_free(pfp->pool);
57 bool prefork_create_pool(TALLOC_CTX *mem_ctx,
58 struct tevent_context *ev_ctx,
59 struct messaging_context *msg_ctx,
60 int listen_fd_size, int *listen_fds,
61 int min_children, int max_children,
62 prefork_main_fn_t *main_fn, void *private_data,
63 struct prefork_pool **pf_pool)
65 struct prefork_pool *pfp;
67 time_t now = time(NULL);
73 pfp = talloc_zero(mem_ctx, struct prefork_pool);
75 DEBUG(1, ("Out of memory!\n"));
78 pfp->listen_fd_size = listen_fd_size;
79 pfp->listen_fds = talloc_array(pfp, int, listen_fd_size);
80 if (!pfp->listen_fds) {
81 DEBUG(1, ("Out of memory!\n"));
84 for (i = 0; i < listen_fd_size; i++) {
85 pfp->listen_fds[i] = listen_fds[i];
87 pfp->main_fn = main_fn;
88 pfp->private_data = private_data;
90 pfp->pool_size = max_children;
91 data_size = sizeof(struct pf_worker_data) * max_children;
93 pfp->pool = anonymous_shared_allocate(data_size);
94 if (pfp->pool == NULL) {
95 DEBUG(1, ("Failed to mmap memory for prefork pool!\n"));
99 talloc_set_destructor(pfp, prefork_pool_destructor);
101 for (i = 0; i < min_children; i++) {
103 pfp->pool[i].allowed_clients = 1;
104 pfp->pool[i].started = now;
109 DEBUG(1, ("Failed to prefork child n. %d !\n", i));
112 case 0: /* THE CHILD */
114 pfp->pool[i].status = PF_WORKER_ALIVE;
115 ret = pfp->main_fn(ev_ctx, msg_ctx,
116 &pfp->pool[i], i + 1,
122 default: /* THE PARENT */
123 pfp->pool[i].pid = pid;
128 ok = prefork_setup_sigchld_handler(ev_ctx, pfp);
130 DEBUG(1, ("Failed to setup SIGCHLD Handler!\n"));
139 /* Provide the new max children number in new_max
140 * (must be larger than current max).
141 * Returns: 0 if all fine
142 * ENOSPC if mremap fails to expand
143 * EINVAL if new_max is invalid
145 int prefork_expand_pool(struct prefork_pool *pfp, int new_max)
147 struct prefork_pool *pool;
152 if (new_max <= pfp->pool_size) {
156 old_size = sizeof(struct pf_worker_data) * pfp->pool_size;
157 new_size = sizeof(struct pf_worker_data) * new_max;
159 pool = anonymous_shared_resize(&pfp->pool, new_size, false);
162 DEBUG(3, ("Failed to mremap memory (%d: %s)!\n",
163 ret, strerror(ret)));
167 memset(&pool[pfp->pool_size], 0, new_size - old_size);
169 pfp->pool_size = new_max;
174 int prefork_add_children(struct tevent_context *ev_ctx,
175 struct messaging_context *msg_ctx,
176 struct prefork_pool *pfp,
180 time_t now = time(NULL);
184 for (i = 0, j = 0; i < pfp->pool_size && j < num_children; i++) {
186 if (pfp->pool[i].status != PF_WORKER_NONE) {
190 pfp->pool[i].allowed_clients = 1;
191 pfp->pool[i].started = now;
196 DEBUG(1, ("Failed to prefork child n. %d !\n", j));
199 case 0: /* THE CHILD */
201 pfp->pool[i].status = PF_WORKER_ALIVE;
202 ret = pfp->main_fn(ev_ctx, msg_ctx,
203 &pfp->pool[i], i + 1,
208 pfp->pool[i].status = PF_WORKER_EXITING;
211 default: /* THE PARENT */
212 pfp->pool[i].pid = pid;
218 DEBUG(5, ("Added %d children!\n", j));
223 struct prefork_oldest {
228 /* sort in inverse order */
229 static int prefork_sort_oldest(const void *ap, const void *bp)
231 const struct prefork_oldest *a = (const struct prefork_oldest *)ap;
232 const struct prefork_oldest *b = (const struct prefork_oldest *)bp;
234 if (a->started == b->started) {
237 if (a->started < b->started) {
243 int prefork_retire_children(struct prefork_pool *pfp,
244 int num_children, time_t age_limit)
246 time_t now = time(NULL);
247 struct prefork_oldest *oldest;
250 oldest = talloc_array(pfp, struct prefork_oldest, pfp->pool_size);
255 for (i = 0; i < pfp->pool_size; i++) {
257 if (pfp->pool[i].status == PF_WORKER_ALIVE ||
258 pfp->pool[i].status == PF_WORKER_ACCEPTING) {
259 oldest[i].started = pfp->pool[i].started;
261 oldest[i].started = now;
265 qsort(oldest, pfp->pool_size,
266 sizeof(struct prefork_oldest),
267 prefork_sort_oldest);
269 for (i = 0, j = 0; i < pfp->pool_size && j < num_children; i++) {
270 if ((pfp->pool[i].status == PF_WORKER_ALIVE ||
271 pfp->pool[i].status == PF_WORKER_ACCEPTING) &&
272 pfp->pool[i].started <= age_limit) {
273 /* tell the child it's time to give up */
274 DEBUG(5, ("Retiring pid %d!\n", pfp->pool[i].pid));
275 pfp->pool[i].cmds = PF_SRV_MSG_EXIT;
276 kill(pfp->pool[i].pid, SIGHUP);
284 int prefork_count_children(struct prefork_pool *pfp, int *active)
290 for (i = 0; i < pfp->pool_size; i++) {
291 if (pfp->pool[i].status == PF_WORKER_NONE) {
297 if ((pfp->pool[i].status == PF_WORKER_EXITING) ||
298 (pfp->pool[i].num_clients <= 0)) {
311 static void prefork_cleanup_loop(struct prefork_pool *pfp)
317 /* TODO: should we use a process group id wait instead of looping ? */
318 for (i = 0; i < pfp->pool_size; i++) {
319 if (pfp->pool[i].status == PF_WORKER_NONE ||
320 pfp->pool[i].pid == 0) {
324 pid = sys_waitpid(pfp->pool[i].pid, &status, WNOHANG);
327 if (pfp->pool[i].status != PF_WORKER_EXITING) {
328 DEBUG(3, ("Child (%d) terminated abnormally:"
329 " %d\n", (int)pid, status));
331 DEBUG(10, ("Child (%d) terminated with status:"
332 " %d\n", (int)pid, status));
336 * this makes status = PF_WORK_NONE */
337 memset(&pfp->pool[i], 0,
338 sizeof(struct pf_worker_data));
344 int prefork_count_allowed_connections(struct prefork_pool *pfp)
350 for (i = 0; i < pfp->pool_size; i++) {
351 if (pfp->pool[i].status == PF_WORKER_NONE ||
352 pfp->pool[i].status == PF_WORKER_EXITING) {
356 if (pfp->pool[i].num_clients < 0) {
360 c += pfp->pool[i].allowed_clients - pfp->pool[i].num_clients;
366 void prefork_increase_allowed_clients(struct prefork_pool *pfp, int max)
370 for (i = 0; i < pfp->pool_size; i++) {
371 if (pfp->pool[i].status == PF_WORKER_NONE ||
372 pfp->pool[i].status == PF_WORKER_EXITING) {
376 if (pfp->pool[i].num_clients < 0) {
380 if (pfp->pool[i].allowed_clients < max) {
381 pfp->pool[i].allowed_clients++;
386 void prefork_decrease_allowed_clients(struct prefork_pool *pfp)
390 for (i = 0; i < pfp->pool_size; i++) {
391 if (pfp->pool[i].status == PF_WORKER_NONE ||
392 pfp->pool[i].status == PF_WORKER_EXITING) {
396 if (pfp->pool[i].num_clients < 0) {
400 if (pfp->pool[i].allowed_clients > 1) {
401 pfp->pool[i].allowed_clients--;
406 void prefork_reset_allowed_clients(struct prefork_pool *pfp)
410 for (i = 0; i < pfp->pool_size; i++) {
411 pfp->pool[i].allowed_clients = 1;
415 void prefork_send_signal_to_all(struct prefork_pool *pfp, int signal_num)
419 for (i = 0; i < pfp->pool_size; i++) {
420 if (pfp->pool[i].status == PF_WORKER_NONE) {
424 kill(pfp->pool[i].pid, signal_num);
428 void prefork_warn_active_children(struct messaging_context *msg_ctx,
429 struct prefork_pool *pfp)
431 const DATA_BLOB ping = data_blob_null;
434 for (i = 0; i < pfp->pool_size; i++) {
435 if (pfp->pool[i].status == PF_WORKER_NONE) {
439 messaging_send(msg_ctx,
440 pid_to_procid(pfp->pool[i].pid),
441 MSG_PREFORK_PARENT_EVENT, &ping);
445 static void prefork_sigchld_handler(struct tevent_context *ev_ctx,
446 struct tevent_signal *se,
447 int signum, int count,
448 void *siginfo, void *pvt)
450 struct prefork_pool *pfp;
452 pfp = talloc_get_type_abort(pvt, struct prefork_pool);
454 /* run the cleanup function to make sure all dead children are
455 * properly and timely retired. */
456 prefork_cleanup_loop(pfp);
458 if (pfp->sigchld_fn) {
459 pfp->sigchld_fn(ev_ctx, pfp, pfp->sigchld_data);
463 static bool prefork_setup_sigchld_handler(struct tevent_context *ev_ctx,
464 struct prefork_pool *pfp)
466 struct tevent_signal *se;
468 se = tevent_add_signal(ev_ctx, pfp, SIGCHLD, 0,
469 prefork_sigchld_handler, pfp);
471 DEBUG(0, ("Failed to setup SIGCHLD handler!\n"));
478 void prefork_set_sigchld_callback(struct prefork_pool *pfp,
479 prefork_sigchld_fn_t *sigchld_fn,
482 pfp->sigchld_fn = sigchld_fn;
483 pfp->sigchld_data = private_data;
486 /* ==== Functions used by children ==== */
488 struct pf_listen_state {
489 struct tevent_context *ev;
490 struct pf_worker_data *pf;
497 struct tsocket_address *srv_addr;
498 struct tsocket_address *cli_addr;
503 struct pf_listen_ctx {
505 struct tevent_req *req;
509 static void prefork_listen_accept_handler(struct tevent_context *ev,
510 struct tevent_fd *fde,
511 uint16_t flags, void *pvt);
513 struct tevent_req *prefork_listen_send(TALLOC_CTX *mem_ctx,
514 struct tevent_context *ev,
515 struct pf_worker_data *pf,
519 struct tevent_req *req;
520 struct pf_listen_state *state;
521 struct pf_listen_ctx *ctx;
522 struct tevent_fd *fde;
526 req = tevent_req_create(mem_ctx, &state, struct pf_listen_state);
533 state->listen_fd_size = listen_fd_size;
534 state->listen_fds = listen_fds;
535 state->accept_fd = -1;
538 fde_ctx = talloc_new(state);
539 if (tevent_req_nomem(fde_ctx, req)) {
540 return tevent_req_post(req, ev);
544 for (i = 0; i < state->listen_fd_size; i++) {
545 ctx = talloc(fde_ctx, struct pf_listen_ctx);
546 if (tevent_req_nomem(ctx, req)) {
547 return tevent_req_post(req, ev);
549 ctx->fde_ctx = fde_ctx;
551 ctx->listen_fd = state->listen_fds[i];
553 fde = tevent_add_fd(state->ev, fde_ctx,
554 ctx->listen_fd, TEVENT_FD_READ,
555 prefork_listen_accept_handler, ctx);
556 if (tevent_req_nomem(fde, req)) {
557 return tevent_req_post(req, ev);
561 pf->status = PF_WORKER_ACCEPTING;
566 static void prefork_listen_accept_handler(struct tevent_context *ev,
567 struct tevent_fd *fde,
568 uint16_t flags, void *pvt)
570 struct pf_listen_state *state;
571 struct tevent_req *req;
572 struct pf_listen_ctx *ctx;
573 struct sockaddr_storage addr;
576 socklen_t solen = sizeof(soerr);
580 ctx = talloc_get_type_abort(pvt, struct pf_listen_ctx);
582 state = tevent_req_data(ctx->req, struct pf_listen_state);
584 if (state->pf->cmds == PF_SRV_MSG_EXIT) {
585 /* We have been asked to exit, so drop here and the next
586 * child will pick it up */
587 if (state->pf->num_clients <= 0) {
588 state->pf->status = PF_WORKER_EXITING;
590 state->error = EINTR;
594 /* before proceeding check that the listening fd is ok */
595 ret = getsockopt(ctx->listen_fd, SOL_SOCKET, SO_ERROR, &soerr, &solen);
597 /* this is a fatal error, we cannot continue listening */
598 state->error = EBADF;
602 /* this is a fatal error, we cannot continue listening */
603 state->error = soerr;
608 addrlen = sizeof(addr);
609 sd = accept(ctx->listen_fd, (struct sockaddr *)&addr, &addrlen);
611 state->error = errno;
612 DEBUG(6, ("Accept failed! (%d, %s)\n",
613 state->error, strerror(state->error)));
617 state->accept_fd = sd;
619 ret = tsocket_address_bsd_from_sockaddr(state,
620 (struct sockaddr *)(void *)&addr,
621 addrlen, &state->cli_addr);
623 state->error = errno;
628 addrlen = sizeof(addr);
629 ret = getsockname(sd, (struct sockaddr *)(void *)&addr, &addrlen);
631 state->error = errno;
635 ret = tsocket_address_bsd_from_sockaddr(state,
636 (struct sockaddr *)(void *)&addr,
637 addrlen, &state->srv_addr);
639 state->error = errno;
644 /* do not track the listen fds anymore */
645 talloc_free(ctx->fde_ctx);
646 tevent_req_done(req);
649 int prefork_listen_recv(struct tevent_req *req,
650 TALLOC_CTX *mem_ctx, int *fd,
651 struct tsocket_address **srv_addr,
652 struct tsocket_address **cli_addr)
654 struct pf_listen_state *state;
657 state = tevent_req_data(req, struct pf_listen_state);
662 tevent_req_is_unix_error(req, &ret);
666 if (state->accept_fd != -1) {
667 close(state->accept_fd);
670 *fd = state->accept_fd;
671 *srv_addr = talloc_move(mem_ctx, &state->srv_addr);
672 *cli_addr = talloc_move(mem_ctx, &state->cli_addr);
673 state->pf->num_clients++;
675 if (state->pf->status == PF_WORKER_ACCEPTING) {
676 state->pf->status = PF_WORKER_ALIVE;
679 tevent_req_received(req);