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/>.
22 #include "system/time.h"
23 #include "system/shmem.h"
24 #include "system/filesys.h"
25 #include "server_prefork.h"
26 #include "../lib/util/util.h"
27 #include "../lib/util/tevent_unix.h"
36 prefork_main_fn_t
*main_fn
;
40 struct pf_worker_data
*pool
;
45 int prefork_pool_destructor(struct prefork_pool
*pfp
)
47 munmap(pfp
->pool
, pfp
->pool_size
* sizeof(struct pf_worker_data
));
51 bool prefork_create_pool(struct tevent_context
*ev_ctx
, TALLOC_CTX
*mem_ctx
,
52 int listen_fd_size
, int *listen_fds
,
53 int min_children
, int max_children
,
54 prefork_main_fn_t
*main_fn
, void *private_data
,
55 struct prefork_pool
**pf_pool
)
57 struct prefork_pool
*pfp
;
59 time_t now
= time(NULL
);
64 pfp
= talloc(mem_ctx
, struct prefork_pool
);
66 DEBUG(1, ("Out of memory!\n"));
69 pfp
->listen_fd_size
= listen_fd_size
;
70 pfp
->listen_fds
= talloc_array(pfp
, int, listen_fd_size
);
71 if (!pfp
->listen_fds
) {
72 DEBUG(1, ("Out of memory!\n"));
75 for (i
= 0; i
< listen_fd_size
; i
++) {
76 pfp
->listen_fds
[i
] = listen_fds
[i
];
78 pfp
->main_fn
= main_fn
;
79 pfp
->private_data
= private_data
;
81 pfp
->lock_fd
= create_unlink_tmp(NULL
);
82 if (pfp
->lock_fd
== -1) {
83 DEBUG(1, ("Failed to create prefork lock fd!\n"));
88 pfp
->pool_size
= max_children
;
89 data_size
= sizeof(struct pf_worker_data
) * max_children
;
91 pfp
->pool
= mmap(NULL
, data_size
, PROT_READ
|PROT_WRITE
,
92 MAP_SHARED
|MAP_ANONYMOUS
, -1, 0);
93 if (pfp
->pool
== MAP_FAILED
) {
94 DEBUG(1, ("Failed to mmap memory for prefork pool!\n"));
98 talloc_set_destructor(pfp
, prefork_pool_destructor
);
100 for (i
= 0; i
< min_children
; i
++) {
102 pfp
->pool
[i
].allowed_clients
= 1;
103 pfp
->pool
[i
].started
= now
;
108 DEBUG(1, ("Failed to prefork child n. %d !\n", i
));
111 case 0: /* THE CHILD */
113 pfp
->pool
[i
].status
= PF_WORKER_IDLE
;
114 ret
= pfp
->main_fn(ev_ctx
, &pfp
->pool
[i
],
121 default: /* THE PARENT */
122 pfp
->pool
[i
].pid
= pid
;
131 /* Provide the new max children number in new_max
132 * (must be larger than current max).
133 * Returns: 0 if all fine
134 * ENOSPC if mremap fails to expand
135 * EINVAL if new_max is invalid
137 int prefork_expand_pool(struct prefork_pool
*pfp
, int new_max
)
139 struct pf_worker_data
*pool
;
143 if (new_max
<= pfp
->pool_size
) {
147 old_size
= sizeof(struct pf_worker_data
) * pfp
->pool_size
;
148 new_size
= sizeof(struct pf_worker_data
) * new_max
;
150 pool
= mremap(pfp
->pool
, old_size
, new_size
, 0);
151 if (pool
== MAP_FAILED
) {
152 DEBUG(3, ("Failed to mremap memory for prefork pool!\n"));
156 memset(&pool
[pfp
->pool_size
], 0, new_size
- old_size
);
158 pfp
->pool_size
= new_max
;
163 int prefork_add_children(struct tevent_context
*ev_ctx
,
164 struct prefork_pool
*pfp
,
168 time_t now
= time(NULL
);
172 for (i
= 0, j
= 0; i
< pfp
->pool_size
&& j
< num_children
; i
++) {
174 if (pfp
->pool
[i
].status
!= PF_WORKER_NONE
) {
178 pfp
->pool
[i
].allowed_clients
= 1;
179 pfp
->pool
[i
].started
= now
;
184 DEBUG(1, ("Failed to prefork child n. %d !\n", j
));
187 case 0: /* THE CHILD */
189 pfp
->pool
[i
].status
= PF_WORKER_IDLE
;
190 ret
= pfp
->main_fn(ev_ctx
, &pfp
->pool
[i
],
196 pfp
->pool
[i
].status
= PF_WORKER_EXITING
;
199 default: /* THE PARENT */
200 pfp
->pool
[i
].pid
= pid
;
206 DEBUG(5, ("Added %d children!\n", j
));
211 struct prefork_oldest
{
216 /* sort in inverse order */
217 static int prefork_sort_oldest(const void *ap
, const void *bp
)
219 struct prefork_oldest
*a
= (struct prefork_oldest
*)ap
;
220 struct prefork_oldest
*b
= (struct prefork_oldest
*)bp
;
222 if (a
->started
== b
->started
) {
225 if (a
->started
< b
->started
) {
231 int prefork_retire_children(struct prefork_pool
*pfp
,
232 int num_children
, time_t age_limit
)
234 time_t now
= time(NULL
);
235 struct prefork_oldest
*oldest
;
238 oldest
= talloc_array(pfp
, struct prefork_oldest
, pfp
->pool_size
);
243 for (i
= 0; i
< pfp
->pool_size
; i
++) {
245 if (pfp
->pool
[i
].status
== PF_WORKER_IDLE
) {
246 oldest
[i
].started
= pfp
->pool
[i
].started
;
248 oldest
[i
].started
= now
;
252 qsort(oldest
, pfp
->pool_size
,
253 sizeof(struct prefork_oldest
),
254 prefork_sort_oldest
);
256 for (i
= 0, j
= 0; i
< pfp
->pool_size
&& j
< num_children
; i
++) {
257 if (pfp
->pool
[i
].status
== PF_WORKER_IDLE
&&
258 pfp
->pool
[i
].started
<= age_limit
) {
259 /* tell the child it's time to give up */
260 DEBUG(5, ("Retiring pid %d!\n", pfp
->pool
[i
].pid
));
261 pfp
->pool
[i
].cmds
= PF_SRV_MSG_EXIT
;
262 kill(pfp
->pool
[i
].pid
, SIGHUP
);
270 int prefork_count_active_children(struct prefork_pool
*pfp
, int *total
)
276 for (i
= 0; i
< pfp
->pool_size
; i
++) {
277 if (pfp
->pool
[i
].status
== PF_WORKER_NONE
) {
283 if (pfp
->pool
[i
].num_clients
== 0) {
294 /* to be used to finally mark a children as dead, so that it's slot can
296 bool prefork_mark_pid_dead(struct prefork_pool
*pfp
, pid_t pid
)
300 for (i
= 0; i
< pfp
->pool_size
; i
++) {
301 if (pfp
->pool
[i
].pid
== pid
) {
302 if (pfp
->pool
[i
].status
!= PF_WORKER_EXITING
) {
303 DEBUG(2, ("pid %d terminated abnormally!\n",
308 * this makes status = PF_WORK_NONE */
309 memset(&pfp
->pool
[i
], 0,
310 sizeof(struct pf_worker_data
));
319 void prefork_increase_allowed_clients(struct prefork_pool
*pfp
, int max
)
323 for (i
= 0; i
< pfp
->pool_size
; i
++) {
324 if (pfp
->pool
[i
].status
== PF_WORKER_NONE
) {
328 if (pfp
->pool
[i
].allowed_clients
< max
) {
329 pfp
->pool
[i
].allowed_clients
++;
334 void prefork_reset_allowed_clients(struct prefork_pool
*pfp
)
338 for (i
= 0; i
< pfp
->pool_size
; i
++) {
339 pfp
->pool
[i
].allowed_clients
= 1;
343 void prefork_send_signal_to_all(struct prefork_pool
*pfp
, int signal_num
)
347 for (i
= 0; i
< pfp
->pool_size
; i
++) {
348 if (pfp
->pool
[i
].status
== PF_WORKER_NONE
) {
352 kill(pfp
->pool
[i
].pid
, signal_num
);
357 /* ==== Functions used by children ==== */
359 static SIG_ATOMIC_T pf_alarm
;
361 static void pf_alarm_cb(int signum
)
369 * pf - the worker shared data structure
370 * lock_fd - the file descriptor used for locking
371 * timeout - expressed in seconds:
373 * 0 timeouts immediately
374 * N seconds before timing out
377 * negative errno on fatal error
378 * 0 on success to acquire lock
379 * -1 on timeout/lock held by other
380 * -2 on server msg to terminate
381 * ERRNO on other errors
384 static int prefork_grab_lock(struct pf_worker_data
*pf
,
385 int lock_fd
, int timeout
)
391 if (pf
->cmds
== PF_SRV_MSG_EXIT
) {
398 CatchSignal(SIGALRM
, pf_alarm_cb
);
411 lock
.l_type
= F_WRLCK
;
412 lock
.l_whence
= SEEK_SET
;
414 ret
= fcntl(lock_fd
, op
, &lock
);
419 if (pf
->cmds
== PF_SRV_MSG_EXIT
) {
430 /* lock held by other proc */
442 } while (timeout
!= 0);
445 /* We have the Lock */
446 pf
->status
= PF_WORKER_ACCEPTING
;
452 CatchSignal(SIGALRM
, SIG_IGN
);
456 DEBUG(1, ("Failed to get lock (%d, %s)!\n",
457 ret
, strerror(ret
)));
464 * pf - the worker shared data structure
465 * lock_fd - the file descriptor used for locking
466 * timeout - expressed in seconds:
468 * 0 timeouts immediately
469 * N seconds before timing out
472 * negative errno on fatal error
473 * 0 on success to release lock
478 static int prefork_release_lock(struct pf_worker_data
*pf
,
479 int lock_fd
, int timeout
)
488 CatchSignal(SIGALRM
, pf_alarm_cb
);
500 lock
.l_type
= F_UNLCK
;
501 lock
.l_whence
= SEEK_SET
;
503 ret
= fcntl(lock_fd
, op
, &lock
);
517 } while (timeout
!= 0);
522 CatchSignal(SIGALRM
, SIG_IGN
);
526 DEBUG(1, ("Failed to release lock (%d, %s)!\n",
527 ret
, strerror(ret
)));
532 /* ==== async code ==== */
534 #define PF_ASYNC_LOCK_GRAB 0x01
535 #define PF_ASYNC_LOCK_RELEASE 0x02
536 #define PF_ASYNC_ACTION_MASK 0x03
537 #define PF_ASYNC_LOCK_DONE 0x04
539 struct pf_lock_state
{
540 struct pf_worker_data
*pf
;
545 static void prefork_lock_handler(struct tevent_context
*ev
,
546 struct tevent_timer
*te
,
547 struct timeval curtime
, void *pvt
);
549 static struct tevent_req
*prefork_lock_send(TALLOC_CTX
*mem_ctx
,
550 struct tevent_context
*ev
,
551 struct pf_worker_data
*pf
,
552 int lock_fd
, int action
)
554 struct tevent_req
*req
;
555 struct pf_lock_state
*state
;
557 req
= tevent_req_create(mem_ctx
, &state
, struct pf_lock_state
);
563 state
->lock_fd
= lock_fd
;
564 state
->flags
= action
;
566 /* try once immediately */
567 prefork_lock_handler(ev
, NULL
, tevent_timeval_zero(), req
);
568 if (state
->flags
& PF_ASYNC_LOCK_DONE
) {
569 tevent_req_post(req
, ev
);
575 static void prefork_lock_handler(struct tevent_context
*ev
,
576 struct tevent_timer
*te
,
577 struct timeval curtime
, void *pvt
)
579 struct tevent_req
*req
;
580 struct pf_lock_state
*state
;
583 req
= talloc_get_type_abort(pvt
, struct tevent_req
);
584 state
= tevent_req_data(req
, struct pf_lock_state
);
586 switch (state
->flags
& PF_ASYNC_ACTION_MASK
) {
587 case PF_ASYNC_LOCK_GRAB
:
588 ret
= prefork_grab_lock(state
->pf
, state
->lock_fd
, 0);
590 case PF_ASYNC_LOCK_RELEASE
:
591 ret
= prefork_release_lock(state
->pf
, state
->lock_fd
, 0);
600 state
->flags
|= PF_ASYNC_LOCK_DONE
;
601 tevent_req_done(req
);
604 te
= tevent_add_timer(ev
, state
,
605 tevent_timeval_current_ofs(1, 0),
606 prefork_lock_handler
, req
);
607 tevent_req_nomem(te
, req
);
610 /* server tells us to stop */
611 state
->flags
|= PF_ASYNC_LOCK_DONE
;
612 tevent_req_error(req
, -2);
615 state
->flags
|= PF_ASYNC_LOCK_DONE
;
616 tevent_req_error(req
, ret
);
621 static int prefork_lock_recv(struct tevent_req
*req
)
625 if (!tevent_req_is_unix_error(req
, &ret
)) {
629 tevent_req_received(req
);
633 struct pf_listen_state
{
634 struct tevent_context
*ev
;
635 struct pf_worker_data
*pf
;
642 struct sockaddr
*addr
;
650 static void prefork_listen_lock_done(struct tevent_req
*subreq
);
651 static void prefork_listen_accept_handler(struct tevent_context
*ev
,
652 struct tevent_fd
*fde
,
653 uint16_t flags
, void *pvt
);
654 static void prefork_listen_release_done(struct tevent_req
*subreq
);
656 struct tevent_req
*prefork_listen_send(TALLOC_CTX
*mem_ctx
,
657 struct tevent_context
*ev
,
658 struct pf_worker_data
*pf
,
662 struct sockaddr
*addr
,
665 struct tevent_req
*req
, *subreq
;
666 struct pf_listen_state
*state
;
668 req
= tevent_req_create(mem_ctx
, &state
, struct pf_listen_state
);
675 state
->lock_fd
= lock_fd
;
676 state
->listen_fd_size
= listen_fd_size
;
677 state
->listen_fds
= listen_fds
;
679 state
->addrlen
= addrlen
;
680 state
->accept_fd
= -1;
683 subreq
= prefork_lock_send(state
, state
->ev
, state
->pf
,
684 state
->lock_fd
, PF_ASYNC_LOCK_GRAB
);
685 if (tevent_req_nomem(subreq
, req
)) {
686 return tevent_req_post(req
, ev
);
689 tevent_req_set_callback(subreq
, prefork_listen_lock_done
, req
);
693 struct pf_listen_ctx
{
695 struct tevent_req
*req
;
699 static void prefork_listen_lock_done(struct tevent_req
*subreq
)
701 struct tevent_req
*req
;
702 struct pf_listen_state
*state
;
703 struct pf_listen_ctx
*ctx
;
704 struct tevent_fd
*fde
;
709 req
= tevent_req_callback_data(subreq
, struct tevent_req
);
710 state
= tevent_req_data(req
, struct pf_listen_state
);
712 ret
= prefork_lock_recv(subreq
);
714 tevent_req_error(req
, ret
);
718 fde_ctx
= talloc_new(state
);
719 if (tevent_req_nomem(fde_ctx
, req
)) {
723 /* next step, accept */
724 for (i
= 0; i
< state
->listen_fd_size
; i
++) {
725 ctx
= talloc(fde_ctx
, struct pf_listen_ctx
);
726 if (tevent_req_nomem(ctx
, req
)) {
729 ctx
->fde_ctx
= fde_ctx
;
731 ctx
->listen_fd
= state
->listen_fds
[i
];
733 fde
= tevent_add_fd(state
->ev
, fde_ctx
,
734 ctx
->listen_fd
, TEVENT_FD_READ
,
735 prefork_listen_accept_handler
, ctx
);
736 if (tevent_req_nomem(fde
, req
)) {
742 static void prefork_listen_accept_handler(struct tevent_context
*ev
,
743 struct tevent_fd
*fde
,
744 uint16_t flags
, void *pvt
)
746 struct pf_listen_state
*state
;
747 struct tevent_req
*req
, *subreq
;
748 struct pf_listen_ctx
*ctx
;
752 ctx
= talloc_get_type_abort(pvt
, struct pf_listen_ctx
);
753 state
= tevent_req_data(ctx
->req
, struct pf_listen_state
);
755 sd
= accept(ctx
->listen_fd
, state
->addr
, state
->addrlen
);
757 if (errno
== EINTR
) {
762 DEBUG(6, ("Accept failed! (%d, %s)\n", err
, strerror(err
)));
766 /* do not track the listen fds anymore */
768 talloc_free(ctx
->fde_ctx
);
771 tevent_req_error(req
, err
);
775 state
->accept_fd
= sd
;
777 /* release lock now */
778 subreq
= prefork_lock_send(state
, state
->ev
, state
->pf
,
779 state
->lock_fd
, PF_ASYNC_LOCK_RELEASE
);
780 if (tevent_req_nomem(subreq
, req
)) {
783 tevent_req_set_callback(subreq
, prefork_listen_release_done
, req
);
786 static void prefork_listen_release_done(struct tevent_req
*subreq
)
788 struct tevent_req
*req
;
791 req
= tevent_req_callback_data(subreq
, struct tevent_req
);
793 ret
= prefork_lock_recv(subreq
);
795 tevent_req_error(req
, ret
);
799 tevent_req_done(req
);
802 int prefork_listen_recv(struct tevent_req
*req
, int *fd
)
804 struct pf_listen_state
*state
;
807 state
= tevent_req_data(req
, struct pf_listen_state
);
809 if (tevent_req_is_unix_error(req
, &ret
)) {
810 if (state
->accept_fd
!= -1) {
811 close(state
->accept_fd
);
814 *fd
= state
->accept_fd
;
816 state
->pf
->status
= PF_WORKER_BUSY
;
817 state
->pf
->num_clients
++;
820 tevent_req_received(req
);