[PATCH] Add a prefork process model

Gary Lockyer gary at catalyst.net.nz
Mon Sep 25 18:32:08 UTC 2017


Updated patch set attached, adds documentation of the process models to
man 8 samba.

Reviews greatly appreciated, I'd like to get this one finished.

Thanks
Gary

On 19/09/17 17:11, Uri Simchoni via samba-technical wrote:
> On 09/19/2017 07:03 AM, Andrew Bartlett wrote:
>> On Tue, 2017-09-19 at 06:55 +0300, Uri Simchoni via samba-technical
>> wrote:
>>> On 09/18/2017 11:13 PM, Gary Lockyer via samba-technical wrote:
>>>> Patch set to add a pre-fork process model
>>>>
>>>> Reviews appreciated
>>>> Gary
>>>>
>>>
>>>
>>>> This option controls the number of worker processes that are
>>>> +		started for each service when prefork process
>>>> model is enabled
>>>
>>> Please correct me if I'm wrong, but isn't "process model" a
>>> Samba-devel-internal concept?
>>
>> Sort of.  It is a concept that extends beyond Samba from Apache HTTPD,
>> eg:
>>
>> http://httpd.apache.org/dev/whiteboard/process-model.html
>> http://httpd.apache.org/docs/2.2/mod/
>>
>> It maps to this option to 'samba':
>> -M, --model=MODEL                  Select process model
>>
>> Naturally better text would be most helpful in both spots, if you have
>> some ideas.
>>
> 
> To be perfectly honest, I have only a vague idea of what "process model"
> is. Having to know this never was part of my Samba work. One time I got
> curious (maybe because for me, "Standard Model" is a term from particle
> physics), so I skimmed Wiki and past mailing list threads and couldn't
> find a useful answer. So this is why when a new patch set introducing a
> new process model appeared, I had a look at the additions to the man
> page and what I found was somewhat tautological. I hope you can see my
> frustration - this is not nitpicking.
> 
> I now see we have a rather useful definition in the samba.8 man page
> (maybe it is incomplete but it sets the context). Reading that
> definition helped me understand (hopefully) what the "prefork process
> model" is all about. So what I'd like to propose is that "prefork" gets
> covered in the samba.8 manpage (perhaps mentioning that only ldap
> supports it and with other services it decays into single), and add a
> reference to that on smb.conf.5.
> 
> Thanks,
> Uri.
>> Thanks,
>>
>> Andrew Bartlett
>>
> 
> 
-------------- next part --------------
From 7742a4c92ab9d25262450ce9506b7967dd79c247 Mon Sep 17 00:00:00 2001
From: Gary Lockyer <gary at catalyst.net.nz>
Date: Wed, 13 Sep 2017 11:21:02 +1200
Subject: [PATCH 1/9] tests: Add a blackbox test for smbcontrol

Add tests to check that samba processes have started and that they can be
pinged.

Signed-off-by: Gary Lockyer <gary at catalyst.net.nz>
Reviewed-by: Andrew Bartlett <abartlet at samba.org>
---
 python/samba/tests/blackbox/smbcontrol.py | 50 +++++++++++++++++++++++++++++++
 source4/selftest/tests.py                 |  7 +++++
 2 files changed, 57 insertions(+)
 create mode 100644 python/samba/tests/blackbox/smbcontrol.py

diff --git a/python/samba/tests/blackbox/smbcontrol.py b/python/samba/tests/blackbox/smbcontrol.py
new file mode 100644
index 0000000..e151761
--- /dev/null
+++ b/python/samba/tests/blackbox/smbcontrol.py
@@ -0,0 +1,50 @@
+# Blackbox tests for smbcontrol
+#
+# Copyright (C) Catalyst IT Ltd. 2017
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+
+from samba.tests import BlackboxTestCase
+from samba.messaging import Messaging
+
+COMMAND = "bin/smbcontrol"
+PING    = "ping"
+USAGE   = "pool-usage"
+
+
+class SmbcontrolBlockboxTests(BlackboxTestCase):
+
+    def setUp(self):
+        super(SmbcontrolBlockboxTests, self).setUp()
+        lp_ctx = self.get_loadparm()
+        self.msg_ctx = Messaging(lp_ctx=lp_ctx)
+
+    def test_expected_processes(self):
+        """
+        Test that the expected samba processes are running, currently we only
+        check that at least one process is running
+        """
+        processes = self.msg_ctx.irpc_all_servers()
+        if not processes:
+            self.fail("No samba processes returned")
+
+    def test_ping(self):
+        """Test that all the samba processes can be pinged"""
+
+        processes = self.msg_ctx.irpc_all_servers()
+        for p in processes:
+            for id in p.ids:
+                if p.name != "samba":
+                    self.check_run("%s %d %s" % (COMMAND, id.pid, PING))
diff --git a/source4/selftest/tests.py b/source4/selftest/tests.py
index f8d2229..0feb120 100755
--- a/source4/selftest/tests.py
+++ b/source4/selftest/tests.py
@@ -703,6 +703,13 @@ planoldpythontestsuite("ad_dc_ntvfs",
 planoldpythontestsuite("ad_dc_ntvfs",
                        "samba.tests.blackbox.traffic_summary",
                        extra_args=['-U"$USERNAME%$PASSWORD"'])
+#
+# Want a selection of environments across the process models
+#
+for env in ["ad_dc_ntvfs:local", "ad_dc:local",
+            "fl2003dc:local", "fl2008rdc:local",
+            "promoted_dc:local"]:
+    planoldpythontestsuite(env, "samba.tests.blackbox.smbcontrol")
 
 plantestsuite_loadlist("samba4.ldap.python(ad_dc_ntvfs)", "ad_dc_ntvfs", [python, os.path.join(samba4srcdir, "dsdb/tests/python/ldap.py"), '$SERVER', '-U"$USERNAME%$PASSWORD"', '--workgroup=$DOMAIN', '$LOADLIST', '$LISTOPT'])
 plantestsuite_loadlist("samba4.tokengroups.krb5.python(ad_dc_ntvfs)", "ad_dc_ntvfs:local", [python, os.path.join(samba4srcdir, "dsdb/tests/python/token_group.py"), '$SERVER', '-U"$USERNAME%$PASSWORD"', '--workgroup=$DOMAIN', '-k', 'yes', '$LOADLIST', '$LISTOPT'])
-- 
2.7.4


From 80a0276d5bee18c0bf6e9e79f91b1256e16b4213 Mon Sep 17 00:00:00 2001
From: Gary Lockyer <gary at catalyst.net.nz>
Date: Fri, 15 Sep 2017 07:09:23 +1200
Subject: [PATCH 2/9]  source4/smbd: refactor the process model for prefork

    Refactor the process model code to allow the addition of a prefork
    process model.

    - Add a process context to contain process model specific state
    - Add a service details structure to allow service to indicate which
      process model options they can support.

    In the new code the services advertise the features they support to the
    process model.  The process model context is plumbed through to allow the
    process model to keep track of the supported options, and any state
    the process model may require.

Signed-off-by: Gary Lockyer <gary at catalyst.net.nz>
Reviewed-by: Andrew Bartlett <abartlet at samba.org>
---
 file_server/file_server.c                  |  6 +++-
 source4/cldap_server/cldap_server.c        |  7 +++-
 source4/dns_server/dns_server.c            |  9 +++--
 source4/dsdb/dns/dns_update.c              |  7 +++-
 source4/dsdb/kcc/kcc_service.c             |  6 +++-
 source4/dsdb/repl/drepl_service.c          |  7 +++-
 source4/echo_server/echo_server.c          |  6 +++-
 source4/kdc/kdc-heimdal.c                  | 17 ++++++++-
 source4/kdc/kdc-server.c                   |  3 +-
 source4/kdc/kdc-service-mit.c              | 18 +++++++++-
 source4/ldap_server/ldap_server.c          | 23 ++++++++----
 source4/nbt_server/nbt_server.c            |  6 +++-
 source4/ntp_signd/ntp_signd.c              | 10 ++++--
 source4/rpc_server/dcerpc_server.c         | 56 ++++++++++++++++++++----------
 source4/rpc_server/dcerpc_server.h         |  3 +-
 source4/rpc_server/service_rpc.c           | 15 ++++++--
 source4/smb_server/service_smb.c           | 18 ++++++++--
 source4/smb_server/smb_server.c            |  5 +--
 source4/smb_server/smb_server.h            |  3 +-
 source4/smbd/process_model.h               | 15 ++++----
 source4/smbd/process_single.c              | 21 +++++++----
 source4/smbd/process_standard.c            | 32 ++++++++++-------
 source4/smbd/service.c                     | 11 ++++--
 source4/smbd/service.h                     | 18 ++++++++++
 source4/smbd/service_named_pipe.c          |  6 ++--
 source4/smbd/service_stream.c              | 23 ++++++++----
 source4/smbd/service_stream.h              |  1 +
 source4/smbd/service_task.c                | 20 ++++++-----
 source4/smbd/service_task.h                |  1 +
 source4/torture/rpc/spoolss_notify.c       |  7 ++--
 source4/web_server/web_server.c            | 15 +++++---
 source4/winbind/winbindd.c                 | 11 ++++--
 source4/wrepl_server/wrepl_in_connection.c | 10 +++---
 source4/wrepl_server/wrepl_out_helpers.c   |  4 ++-
 source4/wrepl_server/wrepl_server.c        |  7 +++-
 35 files changed, 322 insertions(+), 105 deletions(-)

diff --git a/file_server/file_server.c b/file_server/file_server.c
index 83641f8..20fa577 100644
--- a/file_server/file_server.c
+++ b/file_server/file_server.c
@@ -97,5 +97,9 @@ NTSTATUS server_service_s3fs_init(TALLOC_CTX *);
 
 NTSTATUS server_service_s3fs_init(TALLOC_CTX *ctx)
 {
-	return register_server_service(ctx, "s3fs", s3fs_task_init);
+	struct service_details details = {
+		.inhibit_fork_on_accept = true,
+		.inhibit_pre_fork = true
+	};
+	return register_server_service(ctx, "s3fs", s3fs_task_init, &details);
 }
diff --git a/source4/cldap_server/cldap_server.c b/source4/cldap_server/cldap_server.c
index 35a2b99..3f845c7 100644
--- a/source4/cldap_server/cldap_server.c
+++ b/source4/cldap_server/cldap_server.c
@@ -243,5 +243,10 @@ static void cldapd_task_init(struct task_server *task)
 */
 NTSTATUS server_service_cldapd_init(TALLOC_CTX *ctx)
 {
-	return register_server_service(ctx, "cldap", cldapd_task_init);
+	struct service_details details = {
+		.inhibit_fork_on_accept = true,
+		.inhibit_pre_fork = true
+	};
+	return register_server_service(ctx, "cldap", cldapd_task_init,
+				       &details);
 }
diff --git a/source4/dns_server/dns_server.c b/source4/dns_server/dns_server.c
index d4f5f27..6a90e30 100644
--- a/source4/dns_server/dns_server.c
+++ b/source4/dns_server/dns_server.c
@@ -642,7 +642,8 @@ static NTSTATUS dns_add_socket(struct dns_server *dns,
 				     &dns_tcp_stream_ops,
 				     "ip", address, &port,
 				     lpcfg_socket_options(dns->task->lp_ctx),
-				     dns_socket);
+				     dns_socket,
+				     dns->task->process_context);
 	if (!NT_STATUS_IS_OK(status)) {
 		DEBUG(0,("Failed to bind to %s:%u TCP - %s\n",
 			 address, port, nt_errstr(status)));
@@ -929,5 +930,9 @@ static void dns_task_init(struct task_server *task)
 
 NTSTATUS server_service_dns_init(TALLOC_CTX *ctx)
 {
-	return register_server_service(ctx, "dns", dns_task_init);
+	struct service_details details = {
+		.inhibit_fork_on_accept = true,
+		.inhibit_pre_fork = true,
+	};
+	return register_server_service(ctx, "dns", dns_task_init, &details);
 }
diff --git a/source4/dsdb/dns/dns_update.c b/source4/dsdb/dns/dns_update.c
index ba8431a..10be7ce 100644
--- a/source4/dsdb/dns/dns_update.c
+++ b/source4/dsdb/dns/dns_update.c
@@ -707,5 +707,10 @@ static void dnsupdate_task_init(struct task_server *task)
 */
 NTSTATUS server_service_dnsupdate_init(TALLOC_CTX *ctx)
 {
-	return register_server_service(ctx, "dnsupdate", dnsupdate_task_init);
+	struct service_details details = {
+		.inhibit_fork_on_accept = true,
+		.inhibit_pre_fork = true,
+	};
+	return register_server_service(ctx, "dnsupdate", dnsupdate_task_init,
+				       &details);
 }
diff --git a/source4/dsdb/kcc/kcc_service.c b/source4/dsdb/kcc/kcc_service.c
index 946a8fe..a5508af 100644
--- a/source4/dsdb/kcc/kcc_service.c
+++ b/source4/dsdb/kcc/kcc_service.c
@@ -347,5 +347,9 @@ static void kccsrv_task_init(struct task_server *task)
 */
 NTSTATUS server_service_kcc_init(TALLOC_CTX *ctx)
 {
-	return register_server_service(ctx, "kcc", kccsrv_task_init);
+	struct service_details details = {
+		.inhibit_fork_on_accept = true,
+		.inhibit_pre_fork = true
+	};
+	return register_server_service(ctx, "kcc", kccsrv_task_init, &details);
 }
diff --git a/source4/dsdb/repl/drepl_service.c b/source4/dsdb/repl/drepl_service.c
index 0951a0f..8f16a2e 100644
--- a/source4/dsdb/repl/drepl_service.c
+++ b/source4/dsdb/repl/drepl_service.c
@@ -528,5 +528,10 @@ static void dreplsrv_task_init(struct task_server *task)
 */
 NTSTATUS server_service_drepl_init(TALLOC_CTX *ctx)
 {
-	return register_server_service(ctx, "drepl", dreplsrv_task_init);
+	struct service_details details = {
+		.inhibit_fork_on_accept = true,
+		.inhibit_pre_fork = true,
+	};
+	return register_server_service(ctx, "drepl",  dreplsrv_task_init,
+				       &details);
 }
diff --git a/source4/echo_server/echo_server.c b/source4/echo_server/echo_server.c
index e610232..9a66211 100644
--- a/source4/echo_server/echo_server.c
+++ b/source4/echo_server/echo_server.c
@@ -343,5 +343,9 @@ static void echo_task_init(struct task_server *task)
  */
 NTSTATUS server_service_echo_init(TALLOC_CTX *ctx)
 {
-	return register_server_service(ctx, "echo", echo_task_init);
+	struct service_details details = {
+		.inhibit_fork_on_accept = true,
+		.inhibit_pre_fork = true
+	};
+	return register_server_service(ctx, "echo", echo_task_init, &details);
 }
diff --git a/source4/kdc/kdc-heimdal.c b/source4/kdc/kdc-heimdal.c
index d5c721b..788959a 100644
--- a/source4/kdc/kdc-heimdal.c
+++ b/source4/kdc/kdc-heimdal.c
@@ -468,5 +468,20 @@ static void kdc_task_init(struct task_server *task)
 /* called at smbd startup - register ourselves as a server service */
 NTSTATUS server_service_kdc_init(TALLOC_CTX *ctx)
 {
-	return register_server_service(ctx, "kdc", kdc_task_init);
+	struct service_details details = {
+		.inhibit_fork_on_accept = true,
+		/* 
+		 * Need to prevent pre-forking on kdc.
+		 * The task_init function is run on the master process only
+		 * and the irpc process name is registered in it's event loop.
+		 * The child worker processes initialise their event loops on
+		 * fork, so are not listening for the irpc event.
+		 *
+		 * The master process does not wait on that event context
+		 * the master process is responsible for managing the worker
+		 * processes not performing work.
+		 */
+		.inhibit_pre_fork = true
+	};
+	return register_server_service(ctx, "kdc", kdc_task_init, &details);
 }
diff --git a/source4/kdc/kdc-server.c b/source4/kdc/kdc-server.c
index 13e338d..00b5c74 100644
--- a/source4/kdc/kdc-server.c
+++ b/source4/kdc/kdc-server.c
@@ -578,7 +578,8 @@ NTSTATUS kdc_add_socket(struct kdc_server *kdc,
 					     &kdc_tcp_stream_ops,
 					     "ip", address, &port,
 					     lpcfg_socket_options(kdc->task->lp_ctx),
-					     kdc_socket);
+					     kdc_socket,
+					     kdc->task->process_context);
 		if (!NT_STATUS_IS_OK(status)) {
 			DEBUG(0,("Failed to bind to %s:%u TCP - %s\n",
 				 address, port, nt_errstr(status)));
diff --git a/source4/kdc/kdc-service-mit.c b/source4/kdc/kdc-service-mit.c
index 7febf15..d48b032 100644
--- a/source4/kdc/kdc-service-mit.c
+++ b/source4/kdc/kdc-service-mit.c
@@ -358,5 +358,21 @@ NTSTATUS server_service_mitkdc_init(TALLOC_CTX *mem_ctx);
 
 NTSTATUS server_service_mitkdc_init(TALLOC_CTX *mem_ctx)
 {
-	return register_server_service(mem_ctx, "kdc", mitkdc_task_init);
+	struct service_details details = {
+		.inhibit_fork_on_accept = true,
+		/* 
+		 * Need to prevent pre-forking on kdc.
+		 * The task_init function is run on the master process only
+		 * and the irpc process name is registered in it's event loop.
+		 * The child worker processes initialise their event loops on
+		 * fork, so are not listening for the irpc event.
+		 *
+		 * The master process does not wait on that event context
+		 * the master process is responsible for managing the worker
+		 * processes not performing work.
+		 */
+		.inhibit_pre_fork = true
+	};
+	return register_server_service(mem_ctx, "kdc", mitkdc_task_init,
+				       &details);
 }
diff --git a/source4/ldap_server/ldap_server.c b/source4/ldap_server/ldap_server.c
index 7730ff9..d12c4e7 100644
--- a/source4/ldap_server/ldap_server.c
+++ b/source4/ldap_server/ldap_server.c
@@ -1030,7 +1030,7 @@ static NTSTATUS add_socket(struct task_server *task,
 				     model_ops, &ldap_stream_nonpriv_ops,
 				     "ip", address, &port,
 				     lpcfg_socket_options(lp_ctx),
-				     ldap_service);
+				     ldap_service, task->process_context);
 	if (!NT_STATUS_IS_OK(status)) {
 		DEBUG(0,("ldapsrv failed to bind to %s:%u - %s\n",
 			 address, port, nt_errstr(status)));
@@ -1045,7 +1045,8 @@ static NTSTATUS add_socket(struct task_server *task,
 					     &ldap_stream_nonpriv_ops,
 					     "ip", address, &port,
 					     lpcfg_socket_options(lp_ctx),
-					     ldap_service);
+					     ldap_service,
+					     task->process_context);
 		if (!NT_STATUS_IS_OK(status)) {
 			DEBUG(0,("ldapsrv failed to bind to %s:%u - %s\n",
 				 address, port, nt_errstr(status)));
@@ -1067,7 +1068,8 @@ static NTSTATUS add_socket(struct task_server *task,
 					     &ldap_stream_nonpriv_ops,
 					     "ip", address, &port,
 					     lpcfg_socket_options(lp_ctx),
-					     ldap_service);
+					     ldap_service,
+					     task->process_context);
 		if (!NT_STATUS_IS_OK(status)) {
 			DEBUG(0,("ldapsrv failed to bind to %s:%u - %s\n",
 				 address, port, nt_errstr(status)));
@@ -1081,7 +1083,8 @@ static NTSTATUS add_socket(struct task_server *task,
 						     &ldap_stream_nonpriv_ops,
 						     "ip", address, &port,
 						     lpcfg_socket_options(lp_ctx),
-						     ldap_service);
+						     ldap_service,
+						     task->process_context);
 			if (!NT_STATUS_IS_OK(status)) {
 				DEBUG(0,("ldapsrv failed to bind to %s:%u - %s\n",
 					 address, port, nt_errstr(status)));
@@ -1210,7 +1213,7 @@ static void ldapsrv_task_init(struct task_server *task)
 				     model_ops, &ldap_stream_nonpriv_ops,
 				     "unix", ldapi_path, NULL, 
 				     lpcfg_socket_options(task->lp_ctx),
-				     ldap_service);
+				     ldap_service, task->process_context);
 	talloc_free(ldapi_path);
 	if (!NT_STATUS_IS_OK(status)) {
 		DEBUG(0,("ldapsrv failed to bind to %s - %s\n",
@@ -1241,7 +1244,8 @@ static void ldapsrv_task_init(struct task_server *task)
 				     model_ops, &ldap_stream_priv_ops,
 				     "unix", ldapi_path, NULL,
 				     lpcfg_socket_options(task->lp_ctx),
-				     ldap_service);
+				     ldap_service,
+				     task->process_context);
 	talloc_free(ldapi_path);
 	if (!NT_STATUS_IS_OK(status)) {
 		DEBUG(0,("ldapsrv failed to bind to %s - %s\n",
@@ -1261,5 +1265,10 @@ failed:
 
 NTSTATUS server_service_ldap_init(TALLOC_CTX *ctx)
 {
-	return register_server_service(ctx, "ldap", ldapsrv_task_init);
+	struct service_details details = {
+		.inhibit_fork_on_accept = false,
+		.inhibit_pre_fork = false
+	};
+	return register_server_service(ctx, "ldap", ldapsrv_task_init,
+				       &details);
 }
diff --git a/source4/nbt_server/nbt_server.c b/source4/nbt_server/nbt_server.c
index 2196bb0..834c72f 100644
--- a/source4/nbt_server/nbt_server.c
+++ b/source4/nbt_server/nbt_server.c
@@ -100,5 +100,9 @@ static void nbtd_task_init(struct task_server *task)
 */
 NTSTATUS server_service_nbtd_init(TALLOC_CTX *ctx)
 {
-	return register_server_service(ctx, "nbt", nbtd_task_init);
+	struct service_details details = {
+		.inhibit_fork_on_accept = true,
+		.inhibit_pre_fork = true
+	};
+	return register_server_service(ctx, "nbt", nbtd_task_init, &details);
 }
diff --git a/source4/ntp_signd/ntp_signd.c b/source4/ntp_signd/ntp_signd.c
index 7949dc2f..0884d2f 100644
--- a/source4/ntp_signd/ntp_signd.c
+++ b/source4/ntp_signd/ntp_signd.c
@@ -541,7 +541,8 @@ static void ntp_signd_task_init(struct task_server *task)
 				     &ntp_signd_stream_ops, 
 				     "unix", address, NULL,
 				     lpcfg_socket_options(ntp_signd->task->lp_ctx),
-				     ntp_signd);
+				     ntp_signd,
+				     ntp_signd->task->process_context);
 	if (!NT_STATUS_IS_OK(status)) {
 		DEBUG(0,("Failed to bind to %s - %s\n",
 			 address, nt_errstr(status)));
@@ -554,5 +555,10 @@ static void ntp_signd_task_init(struct task_server *task)
 /* called at smbd startup - register ourselves as a server service */
 NTSTATUS server_service_ntp_signd_init(TALLOC_CTX *ctx)
 {
-	return register_server_service(ctx, "ntp_signd", ntp_signd_task_init);
+	struct service_details details = {
+		.inhibit_fork_on_accept = true,
+		.inhibit_pre_fork = true
+	};
+	return register_server_service(ctx, "ntp_signd", ntp_signd_task_init,
+				       &details);
 }
diff --git a/source4/rpc_server/dcerpc_server.c b/source4/rpc_server/dcerpc_server.c
index 6a985c5..24eaa65 100644
--- a/source4/rpc_server/dcerpc_server.c
+++ b/source4/rpc_server/dcerpc_server.c
@@ -2870,7 +2870,9 @@ static const struct stream_server_ops dcesrv_stream_ops = {
 static NTSTATUS dcesrv_add_ep_unix(struct dcesrv_context *dce_ctx, 
 				   struct loadparm_context *lp_ctx,
 				   struct dcesrv_endpoint *e,
-			    struct tevent_context *event_ctx, const struct model_ops *model_ops)
+				   struct tevent_context *event_ctx,
+				   const struct model_ops *model_ops,
+				   void *process_context)
 {
 	struct dcesrv_socket_context *dcesrv_sock;
 	uint16_t port = 1;
@@ -2890,7 +2892,7 @@ static NTSTATUS dcesrv_add_ep_unix(struct dcesrv_context *dce_ctx,
 				     model_ops, &dcesrv_stream_ops, 
 				     "unix", endpoint, &port,
 				     lpcfg_socket_options(lp_ctx),
-				     dcesrv_sock);
+				     dcesrv_sock, process_context);
 	if (!NT_STATUS_IS_OK(status)) {
 		DEBUG(0,("service_setup_stream_socket(path=%s) failed - %s\n",
 			 endpoint, nt_errstr(status)));
@@ -2902,7 +2904,9 @@ static NTSTATUS dcesrv_add_ep_unix(struct dcesrv_context *dce_ctx,
 static NTSTATUS dcesrv_add_ep_ncalrpc(struct dcesrv_context *dce_ctx, 
 				      struct loadparm_context *lp_ctx,
 				      struct dcesrv_endpoint *e,
-				      struct tevent_context *event_ctx, const struct model_ops *model_ops)
+				      struct tevent_context *event_ctx,
+				      const struct model_ops *model_ops,
+				      void *process_context)
 {
 	struct dcesrv_socket_context *dcesrv_sock;
 	uint16_t port = 1;
@@ -2944,7 +2948,7 @@ static NTSTATUS dcesrv_add_ep_ncalrpc(struct dcesrv_context *dce_ctx,
 				     model_ops, &dcesrv_stream_ops, 
 				     "unix", full_path, &port, 
 				     lpcfg_socket_options(lp_ctx),
-				     dcesrv_sock);
+				     dcesrv_sock, process_context);
 	if (!NT_STATUS_IS_OK(status)) {
 		DEBUG(0,("service_setup_stream_socket(identifier=%s,path=%s) failed - %s\n",
 			 endpoint, full_path, nt_errstr(status)));
@@ -2955,7 +2959,9 @@ static NTSTATUS dcesrv_add_ep_ncalrpc(struct dcesrv_context *dce_ctx,
 static NTSTATUS dcesrv_add_ep_np(struct dcesrv_context *dce_ctx,
 				 struct loadparm_context *lp_ctx,
 				 struct dcesrv_endpoint *e,
-				 struct tevent_context *event_ctx, const struct model_ops *model_ops)
+				 struct tevent_context *event_ctx,
+				 const struct model_ops *model_ops,
+				 void *process_context)
 {
 	struct dcesrv_socket_context *dcesrv_sock;
 	NTSTATUS status;
@@ -2977,7 +2983,7 @@ static NTSTATUS dcesrv_add_ep_np(struct dcesrv_context *dce_ctx,
 	status = tstream_setup_named_pipe(dce_ctx, event_ctx, lp_ctx,
 					  model_ops, &dcesrv_stream_ops,
 					  endpoint,
-					  dcesrv_sock);
+					  dcesrv_sock, process_context);
 	if (!NT_STATUS_IS_OK(status)) {
 		DEBUG(0,("stream_setup_named_pipe(pipe=%s) failed - %s\n",
 			 endpoint, nt_errstr(status)));
@@ -2990,9 +2996,12 @@ static NTSTATUS dcesrv_add_ep_np(struct dcesrv_context *dce_ctx,
 /*
   add a socket address to the list of events, one event per dcerpc endpoint
 */
-static NTSTATUS add_socket_rpc_tcp_iface(struct dcesrv_context *dce_ctx, struct dcesrv_endpoint *e,
-					 struct tevent_context *event_ctx, const struct model_ops *model_ops,
-					 const char *address)
+static NTSTATUS add_socket_rpc_tcp_iface(struct dcesrv_context *dce_ctx,
+					 struct dcesrv_endpoint *e,
+					 struct tevent_context *event_ctx,
+					 const struct model_ops *model_ops,
+					 const char *address,
+					 void *process_context)
 {
 	struct dcesrv_socket_context *dcesrv_sock;
 	uint16_t port = 0;
@@ -3016,7 +3025,7 @@ static NTSTATUS add_socket_rpc_tcp_iface(struct dcesrv_context *dce_ctx, struct
 				     model_ops, &dcesrv_stream_ops, 
 				     "ip", address, &port,
 				     lpcfg_socket_options(dce_ctx->lp_ctx),
-				     dcesrv_sock);
+				     dcesrv_sock, process_context);
 	if (!NT_STATUS_IS_OK(status)) {
 		struct dcesrv_if_list *iface;
 		DEBUG(0,("service_setup_stream_socket(address=%s,port=%u) for ",
@@ -3055,7 +3064,9 @@ static NTSTATUS add_socket_rpc_tcp_iface(struct dcesrv_context *dce_ctx, struct
 static NTSTATUS dcesrv_add_ep_tcp(struct dcesrv_context *dce_ctx, 
 				  struct loadparm_context *lp_ctx,
 				  struct dcesrv_endpoint *e,
-				  struct tevent_context *event_ctx, const struct model_ops *model_ops)
+				  struct tevent_context *event_ctx,
+				  const struct model_ops *model_ops,
+				  void *process_context)
 {
 	NTSTATUS status;
 
@@ -3070,7 +3081,9 @@ static NTSTATUS dcesrv_add_ep_tcp(struct dcesrv_context *dce_ctx,
 		num_interfaces = iface_list_count(ifaces);
 		for(i = 0; i < num_interfaces; i++) {
 			const char *address = iface_list_n_ip(ifaces, i);
-			status = add_socket_rpc_tcp_iface(dce_ctx, e, event_ctx, model_ops, address);
+			status = add_socket_rpc_tcp_iface(dce_ctx, e, event_ctx,
+					                  model_ops, address,
+							  process_context);
 			NT_STATUS_NOT_OK_RETURN(status);
 		}
 	} else {
@@ -3080,7 +3093,9 @@ static NTSTATUS dcesrv_add_ep_tcp(struct dcesrv_context *dce_ctx,
 		wcard = iface_list_wildcard(dce_ctx);
 		NT_STATUS_HAVE_NO_MEMORY(wcard);
 		for (i=0; wcard[i]; i++) {
-			status = add_socket_rpc_tcp_iface(dce_ctx, e, event_ctx, model_ops, wcard[i]);
+			status = add_socket_rpc_tcp_iface(dce_ctx, e, event_ctx,
+							  model_ops, wcard[i],
+							  process_context);
 			if (NT_STATUS_IS_OK(status)) {
 				num_binds++;
 			}
@@ -3098,23 +3113,28 @@ NTSTATUS dcesrv_add_ep(struct dcesrv_context *dce_ctx,
 		       struct loadparm_context *lp_ctx,
 		       struct dcesrv_endpoint *e,
 		       struct tevent_context *event_ctx,
-		       const struct model_ops *model_ops)
+		       const struct model_ops *model_ops,
+		       void *process_context)
 {
 	enum dcerpc_transport_t transport =
 		dcerpc_binding_get_transport(e->ep_description);
 
 	switch (transport) {
 	case NCACN_UNIX_STREAM:
-		return dcesrv_add_ep_unix(dce_ctx, lp_ctx, e, event_ctx, model_ops);
+		return dcesrv_add_ep_unix(dce_ctx, lp_ctx, e, event_ctx,
+					  model_ops, process_context);
 
 	case NCALRPC:
-		return dcesrv_add_ep_ncalrpc(dce_ctx, lp_ctx, e, event_ctx, model_ops);
+		return dcesrv_add_ep_ncalrpc(dce_ctx, lp_ctx, e, event_ctx,
+					     model_ops, process_context);
 
 	case NCACN_IP_TCP:
-		return dcesrv_add_ep_tcp(dce_ctx, lp_ctx, e, event_ctx, model_ops);
+		return dcesrv_add_ep_tcp(dce_ctx, lp_ctx, e, event_ctx,
+					 model_ops, process_context);
 
 	case NCACN_NP:
-		return dcesrv_add_ep_np(dce_ctx, lp_ctx, e, event_ctx, model_ops);
+		return dcesrv_add_ep_np(dce_ctx, lp_ctx, e, event_ctx,
+					model_ops, process_context);
 
 	default:
 		return NT_STATUS_NOT_SUPPORTED;
diff --git a/source4/rpc_server/dcerpc_server.h b/source4/rpc_server/dcerpc_server.h
index c038075..f2fb0f6 100644
--- a/source4/rpc_server/dcerpc_server.h
+++ b/source4/rpc_server/dcerpc_server.h
@@ -467,7 +467,8 @@ NTSTATUS dcesrv_add_ep(struct dcesrv_context *dce_ctx,
 		       struct loadparm_context *lp_ctx,
 		       struct dcesrv_endpoint *e,
 		       struct tevent_context *event_ctx,
-		       const struct model_ops *model_ops);
+		       const struct model_ops *model_ops,
+		       void *process_context);
 
 /**
  * retrieve credentials from a dce_call
diff --git a/source4/rpc_server/service_rpc.c b/source4/rpc_server/service_rpc.c
index 3ff9f6f..eb75184 100644
--- a/source4/rpc_server/service_rpc.c
+++ b/source4/rpc_server/service_rpc.c
@@ -116,7 +116,7 @@ static void dcesrv_task_init(struct task_server *task)
 		}
 
 		status = dcesrv_add_ep(dce_ctx, task->lp_ctx, e, task->event_ctx,
-				       this_model_ops);
+				       this_model_ops, task->process_context);
 		if (!NT_STATUS_IS_OK(status)) {
 			goto failed;
 		}
@@ -142,5 +142,16 @@ failed:
 
 NTSTATUS server_service_rpc_init(TALLOC_CTX *ctx)
 {
-	return register_server_service(ctx, "rpc", dcesrv_task_init);
+	struct service_details details = {
+		/* 
+		 * This is a SNOWFLAKE, but sadly one that we
+		 * will have to keep for now.  The RPC server
+		 * code above overstamps the SINGLE process model
+		 * most of the time, but we need to be in forking
+		 * mode by defult to get a forking NETLOGON server
+		 */
+		.inhibit_fork_on_accept = false,
+		.inhibit_pre_fork = true
+	};
+	return register_server_service(ctx, "rpc", dcesrv_task_init, &details);
 }
diff --git a/source4/smb_server/service_smb.c b/source4/smb_server/service_smb.c
index a607861..ddf24a9 100644
--- a/source4/smb_server/service_smb.c
+++ b/source4/smb_server/service_smb.c
@@ -60,7 +60,11 @@ static void smbsrv_task_init(struct task_server *task)
 		*/
 		for(i = 0; i < num_interfaces; i++) {
 			const char *address = iface_list_n_ip(ifaces, i);
-			status = smbsrv_add_socket(task, task->event_ctx, task->lp_ctx, task->model_ops, address);
+			status = smbsrv_add_socket(task, task->event_ctx,
+					           task->lp_ctx,
+						   task->model_ops,
+						   address,
+						   task->process_context);
 			if (!NT_STATUS_IS_OK(status)) goto failed;
 		}
 	} else {
@@ -72,7 +76,11 @@ static void smbsrv_task_init(struct task_server *task)
 			goto failed;
 		}
 		for (i=0; wcard[i]; i++) {
-			status = smbsrv_add_socket(task, task->event_ctx, task->lp_ctx, task->model_ops, wcard[i]);
+			status = smbsrv_add_socket(task, task->event_ctx,
+						   task->lp_ctx,
+						   task->model_ops,
+						   wcard[i],
+						   task->process_context);
 			if (!NT_STATUS_IS_OK(status)) goto failed;
 		}
 		talloc_free(wcard);
@@ -87,7 +95,11 @@ failed:
 /* called at smbd startup - register ourselves as a server service */
 NTSTATUS server_service_smb_init(TALLOC_CTX *ctx)
 {
+	struct service_details details = {
+		.inhibit_fork_on_accept = true,
+		.inhibit_pre_fork = true
+	};
 	ntvfs_init(cmdline_lp_ctx);
 	share_init();
-	return register_server_service(ctx, "smb", smbsrv_task_init);
+	return register_server_service(ctx, "smb", smbsrv_task_init, &details);
 }
diff --git a/source4/smb_server/smb_server.c b/source4/smb_server/smb_server.c
index 7e4c032..45641a4 100644
--- a/source4/smb_server/smb_server.c
+++ b/source4/smb_server/smb_server.c
@@ -179,7 +179,8 @@ _PUBLIC_ NTSTATUS smbsrv_add_socket(TALLOC_CTX *mem_ctx,
 				    struct tevent_context *event_context,
 				    struct loadparm_context *lp_ctx,
 				    const struct model_ops *model_ops,
-				    const char *address)
+				    const char *address,
+				    void *process_context)
 {
 	const char **ports = lpcfg_smb_ports(lp_ctx);
 	int i;
@@ -192,7 +193,7 @@ _PUBLIC_ NTSTATUS smbsrv_add_socket(TALLOC_CTX *mem_ctx,
 					     model_ops, &smb_stream_ops, 
 					     "ip", address, &port,
 					     lpcfg_socket_options(lp_ctx),
-					     NULL);
+					     NULL, process_context);
 		NT_STATUS_NOT_OK_RETURN(status);
 	}
 
diff --git a/source4/smb_server/smb_server.h b/source4/smb_server/smb_server.h
index 40af4a6..5ddfe78 100644
--- a/source4/smb_server/smb_server.h
+++ b/source4/smb_server/smb_server.h
@@ -397,7 +397,8 @@ NTSTATUS smbsrv_add_socket(TALLOC_CTX *mem_ctx,
 			   struct tevent_context *event_context,
 			   struct loadparm_context *lp_ctx,
 			   const struct model_ops *model_ops,
-			   const char *address);
+			   const char *address,
+			   void *process_context);
 
 struct loadparm_context;
 
diff --git a/source4/smbd/process_model.h b/source4/smbd/process_model.h
index d7bf3c8..656a7f2 100644
--- a/source4/smbd/process_model.h
+++ b/source4/smbd/process_model.h
@@ -51,8 +51,8 @@ struct model_ops {
 				  void (*)(struct tevent_context *, 
 					   struct loadparm_context *,
 					   struct socket_context *, 
-					   struct server_id , void *), 
-				  void *);
+					   struct server_id , void *, void *),
+				  void *, void *);
 
 	/* function to create a task */
 	void (*new_task)(struct tevent_context *, 
@@ -60,13 +60,16 @@ struct model_ops {
 			 const char *service_name,
 			 void (*)(struct tevent_context *, 
 				  struct loadparm_context *, struct server_id, 
-				  void *),
+				  void *, void *),
 			 void *,
-			 int);
+			 const struct service_details*,
+			 const int);
 
 	/* function to terminate a connection or task */
-	void (*terminate)(struct tevent_context *, struct loadparm_context *lp_ctx,
-			  const char *reason);
+	void (*terminate)(struct tevent_context *,
+			  struct loadparm_context *lp_ctx,
+			  const char *reason,
+			  void * process_context);
 
 	/* function to set a title for the connection or task */
 	void (*set_title)(struct tevent_context *, const char *title);
diff --git a/source4/smbd/process_single.c b/source4/smbd/process_single.c
index 54169e9..1027415 100644
--- a/source4/smbd/process_single.c
+++ b/source4/smbd/process_single.c
@@ -44,8 +44,10 @@ static void single_accept_connection(struct tevent_context *ev,
 				     void (*new_conn)(struct tevent_context *, 
 						      struct loadparm_context *,
 						      struct socket_context *, 
-						      struct server_id , void *), 
-				     void *private_data)
+						      struct server_id, void *,
+						      void *),
+				     void *private_data,
+				     void *process_context)
 {
 	NTSTATUS status;
 	struct socket_context *connected_socket;
@@ -79,7 +81,8 @@ static void single_accept_connection(struct tevent_context *ev,
 	 * combination of pid/fd should be unique system-wide
 	 */
 	new_conn(ev, lp_ctx, connected_socket,
-		 cluster_id(pid, socket_get_fd(connected_socket)), private_data);
+		 cluster_id(pid, socket_get_fd(connected_socket)), private_data,
+			    process_context);
 }
 
 /*
@@ -88,8 +91,11 @@ static void single_accept_connection(struct tevent_context *ev,
 static void single_new_task(struct tevent_context *ev,
 			    struct loadparm_context *lp_ctx,
 			    const char *service_name,
-			    void (*new_task)(struct tevent_context *, struct loadparm_context *, struct server_id, void *),
+			    void (*new_task)(struct tevent_context *,
+				             struct loadparm_context *,
+					     struct server_id, void *, void *),
 			    void *private_data,
+			    const struct service_details *service_details,
 			    int from_parent_fd)
 {
 	pid_t pid = getpid();
@@ -105,12 +111,15 @@ static void single_new_task(struct tevent_context *ev,
 	 * Using the pid unaltered makes debugging of which process
 	 * owns the messaging socket easier.
 	 */
-	new_task(ev, lp_ctx, cluster_id(pid, taskid++), private_data);
+	new_task(ev, lp_ctx, cluster_id(pid, taskid++), private_data, NULL);
 }
 
 
 /* called when a task goes down */
-static void single_terminate(struct tevent_context *ev, struct loadparm_context *lp_ctx, const char *reason)
+static void single_terminate(struct tevent_context *ev,
+			     struct loadparm_context *lp_ctx,
+			     const char *reason,
+			     void *process_context)
 {
 	DEBUG(3,("single_terminate: reason[%s]\n",reason));
 }
diff --git a/source4/smbd/process_standard.c b/source4/smbd/process_standard.c
index c6cbfc2..431134e 100644
--- a/source4/smbd/process_standard.c
+++ b/source4/smbd/process_standard.c
@@ -222,13 +222,18 @@ static struct standard_child_state *setup_standard_child_pipe(struct tevent_cont
 /*
   called when a listening socket becomes readable. 
 */
-static void standard_accept_connection(struct tevent_context *ev, 
-				       struct loadparm_context *lp_ctx,
-				       struct socket_context *sock, 
-				       void (*new_conn)(struct tevent_context *,
-							struct loadparm_context *, struct socket_context *, 
-							struct server_id , void *), 
-				       void *private_data)
+static void standard_accept_connection(
+		struct tevent_context *ev,
+		struct loadparm_context *lp_ctx,
+		struct socket_context *sock,
+		void (*new_conn)(struct tevent_context *,
+				struct loadparm_context *,
+				struct socket_context *,
+				struct server_id,
+				void *,
+				void *),
+		void *private_data,
+		void *process_context)
 {
 	NTSTATUS status;
 	struct socket_context *sock2;
@@ -340,7 +345,8 @@ static void standard_accept_connection(struct tevent_context *ev,
 	talloc_free(s);
 
 	/* setup this new connection.  Cluster ID is PID based for this process model */
-	new_conn(ev, lp_ctx, sock2, cluster_id(pid, 0), private_data);
+	new_conn(ev, lp_ctx, sock2, cluster_id(pid, 0), private_data,
+		 NULL);
 
 	/* we can't return to the top level here, as that event context is gone,
 	   so we now process events in the new event context until there are no
@@ -357,8 +363,9 @@ static void standard_accept_connection(struct tevent_context *ev,
 static void standard_new_task(struct tevent_context *ev,
 			      struct loadparm_context *lp_ctx,
 			      const char *service_name,
-			      void (*new_task)(struct tevent_context *, struct loadparm_context *lp_ctx, struct server_id , void *),
+			      void (*new_task)(struct tevent_context *, struct loadparm_context *lp_ctx, struct server_id , void *, void *),
 			      void *private_data,
+			      const struct service_details *service_details,
 			      int new_from_parent_fd)
 {
 	pid_t pid;
@@ -438,11 +445,11 @@ static void standard_new_task(struct tevent_context *ev,
 	setproctitle("task %s server_id[%d]", service_name, (int)pid);
 
 	/* setup this new task.  Cluster ID is PID based for this process model */
-	new_task(ev, lp_ctx, cluster_id(pid, 0), private_data);
+	new_task(ev, lp_ctx, cluster_id(pid, 0), private_data, NULL);
 
 	/* we can't return to the top level here, as that event context is gone,
 	   so we now process events in the new event context until there are no
-	   more to process */	   
+	   more to process */
 	tevent_loop_wait(ev);
 
 	talloc_free(ev);
@@ -452,7 +459,8 @@ static void standard_new_task(struct tevent_context *ev,
 
 /* called when a task goes down */
 _NORETURN_ static void standard_terminate(struct tevent_context *ev, struct loadparm_context *lp_ctx,
-					  const char *reason) 
+					  const char *reason,
+					  void *process_context) 
 {
 	DEBUG(2,("standard_terminate: reason[%s]\n",reason));
 
diff --git a/source4/smbd/service.c b/source4/smbd/service.c
index 61ed684..9a77ca8 100644
--- a/source4/smbd/service.c
+++ b/source4/smbd/service.c
@@ -30,6 +30,7 @@
 static struct registered_server {
 	struct registered_server *next, *prev;
 	const char *service_name;
+	struct service_details *service_details;
 	void (*task_init)(struct task_server *);
 } *registered_servers;
 
@@ -38,13 +39,17 @@ static struct registered_server {
 */
 NTSTATUS register_server_service(TALLOC_CTX *ctx,
 				const char *name,
-				void (*task_init)(struct task_server *))
+				void (*task_init) (struct task_server *),
+				struct service_details *details)
 {
 	struct registered_server *srv;
 	srv = talloc(ctx, struct registered_server);
 	NT_STATUS_HAVE_NO_MEMORY(srv);
 	srv->service_name = name;
 	srv->task_init = task_init;
+	srv->service_details =
+		talloc_memdup(ctx, details, sizeof(struct service_details));
+	NT_STATUS_HAVE_NO_MEMORY(srv->service_details);
 	DLIST_ADD_END(registered_servers, srv);
 	return NT_STATUS_OK;
 }
@@ -64,7 +69,9 @@ static NTSTATUS server_service_init(const char *name,
 		if (strcasecmp(name, srv->service_name) == 0) {
 			return task_server_startup(event_context, lp_ctx,
 						   srv->service_name,
-						   model_ops, srv->task_init,
+						   model_ops,
+						   srv->task_init,
+						   srv->service_details,
 						   from_parent_fd);
 		}
 	}
diff --git a/source4/smbd/service.h b/source4/smbd/service.h
index 23a9e63..2d11f14 100644
--- a/source4/smbd/service.h
+++ b/source4/smbd/service.h
@@ -23,8 +23,26 @@
 #ifndef __SERVICE_H__
 #define __SERVICE_H__
 
+
 #include "smbd/service_stream.h"
 #include "smbd/service_task.h"
+
+struct service_details {
+	/*
+	 * Prevent the standard process model from forking a new worker
+	 * process when accepting a new connection.  Do this when the service
+	 * relies on shared state, or the over-head of forking would be a
+	 * significant part of the response time
+	 */
+	bool inhibit_fork_on_accept;
+	/*
+	 * Prevent the pre-fork process model from pre-forking any worker
+	 * processes. In this mode pre-fork is equivalent to standard with
+	 * inhibit_fork_on_accept set.
+	 */
+	 bool inhibit_pre_fork;
+};
+
 #include "smbd/service_proto.h"
 
 #endif /* __SERVICE_H__ */
diff --git a/source4/smbd/service_named_pipe.c b/source4/smbd/service_named_pipe.c
index c7e31bc..744f942 100644
--- a/source4/smbd/service_named_pipe.c
+++ b/source4/smbd/service_named_pipe.c
@@ -188,7 +188,8 @@ NTSTATUS tstream_setup_named_pipe(TALLOC_CTX *mem_ctx,
 				  const struct model_ops *model_ops,
 				  const struct stream_server_ops *stream_ops,
 				  const char *pipe_name,
-				  void *private_data)
+				  void *private_data,
+				  void *process_context)
 {
 	char *dirname;
 	struct named_pipe_socket *pipe_sock;
@@ -248,7 +249,8 @@ NTSTATUS tstream_setup_named_pipe(TALLOC_CTX *mem_ctx,
 				     pipe_sock->pipe_path,
 				     NULL,
 				     NULL,
-				     pipe_sock);
+				     pipe_sock,
+				     process_context);
 	if (!NT_STATUS_IS_OK(status)) {
 		goto fail;
 	}
diff --git a/source4/smbd/service_stream.c b/source4/smbd/service_stream.c
index 917a187..3f4a04c 100644
--- a/source4/smbd/service_stream.c
+++ b/source4/smbd/service_stream.c
@@ -44,6 +44,7 @@ struct stream_socket {
 	const struct model_ops *model_ops;
 	struct socket_context *sock;
 	void *private_data;
+	void *process_context;
 };
 
 
@@ -55,6 +56,7 @@ void stream_terminate_connection(struct stream_connection *srv_conn, const char
 	struct tevent_context *event_ctx = srv_conn->event.ctx;
 	const struct model_ops *model_ops = srv_conn->model_ops;
 	struct loadparm_context *lp_ctx = srv_conn->lp_ctx;
+	void *process_context = srv_conn->process_context;
 	TALLOC_CTX *frame = NULL;
 
 	if (!reason) reason = "unknown reason";
@@ -89,8 +91,7 @@ void stream_terminate_connection(struct stream_connection *srv_conn, const char
 	srv_conn->event.fde = NULL;
 	imessaging_cleanup(srv_conn->msg_ctx);
 	TALLOC_FREE(srv_conn);
-	model_ops->terminate(event_ctx, lp_ctx, reason);
-
+	model_ops->terminate(event_ctx, lp_ctx, reason, process_context);
 	TALLOC_FREE(frame);
 }
 
@@ -138,7 +139,8 @@ NTSTATUS stream_new_connection_merge(struct tevent_context *ev,
 				     const struct stream_server_ops *stream_ops,
 				     struct imessaging_context *msg_ctx,
 				     void *private_data,
-				     struct stream_connection **_srv_conn)
+				     struct stream_connection **_srv_conn,
+				     void *process_context)
 {
 	struct stream_connection *srv_conn;
 
@@ -154,6 +156,7 @@ NTSTATUS stream_new_connection_merge(struct tevent_context *ev,
 	srv_conn->event.ctx	= ev;
 	srv_conn->lp_ctx	= lp_ctx;
 	srv_conn->event.fde	= NULL;
+	srv_conn->process_context = process_context;
 
 	*_srv_conn = srv_conn;
 	return NT_STATUS_OK;
@@ -166,7 +169,9 @@ NTSTATUS stream_new_connection_merge(struct tevent_context *ev,
 static void stream_new_connection(struct tevent_context *ev,
 				  struct loadparm_context *lp_ctx,
 				  struct socket_context *sock, 
-				  struct server_id server_id, void *private_data)
+				  struct server_id server_id,
+				  void *private_data,
+				  void *process_context)
 {
 	struct stream_socket *stream_socket = talloc_get_type(private_data, struct stream_socket);
 	struct stream_connection *srv_conn;
@@ -186,6 +191,7 @@ static void stream_new_connection(struct tevent_context *ev,
 	srv_conn->ops           = stream_socket->ops;
 	srv_conn->event.ctx	= ev;
 	srv_conn->lp_ctx	= lp_ctx;
+	srv_conn->process_context = process_context;
 
 	if (!socket_check_access(sock, "smbd", lpcfg_hosts_allow(NULL, lpcfg_default_service(lp_ctx)), lpcfg_hosts_deny(NULL, lpcfg_default_service(lp_ctx)))) {
 		stream_terminate_connection(srv_conn, "denied by access rules");
@@ -258,8 +264,9 @@ static void stream_accept_handler(struct tevent_context *ev, struct tevent_fd *f
 	   connection.  When done, it calls stream_new_connection()
 	   with the newly created socket */
 	stream_socket->model_ops->accept_connection(ev, stream_socket->lp_ctx,
-						    stream_socket->sock, 
-						    stream_new_connection, stream_socket);
+						    stream_socket->sock,
+						    stream_new_connection, stream_socket,
+						    stream_socket->process_context);
 }
 
 /*
@@ -279,7 +286,8 @@ NTSTATUS stream_setup_socket(TALLOC_CTX *mem_ctx,
 			     const char *sock_addr,
 			     uint16_t *port,
 			     const char *socket_options,
-			     void *private_data)
+			     void *private_data,
+			     void *process_context)
 {
 	NTSTATUS status;
 	struct stream_socket *stream_socket;
@@ -385,6 +393,7 @@ NTSTATUS stream_setup_socket(TALLOC_CTX *mem_ctx,
 	stream_socket->ops              = stream_ops;
 	stream_socket->event_ctx	= event_context;
 	stream_socket->model_ops        = model_ops;
+	stream_socket->process_context  = process_context;
 
 	return NT_STATUS_OK;
 }
diff --git a/source4/smbd/service_stream.h b/source4/smbd/service_stream.h
index a7d3def..81bf275 100644
--- a/source4/smbd/service_stream.h
+++ b/source4/smbd/service_stream.h
@@ -62,6 +62,7 @@ struct stream_connection {
 
 	uint processing;
 	const char *terminate;
+	void *process_context;
 };
 
 
diff --git a/source4/smbd/service_task.c b/source4/smbd/service_task.c
index e0e98f6..1d33a43 100644
--- a/source4/smbd/service_task.c
+++ b/source4/smbd/service_task.c
@@ -50,8 +50,8 @@ void task_server_terminate(struct task_server *task, const char *reason, bool fa
 
 	imessaging_cleanup(task->msg_ctx);
 
-	model_ops->terminate(event_ctx, task->lp_ctx, reason);
-	
+	model_ops->terminate(event_ctx, task->lp_ctx, reason,
+			     task->process_context);
 	/* don't free this above, it might contain the 'reason' being printed */
 	talloc_free(task);
 }
@@ -67,9 +67,11 @@ struct task_state {
   called by the process model code when the new task starts up. This then calls
   the server specific startup code
 */
-static void task_server_callback(struct tevent_context *event_ctx, 
+static void task_server_callback(struct tevent_context *event_ctx,
 				 struct loadparm_context *lp_ctx,
-				 struct server_id server_id, void *private_data)
+				 struct server_id server_id,
+				 void *private_data,
+				 void *context)
 {
 	struct task_state *state = talloc_get_type(private_data, struct task_state);
 	struct task_server *task;
@@ -81,6 +83,7 @@ static void task_server_callback(struct tevent_context *event_ctx,
 	task->model_ops = state->model_ops;
 	task->server_id = server_id;
 	task->lp_ctx = lp_ctx;
+	task->process_context = context;
 
 	task->msg_ctx = imessaging_init(task,
 					task->lp_ctx,
@@ -97,11 +100,12 @@ static void task_server_callback(struct tevent_context *event_ctx,
 /*
   startup a task based server
 */
-NTSTATUS task_server_startup(struct tevent_context *event_ctx, 
+NTSTATUS task_server_startup(struct tevent_context *event_ctx,
 			     struct loadparm_context *lp_ctx,
-			     const char *service_name, 
-			     const struct model_ops *model_ops, 
+			     const char *service_name,
+			     const struct model_ops *model_ops,
 			     void (*task_init)(struct task_server *),
+			     const struct service_details *service_details,
 			     int from_parent_fd)
 {
 	struct task_state *state;
@@ -113,7 +117,7 @@ NTSTATUS task_server_startup(struct tevent_context *event_ctx,
 	state->model_ops = model_ops;
 
 	state->model_ops->new_task(event_ctx, lp_ctx, service_name,
-			           task_server_callback, state,
+			           task_server_callback, state, service_details,
 				   from_parent_fd);
 
 	return NT_STATUS_OK;
diff --git a/source4/smbd/service_task.h b/source4/smbd/service_task.h
index ded4590..2499dc1 100644
--- a/source4/smbd/service_task.h
+++ b/source4/smbd/service_task.h
@@ -31,6 +31,7 @@ struct task_server {
 	struct loadparm_context *lp_ctx;
 	struct server_id server_id;
 	void *private_data;
+	void *process_context;
 };
 
 
diff --git a/source4/torture/rpc/spoolss_notify.c b/source4/torture/rpc/spoolss_notify.c
index bcae5f8..dd9cd31 100644
--- a/source4/torture/rpc/spoolss_notify.c
+++ b/source4/torture/rpc/spoolss_notify.c
@@ -474,7 +474,9 @@ static bool test_start_dcerpc_server(struct torture_context *tctx,
 	torture_assert_ntstatus_ok(tctx, status,
 				   "unable to initialize process models");
 
-	status = smbsrv_add_socket(tctx, event_ctx, tctx->lp_ctx, process_model_startup("single"), address);
+	status = smbsrv_add_socket(tctx, event_ctx, tctx->lp_ctx,
+				   process_model_startup("single"),
+				   address, NULL);
 	torture_assert_ntstatus_ok(tctx, status, "starting smb server");
 
 	status = dcesrv_init_context(tctx, tctx->lp_ctx, endpoints, &dce_ctx);
@@ -483,7 +485,8 @@ static bool test_start_dcerpc_server(struct torture_context *tctx,
 
 	for (e=dce_ctx->endpoint_list;e;e=e->next) {
 		status = dcesrv_add_ep(dce_ctx, tctx->lp_ctx,
-				       e, tctx->ev, process_model_startup("single"));
+				       e, tctx->ev,
+				       process_model_startup("single"), NULL);
 		torture_assert_ntstatus_ok(tctx, status,
 				"unable listen on dcerpc endpoint server");
 	}
diff --git a/source4/web_server/web_server.c b/source4/web_server/web_server.c
index d854a29..faf1bc6 100644
--- a/source4/web_server/web_server.c
+++ b/source4/web_server/web_server.c
@@ -330,8 +330,10 @@ static void websrv_task_init(struct task_server *task)
 						     task->lp_ctx, model_ops,
 						     &web_stream_ops, 
 						     "ip", address,
-						     &port, lpcfg_socket_options(task->lp_ctx),
-						     task);
+						     &port,
+						     lpcfg_socket_options(task->lp_ctx),
+						     task,
+						     task->process_context);
 			if (!NT_STATUS_IS_OK(status)) goto failed;
 		}
 
@@ -350,7 +352,8 @@ static void websrv_task_init(struct task_server *task)
 						     &web_stream_ops,
 						     "ip", wcard[i],
 						     &port, lpcfg_socket_options(task->lp_ctx),
-						     wdata);
+						     wdata,
+						     task->process_context);
 			if (!NT_STATUS_IS_OK(status)) goto failed;
 		}
 		talloc_free(wcard);
@@ -372,5 +375,9 @@ failed:
 /* called at smbd startup - register ourselves as a server service */
 NTSTATUS server_service_web_init(TALLOC_CTX *ctx)
 {
-	return register_server_service(ctx, "web", websrv_task_init);
+	struct service_details details = {
+		.inhibit_fork_on_accept = true,
+		.inhibit_pre_fork = true
+	};
+	return register_server_service(ctx, "web", websrv_task_init, &details);
 }
diff --git a/source4/winbind/winbindd.c b/source4/winbind/winbindd.c
index c8c0733..6aa0418 100644
--- a/source4/winbind/winbindd.c
+++ b/source4/winbind/winbindd.c
@@ -90,9 +90,16 @@ NTSTATUS server_service_winbindd_init(TALLOC_CTX *);
 
 NTSTATUS server_service_winbindd_init(TALLOC_CTX *ctx)
 {
-	NTSTATUS status = register_server_service(ctx, "winbindd", winbindd_task_init);
+	struct service_details details = {
+		.inhibit_fork_on_accept = true,
+		.inhibit_pre_fork = true,
+	};
+
+	NTSTATUS status = register_server_service(ctx, "winbindd",
+						  winbindd_task_init, &details);
 	if (!NT_STATUS_IS_OK(status)) {
 		return status;
 	}
-	return register_server_service(ctx, "winbind", winbindd_task_init);
+	return register_server_service(ctx, "winbind", winbindd_task_init,
+				       &details);
 }
diff --git a/source4/wrepl_server/wrepl_in_connection.c b/source4/wrepl_server/wrepl_in_connection.c
index b8e9a16..4920aa0 100644
--- a/source4/wrepl_server/wrepl_in_connection.c
+++ b/source4/wrepl_server/wrepl_in_connection.c
@@ -347,7 +347,8 @@ static const struct stream_server_ops wreplsrv_stream_ops = {
 NTSTATUS wreplsrv_in_connection_merge(struct wreplsrv_partner *partner,
 				      uint32_t peer_assoc_ctx,
 				      struct tstream_context **stream,
-				      struct wreplsrv_in_connection **_wrepl_in)
+				      struct wreplsrv_in_connection **_wrepl_in,
+				      void* process_context)
 {
 	struct wreplsrv_service *service = partner->service;
 	struct wreplsrv_in_connection *wrepl_in;
@@ -379,7 +380,8 @@ NTSTATUS wreplsrv_in_connection_merge(struct wreplsrv_partner *partner,
 					     &wreplsrv_stream_ops,
 					     service->task->msg_ctx,
 					     wrepl_in,
-					     &conn);
+					     &conn,
+					     process_context);
 	NT_STATUS_NOT_OK_RETURN(status);
 
 	/*
@@ -461,7 +463,7 @@ NTSTATUS wreplsrv_setup_sockets(struct wreplsrv_service *service, struct loadpar
 						     &wreplsrv_stream_ops,
 						     "ipv4", address, &port, 
 					              lpcfg_socket_options(task->lp_ctx),
-						     service);
+						     service, task->process_context);
 			if (!NT_STATUS_IS_OK(status)) {
 				DEBUG(0,("stream_setup_socket(address=%s,port=%u) failed - %s\n",
 					 address, port, nt_errstr(status)));
@@ -473,7 +475,7 @@ NTSTATUS wreplsrv_setup_sockets(struct wreplsrv_service *service, struct loadpar
 		status = stream_setup_socket(task, task->event_ctx, task->lp_ctx,
 					     model_ops, &wreplsrv_stream_ops,
 					     "ipv4", address, &port, lpcfg_socket_options(task->lp_ctx),
-					     service);
+					     service, task->process_context);
 		if (!NT_STATUS_IS_OK(status)) {
 			DEBUG(0,("stream_setup_socket(address=%s,port=%u) failed - %s\n",
 				 address, port, nt_errstr(status)));
diff --git a/source4/wrepl_server/wrepl_out_helpers.c b/source4/wrepl_server/wrepl_out_helpers.c
index 782e2f2..0e3f0a4 100644
--- a/source4/wrepl_server/wrepl_out_helpers.c
+++ b/source4/wrepl_server/wrepl_out_helpers.c
@@ -988,6 +988,7 @@ static NTSTATUS wreplsrv_push_notify_wait_update(struct wreplsrv_push_notify_sta
 {
 	struct wreplsrv_in_connection *wrepl_in;
 	struct tstream_context *stream;
+	void *process_context = NULL;
 	NTSTATUS status;
 
 	status = wrepl_request_recv(state->subreq, state, NULL);
@@ -1011,10 +1012,11 @@ static NTSTATUS wreplsrv_push_notify_wait_update(struct wreplsrv_push_notify_sta
 	 * NOTE: stream will be stolen by
 	 *       wreplsrv_in_connection_merge()
 	 */
+	process_context = state->io->in.partner->service->task->process_context;
 	status = wreplsrv_in_connection_merge(state->io->in.partner,
 					      state->wreplconn->assoc_ctx.peer_ctx,
 					      &stream,
-					      &wrepl_in);
+					      &wrepl_in, process_context);
 	NT_STATUS_NOT_OK_RETURN(status);
 
 	/* now we can free the wreplsrv_out_connection */
diff --git a/source4/wrepl_server/wrepl_server.c b/source4/wrepl_server/wrepl_server.c
index dc28e23..bd2ae26 100644
--- a/source4/wrepl_server/wrepl_server.c
+++ b/source4/wrepl_server/wrepl_server.c
@@ -508,5 +508,10 @@ static void wreplsrv_task_init(struct task_server *task)
 */
 NTSTATUS server_service_wrepl_init(TALLOC_CTX *ctx)
 {
-	return register_server_service(ctx, "wrepl", wreplsrv_task_init);
+	struct service_details details = {
+		.inhibit_fork_on_accept = true,
+		.inhibit_pre_fork = true
+	};
+	return register_server_service(ctx, "wrepl", wreplsrv_task_init,
+				       &details);
 }
-- 
2.7.4


From 4a49346f1c4e150742b86fa98b0970c5a8fc0921 Mon Sep 17 00:00:00 2001
From: Gary Lockyer <gary at catalyst.net.nz>
Date: Mon, 18 Sep 2017 12:56:09 +1200
Subject: [PATCH 3/9] process_standard: Use the new process_context

Use the new process_context to control the from_parent_fd and forking behaviour
This avoids the use of global variables, and allows process_standard to run as
what was known as single without over-stamping a different process model.

Signed-off-by: Gary Lockyer <gary at catalyst.net.nz>
Reviewed-by: Andrew Bartlett <abartlet at samba.org>
---
 source4/smbd/process_standard.c | 95 ++++++++++++++++++++++++++++++++---------
 1 file changed, 75 insertions(+), 20 deletions(-)

diff --git a/source4/smbd/process_standard.c b/source4/smbd/process_standard.c
index 431134e..e8fa257 100644
--- a/source4/smbd/process_standard.c
+++ b/source4/smbd/process_standard.c
@@ -41,8 +41,12 @@ struct standard_child_state {
 };
 
 NTSTATUS process_model_standard_init(TALLOC_CTX *);
-
-static int from_parent_fd;
+struct process_context {
+	char *name;
+	int from_parent_fd;
+	bool inhibit_fork_on_accept;
+	bool forked_on_accept;
+};
 
 /*
   called when the process model is selected
@@ -242,26 +246,40 @@ static void standard_accept_connection(
 	struct standard_child_state *state;
 	struct tevent_fd *fde = NULL;
 	struct tevent_signal *se = NULL;
+	struct process_context *proc_ctx = NULL;
 
-	state = setup_standard_child_pipe(ev, NULL);
-	if (state == NULL) {
-		return;
-	}
 
 	/* accept an incoming connection. */
 	status = socket_accept(sock, &sock2);
 	if (!NT_STATUS_IS_OK(status)) {
 		DEBUG(0,("standard_accept_connection: accept: %s\n",
-			 nt_errstr(status)));
+			nt_errstr(status)));
 		/* this looks strange, but is correct. We need to throttle things until
 		   the system clears enough resources to handle this new socket */
 		sleep(1);
-		close(state->to_parent_fd);
-		state->to_parent_fd = -1;
-		TALLOC_FREE(state);
 		return;
 	}
 
+	proc_ctx = talloc_get_type(process_context, struct process_context);
+	if (proc_ctx->inhibit_fork_on_accept) {
+		pid = getpid();
+		/*
+		 * Service does not support forking a new process on a new
+		 * connection, either it's maintaining shared state or the
+		 * overhead of forking a new process is a significant fraction
+		 * of the response time.
+		 */
+		talloc_steal(private_data, sock2);
+		new_conn(ev, lp_ctx, sock2,
+			 cluster_id(pid, socket_get_fd(sock2)), private_data,
+			 process_context);
+		return;
+	}
+
+	state = setup_standard_child_pipe(ev, NULL);
+	if (state == NULL) {
+		return;
+	}
 	pid = fork();
 
 	if (pid != 0) {
@@ -283,7 +301,13 @@ static void standard_accept_connection(
 	/* this leaves state->to_parent_fd open */
 	TALLOC_FREE(state);
 
+	/* Now in the child code so indicate that we forked
+	 * so the terminate code knows what to do
+	 */
+	proc_ctx->forked_on_accept = true;
+
 	pid = getpid();
+	setproctitle("task[%s] standard worker", proc_ctx->name);
 
 	/* This is now the child code. We need a completely new event_context to work with */
 
@@ -308,7 +332,7 @@ static void standard_accept_connection(
 		smb_panic("Failed to re-initialise imessaging after fork");
 	}
 
-	fde = tevent_add_fd(ev, ev, from_parent_fd, TEVENT_FD_READ,
+	fde = tevent_add_fd(ev, ev, proc_ctx->from_parent_fd, TEVENT_FD_READ,
 		      standard_pipe_handler, NULL);
 	if (fde == NULL) {
 		smb_panic("Failed to add fd handler after fork");
@@ -346,7 +370,7 @@ static void standard_accept_connection(
 
 	/* setup this new connection.  Cluster ID is PID based for this process model */
 	new_conn(ev, lp_ctx, sock2, cluster_id(pid, 0), private_data,
-		 NULL);
+		 process_context);
 
 	/* we can't return to the top level here, as that event context is gone,
 	   so we now process events in the new event context until there are no
@@ -366,19 +390,19 @@ static void standard_new_task(struct tevent_context *ev,
 			      void (*new_task)(struct tevent_context *, struct loadparm_context *lp_ctx, struct server_id , void *, void *),
 			      void *private_data,
 			      const struct service_details *service_details,
-			      int new_from_parent_fd)
+			      int from_parent_fd)
 {
 	pid_t pid;
 	NTSTATUS status;
 	struct standard_child_state *state;
 	struct tevent_fd *fde = NULL;
 	struct tevent_signal *se = NULL;
+	struct process_context *proc_ctx = NULL;
 
 	state = setup_standard_child_pipe(ev, service_name);
 	if (state == NULL) {
 		return;
 	}
-	from_parent_fd = new_from_parent_fd;
 
 	pid = fork();
 
@@ -442,10 +466,21 @@ static void standard_new_task(struct tevent_context *ev,
 		smb_panic("Failed to add SIGTERM handler after fork");
 	}
 
-	setproctitle("task %s server_id[%d]", service_name, (int)pid);
+	setproctitle("task[%s]", service_name);
+
+	/*
+	 * Set up the process context to be passed through to the terminate
+	 * and accept_connection functions
+	 */
+	proc_ctx = talloc(ev, struct process_context);
+	proc_ctx->name = talloc_strdup(ev, service_name);
+	proc_ctx->from_parent_fd = from_parent_fd;
+	proc_ctx->inhibit_fork_on_accept  =
+		service_details->inhibit_fork_on_accept;
+	proc_ctx->forked_on_accept = false;
 
 	/* setup this new task.  Cluster ID is PID based for this process model */
-	new_task(ev, lp_ctx, cluster_id(pid, 0), private_data, NULL);
+	new_task(ev, lp_ctx, cluster_id(pid, 0), private_data, proc_ctx);
 
 	/* we can't return to the top level here, as that event context is gone,
 	   so we now process events in the new event context until there are no
@@ -458,11 +493,31 @@ static void standard_new_task(struct tevent_context *ev,
 
 
 /* called when a task goes down */
-_NORETURN_ static void standard_terminate(struct tevent_context *ev, struct loadparm_context *lp_ctx,
-					  const char *reason,
-					  void *process_context) 
+static void standard_terminate(struct tevent_context *ev,
+			       struct loadparm_context *lp_ctx,
+			       const char *reason,
+			       void *process_context)
 {
-	DEBUG(2,("standard_terminate: reason[%s]\n",reason));
+	struct process_context *proc_ctx = NULL;
+
+	DBG_DEBUG("process terminating reason[%s]\n", reason);
+	if (process_context == NULL) {
+		smb_panic("Panicking process_context is NULL");
+	}
+
+	proc_ctx = talloc_get_type(process_context, struct process_context);
+	if (proc_ctx->forked_on_accept == false) {
+		/*
+		 * The current task was not forked on accept, so it needs to
+		 * keep running and process requests from other connections
+		 */
+		return;
+	}
+	/*
+	 * The current process was forked on accept to handle a single
+	 * connection/request. That request has now finished and the process
+	 * should terminate
+	 */
 
 	/* this reload_charcnv() has the effect of freeing the iconv context memory,
 	   which makes leak checking easier */
-- 
2.7.4


From d4be1c657f66e2c0c3c0d4fd87ecaee0e11ea893 Mon Sep 17 00:00:00 2001
From: Gary Lockyer <gary at catalyst.net.nz>
Date: Mon, 18 Sep 2017 13:05:24 +1200
Subject: [PATCH 4/9] source4/smbd: Do not overstamp the process model with
 "single"

Instead, except in RPC which is a special SNOWFLAKE, we rely on the struct
service_details in the init function.

Signed-off-by: Gary Lockyer <gary at catalyst.net.nz>
Reviewed-by: Andrew Bartlett <abartlet at samba.org>
---
 source4/dns_server/dns_server.c            | 15 ++--------
 source4/echo_server/echo_server.c          | 27 +++---------------
 source4/kdc/kdc-heimdal.c                  | 19 ++++---------
 source4/kdc/kdc-service-mit.c              |  6 ----
 source4/ldap_server/ldap_server.c          | 18 ++++--------
 source4/ntp_signd/ntp_signd.c              | 13 +--------
 source4/web_server/web_server.c            | 13 ++++-----
 source4/wrepl_server/wrepl_in_connection.c | 44 ++++++++++--------------------
 8 files changed, 39 insertions(+), 116 deletions(-)

diff --git a/source4/dns_server/dns_server.c b/source4/dns_server/dns_server.c
index 6a90e30..cd293b8 100644
--- a/source4/dns_server/dns_server.c
+++ b/source4/dns_server/dns_server.c
@@ -684,23 +684,14 @@ static NTSTATUS dns_add_socket(struct dns_server *dns,
   setup our listening sockets on the configured network interfaces
 */
 static NTSTATUS dns_startup_interfaces(struct dns_server *dns,
-				       struct interface *ifaces)
+				       struct interface *ifaces,
+				       const struct model_ops *model_ops)
 {
-	const struct model_ops *model_ops;
 	int num_interfaces;
 	TALLOC_CTX *tmp_ctx = talloc_new(dns);
 	NTSTATUS status;
 	int i;
 
-	/* within the dns task we want to be a single process, so
-	   ask for the single process model ops and pass these to the
-	   stream_setup_socket() call. */
-	model_ops = process_model_startup("single");
-	if (!model_ops) {
-		DEBUG(0,("Can't find 'single' process model_ops\n"));
-		return NT_STATUS_INTERNAL_ERROR;
-	}
-
 	if (ifaces != NULL) {
 		num_interfaces = iface_list_count(ifaces);
 
@@ -907,7 +898,7 @@ static void dns_task_init(struct task_server *task)
 		return;
 	}
 
-	status = dns_startup_interfaces(dns, ifaces);
+	status = dns_startup_interfaces(dns, ifaces, task->model_ops);
 	if (!NT_STATUS_IS_OK(status)) {
 		task_server_terminate(task, "dns failed to setup interfaces", true);
 		return;
diff --git a/source4/echo_server/echo_server.c b/source4/echo_server/echo_server.c
index 9a66211..90f8616 100644
--- a/source4/echo_server/echo_server.c
+++ b/source4/echo_server/echo_server.c
@@ -241,34 +241,14 @@ static NTSTATUS echo_add_socket(struct echo_server *echo,
 /* Set up the listening sockets */
 static NTSTATUS echo_startup_interfaces(struct echo_server *echo,
 					struct loadparm_context *lp_ctx,
-					struct interface *ifaces)
+					struct interface *ifaces,
+					const struct model_ops *model_ops)
 {
-	const struct model_ops *model_ops;
 	int num_interfaces;
 	TALLOC_CTX *tmp_ctx = talloc_new(echo);
 	NTSTATUS status;
 	int i;
 
-	/*
-	 * Samba allows subtask to set their own process model.
-	 * Available models currently are:
-	 * - onefork  (forks exactly one child process)
-	 * - prefork  (keep a couple of child processes around)
-	 * - single   (only run a single process)
-	 * - standard (fork one subprocess per incoming connection)
-	 * - thread   (use threads instead of forks)
-	 *
-	 * For the echo server, the "single" process model works fine,
-	 * you probably don't want to use the thread model unless you really
-	 * know what you're doing.
-	 */
-
-	model_ops = process_model_startup("single");
-	if (model_ops == NULL) {
-		DEBUG(0, ("Can't find 'single' process model_ops\n"));
-		return NT_STATUS_INTERNAL_ERROR;
-	}
-
 	num_interfaces = iface_list_count(ifaces);
 
 	for(i=0; i<num_interfaces; i++) {
@@ -327,7 +307,8 @@ static void echo_task_init(struct task_server *task)
 
 	echo->task = task;
 
-	status = echo_startup_interfaces(echo, task->lp_ctx, ifaces);
+	status = echo_startup_interfaces(echo, task->lp_ctx, ifaces,
+					 task->model_ops);
 	if (!NT_STATUS_IS_OK(status)) {
 		task_server_terminate(task, "echo: Failed to set up interfaces",
 				      true);
diff --git a/source4/kdc/kdc-heimdal.c b/source4/kdc/kdc-heimdal.c
index 788959a..fcc1eb0 100644
--- a/source4/kdc/kdc-heimdal.c
+++ b/source4/kdc/kdc-heimdal.c
@@ -110,10 +110,11 @@ static kdc_code kdc_process(struct kdc_server *kdc,
 /*
   setup our listening sockets on the configured network interfaces
 */
-static NTSTATUS kdc_startup_interfaces(struct kdc_server *kdc, struct loadparm_context *lp_ctx,
-				       struct interface *ifaces)
+static NTSTATUS kdc_startup_interfaces(struct kdc_server *kdc,
+				       struct loadparm_context *lp_ctx,
+				       struct interface *ifaces,
+				       const struct model_ops *model_ops)
 {
-	const struct model_ops *model_ops;
 	int num_interfaces;
 	TALLOC_CTX *tmp_ctx = talloc_new(kdc);
 	NTSTATUS status;
@@ -122,15 +123,6 @@ static NTSTATUS kdc_startup_interfaces(struct kdc_server *kdc, struct loadparm_c
 	uint16_t kpasswd_port = lpcfg_kpasswd_port(lp_ctx);
 	bool done_wildcard = false;
 
-	/* within the kdc task we want to be a single process, so
-	   ask for the single process model ops and pass these to the
-	   stream_setup_socket() call. */
-	model_ops = process_model_startup("single");
-	if (!model_ops) {
-		DEBUG(0,("Can't find 'single' process model_ops\n"));
-		return NT_STATUS_INTERNAL_ERROR;
-	}
-
 	num_interfaces = iface_list_count(ifaces);
 
 	/* if we are allowing incoming packets from any address, then
@@ -448,7 +440,8 @@ static void kdc_task_init(struct task_server *task)
 	kdc->private_data = kdc_config;
 
 	/* start listening on the configured network interfaces */
-	status = kdc_startup_interfaces(kdc, task->lp_ctx, ifaces);
+	status = kdc_startup_interfaces(kdc, task->lp_ctx, ifaces,
+					task->model_ops);
 	if (!NT_STATUS_IS_OK(status)) {
 		task_server_terminate(task, "kdc failed to setup interfaces", true);
 		return;
diff --git a/source4/kdc/kdc-service-mit.c b/source4/kdc/kdc-service-mit.c
index d48b032..53997d5 100644
--- a/source4/kdc/kdc-service-mit.c
+++ b/source4/kdc/kdc-service-mit.c
@@ -72,12 +72,6 @@ static NTSTATUS startup_kpasswd_server(TALLOC_CTX *mem_ctx,
 		return NT_STATUS_OK;
 	}
 
-	model_ops = process_model_startup("single");
-	if (model_ops == NULL) {
-		DBG_ERR("Can't find 'single' process model_ops\n");
-		return NT_STATUS_INTERNAL_ERROR;
-	}
-
 	tmp_ctx = talloc_named_const(mem_ctx, 0, "kpasswd");
 	if (tmp_ctx == NULL) {
 		return NT_STATUS_NO_MEMORY;
diff --git a/source4/ldap_server/ldap_server.c b/source4/ldap_server/ldap_server.c
index d12c4e7..5f7efe9 100644
--- a/source4/ldap_server/ldap_server.c
+++ b/source4/ldap_server/ldap_server.c
@@ -1112,7 +1112,6 @@ static void ldapsrv_task_init(struct task_server *task)
 	const char *dns_host_name;
 	struct ldapsrv_service *ldap_service;
 	NTSTATUS status;
-	const struct model_ops *model_ops;
 
 	switch (lpcfg_server_role(task->lp_ctx)) {
 	case ROLE_STANDALONE:
@@ -1130,13 +1129,6 @@ static void ldapsrv_task_init(struct task_server *task)
 
 	task_server_set_title(task, "task[ldapsrv]");
 
-	/*
-	 * Here we used to run the ldap server as a single process,
-	 * but we don't want transaction locks for one task in a write
-	 * blocking all other reads, so we go multi-process.
-	 */
-	model_ops = task->model_ops;
-
 	ldap_service = talloc_zero(task, struct ldapsrv_service);
 	if (ldap_service == NULL) goto failed;
 
@@ -1180,7 +1172,8 @@ static void ldapsrv_task_init(struct task_server *task)
 		*/
 		for(i = 0; i < num_interfaces; i++) {
 			const char *address = iface_list_n_ip(ifaces, i);
-			status = add_socket(task, task->lp_ctx, model_ops, address, ldap_service);
+			status = add_socket(task, task->lp_ctx, task->model_ops,
+					    address, ldap_service);
 			if (!NT_STATUS_IS_OK(status)) goto failed;
 		}
 	} else {
@@ -1193,7 +1186,8 @@ static void ldapsrv_task_init(struct task_server *task)
 			goto failed;
 		}
 		for (i=0; wcard[i]; i++) {
-			status = add_socket(task, task->lp_ctx, model_ops, wcard[i], ldap_service);
+			status = add_socket(task, task->lp_ctx, task->model_ops,
+					    wcard[i], ldap_service);
 			if (NT_STATUS_IS_OK(status)) {
 				num_binds++;
 			}
@@ -1210,7 +1204,7 @@ static void ldapsrv_task_init(struct task_server *task)
 	}
 
 	status = stream_setup_socket(task, task->event_ctx, task->lp_ctx,
-				     model_ops, &ldap_stream_nonpriv_ops,
+				     task->model_ops, &ldap_stream_nonpriv_ops,
 				     "unix", ldapi_path, NULL, 
 				     lpcfg_socket_options(task->lp_ctx),
 				     ldap_service, task->process_context);
@@ -1241,7 +1235,7 @@ static void ldapsrv_task_init(struct task_server *task)
 	}
 
 	status = stream_setup_socket(task, task->event_ctx, task->lp_ctx,
-				     model_ops, &ldap_stream_priv_ops,
+				     task->model_ops, &ldap_stream_priv_ops,
 				     "unix", ldapi_path, NULL,
 				     lpcfg_socket_options(task->lp_ctx),
 				     ldap_service,
diff --git a/source4/ntp_signd/ntp_signd.c b/source4/ntp_signd/ntp_signd.c
index 0884d2f..e09c693 100644
--- a/source4/ntp_signd/ntp_signd.c
+++ b/source4/ntp_signd/ntp_signd.c
@@ -494,8 +494,6 @@ static void ntp_signd_task_init(struct task_server *task)
 	struct ntp_signd_server *ntp_signd;
 	NTSTATUS status;
 
-	const struct model_ops *model_ops;
-
 	const char *address;
 
 	if (!directory_create_or_exist_strict(lpcfg_ntp_signd_socket_directory(task->lp_ctx), geteuid(), 0750)) {
@@ -506,15 +504,6 @@ static void ntp_signd_task_init(struct task_server *task)
 		return;
 	}
 
-	/* within the ntp_signd task we want to be a single process, so
-	   ask for the single process model ops and pass these to the
-	   stream_setup_socket() call. */
-	model_ops = process_model_startup("single");
-	if (!model_ops) {
-		DEBUG(0,("Can't find 'single' process model_ops\n"));
-		return;
-	}
-
 	task_server_set_title(task, "task[ntp_signd]");
 
 	ntp_signd = talloc(task, struct ntp_signd_server);
@@ -537,7 +526,7 @@ static void ntp_signd_task_init(struct task_server *task)
 	status = stream_setup_socket(ntp_signd->task,
 				     ntp_signd->task->event_ctx,
 				     ntp_signd->task->lp_ctx,
-				     model_ops, 
+				     task->model_ops,
 				     &ntp_signd_stream_ops, 
 				     "unix", address, NULL,
 				     lpcfg_socket_options(ntp_signd->task->lp_ctx),
diff --git a/source4/web_server/web_server.c b/source4/web_server/web_server.c
index faf1bc6..c351689 100644
--- a/source4/web_server/web_server.c
+++ b/source4/web_server/web_server.c
@@ -298,15 +298,10 @@ static void websrv_task_init(struct task_server *task)
 {
 	NTSTATUS status;
 	uint16_t port = lpcfg_web_port(task->lp_ctx);
-	const struct model_ops *model_ops;
 	struct web_server_data *wdata;
 
 	task_server_set_title(task, "task[websrv]");
 
-	/* run the web server as a single process */
-	model_ops = process_model_startup("single");
-	if (!model_ops) goto failed;
-
 	/* startup the Python processor - unfortunately we can't do this
 	   per connection as that wouldn't allow for session variables */
 	wdata = talloc_zero(task, struct web_server_data);
@@ -327,8 +322,9 @@ static void websrv_task_init(struct task_server *task)
 			const char *address = iface_list_n_ip(ifaces, i);
 			status = stream_setup_socket(task,
 						     task->event_ctx,
-						     task->lp_ctx, model_ops,
-						     &web_stream_ops, 
+						     task->lp_ctx,
+						     task->model_ops,
+						     &web_stream_ops,
 						     "ip", address,
 						     &port,
 						     lpcfg_socket_options(task->lp_ctx),
@@ -348,7 +344,8 @@ static void websrv_task_init(struct task_server *task)
 		}
 		for (i=0; wcard[i]; i++) {
 			status = stream_setup_socket(task, task->event_ctx,
-						     task->lp_ctx, model_ops,
+						     task->lp_ctx,
+						     task->model_ops,
 						     &web_stream_ops,
 						     "ip", wcard[i],
 						     &port, lpcfg_socket_options(task->lp_ctx),
diff --git a/source4/wrepl_server/wrepl_in_connection.c b/source4/wrepl_server/wrepl_in_connection.c
index 4920aa0..5cbb086 100644
--- a/source4/wrepl_server/wrepl_in_connection.c
+++ b/source4/wrepl_server/wrepl_in_connection.c
@@ -352,20 +352,10 @@ NTSTATUS wreplsrv_in_connection_merge(struct wreplsrv_partner *partner,
 {
 	struct wreplsrv_service *service = partner->service;
 	struct wreplsrv_in_connection *wrepl_in;
-	const struct model_ops *model_ops;
 	struct stream_connection *conn;
 	struct tevent_req *subreq;
 	NTSTATUS status;
 
-	/* within the wrepl task we want to be a single process, so
-	   ask for the single process model ops and pass these to the
-	   stream_setup_socket() call. */
-	model_ops = process_model_startup("single");
-	if (!model_ops) {
-		DEBUG(0,("Can't find 'single' process model_ops"));
-		return NT_STATUS_INTERNAL_ERROR;
-	}
-
 	wrepl_in = talloc_zero(partner, struct wreplsrv_in_connection);
 	NT_STATUS_HAVE_NO_MEMORY(wrepl_in);
 
@@ -376,7 +366,7 @@ NTSTATUS wreplsrv_in_connection_merge(struct wreplsrv_partner *partner,
 
 	status = stream_new_connection_merge(service->task->event_ctx,
 					     service->task->lp_ctx,
-					     model_ops,
+					     service->task->model_ops,
 					     &wreplsrv_stream_ops,
 					     service->task->msg_ctx,
 					     wrepl_in,
@@ -427,19 +417,9 @@ NTSTATUS wreplsrv_setup_sockets(struct wreplsrv_service *service, struct loadpar
 {
 	NTSTATUS status;
 	struct task_server *task = service->task;
-	const struct model_ops *model_ops;
 	const char *address;
 	uint16_t port = WINS_REPLICATION_PORT;
 
-	/* within the wrepl task we want to be a single process, so
-	   ask for the single process model ops and pass these to the
-	   stream_setup_socket() call. */
-	model_ops = process_model_startup("single");
-	if (!model_ops) {
-		DEBUG(0,("Can't find 'single' process model_ops"));
-		return NT_STATUS_INTERNAL_ERROR;
-	}
-
 	if (lpcfg_interfaces(lp_ctx) && lpcfg_bind_interfaces_only(lp_ctx)) {
 		int num_interfaces;
 		int i;
@@ -458,12 +438,14 @@ NTSTATUS wreplsrv_setup_sockets(struct wreplsrv_service *service, struct loadpar
 				continue;
 			}
 			address = iface_list_n_ip(ifaces, i);
-			status = stream_setup_socket(task, task->event_ctx,
-						     task->lp_ctx, model_ops,
-						     &wreplsrv_stream_ops,
-						     "ipv4", address, &port, 
-					              lpcfg_socket_options(task->lp_ctx),
-						     service, task->process_context);
+			status = stream_setup_socket(
+					task, task->event_ctx,
+					task->lp_ctx,
+					task->model_ops,
+					&wreplsrv_stream_ops,
+					"ipv4", address, &port,
+					lpcfg_socket_options(task->lp_ctx),
+					service, task->process_context);
 			if (!NT_STATUS_IS_OK(status)) {
 				DEBUG(0,("stream_setup_socket(address=%s,port=%u) failed - %s\n",
 					 address, port, nt_errstr(status)));
@@ -472,9 +454,11 @@ NTSTATUS wreplsrv_setup_sockets(struct wreplsrv_service *service, struct loadpar
 		}
 	} else {
 		address = "0.0.0.0";
-		status = stream_setup_socket(task, task->event_ctx, task->lp_ctx,
-					     model_ops, &wreplsrv_stream_ops,
-					     "ipv4", address, &port, lpcfg_socket_options(task->lp_ctx),
+		status = stream_setup_socket(task, task->event_ctx,
+					     task->lp_ctx, task->model_ops,
+					     &wreplsrv_stream_ops,
+					     "ipv4", address, &port,
+					     lpcfg_socket_options(task->lp_ctx),
 					     service, task->process_context);
 		if (!NT_STATUS_IS_OK(status)) {
 			DEBUG(0,("stream_setup_socket(address=%s,port=%u) failed - %s\n",
-- 
2.7.4


From 9aba8bc6c6f27282f617152dd64185b9bb807cd9 Mon Sep 17 00:00:00 2001
From: Gary Lockyer <gary at catalyst.net.nz>
Date: Mon, 18 Sep 2017 13:02:13 +1200
Subject: [PATCH 5/9] process_standard: Do not log at level 2 every time a
 child exits

Signed-off-by: Gary Lockyer <gary at catalyst.net.nz>
Reviewed-by: Andrew Bartlett <abartlet at samba.org>
---
 source4/smbd/process_standard.c | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/source4/smbd/process_standard.c b/source4/smbd/process_standard.c
index e8fa257..d0eec4a 100644
--- a/source4/smbd/process_standard.c
+++ b/source4/smbd/process_standard.c
@@ -148,8 +148,10 @@ static void standard_child_pipe_handler(struct tevent_context *ev,
 	}
 	if (WIFEXITED(status)) {
 		status = WEXITSTATUS(status);
-		DEBUG(2, ("Child %d (%s) exited with status %d\n",
-			  (int)state->pid, state->name, status));
+		if (status != 0) {
+			DBG_ERR("Child %d (%s) exited with status %d\n",
+				(int)state->pid, state->name, status);
+		}
 	} else if (WIFSIGNALED(status)) {
 		status = WTERMSIG(status);
 		DEBUG(0, ("Child %d (%s) terminated with signal %d\n",
-- 
2.7.4


From e586ee6f3a8faaeb6a2b2812b7f41f831b2bd6c4 Mon Sep 17 00:00:00 2001
From: Gary Lockyer <gary at catalyst.net.nz>
Date: Fri, 15 Sep 2017 09:15:35 +1200
Subject: [PATCH 6/9] source4/smbd: Fix code formatting after refactoring.

Fix code formatting from the refactoring in the previous commits.
Done as a separate patch to make the changes to functionality easier
to review.

Signed-off-by: Gary Lockyer <gary at catalyst.net.nz>
Reviewed-by: Andrew Bartlett <abartlet at samba.org>
---
 source4/smbd/process_standard.c |  6 ++++--
 source4/smbd/service_stream.c   | 43 ++++++++++++++++++++++-------------------
 2 files changed, 27 insertions(+), 22 deletions(-)

diff --git a/source4/smbd/process_standard.c b/source4/smbd/process_standard.c
index d0eec4a..f642b18 100644
--- a/source4/smbd/process_standard.c
+++ b/source4/smbd/process_standard.c
@@ -256,8 +256,10 @@ static void standard_accept_connection(
 	if (!NT_STATUS_IS_OK(status)) {
 		DEBUG(0,("standard_accept_connection: accept: %s\n",
 			nt_errstr(status)));
-		/* this looks strange, but is correct. We need to throttle things until
-		   the system clears enough resources to handle this new socket */
+		/* this looks strange, but is correct. We need to throttle
+		 * things until the system clears enough resources to handle
+		 * this new socket
+		 */
 		sleep(1);
 		return;
 	}
diff --git a/source4/smbd/service_stream.c b/source4/smbd/service_stream.c
index 3f4a04c..63318c6 100644
--- a/source4/smbd/service_stream.c
+++ b/source4/smbd/service_stream.c
@@ -147,15 +147,15 @@ NTSTATUS stream_new_connection_merge(struct tevent_context *ev,
 	srv_conn = talloc_zero(ev, struct stream_connection);
 	NT_STATUS_HAVE_NO_MEMORY(srv_conn);
 
-	srv_conn->private_data  = private_data;
-	srv_conn->model_ops     = model_ops;
-	srv_conn->socket	= NULL;
-	srv_conn->server_id	= cluster_id(0, 0);
-	srv_conn->ops           = stream_ops;
-	srv_conn->msg_ctx	= msg_ctx;
-	srv_conn->event.ctx	= ev;
-	srv_conn->lp_ctx	= lp_ctx;
-	srv_conn->event.fde	= NULL;
+	srv_conn->private_data    = private_data;
+	srv_conn->model_ops       = model_ops;
+	srv_conn->socket	  = NULL;
+	srv_conn->server_id	  = cluster_id(0, 0);
+	srv_conn->ops             = stream_ops;
+	srv_conn->msg_ctx	  = msg_ctx;
+	srv_conn->event.ctx	  = ev;
+	srv_conn->lp_ctx	  = lp_ctx;
+	srv_conn->event.fde	  = NULL;
 	srv_conn->process_context = process_context;
 
 	*_srv_conn = srv_conn;
@@ -184,13 +184,13 @@ static void stream_new_connection(struct tevent_context *ev,
 
 	talloc_steal(srv_conn, sock);
 
-	srv_conn->private_data	= stream_socket->private_data;
-	srv_conn->model_ops     = stream_socket->model_ops;
-	srv_conn->socket	= sock;
-	srv_conn->server_id	= server_id;
-	srv_conn->ops           = stream_socket->ops;
-	srv_conn->event.ctx	= ev;
-	srv_conn->lp_ctx	= lp_ctx;
+	srv_conn->private_data	  = stream_socket->private_data;
+	srv_conn->model_ops       = stream_socket->model_ops;
+	srv_conn->socket	  = sock;
+	srv_conn->server_id	  = server_id;
+	srv_conn->ops             = stream_socket->ops;
+	srv_conn->event.ctx	  = ev;
+	srv_conn->lp_ctx	  = lp_ctx;
 	srv_conn->process_context = process_context;
 
 	if (!socket_check_access(sock, "smbd", lpcfg_hosts_allow(NULL, lpcfg_default_service(lp_ctx)), lpcfg_hosts_deny(NULL, lpcfg_default_service(lp_ctx)))) {
@@ -263,10 +263,13 @@ static void stream_accept_handler(struct tevent_context *ev, struct tevent_fd *f
 	/* ask the process model to create us a process for this new
 	   connection.  When done, it calls stream_new_connection()
 	   with the newly created socket */
-	stream_socket->model_ops->accept_connection(ev, stream_socket->lp_ctx,
-						    stream_socket->sock,
-						    stream_new_connection, stream_socket,
-						    stream_socket->process_context);
+	stream_socket->model_ops->accept_connection(
+		ev,
+		stream_socket->lp_ctx,
+		stream_socket->sock,
+		stream_new_connection,
+		stream_socket,
+		stream_socket->process_context);
 }
 
 /*
-- 
2.7.4


From 0fcc3b948e7d70a7d1a62a16565b46fcf1cdbd99 Mon Sep 17 00:00:00 2001
From: Gary Lockyer <gary at catalyst.net.nz>
Date: Thu, 7 Sep 2017 14:30:15 +1200
Subject: [PATCH 7/9] source4/smbd: add a prefork process model.

Add a pre fork process model to bound the number processes forked by
samba.  Currently workers are only pre-forked for the ldap server,  all
the other services have pre-fork support disabled.

When pre-fork support is disabled a new process is started for each
service, and requests are processed by that process.

This commit partially reverts commit
b5be45c453bd51373bade26c29828b500ba586ec.

Signed-off-by: Gary Lockyer <gary at catalyst.net.nz>
Reviewed-by: Andrew Bartlett <abartlet at samba.org>
---
 docs-xml/manpages/samba.8.xml                |  31 ++-
 docs-xml/smbdotconf/base/preforkchildren.xml |  26 ++
 lib/param/loadparm.c                         |   2 +
 source3/param/loadparm.c                     |   1 +
 source4/smbd/process_prefork.c               | 391 +++++++++++++++++++++++++++
 source4/smbd/wscript_build                   |   7 +
 6 files changed, 454 insertions(+), 4 deletions(-)
 create mode 100644 docs-xml/smbdotconf/base/preforkchildren.xml
 create mode 100644 source4/smbd/process_prefork.c

diff --git a/docs-xml/manpages/samba.8.xml b/docs-xml/manpages/samba.8.xml
index d4c5568..c7b1449 100644
--- a/docs-xml/manpages/samba.8.xml
+++ b/docs-xml/manpages/samba.8.xml
@@ -105,10 +105,33 @@
 		<listitem><para>This parameter can be used to specify the
 		"process model" samba should use. This determines
 		how concurrent clients are handled. Available process
-		models include <emphasis>single</emphasis> (everything in
-		a single process), <emphasis>standard</emphasis> (similar
-		behaviour to that of Samba 3), <emphasis>thread</emphasis>
-		(single process, different threads.
+		models include
+		<itemizedlist>
+			<listitem><para><emphasis>single</emphasis></para>
+			<para>All services run in a single process, not
+			recommended for production configurations.
+			</para></listitem>
+
+			<listitem><para><emphasis>standard</emphasis></para>
+			<para>A process is created for each service,
+			and for those services that support it (currently only
+			ldap and netlogon) a new processes is started
+			for each new connection.</para></listitem>
+
+			<listitem><para><emphasis>prefork</emphasis></para>
+			<para>A process is started for each service, and extra
+			worker processes are started for those services that
+			support it (currently only ldap).
+			Requests for services not supporting prefork are handled
+			by a single process for that service.</para>
+			<para>The number of worker processes started is
+			controlled by the
+			<citerefentry><refentrytitle>smb.conf</refentrytitle>
+			<manvolnum>5</manvolnum></citerefentry> parameter
+		        <smbconfoption name="prefork children"/>
+			, which defaults to 1.
+			</para></listitem>
+		</itemizedlist>
 		</para></listitem>
 		</varlistentry>
 
diff --git a/docs-xml/smbdotconf/base/preforkchildren.xml b/docs-xml/smbdotconf/base/preforkchildren.xml
new file mode 100644
index 0000000..c4bf1ab
--- /dev/null
+++ b/docs-xml/smbdotconf/base/preforkchildren.xml
@@ -0,0 +1,26 @@
+<samba:parameter name="prefork children"
+                 context="G"
+                 type="integer"
+                 xmlns:samba="http://www.samba.org/samba/DTD/samba-doc">
+<description>
+	<para>This option controls the number of worker processes that are
+		started for each service when prefork process model is enabled
+		(see <citerefentry><refentrytitle>samba</refentrytitle>
+			<manvolnum>8</manvolnum></citerefentry> -M)
+		The prefork children are only started for those services that
+		support prefork (currently only ldap). For processes that don't
+		support preforking all requests are handled by a single process
+		for that service.
+	</para>
+
+	<para>This should be set to a small multiple of the number of CPU's
+		available on the server</para>
+
+	<para>Additionally the number of prefork children can be specified for
+		an individual service by using "prefork children: service name"
+		i.e. "prefork children:ldap = 8" to set the number of ldap
+		worker processes.</para>
+</description>
+
+<value type="default">1</value>
+</samba:parameter>
diff --git a/lib/param/loadparm.c b/lib/param/loadparm.c
index b91f965..84235e6 100644
--- a/lib/param/loadparm.c
+++ b/lib/param/loadparm.c
@@ -2995,6 +2995,8 @@ struct loadparm_context *loadparm_init(TALLOC_CTX *mem_ctx)
 				  "rpc server dynamic port range",
 				  "49152-65535");
 
+	lpcfg_do_global_parameter(lp_ctx, "prefork children", "1");
+
 	for (i = 0; parm_table[i].label; i++) {
 		if (!(lp_ctx->flags[i] & FLAG_CMDLINE)) {
 			lp_ctx->flags[i] |= FLAG_DEFAULT;
diff --git a/source3/param/loadparm.c b/source3/param/loadparm.c
index 42e579e..102ca2c 100644
--- a/source3/param/loadparm.c
+++ b/source3/param/loadparm.c
@@ -944,6 +944,7 @@ static void init_globals(struct loadparm_context *lp_ctx, bool reinit_globals)
 			 "49152-65535");
 	Globals.rpc_low_port = SERVER_TCP_LOW_PORT;
 	Globals.rpc_high_port = SERVER_TCP_HIGH_PORT;
+	Globals.prefork_children = 1;
 
 	/* Now put back the settings that were set with lp_set_cmdline() */
 	apply_lp_set_cmdline();
diff --git a/source4/smbd/process_prefork.c b/source4/smbd/process_prefork.c
new file mode 100644
index 0000000..f2033e9
--- /dev/null
+++ b/source4/smbd/process_prefork.c
@@ -0,0 +1,391 @@
+/*
+   Unix SMB/CIFS implementation.
+
+   process model: prefork (n client connections per process)
+
+   Copyright (C) Andrew Tridgell 1992-2005
+   Copyright (C) James J Myers 2003 <myersjj at samba.org>
+   Copyright (C) Stefan (metze) Metzmacher 2004
+   Copyright (C) Andrew Bartlett 2008 <abartlet at samba.org>
+   Copyright (C) David Disseldorp 2008 <ddiss at sgi.com>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "lib/events/events.h"
+#include "lib/messaging/messaging.h"
+#include "lib/socket/socket.h"
+#include "smbd/process_model.h"
+#include "cluster/cluster.h"
+#include "param/param.h"
+#include "ldb_wrap.h"
+#include "lib/util/tfork.h"
+
+NTSTATUS process_model_prefork_init(void);
+
+static void sighup_signal_handler(struct tevent_context *ev,
+				struct tevent_signal *se,
+				int signum, int count, void *siginfo,
+				void *private_data)
+{
+	debug_schedule_reopen_logs();
+}
+
+static void sigterm_signal_handler(struct tevent_context *ev,
+				struct tevent_signal *se,
+				int signum, int count, void *siginfo,
+				void *private_data)
+{
+#if HAVE_GETPGRP
+	if (getpgrp() == getpid()) {
+		/*
+		 * We're the process group leader, send
+		 * SIGTERM to our process group.
+		 */
+		DBG_NOTICE("SIGTERM: killing children\n");
+		kill(-getpgrp(), SIGTERM);
+	}
+#endif
+	DBG_NOTICE("Exiting pid %d on SIGTERM\n", getpid());
+	talloc_free(ev);
+	exit(127);
+}
+
+/*
+  called when the process model is selected
+*/
+static void prefork_model_init(void)
+{
+}
+
+static void prefork_reload_after_fork(void)
+{
+	NTSTATUS status;
+
+	ldb_wrap_fork_hook();
+	/* Must be done after a fork() to reset messaging contexts. */
+	status = imessaging_reinit_all();
+	if (!NT_STATUS_IS_OK(status)) {
+		smb_panic("Failed to re-initialise imessaging after fork");
+	}
+}
+
+/*
+  handle EOF on the parent-to-all-children pipe in the child
+*/
+static void prefork_pipe_handler(struct tevent_context *event_ctx,
+		                 struct tevent_fd *fde, uint16_t flags,
+				 void *private_data)
+{
+	/* free the fde which removes the event and stops it firing again */
+	TALLOC_FREE(fde);
+	DBG_NOTICE("Child %d exiting\n", getpid());
+	talloc_free(event_ctx);
+	exit(0);
+}
+
+/*
+  handle EOF on the child pipe in the parent, so we know when a
+  process terminates without using SIGCHLD or waiting on all possible pids.
+
+  We need to ensure we do not ignore SIGCHLD because we need it to
+  work to get a valid error code from samba_runcmd_*().
+ */
+static void prefork_child_pipe_handler(struct tevent_context *ev,
+				       struct tevent_fd *fde,
+				       uint16_t flags,
+				       void *private_data)
+{
+	struct tfork *t = NULL;
+	int status = 0;
+	pid_t pid = 0;
+
+	/* free the fde which removes the event and stops it firing again */
+	TALLOC_FREE(fde);
+
+	/* the child has closed the pipe, assume its dead */
+
+	/* tfork allocates tfork structures with malloc  */
+	t = (struct tfork*)private_data;
+	pid = tfork_child_pid(t);
+	errno = 0;
+	status = tfork_status(&t, false);
+	if (status == -1) {
+		DBG_ERR("Parent %d, Child %d terminated, "
+			"unable to get status code from tfork\n",
+			getpid(), pid);
+	} else if (WIFEXITED(status)) {
+		status = WEXITSTATUS(status);
+		DBG_ERR("Parent %d, Child %d exited with status %d\n",
+			 getpid(), pid,  status);
+	} else if (WIFSIGNALED(status)) {
+		status = WTERMSIG(status);
+		DBG_ERR("Parent %d, Child %d terminated with signal %d\n",
+			getpid(), pid, status);
+	}
+	/* tfork allocates tfork structures with malloc */
+	free(t);
+	return;
+}
+
+/*
+  called when a listening socket becomes readable.
+*/
+static void prefork_accept_connection(
+	struct tevent_context *ev,
+	struct loadparm_context *lp_ctx,
+	struct socket_context *listen_socket,
+	void (*new_conn)(struct tevent_context *,
+			struct loadparm_context *,
+			struct socket_context *,
+			struct server_id,
+			void *,
+			void *),
+	void *private_data,
+	void *process_context)
+{
+	NTSTATUS status;
+	struct socket_context *connected_socket;
+	pid_t pid = getpid();
+
+	/* accept an incoming connection. */
+	status = socket_accept(listen_socket, &connected_socket);
+	if (!NT_STATUS_IS_OK(status)) {
+		/*
+		 * For prefork we can ignore STATUS_MORE_ENTRIES, as  once a
+		 * connection becomes available all waiting processes are
+		 * woken, but only one gets work to  process.
+		 * AKA the thundering herd.
+		 * In the short term this should not be an issue as the number
+		 * of workers should be a small multiple of the number of cpus
+		 * In the longer term socket_accept needs to implement a
+		 * mutex/semaphore (like apache does) to serialise the accepts
+		 */
+		if (!NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES)) {
+			DBG_ERR("Worker process (%d), error in accept [%s]\n",
+				getpid(), nt_errstr(status));
+		}
+		return;
+	}
+
+	talloc_steal(private_data, connected_socket);
+
+	new_conn(ev, lp_ctx, connected_socket,
+		 cluster_id(pid, socket_get_fd(connected_socket)),
+		 private_data, process_context);
+}
+
+static void setup_handlers(struct tevent_context *ev, int from_parent_fd) {
+	struct tevent_fd *fde = NULL;
+	struct tevent_signal *se = NULL;
+
+	fde = tevent_add_fd(ev, ev, from_parent_fd, TEVENT_FD_READ,
+		      prefork_pipe_handler, NULL);
+	if (fde == NULL) {
+		smb_panic("Failed to add fd handler after fork");
+	}
+
+	se = tevent_add_signal(ev,
+			       ev,
+			       SIGHUP,
+			       0,
+			       sighup_signal_handler,
+			       NULL);
+	if (se == NULL) {
+		smb_panic("Failed to add SIGHUP handler after fork");
+	}
+
+	se = tevent_add_signal(ev,
+			       ev,
+			       SIGTERM,
+			       0,
+			       sigterm_signal_handler,
+			       NULL);
+	if (se == NULL) {
+		smb_panic("Failed to add SIGTERM handler after fork");
+	}
+}
+
+/*
+ * called to create a new server task
+ */
+static void prefork_new_task(
+	struct tevent_context *ev,
+	struct loadparm_context *lp_ctx,
+	const char *service_name,
+	void (*new_task_fn)(struct tevent_context *,
+			    struct loadparm_context *lp_ctx,
+			    struct server_id , void *, void *),
+	void *private_data,
+	const struct service_details *service_details,
+	int from_parent_fd)
+{
+	pid_t pid;
+	struct tfork* t = NULL;
+	int i, num_children;
+
+	struct tevent_context *ev2;
+
+	t = tfork_create();
+	if (t == NULL) {
+		smb_panic("failure in tfork\n");
+	}
+
+	pid = tfork_child_pid(t);
+	if (pid != 0) {
+		struct tevent_fd *fde = NULL;
+		int fd = tfork_event_fd(t);
+
+		/* Register a pipe handler that gets called when the prefork
+		 * master process terminates.
+		 */
+		fde = tevent_add_fd(ev, ev, fd, TEVENT_FD_READ,
+				    prefork_child_pipe_handler, t);
+		if (fde == NULL) {
+			smb_panic("Failed to add child pipe handler, "
+				  "after fork");
+		}
+		tevent_fd_set_auto_close(fde);
+		return;
+	}
+
+	pid = getpid();
+	setproctitle("task[%s] pre-fork master", service_name);
+
+	/*
+	 * this will free all the listening sockets and all state that
+	 * is not associated with this new connection
+	 */
+	if (tevent_re_initialise(ev) != 0) {
+		smb_panic("Failed to re-initialise tevent after fork");
+	}
+	prefork_reload_after_fork();
+	setup_handlers(ev, from_parent_fd);
+
+	if (service_details->inhibit_pre_fork) {
+		new_task_fn(ev, lp_ctx, cluster_id(pid, 0), private_data, NULL);
+		/* The task does not support pre-fork */
+		tevent_loop_wait(ev);
+		TALLOC_FREE(ev);
+		exit(0);
+	}
+
+	/*
+	 * This is now the child code. We need a completely new event_context
+	 * to work with
+	 */
+	ev2 = s4_event_context_init(NULL);
+
+	/* setup this new connection: process will bind to it's sockets etc
+	 *
+	 * While we can use ev for the child, which has been re-initialised
+	 * above we must run the new task under ev2 otherwise the children would
+	 * be listening on the sockets.  Also we don't want the top level
+	 * process accepting and handling requests, it's responsible for
+	 * monitoring and controlling the child work processes.
+	 */
+	new_task_fn(ev2, lp_ctx, cluster_id(pid, 0), private_data, NULL);
+
+	{
+		int default_children;
+		default_children = lpcfg_prefork_children(lp_ctx);
+		num_children = lpcfg_parm_int(lp_ctx, NULL, "prefork children",
+			                      service_name, default_children);
+	}
+	if (num_children == 0) {
+		DBG_WARNING("Number of pre-fork children for %s is zero, "
+			    "NO worker processes will be started for %s\n",
+			    service_name, service_name);
+	}
+	DBG_NOTICE("Forking %d %s worker processes\n",
+		   num_children, service_name);
+	/* We are now free to spawn some worker processes */
+	for (i=0; i < num_children; i++) {
+		struct tfork* w = NULL;
+
+		w = tfork_create();
+		if (t == NULL) {
+			smb_panic("failure in tfork\n");
+		}
+
+		pid = tfork_child_pid(w);
+		if (pid != 0) {
+			struct tevent_fd *fde = NULL;
+			int fd = tfork_event_fd(w);
+
+			fde = tevent_add_fd(ev, ev, fd, TEVENT_FD_READ,
+					    prefork_child_pipe_handler, w);
+			if (fde == NULL) {
+				smb_panic("Failed to add child pipe handler, "
+					  "after fork");
+			}
+			tevent_fd_set_auto_close(fde);
+		} else {
+			/* tfork uses malloc */
+			free(w);
+
+			TALLOC_FREE(ev);
+			pid = getpid();
+			setproctitle("task[%s] pre-forked worker",
+				     service_name);
+			prefork_reload_after_fork();
+			setup_handlers(ev2, from_parent_fd);
+			tevent_loop_wait(ev2);
+			talloc_free(ev2);
+			exit(0);
+		}
+	}
+
+	/* Don't listen on the sockets we just gave to the children */
+	tevent_loop_wait(ev);
+	TALLOC_FREE(ev);
+	/* We need to keep ev2 until we're finished for the messaging to work */
+	TALLOC_FREE(ev2);
+	exit(0);
+
+}
+
+
+/* called when a task goes down */
+static void prefork_terminate(struct tevent_context *ev,
+			      struct loadparm_context *lp_ctx,
+			      const char *reason,
+			      void *process_context)
+{
+	DBG_DEBUG("called with reason[%s]\n", reason);
+}
+
+/* called to set a title of a task or connection */
+static void prefork_set_title(struct tevent_context *ev, const char *title)
+{
+}
+
+static const struct model_ops prefork_ops = {
+	.name			= "prefork",
+	.model_init		= prefork_model_init,
+	.accept_connection	= prefork_accept_connection,
+	.new_task		= prefork_new_task,
+	.terminate		= prefork_terminate,
+	.set_title		= prefork_set_title,
+};
+
+/*
+ * initialise the prefork process model, registering ourselves with the
+ * process model subsystem
+ */
+NTSTATUS process_model_prefork_init(void)
+{
+	return register_process_model(&prefork_ops);
+}
diff --git a/source4/smbd/wscript_build b/source4/smbd/wscript_build
index c28bc1d..ef0aaf7 100644
--- a/source4/smbd/wscript_build
+++ b/source4/smbd/wscript_build
@@ -44,3 +44,10 @@ bld.SAMBA_MODULE('process_model_standard',
                  internal_module=False
                  )
 
+bld.SAMBA_MODULE('process_model_prefork',
+                 source='process_prefork.c',
+                 subsystem='process_model',
+                 init_function='process_model_prefork_init',
+                 deps='MESSAGING events ldbsamba cluster samba-sockets process_model messages_dgm',
+                 internal_module=False
+                 )
-- 
2.7.4


From 2acc83ba409ee74810fa9362723dbce6fc562741 Mon Sep 17 00:00:00 2001
From: Gary Lockyer <gary at catalyst.net.nz>
Date: Tue, 5 Sep 2017 12:31:52 +1200
Subject: [PATCH 8/9] selftest: set ad_dc process model to prefork

Set the process model for ad_dc to prefork, so that the pre-fork gets
exercised during self test.

Signed-off-by: Gary Lockyer <gary at catalyst.net.nz>
Reviewed-by: Andrew Bartlett <abartlet at samba.org>
---
 selftest/target/Samba4.pm | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/selftest/target/Samba4.pm b/selftest/target/Samba4.pm
index 7930a4e..631cb95 100755
--- a/selftest/target/Samba4.pm
+++ b/selftest/target/Samba4.pm
@@ -619,6 +619,8 @@ sub provision_raw_step1($$)
 	dreplsrv:periodic_startup_interval = 0
 	dsdb:schema update allowed = yes
 
+        prefork children = 4
+
         vfs objects = dfs_samba4 acl_xattr fake_acls xattr_tdb streams_depot
 
         idmap_ldb:use rfc2307=yes
@@ -2516,7 +2518,7 @@ sub setup_ad_dc($$)
 		$env->{NSS_WRAPPER_MODULE_FN_PREFIX} = undef;
 	}
 
-	if (not defined($self->check_or_start($env, "single"))) {
+	if (not defined($self->check_or_start($env, "prefork"))) {
 	    return undef;
 	}
 
-- 
2.7.4


From c477b21d402a2035e8a1d8fa09039545416d852a Mon Sep 17 00:00:00 2001
From: Gary Lockyer <gary at catalyst.net.nz>
Date: Mon, 11 Sep 2017 11:39:39 +1200
Subject: [PATCH 9/9] source4/smbd: replace DEBUG( with DBG_

Update the debug logging to use the currently preferred debug macros

Signed-off-by: Gary Lockyer <gary at catalyst.net.nz>
Reviewed-by: Andrew Bartlett <abartlet at samba.org>
---
 source4/smbd/process_model.c      |  7 +++----
 source4/smbd/process_single.c     |  5 +++--
 source4/smbd/process_standard.c   | 32 ++++++++++++++++----------------
 source4/smbd/server.c             | 30 +++++++++++++++---------------
 source4/smbd/service.c            |  9 +++++----
 source4/smbd/service_named_pipe.c | 25 ++++++++++++-------------
 source4/smbd/service_stream.c     | 12 ++++++------
 source4/smbd/service_task.c       |  6 +++++-
 8 files changed, 65 insertions(+), 61 deletions(-)

diff --git a/source4/smbd/process_model.c b/source4/smbd/process_model.c
index 20a130b..b75efac 100644
--- a/source4/smbd/process_model.c
+++ b/source4/smbd/process_model.c
@@ -57,7 +57,7 @@ _PUBLIC_ const struct model_ops *process_model_startup(const char *model)
 
 	m = process_model_byname(model);
 	if (m == NULL) {
-		DEBUG(0,("Unknown process model '%s'\n", model));
+		DBG_ERR("Unknown process model '%s'\n", model);
 		exit(-1);
 	}
 
@@ -79,8 +79,7 @@ _PUBLIC_ NTSTATUS register_process_model(const struct model_ops *ops)
 {
 	if (process_model_byname(ops->name) != NULL) {
 		/* its already registered! */
-		DEBUG(0,("PROCESS_MODEL '%s' already registered\n", 
-			 ops->name));
+		DBG_ERR("PROCESS_MODEL '%s' already registered\n", ops->name);
 		return NT_STATUS_OBJECT_NAME_COLLISION;
 	}
 
@@ -94,7 +93,7 @@ _PUBLIC_ NTSTATUS register_process_model(const struct model_ops *ops)
 
 	num_models++;
 
-	DEBUG(3,("PROCESS_MODEL '%s' registered\n", ops->name));
+	DBG_NOTICE("PROCESS_MODEL '%s' registered\n", ops->name);
 
 	return NT_STATUS_OK;
 }
diff --git a/source4/smbd/process_single.c b/source4/smbd/process_single.c
index 1027415..1859c96 100644
--- a/source4/smbd/process_single.c
+++ b/source4/smbd/process_single.c
@@ -56,7 +56,8 @@ static void single_accept_connection(struct tevent_context *ev,
 	/* accept an incoming connection. */
 	status = socket_accept(listen_socket, &connected_socket);
 	if (!NT_STATUS_IS_OK(status)) {
-		DEBUG(0,("single_accept_connection: accept: %s\n", nt_errstr(status)));
+		DBG_ERR("single_accept_connection: accept: %s\n",
+			nt_errstr(status));
 		/* this looks strange, but is correct. 
 
 		   We can only be here if woken up from select, due to
@@ -121,7 +122,7 @@ static void single_terminate(struct tevent_context *ev,
 			     const char *reason,
 			     void *process_context)
 {
-	DEBUG(3,("single_terminate: reason[%s]\n",reason));
+	DBG_NOTICE("single_terminate: reason[%s]\n",reason);
 }
 
 /* called to set a title of a task or connection */
diff --git a/source4/smbd/process_standard.c b/source4/smbd/process_standard.c
index f642b18..68266bd 100644
--- a/source4/smbd/process_standard.c
+++ b/source4/smbd/process_standard.c
@@ -74,11 +74,11 @@ static void sigterm_signal_handler(struct tevent_context *ev,
 		 * We're the process group leader, send
 		 * SIGTERM to our process group.
 		 */
-		DEBUG(0,("SIGTERM: killing children\n"));
+		DBG_ERR("SIGTERM: killing children\n");
 		kill(-getpgrp(), SIGTERM);
 	}
 #endif
-	DEBUG(0,("Exiting pid %u on SIGTERM\n", (unsigned int)getpid()));
+	DBG_ERR("Exiting pid %u on SIGTERM\n", (unsigned int)getpid());
 	talloc_free(ev);
 	exit(127);
 }
@@ -89,7 +89,7 @@ static void sigterm_signal_handler(struct tevent_context *ev,
 static void standard_pipe_handler(struct tevent_context *event_ctx, struct tevent_fd *fde, 
 				  uint16_t flags, void *private_data)
 {
-	DEBUG(10,("Child %d exiting\n", (int)getpid()));
+	DBG_DEBUG("Child %d exiting\n", (int)getpid());
 	talloc_free(event_ctx);
 	exit(0);
 }
@@ -130,16 +130,16 @@ static void standard_child_pipe_handler(struct tevent_context *ev,
 			 * SIGCHLD in the standard
 			 * process model.
 			 */
-			DEBUG(0, ("Error in waitpid() unexpectedly got ECHILD "
-				  "for child %d (%s) - %s, someone has set SIGCHLD "
-				  "to SIG_IGN!\n",
-				  (int)state->pid, state->name,
-				  strerror(errno)));
+			DBG_ERR("Error in waitpid() unexpectedly got ECHILD "
+				"for child %d (%s) - %s, someone has set SIGCHLD "
+				"to SIG_IGN!\n",
+				(int)state->pid, state->name,
+				strerror(errno));
 			TALLOC_FREE(state);
 			return;
 		}
-		DEBUG(0, ("Error in waitpid() for child %d (%s) - %s \n",
-			  (int)state->pid, state->name, strerror(errno)));
+		DBG_ERR("Error in waitpid() for child %d (%s) - %s \n",
+			(int)state->pid, state->name, strerror(errno));
 		if (errno == 0) {
 			errno = ECHILD;
 		}
@@ -154,8 +154,8 @@ static void standard_child_pipe_handler(struct tevent_context *ev,
 		}
 	} else if (WIFSIGNALED(status)) {
 		status = WTERMSIG(status);
-		DEBUG(0, ("Child %d (%s) terminated with signal %d\n",
-			  (int)state->pid, state->name, status));
+		DBG_ERR("Child %d (%s) terminated with signal %d\n",
+			(int)state->pid, state->name, status);
 	}
 	TALLOC_FREE(state);
 	return;
@@ -193,8 +193,8 @@ static struct standard_child_state *setup_standard_child_pipe(struct tevent_cont
 
 	ret = pipe(parent_child_pipe);
 	if (ret == -1) {
-		DEBUG(0, ("Failed to create parent-child pipe to handle "
-			  "SIGCHLD to track new process for socket\n"));
+		DBG_ERR("Failed to create parent-child pipe to handle "
+			"SIGCHLD to track new process for socket\n");
 		TALLOC_FREE(state);
 		return NULL;
 	}
@@ -254,8 +254,8 @@ static void standard_accept_connection(
 	/* accept an incoming connection. */
 	status = socket_accept(sock, &sock2);
 	if (!NT_STATUS_IS_OK(status)) {
-		DEBUG(0,("standard_accept_connection: accept: %s\n",
-			nt_errstr(status)));
+		DBG_DEBUG("standard_accept_connection: accept: %s\n",
+			  nt_errstr(status));
 		/* this looks strange, but is correct. We need to throttle
 		 * things until the system clears enough resources to handle
 		 * this new socket
diff --git a/source4/smbd/server.c b/source4/smbd/server.c
index e229415..3548e12 100644
--- a/source4/smbd/server.c
+++ b/source4/smbd/server.c
@@ -85,8 +85,8 @@ static void recursive_delete(const char *path)
 			continue;
 		}
 		if (unlink(fname) != 0) {
-			DEBUG(0,("Unabled to delete '%s' - %s\n",
-				 fname, strerror(errno)));
+			DBG_ERR("Unabled to delete '%s' - %s\n",
+				 fname, strerror(errno));
 			smb_panic("unable to cleanup tmp files");
 		}
 		talloc_free(fname);
@@ -125,11 +125,11 @@ static void sig_term(int sig)
 		 * We're the process group leader, send
 		 * SIGTERM to our process group.
 		 */
-		DEBUG(0,("SIGTERM: killing children\n"));
+		DBG_ERR("SIGTERM: killing children\n");
 		kill(-getpgrp(), SIGTERM);
 	}
 #endif
-	DEBUG(0,("Exiting pid %d on SIGTERM\n", (int)getpid()));
+	DBG_ERR("Exiting pid %d on SIGTERM\n", (int)getpid());
 	exit(127);
 }
 
@@ -141,7 +141,7 @@ static void sigterm_signal_handler(struct tevent_context *ev,
 	struct server_state *state = talloc_get_type_abort(
                 private_data, struct server_state);
 
-	DEBUG(10,("Process %s got SIGTERM\n", state->binary_name));
+	DBG_DEBUG("Process %s got SIGTERM\n", state->binary_name);
 	TALLOC_FREE(state);
 	sig_term(SIGTERM);
 }
@@ -189,12 +189,12 @@ static void server_stdin_handler(struct tevent_context *event_ctx,
 		private_data, struct server_state);
 	uint8_t c;
 	if (read(0, &c, 1) == 0) {
-		DEBUG(0,("%s: EOF on stdin - PID %d terminating\n",
-				state->binary_name, (int)getpid()));
+		DBG_ERR("%s: EOF on stdin - PID %d terminating\n",
+			state->binary_name, (int)getpid());
 #if HAVE_GETPGRP
 		if (getpgrp() == getpid()) {
-			DEBUG(0,("Sending SIGTERM from pid %d\n",
-				(int)getpid()));
+			DBG_ERR("Sending SIGTERM from pid %d\n",
+				(int)getpid());
 			kill(-getpgrp(), SIGTERM);
 		}
 #endif
@@ -212,12 +212,12 @@ _NORETURN_ static void max_runtime_handler(struct tevent_context *ev,
 {
 	struct server_state *state = talloc_get_type_abort(
 		private_data, struct server_state);
-	DEBUG(0,("%s: maximum runtime exceeded - "
+	DBG_ERR("%s: maximum runtime exceeded - "
 		"terminating PID %d at %llu, current ts: %llu\n",
 		 state->binary_name,
 		(int)getpid(),
 		(unsigned long long)t.tv_sec,
-		(unsigned long long)time(NULL)));
+		(unsigned long long)time(NULL));
 	TALLOC_FREE(state);
 	exit(0);
 }
@@ -471,7 +471,7 @@ static int binary_smbd_main(const char *binary_name,
 	}
 
 	if (opt_daemon) {
-		DEBUG(3,("Becoming a daemon.\n"));
+		DBG_NOTICE("Becoming a daemon.\n");
 		become_daemon(true, false, false);
 	}
 
@@ -571,10 +571,10 @@ static int binary_smbd_main(const char *binary_name,
 
 	if (max_runtime) {
 		struct tevent_timer *te;
-		DEBUG(0,("%s PID %d was called with maxruntime %d - "
+		DBG_ERR("%s PID %d was called with maxruntime %d - "
 			"current ts %llu\n",
 			binary_name, (int)getpid(),
-			max_runtime, (unsigned long long) time(NULL)));
+			max_runtime, (unsigned long long) time(NULL));
 		te = tevent_add_timer(state->event_ctx, state->event_ctx,
 				 timeval_current_ofs(max_runtime, 0),
 				 max_runtime_handler,
@@ -625,7 +625,7 @@ static int binary_smbd_main(const char *binary_name,
 			NT_STATUS_V(status));
 	}
 
-	DEBUG(0,("%s: using '%s' process model\n", binary_name, model));
+	DBG_ERR("%s: using '%s' process model\n", binary_name, model);
 
 	{
 		int child_pipe[2];
diff --git a/source4/smbd/service.c b/source4/smbd/service.c
index 9a77ca8..e780872 100644
--- a/source4/smbd/service.c
+++ b/source4/smbd/service.c
@@ -91,13 +91,14 @@ NTSTATUS server_service_startup(struct tevent_context *event_ctx,
 	const struct model_ops *model_ops;
 
 	if (!server_services) {
-		DEBUG(0,("server_service_startup: no endpoint servers configured\n"));
+		DBG_ERR("server_service_startup: "
+			"no endpoint servers configured\n");
 		return NT_STATUS_INVALID_PARAMETER;
 	}
 
 	model_ops = process_model_startup(model);
 	if (!model_ops) {
-		DEBUG(0,("process_model_startup('%s') failed\n", model));
+		DBG_ERR("process_model_startup('%s') failed\n", model);
 		return NT_STATUS_INTERNAL_ERROR;
 	}
 
@@ -107,8 +108,8 @@ NTSTATUS server_service_startup(struct tevent_context *event_ctx,
 		status = server_service_init(server_services[i], event_ctx,
 					     lp_ctx, model_ops, from_parent_fd);
 		if (!NT_STATUS_IS_OK(status)) {
-			DEBUG(0,("Failed to start service '%s' - %s\n", 
-				 server_services[i], nt_errstr(status)));
+			DBG_ERR("Failed to start service '%s' - %s\n",
+				 server_services[i], nt_errstr(status));
 		}
 		NT_STATUS_NOT_OK_RETURN(status);
 	}
diff --git a/source4/smbd/service_named_pipe.c b/source4/smbd/service_named_pipe.c
index 744f942..d054c6f 100644
--- a/source4/smbd/service_named_pipe.c
+++ b/source4/smbd/service_named_pipe.c
@@ -122,13 +122,13 @@ static void named_pipe_accept_done(struct tevent_req *subreq)
 	conn->local_address = talloc_move(conn, &local_server_addr);
 	conn->remote_address = talloc_move(conn, &remote_client_addr);
 
-	DEBUG(10, ("Accepted npa connection from %s. "
-		   "Client: %s (%s). Server: %s (%s)\n",
-		   tsocket_address_string(conn->remote_address, tmp_ctx),
-		   local_server_name,
-		   tsocket_address_string(local_server_addr, tmp_ctx),
-		   remote_client_name,
-		   tsocket_address_string(remote_client_addr, tmp_ctx)));
+	DBG_DEBUG("Accepted npa connection from %s. "
+		  "Client: %s (%s). Server: %s (%s)\n",
+		  tsocket_address_string(conn->remote_address, tmp_ctx),
+		  local_server_name,
+		  tsocket_address_string(local_server_addr, tmp_ctx),
+		  remote_client_name,
+		  tsocket_address_string(remote_client_addr, tmp_ctx));
 
 	conn->session_info = auth_session_info_from_transport(conn, session_info_transport,
 							      conn->lp_ctx,
@@ -145,8 +145,7 @@ static void named_pipe_accept_done(struct tevent_req *subreq)
 	conn->private_data = pipe_sock->private_data;
 	conn->ops->accept_connection(conn);
 
-	DEBUG(10, ("named pipe connection [%s] established\n",
-		   conn->ops->name));
+	DBG_DEBUG("named pipe connection [%s] established\n", conn->ops->name);
 
 	talloc_free(tmp_ctx);
 	return;
@@ -208,8 +207,8 @@ NTSTATUS tstream_setup_named_pipe(TALLOC_CTX *mem_ctx,
 
 	if (!directory_create_or_exist(lpcfg_ncalrpc_dir(lp_ctx), 0755)) {
 		status = map_nt_error_from_unix_common(errno);
-		DEBUG(0,(__location__ ": Failed to create ncalrpc pipe directory '%s' - %s\n",
-			 lpcfg_ncalrpc_dir(lp_ctx), nt_errstr(status)));
+		DBG_ERR("Failed to create ncalrpc pipe directory '%s' - %s\n",
+			lpcfg_ncalrpc_dir(lp_ctx), nt_errstr(status));
 		goto fail;
 	}
 
@@ -220,8 +219,8 @@ NTSTATUS tstream_setup_named_pipe(TALLOC_CTX *mem_ctx,
 
 	if (!directory_create_or_exist_strict(dirname, geteuid(), 0700)) {
 		status = map_nt_error_from_unix_common(errno);
-		DEBUG(0,(__location__ ": Failed to create stream pipe directory '%s' - %s\n",
-			 dirname, nt_errstr(status)));
+		DBG_ERR("Failed to create stream pipe directory '%s' - %s\n",
+			dirname, nt_errstr(status));
 		goto fail;
 	}
 
diff --git a/source4/smbd/service_stream.c b/source4/smbd/service_stream.c
index 63318c6..545cd43 100644
--- a/source4/smbd/service_stream.c
+++ b/source4/smbd/service_stream.c
@@ -62,9 +62,9 @@ void stream_terminate_connection(struct stream_connection *srv_conn, const char
 	if (!reason) reason = "unknown reason";
 
 	if (srv_conn->processing) {
-		DEBUG(3,("Terminating connection deferred - '%s'\n", reason));
+		DBG_NOTICE("Terminating connection deferred - '%s'\n", reason);
 	} else {
-		DEBUG(3,("Terminating connection - '%s'\n", reason));
+		DBG_NOTICE("Terminating connection - '%s'\n", reason);
 	}
 
 	srv_conn->terminate = reason;
@@ -178,7 +178,7 @@ static void stream_new_connection(struct tevent_context *ev,
 
 	srv_conn = talloc_zero(ev, struct stream_connection);
 	if (!srv_conn) {
-		DEBUG(0,("talloc(mem_ctx, struct stream_connection) failed\n"));
+		DBG_ERR("talloc(mem_ctx, struct stream_connection) failed\n");
 		return;
 	}
 
@@ -366,9 +366,9 @@ NTSTATUS stream_setup_socket(TALLOC_CTX *mem_ctx,
 	}
 
 	if (!NT_STATUS_IS_OK(status)) {
-		DEBUG(0,("Failed to listen on %s:%u - %s\n",
+		DBG_ERR("Failed to listen on %s:%u - %s\n",
 			 sock_addr, port ? (unsigned int)(*port) : 0,
-			 nt_errstr(status)));
+			 nt_errstr(status));
 		talloc_free(stream_socket);
 		return status;
 	}
@@ -382,7 +382,7 @@ NTSTATUS stream_setup_socket(TALLOC_CTX *mem_ctx,
 			    TEVENT_FD_READ,
 			    stream_accept_handler, stream_socket);
 	if (!fde) {
-		DEBUG(0,("Failed to setup fd event\n"));
+		DBG_ERR("Failed to setup fd event\n");
 		talloc_free(stream_socket);
 		return NT_STATUS_NO_MEMORY;
 	}
diff --git a/source4/smbd/service_task.c b/source4/smbd/service_task.c
index 1d33a43..729a109 100644
--- a/source4/smbd/service_task.c
+++ b/source4/smbd/service_task.c
@@ -32,7 +32,11 @@ void task_server_terminate(struct task_server *task, const char *reason, bool fa
 {
 	struct tevent_context *event_ctx = task->event_ctx;
 	const struct model_ops *model_ops = task->model_ops;
-	DEBUG(0,("task_server_terminate: [%s]\n", reason));
+	if (fatal) {
+		DBG_ERR("task_server_terminate: [%s]\n", reason);
+	} else {
+		DBG_NOTICE("task_server_terminate: [%s]\n", reason);
+	}
 
 	if (fatal && task->msg_ctx != NULL) {
 		struct dcerpc_binding_handle *irpc_handle;
-- 
2.7.4

-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 473 bytes
Desc: OpenPGP digital signature
URL: <http://lists.samba.org/pipermail/samba-technical/attachments/20170926/93155240/signature.sig>


More information about the samba-technical mailing list