smbd: Remove an unnecessary if-statement
[Samba.git] / source4 / samba / process_prefork.c
blob35145c7403d0bfa82d00a5d5641f73cc2ccb2d15
1 /*
2 Unix SMB/CIFS implementation.
4 process model: prefork (n client connections per process)
6 Copyright (C) Andrew Tridgell 1992-2005
7 Copyright (C) James J Myers 2003 <myersjj@samba.org>
8 Copyright (C) Stefan (metze) Metzmacher 2004
9 Copyright (C) Andrew Bartlett 2008 <abartlet@samba.org>
10 Copyright (C) David Disseldorp 2008 <ddiss@sgi.com>
12 This program is free software; you can redistribute it and/or modify
13 it under the terms of the GNU General Public License as published by
14 the Free Software Foundation; either version 3 of the License, or
15 (at your option) any later version.
17 This program is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 GNU General Public License for more details.
22 You should have received a copy of the GNU General Public License
23 along with this program. If not, see <http://www.gnu.org/licenses/>.
26 * The pre-fork process model distributes the server workload amongst several
27 * designated worker threads (e.g. 'prefork-worker-ldap-0',
28 * 'prefork-worker-ldap-1', etc). The number of worker threads is controlled
29 * by the 'prefork children' conf setting. The worker threads are controlled
30 * by a prefork master process (e.g. 'prefork-master-ldap'). The prefork master
31 * doesn't handle the server workload (i.e. processing messages) itself, but is
32 * responsible for restarting workers if they exit unexpectedly. The top-level
33 * samba process is responsible for restarting the master process if it exits.
35 #include "includes.h"
36 #include <unistd.h>
38 #include "lib/events/events.h"
39 #include "lib/messaging/messaging.h"
40 #include "lib/socket/socket.h"
41 #include "samba/process_model.h"
42 #include "cluster/cluster.h"
43 #include "param/param.h"
44 #include "ldb_wrap.h"
45 #include "lib/util/tfork.h"
46 #include "lib/messaging/irpc.h"
47 #include "lib/util/util_process.h"
48 #include "server_util.h"
50 #define min(a, b) (((a) < (b)) ? (a) : (b))
52 NTSTATUS process_model_prefork_init(void);
53 static void prefork_new_task(
54 struct tevent_context *ev,
55 struct loadparm_context *lp_ctx,
56 const char *service_name,
57 struct task_server *(*new_task_fn)(struct tevent_context *,
58 struct loadparm_context *lp_ctx,
59 struct server_id,
60 void *,
61 void *),
62 void *private_data,
63 const struct service_details *service_details,
64 int from_parent_fd);
65 static void prefork_fork_worker(struct task_server *task,
66 struct tevent_context *ev,
67 struct tevent_context *ev2,
68 struct loadparm_context *lp_ctx,
69 const struct service_details *service_details,
70 const char *service_name,
71 int control_pipe[2],
72 unsigned restart_delay,
73 struct process_details *pd);
74 static void prefork_child_pipe_handler(struct tevent_context *ev,
75 struct tevent_fd *fde,
76 uint16_t flags,
77 void *private_data);
78 static void setup_handlers(struct tevent_context *ev,
79 struct loadparm_context *lp_ctx,
80 int from_parent_fd);
83 * State needed to restart the master process or a worker process if they
84 * terminate early.
86 struct master_restart_context {
87 struct task_server *(*new_task_fn)(struct tevent_context *,
88 struct loadparm_context *lp_ctx,
89 struct server_id,
90 void *,
91 void *);
92 void *private_data;
95 struct worker_restart_context {
96 unsigned int instance;
97 struct task_server *task;
98 struct tevent_context *ev2;
99 int control_pipe[2];
102 struct restart_context {
103 struct loadparm_context *lp_ctx;
104 struct tfork *t;
105 int from_parent_fd;
106 const struct service_details *service_details;
107 const char *service_name;
108 unsigned restart_delay;
109 struct master_restart_context *master;
110 struct worker_restart_context *worker;
113 static void sighup_signal_handler(struct tevent_context *ev,
114 struct tevent_signal *se,
115 int signum, int count, void *siginfo,
116 void *private_data)
118 reopen_logs_internal();
121 static void sigterm_signal_handler(struct tevent_context *ev,
122 struct tevent_signal *se,
123 int signum, int count, void *siginfo,
124 void *private_data)
126 #ifdef HAVE_GETPGRP
127 if (getpgrp() == getpid()) {
129 * We're the process group leader, send
130 * SIGTERM to our process group.
132 DBG_NOTICE("SIGTERM: killing children\n");
133 kill(-getpgrp(), SIGTERM);
135 #endif
136 DBG_NOTICE("Exiting pid %d on SIGTERM\n", getpid());
137 TALLOC_FREE(ev);
138 exit(127);
142 called when the process model is selected
144 static void prefork_model_init(void)
148 static void prefork_reload_after_fork(void)
150 NTSTATUS status;
152 ldb_wrap_fork_hook();
153 /* Must be done after a fork() to reset messaging contexts. */
154 status = imessaging_reinit_all();
155 if (!NT_STATUS_IS_OK(status)) {
156 smb_panic("Failed to re-initialise imessaging after fork");
158 force_check_log_size();
162 * clean up any messaging associated with the old process.
165 static void irpc_cleanup(
166 struct loadparm_context *lp_ctx,
167 struct tevent_context *ev,
168 pid_t pid)
170 TALLOC_CTX *mem_ctx = talloc_new(NULL);
171 struct imessaging_context *msg_ctx = NULL;
172 NTSTATUS status = NT_STATUS_OK;
174 if (mem_ctx == NULL) {
175 DBG_ERR("OOM cleaning up irpc\n");
176 return;
178 msg_ctx = imessaging_client_init(mem_ctx, lp_ctx, ev);
179 if (msg_ctx == NULL) {
180 DBG_ERR("Unable to create imessaging_context\n");
181 TALLOC_FREE(mem_ctx);
182 return;
184 status = imessaging_process_cleanup(msg_ctx, pid);
185 if (!NT_STATUS_IS_OK(status)) {
186 DBG_ERR("imessaging_process_cleanup returned (%s)\n",
187 nt_errstr(status));
188 TALLOC_FREE(mem_ctx);
189 return;
192 TALLOC_FREE(mem_ctx);
196 * handle EOF on the parent-to-all-children pipe in the child, i.e.
197 * the parent has died and its end of the pipe has been closed.
198 * The child handles this by exiting as well.
200 static void prefork_pipe_handler(struct tevent_context *event_ctx,
201 struct tevent_fd *fde, uint16_t flags,
202 void *private_data)
204 struct loadparm_context *lp_ctx = NULL;
205 pid_t pid;
208 * free the fde which removes the event and stops it firing again
210 TALLOC_FREE(fde);
213 * Clean up any irpc end points this process had.
215 pid = getpid();
216 lp_ctx = talloc_get_type_abort(private_data, struct loadparm_context);
217 irpc_cleanup(lp_ctx, event_ctx, pid);
219 DBG_NOTICE("Child %d exiting\n", getpid());
220 TALLOC_FREE(event_ctx);
221 exit(0);
226 * Called by the top-level samba process to create a new prefork master process
228 static void prefork_fork_master(
229 struct tevent_context *ev,
230 struct loadparm_context *lp_ctx,
231 const char *service_name,
232 struct task_server *(*new_task_fn)(struct tevent_context *,
233 struct loadparm_context *lp_ctx,
234 struct server_id,
235 void *,
236 void *),
237 void *private_data,
238 const struct service_details *service_details,
239 unsigned restart_delay,
240 int from_parent_fd)
242 pid_t pid;
243 struct tfork* t = NULL;
244 int i, num_children;
246 struct tevent_context *ev2;
247 struct task_server *task = NULL;
248 struct process_details pd = initial_process_details;
249 struct samba_tevent_trace_state *samba_tevent_trace_state = NULL;
250 int control_pipe[2];
252 t = tfork_create();
253 if (t == NULL) {
254 smb_panic("failure in tfork\n");
257 DBG_NOTICE("Forking [%s] pre-fork master process\n", service_name);
258 pid = tfork_child_pid(t);
259 if (pid != 0) {
260 struct tevent_fd *fde = NULL;
261 int fd = tfork_event_fd(t);
262 struct restart_context *rc = NULL;
264 /* Register a pipe handler that gets called when the prefork
265 * master process terminates.
267 rc = talloc_zero(ev, struct restart_context);
268 if (rc == NULL) {
269 smb_panic("OOM allocating restart context\n");
271 rc->t = t;
272 rc->lp_ctx = lp_ctx;
273 rc->service_name = service_name;
274 rc->service_details = service_details;
275 rc->from_parent_fd = from_parent_fd;
276 rc->restart_delay = restart_delay;
277 rc->master = talloc_zero(rc, struct master_restart_context);
278 if (rc->master == NULL) {
279 smb_panic("OOM allocating master restart context\n");
282 rc->master->new_task_fn = new_task_fn;
283 rc->master->private_data = private_data;
285 fde = tevent_add_fd(
286 ev, ev, fd, TEVENT_FD_READ, prefork_child_pipe_handler, rc);
287 if (fde == NULL) {
288 smb_panic("Failed to add child pipe handler, "
289 "after fork");
291 tevent_fd_set_auto_close(fde);
292 return;
295 pid = getpid();
297 process_set_title("%s[master]", "task[%s] pre-fork master", service_name);
300 * this will free all the listening sockets and all state that
301 * is not associated with this new connection
303 if (tevent_re_initialise(ev) != 0) {
304 smb_panic("Failed to re-initialise tevent after fork");
306 prefork_reload_after_fork();
307 setup_handlers(ev, lp_ctx, from_parent_fd);
309 if (service_details->inhibit_pre_fork) {
310 task = new_task_fn(
311 ev, lp_ctx, cluster_id(pid, 0), private_data, NULL);
313 * The task does not support pre-fork
315 if (task != NULL && service_details->post_fork != NULL) {
316 service_details->post_fork(task, &pd);
318 tevent_loop_wait(ev);
319 TALLOC_FREE(ev);
320 exit(0);
324 * This is now the child code. We need a completely new event_context
325 * to work with
327 ev2 = s4_event_context_init(NULL);
329 samba_tevent_trace_state = create_samba_tevent_trace_state(ev2);
330 if (samba_tevent_trace_state == NULL) {
331 TALLOC_FREE(ev);
332 TALLOC_FREE(ev2);
333 exit(127);
336 tevent_set_trace_callback(ev2,
337 samba_tevent_trace_callback,
338 samba_tevent_trace_state);
340 /* setup this new connection: process will bind to it's sockets etc
342 * While we can use ev for the child, which has been re-initialised
343 * above we must run the new task under ev2 otherwise the children would
344 * be listening on the sockets. Also we don't want the top level
345 * process accepting and handling requests, it's responsible for
346 * monitoring and controlling the child work processes.
348 task = new_task_fn(ev2, lp_ctx, cluster_id(pid, 0), private_data, NULL);
349 if (task == NULL) {
350 TALLOC_FREE(ev);
351 TALLOC_FREE(ev2);
352 exit(127);
356 * Register an irpc name that can be used by the samba-tool processes
357 * command
360 struct talloc_ctx *ctx = talloc_new(NULL);
361 char *name = NULL;
362 if (ctx == NULL) {
363 DBG_ERR("Out of memory");
364 exit(127);
366 name = talloc_asprintf(ctx, "prefork-master-%s", service_name);
367 irpc_add_name(task->msg_ctx, name);
368 TALLOC_FREE(ctx);
372 int default_children;
373 default_children = lpcfg_prefork_children(lp_ctx);
374 num_children = lpcfg_parm_int(lp_ctx, NULL, "prefork children",
375 service_name, default_children);
377 if (num_children == 0) {
378 DBG_WARNING("Number of pre-fork children for %s is zero, "
379 "NO worker processes will be started for %s\n",
380 service_name, service_name);
382 DBG_NOTICE("Forking %d %s worker processes\n",
383 num_children, service_name);
386 * the prefork master creates its own control pipe, so the prefork
387 * workers can detect if the master exits (in which case an EOF gets
388 * written). (Whereas from_parent_fd is the control pipe from the
389 * top-level process that the prefork master listens on)
392 int ret;
393 ret = pipe(control_pipe);
394 if (ret != 0) {
395 smb_panic("Unable to create worker control pipe\n");
397 smb_set_close_on_exec(control_pipe[0]);
398 smb_set_close_on_exec(control_pipe[1]);
402 * We are now free to spawn some worker processes
404 for (i=0; i < num_children; i++) {
405 prefork_fork_worker(task,
407 ev2,
408 lp_ctx,
409 service_details,
410 service_name,
411 control_pipe,
413 &pd);
414 pd.instances++;
417 /* Don't listen on the sockets we just gave to the children */
418 tevent_loop_wait(ev);
419 TALLOC_FREE(ev);
420 /* We need to keep ev2 until we're finished for the messaging to work */
421 TALLOC_FREE(ev2);
422 exit(0);
425 static void prefork_restart_fn(struct tevent_context *ev,
426 struct tevent_timer *te,
427 struct timeval tv,
428 void *private_data);
431 * Restarts a child process if it exits unexpectedly
433 static bool prefork_restart(struct tevent_context *ev,
434 struct restart_context *rc)
436 struct tevent_timer *te = NULL;
438 if (rc->restart_delay > 0) {
439 DBG_ERR("Restarting [%s] pre-fork %s in (%d) seconds\n",
440 rc->service_name,
441 (rc->master == NULL) ? "worker" : "master",
442 rc->restart_delay);
446 * Always use an async timer event. If
447 * rc->restart_delay is zero this is the
448 * same as an immediate event and will be
449 * called immediately we go back into the
450 * event loop.
452 te = tevent_add_timer(ev,
454 tevent_timeval_current_ofs(rc->restart_delay, 0),
455 prefork_restart_fn,
456 rc);
457 if (te == NULL) {
458 DBG_ERR("tevent_add_timer fail [%s] pre-fork event %s\n",
459 rc->service_name,
460 (rc->master == NULL) ? "worker" : "master");
461 /* Caller needs to free rc. */
462 return false;
464 /* Caller must not free rc - it's in use. */
465 return true;
468 static void prefork_restart_fn(struct tevent_context *ev,
469 struct tevent_timer *te,
470 struct timeval tv,
471 void *private_data)
473 unsigned max_backoff = 0;
474 unsigned backoff = 0;
475 unsigned default_value = 0;
476 struct restart_context *rc = talloc_get_type(private_data,
477 struct restart_context);
478 unsigned restart_delay = rc->restart_delay;
480 TALLOC_FREE(te);
483 * If the child process is constantly exiting, then restarting it can
484 * consume a lot of resources. In which case, we want to backoff a bit
485 * before respawning it
487 default_value = lpcfg_prefork_backoff_increment(rc->lp_ctx);
488 backoff = lpcfg_parm_int(rc->lp_ctx,
489 NULL,
490 "prefork backoff increment",
491 rc->service_name,
492 default_value);
494 default_value = lpcfg_prefork_maximum_backoff(rc->lp_ctx);
495 max_backoff = lpcfg_parm_int(rc->lp_ctx,
496 NULL,
497 "prefork maximum backoff",
498 rc->service_name,
499 default_value);
501 restart_delay += backoff;
502 restart_delay = min(restart_delay, max_backoff);
504 if (rc->master != NULL) {
505 DBG_ERR("Restarting [%s] pre-fork master\n", rc->service_name);
506 prefork_fork_master(ev,
507 rc->lp_ctx,
508 rc->service_name,
509 rc->master->new_task_fn,
510 rc->master->private_data,
511 rc->service_details,
512 restart_delay,
513 rc->from_parent_fd);
514 } else if (rc->worker != NULL) {
515 struct process_details pd = initial_process_details;
516 DBG_ERR("Restarting [%s] pre-fork worker(%d)\n",
517 rc->service_name,
518 rc->worker->instance);
519 pd.instances = rc->worker->instance;
520 prefork_fork_worker(rc->worker->task,
522 rc->worker->ev2,
523 rc->lp_ctx,
524 rc->service_details,
525 rc->service_name,
526 rc->worker->control_pipe,
527 restart_delay,
528 &pd);
530 /* tfork allocates tfork structures with malloc */
531 tfork_destroy(&rc->t);
532 free(rc->t);
533 TALLOC_FREE(rc);
537 handle EOF on the child pipe in the parent, so we know when a
538 process terminates without using SIGCHLD or waiting on all possible pids.
540 We need to ensure we do not ignore SIGCHLD because we need it to
541 work to get a valid error code from samba_runcmd_*().
543 static void prefork_child_pipe_handler(struct tevent_context *ev,
544 struct tevent_fd *fde,
545 uint16_t flags,
546 void *private_data)
548 struct restart_context *rc = NULL;
549 int status = 0;
550 pid_t pid = 0;
551 bool rc_inuse = false;
553 /* free the fde which removes the event and stops it firing again */
554 TALLOC_FREE(fde);
556 /* the child has closed the pipe, assume its dead */
558 rc = talloc_get_type_abort(private_data, struct restart_context);
559 pid = tfork_child_pid(rc->t);
560 errno = 0;
562 irpc_cleanup(rc->lp_ctx, ev, pid);
563 status = tfork_status(&rc->t, false);
564 if (status == -1) {
565 DBG_ERR("Parent %d, Child %d terminated, "
566 "unable to get status code from tfork\n",
567 getpid(), pid);
568 rc_inuse = prefork_restart(ev, rc);
569 } else if (WIFEXITED(status)) {
570 status = WEXITSTATUS(status);
571 DBG_ERR("Parent %d, Child %d exited with status %d\n",
572 getpid(), pid, status);
573 if (status != 0) {
574 rc_inuse = prefork_restart(ev, rc);
576 } else if (WIFSIGNALED(status)) {
577 status = WTERMSIG(status);
578 DBG_ERR("Parent %d, Child %d terminated with signal %d\n",
579 getpid(), pid, status);
580 if (status == SIGABRT || status == SIGBUS || status == SIGFPE ||
581 status == SIGILL || status == SIGSYS || status == SIGSEGV ||
582 status == SIGKILL) {
584 rc_inuse = prefork_restart(ev, rc);
587 if (!rc_inuse) {
588 /* tfork allocates tfork structures with malloc */
589 tfork_destroy(&rc->t);
590 free(rc->t);
591 TALLOC_FREE(rc);
593 return;
597 called when a listening socket becomes readable.
599 static void prefork_accept_connection(
600 struct tevent_context *ev,
601 struct loadparm_context *lp_ctx,
602 struct socket_context *listen_socket,
603 void (*new_conn)(struct tevent_context *,
604 struct loadparm_context *,
605 struct socket_context *,
606 struct server_id,
607 void *,
608 void *),
609 void *private_data,
610 void *process_context)
612 NTSTATUS status;
613 struct socket_context *connected_socket;
614 pid_t pid = getpid();
616 /* accept an incoming connection. */
617 status = socket_accept(listen_socket, &connected_socket);
618 if (!NT_STATUS_IS_OK(status)) {
620 * For prefork we can ignore STATUS_MORE_ENTRIES, as once a
621 * connection becomes available all waiting processes are
622 * woken, but only one gets work to process.
623 * AKA the thundering herd.
624 * In the short term this should not be an issue as the number
625 * of workers should be a small multiple of the number of cpus
626 * In the longer term socket_accept needs to implement a
627 * mutex/semaphore (like apache does) to serialise the accepts
629 if (!NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)) {
630 DBG_ERR("Worker process (%d), error in accept [%s]\n",
631 getpid(), nt_errstr(status));
633 return;
636 talloc_steal(private_data, connected_socket);
638 new_conn(ev, lp_ctx, connected_socket,
639 cluster_id(pid, socket_get_fd(connected_socket)),
640 private_data, process_context);
643 static void setup_handlers(
644 struct tevent_context *ev,
645 struct loadparm_context *lp_ctx,
646 int from_parent_fd)
648 struct tevent_fd *fde = NULL;
649 struct tevent_signal *se = NULL;
651 fde = tevent_add_fd(ev, ev, from_parent_fd, TEVENT_FD_READ,
652 prefork_pipe_handler, lp_ctx);
653 if (fde == NULL) {
654 smb_panic("Failed to add fd handler after fork");
657 se = tevent_add_signal(ev,
659 SIGHUP,
661 sighup_signal_handler,
662 NULL);
663 if (se == NULL) {
664 smb_panic("Failed to add SIGHUP handler after fork");
667 se = tevent_add_signal(ev,
669 SIGTERM,
671 sigterm_signal_handler,
672 NULL);
673 if (se == NULL) {
674 smb_panic("Failed to add SIGTERM handler after fork");
679 * Called by the prefork master to create a new prefork worker process
681 static void prefork_fork_worker(struct task_server *task,
682 struct tevent_context *ev,
683 struct tevent_context *ev2,
684 struct loadparm_context *lp_ctx,
685 const struct service_details *service_details,
686 const char *service_name,
687 int control_pipe[2],
688 unsigned restart_delay,
689 struct process_details *pd)
691 struct tfork *w = NULL;
692 pid_t pid;
694 w = tfork_create();
695 if (w == NULL) {
696 smb_panic("failure in tfork\n");
699 pid = tfork_child_pid(w);
700 if (pid != 0) {
701 struct tevent_fd *fde = NULL;
702 int fd = tfork_event_fd(w);
703 struct restart_context *rc = NULL;
706 * we're the parent (prefork master), so store enough info to
707 * restart the worker/child if it exits unexpectedly
709 rc = talloc_zero(ev, struct restart_context);
710 if (rc == NULL) {
711 smb_panic("OOM allocating restart context\n");
713 rc->t = w;
714 rc->lp_ctx = lp_ctx;
715 rc->service_name = service_name;
716 rc->service_details = service_details;
717 rc->restart_delay = restart_delay;
718 rc->master = NULL;
719 rc->worker = talloc_zero(rc, struct worker_restart_context);
720 if (rc->worker == NULL) {
721 smb_panic("OOM allocating master restart context\n");
723 rc->worker->ev2 = ev2;
724 rc->worker->instance = pd->instances;
725 rc->worker->task = task;
726 rc->worker->control_pipe[0] = control_pipe[0];
727 rc->worker->control_pipe[1] = control_pipe[1];
729 fde = tevent_add_fd(
730 ev, ev, fd, TEVENT_FD_READ, prefork_child_pipe_handler, rc);
731 if (fde == NULL) {
732 smb_panic("Failed to add child pipe handler, "
733 "after fork");
735 tevent_fd_set_auto_close(fde);
736 } else {
739 * we're the child (prefork-worker). We never write to the
740 * control pipe, but listen on the read end in case our parent
741 * (the pre-fork master) exits
743 close(control_pipe[1]);
744 setup_handlers(ev2, lp_ctx, control_pipe[0]);
747 * tfork uses malloc
749 free(w);
751 TALLOC_FREE(ev);
753 process_set_title("%s(%d)",
754 "task[%s] pre-forked worker(%d)",
755 service_name,
756 pd->instances);
758 prefork_reload_after_fork();
759 if (service_details->post_fork != NULL) {
760 service_details->post_fork(task, pd);
763 struct talloc_ctx *ctx = talloc_new(NULL);
764 char *name = NULL;
765 if (ctx == NULL) {
766 smb_panic("OOM allocating talloc context\n");
768 name = talloc_asprintf(ctx,
769 "prefork-worker-%s-%d",
770 service_name,
771 pd->instances);
772 irpc_add_name(task->msg_ctx, name);
773 TALLOC_FREE(ctx);
775 tevent_loop_wait(ev2);
776 talloc_free(ev2);
777 exit(0);
781 * called to create a new server task
783 static void prefork_new_task(
784 struct tevent_context *ev,
785 struct loadparm_context *lp_ctx,
786 const char *service_name,
787 struct task_server *(*new_task_fn)(struct tevent_context *,
788 struct loadparm_context *lp_ctx,
789 struct server_id , void *, void *),
790 void *private_data,
791 const struct service_details *service_details,
792 int from_parent_fd)
794 prefork_fork_master(ev,
795 lp_ctx,
796 service_name,
797 new_task_fn,
798 private_data,
799 service_details,
801 from_parent_fd);
806 * called when a task terminates
808 static void prefork_terminate_task(struct tevent_context *ev,
809 struct loadparm_context *lp_ctx,
810 const char *reason,
811 bool fatal,
812 void *process_context)
814 DBG_DEBUG("called with reason[%s]\n", reason);
815 TALLOC_FREE(ev);
816 if (fatal == true) {
817 exit(127);
818 } else {
819 exit(0);
824 * called when a connection completes
826 static void prefork_terminate_connection(struct tevent_context *ev,
827 struct loadparm_context *lp_ctx,
828 const char *reason,
829 void *process_context)
833 /* called to set a title of a task or connection */
834 static void prefork_set_title(struct tevent_context *ev, const char *title)
838 static const struct model_ops prefork_ops = {
839 .name = "prefork",
840 .model_init = prefork_model_init,
841 .accept_connection = prefork_accept_connection,
842 .new_task = prefork_new_task,
843 .terminate_task = prefork_terminate_task,
844 .terminate_connection = prefork_terminate_connection,
845 .set_title = prefork_set_title,
849 * initialise the prefork process model, registering ourselves with the
850 * process model subsystem
852 NTSTATUS process_model_prefork_init(void)
854 return register_process_model(&prefork_ops);