/*
* Unix SMB/CIFS implementation.
*
* Copyright (C) Volker Lendecke 2014
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "replace.h"
#include <tevent.h>
#include "notifyd_private.h"
#include "lib/util/server_id.h"
#include "lib/util/data_blob.h"
#include "librpc/gen_ndr/notify.h"
#include "librpc/gen_ndr/messaging.h"
#include "librpc/gen_ndr/server_id.h"
#include "lib/dbwrap/dbwrap.h"
#include "lib/dbwrap/dbwrap_rbt.h"
#include "messages.h"
#include "tdb.h"
#include "util_tdb.h"
#include "notifyd.h"
#include "lib/util/server_id_db.h"
#include "lib/util/tevent_unix.h"
#include "lib/util/tevent_ntstatus.h"
#include "ctdbd_conn.h"
#include "ctdb_srvids.h"
#include "server_id_db_util.h"
#include "lib/util/iov_buf.h"
#include "messages_util.h"
#ifdef CLUSTER_SUPPORT
#include "ctdb_protocol.h"
#endif
struct notifyd_peer;
/*
* All of notifyd's state
*/
struct notifyd_state {
struct tevent_context *ev;
struct messaging_context *msg_ctx;
struct ctdbd_connection *ctdbd_conn;
/*
* Database of everything clients show interest in. Indexed by
* absolute path. The database keys are not 0-terminated
* to allow the critical operation, notifyd_trigger, to walk
* the structure from the top without adding intermediate 0s.
* The database records contain an array of
*
* struct notifyd_instance
*
* to be maintained and parsed by notifyd_parse_entry()
*/
struct db_context *entries;
/*
* In the cluster case, this is the place where we store a log
* of all MSG_SMB_NOTIFY_REC_CHANGE messages. We just 1:1
* forward them to our peer notifyd's in the cluster once a
* second or when the log grows too large.
*/
struct messaging_reclog *log;
/*
* Array of companion notifyd's in a cluster. Every notifyd
* broadcasts its messaging_reclog to every other notifyd in
* the cluster. This is done by making ctdb send a message to
* srvid CTDB_SRVID_SAMBA_NOTIFY_PROXY with destination node
* number CTDB_BROADCAST_CONNECTED. Everybody in the cluster who
* had called register_with_ctdbd this srvid will receive the
* broadcasts.
*
* Database replication happens via these broadcasts. Also,
* they serve as liveness indication. If a notifyd receives a
* broadcast from an unknown peer, it will create one for this
* srvid. Also when we don't hear anything from a peer for a
* while, we will discard it.
*/
struct notifyd_peer **peers;
size_t num_peers;
sys_notify_watch_fn sys_notify_watch;
struct sys_notify_context *sys_notify_ctx;
};
struct notifyd_peer {
struct notifyd_state *state;
struct server_id pid;
uint64_t rec_index;
struct db_context *db;
time_t last_broadcast;
};
static void notifyd_rec_change(struct messaging_context *msg_ctx,
void *private_data, uint32_t msg_type,
struct server_id src, DATA_BLOB *data);
static void notifyd_trigger(struct messaging_context *msg_ctx,
void *private_data, uint32_t msg_type,
struct server_id src, DATA_BLOB *data);
static void notifyd_get_db(struct messaging_context *msg_ctx,
void *private_data, uint32_t msg_type,
struct server_id src, DATA_BLOB *data);
#ifdef CLUSTER_SUPPORT
static void notifyd_got_db(struct messaging_context *msg_ctx,
void *private_data, uint32_t msg_type,
struct server_id src, DATA_BLOB *data);
static void notifyd_broadcast_reclog(struct ctdbd_connection *ctdbd_conn,
struct server_id src,
struct messaging_reclog *log);
#endif
static void notifyd_sys_callback(struct sys_notify_context *ctx,
void *private_data, struct notify_event *ev,
uint32_t filter);
#ifdef CLUSTER_SUPPORT
static struct tevent_req *notifyd_broadcast_reclog_send(
TALLOC_CTX *mem_ctx, struct tevent_context *ev,
struct ctdbd_connection *ctdbd_conn, struct server_id src,
struct messaging_reclog *log);
static int notifyd_broadcast_reclog_recv(struct tevent_req *req);
static struct tevent_req *notifyd_clean_peers_send(
TALLOC_CTX *mem_ctx, struct tevent_context *ev,
struct notifyd_state *notifyd);
static int notifyd_clean_peers_recv(struct tevent_req *req);
#endif
static int sys_notify_watch_dummy(
TALLOC_CTX *mem_ctx,
struct sys_notify_context *ctx,
const char *path,
uint32_t *filter,
uint32_t *subdir_filter,
void (*callback)(struct sys_notify_context *ctx,
void *private_data,
struct notify_event *ev,
uint32_t filter),
void *private_data,
void *handle_p)
{
void **handle = handle_p;
*handle = NULL;
return 0;
}
#ifdef CLUSTER_SUPPORT
static void notifyd_broadcast_reclog_finished(struct tevent_req *subreq);
static void notifyd_clean_peers_finished(struct tevent_req *subreq);
static int notifyd_snoop_broadcast(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);
#endif
struct tevent_req *notifyd_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
struct messaging_context *msg_ctx,
struct ctdbd_connection *ctdbd_conn,
sys_notify_watch_fn sys_notify_watch,
struct sys_notify_context *sys_notify_ctx)
{
struct tevent_req *req;
#ifdef CLUSTER_SUPPORT
struct tevent_req *subreq;
#endif
struct notifyd_state *state;
struct server_id_db *names_db;
NTSTATUS status;
int ret;
req = tevent_req_create(mem_ctx, &state, struct notifyd_state);
if (req == NULL) {
return NULL;
}
state->ev = ev;
state->msg_ctx = msg_ctx;
state->ctdbd_conn = ctdbd_conn;
if (sys_notify_watch == NULL) {
sys_notify_watch = sys_notify_watch_dummy;
}
state->sys_notify_watch = sys_notify_watch;
state->sys_notify_ctx = sys_notify_ctx;
state->entries = db_open_rbt(state);
if (tevent_req_nomem(state->entries, req)) {
return tevent_req_post(req, ev);
}
status = messaging_register(msg_ctx, state, MSG_SMB_NOTIFY_REC_CHANGE,
notifyd_rec_change);
if (tevent_req_nterror(req, status)) {
return tevent_req_post(req, ev);
}
status = messaging_register(msg_ctx, state, MSG_SMB_NOTIFY_TRIGGER,
notifyd_trigger);
if (t
|