summaryrefslogtreecommitdiffstats
path: root/source4/samba
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 17:20:00 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 17:20:00 +0000
commit8daa83a594a2e98f39d764422bfbdbc62c9efd44 (patch)
tree4099e8021376c7d8c05bdf8503093d80e9c7bad0 /source4/samba
parentInitial commit. (diff)
downloadsamba-8daa83a594a2e98f39d764422bfbdbc62c9efd44.tar.xz
samba-8daa83a594a2e98f39d764422bfbdbc62c9efd44.zip
Adding upstream version 2:4.20.0+dfsg.upstream/2%4.20.0+dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'source4/samba')
-rw-r--r--source4/samba/process_model.c137
-rw-r--r--source4/samba/process_model.h96
-rw-r--r--source4/samba/process_prefork.c919
-rw-r--r--source4/samba/process_single.c169
-rw-r--r--source4/samba/process_standard.c626
-rw-r--r--source4/samba/server.c1000
-rw-r--r--source4/samba/server_util.c94
-rw-r--r--source4/samba/server_util.h33
-rw-r--r--source4/samba/service.c140
-rw-r--r--source4/samba/service.h111
-rw-r--r--source4/samba/service_named_pipe.c290
-rw-r--r--source4/samba/service_stream.c413
-rw-r--r--source4/samba/service_stream.h80
-rw-r--r--source4/samba/service_task.c140
-rw-r--r--source4/samba/service_task.h39
-rw-r--r--source4/samba/wscript_build58
16 files changed, 4345 insertions, 0 deletions
diff --git a/source4/samba/process_model.c b/source4/samba/process_model.c
new file mode 100644
index 0000000..754da79
--- /dev/null
+++ b/source4/samba/process_model.c
@@ -0,0 +1,137 @@
+/*
+ Unix SMB/CIFS implementation.
+ process model manager - main loop
+ Copyright (C) Andrew Tridgell 1992-2003
+ Copyright (C) James J Myers 2003 <myersjj@samba.org>
+
+ 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 "samba/process_model.h"
+#include "param/param.h"
+#include "lib/util/samba_modules.h"
+
+/* the list of currently registered process models */
+static struct process_model {
+ const struct model_ops *ops;
+ bool initialised;
+} *models = NULL;
+static int num_models;
+
+
+/*
+ return the operations structure for a named backend of the specified type
+*/
+static struct process_model *process_model_byname(const char *name)
+{
+ int i;
+
+ for (i=0;i<num_models;i++) {
+ if (strcmp(models[i].ops->name, name) == 0) {
+ return &models[i];
+ }
+ }
+
+ return NULL;
+}
+
+
+/*
+ setup the events for the chosen process model
+*/
+_PUBLIC_ const struct model_ops *process_model_startup(const char *model)
+{
+ struct process_model *m;
+
+ m = process_model_byname(model);
+ if (m == NULL) {
+ DBG_ERR("Unknown process model '%s'\n", model);
+ exit(-1);
+ }
+
+ if (!m->initialised) {
+ m->initialised = true;
+ m->ops->model_init();
+ }
+
+ return m->ops;
+}
+
+/*
+ register a process model.
+
+ The 'name' can be later used by other backends to find the operations
+ structure for this backend.
+*/
+_PUBLIC_ NTSTATUS register_process_model(const struct model_ops *ops)
+{
+ if (process_model_byname(ops->name) != NULL) {
+ /* its already registered! */
+ DBG_ERR("PROCESS_MODEL '%s' already registered\n", ops->name);
+ return NT_STATUS_OBJECT_NAME_COLLISION;
+ }
+
+ models = talloc_realloc(NULL, models, struct process_model, num_models+1);
+ if (!models) {
+ smb_panic("out of memory in register_process_model");
+ }
+
+ models[num_models].ops = ops;
+ models[num_models].initialised = false;
+
+ num_models++;
+
+ DBG_NOTICE("PROCESS_MODEL '%s' registered\n", ops->name);
+
+ return NT_STATUS_OK;
+}
+
+_PUBLIC_ NTSTATUS process_model_init(struct loadparm_context *lp_ctx)
+{
+#define _MODULE_PROTO(init) extern NTSTATUS init(TALLOC_CTX *);
+ STATIC_process_model_MODULES_PROTO;
+ init_module_fn static_init[] = { STATIC_process_model_MODULES };
+ init_module_fn *shared_init;
+ static bool initialised;
+
+ if (initialised) {
+ return NT_STATUS_OK;
+ }
+ initialised = true;
+
+ shared_init = load_samba_modules(NULL, "process_model");
+
+ run_init_functions(NULL, static_init);
+ run_init_functions(NULL, shared_init);
+
+ talloc_free(shared_init);
+
+ return NT_STATUS_OK;
+}
+
+/*
+ return the PROCESS_MODEL module version, and the size of some critical types
+ This can be used by process model modules to either detect compilation errors, or provide
+ multiple implementations for different smbd compilation options in one module
+*/
+const struct process_model_critical_sizes *process_model_version(void)
+{
+ static const struct process_model_critical_sizes critical_sizes = {
+ PROCESS_MODEL_VERSION,
+ sizeof(struct model_ops)
+ };
+
+ return &critical_sizes;
+}
diff --git a/source4/samba/process_model.h b/source4/samba/process_model.h
new file mode 100644
index 0000000..446e0f1
--- /dev/null
+++ b/source4/samba/process_model.h
@@ -0,0 +1,96 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ process model manager - structures
+
+ Copyright (C) Andrew Tridgell 1992-2005
+ Copyright (C) James J Myers 2003 <myersjj@samba.org>
+ Copyright (C) Stefan (metze) Metzmacher 2004-2005
+
+ 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/>.
+*/
+
+#ifndef __PROCESS_MODEL_H__
+#define __PROCESS_MODEL_H__
+
+#include "lib/socket/socket.h"
+#include "samba/service.h"
+#include "samba/process_model_proto.h"
+
+/* modules can use the following to determine if the interface has changed
+ * please increment the version number after each interface change
+ * with a comment and maybe update struct process_model_critical_sizes.
+ */
+/* version 1 - initial version - metze */
+#define PROCESS_MODEL_VERSION 1
+
+/* the process model operations structure - contains function pointers to
+ the model-specific implementations of each operation */
+struct model_ops {
+ /* the name of the process_model */
+ const char *name;
+
+ /* called at startup when the model is selected */
+ void (*model_init)(void);
+
+ /* function to accept new connection */
+ void (*accept_connection)(struct tevent_context *,
+ struct loadparm_context *,
+ struct socket_context *,
+ void (*)(struct tevent_context *,
+ struct loadparm_context *,
+ struct socket_context *,
+ struct server_id , void *, void *),
+ void *, void *);
+
+ /* function to create a task */
+ void (*new_task)(struct tevent_context *,
+ struct loadparm_context *lp_ctx,
+ const char *service_name,
+ struct task_server * (*)(struct tevent_context *,
+ struct loadparm_context *, struct server_id,
+ void *, void *),
+ void *,
+ const struct service_details*,
+ const int);
+
+ /* function to terminate a task */
+ void (*terminate_task)(struct tevent_context *,
+ struct loadparm_context *lp_ctx,
+ const char *reason,
+ bool fatal,
+ void *process_context);
+ /* function to terminate a connection */
+ void (*terminate_connection)(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);
+};
+
+/* this structure is used by modules to determine the size of some critical types */
+struct process_model_critical_sizes {
+ int interface_version;
+ int sizeof_model_ops;
+};
+
+extern const struct model_ops single_ops;
+
+const struct model_ops *process_model_startup(const char *model);
+NTSTATUS register_process_model(const struct model_ops *ops);
+NTSTATUS process_model_init(struct loadparm_context *lp_ctx);
+
+#endif /* __PROCESS_MODEL_H__ */
diff --git a/source4/samba/process_prefork.c b/source4/samba/process_prefork.c
new file mode 100644
index 0000000..92e145d
--- /dev/null
+++ b/source4/samba/process_prefork.c
@@ -0,0 +1,919 @@
+/*
+ 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@samba.org>
+ Copyright (C) Stefan (metze) Metzmacher 2004
+ Copyright (C) Andrew Bartlett 2008 <abartlet@samba.org>
+ Copyright (C) David Disseldorp 2008 <ddiss@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/>.
+*/
+/*
+ * The pre-fork process model distributes the server workload amongst several
+ * designated worker threads (e.g. 'prefork-worker-ldap-0',
+ * 'prefork-worker-ldap-1', etc). The number of worker threads is controlled
+ * by the 'prefork children' conf setting. The worker threads are controlled
+ * by a prefork master process (e.g. 'prefork-master-ldap'). The prefork master
+ * doesn't handle the server workload (i.e. processing messages) itself, but is
+ * responsible for restarting workers if they exit unexpectedly. The top-level
+ * samba process is responsible for restarting the master process if it exits.
+ */
+#include "includes.h"
+#include <unistd.h>
+
+#include "lib/events/events.h"
+#include "lib/messaging/messaging.h"
+#include "lib/socket/socket.h"
+#include "samba/process_model.h"
+#include "cluster/cluster.h"
+#include "param/param.h"
+#include "ldb_wrap.h"
+#include "lib/util/tfork.h"
+#include "lib/messaging/irpc.h"
+#include "lib/util/util_process.h"
+#include "server_util.h"
+
+#define min(a, b) (((a) < (b)) ? (a) : (b))
+
+NTSTATUS process_model_prefork_init(void);
+static void prefork_new_task(
+ struct tevent_context *ev,
+ struct loadparm_context *lp_ctx,
+ const char *service_name,
+ struct task_server *(*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);
+static void prefork_fork_worker(struct task_server *task,
+ struct tevent_context *ev,
+ struct tevent_context *ev2,
+ struct loadparm_context *lp_ctx,
+ const struct service_details *service_details,
+ const char *service_name,
+ int control_pipe[2],
+ unsigned restart_delay,
+ struct process_details *pd);
+static void prefork_child_pipe_handler(struct tevent_context *ev,
+ struct tevent_fd *fde,
+ uint16_t flags,
+ void *private_data);
+static void setup_handlers(struct tevent_context *ev,
+ struct loadparm_context *lp_ctx,
+ int from_parent_fd);
+
+/*
+ * State needed to restart the master process or a worker process if they
+ * terminate early.
+ */
+struct master_restart_context {
+ struct task_server *(*new_task_fn)(struct tevent_context *,
+ struct loadparm_context *lp_ctx,
+ struct server_id,
+ void *,
+ void *);
+ void *private_data;
+};
+
+struct worker_restart_context {
+ unsigned int instance;
+ struct task_server *task;
+ struct tevent_context *ev2;
+ int control_pipe[2];
+};
+
+struct restart_context {
+ struct loadparm_context *lp_ctx;
+ struct tfork *t;
+ int from_parent_fd;
+ const struct service_details *service_details;
+ const char *service_name;
+ unsigned restart_delay;
+ struct master_restart_context *master;
+ struct worker_restart_context *worker;
+};
+
+static void sighup_signal_handler(struct tevent_context *ev,
+ struct tevent_signal *se,
+ int signum, int count, void *siginfo,
+ void *private_data)
+{
+ reopen_logs_internal();
+}
+
+static void sigterm_signal_handler(struct tevent_context *ev,
+ struct tevent_signal *se,
+ int signum, int count, void *siginfo,
+ void *private_data)
+{
+#ifdef 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");
+ }
+ force_check_log_size();
+}
+
+/*
+ * clean up any messaging associated with the old process.
+ *
+ */
+static void irpc_cleanup(
+ struct loadparm_context *lp_ctx,
+ struct tevent_context *ev,
+ pid_t pid)
+{
+ TALLOC_CTX *mem_ctx = talloc_new(NULL);
+ struct imessaging_context *msg_ctx = NULL;
+ NTSTATUS status = NT_STATUS_OK;
+
+ if (mem_ctx == NULL) {
+ DBG_ERR("OOM cleaning up irpc\n");
+ return;
+ }
+ msg_ctx = imessaging_client_init(mem_ctx, lp_ctx, ev);
+ if (msg_ctx == NULL) {
+ DBG_ERR("Unable to create imessaging_context\n");
+ TALLOC_FREE(mem_ctx);
+ return;
+ }
+ status = imessaging_process_cleanup(msg_ctx, pid);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_ERR("imessaging_process_cleanup returned (%s)\n",
+ nt_errstr(status));
+ TALLOC_FREE(mem_ctx);
+ return;
+ }
+
+ TALLOC_FREE(mem_ctx);
+}
+
+/*
+ * handle EOF on the parent-to-all-children pipe in the child, i.e.
+ * the parent has died and its end of the pipe has been closed.
+ * The child handles this by exiting as well.
+ */
+static void prefork_pipe_handler(struct tevent_context *event_ctx,
+ struct tevent_fd *fde, uint16_t flags,
+ void *private_data)
+{
+ struct loadparm_context *lp_ctx = NULL;
+ pid_t pid;
+
+ /*
+ * free the fde which removes the event and stops it firing again
+ */
+ TALLOC_FREE(fde);
+
+ /*
+ * Clean up any irpc end points this process had.
+ */
+ pid = getpid();
+ lp_ctx = talloc_get_type_abort(private_data, struct loadparm_context);
+ irpc_cleanup(lp_ctx, event_ctx, pid);
+
+ DBG_NOTICE("Child %d exiting\n", getpid());
+ TALLOC_FREE(event_ctx);
+ exit(0);
+}
+
+
+/*
+ * Called by the top-level samba process to create a new prefork master process
+ */
+static void prefork_fork_master(
+ struct tevent_context *ev,
+ struct loadparm_context *lp_ctx,
+ const char *service_name,
+ struct task_server *(*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,
+ unsigned restart_delay,
+ int from_parent_fd)
+{
+ pid_t pid;
+ struct tfork* t = NULL;
+ int i, num_children;
+
+ struct tevent_context *ev2;
+ struct task_server *task = NULL;
+ struct process_details pd = initial_process_details;
+ struct samba_tevent_trace_state *samba_tevent_trace_state = NULL;
+ int control_pipe[2];
+
+ t = tfork_create();
+ if (t == NULL) {
+ smb_panic("failure in tfork\n");
+ }
+
+ DBG_NOTICE("Forking [%s] pre-fork master process\n", service_name);
+ pid = tfork_child_pid(t);
+ if (pid != 0) {
+ struct tevent_fd *fde = NULL;
+ int fd = tfork_event_fd(t);
+ struct restart_context *rc = NULL;
+
+ /* Register a pipe handler that gets called when the prefork
+ * master process terminates.
+ */
+ rc = talloc_zero(ev, struct restart_context);
+ if (rc == NULL) {
+ smb_panic("OOM allocating restart context\n");
+ }
+ rc->t = t;
+ rc->lp_ctx = lp_ctx;
+ rc->service_name = service_name;
+ rc->service_details = service_details;
+ rc->from_parent_fd = from_parent_fd;
+ rc->restart_delay = restart_delay;
+ rc->master = talloc_zero(rc, struct master_restart_context);
+ if (rc->master == NULL) {
+ smb_panic("OOM allocating master restart context\n");
+ }
+
+ rc->master->new_task_fn = new_task_fn;
+ rc->master->private_data = private_data;
+
+ fde = tevent_add_fd(
+ ev, ev, fd, TEVENT_FD_READ, prefork_child_pipe_handler, rc);
+ if (fde == NULL) {
+ smb_panic("Failed to add child pipe handler, "
+ "after fork");
+ }
+ tevent_fd_set_auto_close(fde);
+ return;
+ }
+
+ pid = getpid();
+
+ process_set_title("%s[master]", "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, lp_ctx, from_parent_fd);
+
+ if (service_details->inhibit_pre_fork) {
+ task = new_task_fn(
+ ev, lp_ctx, cluster_id(pid, 0), private_data, NULL);
+ /*
+ * The task does not support pre-fork
+ */
+ if (task != NULL && service_details->post_fork != NULL) {
+ service_details->post_fork(task, &pd);
+ }
+ if (task != NULL && service_details->before_loop != NULL) {
+ service_details->before_loop(task);
+ }
+ 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);
+
+ samba_tevent_trace_state = create_samba_tevent_trace_state(ev2);
+ if (samba_tevent_trace_state == NULL) {
+ TALLOC_FREE(ev);
+ TALLOC_FREE(ev2);
+ exit(127);
+ }
+
+ tevent_set_trace_callback(ev2,
+ samba_tevent_trace_callback,
+ samba_tevent_trace_state);
+
+ /* 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.
+ */
+ task = new_task_fn(ev2, lp_ctx, cluster_id(pid, 0), private_data, NULL);
+ if (task == NULL) {
+ TALLOC_FREE(ev);
+ TALLOC_FREE(ev2);
+ exit(127);
+ }
+
+ /*
+ * Register an irpc name that can be used by the samba-tool processes
+ * command
+ */
+ {
+ struct talloc_ctx *ctx = talloc_new(NULL);
+ char *name = NULL;
+ if (ctx == NULL) {
+ DBG_ERR("Out of memory\n");
+ exit(127);
+ }
+ name = talloc_asprintf(ctx, "prefork-master-%s", service_name);
+ irpc_add_name(task->msg_ctx, name);
+ TALLOC_FREE(ctx);
+ }
+
+ {
+ 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);
+
+ /*
+ * the prefork master creates its own control pipe, so the prefork
+ * workers can detect if the master exits (in which case an EOF gets
+ * written). (Whereas from_parent_fd is the control pipe from the
+ * top-level process that the prefork master listens on)
+ */
+ {
+ int ret;
+ ret = pipe(control_pipe);
+ if (ret != 0) {
+ smb_panic("Unable to create worker control pipe\n");
+ }
+ smb_set_close_on_exec(control_pipe[0]);
+ smb_set_close_on_exec(control_pipe[1]);
+ }
+
+ /*
+ * Note, we call this before the first
+ * prefork_fork_worker() in order to have
+ * a stable order of:
+ * task_init(master) -> before_loop(master)
+ * -> post_fork(worker) -> before_loop(worker)
+ *
+ * Otherwise we would have different behaviors
+ * between the first prefork_fork_worker() loop
+ * and restarting of died workers
+ */
+ if (task != NULL && service_details->before_loop != NULL) {
+ struct task_server *task_copy = NULL;
+
+ /*
+ * We need to use ev as parent in order to
+ * keep everything alive during the loop
+ */
+ task_copy = talloc(ev, struct task_server);
+ if (task_copy == NULL) {
+ TALLOC_FREE(ev);
+ TALLOC_FREE(ev2);
+ exit(127);
+ }
+ *task_copy = *task;
+
+ /*
+ * In order to allow the before_loop() hook
+ * to register messages or event handlers,
+ * we need to fix up task->event_ctx
+ * and create a new task->msg_ctx
+ */
+ task_copy->event_ctx = ev;
+ task_copy->msg_ctx = imessaging_init(task_copy,
+ task_copy->lp_ctx,
+ task_copy->server_id,
+ task_copy->event_ctx);
+ if (task_copy->msg_ctx == NULL) {
+ TALLOC_FREE(ev);
+ TALLOC_FREE(ev2);
+ exit(127);
+ }
+ service_details->before_loop(task_copy);
+ }
+
+ /*
+ * We are now free to spawn some worker processes
+ */
+ for (i=0; i < num_children; i++) {
+ prefork_fork_worker(task,
+ ev,
+ ev2,
+ lp_ctx,
+ service_details,
+ service_name,
+ control_pipe,
+ 0,
+ &pd);
+ pd.instances++;
+ }
+
+ /*
+ * Make sure the messaging context
+ * used by the workers is no longer
+ * active on ev2, otherwise we
+ * would have memory leaks, because
+ * we queue incoming messages
+ * and never process them via ev2.
+ */
+ imessaging_dgm_unref_ev(ev2);
+
+ /* Don't listen on the sockets we just gave to the children */
+ tevent_loop_wait(ev);
+ imessaging_dgm_unref_ev(ev);
+ TALLOC_FREE(ev);
+ /* We need to keep ev2 until we're finished for the messaging to work */
+ TALLOC_FREE(ev2);
+ exit(0);
+}
+
+static void prefork_restart_fn(struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval tv,
+ void *private_data);
+
+/*
+ * Restarts a child process if it exits unexpectedly
+ */
+static bool prefork_restart(struct tevent_context *ev,
+ struct restart_context *rc)
+{
+ struct tevent_timer *te = NULL;
+
+ if (rc->restart_delay > 0) {
+ DBG_ERR("Restarting [%s] pre-fork %s in (%d) seconds\n",
+ rc->service_name,
+ (rc->master == NULL) ? "worker" : "master",
+ rc->restart_delay);
+ }
+
+ /*
+ * Always use an async timer event. If
+ * rc->restart_delay is zero this is the
+ * same as an immediate event and will be
+ * called immediately we go back into the
+ * event loop.
+ */
+ te = tevent_add_timer(ev,
+ ev,
+ tevent_timeval_current_ofs(rc->restart_delay, 0),
+ prefork_restart_fn,
+ rc);
+ if (te == NULL) {
+ DBG_ERR("tevent_add_timer fail [%s] pre-fork event %s\n",
+ rc->service_name,
+ (rc->master == NULL) ? "worker" : "master");
+ /* Caller needs to free rc. */
+ return false;
+ }
+ /* Caller must not free rc - it's in use. */
+ return true;
+}
+
+static void prefork_restart_fn(struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval tv,
+ void *private_data)
+{
+ unsigned max_backoff = 0;
+ unsigned backoff = 0;
+ unsigned default_value = 0;
+ struct restart_context *rc = talloc_get_type(private_data,
+ struct restart_context);
+ unsigned restart_delay = rc->restart_delay;
+
+ TALLOC_FREE(te);
+
+ /*
+ * If the child process is constantly exiting, then restarting it can
+ * consume a lot of resources. In which case, we want to backoff a bit
+ * before respawning it
+ */
+ default_value = lpcfg_prefork_backoff_increment(rc->lp_ctx);
+ backoff = lpcfg_parm_int(rc->lp_ctx,
+ NULL,
+ "prefork backoff increment",
+ rc->service_name,
+ default_value);
+
+ default_value = lpcfg_prefork_maximum_backoff(rc->lp_ctx);
+ max_backoff = lpcfg_parm_int(rc->lp_ctx,
+ NULL,
+ "prefork maximum backoff",
+ rc->service_name,
+ default_value);
+
+ restart_delay += backoff;
+ restart_delay = min(restart_delay, max_backoff);
+
+ if (rc->master != NULL) {
+ DBG_ERR("Restarting [%s] pre-fork master\n", rc->service_name);
+ prefork_fork_master(ev,
+ rc->lp_ctx,
+ rc->service_name,
+ rc->master->new_task_fn,
+ rc->master->private_data,
+ rc->service_details,
+ restart_delay,
+ rc->from_parent_fd);
+ } else if (rc->worker != NULL) {
+ struct process_details pd = initial_process_details;
+ DBG_ERR("Restarting [%s] pre-fork worker(%d)\n",
+ rc->service_name,
+ rc->worker->instance);
+ pd.instances = rc->worker->instance;
+ prefork_fork_worker(rc->worker->task,
+ ev,
+ rc->worker->ev2,
+ rc->lp_ctx,
+ rc->service_details,
+ rc->service_name,
+ rc->worker->control_pipe,
+ restart_delay,
+ &pd);
+ }
+ /* tfork allocates tfork structures with malloc */
+ tfork_destroy(&rc->t);
+ free(rc->t);
+ TALLOC_FREE(rc);
+}
+
+/*
+ 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 restart_context *rc = NULL;
+ int status = 0;
+ pid_t pid = 0;
+ bool rc_inuse = false;
+
+ /* free the fde which removes the event and stops it firing again */
+ TALLOC_FREE(fde);
+
+ /* the child has closed the pipe, assume its dead */
+
+ rc = talloc_get_type_abort(private_data, struct restart_context);
+ pid = tfork_child_pid(rc->t);
+ errno = 0;
+
+ irpc_cleanup(rc->lp_ctx, ev, pid);
+ status = tfork_status(&rc->t, false);
+ if (status == -1) {
+ DBG_ERR("Parent %d, Child %d terminated, "
+ "unable to get status code from tfork\n",
+ getpid(), pid);
+ rc_inuse = prefork_restart(ev, rc);
+ } else if (WIFEXITED(status)) {
+ status = WEXITSTATUS(status);
+ DBG_ERR("Parent %d, Child %d exited with status %d\n",
+ getpid(), pid, status);
+ if (status != 0) {
+ rc_inuse = prefork_restart(ev, rc);
+ }
+ } else if (WIFSIGNALED(status)) {
+ status = WTERMSIG(status);
+ DBG_ERR("Parent %d, Child %d terminated with signal %d\n",
+ getpid(), pid, status);
+ if (status == SIGABRT || status == SIGBUS || status == SIGFPE ||
+ status == SIGILL || status == SIGSYS || status == SIGSEGV ||
+ status == SIGKILL) {
+
+ rc_inuse = prefork_restart(ev, rc);
+ }
+ }
+ if (!rc_inuse) {
+ /* tfork allocates tfork structures with malloc */
+ tfork_destroy(&rc->t);
+ free(rc->t);
+ TALLOC_FREE(rc);
+ }
+ 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,
+ struct loadparm_context *lp_ctx,
+ 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, lp_ctx);
+ 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 by the prefork master to create a new prefork worker process
+ */
+static void prefork_fork_worker(struct task_server *task,
+ struct tevent_context *ev,
+ struct tevent_context *ev2,
+ struct loadparm_context *lp_ctx,
+ const struct service_details *service_details,
+ const char *service_name,
+ int control_pipe[2],
+ unsigned restart_delay,
+ struct process_details *pd)
+{
+ struct tfork *w = NULL;
+ pid_t pid;
+
+ w = tfork_create();
+ if (w == 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);
+ struct restart_context *rc = NULL;
+
+ /*
+ * we're the parent (prefork master), so store enough info to
+ * restart the worker/child if it exits unexpectedly
+ */
+ rc = talloc_zero(ev, struct restart_context);
+ if (rc == NULL) {
+ smb_panic("OOM allocating restart context\n");
+ }
+ rc->t = w;
+ rc->lp_ctx = lp_ctx;
+ rc->service_name = service_name;
+ rc->service_details = service_details;
+ rc->restart_delay = restart_delay;
+ rc->master = NULL;
+ rc->worker = talloc_zero(rc, struct worker_restart_context);
+ if (rc->worker == NULL) {
+ smb_panic("OOM allocating master restart context\n");
+ }
+ rc->worker->ev2 = ev2;
+ rc->worker->instance = pd->instances;
+ rc->worker->task = task;
+ rc->worker->control_pipe[0] = control_pipe[0];
+ rc->worker->control_pipe[1] = control_pipe[1];
+
+ fde = tevent_add_fd(
+ ev, ev, fd, TEVENT_FD_READ, prefork_child_pipe_handler, rc);
+ if (fde == NULL) {
+ smb_panic("Failed to add child pipe handler, "
+ "after fork");
+ }
+ tevent_fd_set_auto_close(fde);
+ } else {
+
+ /*
+ * we're the child (prefork-worker). We never write to the
+ * control pipe, but listen on the read end in case our parent
+ * (the pre-fork master) exits
+ */
+ close(control_pipe[1]);
+ setup_handlers(ev2, lp_ctx, control_pipe[0]);
+
+ /*
+ * tfork uses malloc
+ */
+ free(w);
+
+ imessaging_dgm_unref_ev(ev);
+ TALLOC_FREE(ev);
+
+ process_set_title("%s(%d)",
+ "task[%s] pre-forked worker(%d)",
+ service_name,
+ pd->instances);
+
+ prefork_reload_after_fork();
+ if (service_details->post_fork != NULL) {
+ service_details->post_fork(task, pd);
+ }
+ {
+ struct talloc_ctx *ctx = talloc_new(NULL);
+ char *name = NULL;
+ if (ctx == NULL) {
+ smb_panic("OOM allocating talloc context\n");
+ }
+ name = talloc_asprintf(ctx,
+ "prefork-worker-%s-%d",
+ service_name,
+ pd->instances);
+ irpc_add_name(task->msg_ctx, name);
+ TALLOC_FREE(ctx);
+ }
+ if (service_details->before_loop != NULL) {
+ service_details->before_loop(task);
+ }
+ tevent_loop_wait(ev2);
+ imessaging_dgm_unref_ev(ev2);
+ talloc_free(ev2);
+ exit(0);
+ }
+}
+/*
+ * 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,
+ struct task_server *(*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)
+{
+ prefork_fork_master(ev,
+ lp_ctx,
+ service_name,
+ new_task_fn,
+ private_data,
+ service_details,
+ 0,
+ from_parent_fd);
+
+}
+
+/*
+ * called when a task terminates
+ */
+static void prefork_terminate_task(struct tevent_context *ev,
+ struct loadparm_context *lp_ctx,
+ const char *reason,
+ bool fatal,
+ void *process_context)
+{
+ DBG_DEBUG("called with reason[%s]\n", reason);
+ TALLOC_FREE(ev);
+ if (fatal == true) {
+ exit(127);
+ } else {
+ exit(0);
+ }
+}
+
+/*
+ * called when a connection completes
+ */
+static void prefork_terminate_connection(struct tevent_context *ev,
+ struct loadparm_context *lp_ctx,
+ const char *reason,
+ void *process_context)
+{
+}
+
+/* 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_task = prefork_terminate_task,
+ .terminate_connection = prefork_terminate_connection,
+ .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/samba/process_single.c b/source4/samba/process_single.c
new file mode 100644
index 0000000..df817e2
--- /dev/null
+++ b/source4/samba/process_single.c
@@ -0,0 +1,169 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ process model: process (1 process handles all client connections)
+
+ Copyright (C) Andrew Tridgell 2003
+ Copyright (C) James J Myers 2003 <myersjj@samba.org>
+ Copyright (C) Stefan (metze) Metzmacher 2004
+
+ 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 "samba/process_model.h"
+#include "system/filesys.h"
+#include "cluster/cluster.h"
+
+NTSTATUS process_model_single_init(TALLOC_CTX *);
+
+/*
+ called when the process model is selected
+*/
+static void single_model_init(void)
+{
+}
+
+/*
+ called when a listening socket becomes readable.
+*/
+static void single_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)) {
+ 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
+ an incoming connection.
+
+ We need to throttle things until the system clears
+ enough resources to handle this new socket.
+
+ If we don't then we will spin filling the log and
+ causing more problems. We don't panic as this is
+ probably a temporary resource constraint */
+ sleep(1);
+ return;
+ }
+
+ talloc_steal(private_data, connected_socket);
+
+ /*
+ * We use the PID so we cannot collide in with cluster ids
+ * generated in other single mode tasks, and, and won't
+ * collide with PIDs from process model standard because the
+ * 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,
+ process_context);
+}
+
+/*
+ called to startup a new task
+*/
+static void single_new_task(struct tevent_context *ev,
+ struct loadparm_context *lp_ctx,
+ const char *service_name,
+ struct task_server *(*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();
+ /* start our taskids at MAX_INT32, the first 2^31 tasks are is reserved for fd numbers */
+ static uint32_t taskid = INT32_MAX;
+ struct task_server *task = NULL;
+ /*
+ * We use the PID so we cannot collide in with cluster ids
+ * generated in other single mode tasks, and, and won't
+ * collide with PIDs from process model standard because the
+ * combination of pid/task_id should be unique system-wide
+ *
+ * Using the pid unaltered makes debugging of which process
+ * owns the messaging socket easier.
+ */
+ task = new_task(ev, lp_ctx, cluster_id(pid, taskid++), private_data, NULL);
+ if (task != NULL && service_details->post_fork != NULL) {
+ struct process_details pd = initial_process_details;
+ service_details->post_fork(task, &pd);
+ }
+ if (task != NULL && service_details->before_loop != NULL) {
+ service_details->before_loop(task);
+ }
+}
+
+/*
+ * Called when a task goes down
+ */
+static void single_terminate_task(struct tevent_context *ev,
+ struct loadparm_context *lp_ctx,
+ const char *reason,
+ bool fatal,
+ void *process_context)
+{
+ DBG_NOTICE("single_terminate: reason[%s]\n",reason);
+}
+
+/*
+ * Called when a connection has ended
+ */
+static void single_terminate_connection(struct tevent_context *ev,
+ struct loadparm_context *lp_ctx,
+ const char *reason,
+ void *process_context)
+{
+}
+
+/* called to set a title of a task or connection */
+static void single_set_title(struct tevent_context *ev, const char *title)
+{
+}
+
+const struct model_ops single_ops = {
+ .name = "single",
+ .model_init = single_model_init,
+ .new_task = single_new_task,
+ .accept_connection = single_accept_connection,
+ .terminate_task = single_terminate_task,
+ .terminate_connection = single_terminate_connection,
+ .set_title = single_set_title,
+};
+
+/*
+ initialise the single process model, registering ourselves with the
+ process model subsystem
+ */
+NTSTATUS process_model_single_init(TALLOC_CTX *ctx)
+{
+ return register_process_model(&single_ops);
+}
diff --git a/source4/samba/process_standard.c b/source4/samba/process_standard.c
new file mode 100644
index 0000000..fba24ff
--- /dev/null
+++ b/source4/samba/process_standard.c
@@ -0,0 +1,626 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ process model: standard (1 process per client connection)
+
+ Copyright (C) Andrew Tridgell 1992-2005
+ Copyright (C) James J Myers 2003 <myersjj@samba.org>
+ Copyright (C) Stefan (metze) Metzmacher 2004
+
+ 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 "samba/process_model.h"
+#include "system/filesys.h"
+#include "cluster/cluster.h"
+#include "param/param.h"
+#include "ldb_wrap.h"
+#include "lib/messaging/messaging.h"
+#include "lib/util/debug.h"
+#include "lib/messaging/messages_dgm.h"
+#include "lib/util/util_process.h"
+
+static unsigned connections_active = 0;
+static unsigned smbd_max_processes = 0;
+
+struct standard_child_state {
+ const char *name;
+ pid_t pid;
+ int to_parent_fd;
+ int from_child_fd;
+ struct tevent_fd *from_child_fde;
+};
+
+NTSTATUS process_model_standard_init(TALLOC_CTX *);
+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
+*/
+static void standard_model_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)
+{
+#ifdef HAVE_GETPGRP
+ if (getpgrp() == getpid()) {
+ /*
+ * We're the process group leader, send
+ * SIGTERM to our process group.
+ */
+ DBG_ERR("SIGTERM: killing children\n");
+ kill(-getpgrp(), SIGTERM);
+ }
+#endif
+ DBG_ERR("Exiting pid %u on SIGTERM\n", (unsigned int)getpid());
+ talloc_free(ev);
+ exit(127);
+}
+
+/*
+ handle EOF on the parent-to-all-children pipe in the child
+*/
+static void standard_pipe_handler(struct tevent_context *event_ctx, struct tevent_fd *fde,
+ uint16_t flags, void *private_data)
+{
+ DBG_DEBUG("Child %d exiting\n", (int)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 standard_child_pipe_handler(struct tevent_context *ev,
+ struct tevent_fd *fde,
+ uint16_t flags,
+ void *private_data)
+{
+ struct standard_child_state *state
+ = talloc_get_type_abort(private_data, struct standard_child_state);
+ int status = 0;
+ pid_t pid;
+
+ messaging_dgm_cleanup(state->pid);
+
+ /* the child has closed the pipe, assume its dead */
+ errno = 0;
+ pid = waitpid(state->pid, &status, 0);
+
+ if (pid != state->pid) {
+ if (errno == ECHILD) {
+ /*
+ * this happens when the
+ * parent has set SIGCHLD to
+ * SIG_IGN. In that case we
+ * can only get error
+ * information for the child
+ * via its logging. We should
+ * stop using SIG_IGN on
+ * SIGCHLD in the standard
+ * process model.
+ */
+ 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;
+ }
+ DBG_ERR("Error in waitpid() for child %d (%s) - %s \n",
+ (int)state->pid, state->name, strerror(errno));
+ if (errno == 0) {
+ errno = ECHILD;
+ }
+ goto done;
+ }
+ if (WIFEXITED(status)) {
+ status = WEXITSTATUS(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);
+ DBG_ERR("Child %d (%s) terminated with signal %d\n",
+ (int)state->pid, state->name, status);
+ }
+done:
+ TALLOC_FREE(state);
+ if (smbd_max_processes > 0) {
+ if (connections_active < 1) {
+ DBG_ERR("Number of active connections "
+ "less than 1 (%d)\n",
+ connections_active);
+ connections_active = 1;
+ }
+ connections_active--;
+ }
+ return;
+}
+
+static struct standard_child_state *setup_standard_child_pipe(struct tevent_context *ev,
+ const char *name)
+{
+ struct standard_child_state *state;
+ int parent_child_pipe[2];
+ int ret;
+
+ /*
+ * Prepare a pipe to allow us to know when the child exits,
+ * because it will trigger a read event on this private
+ * pipe.
+ *
+ * We do all this before the accept and fork(), so we can
+ * clean up if it fails.
+ */
+ state = talloc_zero(ev, struct standard_child_state);
+ if (state == NULL) {
+ return NULL;
+ }
+
+ if (name == NULL) {
+ name = "";
+ }
+
+ state->name = talloc_strdup(state, name);
+ if (state->name == NULL) {
+ TALLOC_FREE(state);
+ return NULL;
+ }
+
+ ret = pipe(parent_child_pipe);
+ if (ret == -1) {
+ DBG_ERR("Failed to create parent-child pipe to handle "
+ "SIGCHLD to track new process for socket\n");
+ TALLOC_FREE(state);
+ return NULL;
+ }
+
+ smb_set_close_on_exec(parent_child_pipe[0]);
+ smb_set_close_on_exec(parent_child_pipe[1]);
+
+ state->from_child_fd = parent_child_pipe[0];
+ state->to_parent_fd = parent_child_pipe[1];
+
+ /*
+ * The basic purpose of calling this handler is to ensure we
+ * call waitpid() and so avoid zombies (now that we no longer
+ * user SIGIGN on for SIGCHLD), but it also allows us to clean
+ * up other resources in the future.
+ */
+ state->from_child_fde = tevent_add_fd(ev, state,
+ state->from_child_fd,
+ TEVENT_FD_READ,
+ standard_child_pipe_handler,
+ state);
+ if (state->from_child_fde == NULL) {
+ TALLOC_FREE(state);
+ return NULL;
+ }
+ tevent_fd_set_auto_close(state->from_child_fde);
+
+ return state;
+}
+
+/*
+ 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 *),
+ void *private_data,
+ void *process_context)
+{
+ NTSTATUS status;
+ struct socket_context *sock2;
+ pid_t pid;
+ struct socket_address *c, *s;
+ struct standard_child_state *state;
+ struct tevent_fd *fde = NULL;
+ struct tevent_signal *se = NULL;
+ struct process_context *proc_ctx = NULL;
+
+
+ /* accept an incoming connection. */
+ status = socket_accept(sock, &sock2);
+ if (!NT_STATUS_IS_OK(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
+ */
+ sleep(1);
+ return;
+ }
+
+ proc_ctx = talloc_get_type_abort(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;
+ }
+
+ if (smbd_max_processes > 0) {
+ if (connections_active >= smbd_max_processes) {
+ DBG_ERR("(%d) connections already active, "
+ "maximum is (%d). Dropping request\n",
+ connections_active,
+ smbd_max_processes);
+ /*
+ * Drop the connection as we're overloaded at the moment
+ */
+ talloc_free(sock2);
+ return;
+ }
+ connections_active++;
+ }
+
+ state = setup_standard_child_pipe(ev, NULL);
+ if (state == NULL) {
+ return;
+ }
+ pid = fork();
+
+ if (pid != 0) {
+ close(state->to_parent_fd);
+ state->to_parent_fd = -1;
+
+ if (pid > 0) {
+ state->pid = pid;
+ } else {
+ TALLOC_FREE(state);
+ }
+
+ /* parent or error code ... */
+ talloc_free(sock2);
+ /* go back to the event loop */
+ return;
+ }
+
+ /* 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();
+
+ process_set_title("%s[work]", "task[%s] standard worker", proc_ctx->name);
+
+ /* This is now the child code. We need a completely new event_context to work with */
+
+ if (tevent_re_initialise(ev) != 0) {
+ smb_panic("Failed to re-initialise tevent after fork");
+ }
+
+ /* this will free all the listening sockets and all state that
+ is not associated with this new connection */
+ talloc_free(sock);
+
+ /* we don't care if the dup fails, as its only a select()
+ speed optimisation */
+ socket_dup(sock2);
+
+ /* tdb needs special fork handling */
+ 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");
+ }
+
+ 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");
+ }
+
+ 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");
+ }
+
+ /* setup the process title */
+ c = socket_get_peer_addr(sock2, ev);
+ s = socket_get_my_addr(sock2, ev);
+ if (s && c) {
+ setproctitle("conn c[%s:%u] s[%s:%u] server_id[%d]",
+ c->addr, c->port, s->addr, s->port, (int)pid);
+ }
+ talloc_free(c);
+ talloc_free(s);
+
+ force_check_log_size();
+
+ /* 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,
+ 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
+ more to process */
+ tevent_loop_wait(ev);
+
+ talloc_free(ev);
+ exit(0);
+}
+
+/*
+ called to create a new server task
+*/
+static void standard_new_task(struct tevent_context *ev,
+ struct loadparm_context *lp_ctx,
+ const char *service_name,
+ struct task_server *(*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 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;
+ struct task_server* task = NULL;
+
+ state = setup_standard_child_pipe(ev, service_name);
+ if (state == NULL) {
+ return;
+ }
+
+ pid = fork();
+
+ if (pid != 0) {
+ close(state->to_parent_fd);
+ state->to_parent_fd = -1;
+
+ if (pid > 0) {
+ state->pid = pid;
+ } else {
+ TALLOC_FREE(state);
+ }
+
+ /* parent or error code ... go back to the event loop */
+ return;
+ }
+
+ /* this leaves state->to_parent_fd open */
+ TALLOC_FREE(state);
+
+ pid = getpid();
+
+ /* 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");
+ }
+
+ /* ldb/tdb need special fork handling */
+ 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");
+ }
+
+ fde = tevent_add_fd(ev, ev, from_parent_fd, TEVENT_FD_READ,
+ standard_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");
+ }
+
+ process_set_title("%s[task]", "task[%s]", service_name);
+
+ force_check_log_size();
+
+ /*
+ * 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;
+
+ smbd_max_processes = lpcfg_max_smbd_processes(lp_ctx);
+
+ /* setup this new task. Cluster ID is PID based for this process model */
+ task = new_task(ev, lp_ctx, cluster_id(pid, 0), private_data, proc_ctx);
+ /*
+ * Currently we don't support the post_fork functionality in the
+ * standard model, i.e. it is only called here not after a new process
+ * is forked in standard_accept_connection.
+ */
+ if (task != NULL && service_details->post_fork != NULL) {
+ struct process_details pd = initial_process_details;
+ service_details->post_fork(task, &pd);
+ }
+
+ if (task != NULL && service_details->before_loop != NULL) {
+ service_details->before_loop(task);
+ }
+
+ /* 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 */
+ tevent_loop_wait(ev);
+
+ talloc_free(ev);
+ exit(0);
+}
+
+
+/* called when a task goes down */
+static void standard_terminate_task(struct tevent_context *ev,
+ struct loadparm_context *lp_ctx,
+ const char *reason,
+ bool fatal,
+ void *process_context)
+{
+ if (fatal == true) {
+ exit(127);
+ }
+ exit(0);
+}
+
+/* called when a connection terminates*/
+static void standard_terminate_connection(struct tevent_context *ev,
+ struct loadparm_context *lp_ctx,
+ const char *reason,
+ void *process_context)
+{
+ struct process_context *proc_ctx = NULL;
+
+ DBG_DEBUG("connection 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 */
+ reload_charcnv(lp_ctx);
+
+ /* Always free event context last before exit. */
+ talloc_free(ev);
+
+ /* terminate this process */
+ exit(0);
+}
+/* called to set a title of a task or connection */
+static void standard_set_title(struct tevent_context *ev, const char *title)
+{
+ if (title) {
+ setproctitle("%s", title);
+ } else {
+ setproctitle(NULL);
+ }
+}
+
+static const struct model_ops standard_ops = {
+ .name = "standard",
+ .model_init = standard_model_init,
+ .accept_connection = standard_accept_connection,
+ .new_task = standard_new_task,
+ .terminate_task = standard_terminate_task,
+ .terminate_connection = standard_terminate_connection,
+ .set_title = standard_set_title,
+};
+
+/*
+ initialise the standard process model, registering ourselves with the process model subsystem
+ */
+NTSTATUS process_model_standard_init(TALLOC_CTX *ctx)
+{
+ return register_process_model(&standard_ops);
+}
diff --git a/source4/samba/server.c b/source4/samba/server.c
new file mode 100644
index 0000000..011d9d0
--- /dev/null
+++ b/source4/samba/server.c
@@ -0,0 +1,1000 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Main SMB server routines
+
+ Copyright (C) Andrew Tridgell 1992-2005
+ Copyright (C) Martin Pool 2002
+ Copyright (C) Jelmer Vernooij 2002
+ Copyright (C) James J Myers 2003 <myersjj@samba.org>
+
+ 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 "version.h"
+#include "lib/cmdline/cmdline.h"
+#include "system/dir.h"
+#include "system/filesys.h"
+#include "auth/gensec/gensec.h"
+#include "libcli/auth/schannel.h"
+#include "samba/process_model.h"
+#include "param/secrets.h"
+#include "lib/util/pidfile.h"
+#include "param/param.h"
+#include "dsdb/samdb/samdb.h"
+#include "auth/session.h"
+#include "lib/messaging/irpc.h"
+#include "librpc/gen_ndr/ndr_irpc.h"
+#include "cluster/cluster.h"
+#include "dynconfig/dynconfig.h"
+#include "nsswitch/winbind_client.h"
+#include "libds/common/roles.h"
+#include "lib/util/tfork.h"
+#include "dsdb/samdb/ldb_modules/util.h"
+#include "lib/util/server_id.h"
+#include "server_util.h"
+
+#ifdef HAVE_PTHREAD
+#include <pthread.h>
+#endif
+
+struct server_state {
+ struct tevent_context *event_ctx;
+ const char *binary_name;
+};
+
+/*
+ recursively delete a directory tree
+*/
+static void recursive_delete(const char *path)
+{
+ DIR *dir;
+ struct dirent *de;
+
+ dir = opendir(path);
+ if (!dir) {
+ return;
+ }
+
+ for (de=readdir(dir);de;de=readdir(dir)) {
+ char *fname;
+ struct stat st;
+
+ if (ISDOT(de->d_name) || ISDOTDOT(de->d_name)) {
+ continue;
+ }
+
+ fname = talloc_asprintf(path, "%s/%s", path, de->d_name);
+ if (stat(fname, &st) != 0) {
+ continue;
+ }
+ if (S_ISDIR(st.st_mode)) {
+ recursive_delete(fname);
+ talloc_free(fname);
+ continue;
+ }
+ if (unlink(fname) != 0) {
+ DBG_ERR("Unabled to delete '%s' - %s\n",
+ fname, strerror(errno));
+ smb_panic("unable to cleanup tmp files");
+ }
+ talloc_free(fname);
+ }
+ closedir(dir);
+}
+
+/*
+ cleanup temporary files. This is the new alternative to
+ TDB_CLEAR_IF_FIRST. Unfortunately TDB_CLEAR_IF_FIRST is not
+ efficient on unix systems due to the lack of scaling of the byte
+ range locking system. So instead of putting the burden on tdb to
+ cleanup tmp files, this function deletes them.
+*/
+static void cleanup_tmp_files(struct loadparm_context *lp_ctx)
+{
+ char *path;
+ TALLOC_CTX *mem_ctx = talloc_new(NULL);
+ if (mem_ctx == NULL) {
+ exit_daemon("Failed to create memory context",
+ ENOMEM);
+ }
+
+ path = smbd_tmp_path(mem_ctx, lp_ctx, NULL);
+ if (path == NULL) {
+ exit_daemon("Failed to cleanup temporary files",
+ EINVAL);
+ }
+
+ recursive_delete(path);
+ talloc_free(mem_ctx);
+}
+
+static void sig_hup(int sig)
+{
+ debug_schedule_reopen_logs();
+}
+
+static void sig_term(int sig)
+{
+#ifdef HAVE_GETPGRP
+ if (getpgrp() == getpid()) {
+ /*
+ * We're the process group leader, send
+ * SIGTERM to our process group.
+ */
+ kill(-getpgrp(), SIGTERM);
+ }
+#endif
+ _exit(127);
+}
+
+static void sigterm_signal_handler(struct tevent_context *ev,
+ struct tevent_signal *se,
+ int signum, int count, void *siginfo,
+ void *private_data)
+{
+ struct server_state *state = talloc_get_type_abort(
+ private_data, struct server_state);
+
+ DBG_DEBUG("Process %s got SIGTERM\n", state->binary_name);
+ TALLOC_FREE(state);
+ sig_term(SIGTERM);
+}
+
+static void sighup_signal_handler(struct tevent_context *ev,
+ struct tevent_signal *se,
+ int signum, int count, void *siginfo,
+ void *private_data)
+{
+ struct server_state *state = talloc_get_type_abort(
+ private_data, struct server_state);
+
+ DBG_DEBUG("Process %s got SIGHUP\n", state->binary_name);
+
+ reopen_logs_internal();
+}
+
+/*
+ setup signal masks
+*/
+static void setup_signals(void)
+{
+ /* we are never interested in SIGPIPE */
+ BlockSignals(true,SIGPIPE);
+
+#if defined(SIGFPE)
+ /* we are never interested in SIGFPE */
+ BlockSignals(true,SIGFPE);
+#endif
+
+ /* We are no longer interested in USR1 */
+ BlockSignals(true, SIGUSR1);
+
+#if defined(SIGUSR2)
+ /* We are no longer interested in USR2 */
+ BlockSignals(true,SIGUSR2);
+#endif
+
+ /* POSIX demands that signals are inherited. If the invoking process has
+ * these signals masked, we will have problems,
+ * as we won't receive them. */
+ BlockSignals(false, SIGHUP);
+ BlockSignals(false, SIGTERM);
+
+ CatchSignal(SIGHUP, sig_hup);
+ CatchSignal(SIGTERM, sig_term);
+}
+
+/*
+ handle io on stdin
+*/
+static void server_stdin_handler(struct tevent_context *event_ctx,
+ struct tevent_fd *fde,
+ uint16_t flags,
+ void *private_data)
+{
+ struct server_state *state = talloc_get_type_abort(
+ private_data, struct server_state);
+ uint8_t c;
+ if (read(0, &c, 1) == 0) {
+ DBG_ERR("%s: EOF on stdin - PID %d terminating\n",
+ state->binary_name, (int)getpid());
+#ifdef HAVE_GETPGRP
+ if (getpgrp() == getpid()) {
+ DBG_ERR("Sending SIGTERM from pid %d\n",
+ (int)getpid());
+ kill(-getpgrp(), SIGTERM);
+ }
+#endif
+ TALLOC_FREE(state);
+ exit(0);
+ }
+}
+
+/*
+ die if the user selected maximum runtime is exceeded
+*/
+_NORETURN_ static void max_runtime_handler(struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval t, void *private_data)
+{
+ struct server_state *state = talloc_get_type_abort(
+ private_data, struct server_state);
+ 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));
+ TALLOC_FREE(state);
+ exit(0);
+}
+
+/*
+ * When doing an in-place upgrade of Samba, the database format may have
+ * changed between versions. E.g. between 4.7 and 4.8 the DB changed from
+ * DN-based indexes to GUID-based indexes, so we have to re-index the DB after
+ * upgrading.
+ * This function handles migrating an older samba DB to a new Samba release.
+ * Note that we have to maintain DB compatibility between *all* older versions
+ * of Samba, not just the ones still under maintenance support.
+ *
+ * Finally, while the transaction is open, check if we support the set
+ * domain functional level, if the DC functional level on our object
+ * is correct and if not to update it (except on the RODC)
+ */
+static int handle_inplace_db_upgrade_check_and_update_fl(struct ldb_context *ldb_ctx,
+ struct loadparm_context *lp_ctx)
+{
+ int ret;
+
+ /*
+ * The DSDB stack will handle reindexing the DB (if needed) upon the first
+ * DB write. Open and close a transaction on the DB now to trigger a
+ * reindex if required, rather than waiting for the first write.
+ * We do this here to guarantee that the DB will have been re-indexed by
+ * the time the main samba code runs.
+ * Refer to dsdb_schema_set_indices_and_attributes() for the actual reindexing
+ * code, called from
+ * source4/dsdb/samdb/ldb_modules/schema_load.c:schema_load_start_transaction()
+ */
+ ret = ldb_transaction_start(ldb_ctx);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+
+ ret = dsdb_check_and_update_fl(ldb_ctx, lp_ctx);
+ if (ret != LDB_SUCCESS) {
+ ldb_transaction_cancel(ldb_ctx);
+ return ret;
+ }
+
+ ret = ldb_transaction_commit(ldb_ctx);
+ if (ret != LDB_SUCCESS) {
+ return ret;
+ }
+ return LDB_SUCCESS;
+}
+
+/*
+ pre-open the key databases. This saves a lot of time in child
+ processes
+ */
+static int prime_ldb_databases(struct tevent_context *event_ctx, bool *am_backup)
+{
+ struct ldb_result *res = NULL;
+ struct ldb_dn *samba_dsdb_dn = NULL;
+ struct ldb_context *ldb_ctx = NULL;
+ struct ldb_context *pdb = NULL;
+ static const char *attrs[] = { "backupDate", NULL };
+ struct loadparm_context *lp_ctx = samba_cmdline_get_lp_ctx();
+ const char *msg = NULL;
+ int ret;
+ TALLOC_CTX *db_context = talloc_new(event_ctx);
+ if (db_context == NULL) {
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ *am_backup = false;
+
+ /* note we deliberately leave these open, which allows them to be
+ * re-used in ldb_wrap_connect() */
+ ldb_ctx = samdb_connect(db_context,
+ event_ctx,
+ lp_ctx,
+ system_session(lp_ctx),
+ NULL,
+ 0);
+ if (ldb_ctx == NULL) {
+ talloc_free(db_context);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ret = handle_inplace_db_upgrade_check_and_update_fl(ldb_ctx,
+ lp_ctx);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(db_context);
+ return ret;
+ }
+
+ pdb = privilege_connect(db_context, lp_ctx);
+ if (pdb == NULL) {
+ talloc_free(db_context);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ /* check the root DB object to see if it's marked as a backup */
+ samba_dsdb_dn = ldb_dn_new(db_context, ldb_ctx, "@SAMBA_DSDB");
+ if (!samba_dsdb_dn) {
+ talloc_free(db_context);
+ return LDB_ERR_OPERATIONS_ERROR;
+ }
+
+ ret = dsdb_search_dn(ldb_ctx, db_context, &res, samba_dsdb_dn, attrs,
+ DSDB_FLAG_AS_SYSTEM);
+ if (ret != LDB_SUCCESS) {
+ talloc_free(db_context);
+ return ret;
+ }
+
+ if (res->count > 0) {
+ msg = ldb_msg_find_attr_as_string(res->msgs[0], "backupDate",
+ NULL);
+ if (msg != NULL) {
+ *am_backup = true;
+ }
+ }
+ return LDB_SUCCESS;
+}
+
+/*
+ called from 'smbcontrol samba shutdown'
+ */
+static void samba_parent_shutdown(struct imessaging_context *msg,
+ void *private_data,
+ uint32_t msg_type,
+ struct server_id src,
+ size_t num_fds,
+ int *fds,
+ DATA_BLOB *data)
+{
+ struct server_state *state =
+ talloc_get_type_abort(private_data,
+ struct server_state);
+ struct server_id_buf src_buf;
+ struct server_id dst = imessaging_get_server_id(msg);
+ struct server_id_buf dst_buf;
+
+ if (num_fds != 0) {
+ DBG_WARNING("Received %zu fds, ignoring message\n", num_fds);
+ return;
+ }
+
+ DBG_ERR("samba_shutdown of %s %s: from %s\n",
+ state->binary_name,
+ server_id_str_buf(dst, &dst_buf),
+ server_id_str_buf(src, &src_buf));
+
+ TALLOC_FREE(state);
+ exit(0);
+}
+
+/*
+ called when a fatal condition occurs in a child task
+ */
+static NTSTATUS samba_terminate(struct irpc_message *msg,
+ struct samba_terminate *r)
+{
+ struct server_state *state = talloc_get_type(msg->private_data,
+ struct server_state);
+ DBG_ERR("samba_terminate of %s %d: %s\n",
+ state->binary_name, (int)getpid(), r->in.reason);
+ TALLOC_FREE(state);
+ exit(1);
+}
+
+/*
+ setup messaging for the top level samba (parent) task
+ */
+static NTSTATUS setup_parent_messaging(struct server_state *state,
+ struct loadparm_context *lp_ctx)
+{
+ struct imessaging_context *msg;
+ NTSTATUS status;
+ if (state == NULL) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+ msg = imessaging_init(state->event_ctx,
+ lp_ctx,
+ cluster_id(getpid(), SAMBA_PARENT_TASKID),
+ state->event_ctx);
+ NT_STATUS_HAVE_NO_MEMORY(msg);
+
+ status = irpc_add_name(msg, "samba");
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = imessaging_register(msg, state, MSG_SHUTDOWN,
+ samba_parent_shutdown);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = IRPC_REGISTER(msg, irpc, SAMBA_TERMINATE,
+ samba_terminate, state);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ show build info
+ */
+static void show_build(void)
+{
+#define CONFIG_OPTION(n) { #n, dyn_ ## n }
+ struct {
+ const char *name;
+ const char *value;
+ } config_options[] = {
+ CONFIG_OPTION(BINDIR),
+ CONFIG_OPTION(SBINDIR),
+ CONFIG_OPTION(CONFIGFILE),
+ CONFIG_OPTION(NCALRPCDIR),
+ CONFIG_OPTION(LOGFILEBASE),
+ CONFIG_OPTION(LMHOSTSFILE),
+ CONFIG_OPTION(DATADIR),
+ CONFIG_OPTION(MODULESDIR),
+ CONFIG_OPTION(LOCKDIR),
+ CONFIG_OPTION(STATEDIR),
+ CONFIG_OPTION(CACHEDIR),
+ CONFIG_OPTION(PIDDIR),
+ CONFIG_OPTION(PRIVATE_DIR),
+ CONFIG_OPTION(CODEPAGEDIR),
+ CONFIG_OPTION(SETUPDIR),
+ CONFIG_OPTION(WINBINDD_SOCKET_DIR),
+ CONFIG_OPTION(NTP_SIGND_SOCKET_DIR),
+ { NULL, NULL}
+ };
+ int i;
+
+ printf("Samba version: %s\n", SAMBA_VERSION_STRING);
+ printf("Build environment:\n");
+
+ printf("Paths:\n");
+ for (i=0; config_options[i].name; i++) {
+ printf(" %s: %s\n",
+ config_options[i].name,
+ config_options[i].value);
+ }
+
+ exit(0);
+}
+
+static int event_ctx_destructor(struct tevent_context *event_ctx)
+{
+ imessaging_dgm_unref_ev(event_ctx);
+ return 0;
+}
+
+#ifdef HAVE_PTHREAD
+static int to_children_fd = -1;
+static void atfork_prepare(void) {
+}
+static void atfork_parent(void) {
+}
+static void atfork_child(void) {
+ if (to_children_fd != -1) {
+ close(to_children_fd);
+ to_children_fd = -1;
+ }
+}
+#endif
+
+/*
+ main server.
+*/
+static int binary_smbd_main(TALLOC_CTX *mem_ctx,
+ const char *binary_name,
+ int argc,
+ const char *argv[])
+{
+ struct samba_cmdline_daemon_cfg *cmdline_daemon_cfg = NULL;
+ bool db_is_backup = false;
+ int opt;
+ int ret;
+ poptContext pc;
+ uint16_t stdin_event_flags;
+ NTSTATUS status;
+ const char *model = "prefork";
+ int max_runtime = 0;
+ struct stat st;
+ enum {
+ OPT_PROCESS_MODEL = 1000,
+ OPT_SHOW_BUILD,
+ };
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+ {
+ .longName = "model",
+ .shortName = 'M',
+ .argInfo = POPT_ARG_STRING,
+ .val = OPT_PROCESS_MODEL,
+ .descrip = "Select process model",
+ .argDescrip = "MODEL",
+ },
+ {
+ .longName = "maximum-runtime",
+ .argInfo = POPT_ARG_INT,
+ .arg = &max_runtime,
+ .descrip = "set maximum runtime of the server process, "
+ "till autotermination",
+ .argDescrip = "seconds"
+ },
+ {
+ .longName = "show-build",
+ .shortName = 'b',
+ .argInfo = POPT_ARG_NONE,
+ .val = OPT_SHOW_BUILD,
+ .descrip = "show build info",
+ },
+ POPT_COMMON_SAMBA
+ POPT_COMMON_DAEMON
+ POPT_COMMON_VERSION
+ POPT_TABLEEND
+ };
+ struct server_state *state = NULL;
+ struct tevent_signal *se = NULL;
+ struct samba_tevent_trace_state *samba_tevent_trace_state = NULL;
+ struct loadparm_context *lp_ctx = NULL;
+ bool ok;
+
+ setproctitle("root process");
+
+ ok = samba_cmdline_init(mem_ctx,
+ SAMBA_CMDLINE_CONFIG_SERVER,
+ true /* require_smbconf */);
+ if (!ok) {
+ DBG_ERR("Failed to init cmdline parser!\n");
+ TALLOC_FREE(mem_ctx);
+ exit(1);
+ }
+
+ cmdline_daemon_cfg = samba_cmdline_get_daemon_cfg();
+
+ pc = samba_popt_get_context(binary_name,
+ argc,
+ argv,
+ long_options,
+ 0);
+ if (pc == NULL) {
+ DBG_ERR("Failed to setup popt context!\n");
+ TALLOC_FREE(mem_ctx);
+ exit(1);
+ }
+
+ while((opt = poptGetNextOpt(pc)) != -1) {
+ switch(opt) {
+ case OPT_PROCESS_MODEL:
+ model = poptGetOptArg(pc);
+ break;
+ case OPT_SHOW_BUILD:
+ show_build();
+ break;
+ default:
+ fprintf(stderr, "\nInvalid option %s: %s\n\n",
+ poptBadOption(pc, 0), poptStrerror(opt));
+ poptPrintUsage(pc, stderr, 0);
+ return 1;
+ }
+ }
+
+ if (cmdline_daemon_cfg->daemon && cmdline_daemon_cfg->interactive) {
+ fprintf(stderr,"\nERROR: "
+ "Option -i|--interactive is "
+ "not allowed together with -D|--daemon\n\n");
+ poptPrintUsage(pc, stderr, 0);
+ return 1;
+ } else if (!cmdline_daemon_cfg->interactive &&
+ cmdline_daemon_cfg->fork) {
+ /* default is --daemon */
+ cmdline_daemon_cfg->daemon = true;
+ }
+
+ poptFreeContext(pc);
+
+ lp_ctx = samba_cmdline_get_lp_ctx();
+
+ talloc_enable_null_tracking();
+
+ setup_signals();
+
+ /* we want total control over the permissions on created files,
+ so set our umask to 0 */
+ umask(0);
+
+ DBG_STARTUP_NOTICE("%s version %s started.\n%s\n",
+ binary_name,
+ SAMBA_VERSION_STRING,
+ SAMBA_COPYRIGHT_STRING);
+
+ if (sizeof(uint16_t) < 2 ||
+ sizeof(uint32_t) < 4 ||
+ sizeof(uint64_t) < 8) {
+ DEBUG(0,("ERROR: Samba is not configured correctly "
+ "for the word size on your machine\n"));
+ DEBUGADD(0,("sizeof(uint16_t) = %u, sizeof(uint32_t) %u, "
+ "sizeof(uint64_t) = %u\n",
+ (unsigned int)sizeof(uint16_t),
+ (unsigned int)sizeof(uint32_t),
+ (unsigned int)sizeof(uint64_t)));
+ return 1;
+ }
+
+ if (cmdline_daemon_cfg->daemon) {
+ DBG_NOTICE("Becoming a daemon.\n");
+ become_daemon(cmdline_daemon_cfg->fork,
+ cmdline_daemon_cfg->no_process_group,
+ false);
+ } else if (!cmdline_daemon_cfg->interactive) {
+ daemon_status("samba", "Starting process...");
+ }
+
+ /* Create the memory context to hang everything off. */
+ state = talloc_zero(mem_ctx, struct server_state);
+ if (state == NULL) {
+ exit_daemon("Samba cannot create server state", ENOMEM);
+ /*
+ * return is never reached but is here to satisfy static
+ * checkers
+ */
+ return 1;
+ };
+ state->binary_name = binary_name;
+
+ cleanup_tmp_files(lp_ctx);
+
+ if (!directory_exist(lpcfg_lock_directory(lp_ctx))) {
+ mkdir(lpcfg_lock_directory(lp_ctx), 0755);
+ }
+
+ if (!directory_exist(lpcfg_pid_directory(lp_ctx))) {
+ mkdir(lpcfg_pid_directory(lp_ctx), 0755);
+ }
+
+ pidfile_create(lpcfg_pid_directory(lp_ctx), binary_name);
+
+ if (lpcfg_server_role(lp_ctx) == ROLE_ACTIVE_DIRECTORY_DC) {
+ if (!open_schannel_session_store(state,
+ lp_ctx)) {
+ TALLOC_FREE(state);
+ exit_daemon("Samba cannot open schannel store "
+ "for secured NETLOGON operations.", EACCES);
+ /*
+ * return is never reached but is here to satisfy static
+ * checkers
+ */
+ return 1;
+ }
+ }
+
+ /* make sure we won't go through nss_winbind */
+ if (!winbind_off()) {
+ TALLOC_FREE(state);
+ exit_daemon("Samba failed to disable recursive "
+ "winbindd calls.", EACCES);
+ /*
+ * return is never reached but is here to satisfy static
+ * checkers
+ */
+ return 1;
+ }
+
+ gensec_init(); /* FIXME: */
+
+ process_model_init(lp_ctx);
+
+ samba_service_init();
+
+ /* the event context is the top level structure in smbd. Everything else
+ should hang off that */
+ state->event_ctx = s4_event_context_init(state);
+
+ if (state->event_ctx == NULL) {
+ TALLOC_FREE(state);
+ exit_daemon("Initializing event context failed", EACCES);
+ /*
+ * return is never reached but is here to satisfy static
+ * checkers
+ */
+ return 1;
+ }
+
+ talloc_set_destructor(state->event_ctx, event_ctx_destructor);
+
+ samba_tevent_trace_state = create_samba_tevent_trace_state(state);
+ if (samba_tevent_trace_state == NULL) {
+ exit_daemon("Samba failed to setup tevent tracing state",
+ ENOTTY);
+ /*
+ * return is never reached but is here to satisfy static
+ * checkers
+ */
+ return 1;
+ }
+
+ tevent_set_trace_callback(state->event_ctx,
+ samba_tevent_trace_callback,
+ samba_tevent_trace_state);
+
+ if (cmdline_daemon_cfg->interactive) {
+ /* terminate when stdin goes away */
+ stdin_event_flags = TEVENT_FD_READ;
+ } else {
+ /* stay alive forever */
+ stdin_event_flags = 0;
+ }
+
+#ifdef HAVE_SETPGID
+ /*
+ * If we're interactive we want to set our own process group for
+ * signal management, unless --no-process-group specified.
+ */
+ if (cmdline_daemon_cfg->interactive &&
+ !cmdline_daemon_cfg->no_process_group)
+ {
+ setpgid((pid_t)0, (pid_t)0);
+ }
+#endif
+
+ /* catch EOF on stdin */
+#ifdef SIGTTIN
+ signal(SIGTTIN, SIG_IGN);
+#endif
+
+ if (fstat(0, &st) != 0) {
+ TALLOC_FREE(state);
+ exit_daemon("Samba failed to set standard input handler",
+ ENOTTY);
+ /*
+ * return is never reached but is here to satisfy static
+ * checkers
+ */
+ return 1;
+ }
+
+ if (S_ISFIFO(st.st_mode) || S_ISSOCK(st.st_mode)) {
+ struct tevent_fd *fde = tevent_add_fd(state->event_ctx,
+ state->event_ctx,
+ 0,
+ stdin_event_flags,
+ server_stdin_handler,
+ state);
+ if (fde == NULL) {
+ TALLOC_FREE(state);
+ exit_daemon("Initializing stdin failed", ENOMEM);
+ /*
+ * return is never reached but is here to
+ * satisfy static checkers
+ */
+ return 1;
+ }
+ }
+
+ if (max_runtime) {
+ struct tevent_timer *te;
+ DBG_NOTICE("%s PID %d was called with maxruntime %d - "
+ "current ts %llu\n",
+ binary_name, (int)getpid(),
+ 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,
+ state);
+ if (te == NULL) {
+ TALLOC_FREE(state);
+ exit_daemon("Maxruntime handler failed", ENOMEM);
+ /*
+ * return is never reached but is here to
+ * satisfy static checkers
+ */
+ return 1;
+ }
+ }
+
+ se = tevent_add_signal(state->event_ctx,
+ state->event_ctx,
+ SIGTERM,
+ 0,
+ sigterm_signal_handler,
+ state);
+ if (se == NULL) {
+ TALLOC_FREE(state);
+ exit_daemon("Initialize SIGTERM handler failed", ENOMEM);
+ /*
+ * return is never reached but is here to satisfy static
+ * checkers
+ */
+ return 1;
+ }
+
+ se = tevent_add_signal(state->event_ctx,
+ state->event_ctx,
+ SIGHUP,
+ 0,
+ sighup_signal_handler,
+ state);
+ if (se == NULL) {
+ TALLOC_FREE(state);
+ exit_daemon("Initialize SIGHUP handler failed", ENOMEM);
+ /*
+ * return is never reached but is here to satisfy static
+ * checkers
+ */
+ return 1;
+ }
+
+ if (lpcfg_server_role(lp_ctx) != ROLE_ACTIVE_DIRECTORY_DC
+ && !lpcfg_parm_bool(lp_ctx, NULL,
+ "server role check", "inhibit", false)
+ && !str_list_check_ci(lpcfg_server_services(lp_ctx), "smb")
+ && !str_list_check_ci(lpcfg_dcerpc_endpoint_servers(lp_ctx),
+ "remote")
+ && !str_list_check_ci(lpcfg_dcerpc_endpoint_servers(lp_ctx),
+ "mapiproxy")) {
+ DEBUG(0, ("At this time the 'samba' binary should only be used "
+ "for either:\n"));
+ DEBUGADD(0, ("'server role = active directory domain "
+ "controller' or the rpc proxy "
+ "with 'dcerpc endpoint servers = remote'\n"));
+ DEBUGADD(0, ("You should start smbd/nmbd/winbindd instead for "
+ "domain member and standalone file server tasks\n"));
+ exit_daemon("Samba detected misconfigured 'server role' "
+ "and exited. Check logs for details", EINVAL);
+ };
+
+ ret = prime_ldb_databases(state->event_ctx, &db_is_backup);
+ if (ret != LDB_SUCCESS) {
+ TALLOC_FREE(state);
+ exit_daemon("Samba failed to prime database", EINVAL);
+ /*
+ * return is never reached but is here to satisfy static
+ * checkers
+ */
+ return 1;
+ }
+
+ if (db_is_backup) {
+ TALLOC_FREE(state);
+ exit_daemon("Database is a backup. Please run samba-tool domain"
+ " backup restore", EINVAL);
+ /*
+ * return is never reached but is here to satisfy static
+ * checkers
+ */
+ return 1;
+ }
+
+ status = setup_parent_messaging(state, lp_ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(state);
+ exit_daemon("Samba failed to setup parent messaging",
+ NT_STATUS_V(status));
+ /*
+ * return is never reached but is here to satisfy static
+ * checkers
+ */
+ return 1;
+ }
+
+ DBG_NOTICE("%s: using '%s' process model\n", binary_name, model);
+
+ {
+ int child_pipe[2];
+ int rc;
+ bool start_services = false;
+
+ rc = pipe(child_pipe);
+ if (rc < 0) {
+ TALLOC_FREE(state);
+ exit_daemon("Samba failed to open process control pipe",
+ errno);
+ /*
+ * return is never reached but is here to satisfy static
+ * checkers
+ */
+ return 1;
+ }
+ smb_set_close_on_exec(child_pipe[0]);
+ smb_set_close_on_exec(child_pipe[1]);
+
+#ifdef HAVE_PTHREAD
+ to_children_fd = child_pipe[1];
+ pthread_atfork(atfork_prepare, atfork_parent,
+ atfork_child);
+ start_services = true;
+#else
+ pid_t pid;
+ struct tfork *t = NULL;
+ t = tfork_create();
+ if (t == NULL) {
+ exit_daemon(
+ "Samba unable to fork master process",
+ 0);
+ }
+ pid = tfork_child_pid(t);
+ if (pid == 0) {
+ start_services = false;
+ } else {
+ /* In the child process */
+ start_services = true;
+ close(child_pipe[1]);
+ }
+#endif
+ if (start_services) {
+ status = server_service_startup(
+ state->event_ctx, lp_ctx, model,
+ lpcfg_server_services(lp_ctx),
+ child_pipe[0]);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(state);
+ exit_daemon("Samba failed to start services",
+ NT_STATUS_V(status));
+ /*
+ * return is never reached but is here to
+ * satisfy static checkers
+ */
+ return 1;
+ }
+ }
+ }
+
+ if (!cmdline_daemon_cfg->interactive) {
+ daemon_ready("samba");
+ }
+
+ /* wait for events - this is where smbd sits for most of its
+ life */
+ tevent_loop_wait(state->event_ctx);
+
+ /* as everything hangs off this state->event context, freeing state
+ will initiate a clean shutdown of all services */
+ TALLOC_FREE(state);
+
+ return 0;
+}
+
+int main(int argc, const char *argv[])
+{
+ TALLOC_CTX *mem_ctx = NULL;
+ int rc;
+
+ mem_ctx = talloc_init("samba/server.c#main");
+ if (mem_ctx == NULL) {
+ exit(ENOMEM);
+ }
+
+ setproctitle_init(argc, discard_const(argv), environ);
+
+ rc = binary_smbd_main(mem_ctx, "samba", argc, argv);
+
+ TALLOC_FREE(mem_ctx);
+ return rc;
+}
diff --git a/source4/samba/server_util.c b/source4/samba/server_util.c
new file mode 100644
index 0000000..282ad9b
--- /dev/null
+++ b/source4/samba/server_util.c
@@ -0,0 +1,94 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Utility routines
+
+ Copyright (C) 2020 Ralph Boehme <slow@samba.org>
+
+ 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/tevent/tevent.h"
+#include "lib/util/unix_privs.h"
+#include "server_util.h"
+
+struct samba_tevent_trace_state {
+ size_t events;
+ time_t last_logsize_check;
+};
+
+struct samba_tevent_trace_state *create_samba_tevent_trace_state(
+ TALLOC_CTX *mem_ctx)
+{
+ return talloc_zero(mem_ctx, struct samba_tevent_trace_state);
+}
+
+void samba_tevent_trace_callback(enum tevent_trace_point point,
+ void *private_data)
+{
+ struct samba_tevent_trace_state *state =
+ talloc_get_type_abort(private_data,
+ struct samba_tevent_trace_state);
+ time_t now = time(NULL);
+ bool do_check_logs = false;
+ void *priv = NULL;
+
+ switch (point) {
+ case TEVENT_TRACE_BEFORE_WAIT:
+ break;
+ default:
+ return;
+ }
+
+ state->events++;
+
+ /*
+ * Throttling by some random numbers. smbd uses a similar logic
+ * checking every 50 SMB requests. Assuming 4 events per request
+ * we get to the number of 200.
+ */
+ if ((state->events % 200) == 0) {
+ do_check_logs = true;
+ }
+ /*
+ * Throttling by some delay, choosing 29 to avoid lockstep with
+ * the default tevent tickle timer.
+ */
+ if ((state->last_logsize_check + 29) < now) {
+ do_check_logs = true;
+ }
+
+ if (!do_check_logs) {
+ return;
+ }
+
+ /*
+ * need_to_check_log_size() checks both the number of messages
+ * that have been logged and if the logging backend is actually
+ * going to file. We want to bypass the "number of messages"
+ * check, so we have to call force_check_log_size() before.
+ */
+ force_check_log_size();
+ if (!need_to_check_log_size()) {
+ return;
+ }
+
+ priv = root_privileges();
+ check_log_size();
+ TALLOC_FREE(priv);
+
+ state->last_logsize_check = now;
+ return;
+}
diff --git a/source4/samba/server_util.h b/source4/samba/server_util.h
new file mode 100644
index 0000000..08c09cc
--- /dev/null
+++ b/source4/samba/server_util.h
@@ -0,0 +1,33 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Utility routines
+
+ Copyright (C) 2020 Ralph Boehme <slow@samba.org>
+
+ 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/>.
+*/
+
+#ifndef SAMBA_SERVER_UTIL_H
+#define SAMBA_SERVER_UTIL_H
+
+struct samba_tevent_trace_state;
+
+struct samba_tevent_trace_state *create_samba_tevent_trace_state(
+ TALLOC_CTX *mem_ctx);
+
+void samba_tevent_trace_callback(enum tevent_trace_point point,
+ void *private_data);
+
+#endif
diff --git a/source4/samba/service.c b/source4/samba/service.c
new file mode 100644
index 0000000..2ad6c8f
--- /dev/null
+++ b/source4/samba/service.c
@@ -0,0 +1,140 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ SERVER SERVICE code
+
+ Copyright (C) Andrew Tridgell 2003-2005
+ Copyright (C) Stefan (metze) Metzmacher 2004
+
+ 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/util/dlinklist.h"
+#include "samba/process_model.h"
+#include "lib/util/samba_modules.h"
+
+#undef strcasecmp
+
+/*
+ a linked list of registered servers
+*/
+static struct registered_server {
+ struct registered_server *next, *prev;
+ const char *service_name;
+ const struct service_details *service_details;
+} *registered_servers;
+
+/*
+ register a server service.
+*/
+NTSTATUS register_server_service(TALLOC_CTX *ctx,
+ const char *name,
+ const 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->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;
+}
+
+
+/*
+ initialise a server service
+*/
+static NTSTATUS server_service_init(const char *name,
+ struct tevent_context *event_context,
+ struct loadparm_context *lp_ctx,
+ const struct model_ops *model_ops,
+ int from_parent_fd)
+{
+ struct registered_server *srv;
+ for (srv=registered_servers; srv; srv=srv->next) {
+ if (strcasecmp(name, srv->service_name) == 0) {
+ return task_server_startup(event_context, lp_ctx,
+ srv->service_name,
+ model_ops,
+ srv->service_details,
+ from_parent_fd);
+ }
+ }
+ return NT_STATUS_INVALID_SYSTEM_SERVICE;
+}
+
+
+/*
+ startup all of our server services
+*/
+NTSTATUS server_service_startup(struct tevent_context *event_ctx,
+ struct loadparm_context *lp_ctx,
+ const char *model, const char **server_services,
+ int from_parent_fd)
+{
+ int i;
+ const struct model_ops *model_ops;
+
+ if (!server_services) {
+ DBG_ERR("server_service_startup: "
+ "no endpoint servers configured\n");
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ model_ops = process_model_startup(model);
+ if (!model_ops) {
+ DBG_ERR("process_model_startup('%s') failed\n", model);
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ for (i=0;server_services[i];i++) {
+ NTSTATUS status;
+
+ status = server_service_init(server_services[i], event_ctx,
+ lp_ctx, model_ops, from_parent_fd);
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_ERR("Failed to start service '%s' - %s\n",
+ server_services[i], nt_errstr(status));
+ }
+ NT_STATUS_NOT_OK_RETURN(status);
+ }
+
+ return NT_STATUS_OK;
+}
+
+_PUBLIC_ NTSTATUS samba_service_init(void)
+{
+#define _MODULE_PROTO(init) extern NTSTATUS init(TALLOC_CTX *);
+ STATIC_service_MODULES_PROTO;
+ init_module_fn static_init[] = { STATIC_service_MODULES };
+ init_module_fn *shared_init = NULL;
+ static bool initialised;
+
+ if (initialised) {
+ return NT_STATUS_OK;
+ }
+ initialised = true;
+
+ shared_init = load_samba_modules(NULL, "service");
+
+ run_init_functions(NULL, static_init);
+ run_init_functions(NULL, shared_init);
+
+ TALLOC_FREE(shared_init);
+
+ return NT_STATUS_OK;
+}
diff --git a/source4/samba/service.h b/source4/samba/service.h
new file mode 100644
index 0000000..35e3f51
--- /dev/null
+++ b/source4/samba/service.h
@@ -0,0 +1,111 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ SERVER SERVICE code
+
+ Copyright (C) Andrew Tridgell 2003-2005
+ Copyright (C) Stefan (metze) Metzmacher 2004
+
+ 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/>.
+*/
+
+#ifndef __SERVICE_H__
+#define __SERVICE_H__
+
+
+#include "samba/service_stream.h"
+#include "samba/service_task.h"
+
+struct process_details {
+ unsigned int instances;
+};
+
+static const struct process_details initial_process_details = {
+ .instances = 0
+};
+
+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;
+ /*
+ * Initialise the server task.
+ */
+ NTSTATUS (*task_init) (struct task_server *);
+ /*
+ * post fork processing this is called:
+ * - standard process model
+ * immediately after the task_init.
+ *
+ * - single process model
+ * immediately after the task_init
+ *
+ * - prefork process model, inhibit_pre_fork = true
+ * immediately after the task_init
+ *
+ * - prefork process model, inhibit_pre_fork = false
+ * after each service worker has forked. It is not run on the
+ * service master process.
+ *
+ * The post fork hook is not called in the standard model if a new
+ * process is forked on a new connection. It is instead called
+ * immediately after the task_init.
+ */
+ void (*post_fork) (struct task_server *, struct process_details *);
+ /*
+ * This is called before entering the tevent_loop_wait():
+ *
+ * Note that task_server->msg_ctx, task_server->event_ctx
+ * and maybe other fields might have changed compared to
+ * task_init()/post_fork(). The struct task_server pointer
+ * may also change!
+ *
+ * before loop processing this is called in this order:
+ * - standard process model
+ * task_init() -> post_fork() -> before_loop()
+ *
+ * - single process model
+ * task_init() -> post_fork() -> before_loop()
+ *
+ * - prefork process model, inhibit_pre_fork = true
+ * task_init() -> post_fork() -> before_loop()
+ *
+ * - prefork process model, inhibit_pre_fork = false
+ * In the service master process:
+ * task_init(master) -> before_loop(master)
+ *
+ * After each service worker has forked:
+ * post_fork(worker) -> before_loop(worker)
+ *
+ * This gives the service a chance to register messaging
+ * and/or event handlers on the correct contexts.
+ */
+ void (*before_loop) (struct task_server *);
+};
+
+NTSTATUS samba_service_init(void);
+
+#include "samba/service_proto.h"
+
+#endif /* __SERVICE_H__ */
diff --git a/source4/samba/service_named_pipe.c b/source4/samba/service_named_pipe.c
new file mode 100644
index 0000000..bf2350c
--- /dev/null
+++ b/source4/samba/service_named_pipe.c
@@ -0,0 +1,290 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ helper functions for NAMED PIPE servers
+
+ Copyright (C) Stefan (metze) Metzmacher 2008
+
+ 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 <tevent.h>
+#include "samba/service.h"
+#include "param/param.h"
+#include "auth/auth.h"
+#include "auth/session.h"
+#include "auth/auth_sam_reply.h"
+#include "lib/socket/socket.h"
+#include "lib/tsocket/tsocket.h"
+#include "libcli/util/tstream.h"
+#include "librpc/gen_ndr/ndr_named_pipe_auth.h"
+#include "system/passwd.h"
+#include "system/network.h"
+#include "libcli/raw/smb.h"
+#include "auth/session.h"
+#include "libcli/security/security.h"
+#include "libcli/named_pipe_auth/npa_tstream.h"
+
+struct named_pipe_socket {
+ const char *pipe_name;
+ const char *pipe_path;
+ const struct stream_server_ops *ops;
+ void *private_data;
+};
+
+static void named_pipe_accept_done(struct tevent_req *subreq);
+
+static void named_pipe_accept(struct stream_connection *conn)
+{
+ struct tstream_context *plain_tstream;
+ int fd;
+ struct tevent_req *subreq;
+ int ret;
+
+ /* Let tstream take over fd operations */
+
+ fd = socket_get_fd(conn->socket);
+ socket_set_flags(conn->socket, SOCKET_FLAG_NOCLOSE);
+ TALLOC_FREE(conn->event.fde);
+ TALLOC_FREE(conn->socket);
+
+ ret = tstream_bsd_existing_socket(conn, fd, &plain_tstream);
+ if (ret != 0) {
+ stream_terminate_connection(conn,
+ "named_pipe_accept: out of memory");
+ return;
+ }
+ /* as server we want to fail early */
+ tstream_bsd_fail_readv_first_error(plain_tstream, true);
+
+ subreq = tstream_npa_accept_existing_send(conn, conn->event.ctx,
+ plain_tstream,
+ FILE_TYPE_MESSAGE_MODE_PIPE,
+ 0xff | 0x0400 | 0x0100,
+ 4096);
+ if (subreq == NULL) {
+ stream_terminate_connection(conn,
+ "named_pipe_accept: "
+ "no memory for tstream_npa_accept_existing_send");
+ return;
+ }
+ tevent_req_set_callback(subreq, named_pipe_accept_done, conn);
+}
+
+static void named_pipe_accept_done(struct tevent_req *subreq)
+{
+ struct stream_connection *conn = tevent_req_callback_data(subreq,
+ struct stream_connection);
+ struct named_pipe_socket *pipe_sock =
+ talloc_get_type(conn->private_data,
+ struct named_pipe_socket);
+ enum dcerpc_transport_t transport;
+ struct tsocket_address *remote_client_addr;
+ char *remote_client_name;
+ struct tsocket_address *local_server_addr;
+ char *local_server_name;
+ struct auth_session_info_transport *session_info_transport;
+ const char *reason = NULL;
+ TALLOC_CTX *tmp_ctx;
+ int error;
+ int ret;
+
+ tmp_ctx = talloc_new(conn);
+ if (!tmp_ctx) {
+ reason = "Out of memory!\n";
+ goto out;
+ }
+
+ ret = tstream_npa_accept_existing_recv(subreq, &error, tmp_ctx,
+ &conn->tstream,
+ NULL,
+ &transport,
+ &remote_client_addr,
+ &remote_client_name,
+ &local_server_addr,
+ &local_server_name,
+ &session_info_transport);
+ TALLOC_FREE(subreq);
+ if (ret != 0) {
+ reason = talloc_asprintf(conn,
+ "tstream_npa_accept_existing_recv()"
+ " failed: %s", strerror(error));
+ goto out;
+ }
+
+ conn->local_address = talloc_move(conn, &local_server_addr);
+ conn->remote_address = talloc_move(conn, &remote_client_addr);
+
+ 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,
+ &reason);
+ if (!conn->session_info) {
+ goto out;
+ }
+
+ if (transport == NCACN_NP) {
+ if (security_token_is_system(conn->session_info->security_token)) {
+ reason = talloc_asprintf(
+ conn,
+ "System token not allowed on transport %d\n",
+ transport);
+ goto out;
+ }
+ } else if (transport == NCALRPC) {
+ /*
+ * TODO:
+ * we should somehow remember the given transport on
+ * the connection, but that's a task for another day
+ * as it's not trivial to do...
+ */
+ } else {
+ reason = talloc_asprintf(
+ conn,
+ "Only allow NCACN_NP or NCALRPC transport on named pipes, "
+ "got %d\n",
+ (int)transport);
+ goto out;
+ }
+
+ /*
+ * hand over to the real pipe implementation,
+ * now that we have setup the transport session_info
+ */
+ conn->ops = pipe_sock->ops;
+ conn->private_data = pipe_sock->private_data;
+ conn->ops->accept_connection(conn);
+
+ DBG_DEBUG("named pipe connection [%s] established\n", conn->ops->name);
+
+ talloc_free(tmp_ctx);
+ return;
+
+out:
+ talloc_free(tmp_ctx);
+ if (!reason) {
+ reason = "Internal error";
+ }
+ stream_terminate_connection(conn, reason);
+}
+
+/*
+ called when a pipe socket becomes readable
+*/
+static void named_pipe_recv(struct stream_connection *conn, uint16_t flags)
+{
+ stream_terminate_connection(conn, "named_pipe_recv: called");
+}
+
+/*
+ called when a pipe socket becomes writable
+*/
+static void named_pipe_send(struct stream_connection *conn, uint16_t flags)
+{
+ stream_terminate_connection(conn, "named_pipe_send: called");
+}
+
+static const struct stream_server_ops named_pipe_stream_ops = {
+ .name = "named_pipe",
+ .accept_connection = named_pipe_accept,
+ .recv_handler = named_pipe_recv,
+ .send_handler = named_pipe_send,
+};
+
+NTSTATUS tstream_setup_named_pipe(TALLOC_CTX *mem_ctx,
+ struct tevent_context *event_context,
+ struct loadparm_context *lp_ctx,
+ const struct model_ops *model_ops,
+ const struct stream_server_ops *stream_ops,
+ const char *pipe_name,
+ void *private_data,
+ void *process_context)
+{
+ char *dirname;
+ struct named_pipe_socket *pipe_sock;
+ NTSTATUS status = NT_STATUS_NO_MEMORY;;
+
+ pipe_sock = talloc(mem_ctx, struct named_pipe_socket);
+ if (pipe_sock == NULL) {
+ goto fail;
+ }
+
+ /* remember the details about the pipe */
+ pipe_sock->pipe_name = strlower_talloc(pipe_sock, pipe_name);
+ if (pipe_sock->pipe_name == NULL) {
+ goto fail;
+ }
+
+ if (!directory_create_or_exist(lpcfg_ncalrpc_dir(lp_ctx), 0755)) {
+ status = map_nt_error_from_unix_common(errno);
+ DBG_ERR("Failed to create ncalrpc pipe directory '%s' - %s\n",
+ lpcfg_ncalrpc_dir(lp_ctx), nt_errstr(status));
+ goto fail;
+ }
+
+ dirname = talloc_asprintf(pipe_sock, "%s/np", lpcfg_ncalrpc_dir(lp_ctx));
+ if (dirname == NULL) {
+ goto fail;
+ }
+
+ if (!directory_create_or_exist_strict(dirname, geteuid(), 0700)) {
+ status = map_nt_error_from_unix_common(errno);
+ DBG_ERR("Failed to create stream pipe directory '%s' - %s\n",
+ dirname, nt_errstr(status));
+ goto fail;
+ }
+
+ if (strncmp(pipe_name, "\\pipe\\", 6) == 0) {
+ pipe_name += 6;
+ }
+
+ pipe_sock->pipe_path = talloc_asprintf(pipe_sock, "%s/%s", dirname,
+ pipe_name);
+ if (pipe_sock->pipe_path == NULL) {
+ goto fail;
+ }
+
+ talloc_free(dirname);
+
+ pipe_sock->ops = stream_ops;
+ pipe_sock->private_data = private_data;
+
+ status = stream_setup_socket(pipe_sock,
+ event_context,
+ lp_ctx,
+ model_ops,
+ &named_pipe_stream_ops,
+ "unix",
+ pipe_sock->pipe_path,
+ NULL,
+ NULL,
+ pipe_sock,
+ process_context);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+ return NT_STATUS_OK;
+
+ fail:
+ talloc_free(pipe_sock);
+ return status;
+}
diff --git a/source4/samba/service_stream.c b/source4/samba/service_stream.c
new file mode 100644
index 0000000..458ebcf
--- /dev/null
+++ b/source4/samba/service_stream.c
@@ -0,0 +1,413 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ helper functions for stream based servers
+
+ Copyright (C) Andrew Tridgell 2003-2005
+ Copyright (C) Stefan (metze) Metzmacher 2004
+
+ 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 <tevent.h>
+#include "process_model.h"
+#include "lib/util/server_id.h"
+#include "lib/messaging/irpc.h"
+#include "cluster/cluster.h"
+#include "param/param.h"
+#include "../lib/tsocket/tsocket.h"
+#include "lib/util/util_net.h"
+
+/* size of listen() backlog in smbd */
+#define SERVER_LISTEN_BACKLOG 10
+
+
+/*
+ private structure for a single listening stream socket
+*/
+struct stream_socket {
+ const struct stream_server_ops *ops;
+ struct loadparm_context *lp_ctx;
+ struct tevent_context *event_ctx;
+ const struct model_ops *model_ops;
+ struct socket_context *sock;
+ void *private_data;
+ void *process_context;
+};
+
+
+/*
+ close the socket and shutdown a stream_connection
+*/
+void stream_terminate_connection(struct stream_connection *srv_conn, const char *reason)
+{
+ 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";
+
+ if (srv_conn->processing) {
+ DBG_NOTICE("Terminating connection deferred - '%s'\n", reason);
+ } else {
+ DBG_NOTICE("Terminating connection - '%s'\n", reason);
+ }
+
+ srv_conn->terminate = reason;
+
+ if (srv_conn->processing) {
+ /*
+ * if we're currently inside the stream_io_handler(),
+ * defer the termination to the end of stream_io_hendler()
+ *
+ * and we don't want to read or write to the connection...
+ */
+ tevent_fd_set_flags(srv_conn->event.fde, 0);
+ return;
+ }
+
+ frame = talloc_stackframe();
+
+ reason = talloc_strdup(frame, reason);
+ if (reason == NULL) {
+ reason = "OOM - unknown reason";
+ }
+
+ TALLOC_FREE(srv_conn->event.fde);
+ imessaging_cleanup(srv_conn->msg_ctx);
+ TALLOC_FREE(srv_conn);
+ model_ops->terminate_connection(
+ event_ctx, lp_ctx, reason, process_context);
+ TALLOC_FREE(frame);
+}
+
+/**
+ the select loop has indicated that a stream is ready for IO
+*/
+static void stream_io_handler(struct stream_connection *conn, uint16_t flags)
+{
+ conn->processing++;
+ if (flags & TEVENT_FD_WRITE) {
+ conn->ops->send_handler(conn, flags);
+ } else if (flags & TEVENT_FD_READ) {
+ conn->ops->recv_handler(conn, flags);
+ }
+ conn->processing--;
+
+ if (conn->terminate) {
+ stream_terminate_connection(conn, conn->terminate);
+ }
+}
+
+void stream_io_handler_fde(struct tevent_context *ev, struct tevent_fd *fde,
+ uint16_t flags, void *private_data)
+{
+ struct stream_connection *conn = talloc_get_type(private_data,
+ struct stream_connection);
+ stream_io_handler(conn, flags);
+}
+
+void stream_io_handler_callback(void *private_data, uint16_t flags)
+{
+ struct stream_connection *conn = talloc_get_type(private_data,
+ struct stream_connection);
+ stream_io_handler(conn, flags);
+}
+
+/*
+ this creates a stream_connection from an already existing connection,
+ used for protocols, where a client connection needs to switched into
+ a server connection
+*/
+NTSTATUS stream_new_connection_merge(struct tevent_context *ev,
+ struct loadparm_context *lp_ctx,
+ const struct model_ops *model_ops,
+ const struct stream_server_ops *stream_ops,
+ struct imessaging_context *msg_ctx,
+ void *private_data,
+ struct stream_connection **_srv_conn,
+ void *process_context)
+{
+ struct stream_connection *srv_conn;
+
+ 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->process_context = process_context;
+
+ *_srv_conn = srv_conn;
+ return NT_STATUS_OK;
+}
+
+/*
+ called when a new socket connection has been established. This is called in the process
+ context of the new process (if appropriate)
+*/
+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,
+ void *process_context)
+{
+ struct stream_socket *stream_socket = talloc_get_type(private_data, struct stream_socket);
+ struct stream_connection *srv_conn;
+
+ srv_conn = talloc_zero(ev, struct stream_connection);
+ if (!srv_conn) {
+ DBG_ERR("talloc(mem_ctx, struct stream_connection) failed\n");
+ return;
+ }
+
+ 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->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");
+ return;
+ }
+
+ srv_conn->event.fde = tevent_add_fd(ev, srv_conn, socket_get_fd(sock),
+ 0, stream_io_handler_fde, srv_conn);
+ if (!srv_conn->event.fde) {
+ stream_terminate_connection(srv_conn, "tevent_add_fd() failed");
+ return;
+ }
+
+ /* setup to receive internal messages on this connection */
+ srv_conn->msg_ctx = imessaging_init(srv_conn,
+ lp_ctx,
+ srv_conn->server_id, ev);
+ if (!srv_conn->msg_ctx) {
+ stream_terminate_connection(srv_conn, "imessaging_init() failed");
+ return;
+ }
+
+ srv_conn->remote_address = socket_get_remote_addr(srv_conn->socket, srv_conn);
+ if (!srv_conn->remote_address) {
+ stream_terminate_connection(srv_conn, "socket_get_remote_addr() failed");
+ return;
+ }
+
+ srv_conn->local_address = socket_get_local_addr(srv_conn->socket, srv_conn);
+ if (!srv_conn->local_address) {
+ stream_terminate_connection(srv_conn, "socket_get_local_addr() failed");
+ return;
+ }
+
+ {
+ TALLOC_CTX *tmp_ctx;
+ const char *title;
+ struct server_id_buf idbuf;
+
+ tmp_ctx = talloc_new(srv_conn);
+
+ title = talloc_asprintf(tmp_ctx, "conn[%s] c[%s] s[%s] server_id[%s]",
+ stream_socket->ops->name,
+ tsocket_address_string(srv_conn->remote_address, tmp_ctx),
+ tsocket_address_string(srv_conn->local_address, tmp_ctx),
+ server_id_str_buf(server_id, &idbuf));
+ if (title) {
+ stream_connection_set_title(srv_conn, title);
+ }
+ talloc_free(tmp_ctx);
+ }
+
+ /* we're now ready to start receiving events on this stream */
+ TEVENT_FD_READABLE(srv_conn->event.fde);
+
+ /* call the server specific accept code */
+ stream_socket->ops->accept_connection(srv_conn);
+}
+
+
+/*
+ called when someone opens a connection to one of our listening ports
+*/
+static void stream_accept_handler(struct tevent_context *ev, struct tevent_fd *fde,
+ uint16_t flags, void *private_data)
+{
+ struct stream_socket *stream_socket = talloc_get_type(private_data, struct stream_socket);
+
+ /* 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);
+}
+
+/*
+ setup a listen stream socket
+ if you pass *port == 0, then a port > 1024 is used
+
+ FIXME: This function is TCP/IP specific - uses an int rather than
+ a string for the port. Should leave allocating a port nr
+ to the socket implementation - JRV20070903
+ */
+NTSTATUS stream_setup_socket(TALLOC_CTX *mem_ctx,
+ struct tevent_context *event_context,
+ struct loadparm_context *lp_ctx,
+ const struct model_ops *model_ops,
+ const struct stream_server_ops *stream_ops,
+ const char *family,
+ const char *sock_addr,
+ uint16_t *port,
+ const char *socket_options,
+ void *private_data,
+ void *process_context)
+{
+ NTSTATUS status;
+ struct stream_socket *stream_socket;
+ struct socket_address *socket_address;
+ struct tevent_fd *fde;
+ int i;
+ struct sockaddr_storage ss;
+
+ stream_socket = talloc_zero(mem_ctx, struct stream_socket);
+ NT_STATUS_HAVE_NO_MEMORY(stream_socket);
+
+ if (strcmp(family, "ip") == 0) {
+ /* we will get the real family from the address itself */
+ if (!interpret_string_addr(&ss, sock_addr, 0)) {
+ talloc_free(stream_socket);
+ return NT_STATUS_INVALID_ADDRESS;
+ }
+
+ socket_address = socket_address_from_sockaddr_storage(stream_socket, &ss, port?*port:0);
+ if (socket_address == NULL) {
+ TALLOC_FREE(stream_socket);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = socket_create(stream_socket, socket_address->family,
+ SOCKET_TYPE_STREAM,
+ &stream_socket->sock, 0);
+ NT_STATUS_NOT_OK_RETURN(status);
+ } else {
+ status = socket_create(stream_socket, family,
+ SOCKET_TYPE_STREAM,
+ &stream_socket->sock, 0);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ /* this is for non-IP sockets, eg. unix domain sockets */
+ socket_address = socket_address_from_strings(stream_socket,
+ stream_socket->sock->backend_name,
+ sock_addr, port?*port:0);
+ NT_STATUS_HAVE_NO_MEMORY(socket_address);
+ }
+
+
+ stream_socket->lp_ctx = talloc_reference(stream_socket, lp_ctx);
+
+ /* ready to listen */
+ status = socket_set_option(stream_socket->sock, "SO_KEEPALIVE", NULL);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ if (socket_options != NULL) {
+ status = socket_set_option(stream_socket->sock, socket_options, NULL);
+ NT_STATUS_NOT_OK_RETURN(status);
+ }
+
+ /* TODO: set socket ACL's (host allow etc) here when they're
+ * implemented */
+
+ /* Some sockets don't have a port, or are just described from
+ * the string. We are indicating this by having port == NULL */
+ if (!port) {
+ status = socket_listen(stream_socket->sock, socket_address, SERVER_LISTEN_BACKLOG, 0);
+ } else if (*port == 0) {
+ for (i = lpcfg_rpc_low_port(lp_ctx);
+ i <= lpcfg_rpc_high_port(lp_ctx);
+ i++) {
+ socket_address->port = i;
+ status = socket_listen(stream_socket->sock, socket_address,
+ SERVER_LISTEN_BACKLOG, 0);
+ if (NT_STATUS_IS_OK(status)) {
+ *port = i;
+ break;
+ }
+ }
+ } else {
+ status = socket_listen(stream_socket->sock, socket_address, SERVER_LISTEN_BACKLOG, 0);
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ DBG_ERR("Failed to listen on %s:%u - %s\n",
+ sock_addr, port ? (unsigned int)(*port) : 0,
+ nt_errstr(status));
+ talloc_free(stream_socket);
+ return status;
+ }
+
+ /* Add the FD from the newly created socket into the event
+ * subsystem. it will call the accept handler whenever we get
+ * new connections */
+
+ fde = tevent_add_fd(event_context, stream_socket->sock,
+ socket_get_fd(stream_socket->sock),
+ TEVENT_FD_READ,
+ stream_accept_handler, stream_socket);
+ if (!fde) {
+ DBG_ERR("Failed to setup fd event\n");
+ talloc_free(stream_socket);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* we let events system to the close on the socket. This avoids
+ * nasty interactions with waiting for talloc to close the socket. */
+ tevent_fd_set_close_fn(fde, socket_tevent_fd_close_fn);
+ socket_set_flags(stream_socket->sock, SOCKET_FLAG_NOCLOSE);
+
+ stream_socket->private_data = talloc_reference(stream_socket, private_data);
+ 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;
+}
+
+
+/*
+ setup a connection title
+*/
+void stream_connection_set_title(struct stream_connection *conn, const char *title)
+{
+ conn->model_ops->set_title(conn->event.ctx, title);
+}
diff --git a/source4/samba/service_stream.h b/source4/samba/service_stream.h
new file mode 100644
index 0000000..81bf275
--- /dev/null
+++ b/source4/samba/service_stream.h
@@ -0,0 +1,80 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ structures specific to stream servers
+
+ Copyright (C) Stefan (metze) Metzmacher 2004
+ Copyright (C) Andrew Tridgell 2005
+
+ 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/>.
+*/
+
+#ifndef __SERVICE_STREAM_H__
+#define __SERVICE_STREAM_H__
+
+#include "librpc/gen_ndr/server_id.h"
+
+/* modules can use the following to determine if the interface has changed
+ * please increment the version number after each interface change
+ * with a comment and maybe update struct stream_connection_critical_sizes.
+ */
+/* version 0 - initial version - metze */
+#define SERVER_SERVICE_VERSION 0
+
+/*
+ top level context for an established stream connection
+*/
+struct stream_connection {
+ const struct stream_server_ops *ops;
+ const struct model_ops *model_ops;
+ struct server_id server_id;
+ void *private_data;
+
+ struct {
+ struct tevent_context *ctx;
+ struct tevent_fd *fde;
+ } event;
+
+ struct socket_context *socket;
+ struct imessaging_context *msg_ctx;
+ struct loadparm_context *lp_ctx;
+
+ struct tstream_context *tstream;
+ struct tsocket_address *local_address;
+ struct tsocket_address *remote_address;
+
+ /*
+ * this transport layer session info, normally NULL
+ * which means the same as an anonymous session info
+ */
+ struct auth_session_info *session_info;
+
+ uint processing;
+ const char *terminate;
+ void *process_context;
+};
+
+
+/* operations passed to the service_stream code */
+struct stream_server_ops {
+ /* the name of the server_service */
+ const char *name;
+ void (*accept_connection)(struct stream_connection *);
+ void (*recv_handler)(struct stream_connection *, uint16_t);
+ void (*send_handler)(struct stream_connection *, uint16_t);
+};
+
+void stream_terminate_connection(struct stream_connection *srv_conn, const char *reason);
+
+#endif /* __SERVICE_STREAM_H__ */
diff --git a/source4/samba/service_task.c b/source4/samba/service_task.c
new file mode 100644
index 0000000..d911027
--- /dev/null
+++ b/source4/samba/service_task.c
@@ -0,0 +1,140 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ helper functions for task based servers (nbtd, winbind etc)
+
+ Copyright (C) Andrew Tridgell 2005
+
+ 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 "process_model.h"
+#include "lib/messaging/irpc.h"
+#include "param/param.h"
+#include "librpc/gen_ndr/ndr_irpc_c.h"
+
+/*
+ terminate a task service
+*/
+void task_server_terminate(struct task_server *task, const char *reason, bool fatal)
+{
+ struct tevent_context *event_ctx = task->event_ctx;
+ const struct model_ops *model_ops = task->model_ops;
+ 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;
+ struct samba_terminate r;
+
+ irpc_handle = irpc_binding_handle_by_name(task, task->msg_ctx,
+ "samba", &ndr_table_irpc);
+ if (irpc_handle != NULL) {
+ /* Note: this makes use of nested event loops... */
+ dcerpc_binding_handle_set_sync_ev(irpc_handle, event_ctx);
+ r.in.reason = reason;
+ dcerpc_samba_terminate_r(irpc_handle, task, &r);
+ }
+ }
+
+ imessaging_cleanup(task->msg_ctx);
+
+ model_ops->terminate_task(
+ event_ctx, task->lp_ctx, reason, fatal, task->process_context);
+ /* don't free this above, it might contain the 'reason' being printed */
+ talloc_free(task);
+}
+
+/* used for the callback from the process model code */
+struct task_state {
+ const struct service_details *service_details;
+ const struct model_ops *model_ops;
+};
+
+
+/*
+ called by the process model code when the new task starts up. This then calls
+ the server specific startup code
+*/
+static struct task_server *task_server_callback(struct tevent_context *event_ctx,
+ struct loadparm_context *lp_ctx,
+ struct server_id server_id,
+ void *private_data,
+ void *context)
+{
+ struct task_server *task;
+ NTSTATUS status = NT_STATUS_OK;
+
+ struct task_state *state = talloc_get_type(private_data, struct task_state);
+ task = talloc(event_ctx, struct task_server);
+ if (task == NULL) return NULL;
+
+ task->event_ctx = 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,
+ task->server_id,
+ task->event_ctx);
+ if (!task->msg_ctx) {
+ task_server_terminate(task, "imessaging_init() failed", true);
+ return NULL;
+ }
+
+ status = state->service_details->task_init(task);
+ if (!NT_STATUS_IS_OK(status)) {
+ return NULL;
+ }
+ return task;
+}
+
+/*
+ startup a task based server
+*/
+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 struct service_details *service_details,
+ int from_parent_fd)
+{
+ struct task_state *state;
+
+ state = talloc(event_ctx, struct task_state);
+ NT_STATUS_HAVE_NO_MEMORY(state);
+
+ state->service_details = service_details;
+ state->model_ops = model_ops;
+
+ state->model_ops->new_task(event_ctx, lp_ctx, service_name,
+ task_server_callback, state, service_details,
+ from_parent_fd);
+
+ return NT_STATUS_OK;
+}
+
+/*
+ setup a task title
+*/
+void task_server_set_title(struct task_server *task, const char *title)
+{
+ task->model_ops->set_title(task->event_ctx, title);
+}
diff --git a/source4/samba/service_task.h b/source4/samba/service_task.h
new file mode 100644
index 0000000..2499dc1
--- /dev/null
+++ b/source4/samba/service_task.h
@@ -0,0 +1,39 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ structures for task based servers
+
+ Copyright (C) Andrew Tridgell 2005
+
+ 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/>.
+*/
+
+#ifndef __SERVICE_TASK_H__
+#define __SERVICE_TASK_H__
+
+#include "librpc/gen_ndr/server_id.h"
+
+struct task_server {
+ struct tevent_context *event_ctx;
+ const struct model_ops *model_ops;
+ struct imessaging_context *msg_ctx;
+ struct loadparm_context *lp_ctx;
+ struct server_id server_id;
+ void *private_data;
+ void *process_context;
+};
+
+
+
+#endif /* __SERVICE_TASK_H__ */
diff --git a/source4/samba/wscript_build b/source4/samba/wscript_build
new file mode 100644
index 0000000..3dab850
--- /dev/null
+++ b/source4/samba/wscript_build
@@ -0,0 +1,58 @@
+#!/usr/bin/env python
+
+bld.SAMBA_LIBRARY('service',
+ source='service.c service_stream.c service_named_pipe.c service_task.c',
+ autoproto='service_proto.h',
+ deps='tevent MESSAGING samba_socket RPC_NDR_IRPC NDR_NAMED_PIPE_AUTH npa_tstream gssapi samba-credentials LIBTSOCKET LIBSAMBA_TSOCKET process_model',
+ private_library=True,
+ enabled=bld.AD_DC_BUILD_IS_ENABLED()
+ )
+
+
+bld.SAMBA_LIBRARY('process_model',
+ source='process_model.c',
+ autoproto='process_model_proto.h',
+ deps='samba-util samba-hostconfig samba-modules',
+ private_library=True,
+ enabled=bld.AD_DC_BUILD_IS_ENABLED()
+ )
+
+bld.SAMBA_SUBSYSTEM('samba_server_util',
+ source='server_util.c',
+ deps='samba-util')
+
+bld.SAMBA_BINARY('samba',
+ source='server.c',
+ subsystem_name='service',
+ deps='''events process_model service samba-hostconfig samba-util CMDLINE_S4
+ popt gensec registry ntvfs share cluster COMMON_SCHANNEL SECRETS
+ samba_server_util''',
+ pyembed=True,
+ install_path='${SBINDIR}',
+ enabled=bld.AD_DC_BUILD_IS_ENABLED()
+ )
+
+bld.SAMBA_MODULE('process_model_single',
+ source='process_single.c',
+ subsystem='process_model',
+ init_function='process_model_single_init',
+ deps='cluster process_model samba-sockets',
+ internal_module=True
+ )
+
+
+bld.SAMBA_MODULE('process_model_standard',
+ source='process_standard.c',
+ subsystem='process_model',
+ init_function='process_model_standard_init',
+ deps='MESSAGING events ldbsamba process_model samba-sockets cluster messages_dgm',
+ 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 samba_server_util',
+ internal_module=False
+ )