ctdb-daemon: Fix signed/unsigned comparison
[samba.git] / source3 / lib / server_prefork.c
blobd3fb8d1a8bcfe27f173d58685fc9b070266d8c6e
1 /*
2 Unix SMB/CIFS implementation.
3 Common server globals
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/>.
21 #include "includes.h"
22 #include "serverid.h"
23 #include "messages.h"
24 #include "system/time.h"
25 #include "system/shmem.h"
26 #include "system/filesys.h"
27 #include "server_prefork.h"
28 #include "../lib/util/samba_util.h"
29 #include "../lib/util/tevent_unix.h"
31 struct prefork_pool {
32 int listen_fd_size;
33 struct pf_listen_fd *listen_fds;
35 prefork_main_fn_t *main_fn;
36 void *private_data;
38 int pool_size;
39 struct pf_worker_data *pool;
41 int allowed_clients;
43 prefork_sigchld_fn_t *sigchld_fn;
44 void *sigchld_data;
47 static bool prefork_setup_sigchld_handler(struct tevent_context *ev_ctx,
48 struct prefork_pool *pfp);
50 static int prefork_pool_destructor(struct prefork_pool *pfp)
52 anonymous_shared_free(pfp->pool);
53 return 0;
56 bool prefork_create_pool(TALLOC_CTX *mem_ctx,
57 struct tevent_context *ev_ctx,
58 struct messaging_context *msg_ctx,
59 int listen_fd_size, struct pf_listen_fd *listen_fds,
60 int min_children, int max_children,
61 prefork_main_fn_t *main_fn, void *private_data,
62 struct prefork_pool **pf_pool)
64 struct prefork_pool *pfp;
65 pid_t pid;
66 time_t now = time(NULL);
67 size_t data_size;
68 int ret;
69 int i;
70 bool ok;
72 pfp = talloc_zero(mem_ctx, struct prefork_pool);
73 if (!pfp) {
74 DEBUG(1, ("Out of memory!\n"));
75 return false;
77 pfp->listen_fd_size = listen_fd_size;
78 pfp->listen_fds = talloc_array(pfp, struct pf_listen_fd,
79 listen_fd_size);
80 if (!pfp->listen_fds) {
81 DEBUG(1, ("Out of memory!\n"));
82 return false;
84 for (i = 0; i < listen_fd_size; i++) {
85 pfp->listen_fds[i] = listen_fds[i];
86 /* force sockets in non-blocking mode */
87 set_blocking(listen_fds[i].fd, false);
89 pfp->main_fn = main_fn;
90 pfp->private_data = private_data;
92 pfp->pool_size = max_children;
93 data_size = sizeof(struct pf_worker_data) * max_children;
95 pfp->pool = (struct pf_worker_data *)anonymous_shared_allocate(
96 data_size);
97 if (pfp->pool == NULL) {
98 DEBUG(1, ("Failed to mmap memory for prefork pool!\n"));
99 talloc_free(pfp);
100 return false;
102 talloc_set_destructor(pfp, prefork_pool_destructor);
104 for (i = 0; i < min_children; i++) {
106 pfp->pool[i].allowed_clients = 1;
107 pfp->pool[i].started = now;
109 pid = fork();
110 switch (pid) {
111 case -1:
112 DEBUG(1, ("Failed to prefork child n. %d !\n", i));
113 break;
115 case 0: /* THE CHILD */
117 pfp->pool[i].status = PF_WORKER_ALIVE;
118 ret = pfp->main_fn(ev_ctx, msg_ctx,
119 &pfp->pool[i], i + 1,
120 pfp->listen_fd_size,
121 pfp->listen_fds,
122 pfp->private_data);
123 exit(ret);
125 default: /* THE PARENT */
126 pfp->pool[i].pid = pid;
127 break;
131 ok = prefork_setup_sigchld_handler(ev_ctx, pfp);
132 if (!ok) {
133 DEBUG(1, ("Failed to setup SIGCHLD Handler!\n"));
134 talloc_free(pfp);
135 return false;
138 *pf_pool = pfp;
139 return true;
142 /* Provide the new max children number in new_max
143 * (must be larger than current max).
144 * Returns: 0 if all fine
145 * ENOSPC if mremap fails to expand
146 * EINVAL if new_max is invalid
148 int prefork_expand_pool(struct prefork_pool *pfp, int new_max)
150 struct prefork_pool *pool;
151 size_t old_size;
152 size_t new_size;
153 int ret;
155 if (new_max <= pfp->pool_size) {
156 return EINVAL;
159 old_size = sizeof(struct pf_worker_data) * pfp->pool_size;
160 new_size = sizeof(struct pf_worker_data) * new_max;
162 pool = (struct prefork_pool *)anonymous_shared_resize(
163 &pfp->pool, new_size, false);
164 if (pool == NULL) {
165 ret = errno;
166 DEBUG(3, ("Failed to mremap memory (%d: %s)!\n",
167 ret, strerror(ret)));
168 return ret;
171 memset(&pool[pfp->pool_size], 0, new_size - old_size);
173 pfp->pool_size = new_max;
175 return 0;
178 int prefork_add_children(struct tevent_context *ev_ctx,
179 struct messaging_context *msg_ctx,
180 struct prefork_pool *pfp,
181 int num_children)
183 pid_t pid;
184 time_t now = time(NULL);
185 int ret;
186 int i, j;
188 for (i = 0, j = 0; i < pfp->pool_size && j < num_children; i++) {
190 if (pfp->pool[i].status != PF_WORKER_NONE) {
191 continue;
194 pfp->pool[i].allowed_clients = 1;
195 pfp->pool[i].started = now;
197 pid = fork();
198 switch (pid) {
199 case -1:
200 DEBUG(1, ("Failed to prefork child n. %d !\n", j));
201 break;
203 case 0: /* THE CHILD */
205 pfp->pool[i].status = PF_WORKER_ALIVE;
206 ret = pfp->main_fn(ev_ctx, msg_ctx,
207 &pfp->pool[i], i + 1,
208 pfp->listen_fd_size,
209 pfp->listen_fds,
210 pfp->private_data);
212 pfp->pool[i].status = PF_WORKER_EXITING;
213 exit(ret);
215 default: /* THE PARENT */
216 pfp->pool[i].pid = pid;
217 j++;
218 break;
222 DEBUG(5, ("Added %d children!\n", j));
224 return j;
227 struct prefork_oldest {
228 int num;
229 time_t started;
232 /* sort in inverse order */
233 static int prefork_sort_oldest(const void *ap, const void *bp)
235 const struct prefork_oldest *a = (const struct prefork_oldest *)ap;
236 const struct prefork_oldest *b = (const struct prefork_oldest *)bp;
238 if (a->started == b->started) {
239 return 0;
241 if (a->started < b->started) {
242 return 1;
244 return -1;
247 int prefork_retire_children(struct messaging_context *msg_ctx,
248 struct prefork_pool *pfp,
249 int num_children, time_t age_limit)
251 const DATA_BLOB ping = data_blob_null;
252 time_t now = time(NULL);
253 struct prefork_oldest *oldest;
254 int i, j;
256 oldest = talloc_array(pfp, struct prefork_oldest, pfp->pool_size);
257 if (!oldest) {
258 return -1;
261 for (i = 0; i < pfp->pool_size; i++) {
262 oldest[i].num = i;
263 if (pfp->pool[i].status == PF_WORKER_ALIVE ||
264 pfp->pool[i].status == PF_WORKER_ACCEPTING) {
265 oldest[i].started = pfp->pool[i].started;
266 } else {
267 oldest[i].started = now;
271 qsort(oldest, pfp->pool_size,
272 sizeof(struct prefork_oldest),
273 prefork_sort_oldest);
275 for (i = 0, j = 0; i < pfp->pool_size && j < num_children; i++) {
276 if (((pfp->pool[i].status == PF_WORKER_ALIVE) &&
277 (pfp->pool[i].num_clients < 1)) &&
278 (pfp->pool[i].started <= age_limit)) {
279 /* tell the child it's time to give up */
280 DEBUG(5, ("Retiring pid %u!\n", (unsigned int)pfp->pool[i].pid));
281 pfp->pool[i].cmds = PF_SRV_MSG_EXIT;
282 messaging_send(msg_ctx,
283 pid_to_procid(pfp->pool[i].pid),
284 MSG_PREFORK_PARENT_EVENT, &ping);
285 j++;
289 return j;
292 int prefork_count_children(struct prefork_pool *pfp, int *active)
294 int i, a, t;
296 a = 0;
297 t = 0;
298 for (i = 0; i < pfp->pool_size; i++) {
299 if (pfp->pool[i].status == PF_WORKER_NONE) {
300 continue;
303 t++;
305 if ((pfp->pool[i].status == PF_WORKER_EXITING) ||
306 (pfp->pool[i].num_clients <= 0)) {
307 continue;
310 a++;
313 if (active) {
314 *active = a;
316 return t;
319 static void prefork_cleanup_loop(struct prefork_pool *pfp)
321 int status;
322 pid_t pid;
323 int i;
325 /* TODO: should we use a process group id wait instead of looping ? */
326 for (i = 0; i < pfp->pool_size; i++) {
327 if (pfp->pool[i].status == PF_WORKER_NONE ||
328 pfp->pool[i].pid == 0) {
329 continue;
332 pid = waitpid(pfp->pool[i].pid, &status, WNOHANG);
333 if (pid > 0) {
335 if (pfp->pool[i].status != PF_WORKER_EXITING) {
336 DEBUG(3, ("Child (%d) terminated abnormally:"
337 " %d\n", (int)pid, status));
338 } else {
339 DEBUG(10, ("Child (%d) terminated with status:"
340 " %d\n", (int)pid, status));
343 /* reset all fields,
344 * this makes status = PF_WORK_NONE */
345 memset(&pfp->pool[i], 0,
346 sizeof(struct pf_worker_data));
352 int prefork_count_allowed_connections(struct prefork_pool *pfp)
354 int c;
355 int i;
357 c = 0;
358 for (i = 0; i < pfp->pool_size; i++) {
359 if (pfp->pool[i].status == PF_WORKER_NONE ||
360 pfp->pool[i].status == PF_WORKER_EXITING) {
361 continue;
364 if (pfp->pool[i].num_clients < 0) {
365 continue;
368 c += pfp->pool[i].allowed_clients - pfp->pool[i].num_clients;
371 return c;
374 void prefork_increase_allowed_clients(struct prefork_pool *pfp, int max)
376 int i;
378 for (i = 0; i < pfp->pool_size; i++) {
379 if (pfp->pool[i].status == PF_WORKER_NONE ||
380 pfp->pool[i].status == PF_WORKER_EXITING) {
381 continue;
384 if (pfp->pool[i].num_clients < 0) {
385 continue;
388 if (pfp->pool[i].allowed_clients < max) {
389 pfp->pool[i].allowed_clients++;
394 void prefork_decrease_allowed_clients(struct prefork_pool *pfp)
396 int i;
398 for (i = 0; i < pfp->pool_size; i++) {
399 if (pfp->pool[i].status == PF_WORKER_NONE ||
400 pfp->pool[i].status == PF_WORKER_EXITING) {
401 continue;
404 if (pfp->pool[i].num_clients < 0) {
405 continue;
408 if (pfp->pool[i].allowed_clients > 1) {
409 pfp->pool[i].allowed_clients--;
414 void prefork_reset_allowed_clients(struct prefork_pool *pfp)
416 int i;
418 for (i = 0; i < pfp->pool_size; i++) {
419 pfp->pool[i].allowed_clients = 1;
423 void prefork_send_signal_to_all(struct prefork_pool *pfp, int signal_num)
425 int i;
427 for (i = 0; i < pfp->pool_size; i++) {
428 if (pfp->pool[i].status == PF_WORKER_NONE) {
429 continue;
432 kill(pfp->pool[i].pid, signal_num);
436 void prefork_warn_active_children(struct messaging_context *msg_ctx,
437 struct prefork_pool *pfp)
439 const DATA_BLOB ping = data_blob_null;
440 int i;
442 for (i = 0; i < pfp->pool_size; i++) {
443 if (pfp->pool[i].status == PF_WORKER_NONE) {
444 continue;
447 messaging_send(msg_ctx,
448 pid_to_procid(pfp->pool[i].pid),
449 MSG_PREFORK_PARENT_EVENT, &ping);
453 static void prefork_sigchld_handler(struct tevent_context *ev_ctx,
454 struct tevent_signal *se,
455 int signum, int count,
456 void *siginfo, void *pvt)
458 struct prefork_pool *pfp;
460 pfp = talloc_get_type_abort(pvt, struct prefork_pool);
462 /* run the cleanup function to make sure all dead children are
463 * properly and timely retired. */
464 prefork_cleanup_loop(pfp);
466 if (pfp->sigchld_fn) {
467 pfp->sigchld_fn(ev_ctx, pfp, pfp->sigchld_data);
471 static bool prefork_setup_sigchld_handler(struct tevent_context *ev_ctx,
472 struct prefork_pool *pfp)
474 struct tevent_signal *se;
476 se = tevent_add_signal(ev_ctx, pfp, SIGCHLD, 0,
477 prefork_sigchld_handler, pfp);
478 if (!se) {
479 DEBUG(0, ("Failed to setup SIGCHLD handler!\n"));
480 return false;
483 return true;
486 void prefork_set_sigchld_callback(struct prefork_pool *pfp,
487 prefork_sigchld_fn_t *sigchld_fn,
488 void *private_data)
490 pfp->sigchld_fn = sigchld_fn;
491 pfp->sigchld_data = private_data;
494 /* ==== Functions used by children ==== */
496 struct pf_listen_state {
497 struct tevent_context *ev;
498 struct pf_worker_data *pf;
500 int listen_fd_size;
501 struct pf_listen_fd *listen_fds;
503 struct pf_listen_fd accept;
505 struct tsocket_address *srv_addr;
506 struct tsocket_address *cli_addr;
508 int error;
511 struct pf_listen_ctx {
512 TALLOC_CTX *fde_ctx;
513 struct tevent_req *req;
514 int listen_fd;
515 void *listen_fd_data;
518 static void prefork_listen_accept_handler(struct tevent_context *ev,
519 struct tevent_fd *fde,
520 uint16_t flags, void *pvt);
522 struct tevent_req *prefork_listen_send(TALLOC_CTX *mem_ctx,
523 struct tevent_context *ev,
524 struct pf_worker_data *pf,
525 int listen_fd_size,
526 struct pf_listen_fd *listen_fds)
528 struct tevent_req *req;
529 struct pf_listen_state *state;
530 struct pf_listen_ctx *ctx;
531 struct tevent_fd *fde;
532 TALLOC_CTX *fde_ctx;
533 int i;
535 req = tevent_req_create(mem_ctx, &state, struct pf_listen_state);
536 if (!req) {
537 return NULL;
540 state->ev = ev;
541 state->pf = pf;
542 state->listen_fd_size = listen_fd_size;
543 state->listen_fds = listen_fds;
544 state->accept.fd = -1;
545 state->accept.fd_data = NULL;
546 state->error = 0;
548 fde_ctx = talloc_new(state);
549 if (tevent_req_nomem(fde_ctx, req)) {
550 return tevent_req_post(req, ev);
553 /* race on accept */
554 for (i = 0; i < state->listen_fd_size; i++) {
555 ctx = talloc(fde_ctx, struct pf_listen_ctx);
556 if (tevent_req_nomem(ctx, req)) {
557 return tevent_req_post(req, ev);
559 ctx->fde_ctx = fde_ctx;
560 ctx->req = req;
561 ctx->listen_fd = state->listen_fds[i].fd;
562 ctx->listen_fd_data = state->listen_fds[i].fd_data;
564 fde = tevent_add_fd(state->ev, fde_ctx,
565 ctx->listen_fd, TEVENT_FD_READ,
566 prefork_listen_accept_handler, ctx);
567 if (tevent_req_nomem(fde, req)) {
568 return tevent_req_post(req, ev);
572 pf->status = PF_WORKER_ACCEPTING;
574 return req;
577 static void prefork_listen_accept_handler(struct tevent_context *ev,
578 struct tevent_fd *fde,
579 uint16_t flags, void *pvt)
581 struct pf_listen_state *state;
582 struct tevent_req *req;
583 struct pf_listen_ctx *ctx;
584 struct sockaddr_storage addr;
585 socklen_t addrlen;
586 int soerr = 0;
587 socklen_t solen = sizeof(soerr);
588 int sd = -1;
589 int ret;
591 ctx = talloc_get_type_abort(pvt, struct pf_listen_ctx);
592 req = ctx->req;
593 state = tevent_req_data(ctx->req, struct pf_listen_state);
595 if ((state->pf->cmds == PF_SRV_MSG_EXIT) &&
596 (state->pf->num_clients <= 0)) {
597 /* We have been asked to exit, so drop here and the next
598 * child will pick it up */
599 state->pf->status = PF_WORKER_EXITING;
600 state->error = EINTR;
601 goto done;
604 /* before proceeding check that the listening fd is ok */
605 ret = getsockopt(ctx->listen_fd, SOL_SOCKET, SO_ERROR, &soerr, &solen);
606 if (ret == -1) {
607 /* this is a fatal error, we cannot continue listening */
608 state->error = EBADF;
609 goto done;
611 if (soerr != 0) {
612 /* this is a fatal error, we cannot continue listening */
613 state->error = soerr;
614 goto done;
617 ZERO_STRUCT(addr);
618 addrlen = sizeof(addr);
619 sd = accept(ctx->listen_fd, (struct sockaddr *)&addr, &addrlen);
620 if (sd == -1) {
621 state->error = errno;
622 DEBUG(6, ("Accept failed! (%d, %s)\n",
623 state->error, strerror(state->error)));
624 goto done;
626 smb_set_close_on_exec(sd);
628 state->accept.fd = sd;
629 state->accept.fd_data = ctx->listen_fd_data;
631 ret = tsocket_address_bsd_from_sockaddr(state,
632 (struct sockaddr *)(void *)&addr,
633 addrlen, &state->cli_addr);
634 if (ret < 0) {
635 state->error = errno;
636 goto done;
639 ZERO_STRUCT(addr);
640 addrlen = sizeof(addr);
641 ret = getsockname(sd, (struct sockaddr *)(void *)&addr, &addrlen);
642 if (ret < 0) {
643 state->error = errno;
644 goto done;
647 ret = tsocket_address_bsd_from_sockaddr(state,
648 (struct sockaddr *)(void *)&addr,
649 addrlen, &state->srv_addr);
650 if (ret < 0) {
651 state->error = errno;
652 goto done;
655 done:
656 /* do not track the listen fds anymore */
657 talloc_free(ctx->fde_ctx);
658 tevent_req_done(req);
661 int prefork_listen_recv(struct tevent_req *req,
662 TALLOC_CTX *mem_ctx, int *fd,
663 void **fd_data,
664 struct tsocket_address **srv_addr,
665 struct tsocket_address **cli_addr)
667 struct pf_listen_state *state;
668 int ret = 0;
670 state = tevent_req_data(req, struct pf_listen_state);
672 if (state->error) {
673 ret = state->error;
674 } else {
675 if (!tevent_req_is_unix_error(req, &ret)) {
676 ret = 0;
680 if (ret) {
681 if (state->accept.fd != -1) {
682 close(state->accept.fd);
684 } else {
685 *fd = state->accept.fd;
686 if (fd_data != NULL) {
687 *fd_data = state->accept.fd_data;
689 *srv_addr = talloc_move(mem_ctx, &state->srv_addr);
690 *cli_addr = talloc_move(mem_ctx, &state->cli_addr);
691 state->pf->num_clients++;
693 if (state->pf->status == PF_WORKER_ACCEPTING) {
694 state->pf->status = PF_WORKER_ALIVE;
697 tevent_req_received(req);
698 return ret;