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

Generated by: LCOV version 1.13