python-s4: use PyErr_SetNTSTATUS instead of PyErr_NTSTATUS_IS_ERR_RAISE as it use...
[Samba/gebeck_regimport.git] / source3 / lib / server_prefork.c
bloba241e24798e6bb8beaab2e34684486781523f698
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/util.h"
29 #include "../lib/util/tevent_unix.h"
31 struct prefork_pool {
33 int listen_fd_size;
34 int *listen_fds;
36 prefork_main_fn_t *main_fn;
37 void *private_data;
39 int pool_size;
40 struct pf_worker_data *pool;
42 int allowed_clients;
44 prefork_sigchld_fn_t *sigchld_fn;
45 void *sigchld_data;
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);
54 return 0;
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;
66 pid_t pid;
67 time_t now = time(NULL);
68 size_t data_size;
69 int ret;
70 int i;
71 bool ok;
73 pfp = talloc_zero(mem_ctx, struct prefork_pool);
74 if (!pfp) {
75 DEBUG(1, ("Out of memory!\n"));
76 return false;
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"));
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], 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 = anonymous_shared_allocate(data_size);
96 if (pfp->pool == NULL) {
97 DEBUG(1, ("Failed to mmap memory for prefork pool!\n"));
98 talloc_free(pfp);
99 return false;
101 talloc_set_destructor(pfp, prefork_pool_destructor);
103 for (i = 0; i < min_children; i++) {
105 pfp->pool[i].allowed_clients = 1;
106 pfp->pool[i].started = now;
108 pid = sys_fork();
109 switch (pid) {
110 case -1:
111 DEBUG(1, ("Failed to prefork child n. %d !\n", i));
112 break;
114 case 0: /* THE CHILD */
116 pfp->pool[i].status = PF_WORKER_ALIVE;
117 ret = pfp->main_fn(ev_ctx, msg_ctx,
118 &pfp->pool[i], i + 1,
119 pfp->listen_fd_size,
120 pfp->listen_fds,
121 pfp->private_data);
122 exit(ret);
124 default: /* THE PARENT */
125 pfp->pool[i].pid = pid;
126 break;
130 ok = prefork_setup_sigchld_handler(ev_ctx, pfp);
131 if (!ok) {
132 DEBUG(1, ("Failed to setup SIGCHLD Handler!\n"));
133 talloc_free(pfp);
134 return false;
137 *pf_pool = pfp;
138 return true;
141 /* Provide the new max children number in new_max
142 * (must be larger than current max).
143 * Returns: 0 if all fine
144 * ENOSPC if mremap fails to expand
145 * EINVAL if new_max is invalid
147 int prefork_expand_pool(struct prefork_pool *pfp, int new_max)
149 struct prefork_pool *pool;
150 size_t old_size;
151 size_t new_size;
152 int ret;
154 if (new_max <= pfp->pool_size) {
155 return EINVAL;
158 old_size = sizeof(struct pf_worker_data) * pfp->pool_size;
159 new_size = sizeof(struct pf_worker_data) * new_max;
161 pool = anonymous_shared_resize(&pfp->pool, new_size, false);
162 if (pool == NULL) {
163 ret = errno;
164 DEBUG(3, ("Failed to mremap memory (%d: %s)!\n",
165 ret, strerror(ret)));
166 return ret;
169 memset(&pool[pfp->pool_size], 0, new_size - old_size);
171 pfp->pool_size = new_max;
173 return 0;
176 int prefork_add_children(struct tevent_context *ev_ctx,
177 struct messaging_context *msg_ctx,
178 struct prefork_pool *pfp,
179 int num_children)
181 pid_t pid;
182 time_t now = time(NULL);
183 int ret;
184 int i, j;
186 for (i = 0, j = 0; i < pfp->pool_size && j < num_children; i++) {
188 if (pfp->pool[i].status != PF_WORKER_NONE) {
189 continue;
192 pfp->pool[i].allowed_clients = 1;
193 pfp->pool[i].started = now;
195 pid = sys_fork();
196 switch (pid) {
197 case -1:
198 DEBUG(1, ("Failed to prefork child n. %d !\n", j));
199 break;
201 case 0: /* THE CHILD */
203 pfp->pool[i].status = PF_WORKER_ALIVE;
204 ret = pfp->main_fn(ev_ctx, msg_ctx,
205 &pfp->pool[i], i + 1,
206 pfp->listen_fd_size,
207 pfp->listen_fds,
208 pfp->private_data);
210 pfp->pool[i].status = PF_WORKER_EXITING;
211 exit(ret);
213 default: /* THE PARENT */
214 pfp->pool[i].pid = pid;
215 j++;
216 break;
220 DEBUG(5, ("Added %d children!\n", j));
222 return j;
225 struct prefork_oldest {
226 int num;
227 time_t started;
230 /* sort in inverse order */
231 static int prefork_sort_oldest(const void *ap, const void *bp)
233 const struct prefork_oldest *a = (const struct prefork_oldest *)ap;
234 const struct prefork_oldest *b = (const struct prefork_oldest *)bp;
236 if (a->started == b->started) {
237 return 0;
239 if (a->started < b->started) {
240 return 1;
242 return -1;
245 int prefork_retire_children(struct messaging_context *msg_ctx,
246 struct prefork_pool *pfp,
247 int num_children, time_t age_limit)
249 const DATA_BLOB ping = data_blob_null;
250 time_t now = time(NULL);
251 struct prefork_oldest *oldest;
252 int i, j;
254 oldest = talloc_array(pfp, struct prefork_oldest, pfp->pool_size);
255 if (!oldest) {
256 return -1;
259 for (i = 0; i < pfp->pool_size; i++) {
260 oldest[i].num = i;
261 if (pfp->pool[i].status == PF_WORKER_ALIVE ||
262 pfp->pool[i].status == PF_WORKER_ACCEPTING) {
263 oldest[i].started = pfp->pool[i].started;
264 } else {
265 oldest[i].started = now;
269 qsort(oldest, pfp->pool_size,
270 sizeof(struct prefork_oldest),
271 prefork_sort_oldest);
273 for (i = 0, j = 0; i < pfp->pool_size && j < num_children; i++) {
274 if (((pfp->pool[i].status == PF_WORKER_ALIVE) &&
275 (pfp->pool[i].num_clients < 1)) &&
276 (pfp->pool[i].started <= age_limit)) {
277 /* tell the child it's time to give up */
278 DEBUG(5, ("Retiring pid %d!\n", pfp->pool[i].pid));
279 pfp->pool[i].cmds = PF_SRV_MSG_EXIT;
280 messaging_send(msg_ctx,
281 pid_to_procid(pfp->pool[i].pid),
282 MSG_PREFORK_PARENT_EVENT, &ping);
283 j++;
287 return j;
290 int prefork_count_children(struct prefork_pool *pfp, int *active)
292 int i, a, t;
294 a = 0;
295 t = 0;
296 for (i = 0; i < pfp->pool_size; i++) {
297 if (pfp->pool[i].status == PF_WORKER_NONE) {
298 continue;
301 t++;
303 if ((pfp->pool[i].status == PF_WORKER_EXITING) ||
304 (pfp->pool[i].num_clients <= 0)) {
305 continue;
308 a++;
311 if (active) {
312 *active = a;
314 return t;
317 static void prefork_cleanup_loop(struct prefork_pool *pfp)
319 int status;
320 pid_t pid;
321 int i;
323 /* TODO: should we use a process group id wait instead of looping ? */
324 for (i = 0; i < pfp->pool_size; i++) {
325 if (pfp->pool[i].status == PF_WORKER_NONE ||
326 pfp->pool[i].pid == 0) {
327 continue;
330 pid = sys_waitpid(pfp->pool[i].pid, &status, WNOHANG);
331 if (pid > 0) {
333 if (pfp->pool[i].status != PF_WORKER_EXITING) {
334 DEBUG(3, ("Child (%d) terminated abnormally:"
335 " %d\n", (int)pid, status));
336 } else {
337 DEBUG(10, ("Child (%d) terminated with status:"
338 " %d\n", (int)pid, status));
341 /* reset all fields,
342 * this makes status = PF_WORK_NONE */
343 memset(&pfp->pool[i], 0,
344 sizeof(struct pf_worker_data));
350 int prefork_count_allowed_connections(struct prefork_pool *pfp)
352 int c;
353 int i;
355 c = 0;
356 for (i = 0; i < pfp->pool_size; i++) {
357 if (pfp->pool[i].status == PF_WORKER_NONE ||
358 pfp->pool[i].status == PF_WORKER_EXITING) {
359 continue;
362 if (pfp->pool[i].num_clients < 0) {
363 continue;
366 c += pfp->pool[i].allowed_clients - pfp->pool[i].num_clients;
369 return c;
372 void prefork_increase_allowed_clients(struct prefork_pool *pfp, int max)
374 int i;
376 for (i = 0; i < pfp->pool_size; i++) {
377 if (pfp->pool[i].status == PF_WORKER_NONE ||
378 pfp->pool[i].status == PF_WORKER_EXITING) {
379 continue;
382 if (pfp->pool[i].num_clients < 0) {
383 continue;
386 if (pfp->pool[i].allowed_clients < max) {
387 pfp->pool[i].allowed_clients++;
392 void prefork_decrease_allowed_clients(struct prefork_pool *pfp)
394 int i;
396 for (i = 0; i < pfp->pool_size; i++) {
397 if (pfp->pool[i].status == PF_WORKER_NONE ||
398 pfp->pool[i].status == PF_WORKER_EXITING) {
399 continue;
402 if (pfp->pool[i].num_clients < 0) {
403 continue;
406 if (pfp->pool[i].allowed_clients > 1) {
407 pfp->pool[i].allowed_clients--;
412 void prefork_reset_allowed_clients(struct prefork_pool *pfp)
414 int i;
416 for (i = 0; i < pfp->pool_size; i++) {
417 pfp->pool[i].allowed_clients = 1;
421 void prefork_send_signal_to_all(struct prefork_pool *pfp, int signal_num)
423 int i;
425 for (i = 0; i < pfp->pool_size; i++) {
426 if (pfp->pool[i].status == PF_WORKER_NONE) {
427 continue;
430 kill(pfp->pool[i].pid, signal_num);
434 void prefork_warn_active_children(struct messaging_context *msg_ctx,
435 struct prefork_pool *pfp)
437 const DATA_BLOB ping = data_blob_null;
438 int i;
440 for (i = 0; i < pfp->pool_size; i++) {
441 if (pfp->pool[i].status == PF_WORKER_NONE) {
442 continue;
445 messaging_send(msg_ctx,
446 pid_to_procid(pfp->pool[i].pid),
447 MSG_PREFORK_PARENT_EVENT, &ping);
451 static void prefork_sigchld_handler(struct tevent_context *ev_ctx,
452 struct tevent_signal *se,
453 int signum, int count,
454 void *siginfo, void *pvt)
456 struct prefork_pool *pfp;
458 pfp = talloc_get_type_abort(pvt, struct prefork_pool);
460 /* run the cleanup function to make sure all dead children are
461 * properly and timely retired. */
462 prefork_cleanup_loop(pfp);
464 if (pfp->sigchld_fn) {
465 pfp->sigchld_fn(ev_ctx, pfp, pfp->sigchld_data);
469 static bool prefork_setup_sigchld_handler(struct tevent_context *ev_ctx,
470 struct prefork_pool *pfp)
472 struct tevent_signal *se;
474 se = tevent_add_signal(ev_ctx, pfp, SIGCHLD, 0,
475 prefork_sigchld_handler, pfp);
476 if (!se) {
477 DEBUG(0, ("Failed to setup SIGCHLD handler!\n"));
478 return false;
481 return true;
484 void prefork_set_sigchld_callback(struct prefork_pool *pfp,
485 prefork_sigchld_fn_t *sigchld_fn,
486 void *private_data)
488 pfp->sigchld_fn = sigchld_fn;
489 pfp->sigchld_data = private_data;
492 /* ==== Functions used by children ==== */
494 struct pf_listen_state {
495 struct tevent_context *ev;
496 struct pf_worker_data *pf;
498 int listen_fd_size;
499 int *listen_fds;
501 int accept_fd;
503 struct tsocket_address *srv_addr;
504 struct tsocket_address *cli_addr;
506 int error;
509 struct pf_listen_ctx {
510 TALLOC_CTX *fde_ctx;
511 struct tevent_req *req;
512 int listen_fd;
515 static void prefork_listen_accept_handler(struct tevent_context *ev,
516 struct tevent_fd *fde,
517 uint16_t flags, void *pvt);
519 struct tevent_req *prefork_listen_send(TALLOC_CTX *mem_ctx,
520 struct tevent_context *ev,
521 struct pf_worker_data *pf,
522 int listen_fd_size,
523 int *listen_fds)
525 struct tevent_req *req;
526 struct pf_listen_state *state;
527 struct pf_listen_ctx *ctx;
528 struct tevent_fd *fde;
529 TALLOC_CTX *fde_ctx;
530 int i;
532 req = tevent_req_create(mem_ctx, &state, struct pf_listen_state);
533 if (!req) {
534 return NULL;
537 state->ev = ev;
538 state->pf = pf;
539 state->listen_fd_size = listen_fd_size;
540 state->listen_fds = listen_fds;
541 state->accept_fd = -1;
542 state->error = 0;
544 fde_ctx = talloc_new(state);
545 if (tevent_req_nomem(fde_ctx, req)) {
546 return tevent_req_post(req, ev);
549 /* race on accept */
550 for (i = 0; i < state->listen_fd_size; i++) {
551 ctx = talloc(fde_ctx, struct pf_listen_ctx);
552 if (tevent_req_nomem(ctx, req)) {
553 return tevent_req_post(req, ev);
555 ctx->fde_ctx = fde_ctx;
556 ctx->req = req;
557 ctx->listen_fd = state->listen_fds[i];
559 fde = tevent_add_fd(state->ev, fde_ctx,
560 ctx->listen_fd, TEVENT_FD_READ,
561 prefork_listen_accept_handler, ctx);
562 if (tevent_req_nomem(fde, req)) {
563 return tevent_req_post(req, ev);
567 pf->status = PF_WORKER_ACCEPTING;
569 return req;
572 static void prefork_listen_accept_handler(struct tevent_context *ev,
573 struct tevent_fd *fde,
574 uint16_t flags, void *pvt)
576 struct pf_listen_state *state;
577 struct tevent_req *req;
578 struct pf_listen_ctx *ctx;
579 struct sockaddr_storage addr;
580 socklen_t addrlen;
581 int soerr = 0;
582 socklen_t solen = sizeof(soerr);
583 int sd = -1;
584 int ret;
586 ctx = talloc_get_type_abort(pvt, struct pf_listen_ctx);
587 req = ctx->req;
588 state = tevent_req_data(ctx->req, struct pf_listen_state);
590 if ((state->pf->cmds == PF_SRV_MSG_EXIT) &&
591 (state->pf->num_clients <= 0)) {
592 /* We have been asked to exit, so drop here and the next
593 * child will pick it up */
594 state->pf->status = PF_WORKER_EXITING;
595 state->error = EINTR;
596 goto done;
599 /* before proceeding check that the listening fd is ok */
600 ret = getsockopt(ctx->listen_fd, SOL_SOCKET, SO_ERROR, &soerr, &solen);
601 if (ret == -1) {
602 /* this is a fatal error, we cannot continue listening */
603 state->error = EBADF;
604 goto done;
606 if (soerr != 0) {
607 /* this is a fatal error, we cannot continue listening */
608 state->error = soerr;
609 goto done;
612 ZERO_STRUCT(addr);
613 addrlen = sizeof(addr);
614 sd = accept(ctx->listen_fd, (struct sockaddr *)&addr, &addrlen);
615 if (sd == -1) {
616 state->error = errno;
617 DEBUG(6, ("Accept failed! (%d, %s)\n",
618 state->error, strerror(state->error)));
619 goto done;
622 state->accept_fd = sd;
624 ret = tsocket_address_bsd_from_sockaddr(state,
625 (struct sockaddr *)(void *)&addr,
626 addrlen, &state->cli_addr);
627 if (ret < 0) {
628 state->error = errno;
629 goto done;
632 ZERO_STRUCT(addr);
633 addrlen = sizeof(addr);
634 ret = getsockname(sd, (struct sockaddr *)(void *)&addr, &addrlen);
635 if (ret < 0) {
636 state->error = errno;
637 goto done;
640 ret = tsocket_address_bsd_from_sockaddr(state,
641 (struct sockaddr *)(void *)&addr,
642 addrlen, &state->srv_addr);
643 if (ret < 0) {
644 state->error = errno;
645 goto done;
648 done:
649 /* do not track the listen fds anymore */
650 talloc_free(ctx->fde_ctx);
651 tevent_req_done(req);
654 int prefork_listen_recv(struct tevent_req *req,
655 TALLOC_CTX *mem_ctx, int *fd,
656 struct tsocket_address **srv_addr,
657 struct tsocket_address **cli_addr)
659 struct pf_listen_state *state;
660 int ret = 0;
662 state = tevent_req_data(req, struct pf_listen_state);
664 if (state->error) {
665 ret = state->error;
666 } else {
667 tevent_req_is_unix_error(req, &ret);
670 if (ret) {
671 if (state->accept_fd != -1) {
672 close(state->accept_fd);
674 } else {
675 *fd = state->accept_fd;
676 *srv_addr = talloc_move(mem_ctx, &state->srv_addr);
677 *cli_addr = talloc_move(mem_ctx, &state->cli_addr);
678 state->pf->num_clients++;
680 if (state->pf->status == PF_WORKER_ACCEPTING) {
681 state->pf->status = PF_WORKER_ALIVE;
684 tevent_req_received(req);
685 return ret;