diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 17:20:00 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 17:20:00 +0000 |
commit | 8daa83a594a2e98f39d764422bfbdbc62c9efd44 (patch) | |
tree | 4099e8021376c7d8c05bdf8503093d80e9c7bad0 /source4/samba | |
parent | Initial commit. (diff) | |
download | samba-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.c | 137 | ||||
-rw-r--r-- | source4/samba/process_model.h | 96 | ||||
-rw-r--r-- | source4/samba/process_prefork.c | 919 | ||||
-rw-r--r-- | source4/samba/process_single.c | 169 | ||||
-rw-r--r-- | source4/samba/process_standard.c | 626 | ||||
-rw-r--r-- | source4/samba/server.c | 1000 | ||||
-rw-r--r-- | source4/samba/server_util.c | 94 | ||||
-rw-r--r-- | source4/samba/server_util.h | 33 | ||||
-rw-r--r-- | source4/samba/service.c | 140 | ||||
-rw-r--r-- | source4/samba/service.h | 111 | ||||
-rw-r--r-- | source4/samba/service_named_pipe.c | 290 | ||||
-rw-r--r-- | source4/samba/service_stream.c | 413 | ||||
-rw-r--r-- | source4/samba/service_stream.h | 80 | ||||
-rw-r--r-- | source4/samba/service_task.c | 140 | ||||
-rw-r--r-- | source4/samba/service_task.h | 39 | ||||
-rw-r--r-- | source4/samba/wscript_build | 58 |
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 + ) |