/* Unix SMB/CIFS implementation. Samba internal messaging functions 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 . */ #include "includes.h" #include "lib/messages_ctdbd.h" #include "lib/util/server_id.h" #include "messages.h" #include "util_tdb.h" #include "lib/util/iov_buf.h" #include "lib/messages_util.h" #include "ctdbd_conn.h" #include "lib/cluster_support.h" struct messaging_ctdbd_context; struct messaging_ctdbd_fde_ev { struct messaging_ctdbd_fde_ev *prev, *next; /* * Backreference to enable DLIST_REMOVE from our * destructor. Also, set to NULL when the dgm_context dies * before the messaging_dgm_fde_ev. */ struct messaging_ctdbd_context *ctx; struct tevent_context *ev; struct tevent_fd *fde; }; struct messaging_ctdbd_context { struct ctdbd_connection *conn; struct messaging_ctdbd_fde_ev *fde_evs; void (*recv_cb)(struct tevent_context *ev, const uint8_t *msg, size_t msg_len, int *fds, size_t num_fds, void *private_data); void *private_data; }; /* * This is a Samba3 hack/optimization. Routines like process_exists need to * talk to ctdbd, and they don't get handed a messaging context. */ static struct ctdbd_connection *global_ctdbd_connection; static int global_ctdb_connection_pid; struct ctdbd_connection *messaging_ctdbd_connection(void) { if (!lp_clustering()) { return NULL; } if (global_ctdb_connection_pid == 0 && global_ctdbd_connection == NULL) { struct tevent_context *ev; struct messaging_context *msg; ev = samba_tevent_context_init(NULL); if (!ev) { DEBUG(0,("samba_tevent_context_init failed\n")); return NULL; } msg = messaging_init(NULL, ev); if (!msg) { DEBUG(0,("messaging_init failed\n")); return NULL; } } if (global_ctdb_connection_pid != getpid()) { DEBUG(0,("messaging_ctdbd_connection():" "valid for pid[%jd] but it's [%jd]\n", (intmax_t)global_ctdb_connection_pid, (intmax_t)getpid())); smb_panic("messaging_ctdbd_connection() invalid process\n"); } return global_ctdbd_connection; } static int messaging_ctdb_send(struct server_id src, struct server_id pid, int msg_type, const struct iovec *iov, int iovlen, const int *fds, size_t num_fds, struct messaging_backend *backend) { struct messaging_ctdbd_context *ctx = talloc_get_type_abort( backend->private_data, struct messaging_ctdbd_context); uint8_t hdr[MESSAGE_HDR_LENGTH]; struct iovec iov2[iovlen+1]; if (num_fds > 0) { return ENOSYS; } message_hdr_put(hdr, msg_type, src, pid); iov2[0] = (struct iovec){ .iov_base = hdr, .iov_len = sizeof(hdr) }; memcpy(&iov2[1], iov, iovlen * sizeof(*iov)); return ctdbd_messaging_send_iov(ctx->conn, pid.vnn, pid.pid, iov2, iovlen+1); } static int messaging_ctdbd_destructor(struct messaging_ctdbd_context *ctx) { /* * The global connection just went away */ global_ctdb_connection_pid = 0; global_ctdbd_connection = NULL; return 0; } static int messaging_ctdb_recv( struct tevent_context *ev, uint32_t src_vnn, uint32_t dst_vnn, uint64_t dst_srvid, const uint8_t *msg, size_t msg_len, void *private_data) { struct messaging_ctdbd_context *ctx = talloc_get_type_abort( private_data, struct messaging_ctdbd_context); ctx->recv_cb(ev, msg, msg_len, NULL, 0, ctx->private_data); return 0; } static void messaging_ctdbd_readable(struct tevent_context *ev, struct tevent_fd *fde, uint16_t flags, void *private_data) { struct ctdbd_connection *conn = talloc_get_type_abort( private_data, struct ctdbd_connection); if ((flags & TEVENT_FD_READ) == 0) { return; } ctdbd_socket_readable(ev, conn); } static int messaging_ctdbd_init_internal( struct messaging_context *msg_ctx, TALLOC_CTX *mem_ctx, struct messaging_ctdbd_context *ctx, void (*recv_cb)(struct tevent_context *ev, const uint8_t *msg, size_t msg_len, int *fds, size_t num_fds, void *private_data), void *private_data, bool reinit) { int ret; if (reinit) { ret = ctdbd_reinit_connection(ctx, lp_ctdbd_socket(), lp_ctdb_timeout(), ctx->conn); if (ret != 0) { DBG_ERR("ctdbd_reinit_connection failed: %s\n", strerror(ret)); return ret; } } else { ret = ctdbd_init_connection(ctx, lp_ctdbd_socket(), lp_ctdb_timeout(), &ctx->conn); if (ret != 0) { DBG_ERR("ctdbd_init_connection failed: %s\n", strerror(ret)); return ret; } } ret = register_with_ctdbd(ctx->conn, MSG_SRVID_SAMBA, NULL, NULL); if (ret != 0) { DBG_DEBUG("Could not register MSG_SRVID_SAMBA: %s\n", strerror(ret)); return ret; } ctx->recv_cb = recv_cb; ctx->private_data = private_data; ret = register_with_ctdbd(ctx->conn, getpid(), messaging_ctdb_recv, ctx); if (ret != 0) { DEBUG(10, ("register_with_ctdbd failed: %s\n", strerror(ret))); return ret; } global_ctdb_connection_pid = getpid(); global_ctdbd_connection = ctx->conn; talloc_set_destructor(ctx, messaging_ctdbd_destructor); set_my_vnn(ctdbd_vnn(ctx->conn)); return 0; } int messaging_ctdbd_init(struct messaging_context *msg_ctx, TALLOC_CTX *mem_ctx, void (*recv_cb)(struct tevent_context *ev, const uint8_t *msg, size_t msg_len, int *fds, size_t num_fds, void *private_data), void *private_data, struct messaging_backend **presult) { struct messaging_backend *result; struct messaging_ctdbd_context *ctx; int ret; if (!(result = talloc(mem_ctx, struct messaging_backend))) { DEBUG(0, ("talloc failed\n")); return ENOMEM; } if (!(ctx = talloc_zero(result, struct messaging_ctdbd_context))) { DEBUG(0, ("talloc failed\n")); TALLOC_FREE(result); return ENOMEM; } ret = messaging_ctdbd_init_internal(msg_ctx, mem_ctx, ctx, recv_cb, private_data, false); if (ret != 0) { TALLOC_FREE(result); return ret; } result->send_fn = messaging_ctdb_send; result->private_data = (void *)ctx; *presult = result; return 0; } int messaging_ctdbd_reinit(struct messaging_context *msg_ctx, TALLOC_CTX *mem_ctx, void (*recv_cb)(struct tevent_context *ev, const uint8_t *msg, size_t msg_len, int *fds, size_t num_fds, void *private_data), void *private_data, struct messaging_backend *backend) { struct messaging_ctdbd_context *ctx = talloc_get_type_abort( backend->private_data, struct messaging_ctdbd_context); int ret; ret = messaging_ctdbd_init_internal(msg_ctx, mem_ctx, ctx, recv_cb, private_data, true); if (ret != 0) { return ret; } return 0; } struct messaging_ctdbd_fde { struct tevent_fd *fde; }; static int messaging_ctdbd_fde_ev_destructor( struct messaging_ctdbd_fde_ev *fde_ev) { if (fde_ev->ctx != NULL) { DLIST_REMOVE(fde_ev->ctx->fde_evs, fde_ev); fde_ev->ctx = NULL; } return 0; } struct messaging_ctdbd_fde *messaging_ctdbd_register_tevent_context( TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct messaging_backend *backend) { struct messaging_ctdbd_context *ctx = talloc_get_type_abort( backend->private_data, struct messaging_ctdbd_context); struct messaging_ctdbd_fde_ev *fde_ev; struct messaging_ctdbd_fde *fde; if (ctx == NULL) { return NULL; } fde = talloc(mem_ctx, struct messaging_ctdbd_fde); if (fde == NULL) { return NULL; } for (fde_ev = ctx->fde_evs; fde_ev != NULL; fde_ev = fde_ev->next) { if ((fde_ev->ev == ev) && (tevent_fd_get_flags(fde_ev->fde) != 0)) { break; } } if (fde_ev == NULL) { int fd = ctdbd_conn_get_fd(ctx->conn); fde_ev = talloc(fde, struct messaging_ctdbd_fde_ev); if (fde_ev == NULL) { return NULL; } fde_ev->fde = tevent_add_fd( ev, fde_ev, fd, TEVENT_FD_READ, messaging_ctdbd_readable, ctx->conn); if (fde_ev->fde == NULL) { TALLOC_FREE(fde); return NULL; } fde_ev->ev = ev; fde_ev->ctx = ctx; DLIST_ADD(ctx->fde_evs, fde_ev); talloc_set_destructor( fde_ev, messaging_ctdbd_fde_ev_destructor); } else { /* * Same trick as with tdb_wrap: The caller will never * see the talloc_referenced object, the * messaging_ctdbd_fde_ev, so problems with * talloc_unlink will not happen. */ if (talloc_reference(fde, fde_ev) == NULL) { TALLOC_FREE(fde); return NULL; } } fde->fde = fde_ev->fde; return fde; }