/*
Unix SMB/CIFS implementation.
Samba internal messaging functions
Copyright (C) Andrew Tridgell 2000
Copyright (C) 2001 by Martin Pool
Copyright (C) 2002 by Jeremy Allison
Copyright (C) 2007 by Volker Lendecke
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 .
*/
/**
@defgroup messages Internal messaging framework
@{
@file messages.c
@brief Module for internal messaging between Samba daemons.
The idea is that if a part of Samba wants to do communication with
another Samba process then it will do a message_register() of a
dispatch function, and use message_send_pid() to send messages to
that process.
The dispatch function is given the pid of the sender, and it can
use that to reply by message_send_pid(). See ping_message() for a
simple example.
@caution Dispatch functions must be able to cope with incoming
messages on an *odd* byte boundary.
This system doesn't have any inherent size limitations but is not
very efficient for large messages or when messages are sent in very
quick succession.
*/
#include "includes.h"
#include "dbwrap/dbwrap.h"
#include "serverid.h"
#include "messages.h"
#include "lib/util/tevent_unix.h"
#include "lib/background.h"
#include "lib/messages_dgm.h"
#include "lib/util/iov_buf.h"
#include "lib/util/server_id_db.h"
#include "lib/messages_dgm_ref.h"
#include "lib/messages_util.h"
struct messaging_callback {
struct messaging_callback *prev, *next;
uint32_t msg_type;
void (*fn)(struct messaging_context *msg, void *private_data,
uint32_t msg_type,
struct server_id server_id, DATA_BLOB *data);
void *private_data;
};
struct messaging_context {
struct server_id id;
struct tevent_context *event_ctx;
struct messaging_callback *callbacks;
struct tevent_req **new_waiters;
unsigned num_new_waiters;
struct tevent_req **waiters;
unsigned num_waiters;
void *msg_dgm_ref;
struct messaging_backend *remote;
struct server_id_db *names_db;
};
static struct messaging_rec *messaging_rec_dup(TALLOC_CTX *mem_ctx,
struct messaging_rec *rec);
static void messaging_dispatch_rec(struct messaging_context *msg_ctx,
struct tevent_context *ev,
struct messaging_rec *rec);
/****************************************************************************
A useful function for testing the message system.
****************************************************************************/
static void ping_message(struct messaging_context *msg_ctx,
void *private_data,
uint32_t msg_type,
struct server_id src,
DATA_BLOB *data)
{
struct server_id_buf idbuf;
DEBUG(1, ("INFO: Received PING message from PID %s [%.*s]\n",
server_id_str_buf(src, &idbuf), (int)data->length,
data->data ? (char *)data->data : ""));
messaging_send(msg_ctx, src, MSG_PONG, data);
}
static struct messaging_rec *messaging_rec_create(
TALLOC_CTX *mem_ctx, struct server_id src, struct server_id dst,
uint32_t msg_type, const struct iovec *iov, int iovlen,
const int *fds, size_t num_fds)
{
ssize_t buflen;
uint8_t *buf;
struct messaging_rec *result;
if (num_fds > INT8_MAX) {
return NULL;
}
buflen = iov_buflen(iov, iovlen);
if (buflen == -1) {
return NULL;
}
buf = talloc_array(mem_ctx, uint8_t, buflen);
if (buf == NULL) {
return NULL;
}
iov_buf(iov, iovlen, buf, buflen);
{
struct messaging_rec rec;
int64_t fds64[num_fds];
size_t i;
for (i=0; i INT8_MAX) {
DBG_WARNING("too many fds: %zu\n", num_fds);
goto close_fail;
}
/*
* "consume" the fds by copying them and setting
* the original variable to -1
*/
for (i=0; i < num_fds; i++) {
fds64[i] = fds[i];
fds[i] = -1;
}
rec = (struct messaging_rec) {
.msg_version = MESSAGE_VERSION,
.buf.data = discard_const_p(uint8_t, msg) + MESSAGE_HDR_LENGTH,
.buf.length = msg_len - MESSAGE_HDR_LENGTH,
.num_fds = num_fds,
.fds = fds64,
};
message_hdr_get(&rec.msg_type, &rec.src, &rec.dest, msg);
DBG_DEBUG("Received message 0x%x len %zu (num_fds:%zu) from %s\n",
(unsigned)rec.msg_type, rec.buf.length, num_fds,
server_id_str_buf(rec.src, &idbuf));
messaging_dispatch_rec(msg_ctx, ev, &rec);
return;
close_fail:
for (i=0; i < num_fds; i++) {
close(fds[i]);
}
}
static int messaging_context_destructor(struct messaging_context *ctx)
{
unsigned i;
for (i=0; inum_new_waiters; i++) {
if (ctx->new_waiters[i] != NULL) {
tevent_req_set_cleanup_fn(ctx->new_waiters[i], NULL);
ctx->new_waiters[i] = NULL;
}
}
for (i=0; inum_waiters; i++) {
if (ctx->waiters[i] != NULL) {
tevent_req_set_cleanup_fn(ctx->waiters[i], NULL);
ctx->waiters[i] = NULL;
}
}
return 0;
}
static const char *private_path(const char *name)
{
return talloc_asprintf(talloc_tos(), "%s/%s", lp_private_dir(), name);
}
static NTSTATUS messaging_init_internal(TALLOC_CTX *mem_ctx,
struct tevent_context *ev,
struct messaging_context **pmsg_ctx)
{
TALLOC_CTX *frame;
struct messaging_context *ctx;
NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
int ret;
const char *lck_path;
const char *priv_path;
bool ok;
lck_path = lock_path("msg.lock");
if (lck_path == NULL) {
return NT_STATUS_NO_MEMORY;
}
ok = directory_create_or_exist_strict(lck_path,
sec_initial_uid(),
0755);
if (!ok) {
DBG_DEBUG("Could not create lock directory: %s\n",
strerror(errno));
return NT_STATUS_ACCESS_DENIED;
}
priv_path = private_path("msg.sock");
if (priv_path == NULL) {
return NT_STATUS_NO_MEMORY;
}
ok = directory_create_or_exist_strict(priv_path, sec_initial_uid(),
0700);
if (!ok) {
DBG_DEBUG("Could not create msg directory: %s\n",
strerror(errno));
return NT_STATUS_ACCESS_DENIED;
}
frame = talloc_stackframe();
if (frame == NULL) {
return NT_STATUS_NO_MEMORY;
}
ctx = talloc_zero(frame, struct messaging_context);
if (ctx == NULL) {
status = NT_STATUS_NO_MEMORY;
goto done;
}
ctx->id = (struct server_id) {
.pid = getpid(), .vnn = NONCLUSTER_VNN
};
ctx->event_ctx = ev;
sec_init();
ctx->msg_dgm_ref = messaging_dgm_ref(ctx,
ctx->event_ctx,
&ctx->id.unique_id,
priv_path,
lck_path,
messaging_recv_cb,
ctx,
&ret);
if (ctx->msg_dgm_ref == NULL) {
DEBUG(2, ("messaging_dgm_ref failed: %s\n", strerror(ret)));
status = NT_STATUS_INTERNAL_ERROR;
goto done;
}
talloc_set_destructor(ctx, messaging_context_destructor);
if (lp_clustering()) {
ret = messaging_ctdbd_init(ctx, ctx, &ctx->remote);
if (ret != 0) {
DEBUG(2, ("messaging_ctdbd_init failed: %s\n",
strerror(ret)));
status = NT_STATUS_INTERNAL_ERROR;
goto done;
}
}
ctx->id.vnn = get_my_vnn();
ctx->names_db = server_id_db_init(ctx,
ctx->id,
lp_lock_directory(),
0,
TDB_INCOMPATIBLE_HASH|TDB_CLEAR_IF_FIRST);
if (ctx->names_db == NULL) {
DBG_DEBUG("server_id_db_init failed\n");
status = NT_STATUS_INTERNAL_ERROR;
goto done;
}
messaging_register(ctx, NULL, MSG_PING, ping_message);
/* Register some debugging related messages */
register_msg_pool_usage(ctx);
register_dmalloc_msgs(ctx);
debug_register_msgs(ctx);
{
struct server_id_buf tmp;
DBG_DEBUG("my id: %s\n", server_id_str_buf(ctx->id, &tmp));
}
*pmsg_ctx = talloc_steal(mem_ctx, ctx);
status = NT_STATUS_OK;
done:
TALLOC_FREE(frame);
return status;
}
struct messaging_context *messaging_init(TALLOC_CTX *mem_ctx,
struct tevent_context *ev)
{
struct messaging_context *ctx = NULL;
NTSTATUS status;
status = messaging_init_internal(mem_ctx,
ev,
&ctx);
if (!NT_STATUS_IS_OK(status)) {
return NULL;
}
return ctx;
}
struct server_id messaging_server_id(const struct messaging_context *msg_ctx)
{
return msg_ctx->id;
}
/*
* re-init after a fork
*/
NTSTATUS messaging_reinit(struct messaging_context *msg_ctx)
{
int ret;
char *lck_path;
TALLOC_FREE(msg_ctx->msg_dgm_ref);
msg_ctx->id = (struct server_id) {
.pid = getpid(), .vnn = msg_ctx->id.vnn
};
lck_path = lock_path("msg.lock");
if (lck_path == NULL) {
return NT_STATUS_NO_MEMORY;
}
msg_ctx->msg_dgm_ref = messaging_dgm_ref(
msg_ctx, msg_ctx->event_ctx, &msg_ctx->id.unique_id,
private_path("msg.sock"), lck_path,
messaging_recv_cb, msg_ctx, &ret);
if (msg_ctx->msg_dgm_ref == NULL) {
DEBUG(2, ("messaging_dgm_ref failed: %s\n", strerror(ret)));
return map_nt_error_from_unix(ret);
}
if (lp_clustering()) {
ret = messaging_ctdbd_reinit(msg_ctx, msg_ctx,
msg_ctx->remote);
if (ret != 0) {
DEBUG(1, ("messaging_ctdbd_init failed: %s\n",
strerror(ret)));
return map_nt_error_from_unix(ret);
}
}
server_id_db_reinit(msg_ctx->names_db, msg_ctx->id);
return NT_STATUS_OK;
}
/*
* Register a dispatch function for a particular message type. Allow multiple
* registrants
*/
NTSTATUS messaging_register(struct messaging_context *msg_ctx,
void *private_data,
uint32_t msg_type,
void (*fn)(struct messaging_context *msg,
void *private_data,
uint32_t msg_type,
struct server_id server_id,
DATA_BLOB *data))
{
struct messaging_callback *cb;
DEBUG(5, ("Registering messaging pointer for type %u - "
"private_data=%p\n",
(unsigned)msg_type, private_data));
/*
* Only one callback per type
*/
for (cb = msg_ctx->callbacks; cb != NULL; cb = cb->next) {
/* we allow a second registration of the same message
type if it has a different private pointer. This is
needed in, for example, the internal notify code,
which creates a new notify context for each tree
connect, and expects to receive messages to each of
them. */
if (cb->msg_type == msg_type && private_data == cb->private_data) {
DEBUG(5,("Overriding messaging pointer for type %u - private_data=%p\n",
(unsigned)msg_type, private_data));
cb->fn = fn;
cb->private_data = private_data;
return NT_STATUS_OK;
}
}
if (!(cb = talloc(msg_ctx, struct messaging_callback))) {
return NT_STATUS_NO_MEMORY;
}
cb->msg_type = msg_type;
cb->fn = fn;
cb->private_data = private_data;
DLIST_ADD(msg_ctx->callbacks, cb);
return NT_STATUS_OK;
}
/*
De-register the function for a particular message type.
*/
void messaging_deregister(struct messaging_context *ctx, uint32_t msg_type,
void *private_data)
{
struct messaging_callback *cb, *next;
for (cb = ctx->callbacks; cb; cb = next) {
next = cb->next;
if ((cb->msg_type == msg_type)
&& (cb->private_data == private_data)) {
DEBUG(5,("Deregistering messaging pointer for type %u - private_data=%p\n",
(unsigned)msg_type, private_data));
DLIST_REMOVE(ctx->callbacks, cb);
TALLOC_FREE(cb);
}
}
}
/*
Send a message to a particular server
*/
NTSTATUS messaging_send(struct messaging_context *msg_ctx,
struct server_id server, uint32_t msg_type,
const DATA_BLOB *data)
{
struct iovec iov = {0};
if (data != NULL) {
iov.iov_base = data->data;
iov.iov_len = data->length;
};
return messaging_send_iov(msg_ctx, server, msg_type, &iov, 1, NULL, 0);
}
NTSTATUS messaging_send_buf(struct messaging_context *msg_ctx,
struct server_id server, uint32_t msg_type,
const uint8_t *buf, size_t len)
{
DATA_BLOB blob = data_blob_const(buf, len);
return messaging_send(msg_ctx, server, msg_type, &blob);
}
struct messaging_post_state {
struct messaging_context *msg_ctx;
struct messaging_rec *rec;
};
static void messaging_post_handler(struct tevent_context *ev,
struct tevent_immediate *ti,
void *private_data);
static int messaging_post_self(struct messaging_context *msg_ctx,
struct server_id src, struct server_id dst,
uint32_t msg_type,
const struct iovec *iov, int iovlen,
const int *fds, size_t num_fds)
{
struct tevent_immediate *ti;
struct messaging_post_state *state;
state = talloc(msg_ctx, struct messaging_post_state);
if (state == NULL) {
return ENOMEM;
}
state->msg_ctx = msg_ctx;
ti = tevent_create_immediate(state);
if (ti == NULL) {
goto fail;
}
state->rec = messaging_rec_create(
state, src, dst, msg_type, iov, iovlen, fds, num_fds);
if (state->rec == NULL) {
goto fail;
}
tevent_schedule_immediate(ti, msg_ctx->event_ctx,
messaging_post_handler, state);
return 0;
fail:
TALLOC_FREE(state);
return ENOMEM;
}
static void messaging_post_handler(struct tevent_context *ev,
struct tevent_immediate *ti,
void *private_data)
{
struct messaging_post_state *state = talloc_get_type_abort(
private_data, struct messaging_post_state);
messaging_dispatch_rec(state->msg_ctx, ev, state->rec);
TALLOC_FREE(state);
}
int messaging_send_iov_from(struct messaging_context *msg_ctx,
struct server_id src, struct server_id dst,
uint32_t msg_type,
const struct iovec *iov, int iovlen,
const int *fds, size_t num_fds)
{
int ret;
uint8_t hdr[MESSAGE_HDR_LENGTH];
struct iovec iov2[iovlen+1];
if (server_id_is_disconnected(&dst)) {
return EINVAL;
}
if (num_fds > INT8_MAX) {
return EINVAL;
}
if (dst.vnn != msg_ctx->id.vnn) {
if (num_fds > 0) {
return ENOSYS;
}
ret = msg_ctx->remote->send_fn(src, dst,
msg_type, iov, iovlen,
NULL, 0,
msg_ctx->remote);
return ret;
}
if (server_id_equal(&dst, &msg_ctx->id)) {
ret = messaging_post_self(msg_ctx, src, dst, msg_type,
iov, iovlen, fds, num_fds);
return ret;
}
message_hdr_put(hdr, msg_type, src, dst);
iov2[0] = (struct iovec){ .iov_base = hdr, .iov_len = sizeof(hdr) };
memcpy(&iov2[1], iov, iovlen * sizeof(*iov));
ret = messaging_dgm_send(dst.pid, iov2, iovlen+1, fds, num_fds);
if (ret == EACCES) {
become_root();
ret = messaging_dgm_send(dst.pid, iov2, iovlen+1,
fds, num_fds);
unbecome_root();
}
return ret;
}
NTSTATUS messaging_send_iov(struct messaging_context *msg_ctx,
struct server_id dst, uint32_t msg_type,
const struct iovec *iov, int iovlen,
const int *fds, size_t num_fds)
{
int ret;
ret = messaging_send_iov_from(msg_ctx, msg_ctx->id, dst, msg_type,
iov, iovlen, fds, num_fds);
if (ret != 0) {
return map_nt_error_from_unix(ret);
}
return NT_STATUS_OK;
}
static struct messaging_rec *messaging_rec_dup(TALLOC_CTX *mem_ctx,
struct messaging_rec *rec)
{
struct messaging_rec *result;
size_t fds_size = sizeof(int64_t) * rec->num_fds;
size_t payload_len;
payload_len = rec->buf.length + fds_size;
if (payload_len < rec->buf.length) {
/* overflow */
return NULL;
}
result = talloc_pooled_object(mem_ctx, struct messaging_rec, 2,
payload_len);
if (result == NULL) {
return NULL;
}
*result = *rec;
/* Doesn't fail, see talloc_pooled_object */
result->buf.data = talloc_memdup(result, rec->buf.data,
rec->buf.length);
result->fds = NULL;
if (result->num_fds > 0) {
result->fds = talloc_memdup(result, rec->fds, fds_size);
}
return result;
}
struct messaging_filtered_read_state {
struct tevent_context *ev;
struct messaging_context *msg_ctx;
struct messaging_dgm_fde *fde;
bool (*filter)(struct messaging_rec *rec, void *private_data);
void *private_data;
struct messaging_rec *rec;
};
static void messaging_filtered_read_cleanup(struct tevent_req *req,
enum tevent_req_state req_state);
struct tevent_req *messaging_filtered_read_send(
TALLOC_CTX *mem_ctx, struct tevent_context *ev,
struct messaging_context *msg_ctx,
bool (*filter)(struct messaging_rec *rec, void *private_data),
void *private_data)
{
struct tevent_req *req;
struct messaging_filtered_read_state *state;
size_t new_waiters_len;
req = tevent_req_create(mem_ctx, &state,
struct messaging_filtered_read_state);
if (req == NULL) {
return NULL;
}
state->ev = ev;
state->msg_ctx = msg_ctx;
state->filter = filter;
state->private_data = private_data;
/*
* We have to defer the callback here, as we might be called from
* within a different tevent_context than state->ev
*/
tevent_req_defer_callback(req, state->ev);
state->fde = messaging_dgm_register_tevent_context(state, ev);
if (tevent_req_nomem(state->fde, req)) {
return tevent_req_post(req, ev);
}
/*
* We add ourselves to the "new_waiters" array, not the "waiters"
* array. If we are called from within messaging_read_done,
* messaging_dispatch_rec will be in an active for-loop on
* "waiters". We must be careful not to mess with this array, because
* it could mean that a single event is being delivered twice.
*/
new_waiters_len = talloc_array_length(msg_ctx->new_waiters);
if (new_waiters_len == msg_ctx->num_new_waiters) {
struct tevent_req **tmp;
tmp = talloc_realloc(msg_ctx, msg_ctx->new_waiters,
struct tevent_req *, new_waiters_len+1);
if (tevent_req_nomem(tmp, req)) {
return tevent_req_post(req, ev);
}
msg_ctx->new_waiters = tmp;
}
msg_ctx->new_waiters[msg_ctx->num_new_waiters] = req;
msg_ctx->num_new_waiters += 1;
tevent_req_set_cleanup_fn(req, messaging_filtered_read_cleanup);
return req;
}
static void messaging_filtered_read_cleanup(struct tevent_req *req,
enum tevent_req_state req_state)
{
struct messaging_filtered_read_state *state = tevent_req_data(
req, struct messaging_filtered_read_state);
struct messaging_context *msg_ctx = state->msg_ctx;
unsigned i;
tevent_req_set_cleanup_fn(req, NULL);
TALLOC_FREE(state->fde);
/*
* Just set the [new_]waiters entry to NULL, be careful not to mess
* with the other "waiters" array contents. We are often called from
* within "messaging_dispatch_rec", which loops over
* "waiters". Messing with the "waiters" array will mess up that
* for-loop.
*/
for (i=0; inum_waiters; i++) {
if (msg_ctx->waiters[i] == req) {
msg_ctx->waiters[i] = NULL;
return;
}
}
for (i=0; inum_new_waiters; i++) {
if (msg_ctx->new_waiters[i] == req) {
msg_ctx->new_waiters[i] = NULL;
return;
}
}
}
static void messaging_filtered_read_done(struct tevent_req *req,
struct messaging_rec *rec)
{
struct messaging_filtered_read_state *state = tevent_req_data(
req, struct messaging_filtered_read_state);
state->rec = messaging_rec_dup(state, rec);
if (tevent_req_nomem(state->rec, req)) {
return;
}
tevent_req_done(req);
}
int messaging_filtered_read_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
struct messaging_rec **presult)
{
struct messaging_filtered_read_state *state = tevent_req_data(
req, struct messaging_filtered_read_state);
int err;
if (tevent_req_is_unix_error(req, &err)) {
tevent_req_received(req);
return err;
}
if (presult != NULL) {
*presult = talloc_move(mem_ctx, &state->rec);
}
return 0;
}
struct messaging_read_state {
uint32_t msg_type;
struct messaging_rec *rec;
};
static bool messaging_read_filter(struct messaging_rec *rec,
void *private_data);
static void messaging_read_done(struct tevent_req *subreq);
struct tevent_req *messaging_read_send(TALLOC_CTX *mem_ctx,
struct tevent_context *ev,
struct messaging_context *msg,
uint32_t msg_type)
{
struct tevent_req *req, *subreq;
struct messaging_read_state *state;
req = tevent_req_create(mem_ctx, &state,
struct messaging_read_state);
if (req == NULL) {
return NULL;
}
state->msg_type = msg_type;
subreq = messaging_filtered_read_send(state, ev, msg,
messaging_read_filter, state);
if (tevent_req_nomem(subreq, req)) {
return tevent_req_post(req, ev);
}
tevent_req_set_callback(subreq, messaging_read_done, req);
return req;
}
static bool messaging_read_filter(struct messaging_rec *rec,
void *private_data)
{
struct messaging_read_state *state = talloc_get_type_abort(
private_data, struct messaging_read_state);
if (rec->num_fds != 0) {
return false;
}
return rec->msg_type == state->msg_type;
}
static void messaging_read_done(struct tevent_req *subreq)
{
struct tevent_req *req = tevent_req_callback_data(
subreq, struct tevent_req);
struct messaging_read_state *state = tevent_req_data(
req, struct messaging_read_state);
int ret;
ret = messaging_filtered_read_recv(subreq, state, &state->rec);
TALLOC_FREE(subreq);
if (tevent_req_error(req, ret)) {
return;
}
tevent_req_done(req);
}
int messaging_read_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
struct messaging_rec **presult)
{
struct messaging_read_state *state = tevent_req_data(
req, struct messaging_read_state);
int err;
if (tevent_req_is_unix_error(req, &err)) {
return err;
}
if (presult != NULL) {
*presult = talloc_move(mem_ctx, &state->rec);
}
return 0;
}
struct messaging_handler_state {
struct tevent_context *ev;
struct messaging_context *msg_ctx;
uint32_t msg_type;
bool (*handler)(struct messaging_context *msg_ctx,
struct messaging_rec **rec, void *private_data);
void *private_data;
};
static void messaging_handler_got_msg(struct tevent_req *subreq);
struct tevent_req *messaging_handler_send(
TALLOC_CTX *mem_ctx, struct tevent_context *ev,
struct messaging_context *msg_ctx, uint32_t msg_type,
bool (*handler)(struct messaging_context *msg_ctx,
struct messaging_rec **rec, void *private_data),
void *private_data)
{
struct tevent_req *req, *subreq;
struct messaging_handler_state *state;
req = tevent_req_create(mem_ctx, &state,
struct messaging_handler_state);
if (req == NULL) {
return NULL;
}
state->ev = ev;
state->msg_ctx = msg_ctx;
state->msg_type = msg_type;
state->handler = handler;
state->private_data = private_data;
subreq = messaging_read_send(state, state->ev, state->msg_ctx,
state->msg_type);
if (tevent_req_nomem(subreq, req)) {
return tevent_req_post(req, ev);
}
tevent_req_set_callback(subreq, messaging_handler_got_msg, req);
return req;
}
static void messaging_handler_got_msg(struct tevent_req *subreq)
{
struct tevent_req *req = tevent_req_callback_data(
subreq, struct tevent_req);
struct messaging_handler_state *state = tevent_req_data(
req, struct messaging_handler_state);
struct messaging_rec *rec;
int ret;
bool ok;
ret = messaging_read_recv(subreq, state, &rec);
TALLOC_FREE(subreq);
if (tevent_req_error(req, ret)) {
return;
}
subreq = messaging_read_send(state, state->ev, state->msg_ctx,
state->msg_type);
if (tevent_req_nomem(subreq, req)) {
return;
}
tevent_req_set_callback(subreq, messaging_handler_got_msg, req);
ok = state->handler(state->msg_ctx, &rec, state->private_data);
TALLOC_FREE(rec);
if (ok) {
/*
* Next round
*/
return;
}
TALLOC_FREE(subreq);
tevent_req_done(req);
}
int messaging_handler_recv(struct tevent_req *req)
{
return tevent_req_simple_recv_unix(req);
}
static bool messaging_append_new_waiters(struct messaging_context *msg_ctx)
{
if (msg_ctx->num_new_waiters == 0) {
return true;
}
if (talloc_array_length(msg_ctx->waiters) <
(msg_ctx->num_waiters + msg_ctx->num_new_waiters)) {
struct tevent_req **tmp;
tmp = talloc_realloc(
msg_ctx, msg_ctx->waiters, struct tevent_req *,
msg_ctx->num_waiters + msg_ctx->num_new_waiters);
if (tmp == NULL) {
DEBUG(1, ("%s: talloc failed\n", __func__));
return false;
}
msg_ctx->waiters = tmp;
}
memcpy(&msg_ctx->waiters[msg_ctx->num_waiters], msg_ctx->new_waiters,
sizeof(struct tevent_req *) * msg_ctx->num_new_waiters);
msg_ctx->num_waiters += msg_ctx->num_new_waiters;
msg_ctx->num_new_waiters = 0;
return true;
}
static void messaging_dispatch_classic(struct messaging_context *msg_ctx,
struct messaging_rec *rec)
{
struct messaging_callback *cb, *next;
for (cb = msg_ctx->callbacks; cb != NULL; cb = next) {
size_t j;
next = cb->next;
if (cb->msg_type != rec->msg_type) {
continue;
}
/*
* the old style callbacks don't support fd passing
*/
for (j=0; j < rec->num_fds; j++) {
int fd = rec->fds[j];
close(fd);
}
rec->num_fds = 0;
rec->fds = NULL;
cb->fn(msg_ctx, cb->private_data, rec->msg_type,
rec->src, &rec->buf);
/*
* we continue looking for matching messages after finding
* one. This matters for subsystems like the internal notify
* code which register more than one handler for the same
* message type
*/
}
}
/*
Dispatch one messaging_rec
*/
static void messaging_dispatch_rec(struct messaging_context *msg_ctx,
struct tevent_context *ev,
struct messaging_rec *rec)
{
unsigned i;
size_t j;
if (ev == msg_ctx->event_ctx) {
messaging_dispatch_classic(msg_ctx, rec);
}
if (!messaging_append_new_waiters(msg_ctx)) {
for (j=0; j < rec->num_fds; j++) {
int fd = rec->fds[j];
close(fd);
}
rec->num_fds = 0;
rec->fds = NULL;
return;
}
i = 0;
while (i < msg_ctx->num_waiters) {
struct tevent_req *req;
struct messaging_filtered_read_state *state;
req = msg_ctx->waiters[i];
if (req == NULL) {
/*
* This got cleaned up. In the meantime,
* move everything down one. We need
* to keep the order of waiters, as
* other code may depend on this.
*/
if (i < msg_ctx->num_waiters - 1) {
memmove(&msg_ctx->waiters[i],
&msg_ctx->waiters[i+1],
sizeof(struct tevent_req *) *
(msg_ctx->num_waiters - i - 1));
}
msg_ctx->num_waiters -= 1;
continue;
}
state = tevent_req_data(
req, struct messaging_filtered_read_state);
if ((ev == state->ev) &&
state->filter(rec, state->private_data)) {
messaging_filtered_read_done(req, rec);
/*
* Only the first one gets the fd-array
*/
rec->num_fds = 0;
rec->fds = NULL;
}
i += 1;
}
if (ev != msg_ctx->event_ctx) {
struct iovec iov;
int fds[rec->num_fds];
int ret;
/*
* We've been listening on a nested event
* context. Messages need to be handled in the main
* event context, so post to ourselves
*/
iov.iov_base = rec->buf.data;
iov.iov_len = rec->buf.length;
for (i=0; inum_fds; i++) {
fds[i] = rec->fds[i];
}
ret = messaging_post_self(
msg_ctx, rec->src, rec->dest, rec->msg_type,
&iov, 1, fds, rec->num_fds);
if (ret == 0) {
return;
}
}
/*
* If the fd-array isn't used, just close it.
*/
for (j=0; j < rec->num_fds; j++) {
int fd = rec->fds[j];
close(fd);
}
rec->num_fds = 0;
rec->fds = NULL;
}
static int mess_parent_dgm_cleanup(void *private_data);
static void mess_parent_dgm_cleanup_done(struct tevent_req *req);
bool messaging_parent_dgm_cleanup_init(struct messaging_context *msg)
{
struct tevent_req *req;
req = background_job_send(
msg, msg->event_ctx, msg, NULL, 0,
lp_parm_int(-1, "messaging", "messaging dgm cleanup interval",
60*15),
mess_parent_dgm_cleanup, msg);
if (req == NULL) {
return false;
}
tevent_req_set_callback(req, mess_parent_dgm_cleanup_done, msg);
return true;
}
static int mess_parent_dgm_cleanup(void *private_data)
{
int ret;
ret = messaging_dgm_wipe();
DEBUG(10, ("messaging_dgm_wipe returned %s\n",
ret ? strerror(ret) : "ok"));
return lp_parm_int(-1, "messaging", "messaging dgm cleanup interval",
60*15);
}
static void mess_parent_dgm_cleanup_done(struct tevent_req *req)
{
struct messaging_context *msg = tevent_req_callback_data(
req, struct messaging_context);
NTSTATUS status;
status = background_job_recv(req);
TALLOC_FREE(req);
DEBUG(1, ("messaging dgm cleanup job ended with %s\n",
nt_errstr(status)));
req = background_job_send(
msg, msg->event_ctx, msg, NULL, 0,
lp_parm_int(-1, "messaging", "messaging dgm cleanup interval",
60*15),
mess_parent_dgm_cleanup, msg);
if (req == NULL) {
DEBUG(1, ("background_job_send failed\n"));
return;
}
tevent_req_set_callback(req, mess_parent_dgm_cleanup_done, msg);
}
int messaging_cleanup(struct messaging_context *msg_ctx, pid_t pid)
{
int ret;
if (pid == 0) {
ret = messaging_dgm_wipe();
} else {
ret = messaging_dgm_cleanup(pid);
}
return ret;
}
struct tevent_context *messaging_tevent_context(
struct messaging_context *msg_ctx)
{
return msg_ctx->event_ctx;
}
struct server_id_db *messaging_names_db(struct messaging_context *msg_ctx)
{
return msg_ctx->names_db;
}
/** @} **/