LCOV - code coverage report
Current view: top level - source4/samba - process_prefork.c (source / functions) Hit Total Coverage
Test: coverage report for support-claim-type-attributes 6b5c566e Lines: 250 319 78.4 %
Date: 2023-11-21 12:31:41 Functions: 16 18 88.9 %

          Line data    Source code
       1             : /*
       2             :    Unix SMB/CIFS implementation.
       3             : 
       4             :    process model: prefork (n client connections per process)
       5             : 
       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>
      11             : 
      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.
      16             : 
      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.
      21             : 
      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/>.
      24             : */
      25             : /*
      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.
      34             :  */
      35             : #include "includes.h"
      36             : #include <unistd.h>
      37             : 
      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"
      49             : 
      50             : #define min(a, b) (((a) < (b)) ? (a) : (b))
      51             : 
      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);
      81             : 
      82             : /*
      83             :  * State needed to restart the master process or a worker process if they
      84             :  * terminate early.
      85             :  */
      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;
      93             : };
      94             : 
      95             : struct worker_restart_context {
      96             :         unsigned int instance;
      97             :         struct task_server *task;
      98             :         struct tevent_context *ev2;
      99             :         int control_pipe[2];
     100             : };
     101             : 
     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;
     111             : };
     112             : 
     113           0 : 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)
     117             : {
     118           0 :         reopen_logs_internal();
     119           0 : }
     120             : 
     121          20 : 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)
     125             : {
     126             : #ifdef HAVE_GETPGRP
     127          20 :         if (getpgrp() == getpid()) {
     128             :                 /*
     129             :                  * We're the process group leader, send
     130             :                  * SIGTERM to our process group.
     131             :                  */
     132           0 :                 DBG_NOTICE("SIGTERM: killing children\n");
     133           0 :                 kill(-getpgrp(), SIGTERM);
     134             :         }
     135             : #endif
     136          20 :         DBG_NOTICE("Exiting pid %d on SIGTERM\n", getpid());
     137          20 :         TALLOC_FREE(ev);
     138          20 :         exit(127);
     139             : }
     140             : 
     141             : /*
     142             :   called when the process model is selected
     143             : */
     144          28 : static void prefork_model_init(void)
     145             : {
     146          28 : }
     147             : 
     148         808 : static void prefork_reload_after_fork(void)
     149             : {
     150          60 :         NTSTATUS status;
     151             : 
     152         808 :         ldb_wrap_fork_hook();
     153             :         /* Must be done after a fork() to reset messaging contexts. */
     154         808 :         status = imessaging_reinit_all();
     155         808 :         if (!NT_STATUS_IS_OK(status)) {
     156           0 :                 smb_panic("Failed to re-initialise imessaging after fork");
     157             :         }
     158         808 :         force_check_log_size();
     159         808 : }
     160             : 
     161             : /*
     162             :  * clean up any messaging associated with the old process.
     163             :  *
     164             :  */
     165         809 : static void irpc_cleanup(
     166             :         struct loadparm_context *lp_ctx,
     167             :         struct tevent_context *ev,
     168             :         pid_t pid)
     169             : {
     170         809 :         TALLOC_CTX *mem_ctx = talloc_new(NULL);
     171         809 :         struct imessaging_context *msg_ctx = NULL;
     172         809 :         NTSTATUS status = NT_STATUS_OK;
     173             : 
     174         809 :         if (mem_ctx == NULL) {
     175           0 :                 DBG_ERR("OOM cleaning up irpc\n");
     176           0 :                 return;
     177             :         }
     178         809 :         msg_ctx = imessaging_client_init(mem_ctx, lp_ctx, ev);
     179         809 :         if (msg_ctx == NULL) {
     180           0 :                 DBG_ERR("Unable to create imessaging_context\n");
     181           0 :                 TALLOC_FREE(mem_ctx);
     182           0 :                 return;
     183             :         }
     184         809 :         status = imessaging_process_cleanup(msg_ctx, pid);
     185         809 :         if (!NT_STATUS_IS_OK(status)) {
     186           0 :                 DBG_ERR("imessaging_process_cleanup returned (%s)\n",
     187             :                         nt_errstr(status));
     188           0 :                 TALLOC_FREE(mem_ctx);
     189           0 :                 return;
     190             :         }
     191             : 
     192         809 :         TALLOC_FREE(mem_ctx);
     193             : }
     194             : 
     195             : /*
     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.
     199             :  */
     200         788 : static void prefork_pipe_handler(struct tevent_context *event_ctx,
     201             :                                  struct tevent_fd *fde, uint16_t flags,
     202             :                                  void *private_data)
     203             : {
     204         788 :         struct loadparm_context *lp_ctx = NULL;
     205          60 :         pid_t pid;
     206             : 
     207             :         /*
     208             :          * free the fde which removes the event and stops it firing again
     209             :          */
     210         788 :         TALLOC_FREE(fde);
     211             : 
     212             :         /*
     213             :          * Clean up any irpc end points this process had.
     214             :          */
     215         788 :         pid = getpid();
     216         788 :         lp_ctx = talloc_get_type_abort(private_data, struct loadparm_context);
     217         788 :         irpc_cleanup(lp_ctx, event_ctx, pid);
     218             : 
     219         788 :         DBG_NOTICE("Child %d exiting\n", getpid());
     220         788 :         TALLOC_FREE(event_ctx);
     221         788 :         exit(0);
     222             : }
     223             : 
     224             : 
     225             : /*
     226             :  * Called by the top-level samba process to create a new prefork master process
     227             :  */
     228         398 : 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)
     241             : {
     242          28 :         pid_t pid;
     243         398 :         struct tfork* t = NULL;
     244          28 :         int i, num_children;
     245             : 
     246          28 :         struct tevent_context *ev2;
     247         398 :         struct task_server *task = NULL;
     248         398 :         struct process_details pd = initial_process_details;
     249         398 :         struct samba_tevent_trace_state *samba_tevent_trace_state = NULL;
     250          28 :         int control_pipe[2];
     251             : 
     252         398 :         t = tfork_create();
     253         796 :         if (t == NULL) {
     254           0 :                 smb_panic("failure in tfork\n");
     255             :         }
     256             : 
     257         796 :         DBG_NOTICE("Forking [%s] pre-fork master process\n", service_name);
     258         796 :         pid = tfork_child_pid(t);
     259         796 :         if (pid != 0) {
     260         398 :                 struct tevent_fd *fde = NULL;
     261         398 :                 int fd = tfork_event_fd(t);
     262         398 :                 struct restart_context *rc = NULL;
     263             : 
     264             :                 /* Register a pipe handler that gets called when the prefork
     265             :                  * master process terminates.
     266             :                  */
     267         398 :                 rc = talloc_zero(ev, struct restart_context);
     268         398 :                 if (rc == NULL) {
     269           0 :                         smb_panic("OOM allocating restart context\n");
     270             :                 }
     271         398 :                 rc->t = t;
     272         398 :                 rc->lp_ctx = lp_ctx;
     273         398 :                 rc->service_name = service_name;
     274         398 :                 rc->service_details = service_details;
     275         398 :                 rc->from_parent_fd = from_parent_fd;
     276         398 :                 rc->restart_delay = restart_delay;
     277         398 :                 rc->master = talloc_zero(rc, struct master_restart_context);
     278         398 :                 if (rc->master == NULL) {
     279           0 :                         smb_panic("OOM allocating master restart context\n");
     280             :                 }
     281             : 
     282         398 :                 rc->master->new_task_fn = new_task_fn;
     283         398 :                 rc->master->private_data = private_data;
     284             : 
     285         398 :                 fde = tevent_add_fd(
     286             :                     ev, ev, fd, TEVENT_FD_READ, prefork_child_pipe_handler, rc);
     287         398 :                 if (fde == NULL) {
     288           0 :                         smb_panic("Failed to add child pipe handler, "
     289             :                                   "after fork");
     290             :                 }
     291         398 :                 tevent_fd_set_auto_close(fde);
     292         398 :                 return;
     293             :         }
     294             : 
     295         398 :         pid = getpid();
     296             : 
     297         398 :         process_set_title("%s[master]", "task[%s] pre-fork master", service_name);
     298             : 
     299             :         /*
     300             :          * this will free all the listening sockets and all state that
     301             :          * is not associated with this new connection
     302             :          */
     303         398 :         if (tevent_re_initialise(ev) != 0) {
     304           0 :                 smb_panic("Failed to re-initialise tevent after fork");
     305             :         }
     306         398 :         prefork_reload_after_fork();
     307         398 :         setup_handlers(ev, lp_ctx, from_parent_fd);
     308             : 
     309         398 :         if (service_details->inhibit_pre_fork) {
     310         287 :                 task = new_task_fn(
     311             :                     ev, lp_ctx, cluster_id(pid, 0), private_data, NULL);
     312             :                 /*
     313             :                  * The task does not support pre-fork
     314             :                  */
     315         287 :                 if (task != NULL && service_details->post_fork != NULL) {
     316           0 :                         service_details->post_fork(task, &pd);
     317             :                 }
     318         287 :                 if (task != NULL && service_details->before_loop != NULL) {
     319           0 :                         service_details->before_loop(task);
     320             :                 }
     321         287 :                 tevent_loop_wait(ev);
     322           0 :                 TALLOC_FREE(ev);
     323           0 :                 exit(0);
     324             :         }
     325             : 
     326             :         /*
     327             :          * This is now the child code. We need a completely new event_context
     328             :          * to work with
     329             :          */
     330         111 :         ev2 = s4_event_context_init(NULL);
     331             : 
     332         111 :         samba_tevent_trace_state = create_samba_tevent_trace_state(ev2);
     333         111 :         if (samba_tevent_trace_state == NULL) {
     334           0 :                 TALLOC_FREE(ev);
     335           0 :                 TALLOC_FREE(ev2);
     336           0 :                 exit(127);
     337             :         }
     338             : 
     339         111 :         tevent_set_trace_callback(ev2,
     340             :                                   samba_tevent_trace_callback,
     341             :                                   samba_tevent_trace_state);
     342             : 
     343             :         /* setup this new connection: process will bind to it's sockets etc
     344             :          *
     345             :          * While we can use ev for the child, which has been re-initialised
     346             :          * above we must run the new task under ev2 otherwise the children would
     347             :          * be listening on the sockets.  Also we don't want the top level
     348             :          * process accepting and handling requests, it's responsible for
     349             :          * monitoring and controlling the child work processes.
     350             :          */
     351         111 :         task = new_task_fn(ev2, lp_ctx, cluster_id(pid, 0), private_data, NULL);
     352         111 :         if (task == NULL) {
     353           0 :                 TALLOC_FREE(ev);
     354           0 :                 TALLOC_FREE(ev2);
     355           0 :                 exit(127);
     356             :         }
     357             : 
     358             :         /*
     359             :          * Register an irpc name that can be used by the samba-tool processes
     360             :          * command
     361             :          */
     362             :         {
     363         111 :                 struct talloc_ctx *ctx = talloc_new(NULL);
     364         111 :                 char *name = NULL;
     365         111 :                 if (ctx == NULL) {
     366           0 :                         DBG_ERR("Out of memory\n");
     367           0 :                         exit(127);
     368             :                 }
     369         111 :                 name = talloc_asprintf(ctx, "prefork-master-%s", service_name);
     370         111 :                 irpc_add_name(task->msg_ctx, name);
     371         111 :                 TALLOC_FREE(ctx);
     372             :         }
     373             : 
     374             :         {
     375           8 :                 int default_children;
     376         111 :                 default_children = lpcfg_prefork_children(lp_ctx);
     377         111 :                 num_children = lpcfg_parm_int(lp_ctx, NULL, "prefork children",
     378             :                                               service_name, default_children);
     379             :         }
     380         111 :         if (num_children == 0) {
     381           0 :                 DBG_WARNING("Number of pre-fork children for %s is zero, "
     382             :                             "NO worker processes will be started for %s\n",
     383             :                             service_name, service_name);
     384             :         }
     385         111 :         DBG_NOTICE("Forking %d %s worker processes\n",
     386             :                    num_children, service_name);
     387             : 
     388             :         /*
     389             :          * the prefork master creates its own control pipe, so the prefork
     390             :          * workers can detect if the master exits (in which case an EOF gets
     391             :          * written). (Whereas from_parent_fd is the control pipe from the
     392             :          * top-level process that the prefork master listens on)
     393             :          */
     394             :         {
     395           8 :                 int ret;
     396         111 :                 ret = pipe(control_pipe);
     397         111 :                 if (ret != 0) {
     398           0 :                         smb_panic("Unable to create worker control pipe\n");
     399             :                 }
     400         111 :                 smb_set_close_on_exec(control_pipe[0]);
     401         111 :                 smb_set_close_on_exec(control_pipe[1]);
     402             :         }
     403             : 
     404             :         /*
     405             :          * Note, we call this before the first
     406             :          * prefork_fork_worker() in order to have
     407             :          * a stable order of:
     408             :          *  task_init(master) -> before_loop(master)
     409             :          *  -> post_fork(worker) -> before_loop(worker)
     410             :          *
     411             :          * Otherwise we would have different behaviors
     412             :          * between the first prefork_fork_worker() loop
     413             :          * and restarting of died workers
     414             :          */
     415         111 :         if (task != NULL && service_details->before_loop != NULL) {
     416          29 :                 struct task_server *task_copy = NULL;
     417             : 
     418             :                 /*
     419             :                  * We need to use ev as parent in order to
     420             :                  * keep everything alive during the loop
     421             :                  */
     422          29 :                 task_copy = talloc(ev, struct task_server);
     423          29 :                 if (task_copy == NULL) {
     424           0 :                         TALLOC_FREE(ev);
     425           0 :                         TALLOC_FREE(ev2);
     426           0 :                         exit(127);
     427             :                 }
     428          29 :                 *task_copy = *task;
     429             : 
     430             :                 /*
     431             :                  * In order to allow the before_loop() hook
     432             :                  * to register messages or event handlers,
     433             :                  * we need to fix up task->event_ctx
     434             :                  * and create a new task->msg_ctx
     435             :                  */
     436          29 :                 task_copy->event_ctx = ev;
     437          29 :                 task_copy->msg_ctx = imessaging_init(task_copy,
     438             :                                                 task_copy->lp_ctx,
     439             :                                                 task_copy->server_id,
     440             :                                                 task_copy->event_ctx);
     441          29 :                 if (task_copy->msg_ctx == NULL) {
     442           0 :                         TALLOC_FREE(ev);
     443           0 :                         TALLOC_FREE(ev2);
     444           0 :                         exit(127);
     445             :                 }
     446          29 :                 service_details->before_loop(task_copy);
     447             :         }
     448             : 
     449             :         /*
     450             :          * We are now free to spawn some worker processes
     451             :          */
     452         507 :         for (i=0; i < num_children; i++) {
     453         396 :                 prefork_fork_worker(task,
     454             :                                     ev,
     455             :                                     ev2,
     456             :                                     lp_ctx,
     457             :                                     service_details,
     458             :                                     service_name,
     459             :                                     control_pipe,
     460             :                                     0,
     461             :                                     &pd);
     462         396 :                 pd.instances++;
     463             :         }
     464             : 
     465             :         /*
     466             :          * Make sure the messaging context
     467             :          * used by the workers is no longer
     468             :          * active on ev2, otherwise we
     469             :          * would have memory leaks, because
     470             :          * we queue incoming messages
     471             :          * and never process them via ev2.
     472             :          */
     473         111 :         imessaging_dgm_unref_ev(ev2);
     474             : 
     475             :         /* Don't listen on the sockets we just gave to the children */
     476         111 :         tevent_loop_wait(ev);
     477           0 :         imessaging_dgm_unref_ev(ev);
     478           0 :         TALLOC_FREE(ev);
     479             :         /* We need to keep ev2 until we're finished for the messaging to work */
     480           0 :         TALLOC_FREE(ev2);
     481           0 :         exit(0);
     482             : }
     483             : 
     484             : static void prefork_restart_fn(struct tevent_context *ev,
     485             :                                struct tevent_timer *te,
     486             :                                struct timeval tv,
     487             :                                void *private_data);
     488             : 
     489             : /*
     490             :  * Restarts a child process if it exits unexpectedly
     491             :  */
     492          21 : static bool prefork_restart(struct tevent_context *ev,
     493             :                             struct restart_context *rc)
     494             : {
     495          21 :         struct tevent_timer *te = NULL;
     496             : 
     497          21 :         if (rc->restart_delay > 0) {
     498           6 :                 DBG_ERR("Restarting [%s] pre-fork %s in (%d) seconds\n",
     499             :                         rc->service_name,
     500             :                         (rc->master == NULL) ? "worker" : "master",
     501             :                         rc->restart_delay);
     502             :         }
     503             : 
     504             :         /*
     505             :          * Always use an async timer event. If
     506             :          * rc->restart_delay is zero this is the
     507             :          * same as an immediate event and will be
     508             :          * called immediately we go back into the
     509             :          * event loop.
     510             :          */
     511          21 :         te = tevent_add_timer(ev,
     512             :                               ev,
     513             :                               tevent_timeval_current_ofs(rc->restart_delay, 0),
     514             :                               prefork_restart_fn,
     515             :                               rc);
     516          21 :         if (te == NULL) {
     517           0 :                 DBG_ERR("tevent_add_timer fail [%s] pre-fork event %s\n",
     518             :                         rc->service_name,
     519             :                         (rc->master == NULL) ? "worker" : "master");
     520             :                 /* Caller needs to free rc. */
     521           0 :                 return false;
     522             :         }
     523             :         /* Caller must not free rc - it's in use. */
     524          21 :         return true;
     525             : }
     526             : 
     527          21 : static void prefork_restart_fn(struct tevent_context *ev,
     528             :                                struct tevent_timer *te,
     529             :                                struct timeval tv,
     530             :                                void *private_data)
     531             : {
     532          21 :         unsigned max_backoff = 0;
     533          21 :         unsigned backoff = 0;
     534          21 :         unsigned default_value = 0;
     535          21 :         struct restart_context *rc = talloc_get_type(private_data,
     536             :                                         struct restart_context);
     537          21 :         unsigned restart_delay = rc->restart_delay;
     538             : 
     539          21 :         TALLOC_FREE(te);
     540             : 
     541             :         /*
     542             :          * If the child process is constantly exiting, then restarting it can
     543             :          * consume a lot of resources. In which case, we want to backoff a bit
     544             :          * before respawning it
     545             :          */
     546          21 :         default_value = lpcfg_prefork_backoff_increment(rc->lp_ctx);
     547          21 :         backoff = lpcfg_parm_int(rc->lp_ctx,
     548             :                                  NULL,
     549             :                                  "prefork backoff increment",
     550             :                                  rc->service_name,
     551             :                                  default_value);
     552             : 
     553          21 :         default_value = lpcfg_prefork_maximum_backoff(rc->lp_ctx);
     554          21 :         max_backoff = lpcfg_parm_int(rc->lp_ctx,
     555             :                                      NULL,
     556             :                                      "prefork maximum backoff",
     557             :                                      rc->service_name,
     558             :                                      default_value);
     559             : 
     560          21 :         restart_delay += backoff;
     561          21 :         restart_delay = min(restart_delay, max_backoff);
     562             : 
     563          21 :         if (rc->master != NULL) {
     564           6 :                 DBG_ERR("Restarting [%s] pre-fork master\n", rc->service_name);
     565           6 :                 prefork_fork_master(ev,
     566             :                                     rc->lp_ctx,
     567             :                                     rc->service_name,
     568           6 :                                     rc->master->new_task_fn,
     569           6 :                                     rc->master->private_data,
     570             :                                     rc->service_details,
     571             :                                     restart_delay,
     572             :                                     rc->from_parent_fd);
     573          15 :         } else if (rc->worker != NULL) {
     574          15 :                 struct process_details pd = initial_process_details;
     575          15 :                 DBG_ERR("Restarting [%s] pre-fork worker(%d)\n",
     576             :                         rc->service_name,
     577             :                         rc->worker->instance);
     578          15 :                 pd.instances = rc->worker->instance;
     579          15 :                 prefork_fork_worker(rc->worker->task,
     580             :                                     ev,
     581          15 :                                     rc->worker->ev2,
     582             :                                     rc->lp_ctx,
     583             :                                     rc->service_details,
     584             :                                     rc->service_name,
     585          15 :                                     rc->worker->control_pipe,
     586             :                                     restart_delay,
     587             :                                     &pd);
     588             :         }
     589             :         /* tfork allocates tfork structures with malloc */
     590          21 :         tfork_destroy(&rc->t);
     591          21 :         free(rc->t);
     592          21 :         TALLOC_FREE(rc);
     593          21 : }
     594             : 
     595             : /*
     596             :   handle EOF on the child pipe in the parent, so we know when a
     597             :   process terminates without using SIGCHLD or waiting on all possible pids.
     598             : 
     599             :   We need to ensure we do not ignore SIGCHLD because we need it to
     600             :   work to get a valid error code from samba_runcmd_*().
     601             :  */
     602          21 : static void prefork_child_pipe_handler(struct tevent_context *ev,
     603             :                                        struct tevent_fd *fde,
     604             :                                        uint16_t flags,
     605             :                                        void *private_data)
     606             : {
     607          21 :         struct restart_context *rc = NULL;
     608          21 :         int status = 0;
     609          21 :         pid_t pid = 0;
     610          21 :         bool rc_inuse = false;
     611             : 
     612             :         /* free the fde which removes the event and stops it firing again */
     613          21 :         TALLOC_FREE(fde);
     614             : 
     615             :         /* the child has closed the pipe, assume its dead */
     616             : 
     617          21 :         rc = talloc_get_type_abort(private_data, struct restart_context);
     618          21 :         pid = tfork_child_pid(rc->t);
     619          21 :         errno = 0;
     620             : 
     621          21 :         irpc_cleanup(rc->lp_ctx, ev, pid);
     622          21 :         status = tfork_status(&rc->t, false);
     623          21 :         if (status == -1) {
     624           0 :                 DBG_ERR("Parent %d, Child %d terminated, "
     625             :                         "unable to get status code from tfork\n",
     626             :                         getpid(), pid);
     627           0 :                 rc_inuse = prefork_restart(ev, rc);
     628          21 :         } else if (WIFEXITED(status)) {
     629          20 :                 status = WEXITSTATUS(status);
     630          20 :                 DBG_ERR("Parent %d, Child %d exited with status %d\n",
     631             :                          getpid(), pid,  status);
     632          20 :                 if (status != 0) {
     633          20 :                         rc_inuse = prefork_restart(ev, rc);
     634             :                 }
     635           1 :         } else if (WIFSIGNALED(status)) {
     636           1 :                 status = WTERMSIG(status);
     637           1 :                 DBG_ERR("Parent %d, Child %d terminated with signal %d\n",
     638             :                         getpid(), pid, status);
     639           1 :                 if (status == SIGABRT || status == SIGBUS || status == SIGFPE ||
     640           0 :                     status == SIGILL || status == SIGSYS || status == SIGSEGV ||
     641             :                     status == SIGKILL) {
     642             : 
     643           1 :                         rc_inuse = prefork_restart(ev, rc);
     644             :                 }
     645             :         }
     646          21 :         if (!rc_inuse) {
     647             :                 /* tfork allocates tfork structures with malloc */
     648           0 :                 tfork_destroy(&rc->t);
     649           0 :                 free(rc->t);
     650           0 :                 TALLOC_FREE(rc);
     651             :         }
     652          21 :         return;
     653             : }
     654             : 
     655             : /*
     656             :   called when a listening socket becomes readable.
     657             : */
     658       73696 : static void prefork_accept_connection(
     659             :         struct tevent_context *ev,
     660             :         struct loadparm_context *lp_ctx,
     661             :         struct socket_context *listen_socket,
     662             :         void (*new_conn)(struct tevent_context *,
     663             :                         struct loadparm_context *,
     664             :                         struct socket_context *,
     665             :                         struct server_id,
     666             :                         void *,
     667             :                         void *),
     668             :         void *private_data,
     669             :         void *process_context)
     670             : {
     671        4084 :         NTSTATUS status;
     672        4084 :         struct socket_context *connected_socket;
     673       73696 :         pid_t pid = getpid();
     674             : 
     675             :         /* accept an incoming connection. */
     676       73696 :         status = socket_accept(listen_socket, &connected_socket);
     677       73696 :         if (!NT_STATUS_IS_OK(status)) {
     678             :                 /*
     679             :                  * For prefork we can ignore STATUS_MORE_ENTRIES, as  once a
     680             :                  * connection becomes available all waiting processes are
     681             :                  * woken, but only one gets work to  process.
     682             :                  * AKA the thundering herd.
     683             :                  * In the short term this should not be an issue as the number
     684             :                  * of workers should be a small multiple of the number of cpus
     685             :                  * In the longer term socket_accept needs to implement a
     686             :                  * mutex/semaphore (like apache does) to serialise the accepts
     687             :                  */
     688       27361 :                 if (!NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)) {
     689           0 :                         DBG_ERR("Worker process (%d), error in accept [%s]\n",
     690             :                                 getpid(), nt_errstr(status));
     691             :                 }
     692       27361 :                 return;
     693             :         }
     694             : 
     695       46335 :         talloc_steal(private_data, connected_socket);
     696             : 
     697       46335 :         new_conn(ev, lp_ctx, connected_socket,
     698       46335 :                  cluster_id(pid, socket_get_fd(connected_socket)),
     699             :                  private_data, process_context);
     700             : }
     701             : 
     702         808 : static void setup_handlers(
     703             :         struct tevent_context *ev,
     704             :         struct loadparm_context *lp_ctx,
     705             :         int from_parent_fd)
     706             : {
     707         808 :         struct tevent_fd *fde = NULL;
     708         808 :         struct tevent_signal *se = NULL;
     709             : 
     710         808 :         fde = tevent_add_fd(ev, ev, from_parent_fd, TEVENT_FD_READ,
     711             :                       prefork_pipe_handler, lp_ctx);
     712         808 :         if (fde == NULL) {
     713           0 :                 smb_panic("Failed to add fd handler after fork");
     714             :         }
     715             : 
     716         808 :         se = tevent_add_signal(ev,
     717             :                                ev,
     718             :                                SIGHUP,
     719             :                                0,
     720             :                                sighup_signal_handler,
     721             :                                NULL);
     722         808 :         if (se == NULL) {
     723           0 :                 smb_panic("Failed to add SIGHUP handler after fork");
     724             :         }
     725             : 
     726         808 :         se = tevent_add_signal(ev,
     727             :                                ev,
     728             :                                SIGTERM,
     729             :                                0,
     730             :                                sigterm_signal_handler,
     731             :                                NULL);
     732         808 :         if (se == NULL) {
     733           0 :                 smb_panic("Failed to add SIGTERM handler after fork");
     734             :         }
     735         808 : }
     736             : 
     737             : /*
     738             :  * Called by the prefork master to create a new prefork worker process
     739             :  */
     740         411 : static void prefork_fork_worker(struct task_server *task,
     741             :                                 struct tevent_context *ev,
     742             :                                 struct tevent_context *ev2,
     743             :                                 struct loadparm_context *lp_ctx,
     744             :                                 const struct service_details *service_details,
     745             :                                 const char *service_name,
     746             :                                 int control_pipe[2],
     747             :                                 unsigned restart_delay,
     748             :                                 struct process_details *pd)
     749             : {
     750         411 :         struct tfork *w = NULL;
     751          32 :         pid_t pid;
     752             : 
     753         411 :         w = tfork_create();
     754         821 :         if (w == NULL) {
     755           0 :                 smb_panic("failure in tfork\n");
     756             :         }
     757             : 
     758         821 :         pid = tfork_child_pid(w);
     759         821 :         if (pid != 0) {
     760         411 :                 struct tevent_fd *fde = NULL;
     761         411 :                 int fd = tfork_event_fd(w);
     762         411 :                 struct restart_context *rc = NULL;
     763             : 
     764             :                 /*
     765             :                  * we're the parent (prefork master), so store enough info to
     766             :                  * restart the worker/child if it exits unexpectedly
     767             :                  */
     768         411 :                 rc = talloc_zero(ev, struct restart_context);
     769         411 :                 if (rc == NULL) {
     770           0 :                         smb_panic("OOM allocating restart context\n");
     771             :                 }
     772         411 :                 rc->t = w;
     773         411 :                 rc->lp_ctx = lp_ctx;
     774         411 :                 rc->service_name = service_name;
     775         411 :                 rc->service_details = service_details;
     776         411 :                 rc->restart_delay = restart_delay;
     777         411 :                 rc->master = NULL;
     778         411 :                 rc->worker = talloc_zero(rc, struct worker_restart_context);
     779         411 :                 if (rc->worker == NULL) {
     780           0 :                         smb_panic("OOM allocating master restart context\n");
     781             :                 }
     782         411 :                 rc->worker->ev2 = ev2;
     783         411 :                 rc->worker->instance = pd->instances;
     784         411 :                 rc->worker->task = task;
     785         411 :                 rc->worker->control_pipe[0] = control_pipe[0];
     786         411 :                 rc->worker->control_pipe[1] = control_pipe[1];
     787             : 
     788         411 :                 fde = tevent_add_fd(
     789             :                     ev, ev, fd, TEVENT_FD_READ, prefork_child_pipe_handler, rc);
     790         411 :                 if (fde == NULL) {
     791           0 :                         smb_panic("Failed to add child pipe handler, "
     792             :                                   "after fork");
     793             :                 }
     794         411 :                 tevent_fd_set_auto_close(fde);
     795             :         } else {
     796             : 
     797             :                 /*
     798             :                  * we're the child (prefork-worker). We never write to the
     799             :                  * control pipe, but listen on the read end in case our parent
     800             :                  * (the pre-fork master) exits
     801             :                  */
     802         410 :                 close(control_pipe[1]);
     803         410 :                 setup_handlers(ev2, lp_ctx, control_pipe[0]);
     804             : 
     805             :                 /*
     806             :                  * tfork uses malloc
     807             :                  */
     808         410 :                 free(w);
     809             : 
     810         410 :                 imessaging_dgm_unref_ev(ev);
     811         410 :                 TALLOC_FREE(ev);
     812             : 
     813         410 :                 process_set_title("%s(%d)",
     814             :                                   "task[%s] pre-forked worker(%d)",
     815             :                                   service_name,
     816             :                                   pd->instances);
     817             : 
     818         410 :                 prefork_reload_after_fork();
     819         410 :                 if (service_details->post_fork != NULL) {
     820         290 :                         service_details->post_fork(task, pd);
     821             :                 }
     822             :                 {
     823         410 :                         struct talloc_ctx *ctx = talloc_new(NULL);
     824         410 :                         char *name = NULL;
     825         410 :                         if (ctx == NULL) {
     826           0 :                                 smb_panic("OOM allocating talloc context\n");
     827             :                         }
     828         410 :                         name = talloc_asprintf(ctx,
     829             :                                                "prefork-worker-%s-%d",
     830             :                                                service_name,
     831             :                                                pd->instances);
     832         410 :                         irpc_add_name(task->msg_ctx, name);
     833         410 :                         TALLOC_FREE(ctx);
     834             :                 }
     835         410 :                 if (service_details->before_loop != NULL) {
     836         109 :                         service_details->before_loop(task);
     837             :                 }
     838         410 :                 tevent_loop_wait(ev2);
     839           0 :                 imessaging_dgm_unref_ev(ev2);
     840           0 :                 talloc_free(ev2);
     841           0 :                 exit(0);
     842             :         }
     843         411 : }
     844             : /*
     845             :  * called to create a new server task
     846             :  */
     847         392 : static void prefork_new_task(
     848             :         struct tevent_context *ev,
     849             :         struct loadparm_context *lp_ctx,
     850             :         const char *service_name,
     851             :         struct task_server *(*new_task_fn)(struct tevent_context *,
     852             :                             struct loadparm_context *lp_ctx,
     853             :                             struct server_id , void *, void *),
     854             :         void *private_data,
     855             :         const struct service_details *service_details,
     856             :         int from_parent_fd)
     857             : {
     858         392 :         prefork_fork_master(ev,
     859             :                             lp_ctx,
     860             :                             service_name,
     861             :                             new_task_fn,
     862             :                             private_data,
     863             :                             service_details,
     864             :                             0,
     865             :                             from_parent_fd);
     866             : 
     867         392 : }
     868             : 
     869             : /*
     870             :  * called when a task terminates
     871             :  */
     872           0 : static void prefork_terminate_task(struct tevent_context *ev,
     873             :                                    struct loadparm_context *lp_ctx,
     874             :                                    const char *reason,
     875             :                                    bool fatal,
     876             :                                    void *process_context)
     877             : {
     878           0 :         DBG_DEBUG("called with reason[%s]\n", reason);
     879           0 :         TALLOC_FREE(ev);
     880           0 :         if (fatal == true) {
     881           0 :                 exit(127);
     882             :         } else {
     883           0 :                 exit(0);
     884             :         }
     885             : }
     886             : 
     887             : /*
     888             :  * called when a connection completes
     889             :  */
     890       46333 : static void prefork_terminate_connection(struct tevent_context *ev,
     891             :                                          struct loadparm_context *lp_ctx,
     892             :                                          const char *reason,
     893             :                                          void *process_context)
     894             : {
     895       46333 : }
     896             : 
     897             : /* called to set a title of a task or connection */
     898       46733 : static void prefork_set_title(struct tevent_context *ev, const char *title)
     899             : {
     900       46733 : }
     901             : 
     902             : static const struct model_ops prefork_ops = {
     903             :         .name                   = "prefork",
     904             :         .model_init             = prefork_model_init,
     905             :         .accept_connection      = prefork_accept_connection,
     906             :         .new_task               = prefork_new_task,
     907             :         .terminate_task         = prefork_terminate_task,
     908             :         .terminate_connection   = prefork_terminate_connection,
     909             :         .set_title              = prefork_set_title,
     910             : };
     911             : 
     912             : /*
     913             :  * initialise the prefork process model, registering ourselves with the
     914             :  * process model subsystem
     915             :  */
     916          68 : NTSTATUS process_model_prefork_init(void)
     917             : {
     918          68 :         return register_process_model(&prefork_ops);
     919             : }

Generated by: LCOV version 1.14