#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 "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 "smbd/notifyd/notifyd.h"
#include "smbd/smbd_cleanupd.h"
#include "lib/util/sys_rw.h"
+#include "cleanupdb.h"
+#include "g_lock.h"
#ifdef CLUSTER_SUPPORT
#include "ctdb_protocol.h"
size_t num_children;
struct server_id cleanupd;
+ struct server_id notifyd;
struct tevent_timer *cleanup_te;
};
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;
#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)
{
* Someone from the family died, validate our locks
*/
- messaging_send_buf(msg_ctx, messaging_server_id(msg_ctx),
- MSG_SMB_BRL_VALIDATE, NULL, 0);
+ if (am_parent) {
+ messaging_send_buf(msg_ctx, am_parent->cleanupd,
+ MSG_SMB_BRL_VALIDATE, NULL, 0);
+ }
return 0;
}
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;
if (lp_kernel_change_notify()) {
}
}
- req = notifyd_send(msg_ctx, ev, msg_ctx,
- messaging_ctdbd_connection(),
+ if (lp_clustering()) {
+ ctdbd_conn = messaging_ctdb_connection();
+ }
+
+ req = notifyd_send(msg_ctx, ev, msg_ctx, ctdbd_conn,
sys_notify_watch, sys_notify_ctx);
if (req == NULL) {
TALLOC_FREE(sys_notify_ctx);
DEBUG(1, ("notifyd stopped: %s\n", strerror(ret)));
}
-static bool smbd_notifyd_init(struct messaging_context *msg, bool interactive)
+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);
}
if (pid != 0) {
+ if (am_parent != 0) {
+ add_child_pid(am_parent, pid);
+ }
+ *ppid = pid_to_procid(pid);
return true;
}
- status = reinit_after_fork(msg, ev, true, "smbd-notifyd");
+ 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);
}
tevent_req_set_callback(req, notifyd_stopped, msg);
- return tevent_req_poll(req, ev);
+
+ /* 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 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 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 notifyd_init_trigger(struct tevent_req *subreq)
+{
+ 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;
+ }
+
+ state->ok = smbd_notifyd_init(state->msg, false, state->ppid);
+ if (state->ok) {
+ DBG_WARNING("notifyd restarted\n");
+ tevent_req_done(req);
+ return;
+ }
+
+ 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(subreq, notifyd_init_trigger, req);
+ return;
+}
+
+static bool notifyd_init_recv(struct tevent_req *req)
+{
+ struct notifyd_init_state *state = tevent_req_data(
+ req, struct notifyd_init_state);
+
+ return state->ok;
+}
+
+static void notifyd_started(struct tevent_req *req)
+{
+ bool ok;
+
+ ok = notifyd_init_recv(req);
+ TALLOC_FREE(req);
+ if (!ok) {
+ DBG_ERR("Failed to restart notifyd, giving up\n");
+ return;
+ }
}
static void cleanupd_stopped(struct tevent_req *req);
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 = reinit_after_fork(msg, ev, true, "cleanupd");
+ 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));
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
and locking database for entries to cleanup. As a side effect this
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),
- MSG_SMB_BRL_VALIDATE, NULL, 0);
+ messaging_send_buf(parent->msg_ctx, parent->cleanupd,
+ MSG_SMB_UNLOCK, NULL, 0);
}
-static void remove_child_pid(struct smbd_parent_context *parent,
- pid_t pid,
- bool unclean_shutdown)
+static void cleanupd_started(struct tevent_req *req)
{
- struct smbd_child_pid *child;
- struct server_id child_id;
- struct iovec iov[2];
+ bool ok;
NTSTATUS status;
- int ret;
-
- child_id = pid_to_procid(pid);
-
- iov[0] = (struct iovec) { .iov_base = (uint8_t *)&pid,
- .iov_len = sizeof(pid) };
- iov[1] = (struct iovec) { .iov_base = (uint8_t *)&unclean_shutdown,
- .iov_len = sizeof(bool) };
-
- status = messaging_send_iov(parent->msg_ctx, parent->cleanupd,
- MSG_SMB_NOTIFY_CLEANUP,
- iov, ARRAY_SIZE(iov), NULL, 0);
- DEBUG(10, ("messaging_send_iov returned %s\n", nt_errstr(status)));
+ struct smbd_parent_context *parent = tevent_req_callback_data(
+ req, struct smbd_parent_context);
- ret = messaging_cleanup(parent->msg_ctx, pid);
+ ok = cleanupd_init_recv(req);
+ TALLOC_FREE(req);
+ if (!ok) {
+ DBG_ERR("Failed to restart cleanupd, giving up\n");
+ return;
+ }
- if ((ret != 0) && (ret != ENOENT)) {
- DEBUG(10, ("%s: messaging_cleanup returned %s\n",
- __func__, strerror(ret)));
+ 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));
}
+}
- smbprofile_cleanup(pid, getpid());
+static void remove_child_pid(struct smbd_parent_context *parent,
+ pid_t pid,
+ bool unclean_shutdown)
+{
+ struct smbd_child_pid *child;
+ NTSTATUS status;
+ bool ok;
for (child = parent->children; child != NULL; child = child->next) {
if (child->pid == pid) {
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
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));
- }
}
/****************************************************************************
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
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;
}
}
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;
}
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,
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()) {
- struct ctdbd_connection *conn = messaging_ctdbd_connection();
+ struct ctdbd_connection *conn = messaging_ctdb_connection();
register_with_ctdbd(conn, CTDB_SRVID_RECONFIGURE,
smbd_parent_ctdb_reconfigured, msg_ctx);
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, 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, 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, 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, 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, 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, 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.
****************************************************************************/
int opt;
poptContext pc;
bool print_build_options = False;
+ struct server_id main_server_id = {0};
enum {
OPT_DAEMON = 1000,
OPT_INTERACTIVE,
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)"},
+ {"interactive", 'i', POPT_ARG_NONE, NULL, OPT_INTERACTIVE, "Run interactive (not a daemon) and log to stdout"},
{"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" },
* 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();
if (ev_ctx == NULL) {
} 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) {
exit_daemon("Samba cannot init server context", EACCES);
}
- status = smbXsrv_session_global_init();
+ status = smbXsrv_session_global_init(msg_ctx);
if (!NT_STATUS_IS_OK(status)) {
exit_daemon("Samba cannot init session context", EACCES);
}
exit_daemon("Samba cannot init leases", EACCES);
}
- if (!smbd_notifyd_init(msg_ctx, interactive)) {
+ if (!smbd_notifyd_init(msg_ctx, interactive, &parent->notifyd)) {
exit_daemon("Samba cannot init notification", EACCES);
}
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() */