mirror of
https://github.com/samba-team/samba.git
synced 2025-03-10 12:58:35 +03:00
Signed-off-by: Amitay Isaacs <amitay@gmail.com> (This used to be ctdb commit 2c19fa78ce0b25c3615b23664df32233bdbdea42)
550 lines
14 KiB
C
550 lines
14 KiB
C
/*
|
|
Misc control routines of libctdb
|
|
|
|
Copyright (C) Rusty Russell 2010
|
|
|
|
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 <sys/time.h>
|
|
#include <sys/socket.h>
|
|
#include <string.h>
|
|
#include <ctdb.h>
|
|
#include <ctdb_protocol.h>
|
|
#include "libctdb_private.h"
|
|
|
|
/* Remove type-safety macros. */
|
|
#undef ctdb_getrecmaster_send
|
|
#undef ctdb_getrecmode_send
|
|
#undef ctdb_getpnn_send
|
|
#undef ctdb_getdbstat_send
|
|
#undef ctdb_check_message_handlers_send
|
|
#undef ctdb_getnodemap_send
|
|
#undef ctdb_getpublicips_send
|
|
#undef ctdb_getdbseqnum_send
|
|
#undef ctdb_getifaces_send
|
|
#undef ctdb_getvnnmap_send
|
|
#undef ctdb_getcapabilities_send
|
|
|
|
bool ctdb_getrecmaster_recv(struct ctdb_connection *ctdb,
|
|
struct ctdb_request *req, uint32_t *recmaster)
|
|
{
|
|
struct ctdb_reply_control *reply;
|
|
|
|
reply = unpack_reply_control(req, CTDB_CONTROL_GET_RECMASTER);
|
|
if (!reply) {
|
|
return false;
|
|
}
|
|
if (reply->status == -1) {
|
|
DEBUG(ctdb, LOG_ERR, "ctdb_getrecmaster_recv: status -1");
|
|
return false;
|
|
}
|
|
/* Note: data is stashed in status - see ctdb_control_dispatch() */
|
|
*recmaster = reply->status;
|
|
return true;
|
|
}
|
|
|
|
struct ctdb_request *ctdb_getrecmaster_send(struct ctdb_connection *ctdb,
|
|
uint32_t destnode,
|
|
ctdb_callback_t callback,
|
|
void *private_data)
|
|
{
|
|
return new_ctdb_control_request(ctdb, CTDB_CONTROL_GET_RECMASTER,
|
|
destnode, NULL, 0,
|
|
callback, private_data);
|
|
}
|
|
|
|
bool ctdb_getrecmode_recv(struct ctdb_connection *ctdb,
|
|
struct ctdb_request *req, uint32_t *recmode)
|
|
{
|
|
struct ctdb_reply_control *reply;
|
|
|
|
reply = unpack_reply_control(req, CTDB_CONTROL_GET_RECMODE);
|
|
if (!reply) {
|
|
return false;
|
|
}
|
|
if (reply->status == -1) {
|
|
DEBUG(ctdb, LOG_ERR, "ctdb_getrecmode_recv: status -1");
|
|
return false;
|
|
}
|
|
/* Note: data is stashed in status - see ctdb_control_dispatch() */
|
|
*recmode = reply->status;
|
|
return true;
|
|
}
|
|
|
|
struct ctdb_request *ctdb_getrecmode_send(struct ctdb_connection *ctdb,
|
|
uint32_t destnode,
|
|
ctdb_callback_t callback,
|
|
void *private_data)
|
|
{
|
|
return new_ctdb_control_request(ctdb, CTDB_CONTROL_GET_RECMODE,
|
|
destnode, NULL, 0,
|
|
callback, private_data);
|
|
}
|
|
|
|
bool ctdb_getpnn_recv(struct ctdb_connection *ctdb,
|
|
struct ctdb_request *req, uint32_t *pnn)
|
|
{
|
|
struct ctdb_reply_control *reply;
|
|
|
|
reply = unpack_reply_control(req, CTDB_CONTROL_GET_PNN);
|
|
if (!reply) {
|
|
return false;
|
|
}
|
|
if (reply->status == -1) {
|
|
DEBUG(ctdb, LOG_ERR, "ctdb_getpnn_recv: status -1");
|
|
return false;
|
|
}
|
|
/* Note: data is stashed in status - see ctdb_control_dispatch() */
|
|
*pnn = reply->status;
|
|
return true;
|
|
}
|
|
|
|
struct ctdb_request *ctdb_getpnn_send(struct ctdb_connection *ctdb,
|
|
uint32_t destnode,
|
|
ctdb_callback_t callback,
|
|
void *private_data)
|
|
{
|
|
return new_ctdb_control_request(ctdb, CTDB_CONTROL_GET_PNN, destnode,
|
|
NULL, 0, callback, private_data);
|
|
}
|
|
|
|
bool ctdb_getdbstat_recv(struct ctdb_connection *ctdb,
|
|
struct ctdb_request *req,
|
|
struct ctdb_db_statistics **stat)
|
|
{
|
|
struct ctdb_reply_control *reply;
|
|
struct ctdb_db_statistics *s;
|
|
struct ctdb_db_statistics_wire *wire;
|
|
int i;
|
|
char *ptr;
|
|
|
|
reply = unpack_reply_control(req, CTDB_CONTROL_GET_DB_STATISTICS);
|
|
if (!reply) {
|
|
return false;
|
|
}
|
|
if (reply->status == -1) {
|
|
DEBUG(ctdb, LOG_ERR, "ctdb_getpnn_recv: status -1");
|
|
return false;
|
|
}
|
|
if (reply->datalen < offsetof(struct ctdb_db_statistics_wire, hot_keys)) {
|
|
DEBUG(ctdb, LOG_ERR, "ctdb_getdbstat_recv: returned data is %d bytes but should be >= %d", reply->datalen, (int)sizeof(struct ctdb_db_statistics));
|
|
return false;
|
|
}
|
|
|
|
wire = (struct ctdb_db_statistics_wire *)reply->data;
|
|
|
|
s = malloc(offsetof(struct ctdb_db_statistics, hot_keys) + sizeof(struct ctdb_db_hot_key) * wire->num_hot_keys);
|
|
if (!s) {
|
|
return false;
|
|
}
|
|
s->db_ro_delegations = wire->db_ro_delegations;
|
|
s->db_ro_revokes = wire->db_ro_revokes;
|
|
for (i = 0; i < MAX_COUNT_BUCKETS; i++) {
|
|
s->hop_count_bucket[i] = wire->hop_count_bucket[i];
|
|
}
|
|
s->num_hot_keys = wire->num_hot_keys;
|
|
ptr = &wire->hot_keys[0];
|
|
for (i = 0; i < wire->num_hot_keys; i++) {
|
|
s->hot_keys[i].count = *(uint32_t *)ptr;
|
|
ptr += 4;
|
|
|
|
s->hot_keys[i].key.dsize = *(uint32_t *)ptr;
|
|
ptr += 4;
|
|
|
|
s->hot_keys[i].key.dptr = malloc(s->hot_keys[i].key.dsize);
|
|
memcpy(s->hot_keys[i].key.dptr, ptr, s->hot_keys[i].key.dsize);
|
|
ptr += s->hot_keys[i].key.dsize;
|
|
}
|
|
|
|
*stat = s;
|
|
|
|
return true;
|
|
}
|
|
|
|
struct ctdb_request *ctdb_getdbstat_send(struct ctdb_connection *ctdb,
|
|
uint32_t destnode,
|
|
uint32_t db_id,
|
|
ctdb_callback_t callback,
|
|
void *private_data)
|
|
{
|
|
uint32_t indata = db_id;
|
|
|
|
return new_ctdb_control_request(ctdb, CTDB_CONTROL_GET_DB_STATISTICS, destnode,
|
|
&indata, sizeof(indata), callback, private_data);
|
|
}
|
|
|
|
void ctdb_free_dbstat(struct ctdb_db_statistics *stat)
|
|
{
|
|
int i;
|
|
|
|
if (stat == NULL) {
|
|
return;
|
|
}
|
|
|
|
for (i = 0; i < stat->num_hot_keys; i++) {
|
|
if (stat->hot_keys[i].key.dptr != NULL) {
|
|
free(stat->hot_keys[i].key.dptr);
|
|
}
|
|
}
|
|
|
|
free(stat);
|
|
}
|
|
|
|
bool ctdb_getnodemap_recv(struct ctdb_connection *ctdb,
|
|
struct ctdb_request *req, struct ctdb_node_map **nodemap)
|
|
{
|
|
struct ctdb_reply_control *reply;
|
|
|
|
*nodemap = NULL;
|
|
reply = unpack_reply_control(req, CTDB_CONTROL_GET_NODEMAP);
|
|
if (!reply) {
|
|
return false;
|
|
}
|
|
if (reply->status == -1) {
|
|
DEBUG(ctdb, LOG_ERR, "ctdb_getnodemap_recv: status -1");
|
|
return false;
|
|
}
|
|
if (reply->datalen == 0) {
|
|
DEBUG(ctdb, LOG_ERR, "ctdb_getnodemap_recv: returned data is 0 bytes");
|
|
return false;
|
|
}
|
|
|
|
*nodemap = malloc(reply->datalen);
|
|
if (*nodemap == NULL) {
|
|
DEBUG(ctdb, LOG_ERR, "ctdb_getnodemap_recv: failed to malloc buffer");
|
|
return false;
|
|
}
|
|
memcpy(*nodemap, reply->data, reply->datalen);
|
|
|
|
return true;
|
|
}
|
|
struct ctdb_request *ctdb_getnodemap_send(struct ctdb_connection *ctdb,
|
|
uint32_t destnode,
|
|
ctdb_callback_t callback,
|
|
void *private_data)
|
|
{
|
|
return new_ctdb_control_request(ctdb, CTDB_CONTROL_GET_NODEMAP,
|
|
destnode,
|
|
NULL, 0, callback, private_data);
|
|
}
|
|
|
|
void ctdb_free_nodemap(struct ctdb_node_map *nodemap)
|
|
{
|
|
if (nodemap == NULL) {
|
|
return;
|
|
}
|
|
free(nodemap);
|
|
}
|
|
|
|
bool ctdb_getpublicips_recv(struct ctdb_connection *ctdb,
|
|
struct ctdb_request *req,
|
|
struct ctdb_all_public_ips **ips)
|
|
{
|
|
struct ctdb_reply_control *reply;
|
|
|
|
*ips = NULL;
|
|
reply = unpack_reply_control(req, CTDB_CONTROL_GET_PUBLIC_IPS);
|
|
if (!reply) {
|
|
return false;
|
|
}
|
|
if (reply->status == -1) {
|
|
DEBUG(ctdb, LOG_ERR, "ctdb_getpublicips_recv: status -1");
|
|
return false;
|
|
}
|
|
if (reply->datalen == 0) {
|
|
DEBUG(ctdb, LOG_ERR, "ctdb_getpublicips_recv: returned data is 0 bytes");
|
|
return false;
|
|
}
|
|
|
|
*ips = malloc(reply->datalen);
|
|
if (*ips == NULL) {
|
|
DEBUG(ctdb, LOG_ERR, "ctdb_getpublicips_recv: failed to malloc buffer");
|
|
return false;
|
|
}
|
|
memcpy(*ips, reply->data, reply->datalen);
|
|
|
|
return true;
|
|
}
|
|
struct ctdb_request *ctdb_getpublicips_send(struct ctdb_connection *ctdb,
|
|
uint32_t destnode,
|
|
ctdb_callback_t callback,
|
|
void *private_data)
|
|
{
|
|
return new_ctdb_control_request(ctdb, CTDB_CONTROL_GET_PUBLIC_IPS,
|
|
destnode,
|
|
NULL, 0, callback, private_data);
|
|
}
|
|
|
|
void ctdb_free_publicips(struct ctdb_all_public_ips *ips)
|
|
{
|
|
if (ips == NULL) {
|
|
return;
|
|
}
|
|
free(ips);
|
|
}
|
|
|
|
bool ctdb_getdbseqnum_recv(struct ctdb_connection *ctdb,
|
|
struct ctdb_request *req, uint64_t *seqnum)
|
|
{
|
|
struct ctdb_reply_control *reply;
|
|
|
|
reply = unpack_reply_control(req, CTDB_CONTROL_GET_DB_SEQNUM);
|
|
if (!reply) {
|
|
return false;
|
|
}
|
|
if (reply->status == -1) {
|
|
DEBUG(ctdb, LOG_ERR, "ctdb_getdbseqnum_recv: status -1");
|
|
return false;
|
|
}
|
|
|
|
if (reply->datalen != sizeof(uint64_t)) {
|
|
DEBUG(ctdb, LOG_ERR, "ctdb_getdbseqnum wrong size of data was %d but expected %d bytes", reply->datalen, (int)sizeof(uint64_t));
|
|
return false;
|
|
}
|
|
|
|
*seqnum = *((uint64_t *)reply->data);
|
|
|
|
return true;
|
|
}
|
|
|
|
struct ctdb_request *ctdb_getdbseqnum_send(struct ctdb_connection *ctdb,
|
|
uint32_t destnode,
|
|
uint32_t dbid,
|
|
ctdb_callback_t callback,
|
|
void *private_data)
|
|
{
|
|
uint64_t indata;
|
|
|
|
*((uint32_t *)&indata) = dbid;
|
|
|
|
return new_ctdb_control_request(ctdb, CTDB_CONTROL_GET_DB_SEQNUM,
|
|
destnode, &indata, sizeof(uint64_t),
|
|
callback, private_data);
|
|
}
|
|
|
|
bool ctdb_check_message_handlers_recv(struct ctdb_connection *ctdb,
|
|
struct ctdb_request *req,
|
|
uint32_t num, uint8_t *result)
|
|
{
|
|
struct ctdb_reply_control *reply;
|
|
int i, count;
|
|
|
|
reply = unpack_reply_control(req, CTDB_CONTROL_CHECK_SRVIDS);
|
|
if (!reply) {
|
|
return false;
|
|
}
|
|
if (reply->status == -1) {
|
|
DEBUG(ctdb, LOG_ERR, "ctdb_check_message_handlers_recv: status -1");
|
|
return false;
|
|
}
|
|
|
|
count = (num + 7) / 8;
|
|
if (count != reply->datalen) {
|
|
DEBUG(ctdb, LOG_ERR, "ctdb_check_message_handlers_recv: wrong amount of data returned, expected %d bytes for %d srvids but received %d bytes", count, num, reply->datalen);
|
|
return false;
|
|
}
|
|
|
|
for (i = 0; i < num; i++) {
|
|
result[i] = !!(reply->data[i / 8] & (1 << (i % 8)));
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
struct ctdb_request *
|
|
ctdb_check_message_handlers_send(struct ctdb_connection *ctdb,
|
|
uint32_t destnode,
|
|
uint32_t num,
|
|
uint64_t *mhs,
|
|
ctdb_callback_t callback,
|
|
void *private_data)
|
|
{
|
|
return new_ctdb_control_request(ctdb, CTDB_CONTROL_CHECK_SRVIDS,
|
|
destnode,
|
|
mhs, num * sizeof(uint64_t) ,
|
|
callback, private_data);
|
|
}
|
|
|
|
|
|
bool ctdb_getifaces_recv(struct ctdb_connection *ctdb,
|
|
struct ctdb_request *req,
|
|
struct ctdb_ifaces_list **ifaces)
|
|
{
|
|
struct ctdb_reply_control *reply;
|
|
struct ctdb_ifaces_list *ifc;
|
|
int i, len;
|
|
|
|
*ifaces = NULL;
|
|
reply = unpack_reply_control(req, CTDB_CONTROL_GET_IFACES);
|
|
if (!reply) {
|
|
return false;
|
|
}
|
|
if (reply->status == -1) {
|
|
DEBUG(ctdb, LOG_ERR, "ctdb_getifaces_recv: status -1");
|
|
return false;
|
|
}
|
|
if (reply->datalen == 0) {
|
|
DEBUG(ctdb, LOG_ERR, "ctdb_getifaces_recv: returned data is 0 bytes");
|
|
return false;
|
|
}
|
|
|
|
len = offsetof(struct ctdb_ifaces_list, ifaces);
|
|
if (len > reply->datalen) {
|
|
DEBUG(ctdb, LOG_ERR, "ctdb_getifaces_recv: returned data is %d bytes but %d is minimum", reply->datalen, (int)offsetof(struct ctdb_ifaces_list, ifaces));
|
|
return false;
|
|
}
|
|
|
|
ifc = (struct ctdb_ifaces_list *)(reply->data);
|
|
len += ifc->num * sizeof(struct ctdb_iface_info);
|
|
|
|
if (len != reply->datalen) {
|
|
DEBUG(ctdb, LOG_ERR, "ctdb_getifaces_recv: returned data is %d bytes but should be %d", reply->datalen, len);
|
|
return false;
|
|
}
|
|
|
|
ifc = malloc(reply->datalen);
|
|
if (ifc == NULL) {
|
|
DEBUG(ctdb, LOG_ERR, "ctdb_getifaces_recv: failed to malloc buffer");
|
|
return false;
|
|
}
|
|
memcpy(ifc, reply->data, reply->datalen);
|
|
|
|
/* make sure we null terminate the returned strings */
|
|
for (i = 0; i < ifc->num; i++) {
|
|
ifc->ifaces[i].name[CTDB_IFACE_SIZE] = '\0';
|
|
}
|
|
|
|
*ifaces = ifc;
|
|
|
|
return true;
|
|
}
|
|
|
|
void ctdb_free_ifaces(struct ctdb_ifaces_list *ifaces)
|
|
{
|
|
free(ifaces);
|
|
}
|
|
|
|
struct ctdb_request *ctdb_getifaces_send(struct ctdb_connection *ctdb,
|
|
uint32_t destnode,
|
|
ctdb_callback_t callback,
|
|
void *private_data)
|
|
{
|
|
return new_ctdb_control_request(ctdb, CTDB_CONTROL_GET_IFACES,
|
|
destnode,
|
|
NULL, 0, callback, private_data);
|
|
}
|
|
|
|
bool ctdb_getvnnmap_recv(struct ctdb_connection *ctdb,
|
|
struct ctdb_request *req,
|
|
struct ctdb_vnn_map **vnnmap)
|
|
{
|
|
struct ctdb_reply_control *reply;
|
|
struct ctdb_vnn_map_wire *map;
|
|
struct ctdb_vnn_map *tmap;
|
|
int len;
|
|
|
|
*vnnmap = NULL;
|
|
reply = unpack_reply_control(req, CTDB_CONTROL_GETVNNMAP);
|
|
if (!reply) {
|
|
return false;
|
|
}
|
|
if (reply->status == -1) {
|
|
DEBUG(ctdb, LOG_ERR, "ctdb_getvnnmap_recv: status -1");
|
|
return false;
|
|
}
|
|
if (reply->datalen == 0) {
|
|
DEBUG(ctdb, LOG_ERR, "ctdb_getvnnmap_recv: returned data is 0 bytes");
|
|
return false;
|
|
}
|
|
|
|
len = offsetof(struct ctdb_vnn_map_wire, map);
|
|
if (len > reply->datalen) {
|
|
DEBUG(ctdb, LOG_ERR, "ctdb_getvnnmap_recv: returned data is %d bytes but %d is minimum", reply->datalen, (int)offsetof(struct ctdb_vnn_map_wire, map));
|
|
return false;
|
|
}
|
|
|
|
map = (struct ctdb_vnn_map_wire *)(reply->data);
|
|
len += map->size * sizeof(uint32_t);
|
|
|
|
if (len != reply->datalen) {
|
|
DEBUG(ctdb, LOG_ERR, "ctdb_getvnnmap_recv: returned data is %d bytes but should be %d", reply->datalen, len);
|
|
return false;
|
|
}
|
|
|
|
tmap = malloc(sizeof(struct ctdb_vnn_map));
|
|
if (tmap == NULL) {
|
|
DEBUG(ctdb, LOG_ERR, "ctdb_getvnnmap_recv: failed to malloc buffer");
|
|
return false;
|
|
}
|
|
|
|
tmap->generation = map->generation;
|
|
tmap->size = map->size;
|
|
tmap->map = malloc(sizeof(uint32_t) * map->size);
|
|
if (tmap->map == NULL) {
|
|
DEBUG(ctdb, LOG_ERR, "ctdb_getvnnmap_recv: failed to malloc buffer");
|
|
free(tmap);
|
|
return false;
|
|
}
|
|
|
|
memcpy(tmap->map, map->map, sizeof(uint32_t)*map->size);
|
|
|
|
*vnnmap = tmap;
|
|
|
|
return true;
|
|
}
|
|
|
|
void ctdb_free_vnnmap(struct ctdb_vnn_map *vnnmap)
|
|
{
|
|
free(vnnmap->map);
|
|
free(vnnmap);
|
|
}
|
|
|
|
struct ctdb_request *ctdb_getvnnmap_send(struct ctdb_connection *ctdb,
|
|
uint32_t destnode,
|
|
ctdb_callback_t callback,
|
|
void *private_data)
|
|
{
|
|
return new_ctdb_control_request(ctdb, CTDB_CONTROL_GETVNNMAP,
|
|
destnode,
|
|
NULL, 0, callback, private_data);
|
|
}
|
|
|
|
bool ctdb_getcapabilities_recv(struct ctdb_connection *ctdb,
|
|
struct ctdb_request *req, uint32_t *capabilities)
|
|
{
|
|
struct ctdb_reply_control *reply;
|
|
|
|
reply = unpack_reply_control(req, CTDB_CONTROL_GET_CAPABILITIES);
|
|
if (!reply) {
|
|
return false;
|
|
}
|
|
if (reply->status == -1) {
|
|
DEBUG(ctdb, LOG_ERR, "ctdb_getcapabilities_recv: status -1");
|
|
return false;
|
|
}
|
|
*capabilities = *((uint32_t *)reply->data);
|
|
return true;
|
|
}
|
|
|
|
struct ctdb_request *ctdb_getcapabilities_send(struct ctdb_connection *ctdb,
|
|
uint32_t destnode,
|
|
ctdb_callback_t callback,
|
|
void *private_data)
|
|
{
|
|
return new_ctdb_control_request(ctdb, CTDB_CONTROL_GET_CAPABILITIES,
|
|
destnode,
|
|
NULL, 0, callback, private_data);
|
|
}
|
|
|