rpc_server: Give epmd its header file
[samba.git] / source3 / smbd / server.c
index 8207bf1676f822c8649f5882351c81e7c860be45..086991e6c8862e93e45ac5a11413573f8babb2cf 100644 (file)
@@ -23,6 +23,7 @@
 
 #include "includes.h"
 #include "system/filesys.h"
+#include "lib/util/server_id.h"
 #include "popt_common.h"
 #include "smbd/smbd.h"
 #include "smbd/globals.h"
 #include "printing/queue_process.h"
 #include "rpc_server/rpc_service_setup.h"
 #include "rpc_server/rpc_config.h"
-#include "serverid.h"
 #include "passdb.h"
 #include "auth.h"
 #include "messages.h"
+#include "messages_ctdb.h"
 #include "smbprofile.h"
 #include "lib/id_cache.h"
 #include "lib/param/param.h"
 #include "lib/smbd_shim.h"
 #include "scavenger.h"
 #include "locking/leases_db.h"
+#include "smbd/notifyd/notifyd.h"
+#include "smbd/smbd_cleanupd.h"
+#include "lib/util/sys_rw.h"
+#include "cleanupdb.h"
+#include "g_lock.h"
+#include "rpc_server/epmd.h"
+
+#ifdef CLUSTER_SUPPORT
+#include "ctdb_protocol.h"
+#endif
 
 struct smbd_open_socket;
 struct smbd_child_pid;
@@ -65,6 +76,9 @@ struct smbd_parent_context {
        struct smbd_child_pid *children;
        size_t num_children;
 
+       struct server_id cleanupd;
+       struct server_id notifyd;
+
        struct tevent_timer *cleanup_te;
 };
 
@@ -80,15 +94,14 @@ struct smbd_child_pid {
        pid_t pid;
 };
 
-extern void start_epmd(struct tevent_context *ev_ctx,
-                      struct messaging_context *msg_ctx);
-
 extern void start_lsasd(struct tevent_context *ev_ctx,
                        struct messaging_context *msg_ctx);
 
-#ifdef WITH_DFS
-extern int dcelogin_atmost_once;
-#endif /* WITH_DFS */
+extern void start_fssd(struct tevent_context *ev_ctx,
+                      struct messaging_context *msg_ctx);
+
+extern void start_mdssd(struct tevent_context *ev_ctx,
+                       struct messaging_context *msg_ctx);
 
 /*******************************************************************
  What to do when smb.conf is updated.
@@ -152,10 +165,11 @@ static void msg_inject_fault(struct messaging_context *msg,
                             DATA_BLOB *data)
 {
        int sig;
+       struct server_id_buf tmp;
 
        if (data->length != sizeof(sig)) {
                DEBUG(0, ("Process %s sent bogus signal injection request\n",
-                         procid_str_static(&src)));
+                         server_id_str_buf(src, &tmp)));
                return;
        }
 
@@ -165,18 +179,48 @@ static void msg_inject_fault(struct messaging_context *msg,
                return;
        }
 
-#if HAVE_STRSIGNAL
+#ifdef HAVE_STRSIGNAL
        DEBUG(0, ("Process %s requested injection of signal %d (%s)\n",
-                 procid_str_static(&src), sig, strsignal(sig)));
+                 server_id_str_buf(src, &tmp), sig, strsignal(sig)));
 #else
        DEBUG(0, ("Process %s requested injection of signal %d\n",
-                 procid_str_static(&src), sig));
+                 server_id_str_buf(src, &tmp), sig));
 #endif
 
        kill(getpid(), sig);
 }
 #endif /* DEVELOPER */
 
+#if defined(DEVELOPER) || defined(ENABLE_SELFTEST)
+/*
+ * Sleep for the specified number of seconds.
+ */
+static void msg_sleep(struct messaging_context *msg,
+                     void *private_data,
+                     uint32_t msg_type,
+                     struct server_id src,
+                     DATA_BLOB *data)
+{
+       unsigned int seconds;
+       struct server_id_buf tmp;
+
+       if (data->length != sizeof(seconds)) {
+               DBG_ERR("Process %s sent bogus sleep request\n",
+                       server_id_str_buf(src, &tmp));
+               return;
+       }
+
+       seconds = *(unsigned int *)data->data;
+       DBG_ERR("Process %s request a sleep of %u seconds\n",
+               server_id_str_buf(src, &tmp),
+               seconds);
+       sleep(seconds);
+       DBG_ERR("Restarting after %u second sleep requested by process %s\n",
+               seconds,
+               server_id_str_buf(src, &tmp));
+}
+#endif /* DEVELOPER */
+
 static NTSTATUS messaging_send_to_children(struct messaging_context *msg_ctx,
                                           uint32_t msg_type, DATA_BLOB* data)
 {
@@ -193,7 +237,8 @@ static NTSTATUS messaging_send_to_children(struct messaging_context *msg_ctx,
                                        pid_to_procid(child->pid),
                                        msg_type, data);
                if (!NT_STATUS_IS_OK(status)) {
-                       return status;
+                       DBG_DEBUG("messaging_send(%d) failed: %s\n",
+                                 (int)child->pid, nt_errstr(status));
                }
        }
        return NT_STATUS_OK;
@@ -256,131 +301,466 @@ static void smbd_parent_id_cache_delete(struct messaging_context *ctx,
        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;
-};
+#ifdef CLUSTER_SUPPORT
+static int smbd_parent_ctdb_reconfigured(
+       struct tevent_context *ev,
+       uint32_t src_vnn, uint32_t dst_vnn, uint64_t dst_srvid,
+       const uint8_t *msg, size_t msglen, void *private_data)
+{
+       struct messaging_context *msg_ctx = talloc_get_type_abort(
+               private_data, struct messaging_context);
+
+       DEBUG(10, ("Got %s message\n", (dst_srvid == CTDB_SRVID_RECONFIGURE)
+                  ? "cluster reconfigure" : "SAMBA_NOTIFY"));
+
+       /*
+        * Someone from the family died, validate our locks
+        */
+
+       if (am_parent) {
+               messaging_send_buf(msg_ctx, am_parent->cleanupd,
+                                  MSG_SMB_BRL_VALIDATE, NULL, 0);
+       }
+
+       return 0;
+}
+#endif
+
+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(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));
+       }
+}
 
-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 void notifyd_stopped(struct tevent_req *req);
 
-static bool smbd_parent_notify_init(TALLOC_CTX *mem_ctx,
-                                   struct messaging_context *msg,
-                                   struct tevent_context *ev)
+static struct tevent_req *notifyd_req(struct messaging_context *msg_ctx,
+                                     struct tevent_context *ev)
 {
-       struct smbd_parent_notify_state *state;
        struct tevent_req *req;
+       sys_notify_watch_fn sys_notify_watch = NULL;
+       struct sys_notify_context *sys_notify_ctx = NULL;
+       struct ctdbd_connection *ctdbd_conn = NULL;
 
-       state = talloc(mem_ctx, struct smbd_parent_notify_state);
-       if (state == NULL) {
-               return false;
+       if (lp_kernel_change_notify()) {
+
+#ifdef HAVE_INOTIFY
+               if (lp_parm_bool(-1, "notify", "inotify", true)) {
+                       sys_notify_watch = inotify_watch;
+               }
+#endif
+
+#ifdef HAVE_FAM
+               if (lp_parm_bool(-1, "notify", "fam",
+                                (sys_notify_watch == NULL))) {
+                       sys_notify_watch = fam_watch;
+               }
+#endif
        }
-       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;
+       if (sys_notify_watch != NULL) {
+               sys_notify_ctx = sys_notify_context_create(msg_ctx, ev);
+               if (sys_notify_ctx == NULL) {
+                       return NULL;
+               }
+       }
+
+       if (lp_clustering()) {
+               ctdbd_conn = messaging_ctdb_connection();
        }
-       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);
+
+       req = notifyd_send(msg_ctx, ev, msg_ctx, ctdbd_conn,
+                          sys_notify_watch, sys_notify_ctx);
        if (req == NULL) {
-               goto fail;
+               TALLOC_FREE(sys_notify_ctx);
+               return NULL;
+       }
+       tevent_req_set_callback(req, notifyd_stopped, msg_ctx);
+
+       return req;
+}
+
+static void notifyd_stopped(struct tevent_req *req)
+{
+       int ret;
+
+       ret = notifyd_recv(req);
+       TALLOC_FREE(req);
+       DEBUG(1, ("notifyd stopped: %s\n", strerror(ret)));
+}
+
+static bool smbd_notifyd_init(struct messaging_context *msg, bool interactive,
+                             struct server_id *ppid)
+{
+       struct tevent_context *ev = messaging_tevent_context(msg);
+       struct tevent_req *req;
+       pid_t pid;
+       NTSTATUS status;
+       bool ok;
+
+       if (interactive) {
+               req = notifyd_req(msg, ev);
+               return (req != NULL);
        }
-       tevent_req_set_callback(req, smbd_parent_notify_cleanup_done, state);
 
-       if (!lp_clustering()) {
+       pid = fork();
+       if (pid == -1) {
+               DEBUG(1, ("%s: fork failed: %s\n", __func__,
+                         strerror(errno)));
+               return false;
+       }
+
+       if (pid != 0) {
+               if (am_parent != 0) {
+                       add_child_pid(am_parent, pid);
+               }
+               *ppid = pid_to_procid(pid);
                return true;
        }
 
-       req = notify_cluster_proxy_send(state, ev, state->notify);
+       status = smbd_reinit_after_fork(msg, ev, true, "smbd-notifyd");
+       if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(1, ("%s: reinit_after_fork failed: %s\n",
+                         __func__, nt_errstr(status)));
+               exit(1);
+       }
+
+       req = notifyd_req(msg, ev);
        if (req == NULL) {
-               goto fail;
+               exit(1);
        }
-       tevent_req_set_callback(req, smbd_parent_notify_proxy_done, state);
+       tevent_req_set_callback(req, notifyd_stopped, msg);
 
-       return true;
-fail:
-       TALLOC_FREE(state);
-       return false;
+       /* Block those signals that we are not handling */
+       BlockSignals(True, SIGHUP);
+       BlockSignals(True, SIGUSR1);
+
+       messaging_send(msg, pid_to_procid(getppid()), MSG_SMB_NOTIFY_STARTED,
+                      NULL);
+
+       ok = tevent_req_poll(req, ev);
+       if (!ok) {
+               DBG_WARNING("tevent_req_poll returned %s\n", strerror(errno));
+               exit(1);
+       }
+       exit(0);
 }
 
-static int smbd_parent_notify_cleanup(void *private_data)
+static void notifyd_init_trigger(struct tevent_req *req);
+
+struct notifyd_init_state {
+       bool ok;
+       struct tevent_context *ev;
+       struct messaging_context *msg;
+       struct server_id *ppid;
+};
+
+static struct tevent_req *notifyd_init_send(struct tevent_context *ev,
+                                           TALLOC_CTX *mem_ctx,
+                                           struct messaging_context *msg,
+                                           struct server_id *ppid)
 {
-       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);
+       struct tevent_req *req = NULL;
+       struct tevent_req *subreq = NULL;
+       struct notifyd_init_state *state = NULL;
+
+       req = tevent_req_create(mem_ctx, &state, struct notifyd_init_state);
+       if (req == NULL) {
+               return NULL;
+       }
+
+       *state = (struct notifyd_init_state) {
+               .msg = msg,
+               .ev = ev,
+               .ppid = ppid
+       };
+
+       subreq = tevent_wakeup_send(state, ev, tevent_timeval_current_ofs(1, 0));
+       if (tevent_req_nomem(subreq, req)) {
+               return tevent_req_post(req, ev);
+       }
+
+       tevent_req_set_callback(subreq, notifyd_init_trigger, req);
+       return req;
 }
 
-static void smbd_parent_notify_cleanup_done(struct tevent_req *req)
+static void notifyd_init_trigger(struct tevent_req *subreq)
 {
-       struct smbd_parent_notify_state *state = tevent_req_callback_data(
-               req, struct smbd_parent_notify_state);
-       NTSTATUS status;
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct notifyd_init_state *state = tevent_req_data(
+               req, struct notifyd_init_state);
+       bool ok;
+
+       DBG_NOTICE("Triggering notifyd startup\n");
+
+       ok = tevent_wakeup_recv(subreq);
+       TALLOC_FREE(subreq);
+       if (!ok) {
+               tevent_req_error(req, ENOMEM);
+               return;
+       }
 
-       status = background_job_recv(req);
-       TALLOC_FREE(req);
-       DEBUG(1, ("notify cleanup job ended with %s\n", nt_errstr(status)));
+       state->ok = smbd_notifyd_init(state->msg, false, state->ppid);
+       if (state->ok) {
+               DBG_WARNING("notifyd restarted\n");
+               tevent_req_done(req);
+               return;
+       }
 
-       /*
-        * 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"));
+       DBG_NOTICE("notifyd startup failed, rescheduling\n");
+
+       subreq = tevent_wakeup_send(state, state->ev,
+                                   tevent_timeval_current_ofs(1, 0));
+       if (tevent_req_nomem(subreq, req)) {
+               DBG_ERR("scheduling notifyd restart failed, giving up\n");
                return;
        }
-       tevent_req_set_callback(req, smbd_parent_notify_cleanup_done, state);
+
+       tevent_req_set_callback(subreq, notifyd_init_trigger, req);
+       return;
 }
 
-static void smbd_parent_notify_proxy_done(struct tevent_req *req)
+static bool notifyd_init_recv(struct tevent_req *req)
 {
-       int ret;
+       struct notifyd_init_state *state = tevent_req_data(
+               req, struct notifyd_init_state);
 
-       ret = notify_cluster_proxy_recv(req);
-       TALLOC_FREE(req);
-       DEBUG(1, ("notify proxy job ended with %s\n", strerror(ret)));
+       return state->ok;
 }
 
-static void add_child_pid(struct smbd_parent_context *parent,
-                         pid_t pid)
+static void notifyd_started(struct tevent_req *req)
 {
-       struct smbd_child_pid *child;
+       bool ok;
 
-       child = talloc_zero(parent, struct smbd_child_pid);
-       if (child == NULL) {
-               DEBUG(0, ("Could not add child struct -- malloc failed\n"));
+       ok = notifyd_init_recv(req);
+       TALLOC_FREE(req);
+       if (!ok) {
+               DBG_ERR("Failed to restart notifyd, giving up\n");
                return;
        }
-       child->pid = pid;
-       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)
+static void cleanupd_stopped(struct tevent_req *req);
+
+static bool cleanupd_init(struct messaging_context *msg, bool interactive,
+                         struct server_id *ppid)
 {
-       uint8_t buf[sizeof(uint32_t)];
+       struct tevent_context *ev = messaging_tevent_context(msg);
+       struct server_id parent_id = messaging_server_id(msg);
+       struct tevent_req *req;
+       pid_t pid;
+       NTSTATUS status;
+       ssize_t rwret;
+       int ret;
+       bool ok;
+       char c;
+       int up_pipe[2];
 
-       if (am_parent) {
-               SIVAL(buf, 0, am_parent->num_children);
-               messaging_send_buf(ctx, srv_id, MSG_SMB_NUM_CHILDREN,
-                                  buf, sizeof(buf));
+       if (interactive) {
+               req = smbd_cleanupd_send(msg, ev, msg, parent_id.pid);
+               *ppid = messaging_server_id(msg);
+               return (req != NULL);
+       }
+
+       ret = pipe(up_pipe);
+       if (ret == -1) {
+               DBG_WARNING("pipe failed: %s\n", strerror(errno));
+               return false;
+       }
+
+       pid = fork();
+       if (pid == -1) {
+               DBG_WARNING("fork failed: %s\n", strerror(errno));
+               close(up_pipe[0]);
+               close(up_pipe[1]);
+               return false;
+       }
+
+       if (pid != 0) {
+
+               close(up_pipe[1]);
+               rwret = sys_read(up_pipe[0], &c, 1);
+               close(up_pipe[0]);
+
+               if (rwret == -1) {
+                       DBG_WARNING("sys_read failed: %s\n", strerror(errno));
+                       return false;
+               }
+               if (rwret == 0) {
+                       DBG_WARNING("cleanupd could not start\n");
+                       return false;
+               }
+               if (c != 0) {
+                       DBG_WARNING("cleanupd returned %d\n", (int)c);
+                       return false;
+               }
+
+               DBG_DEBUG("Started cleanupd pid=%d\n", (int)pid);
+
+               if (am_parent != NULL) {
+                       add_child_pid(am_parent, pid);
+               }
+
+               *ppid = pid_to_procid(pid);
+               return true;
+       }
+
+       close(up_pipe[0]);
+
+       status = smbd_reinit_after_fork(msg, ev, true, "cleanupd");
+       if (!NT_STATUS_IS_OK(status)) {
+               DBG_WARNING("reinit_after_fork failed: %s\n",
+                           nt_errstr(status));
+               c = 1;
+               sys_write(up_pipe[1], &c, 1);
+
+               exit(1);
+       }
+
+       req = smbd_cleanupd_send(msg, ev, msg, parent_id.pid);
+       if (req == NULL) {
+               DBG_WARNING("smbd_cleanupd_send failed\n");
+               c = 2;
+               sys_write(up_pipe[1], &c, 1);
+
+               exit(1);
+       }
+
+       tevent_req_set_callback(req, cleanupd_stopped, msg);
+
+       c = 0;
+       rwret = sys_write(up_pipe[1], &c, 1);
+       close(up_pipe[1]);
+
+       if (rwret == -1) {
+               DBG_WARNING("sys_write failed: %s\n", strerror(errno));
+               exit(1);
+       }
+       if (rwret != 1) {
+               DBG_WARNING("sys_write could not write result\n");
+               exit(1);
+       }
+
+       ok = tevent_req_poll(req, ev);
+       if (!ok) {
+               DBG_WARNING("tevent_req_poll returned %s\n", strerror(errno));
+       }
+       exit(0);
+}
+
+static void cleanupd_stopped(struct tevent_req *req)
+{
+       NTSTATUS status;
+
+       status = smbd_cleanupd_recv(req);
+       DBG_WARNING("cleanupd stopped: %s\n", nt_errstr(status));
+}
+
+static void cleanupd_init_trigger(struct tevent_req *req);
+
+struct cleanup_init_state {
+       bool ok;
+       struct tevent_context *ev;
+       struct messaging_context *msg;
+       struct server_id *ppid;
+};
+
+static struct tevent_req *cleanupd_init_send(struct tevent_context *ev,
+                                            TALLOC_CTX *mem_ctx,
+                                            struct messaging_context *msg,
+                                            struct server_id *ppid)
+{
+       struct tevent_req *req = NULL;
+       struct tevent_req *subreq = NULL;
+       struct cleanup_init_state *state = NULL;
+
+       req = tevent_req_create(mem_ctx, &state, struct cleanup_init_state);
+       if (req == NULL) {
+               return NULL;
+       }
+
+       *state = (struct cleanup_init_state) {
+               .msg = msg,
+               .ev = ev,
+               .ppid = ppid
+       };
+
+       subreq = tevent_wakeup_send(state, ev, tevent_timeval_current_ofs(0, 0));
+       if (tevent_req_nomem(subreq, req)) {
+               return tevent_req_post(req, ev);
+       }
+
+       tevent_req_set_callback(subreq, cleanupd_init_trigger, req);
+       return req;
+}
+
+static void cleanupd_init_trigger(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct cleanup_init_state *state = tevent_req_data(
+               req, struct cleanup_init_state);
+       bool ok;
+
+       DBG_NOTICE("Triggering cleanupd startup\n");
+
+       ok = tevent_wakeup_recv(subreq);
+       TALLOC_FREE(subreq);
+       if (!ok) {
+               tevent_req_error(req, ENOMEM);
+               return;
+       }
+
+       state->ok = cleanupd_init(state->msg, false, state->ppid);
+       if (state->ok) {
+               DBG_WARNING("cleanupd restarted\n");
+               tevent_req_done(req);
+               return;
+       }
+
+       DBG_NOTICE("cleanupd startup failed, rescheduling\n");
+
+       subreq = tevent_wakeup_send(state, state->ev,
+                                   tevent_timeval_current_ofs(1, 0));
+       if (tevent_req_nomem(subreq, req)) {
+               DBG_ERR("scheduling cleanupd restart failed, giving up\n");
+               return;
        }
+
+       tevent_req_set_callback(subreq, cleanupd_init_trigger, req);
+       return;
 }
 
+static bool cleanupd_init_recv(struct tevent_req *req)
+{
+       struct cleanup_init_state *state = tevent_req_data(
+               req, struct cleanup_init_state);
+
+       return state->ok;
+}
 
 /*
   at most every smbd:cleanuptime seconds (default 20), we scan the BRL
@@ -404,29 +784,41 @@ static void cleanup_timeout_fn(struct tevent_context *event_ctx,
 
        parent->cleanup_te = NULL;
 
-       DEBUG(1,("Cleaning up brl and lock database after unclean shutdown\n"));
-       message_send_all(parent->msg_ctx, MSG_SMB_UNLOCK, NULL, 0, NULL);
-       messaging_send_buf(parent->msg_ctx,
-                          messaging_server_id(parent->msg_ctx),
+       messaging_send_buf(parent->msg_ctx, parent->cleanupd,
                           MSG_SMB_BRL_VALIDATE, NULL, 0);
 }
 
+static void cleanupd_started(struct tevent_req *req)
+{
+       bool ok;
+       NTSTATUS status;
+       struct smbd_parent_context *parent = tevent_req_callback_data(
+               req, struct smbd_parent_context);
+
+       ok = cleanupd_init_recv(req);
+       TALLOC_FREE(req);
+       if (!ok) {
+               DBG_ERR("Failed to restart cleanupd, giving up\n");
+               return;
+       }
+
+       status = messaging_send(parent->msg_ctx,
+                               parent->cleanupd,
+                               MSG_SMB_NOTIFY_CLEANUP,
+                               &data_blob_null);
+       if (!NT_STATUS_IS_OK(status)) {
+               DBG_ERR("messaging_send returned %s\n",
+                       nt_errstr(status));
+       }
+}
+
 static void remove_child_pid(struct smbd_parent_context *parent,
                             pid_t pid,
                             bool unclean_shutdown)
 {
        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)));
-       }
+       NTSTATUS status;
+       bool ok;
 
        for (child = parent->children; child != NULL; child = child->next) {
                if (child->pid == pid) {
@@ -444,6 +836,61 @@ static void remove_child_pid(struct smbd_parent_context *parent,
                return;
        }
 
+       if (pid == procid_to_pid(&parent->cleanupd)) {
+               struct tevent_req *req;
+
+               server_id_set_disconnected(&parent->cleanupd);
+
+               DBG_WARNING("Restarting cleanupd\n");
+               req = cleanupd_init_send(messaging_tevent_context(parent->msg_ctx),
+                                        parent,
+                                        parent->msg_ctx,
+                                        &parent->cleanupd);
+               if (req == NULL) {
+                       DBG_ERR("Failed to restart cleanupd\n");
+                       return;
+               }
+               tevent_req_set_callback(req, cleanupd_started, parent);
+               return;
+       }
+
+       if (pid == procid_to_pid(&parent->notifyd)) {
+               struct tevent_req *req;
+               struct tevent_context *ev = messaging_tevent_context(
+                       parent->msg_ctx);
+
+               server_id_set_disconnected(&parent->notifyd);
+
+               DBG_WARNING("Restarting notifyd\n");
+               req = notifyd_init_send(ev,
+                                       parent,
+                                       parent->msg_ctx,
+                                       &parent->notifyd);
+               if (req == NULL) {
+                       DBG_ERR("Failed to restart notifyd\n");
+                       return;
+               }
+               tevent_req_set_callback(req, notifyd_started, parent);
+               return;
+       }
+
+       ok = cleanupdb_store_child(pid, unclean_shutdown);
+       if (!ok) {
+               DBG_ERR("cleanupdb_store_child failed\n");
+               return;
+       }
+
+       if (!server_id_is_disconnected(&parent->cleanupd)) {
+               status = messaging_send(parent->msg_ctx,
+                                       parent->cleanupd,
+                                       MSG_SMB_NOTIFY_CLEANUP,
+                                       &data_blob_null);
+               if (!NT_STATUS_IS_OK(status)) {
+                       DBG_ERR("messaging_send returned %s\n",
+                               nt_errstr(status));
+               }
+       }
+
        if (unclean_shutdown) {
                /* a child terminated uncleanly so tickle all
                   processes to see if they can grab any of the
@@ -462,11 +909,6 @@ static void remove_child_pid(struct smbd_parent_context *parent,
                        DEBUG(1,("Scheduled cleanup of brl and lock database after unclean shutdown\n"));
                }
        }
-
-       if (!serverid_deregister(child_id)) {
-               DEBUG(1, ("Could not remove pid %d from serverid.tdb\n",
-                         (int)pid));
-       }
 }
 
 /****************************************************************************
@@ -496,7 +938,7 @@ static void smbd_sig_chld_handler(struct tevent_context *ev,
                talloc_get_type_abort(private_data,
                struct smbd_parent_context);
 
-       while ((pid = sys_waitpid(-1, &status, WNOHANG)) > 0) {
+       while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
                bool unclean_shutdown = False;
 
                /* If the child terminated normally, assume
@@ -551,7 +993,6 @@ static void smbd_accept_connection(struct tevent_context *ev,
        socklen_t in_addrlen = sizeof(addr);
        int fd;
        pid_t pid = 0;
-       uint64_t unique_id;
 
        fd = accept(s->fd, (struct sockaddr *)(void *)&addr,&in_addrlen);
        if (fd == -1 && errno == EINTR)
@@ -562,9 +1003,10 @@ static void smbd_accept_connection(struct tevent_context *ev,
                         strerror(errno)));
                return;
        }
+       smb_set_close_on_exec(fd);
 
        if (s->parent->interactive) {
-               reinit_after_fork(msg_ctx, ev, true);
+               reinit_after_fork(msg_ctx, ev, true, NULL);
                smbd_process(ev, msg_ctx, fd, true);
                exit_server_cleanly("end of interactive mode");
                return;
@@ -575,19 +1017,10 @@ static void smbd_accept_connection(struct tevent_context *ev,
                return;
        }
 
-       /*
-        * Generate a unique id in the parent process so that we use
-        * the global random state in the parent.
-        */
-       unique_id = serverid_get_random_unique_id();
-
        pid = fork();
        if (pid == 0) {
                NTSTATUS status = NT_STATUS_OK;
 
-               /* Child code ... */
-               am_parent = NULL;
-
                /*
                 * Can't use TALLOC_FREE here. Nulling out the argument to it
                 * would overwrite memory we've just freed.
@@ -595,15 +1028,11 @@ static void smbd_accept_connection(struct tevent_context *ev,
                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);
+               status = smbd_reinit_after_fork(msg_ctx, ev, true, NULL);
                if (!NT_STATUS_IS_OK(status)) {
                        if (NT_STATUS_EQUAL(status,
                                            NT_STATUS_TOO_MANY_OPENED_FILES)) {
@@ -612,11 +1041,14 @@ static void smbd_accept_connection(struct tevent_context *ev,
                                goto exit;
                        }
                        if (lp_clustering() &&
-                           NT_STATUS_EQUAL(status,
-                           NT_STATUS_INTERNAL_DB_ERROR)) {
-                               DEBUG(1,("child process cannot initialize "
-                                        "because connection to CTDB "
-                                        "has failed\n"));
+                           (NT_STATUS_EQUAL(
+                                   status, NT_STATUS_INTERNAL_DB_ERROR) ||
+                            NT_STATUS_EQUAL(
+                                   status, NT_STATUS_CONNECTION_REFUSED))) {
+                               DEBUG(1, ("child process cannot initialize "
+                                         "because connection to CTDB "
+                                         "has failed: %s\n",
+                                         nt_errstr(status)));
                                goto exit;
                        }
 
@@ -686,7 +1118,7 @@ static bool smbd_open_one_socket(struct smbd_parent_context *parent,
                               ifss,
                               true);
        if (s->fd == -1) {
-               DEBUG(0,("smbd_open_once_socket: open_socket_in: "
+               DEBUG(0,("smbd_open_one_socket: open_socket_in: "
                        "%s\n", strerror(errno)));
                TALLOC_FREE(s);
                /*
@@ -704,7 +1136,7 @@ static bool smbd_open_one_socket(struct smbd_parent_context *parent,
        set_blocking(s->fd, False);
 
        if (listen(s->fd, SMBD_LISTEN_BACKLOG) == -1) {
-               DEBUG(0,("open_sockets_smbd: listen: "
+               DEBUG(0,("smbd_open_one_socket: listen: "
                        "%s\n", strerror(errno)));
                        close(s->fd);
                TALLOC_FREE(s);
@@ -717,7 +1149,7 @@ static bool smbd_open_one_socket(struct smbd_parent_context *parent,
                               smbd_accept_connection,
                               s);
        if (!s->fde) {
-               DEBUG(0,("open_sockets_smbd: "
+               DEBUG(0,("smbd_open_one_socket: "
                         "tevent_add_fd: %s\n",
                         strerror(errno)));
                close(s->fd);
@@ -726,7 +1158,7 @@ static bool smbd_open_one_socket(struct smbd_parent_context *parent,
        }
        tevent_fd_set_close_fn(s->fde, smbd_open_socket_close_fn);
 
-       DLIST_ADD_END(parent->sockets, s, struct smbd_open_socket *);
+       DLIST_ADD_END(parent->sockets, s);
 
        return true;
 }
@@ -814,7 +1246,7 @@ static bool open_sockets_smbd(struct smbd_parent_context *parent,
                char *sock_tok;
                const char *sock_ptr;
 
-#if HAVE_IPV6
+#ifdef HAVE_IPV6
                sock_addr = "::,0.0.0.0";
 #else
                sock_addr = "0.0.0.0";
@@ -859,21 +1291,6 @@ static bool open_sockets_smbd(struct smbd_parent_context *parent,
                return false;
        }
 
-       /* Setup the main smbd so that we can get messages. Note that
-          do this after starting listening. This is needed as when in
-          clustered mode, ctdb won't allow us to start doing database
-          operations until it has gone thru a full startup, which
-          includes checking to see that smbd is listening. */
-
-       if (!serverid_register(messaging_server_id(msg_ctx),
-                              FLAG_MSG_GENERAL|FLAG_MSG_SMBD
-                              |FLAG_MSG_PRINT_GENERAL
-                              |FLAG_MSG_DBWRAP)) {
-               DEBUG(0, ("open_sockets_smbd: Failed to register "
-                         "myself in serverid.tdb\n"));
-               return false;
-       }
-
         /* Listen to messages */
 
        messaging_register(msg_ctx, NULL, MSG_SHUTDOWN, msg_exit_server);
@@ -882,8 +1299,6 @@ static bool open_sockets_smbd(struct smbd_parent_context *parent,
        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, 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,
@@ -895,16 +1310,29 @@ static bool open_sockets_smbd(struct smbd_parent_context *parent,
                           ID_CACHE_DELETE, smbd_parent_id_cache_delete);
        messaging_register(msg_ctx, NULL,
                           ID_CACHE_KILL, smbd_parent_id_cache_kill);
+       messaging_register(msg_ctx, NULL, MSG_SMB_NOTIFY_STARTED,
+                          smb_parent_send_to_children);
 
+#ifdef CLUSTER_SUPPORT
        if (lp_clustering()) {
-               ctdbd_register_reconfigure(messaging_ctdbd_connection());
+               struct ctdbd_connection *conn = messaging_ctdb_connection();
+
+               register_with_ctdbd(conn, CTDB_SRVID_RECONFIGURE,
+                                   smbd_parent_ctdb_reconfigured, msg_ctx);
+               register_with_ctdbd(conn, CTDB_SRVID_SAMBA_NOTIFY,
+                                   smbd_parent_ctdb_reconfigured, msg_ctx);
        }
+#endif
 
 #ifdef DEVELOPER
        messaging_register(msg_ctx, NULL, MSG_SMB_INJECT_FAULT,
                           msg_inject_fault);
 #endif
 
+#if defined(DEVELOPER) || defined(ENABLE_SELFTEST)
+       messaging_register(msg_ctx, NULL, MSG_SMB_SLEEP, msg_sleep);
+#endif
+
        if (lp_multicast_dns_register() && (dns_port != 0)) {
 #ifdef WITH_DNSSD_SUPPORT
                smbd_setup_mdns_registration(ev_ctx,
@@ -1044,6 +1472,112 @@ static void smbd_parent_sig_hup_handler(struct tevent_context *ev,
        printing_subsystem_update(parent->ev_ctx, parent->msg_ctx, true);
 }
 
+struct smbd_claim_version_state {
+       TALLOC_CTX *mem_ctx;
+       char *version;
+};
+
+static void smbd_claim_version_parser(const struct g_lock_rec *locks,
+                                     size_t num_locks,
+                                     const uint8_t *data,
+                                     size_t datalen,
+                                     void *private_data)
+{
+       struct smbd_claim_version_state *state = private_data;
+
+       if (datalen == 0) {
+               state->version = NULL;
+               return;
+       }
+       if (data[datalen-1] != '\0') {
+               DBG_WARNING("Invalid samba version\n");
+               dump_data(DBGLVL_WARNING, data, datalen);
+               state->version = NULL;
+               return;
+       }
+       state->version = talloc_strdup(state->mem_ctx, (const char *)data);
+}
+
+static NTSTATUS smbd_claim_version(struct messaging_context *msg,
+                                  const char *version)
+{
+       const char *name = "samba_version_string";
+       struct smbd_claim_version_state state;
+       struct g_lock_ctx *ctx;
+       NTSTATUS status;
+
+       ctx = g_lock_ctx_init(msg, msg);
+       if (ctx == NULL) {
+               DBG_WARNING("g_lock_ctx_init failed\n");
+               return NT_STATUS_UNSUCCESSFUL;
+       }
+
+       status = g_lock_lock(ctx, string_term_tdb_data(name), G_LOCK_READ,
+                            (struct timeval) { .tv_sec = 60 });
+       if (!NT_STATUS_IS_OK(status)) {
+               DBG_WARNING("g_lock_lock(G_LOCK_READ) failed: %s\n",
+                           nt_errstr(status));
+               TALLOC_FREE(ctx);
+               return status;
+       }
+
+       state = (struct smbd_claim_version_state) { .mem_ctx = ctx };
+
+       status = g_lock_dump(ctx, string_term_tdb_data(name),
+                            smbd_claim_version_parser, &state);
+       if (!NT_STATUS_IS_OK(status) &&
+           !NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
+               DBG_ERR("Could not read samba_version_string\n");
+               g_lock_unlock(ctx, string_term_tdb_data(name));
+               TALLOC_FREE(ctx);
+               return status;
+       }
+
+       if ((state.version != NULL) && (strcmp(version, state.version) == 0)) {
+               /*
+                * Leave the read lock for us around. Someone else already
+                * set the version correctly
+                */
+               TALLOC_FREE(ctx);
+               return NT_STATUS_OK;
+       }
+
+       status = g_lock_lock(ctx, string_term_tdb_data(name), G_LOCK_WRITE,
+                            (struct timeval) { .tv_sec = 60 });
+       if (!NT_STATUS_IS_OK(status)) {
+               DBG_WARNING("g_lock_lock(G_LOCK_WRITE) failed: %s\n",
+                           nt_errstr(status));
+               DBG_ERR("smbd %s already running, refusing to start "
+                       "version %s\n", state.version, version);
+               TALLOC_FREE(ctx);
+               return NT_STATUS_SXS_VERSION_CONFLICT;
+       }
+
+       status = g_lock_write_data(ctx, string_term_tdb_data(name),
+                                  (const uint8_t *)version,
+                                  strlen(version)+1);
+       if (!NT_STATUS_IS_OK(status)) {
+               DBG_WARNING("g_lock_write_data failed: %s\n",
+                           nt_errstr(status));
+               TALLOC_FREE(ctx);
+               return status;
+       }
+
+       status = g_lock_lock(ctx, string_term_tdb_data(name), G_LOCK_READ,
+                            (struct timeval) { .tv_sec = 60 });
+       if (!NT_STATUS_IS_OK(status)) {
+               DBG_WARNING("g_lock_lock(G_LOCK_READ) failed: %s\n",
+                           nt_errstr(status));
+               TALLOC_FREE(ctx);
+               return status;
+       }
+
+       /*
+        * Leave "ctx" dangling so that g_lock.tdb keeps opened.
+        */
+       return NT_STATUS_OK;
+}
+
 /****************************************************************************
  main program.
 ****************************************************************************/
@@ -1067,6 +1601,7 @@ extern void build_options(bool screen);
        int opt;
        poptContext pc;
        bool print_build_options = False;
+       struct server_id main_server_id = {0};
         enum {
                OPT_DAEMON = 1000,
                OPT_INTERACTIVE,
@@ -1075,17 +1610,73 @@ extern void build_options(bool screen);
                OPT_LOG_STDOUT
        };
        struct poptOption long_options[] = {
-       POPT_AUTOHELP
-       {"daemon", 'D', POPT_ARG_NONE, NULL, OPT_DAEMON, "Become a daemon (default)" },
-       {"interactive", 'i', POPT_ARG_NONE, NULL, OPT_INTERACTIVE, "Run interactive (not a daemon)"},
-       {"foreground", 'F', POPT_ARG_NONE, NULL, OPT_FORK, "Run daemon in foreground (for daemontools, etc.)" },
-       {"no-process-group", '\0', POPT_ARG_NONE, NULL, OPT_NO_PROCESS_GROUP, "Don't create a new process group" },
-       {"log-stdout", 'S', POPT_ARG_NONE, NULL, OPT_LOG_STDOUT, "Log to stdout" },
-       {"build-options", 'b', POPT_ARG_NONE, NULL, 'b', "Print build options" },
-       {"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_TABLEEND
+               POPT_AUTOHELP
+               {
+                       .longName   = "daemon",
+                       .shortName  = 'D',
+                       .argInfo    = POPT_ARG_NONE,
+                       .arg        = NULL,
+                       .val        = OPT_DAEMON,
+                       .descrip    = "Become a daemon (default)" ,
+               },
+               {
+                       .longName   = "interactive",
+                       .shortName  = 'i',
+                       .argInfo    = POPT_ARG_NONE,
+                       .arg        = NULL,
+                       .val        = OPT_INTERACTIVE,
+                       .descrip    = "Run interactive (not a daemon) and log to stdout",
+               },
+               {
+                       .longName   = "foreground",
+                       .shortName  = 'F',
+                       .argInfo    = POPT_ARG_NONE,
+                       .arg        = NULL,
+                       .val        = OPT_FORK,
+                       .descrip    = "Run daemon in foreground (for daemontools, etc.)",
+               },
+               {
+                       .longName   = "no-process-group",
+                       .shortName  = '\0',
+                       .argInfo    = POPT_ARG_NONE,
+                       .arg        = NULL,
+                       .val        = OPT_NO_PROCESS_GROUP,
+                       .descrip    = "Don't create a new process group" ,
+               },
+               {
+                       .longName   = "log-stdout",
+                       .shortName  = 'S',
+                       .argInfo    = POPT_ARG_NONE,
+                       .arg        = NULL,
+                       .val        = OPT_LOG_STDOUT,
+                       .descrip    = "Log to stdout" ,
+               },
+               {
+                       .longName   = "build-options",
+                       .shortName  = 'b',
+                       .argInfo    = POPT_ARG_NONE,
+                       .arg        = NULL,
+                       .val        = 'b',
+                       .descrip    = "Print build options" ,
+               },
+               {
+                       .longName   = "port",
+                       .shortName  = 'p',
+                       .argInfo    = POPT_ARG_STRING,
+                       .arg        = &ports,
+                       .val        = 0,
+                       .descrip    = "Listen on the specified ports",
+               },
+               {
+                       .longName   = "profiling-level",
+                       .shortName  = 'P',
+                       .argInfo    = POPT_ARG_STRING,
+                       .arg        = &profile_level,
+                       .val        = 0,
+                       .descrip    = "Set profiling level","PROFILE_LEVEL",
+               },
+               POPT_COMMON_SAMBA
+               POPT_TABLEEND
        };
        struct smbd_parent_context *parent = NULL;
        TALLOC_CTX *frame;
@@ -1122,7 +1713,7 @@ extern void build_options(bool screen);
 
        setup_logging(argv[0], DEBUG_DEFAULT_STDOUT);
 
-       load_case_tables();
+       smb_init_locale();
 
        set_smbd_shim(&smbd_shim_fns);
 
@@ -1196,8 +1787,9 @@ extern void build_options(bool screen);
                exit(1);
        }
 
-       /* we want to re-seed early to prevent time delays causing
-           client problems at a later date. (tridge) */
+       /*
+        * We want to die early if we can't open /dev/urandom
+        */
        generate_random_buffer(NULL, 0);
 
        /* get initial effective uid and gid */
@@ -1250,7 +1842,7 @@ extern void build_options(bool screen);
        /* Output the build options to the debug log */ 
        build_options(False);
 
-       if (sizeof(uint16) < 2 || sizeof(uint32) < 4) {
+       if (sizeof(uint16_t) < 2 || sizeof(uint32_t) < 4) {
                DEBUG(0,("ERROR: Samba is not configured correctly for the word size on your machine\n"));
                exit(1);
        }
@@ -1260,6 +1852,13 @@ extern void build_options(bool screen);
                exit(1);
        }
 
+       /*
+        * This calls unshare(CLONE_FS); on linux
+        * in order to check if the running kernel/container
+        * environment supports it.
+        */
+       per_thread_cwd_check();
+
        if (!cluster_probe_ok()) {
                exit(1);
        }
@@ -1271,9 +1870,8 @@ extern void build_options(bool screen);
         * Initialize the event context. The event context needs to be
         * initialized before the messaging context, cause the messaging
         * context holds an event context.
-        * FIXME: This should be s3_tevent_context_init()
         */
-       ev_ctx = server_event_context();
+       ev_ctx = global_event_context();
        if (ev_ctx == NULL) {
                exit(1);
        }
@@ -1282,7 +1880,7 @@ extern void build_options(bool screen);
         * Init the messaging context
         * FIXME: This should only call messaging_init()
         */
-       msg_ctx = server_messaging_context();
+       msg_ctx = global_messaging_context();
        if (msg_ctx == NULL) {
                exit(1);
        }
@@ -1318,7 +1916,8 @@ extern void build_options(bool screen);
        } else {
                profiling_level = lp_smbd_profiling_level();
        }
-       set_profile_level(profiling_level, messaging_server_id(msg_ctx));
+       main_server_id = messaging_server_id(msg_ctx);
+       set_profile_level(profiling_level, &main_server_id);
 
        if (!is_daemon && !is_a_socket(0)) {
                if (!interactive) {
@@ -1339,9 +1938,7 @@ extern void build_options(bool screen);
                become_daemon(Fork, no_process_group, log_stdout);
        }
 
-        set_my_unique_id(serverid_get_random_unique_id());
-
-#if HAVE_SETPGID
+#ifdef HAVE_SETPGID
        /*
         * If we're interactive we want to set our own process group for
         * signal management.
@@ -1359,9 +1956,7 @@ extern void build_options(bool screen);
        if (is_daemon)
                pidfile_create(lp_pid_directory(), "smbd");
 
-       status = reinit_after_fork(msg_ctx,
-                                  ev_ctx,
-                                  false);
+       status = reinit_after_fork(msg_ctx, ev_ctx, false, NULL);
        if (!NT_STATUS_IS_OK(status)) {
                exit_daemon("reinit_after_fork() failed", map_errno_from_nt_status(status));
        }
@@ -1440,7 +2035,12 @@ extern void build_options(bool screen);
                exit_daemon("Samba cannot init server context", EACCES);
        }
 
-       status = smbXsrv_session_global_init();
+       status = smbXsrv_client_global_init();
+       if (!NT_STATUS_IS_OK(status)) {
+               exit_daemon("Samba cannot init clients context", EACCES);
+       }
+
+       status = smbXsrv_session_global_init(msg_ctx);
        if (!NT_STATUS_IS_OK(status)) {
                exit_daemon("Samba cannot init session context", EACCES);
        }
@@ -1457,10 +2057,14 @@ extern void build_options(bool screen);
                exit_daemon("Samba cannot init leases", EACCES);
        }
 
-       if (!smbd_parent_notify_init(NULL, msg_ctx, ev_ctx)) {
+       if (!smbd_notifyd_init(msg_ctx, interactive, &parent->notifyd)) {
                exit_daemon("Samba cannot init notification", EACCES);
        }
 
+       if (!cleanupd_init(msg_ctx, interactive, &parent->cleanupd)) {
+               exit_daemon("Samba cannot init the cleanupd", EACCES);
+       }
+
        if (!messaging_parent_dgm_cleanup_init(msg_ctx)) {
                exit(1);
        }
@@ -1469,10 +2073,6 @@ extern void build_options(bool screen);
                exit_daemon("Samba cannot init scavenging", EACCES);
        }
 
-       if (!serverid_parent_init(ev_ctx)) {
-               exit_daemon("Samba cannot init server id", EACCES);
-       }
-
        if (!W_ERROR_IS_OK(registry_init_full()))
                exit_daemon("Samba cannot init registry", EACCES);
 
@@ -1484,14 +2084,14 @@ extern void build_options(bool screen);
                exit_daemon("ERROR: failed to load share info db.", EACCES);
        }
 
-       status = init_system_session_info();
+       status = init_system_session_info(NULL);
        if (!NT_STATUS_IS_OK(status)) {
                DEBUG(1, ("ERROR: failed to setup system user info: %s.\n",
                          nt_errstr(status)));
                return -1;
        }
 
-       if (!init_guest_info()) {
+       if (!init_guest_session_info(NULL)) {
                DEBUG(0,("ERROR: failed to setup guest info.\n"));
                return -1;
        }
@@ -1505,6 +2105,15 @@ extern void build_options(bool screen);
                exit_daemon("Samba cannot init global open", map_errno_from_nt_status(status));
        }
 
+       if (lp_clustering() && !lp_allow_unsafe_cluster_upgrade()) {
+               status = smbd_claim_version(msg_ctx, samba_version_string());
+               if (!NT_STATUS_IS_OK(status)) {
+                       DBG_WARNING("Could not claim version: %s\n",
+                                   nt_errstr(status));
+                       return -1;
+               }
+       }
+
        /* This MUST be done before start_epmd() because otherwise
         * start_epmd() forks and races against dcesrv_ep_setup() to
         * call directory_create_or_exist() */
@@ -1549,6 +2158,10 @@ extern void build_options(bool screen);
                        start_lsasd(ev_ctx, msg_ctx);
                }
 
+               if (rpc_fss_daemon() == RPC_DAEMON_FORK) {
+                       start_fssd(ev_ctx, msg_ctx);
+               }
+
                if (!lp__disable_spoolss() &&
                    (rpc_spoolss_daemon() != RPC_DAEMON_DISABLED)) {
                        bool bgq = lp_parm_bool(-1, "smbd", "backgroundqueue", true);
@@ -1557,6 +2170,13 @@ extern void build_options(bool screen);
                                exit_daemon("Samba failed to init printing subsystem", EACCES);
                        }
                }
+
+#ifdef WITH_SPOTLIGHT
+               if ((rpc_mdssvc_mode() == RPC_SERVICE_MODE_EXTERNAL) &&
+                   (rpc_mdssd_daemon() == RPC_DAEMON_FORK)) {
+                       start_mdssd(ev_ctx, msg_ctx);
+               }
+#endif
        } else if (!lp__disable_spoolss() &&
                   (rpc_spoolss_daemon() != RPC_DAEMON_DISABLED)) {
                if (!printing_subsystem_init(ev_ctx, msg_ctx, false, false)) {