diff options
Diffstat (limited to 'source4/smbd')
-rw-r--r-- | source4/smbd/config.mk | 43 | ||||
-rw-r--r-- | source4/smbd/pidfile.c | 122 | ||||
-rw-r--r-- | source4/smbd/process_model.c | 128 | ||||
-rw-r--r-- | source4/smbd/process_model.h | 85 | ||||
-rw-r--r-- | source4/smbd/process_model.m4 | 60 | ||||
-rw-r--r-- | source4/smbd/process_model.mk | 50 | ||||
-rw-r--r-- | source4/smbd/process_prefork.c | 222 | ||||
-rw-r--r-- | source4/smbd/process_single.c | 124 | ||||
-rw-r--r-- | source4/smbd/process_standard.c | 238 | ||||
-rw-r--r-- | source4/smbd/process_thread.c | 572 | ||||
-rw-r--r-- | source4/smbd/server.c | 372 | ||||
-rw-r--r-- | source4/smbd/service.c | 104 | ||||
-rw-r--r-- | source4/smbd/service.h | 30 | ||||
-rw-r--r-- | source4/smbd/service_stream.c | 352 | ||||
-rw-r--r-- | source4/smbd/service_stream.h | 73 | ||||
-rw-r--r-- | source4/smbd/service_task.c | 111 | ||||
-rw-r--r-- | source4/smbd/service_task.h | 38 | ||||
-rw-r--r-- | source4/smbd/smbd.8.xml | 177 |
18 files changed, 2901 insertions, 0 deletions
diff --git a/source4/smbd/config.mk b/source4/smbd/config.mk new file mode 100644 index 0000000000..e60f444456 --- /dev/null +++ b/source4/smbd/config.mk @@ -0,0 +1,43 @@ +# server subsystem + +[SUBSYSTEM::service] +PRIVATE_DEPENDENCIES = \ + MESSAGING samba-socket + +service_OBJ_FILES = $(addprefix $(smbdsrcdir)/, \ + service.o \ + service_stream.o \ + service_task.o) + +$(eval $(call proto_header_template,$(smbdsrcdir)/service_proto.h,$(service_OBJ_FILES:.o=.c))) + +[SUBSYSTEM::PIDFILE] + +PIDFILE_OBJ_FILES = $(smbdsrcdir)/pidfile.o + +$(eval $(call proto_header_template,$(smbdsrcdir)/pidfile.h,$(PIDFILE_OBJ_FILES:.o=.c))) + +################################# +# Start BINARY smbd +[BINARY::smbd] +INSTALLDIR = SBINDIR +PRIVATE_DEPENDENCIES = \ + process_model \ + service \ + LIBSAMBA-HOSTCONFIG \ + LIBSAMBA-UTIL \ + POPT_SAMBA \ + PIDFILE \ + LIBPOPT \ + gensec \ + registry \ + ntptr \ + ntvfs \ + share \ + CLUSTER + +smbd_OBJ_FILES = $(smbdsrcdir)/server.o + +MANPAGES += $(smbdsrcdir)/smbd.8 +# End BINARY smbd +################################# diff --git a/source4/smbd/pidfile.c b/source4/smbd/pidfile.c new file mode 100644 index 0000000000..3567846070 --- /dev/null +++ b/source4/smbd/pidfile.c @@ -0,0 +1,122 @@ +/* this code is broken - there is a race condition with the unlink (tridge) */ + +/* + Unix SMB/CIFS implementation. + pidfile handling + Copyright (C) Andrew Tridgell 1998 + + 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 "system/filesys.h" +#include "param/param.h" +#include "smbd/pidfile.h" + +/** + * @file + * @brief Pid file handling + */ + +/** + * return the pid in a pidfile. return 0 if the process (or pidfile) + * does not exist + */ +pid_t pidfile_pid(const char *piddir, const char *name) +{ + int fd; + char pidstr[20]; + pid_t ret; + char *pidFile; + + asprintf(&pidFile, "%s/%s.pid", piddir, name); + + fd = open(pidFile, O_NONBLOCK | O_RDONLY, 0644); + + if (fd == -1) { + SAFE_FREE(pidFile); + return 0; + } + + ZERO_STRUCT(pidstr); + + if (read(fd, pidstr, sizeof(pidstr)-1) <= 0) { + goto noproc; + } + + ret = (pid_t)atoi(pidstr); + + if (!process_exists(ret)) { + goto noproc; + } + + if (fcntl_lock(fd,F_SETLK,0,1,F_RDLCK)) { + /* we could get the lock - it can't be a Samba process */ + goto noproc; + } + + close(fd); + SAFE_FREE(pidFile); + return ret; + + noproc: + close(fd); + unlink(pidFile); + SAFE_FREE(pidFile); + return 0; +} + +/** + * create a pid file in the pid directory. open it and leave it locked + */ +void pidfile_create(const char *piddir, const char *name) +{ + int fd; + char buf[20]; + char *pidFile; + pid_t pid; + + asprintf(&pidFile, "%s/%s.pid", piddir, name); + + pid = pidfile_pid(piddir, name); + if (pid != 0) { + DEBUG(0,("ERROR: %s is already running. File %s exists and process id %d is running.\n", + name, pidFile, (int)pid)); + exit(1); + } + + fd = open(pidFile, O_NONBLOCK | O_CREAT | O_WRONLY | O_EXCL, 0644); + if (fd == -1) { + DEBUG(0,("ERROR: can't open %s: Error was %s\n", pidFile, + strerror(errno))); + exit(1); + } + + if (fcntl_lock(fd,F_SETLK,0,1,F_WRLCK)==false) { + DEBUG(0,("ERROR: %s : fcntl lock of file %s failed. Error was %s\n", + name, pidFile, strerror(errno))); + exit(1); + } + + memset(buf, 0, sizeof(buf)); + slprintf(buf, sizeof(buf) - 1, "%u\n", (unsigned int) getpid()); + if (write(fd, buf, strlen(buf)) != (ssize_t)strlen(buf)) { + DEBUG(0,("ERROR: can't write to file %s: %s\n", + pidFile, strerror(errno))); + exit(1); + } + + /* Leave pid file open & locked for the duration... */ + SAFE_FREE(pidFile); +} diff --git a/source4/smbd/process_model.c b/source4/smbd/process_model.c new file mode 100644 index 0000000000..704e6cc7a2 --- /dev/null +++ b/source4/smbd/process_model.c @@ -0,0 +1,128 @@ +/* + 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 "smbd/process_model.h" +#include "param/param.h" + +/* + setup the events for the chosen process model +*/ +_PUBLIC_ const struct model_ops *process_model_startup(struct event_context *ev, const char *model) +{ + const struct model_ops *ops; + + ops = process_model_byname(model); + if (!ops) { + DEBUG(0,("Unknown process model '%s'\n", model)); + exit(-1); + } + + ops->model_init(ev); + + return ops; +} + +/* the list of currently registered process models */ +static struct process_model { + struct model_ops *ops; +} *models = NULL; +static int num_models; + +/* + 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 void *_ops) +{ + const struct model_ops *ops = _ops; + + if (process_model_byname(ops->name) != NULL) { + /* its already registered! */ + DEBUG(0,("PROCESS_MODEL '%s' already registered\n", + ops->name)); + return NT_STATUS_OBJECT_NAME_COLLISION; + } + + models = realloc_p(models, struct process_model, num_models+1); + if (!models) { + smb_panic("out of memory in register_process_model"); + } + + models[num_models].ops = smb_xmemdup(ops, sizeof(*ops)); + models[num_models].ops->name = smb_xstrdup(ops->name); + + num_models++; + + DEBUG(3,("PROCESS_MODEL '%s' registered\n", + ops->name)); + + return NT_STATUS_OK; +} + +_PUBLIC_ NTSTATUS process_model_init(struct loadparm_context *lp_ctx) +{ + extern NTSTATUS process_model_thread_init(void); + extern NTSTATUS process_model_standard_init(void); + extern NTSTATUS process_model_prefork_init(void); + extern NTSTATUS process_model_single_init(void); + init_module_fn static_init[] = { STATIC_process_model_MODULES }; + init_module_fn *shared_init = load_samba_modules(NULL, lp_ctx, "process_model"); + + run_init_functions(static_init); + run_init_functions(shared_init); + + talloc_free(shared_init); + + return NT_STATUS_OK; +} + +/* + return the operations structure for a named backend of the specified type +*/ +_PUBLIC_ const struct model_ops *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].ops; + } + } + + return NULL; +} + +/* + 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/smbd/process_model.h b/source4/smbd/process_model.h new file mode 100644 index 0000000000..b545212091 --- /dev/null +++ b/source4/smbd/process_model.h @@ -0,0 +1,85 @@ +/* + 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 "smbd/service.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)(struct event_context *); + + /* function to accept new connection */ + void (*accept_connection)(struct event_context *, + struct loadparm_context *, + struct socket_context *, + void (*)(struct event_context *, + struct loadparm_context *, + struct socket_context *, + struct server_id , void *), + void *); + + /* function to create a task */ + void (*new_task)(struct event_context *, + struct loadparm_context *lp_ctx, + const char *service_name, + void (*)(struct event_context *, + struct loadparm_context *, struct server_id, + void *), + void *); + + /* function to terminate a connection or task */ + void (*terminate)(struct event_context *, const char *reason); + + /* function to set a title for the connection or task */ + void (*set_title)(struct event_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(struct event_context *ev, const char *model); +const struct model_ops *process_model_byname(const char *name); +NTSTATUS register_process_model(const void *_ops); +NTSTATUS process_model_init(struct loadparm_context *lp_ctx); + +#endif /* __PROCESS_MODEL_H__ */ diff --git a/source4/smbd/process_model.m4 b/source4/smbd/process_model.m4 new file mode 100644 index 0000000000..6ee8dbf4d8 --- /dev/null +++ b/source4/smbd/process_model.m4 @@ -0,0 +1,60 @@ +dnl # Server process model subsystem + +SMB_ENABLE(process_model_thread,NO) + +################################################# +# check for pthread support +AC_MSG_CHECKING(whether to use pthreads) +AC_ARG_WITH(pthreads, +[ --with-pthreads Include pthreads (default=no) ], +[ case "$withval" in + yes) + AC_MSG_RESULT(yes) + if test x"$ac_cv_func_pread" != x"yes" -o x"$ac_cv_func_pwrite" != x"yes";then + AC_MSG_ERROR([You cannot enable threads when you don't have pread/pwrite!]) + fi + SMB_ENABLE(process_model_thread,YES) + SMB_ENABLE(PTHREAD,YES) + ;; + *) + AC_MSG_RESULT(no) + ;; + esac ], +AC_MSG_RESULT(no) +) + +SMB_EXT_LIB(PTHREAD,[-lpthread]) + +AC_MSG_CHECKING(whether to search for setproctitle support) +AC_ARG_WITH(setproctitle, +[ --with-setproctitle Search for setproctitle support (default=no)], +[ case "$withval" in + yes) + AC_MSG_RESULT(yes) + AC_CHECK_HEADERS(setproctitle.h) + AC_CHECK_FUNC(setproctitle, [], [ + AC_CHECK_LIB_EXT(setproctitle, SETPROCTITLE_LIBS, setproctitle) + ]) + AC_MSG_CHECKING(whether to use setproctitle) + if test x"$ac_cv_func_setproctitle" = x"yes" -o \ + \( x"$ac_cv_header_setproctitle_h" = x"yes" -a \ + x"$ac_cv_lib_ext_setproctitle_setproctitle" = x"yes" \) ; then + AC_MSG_RESULT(yes) + SMB_ENABLE(SETPROCTITLE, YES) + AC_DEFINE(HAVE_SETPROCTITLE,1,[Whether setproctitle() is available]) + else + AC_MSG_RESULT(no) + fi + ;; + *) + AC_MSG_RESULT(no) + ;; + esac ], +AC_MSG_RESULT(no) +) + +SMB_EXT_LIB(SETPROCTITLE, + [${SETPROCTITLE_LIBS}], + [${SETPROCTITLE_CFLAGS}], + [${SETPROCTITLE_CPPFLAGS}], + [${SETPROCTITLE_LDFLAGS}]) diff --git a/source4/smbd/process_model.mk b/source4/smbd/process_model.mk new file mode 100644 index 0000000000..5ed8471a9d --- /dev/null +++ b/source4/smbd/process_model.mk @@ -0,0 +1,50 @@ +# Server process model subsystem + +################################################ +# Start MODULE process_model_single +[MODULE::process_model_single] +INIT_FUNCTION = process_model_single_init +SUBSYSTEM = process_model +# End MODULE process_model_single +################################################ + +process_model_single_OBJ_FILES = $(smbdsrcdir)/process_single.o + +################################################ +# Start MODULE process_model_standard +[MODULE::process_model_standard] +INIT_FUNCTION = process_model_standard_init +SUBSYSTEM = process_model +PRIVATE_DEPENDENCIES = SETPROCTITLE +# End MODULE process_model_standard +################################################ + +process_model_standard_OBJ_FILES = $(smbdsrcdir)/process_standard.o + +################################################ +# Start MODULE process_model_thread +[MODULE::process_model_thread] +INIT_FUNCTION = process_model_thread_init +SUBSYSTEM = process_model +PRIVATE_DEPENDENCIES = PTHREAD +# End MODULE process_model_thread +################################################ + +process_model_thread_OBJ_FILES = $(smbdsrcdir)/process_thread.o + +################################################ +# Start MODULE process_model_prefork +[MODULE::process_model_prefork] +INIT_FUNCTION = process_model_prefork_init +SUBSYSTEM = process_model +# End MODULE process_model_thread +################################################ + +process_model_prefork_OBJ_FILES = $(smbdsrcdir)/process_prefork.o + +[SUBSYSTEM::process_model] +PRIVATE_DEPENDENCIES = LIBSAMBA-UTIL LIBSAMBA-HOSTCONFIG + +process_model_OBJ_FILES = $(smbdsrcdir)/process_model.o + +$(eval $(call proto_header_template,$(smbdsrcdir)/process_model_proto.h,$(process_model_OBJ_FILES:.o=.c))) diff --git a/source4/smbd/process_prefork.c b/source4/smbd/process_prefork.c new file mode 100644 index 0000000000..2ffb724a6c --- /dev/null +++ b/source4/smbd/process_prefork.c @@ -0,0 +1,222 @@ +/* + 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/>. +*/ + +#include "includes.h" +#include "lib/events/events.h" +#include "lib/tdb/include/tdb.h" +#include "lib/socket/socket.h" +#include "smbd/process_model.h" +#include "param/secrets.h" +#include "system/filesys.h" +#include "cluster/cluster.h" +#include "param/param.h" + +#ifdef HAVE_SETPROCTITLE +#ifdef HAVE_SETPROCTITLE_H +#include <setproctitle.h> +#endif +#else +#define setproctitle none_setproctitle +static int none_setproctitle(const char *fmt, ...) PRINTF_ATTRIBUTE(1, 2); +static int none_setproctitle(const char *fmt, ...) +{ + return 0; +} +#endif + +/* + called when the process model is selected +*/ +static void prefork_model_init(struct event_context *ev) +{ + signal(SIGCHLD, SIG_IGN); +} + +static void prefork_reload_after_fork(void) +{ + /* tdb needs special fork handling */ + if (tdb_reopen_all(1) == -1) { + DEBUG(0,("prefork_reload_after_fork: tdb_reopen_all failed.\n")); + } + + /* Ensure that the forked children do not expose identical random streams */ + set_need_random_reseed(); +} + +/* + called when a listening socket becomes readable. +*/ +static void prefork_accept_connection(struct event_context *ev, + struct loadparm_context *lp_ctx, + struct socket_context *listen_socket, + void (*new_conn)(struct event_context *, + struct loadparm_context *, struct socket_context *, + struct server_id , void *), + void *private) +{ + 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)) { + return; + } + + talloc_steal(private, connected_socket); + + new_conn(ev, lp_ctx, connected_socket, cluster_id(pid, socket_get_fd(connected_socket)), private); +} + +/* + called to create a new server task +*/ +static void prefork_new_task(struct event_context *ev, + struct loadparm_context *lp_ctx, + const char *service_name, + void (*new_task_fn)(struct event_context *, struct loadparm_context *lp_ctx, struct server_id , void *), + void *private) +{ + pid_t pid; + int i, num_children; + + struct event_context *ev2, *ev_parent; + + pid = fork(); + + if (pid != 0) { + /* parent or error code ... go back to the event loop */ + return; + } + + pid = getpid(); + + /* This is now the child code. We need a completely new event_context to work with */ + ev2 = s4_event_context_init(NULL); + + /* the service has given us a private pointer that + encapsulates the context it needs for this new connection - + everything else will be freed */ + talloc_steal(ev2, private); + + /* this will free all the listening sockets and all state that + is not associated with this new connection */ + talloc_free(ev); + + setproctitle("task %s server_id[%d]", service_name, pid); + + prefork_reload_after_fork(); + + /* setup this new connection: process will bind to it's sockets etc */ + new_task_fn(ev2, lp_ctx, cluster_id(pid, 0), private); + + num_children = lp_parm_int(lp_ctx, NULL, "prefork children", service_name, 0); + if (num_children == 0) { + + /* We don't want any kids hanging around for this one, + * let the parent do all the work */ + event_loop_wait(ev2); + + talloc_free(ev2); + exit(0); + } + + /* We are now free to spawn some child proccesses */ + + for (i=0; i < num_children; i++) { + + pid = fork(); + if (pid > 0) { + continue; + } else if (pid == -1) { + return; + } else { + pid = getpid(); + setproctitle("task %s server_id[%d]", service_name, pid); + + prefork_reload_after_fork(); + + /* 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 */ + event_loop_wait(ev2); + + talloc_free(ev2); + exit(0); + } + } + + /* Don't listen on the sockets we just gave to the children */ + talloc_free(ev2); + + /* But we need a events system to handle reaping children */ + ev_parent = s4_event_context_init(NULL); + + /* TODO: Handle some events... */ + + /* 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 */ + event_loop_wait(ev_parent); + + talloc_free(ev_parent); + exit(0); + +} + + +/* called when a task goes down */ +_NORETURN_ static void prefork_terminate(struct event_context *ev, const char *reason) +{ + DEBUG(2,("prefork_terminate: reason[%s]\n",reason)); +} + +/* called to set a title of a task or connection */ +static void prefork_set_title(struct event_context *ev, const char *title) +{ + if (title) { + setproctitle("%s", title); + } else { + setproctitle(NULL); + } +} + +static const struct model_ops prefork_ops = { + .name = "prefork", + .model_init = prefork_model_init, + .accept_connection = prefork_accept_connection, + .new_task = prefork_new_task, + .terminate = prefork_terminate, + .set_title = prefork_set_title, +}; + +/* + initialise the prefork process model, registering ourselves with the process model subsystem + */ +NTSTATUS process_model_prefork_init(void) +{ + return register_process_model(&prefork_ops); +} diff --git a/source4/smbd/process_single.c b/source4/smbd/process_single.c new file mode 100644 index 0000000000..a7a27ef9e0 --- /dev/null +++ b/source4/smbd/process_single.c @@ -0,0 +1,124 @@ +/* + 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 "smbd/process_model.h" +#include "system/filesys.h" +#include "cluster/cluster.h" + +/* + called when the process model is selected +*/ +static void single_model_init(struct event_context *ev) +{ +} + +/* + called when a listening socket becomes readable. +*/ +static void single_accept_connection(struct event_context *ev, + struct loadparm_context *lp_ctx, + struct socket_context *listen_socket, + void (*new_conn)(struct event_context *, + struct loadparm_context *, + struct socket_context *, + struct server_id , void *), + void *private) +{ + NTSTATUS status; + struct socket_context *connected_socket; + + /* accept an incoming connection. */ + status = socket_accept(listen_socket, &connected_socket); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0,("single_accept_connection: accept: %s\n", nt_errstr(status))); + /* this looks strange, but is correct. + + We can only be here if woken up from select, due to + an incomming 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, connected_socket); + + /* The cluster_id(0, fd) cannot collide with the incrementing + * task below, as the first component is 0, not 1 */ + new_conn(ev, lp_ctx, connected_socket, + cluster_id(0, socket_get_fd(connected_socket)), private); +} + +/* + called to startup a new task +*/ +static void single_new_task(struct event_context *ev, + struct loadparm_context *lp_ctx, + const char *service_name, + void (*new_task)(struct event_context *, struct loadparm_context *, struct server_id, void *), + void *private) +{ + static uint32_t taskid = 0; + + /* We use 1 so we cannot collide in with cluster ids generated + * in the accept connection above, and unlikly to collide with + * PIDs from process modal standard (don't run samba as + * init) */ + new_task(ev, lp_ctx, cluster_id(1, taskid++), private); +} + + +/* called when a task goes down */ +static void single_terminate(struct event_context *ev, const char *reason) +{ + DEBUG(2,("single_terminate: reason[%s]\n",reason)); +} + +/* called to set a title of a task or connection */ +static void single_set_title(struct event_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 = single_terminate, + .set_title = single_set_title, +}; + +/* + initialise the single process model, registering ourselves with the + process model subsystem + */ +NTSTATUS process_model_single_init(void) +{ + return register_process_model(&single_ops); +} diff --git a/source4/smbd/process_standard.c b/source4/smbd/process_standard.c new file mode 100644 index 0000000000..3f2936bb26 --- /dev/null +++ b/source4/smbd/process_standard.c @@ -0,0 +1,238 @@ +/* + 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 "lib/tdb/include/tdb.h" +#include "lib/socket/socket.h" +#include "smbd/process_model.h" +#include "param/secrets.h" +#include "system/filesys.h" +#include "cluster/cluster.h" +#include "param/param.h" + +#ifdef HAVE_SETPROCTITLE +#ifdef HAVE_SETPROCTITLE_H +#include <setproctitle.h> +#endif +#else +#define setproctitle none_setproctitle +static int none_setproctitle(const char *fmt, ...) PRINTF_ATTRIBUTE(1, 2); +static int none_setproctitle(const char *fmt, ...) +{ + return 0; +} +#endif + +/* + called when the process model is selected +*/ +static void standard_model_init(struct event_context *ev) +{ + signal(SIGCHLD, SIG_IGN); +} + +/* + called when a listening socket becomes readable. +*/ +static void standard_accept_connection(struct event_context *ev, + struct loadparm_context *lp_ctx, + struct socket_context *sock, + void (*new_conn)(struct event_context *, + struct loadparm_context *, struct socket_context *, + struct server_id , void *), + void *private) +{ + NTSTATUS status; + struct socket_context *sock2; + pid_t pid; + struct event_context *ev2; + struct socket_address *c, *s; + + /* accept an incoming connection. */ + status = socket_accept(sock, &sock2); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0,("standard_accept_connection: accept: %s\n", + nt_errstr(status))); + /* 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; + } + + pid = fork(); + + if (pid != 0) { + /* parent or error code ... */ + talloc_free(sock2); + /* go back to the event loop */ + return; + } + + pid = getpid(); + + /* This is now the child code. We need a completely new event_context to work with */ + ev2 = s4_event_context_init(NULL); + + /* the service has given us a private pointer that + encapsulates the context it needs for this new connection - + everything else will be freed */ + talloc_steal(ev2, private); + talloc_steal(private, sock2); + + /* this will free all the listening sockets and all state that + is not associated with this new connection */ + talloc_free(sock); + talloc_free(ev); + + /* we don't care if the dup fails, as its only a select() + speed optimisation */ + socket_dup(sock2); + + /* tdb needs special fork handling */ + if (tdb_reopen_all(1) == -1) { + DEBUG(0,("standard_accept_connection: tdb_reopen_all failed.\n")); + } + + /* Ensure that the forked children do not expose identical random streams */ + set_need_random_reseed(); + + /* setup the process title */ + c = socket_get_peer_addr(sock2, ev2); + s = socket_get_my_addr(sock2, ev2); + if (s && c) { + setproctitle("conn c[%s:%u] s[%s:%u] server_id[%d]", + c->addr, c->port, s->addr, s->port, pid); + } + talloc_free(c); + talloc_free(s); + + /* setup this new connection. Cluster ID is PID based for this process modal */ + new_conn(ev2, lp_ctx, sock2, cluster_id(pid, 0), private); + + /* 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 */ + event_loop_wait(ev2); + + talloc_free(ev2); + exit(0); +} + +/* + called to create a new server task +*/ +static void standard_new_task(struct event_context *ev, + struct loadparm_context *lp_ctx, + const char *service_name, + void (*new_task)(struct event_context *, struct loadparm_context *lp_ctx, struct server_id , void *), + void *private) +{ + pid_t pid; + struct event_context *ev2; + + pid = fork(); + + if (pid != 0) { + /* parent or error code ... go back to the event loop */ + return; + } + + pid = getpid(); + + /* This is now the child code. We need a completely new event_context to work with */ + ev2 = s4_event_context_init(NULL); + + /* the service has given us a private pointer that + encapsulates the context it needs for this new connection - + everything else will be freed */ + talloc_steal(ev2, private); + + /* this will free all the listening sockets and all state that + is not associated with this new connection */ + talloc_free(ev); + + /* tdb needs special fork handling */ + if (tdb_reopen_all(1) == -1) { + DEBUG(0,("standard_accept_connection: tdb_reopen_all failed.\n")); + } + + /* Ensure that the forked children do not expose identical random streams */ + set_need_random_reseed(); + + setproctitle("task %s server_id[%d]", service_name, pid); + + /* setup this new task. Cluster ID is PID based for this process modal */ + new_task(ev2, lp_ctx, cluster_id(pid, 0), private); + + /* 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 */ + event_loop_wait(ev2); + + talloc_free(ev2); + exit(0); +} + + +/* called when a task goes down */ +_NORETURN_ static void standard_terminate(struct event_context *ev, const char *reason) +{ + DEBUG(2,("standard_terminate: reason[%s]\n",reason)); + + /* this reload_charcnv() has the effect of freeing the iconv context memory, + which makes leak checking easier */ + reload_charcnv(global_loadparm); + + talloc_free(ev); + + /* terminate this process */ + exit(0); +} + +/* called to set a title of a task or connection */ +static void standard_set_title(struct event_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 = standard_terminate, + .set_title = standard_set_title, +}; + +/* + initialise the standard process model, registering ourselves with the process model subsystem + */ +NTSTATUS process_model_standard_init(void) +{ + return register_process_model(&standard_ops); +} diff --git a/source4/smbd/process_thread.c b/source4/smbd/process_thread.c new file mode 100644 index 0000000000..540d956420 --- /dev/null +++ b/source4/smbd/process_thread.c @@ -0,0 +1,572 @@ +/* + Unix SMB/CIFS implementation. + + thread model: standard (1 thread per client connection) + + Copyright (C) Andrew Tridgell 2003-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 "version.h" +#include <pthread.h> +#ifdef HAVE_BACKTRACE +#include <execinfo.h> +#endif +#include "system/wait.h" +#include "system/filesys.h" +#include "lib/events/events.h" +#include "lib/util/dlinklist.h" +#include "lib/util/mutex.h" +#include "smbd/process_model.h" + +static pthread_key_t title_key; + +struct new_conn_state { + struct event_context *ev; + struct socket_context *sock; + struct loadparm_context *lp_ctx; + void (*new_conn)(struct event_context *, struct loadparm_context *lp_ctx, struct socket_context *, uint32_t , void *); + void *private; +}; + +static void *thread_connection_fn(void *thread_parm) +{ + struct new_conn_state *new_conn = talloc_get_type(thread_parm, struct new_conn_state); + + new_conn->new_conn(new_conn->ev, new_conn->lp_ctx, new_conn->sock, pthread_self(), new_conn->private); + + /* run this connection from here */ + event_loop_wait(new_conn->ev); + + talloc_free(new_conn); + + return NULL; +} + +/* + called when a listening socket becomes readable +*/ +static void thread_accept_connection(struct event_context *ev, + struct loadparm_context *lp_ctx, + struct socket_context *sock, + void (*new_conn)(struct event_context *, + struct loadparm_context *, + struct socket_context *, + uint32_t , void *), + void *private) +{ + NTSTATUS status; + int rc; + pthread_t thread_id; + pthread_attr_t thread_attr; + struct new_conn_state *state; + struct event_context *ev2; + + ev2 = s4_event_context_init(ev); + if (ev2 == NULL) return; + + state = talloc(ev2, struct new_conn_state); + if (state == NULL) { + talloc_free(ev2); + return; + } + + state->new_conn = new_conn; + state->private = private; + state->lp_ctx = lp_ctx; + state->ev = ev2; + + /* accept an incoming connection. */ + status = socket_accept(sock, &state->sock); + if (!NT_STATUS_IS_OK(status)) { + talloc_free(ev2); + /* 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(state, state->sock); + + pthread_attr_init(&thread_attr); + pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED); + rc = pthread_create(&thread_id, &thread_attr, thread_connection_fn, state); + pthread_attr_destroy(&thread_attr); + if (rc == 0) { + DEBUG(4,("accept_connection_thread: created thread_id=%lu for fd=%d\n", + (unsigned long int)thread_id, socket_get_fd(sock))); + } else { + DEBUG(0,("accept_connection_thread: thread create failed for fd=%d, rc=%d\n", socket_get_fd(sock), rc)); + talloc_free(ev2); + } +} + + +struct new_task_state { + struct event_context *ev; + struct loadparm_context *lp_ctx; + void (*new_task)(struct event_context *, struct loadparm_context *, + uint32_t , void *); + void *private; +}; + +static void *thread_task_fn(void *thread_parm) +{ + struct new_task_state *new_task = talloc_get_type(thread_parm, struct new_task_state); + + new_task->new_task(new_task->ev, new_task->lp_ctx, pthread_self(), + new_task->private); + + /* run this connection from here */ + event_loop_wait(new_task->ev); + + talloc_free(new_task); + + return NULL; +} + +/* + called when a new task is needed +*/ +static void thread_new_task(struct event_context *ev, + struct loadparm_context *lp_ctx, + const char *service_name, + void (*new_task)(struct event_context *, + struct loadparm_context *, + uint32_t , void *), + void *private) +{ + int rc; + pthread_t thread_id; + pthread_attr_t thread_attr; + struct new_task_state *state; + struct event_context *ev2; + + ev2 = s4_event_context_init(ev); + if (ev2 == NULL) return; + + state = talloc(ev2, struct new_task_state); + if (state == NULL) { + talloc_free(ev2); + return; + } + + state->new_task = new_task; + state->lp_ctx = lp_ctx; + state->private = private; + state->ev = ev2; + + pthread_attr_init(&thread_attr); + pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED); + rc = pthread_create(&thread_id, &thread_attr, thread_task_fn, state); + pthread_attr_destroy(&thread_attr); + if (rc == 0) { + DEBUG(4,("thread_new_task: created %s thread_id=%lu\n", + service_name, (unsigned long int)thread_id)); + } else { + DEBUG(0,("thread_new_task: thread create for %s failed rc=%d\n", service_name, rc)); + talloc_free(ev2); + } +} + +/* called when a task goes down */ +static void thread_terminate(struct event_context *event_ctx, const char *reason) +{ + DEBUG(10,("thread_terminate: reason[%s]\n",reason)); + + talloc_free(event_ctx); + + /* terminate this thread */ + pthread_exit(NULL); /* thread cleanup routine will do actual cleanup */ +} + +/* called to set a title of a task or connection */ +static void thread_set_title(struct event_context *ev, const char *title) +{ + char *old_title; + char *new_title; + + old_title = pthread_getspecific(title_key); + talloc_free(old_title); + + new_title = talloc_strdup(ev, title); + pthread_setspecific(title_key, new_title); +} + +/* + mutex init function for thread model +*/ +static int thread_mutex_init(smb_mutex_t *mutex, const char *name) +{ + pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER; + mutex->mutex = memdup(&m, sizeof(m)); + if (! mutex->mutex) { + errno = ENOMEM; + return -1; + } + return pthread_mutex_init((pthread_mutex_t *)mutex->mutex, NULL); +} + +/* + mutex destroy function for thread model +*/ +static int thread_mutex_destroy(smb_mutex_t *mutex, const char *name) +{ + return pthread_mutex_destroy((pthread_mutex_t *)mutex->mutex); +} + +static void mutex_start_timer(struct timeval *tp1) +{ + gettimeofday(tp1,NULL); +} + +static double mutex_end_timer(struct timeval tp1) +{ + struct timeval tp2; + gettimeofday(&tp2,NULL); + return((tp2.tv_sec - tp1.tv_sec) + + (tp2.tv_usec - tp1.tv_usec)*1.0e-6); +} + +/* + mutex lock function for thread model +*/ +static int thread_mutex_lock(smb_mutex_t *mutexP, const char *name) +{ + pthread_mutex_t *mutex = (pthread_mutex_t *)mutexP->mutex; + int rc; + double t; + struct timeval tp1; + /* Test below is ONLY for debugging */ + if ((rc = pthread_mutex_trylock(mutex))) { + if (rc == EBUSY) { + mutex_start_timer(&tp1); + printf("mutex lock: thread %d, lock %s not available\n", + (uint32_t)pthread_self(), name); + print_suspicious_usage("mutex_lock", name); + pthread_mutex_lock(mutex); + t = mutex_end_timer(tp1); + printf("mutex lock: thread %d, lock %s now available, waited %g seconds\n", + (uint32_t)pthread_self(), name, t); + return 0; + } + printf("mutex lock: thread %d, lock %s failed rc=%d\n", + (uint32_t)pthread_self(), name, rc); + SMB_ASSERT(errno == 0); /* force error */ + } + return 0; +} + +/* + mutex unlock for thread model +*/ +static int thread_mutex_unlock(smb_mutex_t *mutex, const char *name) +{ + return pthread_mutex_unlock((pthread_mutex_t *)mutex->mutex); +} + +/***************************************************************** + Read/write lock routines. +*****************************************************************/ +/* + rwlock init function for thread model +*/ +static int thread_rwlock_init(smb_rwlock_t *rwlock, const char *name) +{ + pthread_rwlock_t m = PTHREAD_RWLOCK_INITIALIZER; + rwlock->rwlock = memdup(&m, sizeof(m)); + if (! rwlock->rwlock) { + errno = ENOMEM; + return -1; + } + return pthread_rwlock_init((pthread_rwlock_t *)rwlock->rwlock, NULL); +} + +/* + rwlock destroy function for thread model +*/ +static int thread_rwlock_destroy(smb_rwlock_t *rwlock, const char *name) +{ + return pthread_rwlock_destroy((pthread_rwlock_t *)rwlock->rwlock); +} + +/* + rwlock lock for read function for thread model +*/ +static int thread_rwlock_lock_read(smb_rwlock_t *rwlockP, const char *name) +{ + pthread_rwlock_t *rwlock = (pthread_rwlock_t *)rwlockP->rwlock; + int rc; + double t; + struct timeval tp1; + /* Test below is ONLY for debugging */ + if ((rc = pthread_rwlock_tryrdlock(rwlock))) { + if (rc == EBUSY) { + mutex_start_timer(&tp1); + printf("rwlock lock_read: thread %d, lock %s not available\n", + (uint32_t)pthread_self(), name); + print_suspicious_usage("rwlock_lock_read", name); + pthread_rwlock_rdlock(rwlock); + t = mutex_end_timer(tp1); + printf("rwlock lock_read: thread %d, lock %s now available, waited %g seconds\n", + (uint32_t)pthread_self(), name, t); + return 0; + } + printf("rwlock lock_read: thread %d, lock %s failed rc=%d\n", + (uint32_t)pthread_self(), name, rc); + SMB_ASSERT(errno == 0); /* force error */ + } + return 0; +} + +/* + rwlock lock for write function for thread model +*/ +static int thread_rwlock_lock_write(smb_rwlock_t *rwlockP, const char *name) +{ + pthread_rwlock_t *rwlock = (pthread_rwlock_t *)rwlockP->rwlock; + int rc; + double t; + struct timeval tp1; + /* Test below is ONLY for debugging */ + if ((rc = pthread_rwlock_trywrlock(rwlock))) { + if (rc == EBUSY) { + mutex_start_timer(&tp1); + printf("rwlock lock_write: thread %d, lock %s not available\n", + (uint32_t)pthread_self(), name); + print_suspicious_usage("rwlock_lock_write", name); + pthread_rwlock_wrlock(rwlock); + t = mutex_end_timer(tp1); + printf("rwlock lock_write: thread %d, lock %s now available, waited %g seconds\n", + (uint32_t)pthread_self(), name, t); + return 0; + } + printf("rwlock lock_write: thread %d, lock %s failed rc=%d\n", + (uint32_t)pthread_self(), name, rc); + SMB_ASSERT(errno == 0); /* force error */ + } + return 0; +} + + +/* + rwlock unlock for thread model +*/ +static int thread_rwlock_unlock(smb_rwlock_t *rwlock, const char *name) +{ + return pthread_rwlock_unlock((pthread_rwlock_t *)rwlock->rwlock); +} + +/***************************************************************** + Log suspicious usage (primarily for possible thread-unsafe behavior). +*****************************************************************/ +static void thread_log_suspicious_usage(const char* from, const char* info) +{ + DEBUG(1,("log_suspicious_usage: from %s info='%s'\n", from, info)); +#ifdef HAVE_BACKTRACE + { + void *addresses[10]; + int num_addresses = backtrace(addresses, 8); + char **bt_symbols = backtrace_symbols(addresses, num_addresses); + int i; + + if (bt_symbols) { + for (i=0; i<num_addresses; i++) { + DEBUG(1,("log_suspicious_usage: %s%s\n", DEBUGTAB(1), bt_symbols[i])); + } + free(bt_symbols); + } + } +#endif +} + +/***************************************************************** + Log suspicious usage to stdout (primarily for possible thread-unsafe behavior. + Used in mutex code where DEBUG calls would cause recursion. +*****************************************************************/ +static void thread_print_suspicious_usage(const char* from, const char* info) +{ + printf("log_suspicious_usage: from %s info='%s'\n", from, info); +#ifdef HAVE_BACKTRACE + { + void *addresses[10]; + int num_addresses = backtrace(addresses, 8); + char **bt_symbols = backtrace_symbols(addresses, num_addresses); + int i; + + if (bt_symbols) { + for (i=0; i<num_addresses; i++) { + printf("log_suspicious_usage: %s%s\n", DEBUGTAB(1), bt_symbols[i]); + } + free(bt_symbols); + } + } +#endif +} + +static uint32_t thread_get_task_id(void) +{ + return (uint32_t)pthread_self(); +} + +static void thread_log_task_id(int fd) +{ + char *s= NULL; + + asprintf(&s, "thread[%u][%s]:\n", + (uint32_t)pthread_self(), + (const char *)pthread_getspecific(title_key)); + if (!s) return; + write(fd, s, strlen(s)); + free(s); +} + +/**************************************************************************** +catch serious errors +****************************************************************************/ +static void thread_sig_fault(int sig) +{ + DEBUG(0,("===============================================================\n")); + DEBUG(0,("TERMINAL ERROR: Recursive signal %d in thread [%u][%s] (%s)\n", + sig,(uint32_t)pthread_self(), + (const char *)pthread_getspecific(title_key), + SAMBA_VERSION_STRING)); + DEBUG(0,("===============================================================\n")); + exit(1); /* kill the whole server for now */ +} + +/******************************************************************* +setup our recursive fault handlers +********************************************************************/ +static void thread_fault_setup(void) +{ +#ifdef SIGSEGV + CatchSignal(SIGSEGV,SIGNAL_CAST thread_sig_fault); +#endif +#ifdef SIGBUS + CatchSignal(SIGBUS,SIGNAL_CAST thread_sig_fault); +#endif +#ifdef SIGABRT + CatchSignal(SIGABRT,SIGNAL_CAST thread_sig_fault); +#endif +} + +/******************************************************************* +report a fault in a thread +********************************************************************/ +static void thread_fault_handler(int sig) +{ + static int counter; + + /* try to catch recursive faults */ + thread_fault_setup(); + + counter++; /* count number of faults that have occurred */ + + DEBUG(0,("===============================================================\n")); + DEBUG(0,("INTERNAL ERROR: Signal %d in thread [%u] [%s] (%s)\n", + sig,(uint32_t)pthread_self(), + (const char *)pthread_getspecific(title_key), + SAMBA_VERSION_STRING)); + DEBUG(0,("Please read the file BUGS.txt in the distribution\n")); + DEBUG(0,("===============================================================\n")); +#ifdef HAVE_BACKTRACE + { + void *addresses[10]; + int num_addresses = backtrace(addresses, 8); + char **bt_symbols = backtrace_symbols(addresses, num_addresses); + int i; + + if (bt_symbols) { + for (i=0; i<num_addresses; i++) { + DEBUG(1,("fault_report: %s%s\n", DEBUGTAB(1), bt_symbols[i])); + } + free(bt_symbols); + } + } +#endif + pthread_exit(NULL); /* terminate failing thread only */ +} + +/* + called when the process model is selected +*/ +static void thread_model_init(struct event_context *event_context) +{ + struct mutex_ops m_ops; + struct debug_ops d_ops; + + ZERO_STRUCT(m_ops); + ZERO_STRUCT(d_ops); + + pthread_key_create(&title_key, NULL); + pthread_setspecific(title_key, talloc_strdup(event_context, "")); + + /* register mutex/rwlock handlers */ + m_ops.mutex_init = thread_mutex_init; + m_ops.mutex_lock = thread_mutex_lock; + m_ops.mutex_unlock = thread_mutex_unlock; + m_ops.mutex_destroy = thread_mutex_destroy; + + m_ops.rwlock_init = thread_rwlock_init; + m_ops.rwlock_lock_write = thread_rwlock_lock_write; + m_ops.rwlock_lock_read = thread_rwlock_lock_read; + m_ops.rwlock_unlock = thread_rwlock_unlock; + m_ops.rwlock_destroy = thread_rwlock_destroy; + + register_mutex_handlers("thread", &m_ops); + + register_fault_handler("thread", thread_fault_handler); + + d_ops.log_suspicious_usage = thread_log_suspicious_usage; + d_ops.print_suspicious_usage = thread_print_suspicious_usage; + d_ops.get_task_id = thread_get_task_id; + d_ops.log_task_id = thread_log_task_id; + + register_debug_handlers("thread", &d_ops); +} + + +static const struct model_ops thread_ops = { + .name = "thread", + .model_init = thread_model_init, + .accept_connection = thread_accept_connection, + .new_task = thread_new_task, + .terminate = thread_terminate, + .set_title = thread_set_title, +}; + +/* + initialise the thread process model, registering ourselves with the model subsystem + */ +NTSTATUS process_model_thread_init(void) +{ + NTSTATUS ret; + + /* register ourselves with the PROCESS_MODEL subsystem. */ + ret = register_process_model(&thread_ops); + if (!NT_STATUS_IS_OK(ret)) { + DEBUG(0,("Failed to register process_model 'thread'!\n")); + return ret; + } + + return ret; +} diff --git a/source4/smbd/server.c b/source4/smbd/server.c new file mode 100644 index 0000000000..5bd5568913 --- /dev/null +++ b/source4/smbd/server.c @@ -0,0 +1,372 @@ +/* + 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/popt_common.h" +#include "system/dir.h" +#include "system/filesys.h" +#include "ldb/include/ldb.h" +#include "registry/registry.h" +#include "ntvfs/ntvfs.h" +#include "ntptr/ntptr.h" +#include "auth/gensec/gensec.h" +#include "smbd/process_model.h" +#include "smbd/service.h" +#include "param/secrets.h" +#include "smbd/pidfile.h" +#include "cluster/ctdb/ctdb_cluster.h" +#include "param/param.h" + +/* + 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) { + DEBUG(0,("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); + + path = smbd_tmp_path(mem_ctx, lp_ctx, NULL); + + recursive_delete(path); + talloc_free(mem_ctx); +} + +static void sig_hup(int sig) +{ + debug_schedule_reopen_logs(); +} + +static void sig_term(int sig) +{ +#if HAVE_GETPGRP + static int done_sigterm; + if (done_sigterm == 0 && getpgrp() == getpid()) { + DEBUG(0,("SIGTERM: killing children\n")); + done_sigterm = 1; + kill(-getpgrp(), SIGTERM); + } +#endif + exit(0); +} + +/* + 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 recieve 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 event_context *event_ctx, struct fd_event *fde, + uint16_t flags, void *private) +{ + const char *binary_name = (const char *)private; + uint8_t c; + if (read(0, &c, 1) == 0) { + DEBUG(0,("%s: EOF on stdin - terminating\n", binary_name)); +#if HAVE_GETPGRP + if (getpgrp() == getpid()) { + kill(-getpgrp(), SIGTERM); + } +#endif + exit(0); + } +} + +/* + die if the user selected maximum runtime is exceeded +*/ +_NORETURN_ static void max_runtime_handler(struct event_context *ev, + struct timed_event *te, + struct timeval t, void *private) +{ + const char *binary_name = (const char *)private; + DEBUG(0,("%s: maximum runtime exceeded - terminating\n", binary_name)); + exit(0); +} + +/* + main server. +*/ +static int binary_smbd_main(const char *binary_name, int argc, const char *argv[]) +{ + bool opt_daemon = false; + bool opt_interactive = false; + int opt; + poptContext pc; + extern NTSTATUS server_service_wrepl_init(void); + extern NTSTATUS server_service_kdc_init(void); + extern NTSTATUS server_service_ldap_init(void); + extern NTSTATUS server_service_web_init(void); + extern NTSTATUS server_service_ldap_init(void); + extern NTSTATUS server_service_winbind_init(void); + extern NTSTATUS server_service_nbtd_init(void); + extern NTSTATUS server_service_auth_init(void); + extern NTSTATUS server_service_cldapd_init(void); + extern NTSTATUS server_service_smb_init(void); + extern NTSTATUS server_service_drepl_init(void); + extern NTSTATUS server_service_rpc_init(void); + extern NTSTATUS server_service_ntp_signd_init(void); + init_module_fn static_init[] = { STATIC_smbd_MODULES }; + init_module_fn *shared_init; + struct event_context *event_ctx; + uint16_t stdin_event_flags; + NTSTATUS status; + const char *model = "standard"; + int max_runtime = 0; + enum { + OPT_DAEMON = 1000, + OPT_INTERACTIVE, + OPT_PROCESS_MODEL + }; + struct poptOption long_options[] = { + POPT_AUTOHELP + {"daemon", 'D', POPT_ARG_NONE, NULL, OPT_DAEMON, + "Become a daemon (default)", NULL }, + {"interactive", 'i', POPT_ARG_NONE, NULL, OPT_INTERACTIVE, + "Run interactive (not a daemon)", NULL}, + {"model", 'M', POPT_ARG_STRING, NULL, OPT_PROCESS_MODEL, + "Select process model", "MODEL"}, + {"maximum-runtime",0, POPT_ARG_INT, &max_runtime, 0, + "set maximum runtime of the server process, till autotermination", "seconds"}, + POPT_COMMON_SAMBA + POPT_COMMON_VERSION + { NULL } + }; + + pc = poptGetContext(binary_name, argc, argv, long_options, 0); + while((opt = poptGetNextOpt(pc)) != -1) { + switch(opt) { + case OPT_DAEMON: + opt_daemon = true; + break; + case OPT_INTERACTIVE: + opt_interactive = true; + break; + case OPT_PROCESS_MODEL: + model = poptGetOptArg(pc); + break; + default: + fprintf(stderr, "\nInvalid option %s: %s\n\n", + poptBadOption(pc, 0), poptStrerror(opt)); + poptPrintUsage(pc, stderr, 0); + exit(1); + } + } + + if (opt_daemon && opt_interactive) { + fprintf(stderr,"\nERROR: " + "Option -i|--interactive is not allowed together with -D|--daemon\n\n"); + poptPrintUsage(pc, stderr, 0); + exit(1); + } else if (!opt_interactive) { + /* default is --daemon */ + opt_daemon = true; + } + + poptFreeContext(pc); + + setup_logging(binary_name, opt_interactive?DEBUG_STDOUT:DEBUG_FILE); + setup_signals(); + + /* we want total control over the permissions on created files, + so set our umask to 0 */ + umask(0); + + DEBUG(0,("%s version %s started.\n", binary_name, SAMBA_VERSION_STRING)); + DEBUGADD(0,("Copyright Andrew Tridgell and the Samba Team 1992-2008\n")); + + 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))); + exit(1); + } + + if (opt_daemon) { + DEBUG(3,("Becoming a daemon.\n")); + become_daemon(true); + } + + cleanup_tmp_files(cmdline_lp_ctx); + + if (!directory_exist(lp_lockdir(cmdline_lp_ctx))) { + mkdir(lp_lockdir(cmdline_lp_ctx), 0755); + } + + pidfile_create(lp_piddir(cmdline_lp_ctx), binary_name); + + /* Do *not* remove this, until you have removed + * passdb/secrets.c, and proved that Samba still builds... */ + /* Setup the SECRETS subsystem */ + if (secrets_init(talloc_autofree_context(), cmdline_lp_ctx) == NULL) { + exit(1); + } + + gensec_init(cmdline_lp_ctx); /* FIXME: */ + + ntptr_init(cmdline_lp_ctx); /* FIXME: maybe run this in the initialization function + of the spoolss RPC server instead? */ + + ntvfs_init(cmdline_lp_ctx); /* FIXME: maybe run this in the initialization functions + of the SMB[,2] server instead? */ + + process_model_init(cmdline_lp_ctx); + + shared_init = load_samba_modules(NULL, cmdline_lp_ctx, "service"); + + run_init_functions(static_init); + run_init_functions(shared_init); + + talloc_free(shared_init); + + /* the event context is the top level structure in smbd. Everything else + should hang off that */ + event_ctx = s4_event_context_init(talloc_autofree_context()); + + if (event_ctx == NULL) { + DEBUG(0,("Initializing event context failed\n")); + return 1; + } + + /* initialise clustering if needed */ + cluster_ctdb_init(cmdline_lp_ctx, event_ctx, model); + + if (opt_interactive) { + /* terminate when stdin goes away */ + stdin_event_flags = EVENT_FD_READ; + } else { + /* stay alive forever */ + stdin_event_flags = 0; + } + + /* catch EOF on stdin */ +#ifdef SIGTTIN + signal(SIGTTIN, SIG_IGN); +#endif + event_add_fd(event_ctx, event_ctx, 0, stdin_event_flags, + server_stdin_handler, + discard_const(binary_name)); + + if (max_runtime) { + event_add_timed(event_ctx, event_ctx, + timeval_current_ofs(max_runtime, 0), + max_runtime_handler, + discard_const(binary_name)); + } + + DEBUG(0,("%s: using '%s' process model\n", binary_name, model)); + status = server_service_startup(event_ctx, cmdline_lp_ctx, model, + lp_server_services(cmdline_lp_ctx)); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0,("Starting Services failed - %s\n", nt_errstr(status))); + return 1; + } + + /* wait for events - this is where smbd sits for most of its + life */ + event_loop_wait(event_ctx); + + /* as everything hangs off this event context, freeing it + should initiate a clean shutdown of all services */ + talloc_free(event_ctx); + + return 0; +} + + int main(int argc, const char *argv[]) +{ + return binary_smbd_main("smbd", argc, argv); +} diff --git a/source4/smbd/service.c b/source4/smbd/service.c new file mode 100644 index 0000000000..2b1fcc4bd8 --- /dev/null +++ b/source4/smbd/service.c @@ -0,0 +1,104 @@ +/* + 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 "smbd/process_model.h" + +/* + a linked list of registered servers +*/ +static struct registered_server { + struct registered_server *next, *prev; + const char *service_name; + void (*task_init)(struct task_server *); +} *registered_servers; + +/* + register a server service. +*/ +NTSTATUS register_server_service(const char *name, + void (*task_init)(struct task_server *)) +{ + struct registered_server *srv; + srv = talloc(talloc_autofree_context(), struct registered_server); + NT_STATUS_HAVE_NO_MEMORY(srv); + srv->service_name = name; + srv->task_init = task_init; + DLIST_ADD_END(registered_servers, srv, struct registered_server *); + return NT_STATUS_OK; +} + + +/* + initialise a server service +*/ +static NTSTATUS server_service_init(const char *name, + struct event_context *event_context, + struct loadparm_context *lp_ctx, + const struct model_ops *model_ops) +{ + 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->task_init); + } + } + return NT_STATUS_INVALID_SYSTEM_SERVICE; +} + + +/* + startup all of our server services +*/ +NTSTATUS server_service_startup(struct event_context *event_ctx, + struct loadparm_context *lp_ctx, + const char *model, const char **server_services) +{ + int i; + const struct model_ops *model_ops; + + if (!server_services) { + DEBUG(0,("server_service_startup: no endpoint servers configured\n")); + return NT_STATUS_INVALID_PARAMETER; + } + + model_ops = process_model_startup(event_ctx, model); + if (!model_ops) { + DEBUG(0,("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); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0,("Failed to start service '%s' - %s\n", + server_services[i], nt_errstr(status))); + } + NT_STATUS_NOT_OK_RETURN(status); + } + + return NT_STATUS_OK; +} diff --git a/source4/smbd/service.h b/source4/smbd/service.h new file mode 100644 index 0000000000..23a9e6315b --- /dev/null +++ b/source4/smbd/service.h @@ -0,0 +1,30 @@ +/* + 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 "smbd/service_stream.h" +#include "smbd/service_task.h" +#include "smbd/service_proto.h" + +#endif /* __SERVICE_H__ */ diff --git a/source4/smbd/service_stream.c b/source4/smbd/service_stream.c new file mode 100644 index 0000000000..f27560f6ee --- /dev/null +++ b/source4/smbd/service_stream.c @@ -0,0 +1,352 @@ +/* + 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 "process_model.h" +#include "lib/events/events.h" +#include "lib/socket/socket.h" +#include "smbd/service.h" +#include "smbd/service_stream.h" +#include "lib/messaging/irpc.h" +#include "cluster/cluster.h" +#include "param/param.h" + +/* the range of ports to try for dcerpc over tcp endpoints */ +#define SERVER_TCP_LOW_PORT 1024 +#define SERVER_TCP_HIGH_PORT 1300 + +/* 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 event_context *event_ctx; + const struct model_ops *model_ops; + struct socket_context *sock; + void *private; +}; + + +/* + close the socket and shutdown a stream_connection +*/ +void stream_terminate_connection(struct stream_connection *srv_conn, const char *reason) +{ + struct event_context *event_ctx = srv_conn->event.ctx; + const struct model_ops *model_ops = srv_conn->model_ops; + + if (!reason) reason = "unknown 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... + */ + event_set_fd_flags(srv_conn->event.fde, 0); + return; + } + + talloc_free(srv_conn->event.fde); + srv_conn->event.fde = NULL; + talloc_free(srv_conn); + model_ops->terminate(event_ctx, reason); +} + +/** + 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 & EVENT_FD_WRITE) { + conn->ops->send_handler(conn, flags); + } else if (flags & EVENT_FD_READ) { + conn->ops->recv_handler(conn, flags); + } + conn->processing--; + + if (conn->terminate) { + stream_terminate_connection(conn, conn->terminate); + } +} + +static void stream_io_handler_fde(struct event_context *ev, struct fd_event *fde, + uint16_t flags, void *private) +{ + struct stream_connection *conn = talloc_get_type(private, + struct stream_connection); + stream_io_handler(conn, flags); +} + +void stream_io_handler_callback(void *private, uint16_t flags) +{ + struct stream_connection *conn = talloc_get_type(private, + 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 event_context *ev, + struct loadparm_context *lp_ctx, + const struct model_ops *model_ops, + struct socket_context *sock, + const struct stream_server_ops *stream_ops, + struct messaging_context *msg_ctx, + void *private_data, + struct stream_connection **_srv_conn) +{ + struct stream_connection *srv_conn; + + srv_conn = talloc_zero(ev, struct stream_connection); + NT_STATUS_HAVE_NO_MEMORY(srv_conn); + + talloc_steal(srv_conn, sock); + + srv_conn->private = private_data; + srv_conn->model_ops = model_ops; + srv_conn->socket = sock; + 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 = event_add_fd(ev, srv_conn, socket_get_fd(sock), + EVENT_FD_READ, + stream_io_handler_fde, srv_conn); + *_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 event_context *ev, + struct loadparm_context *lp_ctx, + struct socket_context *sock, + struct server_id server_id, void *private) +{ + struct stream_socket *stream_socket = talloc_get_type(private, struct stream_socket); + struct stream_connection *srv_conn; + struct socket_address *c, *s; + + srv_conn = talloc_zero(ev, struct stream_connection); + if (!srv_conn) { + DEBUG(0,("talloc(mem_ctx, struct stream_connection) failed\n")); + return; + } + + talloc_steal(srv_conn, sock); + + srv_conn->private = stream_socket->private; + 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->event.fde = event_add_fd(ev, srv_conn, socket_get_fd(sock), + 0, stream_io_handler_fde, srv_conn); + + if (!socket_check_access(sock, "smbd", lp_hostsallow(NULL, lp_default_service(lp_ctx)), lp_hostsdeny(NULL, lp_default_service(lp_ctx)))) { + stream_terminate_connection(srv_conn, "denied by access rules"); + return; + } + + /* setup to receive internal messages on this connection */ + srv_conn->msg_ctx = messaging_init(srv_conn, + lp_messaging_path(srv_conn, lp_ctx), + srv_conn->server_id, + lp_iconv_convenience(lp_ctx), + ev); + if (!srv_conn->msg_ctx) { + stream_terminate_connection(srv_conn, "messaging_init() failed"); + return; + } + + c = socket_get_peer_addr(sock, ev); + s = socket_get_my_addr(sock, ev); + if (s && c) { + const char *title; + title = talloc_asprintf(s, "conn[%s] c[%s:%u] s[%s:%u] server_id[%s]", + stream_socket->ops->name, + c->addr, c->port, s->addr, s->port, + cluster_id_string(s, server_id)); + if (title) { + stream_connection_set_title(srv_conn, title); + } + } + talloc_free(c); + talloc_free(s); + + /* we're now ready to start receiving events on this stream */ + EVENT_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 event_context *ev, struct fd_event *fde, + uint16_t flags, void *private) +{ + struct stream_socket *stream_socket = talloc_get_type(private, 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); +} + +/* + 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(struct event_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) +{ + NTSTATUS status; + struct stream_socket *stream_socket; + struct socket_address *socket_address; + int i; + + stream_socket = talloc_zero(event_context, struct stream_socket); + NT_STATUS_HAVE_NO_MEMORY(stream_socket); + + status = socket_create(family, SOCKET_TYPE_STREAM, &stream_socket->sock, 0); + NT_STATUS_NOT_OK_RETURN(status); + + talloc_steal(stream_socket, stream_socket->sock); + + 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) { + socket_address = socket_address_from_strings(stream_socket, + stream_socket->sock->backend_name, + sock_addr, 0); + NT_STATUS_HAVE_NO_MEMORY(socket_address); + status = socket_listen(stream_socket->sock, socket_address, SERVER_LISTEN_BACKLOG, 0); + talloc_free(socket_address); + + } else if (*port == 0) { + for (i=SERVER_TCP_LOW_PORT;i<= SERVER_TCP_HIGH_PORT;i++) { + socket_address = socket_address_from_strings(stream_socket, + stream_socket->sock->backend_name, + sock_addr, i); + NT_STATUS_HAVE_NO_MEMORY(socket_address); + status = socket_listen(stream_socket->sock, socket_address, + SERVER_LISTEN_BACKLOG, 0); + talloc_free(socket_address); + if (NT_STATUS_IS_OK(status)) { + *port = i; + break; + } + } + } else { + socket_address = socket_address_from_strings(stream_socket, + stream_socket->sock->backend_name, + sock_addr, *port); + NT_STATUS_HAVE_NO_MEMORY(socket_address); + status = socket_listen(stream_socket->sock, socket_address, SERVER_LISTEN_BACKLOG, 0); + talloc_free(socket_address); + } + + if (!NT_STATUS_IS_OK(status)) { + DEBUG(0,("Failed to listen on %s:%u - %s\n", + sock_addr, *port, nt_errstr(status))); + talloc_free(stream_socket); + return status; + } + + /* By specifying EVENT_FD_AUTOCLOSE below, we indicate that we + * will close the socket using the events system. This avoids + * nasty interactions with waiting for talloc to close the socket. */ + + socket_set_flags(stream_socket->sock, SOCKET_FLAG_NOCLOSE); + + /* Add the FD from the newly created socket into the event + * subsystem. it will call the accept handler whenever we get + * new connections */ + + event_add_fd(event_context, stream_socket->sock, + socket_get_fd(stream_socket->sock), + EVENT_FD_READ|EVENT_FD_AUTOCLOSE, + stream_accept_handler, stream_socket); + + stream_socket->private = talloc_reference(stream_socket, private); + stream_socket->ops = stream_ops; + stream_socket->event_ctx = event_context; + stream_socket->model_ops = model_ops; + + 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/smbd/service_stream.h b/source4/smbd/service_stream.h new file mode 100644 index 0000000000..d57a54cdc9 --- /dev/null +++ b/source4/smbd/service_stream.h @@ -0,0 +1,73 @@ +/* + 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/misc.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; + + struct { + struct event_context *ctx; + struct fd_event *fde; + } event; + + struct socket_context *socket; + struct messaging_context *msg_ctx; + struct loadparm_context *lp_ctx; + + /* + * this transport layer session info, normally NULL + * which means the same as an anonymous session info + */ + struct auth_session_info *session_info; + + bool processing; + const char *terminate; +}; + + +/* 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); +}; + +#endif /* __SERVICE_STREAM_H__ */ diff --git a/source4/smbd/service_task.c b/source4/smbd/service_task.c new file mode 100644 index 0000000000..2274685059 --- /dev/null +++ b/source4/smbd/service_task.c @@ -0,0 +1,111 @@ +/* + 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/events/events.h" +#include "smbd/service.h" +#include "smbd/service_task.h" +#include "lib/messaging/irpc.h" +#include "param/param.h" + +/* + terminate a task service +*/ +void task_server_terminate(struct task_server *task, const char *reason) +{ + struct event_context *event_ctx = task->event_ctx; + const struct model_ops *model_ops = task->model_ops; + DEBUG(0,("task_server_terminate: [%s]\n", reason)); + model_ops->terminate(event_ctx, reason); + + /* 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 { + void (*task_init)(struct task_server *); + 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 void task_server_callback(struct event_context *event_ctx, + struct loadparm_context *lp_ctx, + struct server_id server_id, void *private) +{ + struct task_state *state = talloc_get_type(private, struct task_state); + struct task_server *task; + + task = talloc(event_ctx, struct task_server); + if (task == NULL) return; + + task->event_ctx = event_ctx; + task->model_ops = state->model_ops; + task->server_id = server_id; + task->lp_ctx = lp_ctx; + + task->msg_ctx = messaging_init(task, + lp_messaging_path(task, task->lp_ctx), + task->server_id, + lp_iconv_convenience(task->lp_ctx), + task->event_ctx); + if (!task->msg_ctx) { + task_server_terminate(task, "messaging_init() failed"); + return; + } + + state->task_init(task); +} + +/* + startup a task based server +*/ +NTSTATUS task_server_startup(struct event_context *event_ctx, + struct loadparm_context *lp_ctx, + const char *service_name, + const struct model_ops *model_ops, + void (*task_init)(struct task_server *)) +{ + struct task_state *state; + + state = talloc(event_ctx, struct task_state); + NT_STATUS_HAVE_NO_MEMORY(state); + + state->task_init = task_init; + state->model_ops = model_ops; + + model_ops->new_task(event_ctx, lp_ctx, service_name, task_server_callback, state); + + 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/smbd/service_task.h b/source4/smbd/service_task.h new file mode 100644 index 0000000000..207e2cbbca --- /dev/null +++ b/source4/smbd/service_task.h @@ -0,0 +1,38 @@ +/* + 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/misc.h" + +struct task_server { + struct event_context *event_ctx; + const struct model_ops *model_ops; + struct messaging_context *msg_ctx; + struct loadparm_context *lp_ctx; + struct server_id server_id; + void *private; +}; + + + +#endif /* __SERVICE_TASK_H__ */ diff --git a/source4/smbd/smbd.8.xml b/source4/smbd/smbd.8.xml new file mode 100644 index 0000000000..a02c2ce80a --- /dev/null +++ b/source4/smbd/smbd.8.xml @@ -0,0 +1,177 @@ +<?xml version="1.0" encoding="iso-8859-1"?> +<!DOCTYPE refentry PUBLIC "-//Samba-Team//DTD DocBook V4.2-Based Variant V1.0//EN" "http://www.samba.org/samba/DTD/samba-doc"> +<refentry id="smbd.8"> + +<refmeta> + <refentrytitle>smbd</refentrytitle> + <manvolnum>8</manvolnum> +</refmeta> + + +<refnamediv> + <refname>smbd</refname> + <refpurpose>server to provide filesharing- and directory services to clients</refpurpose> +</refnamediv> + +<refsynopsisdiv> + <cmdsynopsis> + <command>smbd</command> + <arg choice="opt">-i</arg> + <arg choice="opt">-M model</arg> + </cmdsynopsis> +</refsynopsisdiv> + +<refsect1> + <title>DESCRIPTION</title> + <para>This program is part of the <citerefentry><refentrytitle>samba</refentrytitle> + <manvolnum>7</manvolnum></citerefentry> suite.</para> + + <para><command>smbd</command> is the server daemon that + provides filesharing and directory services to Windows clients. + The server provides filespace and directory services to + clients using the SMB (or CIFS) protocol and other + related protocols such as DCE/RPC, LDAP and Kerberos. + </para> + + <para> + Clients supported include MSCLIENT 3.0 for DOS, Windows for + Workgroups, Windows 95/98/ME, Windows NT, Windows 2000/XP/2003, + OS/2, DAVE for Macintosh, and cifsfs for Linux.</para> + + <para>An extensive description of the services that the + server can provide is given in the man page for the + configuration file controlling the attributes of those + services (see <citerefentry><refentrytitle>smb.conf</refentrytitle> + <manvolnum>5</manvolnum></citerefentry>. This man page will not describe the + services, but will concentrate on the administrative aspects + of running the server.</para> + + <para>Please note that there are significant security + implications to running this server, and the <citerefentry><refentrytitle>smb.conf</refentrytitle> + <manvolnum>5</manvolnum></citerefentry> manual page should be regarded as mandatory reading before + proceeding with installation.</para> + + <para>As of Samba 4, smbd also incorporates all the functionality of + nmbd.</para> + +</refsect1> + +<refsect1> + <title>OPTIONS</title> + + <variablelist> + <varlistentry> + <term>-i</term> + <listitem><para>If this parameter is specified it causes the + server to run "interactively", not as a daemon, even if the + server is executed on the command line of a shell. Setting this + parameter negates the implicit deamon mode when run from the + command line. <command>smbd</command> also logs to standard + output, as if the <command>-S</command> parameter had been + given. + </para></listitem> + </varlistentry> + + <varlistentry> + <term>-M model</term> + <listitem><para>This parameter can be used to specify the + "process model" smbd should use. This determines + how concurrent clients are handled. Available process + models include <emphasis>single</emphasis> (everything in + a single process), <emphasis>standard</emphasis> (similar + behaviour to that of Samba 3), <emphasis>thread</emphasis> + (single process, different threads. + </para></listitem> + </varlistentry> + </variablelist> +</refsect1> + +<refsect1> + <title>FILES</title> + + <variablelist> + <varlistentry> + <term><filename>/etc/rc</filename></term> + <listitem><para>or whatever initialization script your + system uses).</para> + + <para>If running the server as a daemon at startup, + this file will need to contain an appropriate startup + sequence for the server. </para></listitem> + </varlistentry> + + <varlistentry> + <term><filename>/etc/services</filename></term> + <listitem><para>If running the server via the + meta-daemon <command>inetd</command>, this file + must contain a mapping of service name (e.g., netbios-ssn) + to service port (e.g., 139) and protocol type (e.g., tcp). + </para></listitem> + </varlistentry> + + <varlistentry> + <term><filename>/usr/local/samba/lib/smb.conf</filename></term> + <listitem><para>This is the default location of the <citerefentry><refentrytitle>smb.conf</refentrytitle> + <manvolnum>5</manvolnum></citerefentry> server configuration file. Other common places that systems + install this file are <filename>/usr/samba/lib/smb.conf</filename> + and <filename>/etc/samba/smb.conf</filename>.</para> + + <para>This file describes all the services the server + is to make available to clients. See <citerefentry><refentrytitle>smb.conf</refentrytitle> + <manvolnum>5</manvolnum></citerefentry> for more information.</para> + </listitem> + </varlistentry> + </variablelist> +</refsect1> + +<refsect1> + <title>VERSION</title> + + <para>This man page is correct for version 4 of + the Samba suite.</para> +</refsect1> + +<refsect1> + <title>DIAGNOSTICS</title> + + <para>Most diagnostics issued by the server are logged + in a specified log file. The log file name is specified + at compile time, but may be overridden on the command line.</para> + + <para>The number and nature of diagnostics available depends + on the debug level used by the server. If you have problems, set + the debug level to 3 and peruse the log files.</para> + + <para>Most messages are reasonably self-explanatory. Unfortunately, + at the time this man page was created, there are too many diagnostics + available in the source code to warrant describing each and every + diagnostic. At this stage your best bet is still to grep the + source code and inspect the conditions that gave rise to the + diagnostics you are seeing.</para> +</refsect1> + +<refsect1> + <title>SEE ALSO</title> + <para><citerefentry><refentrytitle>hosts_access</refentrytitle> + <manvolnum>5</manvolnum></citerefentry> + <citerefentry><refentrytitle>smb.conf</refentrytitle> + <manvolnum>5</manvolnum></citerefentry>, <citerefentry><refentrytitle>smbclient</refentrytitle> + <manvolnum>1</manvolnum></citerefentry>, <citerefentry><refentrytitle>testparm</refentrytitle> + <manvolnum>1</manvolnum></citerefentry>, and the + Internet RFC's <filename>rfc1001.txt</filename>, <filename>rfc1002.txt</filename>. + In addition the CIFS (formerly SMB) specification is available + as a link from the Web page <ulink noescape="1" url="http://samba.org/cifs/"> + http://samba.org/cifs/</ulink>.</para> +</refsect1> + +<refsect1> + <title>AUTHOR</title> + + <para>The original Samba software and related utilities + were created by Andrew Tridgell. Samba is now developed + by the Samba Team as an Open Source project similar + to the way the Linux kernel is developed.</para> + +</refsect1> + +</refentry> |