#include "registry/reg_init_full.h"
#include "libcli/auth/schannel.h"
#include "secrets.h"
-#include "memcache.h"
+#include "../lib/util/memcache.h"
#include "ctdbd_conn.h"
+#include "util_cluster.h"
#include "printing/queue_process.h"
#include "rpc_server/rpc_service_setup.h"
#include "rpc_server/rpc_config.h"
#include "smbprofile.h"
#include "lib/id_cache.h"
#include "lib/param/param.h"
+#include "lib/background.h"
+#include "lib/conn_tdb.h"
+#include "../lib/util/pidfile.h"
+#include "lib/smbd_shim.h"
+#include "scavenger.h"
+#include "locking/leases_db.h"
+
+struct smbd_open_socket;
+struct smbd_child_pid;
+
+struct smbd_parent_context {
+ bool interactive;
+
+ struct tevent_context *ev_ctx;
+ struct messaging_context *msg_ctx;
+
+ /* the list of listening sockets */
+ struct smbd_open_socket *sockets;
+
+ /* the list of current child processes */
+ struct smbd_child_pid *children;
+ size_t num_children;
+
+ struct tevent_timer *cleanup_te;
+};
+
+struct smbd_open_socket {
+ struct smbd_open_socket *prev, *next;
+ struct smbd_parent_context *parent;
+ int fd;
+ struct tevent_fd *fde;
+};
+
+struct smbd_child_pid {
+ struct smbd_child_pid *prev, *next;
+ pid_t pid;
+};
extern void start_epmd(struct tevent_context *ev_ctx,
struct messaging_context *msg_ctx);
-extern void start_lsasd(struct event_context *ev_ctx,
+extern void start_lsasd(struct tevent_context *ev_ctx,
struct messaging_context *msg_ctx);
#ifdef WITH_DFS
What to do when smb.conf is updated.
********************************************************************/
-static void smb_conf_updated(struct messaging_context *msg,
- void *private_data,
- uint32_t msg_type,
- struct server_id server_id,
- DATA_BLOB *data)
+static void smbd_parent_conf_updated(struct messaging_context *msg,
+ void *private_data,
+ uint32_t msg_type,
+ struct server_id server_id,
+ DATA_BLOB *data)
{
struct tevent_context *ev_ctx =
talloc_get_type_abort(private_data, struct tevent_context);
- struct smbd_server_connection *sconn = msg_ctx_to_sconn(msg);
- DEBUG(10,("smb_conf_updated: Got message saying smb.conf was "
+ DEBUG(10,("smbd_parent_conf_updated: Got message saying smb.conf was "
"updated. Reloading.\n"));
change_to_root_user();
- reload_services(msg, sconn->sock, False);
- if (am_parent) {
- printing_subsystem_update(ev_ctx, msg, false);
- }
-}
-
-/*******************************************************************
- What to do when printcap is updated.
- ********************************************************************/
-
-static void smb_pcap_updated(struct messaging_context *msg,
- void *private_data,
- uint32_t msg_type,
- struct server_id server_id,
- DATA_BLOB *data)
-{
- struct tevent_context *ev_ctx =
- talloc_get_type_abort(private_data, struct tevent_context);
-
- DEBUG(10,("Got message saying pcap was updated. Reloading.\n"));
- change_to_root_user();
- delete_and_reload_printers(ev_ctx, msg);
+ reload_services(NULL, NULL, false);
+ printing_subsystem_update(ev_ctx, msg, false);
}
/*******************************************************************
if(am_parent) kill(0,SIGTERM);
}
-/****************************************************************************
- Process a sam sync message - not sure whether to do this here or
- somewhere else.
-****************************************************************************/
-
-static void msg_sam_sync(struct messaging_context *msg,
- void *private_data,
- uint32_t msg_type,
- struct server_id server_id,
- DATA_BLOB *data)
-{
- DEBUG(10, ("** sam sync message received, ignoring\n"));
-}
-
static void msg_exit_server(struct messaging_context *msg,
void *private_data,
uint32_t msg_type,
procid_str_static(&src), sig));
#endif
- kill(sys_getpid(), sig);
+ kill(getpid(), sig);
}
#endif /* DEVELOPER */
-NTSTATUS messaging_send_to_children(struct messaging_context *msg_ctx,
- uint32_t msg_type, DATA_BLOB* data)
+static NTSTATUS messaging_send_to_children(struct messaging_context *msg_ctx,
+ uint32_t msg_type, DATA_BLOB* data)
{
NTSTATUS status;
- struct child_pid *child;
+ struct smbd_parent_context *parent = am_parent;
+ struct smbd_child_pid *child;
+
+ if (parent == NULL) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
- for (child = children; child != NULL; child = child->next) {
- status = messaging_send(msg_ctx, pid_to_procid(child->pid),
+ for (child = parent->children; child != NULL; child = child->next) {
+ status = messaging_send(parent->msg_ctx,
+ pid_to_procid(child->pid),
msg_type, data);
if (!NT_STATUS_IS_OK(status)) {
return status;
return NT_STATUS_OK;
}
+static void smb_parent_send_to_children(struct messaging_context *ctx,
+ void* data,
+ uint32_t msg_type,
+ struct server_id srv_id,
+ DATA_BLOB* msg_data)
+{
+ messaging_send_to_children(ctx, msg_type, msg_data);
+}
+
/*
* Parent smbd process sets its own debug level first and then
* sends a message to all the smbd children to adjust their debug
messaging_send_to_children(msg_ctx, MSG_DEBUG, data);
}
-static void add_child_pid(pid_t pid)
+static void smbd_parent_id_cache_kill(struct messaging_context *msg_ctx,
+ void *private_data,
+ uint32_t msg_type,
+ struct server_id server_id,
+ DATA_BLOB* data)
{
- struct child_pid *child;
+ const char *msg = (data && data->data)
+ ? (const char *)data->data : "<NULL>";
+ struct id_cache_ref id;
+
+ if (!id_cache_ref_parse(msg, &id)) {
+ DEBUG(0, ("Invalid ?ID: %s\n", msg));
+ return;
+ }
+
+ id_cache_delete_from_cache(&id);
+
+ messaging_send_to_children(msg_ctx, msg_type, data);
+}
+
+static void smbd_parent_id_cache_delete(struct messaging_context *ctx,
+ void* data,
+ uint32_t msg_type,
+ struct server_id srv_id,
+ DATA_BLOB* msg_data)
+{
+ id_cache_delete_message(ctx, data, msg_type, srv_id, msg_data);
+
+ messaging_send_to_children(ctx, msg_type, msg_data);
+}
+
+struct smbd_parent_notify_state {
+ struct tevent_context *ev;
+ struct messaging_context *msg;
+ uint32_t msgtype;
+ struct notify_context *notify;
+};
+
+static int smbd_parent_notify_cleanup(void *private_data);
+static void smbd_parent_notify_cleanup_done(struct tevent_req *req);
+static void smbd_parent_notify_proxy_done(struct tevent_req *req);
+
+static bool smbd_parent_notify_init(TALLOC_CTX *mem_ctx,
+ struct messaging_context *msg,
+ struct tevent_context *ev)
+{
+ struct smbd_parent_notify_state *state;
+ struct tevent_req *req;
+
+ state = talloc(mem_ctx, struct smbd_parent_notify_state);
+ if (state == NULL) {
+ return false;
+ }
+ state->msg = msg;
+ state->ev = ev;
+ state->msgtype = MSG_SMB_NOTIFY_CLEANUP;
+
+ state->notify = notify_init(state, msg, ev);
+ if (state->notify == NULL) {
+ goto fail;
+ }
+ req = background_job_send(
+ state, state->ev, state->msg, &state->msgtype, 1,
+ lp_parm_int(-1, "smbd", "notify cleanup interval", 60),
+ smbd_parent_notify_cleanup, state->notify);
+ if (req == NULL) {
+ goto fail;
+ }
+ tevent_req_set_callback(req, smbd_parent_notify_cleanup_done, state);
- child = SMB_MALLOC_P(struct child_pid);
+ if (!lp_clustering()) {
+ return true;
+ }
+
+ req = notify_cluster_proxy_send(state, ev, state->notify);
+ if (req == NULL) {
+ goto fail;
+ }
+ tevent_req_set_callback(req, smbd_parent_notify_proxy_done, state);
+
+ return true;
+fail:
+ TALLOC_FREE(state);
+ return false;
+}
+
+static int smbd_parent_notify_cleanup(void *private_data)
+{
+ struct notify_context *notify = talloc_get_type_abort(
+ private_data, struct notify_context);
+ notify_cleanup(notify);
+ return lp_parm_int(-1, "smbd", "notify cleanup interval", 60);
+}
+
+static void smbd_parent_notify_cleanup_done(struct tevent_req *req)
+{
+ struct smbd_parent_notify_state *state = tevent_req_callback_data(
+ req, struct smbd_parent_notify_state);
+ NTSTATUS status;
+
+ status = background_job_recv(req);
+ TALLOC_FREE(req);
+ DEBUG(1, ("notify cleanup job ended with %s\n", nt_errstr(status)));
+
+ /*
+ * Provide self-healing: Whatever the error condition was, it
+ * will have printed it into log.smbd. Just retrying and
+ * spamming log.smbd once a minute should be fine.
+ */
+ req = background_job_send(
+ state, state->ev, state->msg, &state->msgtype, 1, 60,
+ smbd_parent_notify_cleanup, state->notify);
+ if (req == NULL) {
+ DEBUG(1, ("background_job_send failed\n"));
+ return;
+ }
+ tevent_req_set_callback(req, smbd_parent_notify_cleanup_done, state);
+}
+
+static void smbd_parent_notify_proxy_done(struct tevent_req *req)
+{
+ int ret;
+
+ ret = notify_cluster_proxy_recv(req);
+ TALLOC_FREE(req);
+ DEBUG(1, ("notify proxy job ended with %s\n", strerror(ret)));
+}
+
+static void add_child_pid(struct smbd_parent_context *parent,
+ pid_t pid)
+{
+ struct smbd_child_pid *child;
+
+ child = talloc_zero(parent, struct smbd_child_pid);
if (child == NULL) {
DEBUG(0, ("Could not add child struct -- malloc failed\n"));
return;
}
child->pid = pid;
- DLIST_ADD(children, child);
- num_children += 1;
+ DLIST_ADD(parent->children, child);
+ parent->num_children += 1;
+}
+
+static void smb_tell_num_children(struct messaging_context *ctx, void *data,
+ uint32_t msg_type, struct server_id srv_id,
+ DATA_BLOB *msg_data)
+{
+ uint8_t buf[sizeof(uint32_t)];
+
+ if (am_parent) {
+ SIVAL(buf, 0, am_parent->num_children);
+ messaging_send_buf(ctx, srv_id, MSG_SMB_NUM_CHILDREN,
+ buf, sizeof(buf));
+ }
}
+
/*
at most every smbd:cleanuptime seconds (default 20), we scan the BRL
and locking database for entries to cleanup. As a side effect this
network outage).
*/
-static void cleanup_timeout_fn(struct event_context *event_ctx,
- struct timed_event *te,
+static void cleanup_timeout_fn(struct tevent_context *event_ctx,
+ struct tevent_timer *te,
struct timeval now,
void *private_data)
{
- struct timed_event **cleanup_te = (struct timed_event **)private_data;
- struct messaging_context *msg = smbd_messaging_context();
+ struct smbd_parent_context *parent =
+ talloc_get_type_abort(private_data,
+ struct smbd_parent_context);
+
+ parent->cleanup_te = NULL;
DEBUG(1,("Cleaning up brl and lock database after unclean shutdown\n"));
- message_send_all(msg, MSG_SMB_UNLOCK, NULL, 0, NULL);
- messaging_send_buf(msg, messaging_server_id(msg),
- MSG_SMB_BRL_VALIDATE, NULL, 0);
- /* mark the cleanup as having been done */
- (*cleanup_te) = NULL;
+ message_send_all(parent->msg_ctx, MSG_SMB_UNLOCK, NULL, 0, NULL);
+ messaging_send_buf(parent->msg_ctx,
+ messaging_server_id(parent->msg_ctx),
+ MSG_SMB_BRL_VALIDATE, NULL, 0);
}
-static void remove_child_pid(struct tevent_context *ev_ctx,
+static void remove_child_pid(struct smbd_parent_context *parent,
pid_t pid,
bool unclean_shutdown)
{
- struct child_pid *child;
- static struct timed_event *cleanup_te;
+ struct smbd_child_pid *child;
struct server_id child_id;
+ int ret;
+
+ child_id = pid_to_procid(pid);
+
+ ret = messaging_cleanup(parent->msg_ctx, pid);
+
+ if ((ret != 0) && (ret != ENOENT)) {
+ DEBUG(10, ("%s: messaging_cleanup returned %s\n",
+ __func__, strerror(ret)));
+ }
+
+ for (child = parent->children; child != NULL; child = child->next) {
+ if (child->pid == pid) {
+ struct smbd_child_pid *tmp = child;
+ DLIST_REMOVE(parent->children, child);
+ TALLOC_FREE(tmp);
+ parent->num_children -= 1;
+ break;
+ }
+ }
+
+ if (child == NULL) {
+ /* not all forked child processes are added to the children list */
+ DEBUG(2, ("Could not find child %d -- ignoring\n", (int)pid));
+ return;
+ }
if (unclean_shutdown) {
/* a child terminated uncleanly so tickle all
*/
DEBUG(3,(__location__ " Unclean shutdown of pid %u\n",
(unsigned int)pid));
- if (!cleanup_te) {
+ if (parent->cleanup_te == NULL) {
/* call the cleanup timer, but not too often */
int cleanup_time = lp_parm_int(-1, "smbd", "cleanuptime", 20);
- cleanup_te = event_add_timed(ev_ctx, NULL,
+ parent->cleanup_te = tevent_add_timer(parent->ev_ctx,
+ parent,
timeval_current_ofs(cleanup_time, 0),
cleanup_timeout_fn,
- &cleanup_te);
+ parent);
DEBUG(1,("Scheduled cleanup of brl and lock database after unclean shutdown\n"));
}
}
- child_id = procid_self(); /* Just initialize pid and potentially vnn */
- child_id.pid = pid;
-
if (!serverid_deregister(child_id)) {
DEBUG(1, ("Could not remove pid %d from serverid.tdb\n",
(int)pid));
}
-
- for (child = children; child != NULL; child = child->next) {
- if (child->pid == pid) {
- struct child_pid *tmp = child;
- DLIST_REMOVE(children, child);
- SAFE_FREE(tmp);
- num_children -= 1;
- return;
- }
- }
-
- /* not all forked child processes are added to the children list */
- DEBUG(1, ("Could not find child %d -- ignoring\n", (int)pid));
}
/****************************************************************************
Have we reached the process limit ?
****************************************************************************/
-static bool allowable_number_of_smbd_processes(void)
+static bool allowable_number_of_smbd_processes(struct smbd_parent_context *parent)
{
int max_processes = lp_max_smbd_processes();
if (!max_processes)
return True;
- return num_children < max_processes;
+ return parent->num_children < max_processes;
}
static void smbd_sig_chld_handler(struct tevent_context *ev,
{
pid_t pid;
int status;
+ struct smbd_parent_context *parent =
+ talloc_get_type_abort(private_data,
+ struct smbd_parent_context);
while ((pid = sys_waitpid(-1, &status, WNOHANG)) > 0) {
bool unclean_shutdown = False;
if (WIFSIGNALED(status)) {
unclean_shutdown = True;
}
- remove_child_pid(ev, pid, unclean_shutdown);
+ remove_child_pid(parent, pid, unclean_shutdown);
}
}
-static void smbd_setup_sig_chld_handler(struct tevent_context *ev_ctx)
+static void smbd_setup_sig_chld_handler(struct smbd_parent_context *parent)
{
struct tevent_signal *se;
- se = tevent_add_signal(ev_ctx,
- ev_ctx, /* mem_ctx */
+ se = tevent_add_signal(parent->ev_ctx,
+ parent, /* mem_ctx */
SIGCHLD, 0,
smbd_sig_chld_handler,
- NULL);
+ parent);
if (!se) {
exit_server("failed to setup SIGCHLD handler");
}
}
-struct smbd_open_socket;
-
-struct smbd_parent_context {
- bool interactive;
-
- /* the list of listening sockets */
- struct smbd_open_socket *sockets;
-};
-
-struct smbd_open_socket {
- struct smbd_open_socket *prev, *next;
- struct smbd_parent_context *parent;
- int fd;
- struct tevent_fd *fde;
- struct messaging_context *msg_ctx;
-};
-
static void smbd_open_socket_close_fn(struct tevent_context *ev,
struct tevent_fd *fde,
int fd,
{
struct smbd_open_socket *s = talloc_get_type_abort(private_data,
struct smbd_open_socket);
- struct messaging_context *msg_ctx = s->msg_ctx;
- struct smbd_server_connection *sconn = msg_ctx_to_sconn(msg_ctx);
+ struct messaging_context *msg_ctx = s->parent->msg_ctx;
struct sockaddr_storage addr;
socklen_t in_addrlen = sizeof(addr);
int fd;
uint64_t unique_id;
fd = accept(s->fd, (struct sockaddr *)(void *)&addr,&in_addrlen);
- sconn->sock = fd;
if (fd == -1 && errno == EINTR)
return;
if (fd == -1) {
- DEBUG(0,("open_sockets_smbd: accept: %s\n",
+ DEBUG(0,("accept: %s\n",
strerror(errno)));
return;
}
if (s->parent->interactive) {
- smbd_process(ev, sconn);
+ reinit_after_fork(msg_ctx, ev, true);
+ smbd_process(ev, msg_ctx, fd, true);
exit_server_cleanly("end of interactive mode");
return;
}
- if (!allowable_number_of_smbd_processes()) {
+ if (!allowable_number_of_smbd_processes(s->parent)) {
close(fd);
- sconn->sock = -1;
return;
}
* Generate a unique id in the parent process so that we use
* the global random state in the parent.
*/
- generate_random_buffer((uint8_t *)&unique_id, sizeof(unique_id));
+ unique_id = serverid_get_random_unique_id();
- pid = sys_fork();
+ pid = fork();
if (pid == 0) {
NTSTATUS status = NT_STATUS_OK;
/* Child code ... */
- am_parent = 0;
-
- set_my_unique_id(unique_id);
-
- /* Stop zombies, the parent explicitly handles
- * them, counting worker smbds. */
- CatchChild();
-
- /* close our standard file
- descriptors */
- if (!debug_get_output_is_stdout()) {
- close_low_fds(False); /* Don't close stderr */
- }
+ am_parent = NULL;
/*
* Can't use TALLOC_FREE here. Nulling out the argument to it
talloc_free(s->parent);
s = NULL;
+ set_my_unique_id(unique_id);
+
+ /* Stop zombies, the parent explicitly handles
+ * them, counting worker smbds. */
+ CatchChild();
+
status = reinit_after_fork(msg_ctx,
ev,
true);
smb_panic("reinit_after_fork() failed");
}
- smbd_setup_sig_term_handler();
- smbd_setup_sig_hup_handler(ev,
- msg_ctx);
-
- if (!serverid_register(messaging_server_id(msg_ctx),
- FLAG_MSG_GENERAL|FLAG_MSG_SMBD
- |FLAG_MSG_DBWRAP
- |FLAG_MSG_PRINT_GENERAL)) {
- exit_server_cleanly("Could not register myself in "
- "serverid.tdb");
- }
-
- smbd_process(ev, sconn);
+ smbd_process(ev, msg_ctx, fd, false);
exit:
exit_server_cleanly("end of child");
return;
}
if (pid < 0) {
- DEBUG(0,("smbd_accept_connection: sys_fork() failed: %s\n",
+ DEBUG(0,("smbd_accept_connection: fork() failed: %s\n",
strerror(errno)));
}
getpeername failure if we reopen the logs
and use %I in the filename.
*/
- sconn->sock = -1;
if (pid != 0) {
- add_child_pid(pid);
+ add_child_pid(s->parent, pid);
}
/* Force parent to check log size after
static bool smbd_open_one_socket(struct smbd_parent_context *parent,
struct tevent_context *ev_ctx,
- struct messaging_context *msg_ctx,
const struct sockaddr_storage *ifss,
uint16_t port)
{
return false;
}
- s->msg_ctx = msg_ctx;
s->fde = tevent_add_fd(ev_ctx,
s,
s->fd, TEVENT_FD_READ,
const char *smb_ports)
{
int num_interfaces = iface_count();
- int i;
- const char *ports;
+ int i,j;
+ const char **ports;
unsigned dns_port = 0;
#ifdef HAVE_ATEXIT
#endif
/* Stop zombies */
- smbd_setup_sig_chld_handler(ev_ctx);
+ smbd_setup_sig_chld_handler(parent);
+
+ ports = lp_smb_ports();
/* use a reasonable default set of ports - listing on 445 and 139 */
- if (!smb_ports) {
- ports = lp_smb_ports();
- if (!ports || !*ports) {
- ports = talloc_strdup(talloc_tos(), SMB_PORTS);
- } else {
- ports = talloc_strdup(talloc_tos(), ports);
+ if (smb_ports) {
+ char **l;
+ l = str_list_make_v3(talloc_tos(), smb_ports, NULL);
+ ports = discard_const_p(const char *, l);
+ }
+
+ for (j = 0; ports && ports[j]; j++) {
+ unsigned port = atoi(ports[j]);
+
+ if (port == 0 || port > 0xffff) {
+ exit_server_cleanly("Invalid port in the config or on "
+ "the commandline specified!");
}
- } else {
- ports = talloc_strdup(talloc_tos(), smb_ports);
}
if (lp_interfaces() && lp_bind_interfaces_only()) {
for(i = 0; i < num_interfaces; i++) {
const struct sockaddr_storage *ifss =
iface_n_sockaddr_storage(i);
- char *tok;
- const char *ptr;
-
if (ifss == NULL) {
DEBUG(0,("open_sockets_smbd: "
"interface %d has NULL IP address !\n",
continue;
}
- for (ptr=ports;
- next_token_talloc(talloc_tos(),&ptr, &tok, " \t,");) {
- unsigned port = atoi(tok);
- if (port == 0 || port > 0xffff) {
- continue;
- }
+ for (j = 0; ports && ports[j]; j++) {
+ unsigned port = atoi(ports[j]);
/* Keep the first port for mDNS service
* registration.
if (!smbd_open_one_socket(parent,
ev_ctx,
- msg_ctx,
ifss,
port)) {
return false;
/* Just bind to 0.0.0.0 - accept connections
from anywhere. */
- char *tok;
- const char *ptr;
- const char *sock_addr = lp_socket_address();
+ const char *sock_addr;
char *sock_tok;
const char *sock_ptr;
- if (strequal(sock_addr, "0.0.0.0") ||
- strequal(sock_addr, "::")) {
#if HAVE_IPV6
- sock_addr = "::,0.0.0.0";
+ sock_addr = "::,0.0.0.0";
#else
- sock_addr = "0.0.0.0";
+ sock_addr = "0.0.0.0";
#endif
- }
for (sock_ptr=sock_addr;
next_token_talloc(talloc_tos(), &sock_ptr, &sock_tok, " \t,"); ) {
- for (ptr=ports; next_token_talloc(talloc_tos(), &ptr, &tok, " \t,"); ) {
+ for (j = 0; ports && ports[j]; j++) {
struct sockaddr_storage ss;
-
- unsigned port = atoi(tok);
- if (port == 0 || port > 0xffff) {
- continue;
- }
+ unsigned port = atoi(ports[j]);
/* Keep the first port for mDNS service
* registration.
continue;
}
- if (!smbd_open_one_socket(parent,
- ev_ctx,
- msg_ctx,
- &ss,
- port)) {
- return false;
- }
+ /*
+ * If we fail to open any sockets
+ * in this loop the parent-sockets == NULL
+ * case below will prevent us from starting.
+ */
+
+ (void)smbd_open_one_socket(parent,
+ ev_ctx,
+ &ss,
+ port);
}
}
}
/* Listen to messages */
- messaging_register(msg_ctx, NULL, MSG_SMB_SAM_SYNC, msg_sam_sync);
messaging_register(msg_ctx, NULL, MSG_SHUTDOWN, msg_exit_server);
- messaging_register(msg_ctx, NULL, MSG_SMB_FILE_RENAME,
- msg_file_was_renamed);
messaging_register(msg_ctx, ev_ctx, MSG_SMB_CONF_UPDATED,
- smb_conf_updated);
+ smbd_parent_conf_updated);
messaging_register(msg_ctx, NULL, MSG_SMB_STAT_CACHE_DELETE,
smb_stat_cache_delete);
messaging_register(msg_ctx, NULL, MSG_DEBUG, smbd_msg_debug);
- messaging_register(msg_ctx, ev_ctx, MSG_PRINTER_PCAP,
- smb_pcap_updated);
- brl_register_msgs(msg_ctx);
+ messaging_register(msg_ctx, NULL, MSG_SMB_BRL_VALIDATE,
+ brl_revalidate);
+ messaging_register(msg_ctx, NULL, MSG_SMB_FORCE_TDIS,
+ smb_parent_send_to_children);
+ messaging_register(msg_ctx, NULL, MSG_SMB_KILL_CLIENT_IP,
+ smb_parent_send_to_children);
+ messaging_register(msg_ctx, NULL, MSG_SMB_TELL_NUM_CHILDREN,
+ smb_tell_num_children);
+
+ messaging_register(msg_ctx, NULL,
+ ID_CACHE_DELETE, smbd_parent_id_cache_delete);
+ messaging_register(msg_ctx, NULL,
+ ID_CACHE_KILL, smbd_parent_id_cache_kill);
- msg_idmap_register_msg(msg_ctx);
-
-#ifdef CLUSTER_SUPPORT
if (lp_clustering()) {
ctdbd_register_reconfigure(messaging_ctdbd_connection());
}
-#endif
#ifdef DEVELOPER
messaging_register(msg_ctx, NULL, MSG_SMB_INJECT_FAULT,
return true;
}
+
+/*
+ handle stdin becoming readable when we are in --foreground mode
+ */
+static void smbd_stdin_handler(struct tevent_context *ev,
+ struct tevent_fd *fde,
+ uint16_t flags,
+ void *private_data)
+{
+ char c;
+ if (read(0, &c, 1) != 1) {
+ /* we have reached EOF on stdin, which means the
+ parent has exited. Shutdown the server */
+ exit_server_cleanly("EOF on stdin");
+ }
+}
+
+struct smbd_parent_tevent_trace_state {
+ TALLOC_CTX *frame;
+};
+
+static void smbd_parent_tevent_trace_callback(enum tevent_trace_point point,
+ void *private_data)
+{
+ struct smbd_parent_tevent_trace_state *state =
+ (struct smbd_parent_tevent_trace_state *)private_data;
+
+ switch (point) {
+ case TEVENT_TRACE_BEFORE_WAIT:
+ break;
+ case TEVENT_TRACE_AFTER_WAIT:
+ break;
+ case TEVENT_TRACE_BEFORE_LOOP_ONCE:
+ TALLOC_FREE(state->frame);
+ state->frame = talloc_stackframe();
+ break;
+ case TEVENT_TRACE_AFTER_LOOP_ONCE:
+ TALLOC_FREE(state->frame);
+ break;
+ }
+
+ errno = 0;
+}
+
static void smbd_parent_loop(struct tevent_context *ev_ctx,
struct smbd_parent_context *parent)
{
+ struct smbd_parent_tevent_trace_state trace_state = {
+ .frame = NULL,
+ };
+ int ret = 0;
+
+ tevent_set_trace_callback(ev_ctx, smbd_parent_tevent_trace_callback,
+ &trace_state);
+
/* now accept incoming connections - forking a new process
for each incoming connection */
DEBUG(2,("waiting for connections\n"));
- while (1) {
- int ret;
- TALLOC_CTX *frame = talloc_stackframe();
- ret = tevent_loop_once(ev_ctx);
- if (ret != 0) {
- exit_server_cleanly("tevent_loop_once() error");
- }
+ ret = tevent_loop_wait(ev_ctx);
+ if (ret != 0) {
+ DEBUG(0, ("tevent_loop_wait failed: %d, %s, exiting\n",
+ ret, strerror(errno)));
+ }
- TALLOC_FREE(frame);
- } /* end while 1 */
+ TALLOC_FREE(trace_state.frame);
/* NOTREACHED return True; */
}
return True;
}
+static void smbd_parent_sig_term_handler(struct tevent_context *ev,
+ struct tevent_signal *se,
+ int signum,
+ int count,
+ void *siginfo,
+ void *private_data)
+{
+ exit_server_cleanly("termination signal");
+}
+
+static void smbd_parent_sig_hup_handler(struct tevent_context *ev,
+ struct tevent_signal *se,
+ int signum,
+ int count,
+ void *siginfo,
+ void *private_data)
+{
+ struct smbd_parent_context *parent =
+ talloc_get_type_abort(private_data,
+ struct smbd_parent_context);
+
+ change_to_root_user();
+ DEBUG(1,("parent: Reloading services after SIGHUP\n"));
+ reload_services(NULL, NULL, false);
+
+ printing_subsystem_update(parent->ev_ctx, parent->msg_ctx, true);
+}
+
/****************************************************************************
main program.
****************************************************************************/
{"port", 'p', POPT_ARG_STRING, &ports, 0, "Listen on the specified ports"},
{"profiling-level", 'P', POPT_ARG_STRING, &profile_level, 0, "Set profiling level","PROFILE_LEVEL"},
POPT_COMMON_SAMBA
- POPT_COMMON_DYNCONFIG
POPT_TABLEEND
};
struct smbd_parent_context *parent = NULL;
TALLOC_CTX *frame;
NTSTATUS status;
- uint64_t unique_id;
struct tevent_context *ev_ctx;
struct messaging_context *msg_ctx;
+ struct server_id server_id;
+ struct tevent_signal *se;
+ int profiling_level;
+ char *np_dir = NULL;
+ static const struct smbd_shim smbd_shim_fns =
+ {
+ .cancel_pending_lock_requests_by_fid = smbd_cancel_pending_lock_requests_by_fid,
+ .send_stat_cache_delete_message = smbd_send_stat_cache_delete_message,
+ .change_to_root_user = smbd_change_to_root_user,
+ .become_authenticated_pipe_user = smbd_become_authenticated_pipe_user,
+ .unbecome_authenticated_pipe_user = smbd_unbecome_authenticated_pipe_user,
+
+ .contend_level2_oplocks_begin = smbd_contend_level2_oplocks_begin,
+ .contend_level2_oplocks_end = smbd_contend_level2_oplocks_end,
+
+ .become_root = smbd_become_root,
+ .unbecome_root = smbd_unbecome_root,
+
+ .exit_server = smbd_exit_server,
+ .exit_server_cleanly = smbd_exit_server_cleanly,
+ };
/*
* Do this before any other talloc operation
load_case_tables();
+ set_smbd_shim(&smbd_shim_fns);
+
smbd_init_globals();
TimeInit();
gain_root_group_privilege();
fault_setup();
- dump_core_setup("smbd", lp_logfile());
+ dump_core_setup("smbd", lp_logfile(talloc_tos()));
/* we are never interested in SIGPIPE */
BlockSignals(True,SIGPIPE);
exit(1);
}
+ if (!cluster_probe_ok()) {
+ exit(1);
+ }
+
/* Init the security context and global current_user */
init_sec_ctx();
* Reloading of the printers will not work here as we don't have a
* server info and rpc services set up. It will be called later.
*/
- if (!reload_services(NULL, -1, False)) {
+ if (!reload_services(NULL, NULL, false)) {
+ exit(1);
+ }
+
+ if (lp_server_role() == ROLE_ACTIVE_DIRECTORY_DC
+ && !lp_parm_bool(-1, "server role check", "inhibit", false)) {
+ DEBUG(0, ("server role = 'active directory domain controller' not compatible with running smbd standalone. \n"));
+ DEBUGADD(0, ("You should start 'samba' instead, and it will control starting smbd if required\n"));
exit(1);
}
init_structs();
-#ifdef WITH_PROFILE
if (!profile_setup(msg_ctx, False)) {
DEBUG(0,("ERROR: failed to setup profiling\n"));
return -1;
}
- if (profile_level != NULL) {
- int pl = atoi(profile_level);
- struct server_id src;
- DEBUG(1, ("setting profiling level: %s\n",profile_level));
- src.pid = getpid();
- set_profile_level(pl, src);
+ if (profile_level != NULL) {
+ profiling_level = atoi(profile_level);
+ } else {
+ profiling_level = lp_smbd_profiling_level();
}
-#endif
+ set_profile_level(profiling_level, messaging_server_id(msg_ctx));
if (!is_daemon && !is_a_socket(0)) {
- if (!interactive)
- DEBUG(0,("standard input is not a socket, assuming -D option\n"));
+ if (!interactive) {
+ DEBUG(3, ("Standard input is not a socket, "
+ "assuming -D option\n"));
+ }
/*
* Setting is_daemon here prevents us from eventually calling
}
if (is_daemon && !interactive) {
- DEBUG( 3, ( "Becoming a daemon.\n" ) );
+ DEBUG(3, ("Becoming a daemon.\n"));
become_daemon(Fork, no_process_group, log_stdout);
}
- generate_random_buffer((uint8_t *)&unique_id, sizeof(unique_id));
- set_my_unique_id(unique_id);
+ set_my_unique_id(serverid_get_random_unique_id());
#if HAVE_SETPGID
/*
setpgid( (pid_t)0, (pid_t)0);
#endif
- if (!directory_exist(lp_lockdir()))
- mkdir(lp_lockdir(), 0755);
+ if (!directory_exist(lp_lock_directory()))
+ mkdir(lp_lock_directory(), 0755);
+
+ if (!directory_exist(lp_pid_directory()))
+ mkdir(lp_pid_directory(), 0755);
if (is_daemon)
- pidfile_create("smbd");
+ pidfile_create(lp_pid_directory(), "smbd");
status = reinit_after_fork(msg_ctx,
ev_ctx,
false);
if (!NT_STATUS_IS_OK(status)) {
- DEBUG(0,("reinit_after_fork() failed\n"));
- exit(1);
+ exit_daemon("reinit_after_fork() failed", map_errno_from_nt_status(status));
}
- smbd_server_conn->msg_ctx = msg_ctx;
+ if (!interactive) {
+ /*
+ * Do not initialize the parent-child-pipe before becoming a
+ * daemon: this is used to detect a died parent in the child
+ * process.
+ */
+ status = init_before_fork();
+ if (!NT_STATUS_IS_OK(status)) {
+ exit_daemon(nt_errstr(status), map_errno_from_nt_status(status));
+ }
+ }
- smbd_setup_sig_term_handler();
- smbd_setup_sig_hup_handler(ev_ctx,
- msg_ctx);
+ parent = talloc_zero(ev_ctx, struct smbd_parent_context);
+ if (!parent) {
+ exit_server("talloc(struct smbd_parent_context) failed");
+ }
+ parent->interactive = interactive;
+ parent->ev_ctx = ev_ctx;
+ parent->msg_ctx = msg_ctx;
+ am_parent = parent;
+
+ se = tevent_add_signal(parent->ev_ctx,
+ parent,
+ SIGTERM, 0,
+ smbd_parent_sig_term_handler,
+ parent);
+ if (!se) {
+ exit_server("failed to setup SIGTERM handler");
+ }
+ se = tevent_add_signal(parent->ev_ctx,
+ parent,
+ SIGHUP, 0,
+ smbd_parent_sig_hup_handler,
+ parent);
+ if (!se) {
+ exit_server("failed to setup SIGHUP handler");
+ }
/* Setup all the TDB's - including CLEAR_IF_FIRST tdb's. */
if (smbd_memcache() == NULL) {
- exit(1);
+ exit_daemon("no memcache available", EACCES);
}
memcache_set_global(smbd_memcache());
exit(1);
if (!secrets_init()) {
- DEBUG(0, ("ERROR: smbd can not open secrets.tdb\n"));
- exit(1);
+ exit_daemon("smbd can not open secrets.tdb", EACCES);
}
if (lp_server_role() == ROLE_DOMAIN_BDC || lp_server_role() == ROLE_DOMAIN_PDC) {
- struct loadparm_context *lp_ctx = loadparm_init_s3(NULL, loadparm_s3_context());
+ struct loadparm_context *lp_ctx = loadparm_init_s3(NULL, loadparm_s3_helpers());
if (!open_schannel_session_store(NULL, lp_ctx)) {
- DEBUG(0,("ERROR: Samba cannot open schannel store for secured NETLOGON operations.\n"));
- exit(1);
+ exit_daemon("ERROR: Samba cannot open schannel store for secured NETLOGON operations.", EACCES);
}
TALLOC_FREE(lp_ctx);
}
if(!get_global_sam_sid()) {
- DEBUG(0,("ERROR: Samba cannot create a SAM SID.\n"));
- exit(1);
+ exit_daemon("Samba cannot create a SAM SID", EACCES);
}
- if (!sessionid_init()) {
- exit(1);
+ server_id = messaging_server_id(msg_ctx);
+ status = smbXsrv_version_global_init(&server_id);
+ if (!NT_STATUS_IS_OK(status)) {
+ exit_daemon("Samba cannot init server context", EACCES);
}
- if (!connections_init(True))
- exit(1);
+ status = smbXsrv_session_global_init();
+ if (!NT_STATUS_IS_OK(status)) {
+ exit_daemon("Samba cannot init session context", EACCES);
+ }
+
+ status = smbXsrv_tcon_global_init();
+ if (!NT_STATUS_IS_OK(status)) {
+ exit_daemon("Samba cannot init tcon context", EACCES);
+ }
if (!locking_init())
- exit(1);
+ exit_daemon("Samba cannot init locking", EACCES);
- if (!messaging_tdb_parent_init(ev_ctx)) {
- exit(1);
+ if (!leases_db_init(false)) {
+ exit_daemon("Samba cannot init leases", EACCES);
}
- if (!notify_internal_parent_init(ev_ctx)) {
+ if (!smbd_parent_notify_init(NULL, msg_ctx, ev_ctx)) {
+ exit_daemon("Samba cannot init notification", EACCES);
+ }
+
+ if (!messaging_parent_dgm_cleanup_init(msg_ctx)) {
exit(1);
}
+ if (!smbd_scavenger_init(NULL, msg_ctx, ev_ctx)) {
+ exit_daemon("Samba cannot init scavenging", EACCES);
+ }
+
if (!serverid_parent_init(ev_ctx)) {
- exit(1);
+ exit_daemon("Samba cannot init server id", EACCES);
}
if (!W_ERROR_IS_OK(registry_init_full()))
- exit(1);
+ exit_daemon("Samba cannot init registry", EACCES);
/* Open the share_info.tdb here, so we don't have to open
after the fork on every single connection. This is a small
performance improvment and reduces the total number of system
fds used. */
if (!share_info_db_init()) {
- DEBUG(0,("ERROR: failed to load share info db.\n"));
- exit(1);
+ exit_daemon("ERROR: failed to load share info db.", EACCES);
}
- status = init_system_info();
+ status = init_system_session_info();
if (!NT_STATUS_IS_OK(status)) {
DEBUG(1, ("ERROR: failed to setup system user info: %s.\n",
nt_errstr(status)));
return -1;
}
- if (!file_init(smbd_server_conn)) {
- DEBUG(0, ("ERROR: file_init failed\n"));
+ if (!file_init_global()) {
+ DEBUG(0, ("ERROR: file_init_global() failed\n"));
return -1;
}
+ status = smbXsrv_open_global_init();
+ if (!NT_STATUS_IS_OK(status)) {
+ exit_daemon("Samba cannot init global open", map_errno_from_nt_status(status));
+ }
/* This MUST be done before start_epmd() because otherwise
* start_epmd() forks and races against dcesrv_ep_setup() to
* call directory_create_or_exist() */
- if (!directory_create_or_exist(lp_ncalrpc_dir(), geteuid(), 0755)) {
+ if (!directory_create_or_exist(lp_ncalrpc_dir(), 0755)) {
DEBUG(0, ("Failed to create pipe directory %s - %s\n",
lp_ncalrpc_dir(), strerror(errno)));
return -1;
}
+ np_dir = talloc_asprintf(talloc_tos(), "%s/np", lp_ncalrpc_dir());
+ if (!np_dir) {
+ DEBUG(0, ("%s: Out of memory\n", __location__));
+ return -1;
+ }
+
+ if (!directory_create_or_exist_strict(np_dir, geteuid(), 0700)) {
+ DEBUG(0, ("Failed to create pipe directory %s - %s\n",
+ np_dir, strerror(errno)));
+ return -1;
+ }
+
if (is_daemon && !interactive) {
if (rpc_epmapper_daemon() == RPC_DAEMON_FORK) {
start_epmd(ev_ctx, msg_ctx);
}
if (!dcesrv_ep_setup(ev_ctx, msg_ctx)) {
- exit(1);
+ exit_daemon("Samba cannot setup ep pipe", EACCES);
+ }
+
+ if (is_daemon && !interactive) {
+ daemon_ready("smbd");
}
/* only start other daemons if we are running as a daemon
start_lsasd(ev_ctx, msg_ctx);
}
- if (!_lp_disable_spoolss() &&
+ if (!lp__disable_spoolss() &&
(rpc_spoolss_daemon() != RPC_DAEMON_DISABLED)) {
bool bgq = lp_parm_bool(-1, "smbd", "backgroundqueue", true);
if (!printing_subsystem_init(ev_ctx, msg_ctx, true, bgq)) {
- exit(1);
+ exit_daemon("Samba failed to init printing subsystem", EACCES);
}
}
- } else if (!_lp_disable_spoolss() &&
+ } else if (!lp__disable_spoolss() &&
(rpc_spoolss_daemon() != RPC_DAEMON_DISABLED)) {
if (!printing_subsystem_init(ev_ctx, msg_ctx, false, false)) {
exit(1);
}
if (!is_daemon) {
+ int sock;
+
/* inetd mode */
TALLOC_FREE(frame);
/* Started from inetd. fd 0 is the socket. */
/* We will abort gracefully when the client or remote system
goes away */
- smbd_server_conn->sock = dup(0);
+ sock = dup(0);
- /* close our standard file descriptors */
- if (!debug_get_output_is_stdout()) {
- close_low_fds(False); /* Don't close stderr */
- }
+ /* close stdin, stdout (if not logging to it), but not stderr */
+ close_low_fds(true, !debug_get_output_is_stdout(), false);
#ifdef HAVE_ATEXIT
atexit(killkids);
#endif
/* Stop zombies */
- smbd_setup_sig_chld_handler(ev_ctx);
+ smbd_setup_sig_chld_handler(parent);
- smbd_process(ev_ctx, smbd_server_conn);
+ smbd_process(ev_ctx, msg_ctx, sock, true);
exit_server_cleanly(NULL);
return(0);
}
- parent = talloc_zero(ev_ctx, struct smbd_parent_context);
- if (!parent) {
- exit_server("talloc(struct smbd_parent_context) failed");
- }
- parent->interactive = interactive;
-
if (!open_sockets_smbd(parent, ev_ctx, msg_ctx, ports))
exit_server("open_sockets_smbd() failed");
/* do a printer update now that all messaging has been set up,
* before we allow clients to start connecting */
- printing_subsystem_update(ev_ctx, msg_ctx, false);
+ if (!lp__disable_spoolss() &&
+ (rpc_spoolss_daemon() != RPC_DAEMON_DISABLED)) {
+ printing_subsystem_update(ev_ctx, msg_ctx, false);
+ }
TALLOC_FREE(frame);
/* make sure we always have a valid stackframe */
frame = talloc_stackframe();
+ if (!Fork) {
+ /* if we are running in the foreground then look for
+ EOF on stdin, and exit if it happens. This allows
+ us to die if the parent process dies
+ Only do this on a pipe or socket, no other device.
+ */
+ struct stat st;
+ if (fstat(0, &st) != 0) {
+ return false;
+ }
+ if (S_ISFIFO(st.st_mode) || S_ISSOCK(st.st_mode)) {
+ tevent_add_fd(ev_ctx,
+ parent,
+ 0,
+ TEVENT_FD_READ,
+ smbd_stdin_handler,
+ NULL);
+ }
+ }
+
smbd_parent_loop(ev_ctx, parent);
exit_server_cleanly(NULL);