mirror of
https://github.com/samba-team/samba.git
synced 2025-08-26 01:49:31 +03:00
separate out the freeze/thaw handling from recovery
(This used to be ctdb commit 0b0640bd8b8334961f240e0cf276ac112cd6e616)
This commit is contained in:
@ -29,7 +29,7 @@ CTDB_COMMON_OBJ = common/ctdb.o common/ctdb_daemon.o common/ctdb_client.o \
|
||||
common/ctdb_io.o common/util.o common/ctdb_util.o \
|
||||
common/ctdb_call.o common/ctdb_ltdb.o common/ctdb_lockwait.o \
|
||||
common/ctdb_message.o common/cmdline.o common/ctdb_control.o \
|
||||
lib/util/debug.o common/ctdb_recover.o common/ctdb_traverse.o
|
||||
lib/util/debug.o common/ctdb_recover.o common/ctdb_freeze.o common/ctdb_traverse.o
|
||||
|
||||
CTDB_TCP_OBJ = tcp/tcp_connect.o tcp/tcp_io.o tcp/tcp_init.o
|
||||
|
||||
|
@ -937,7 +937,7 @@ int ctdb_ctrl_setrecmaster(struct ctdb_context *ctdb, struct timeval timeout, ui
|
||||
CTDB_CONTROL_SET_RECMASTER, 0, data,
|
||||
ctdb, &outdata, &res, &timeout);
|
||||
if (ret != 0 || res != 0) {
|
||||
DEBUG(0,(__location__ " ctdb_control for getrecmode failed\n"));
|
||||
DEBUG(0,(__location__ " ctdb_control for setrecmaster failed\n"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -1640,3 +1640,41 @@ int ctdb_ctrl_getpid(struct ctdb_context *ctdb, struct timeval timeout, uint32_t
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
freeze a node
|
||||
*/
|
||||
int ctdb_ctrl_freeze(struct ctdb_context *ctdb, struct timeval timeout, uint32_t destnode)
|
||||
{
|
||||
int ret;
|
||||
int32_t res;
|
||||
|
||||
ret = ctdb_control(ctdb, destnode, 0,
|
||||
CTDB_CONTROL_FREEZE, 0, tdb_null,
|
||||
NULL, NULL, &res, &timeout);
|
||||
if (ret != 0 || res != 0) {
|
||||
DEBUG(0,(__location__ " ctdb_control freeze failed\n"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
thaw a node
|
||||
*/
|
||||
int ctdb_ctrl_thaw(struct ctdb_context *ctdb, struct timeval timeout, uint32_t destnode)
|
||||
{
|
||||
int ret;
|
||||
int32_t res;
|
||||
|
||||
ret = ctdb_control(ctdb, destnode, 0,
|
||||
CTDB_CONTROL_THAW, 0, tdb_null,
|
||||
NULL, NULL, &res, &timeout);
|
||||
if (ret != 0 || res != 0) {
|
||||
DEBUG(0,(__location__ " ctdb_control thaw failed\n"));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -38,11 +38,15 @@ struct ctdb_control_state {
|
||||
process a control request
|
||||
*/
|
||||
static int32_t ctdb_control_dispatch(struct ctdb_context *ctdb,
|
||||
uint32_t opcode,
|
||||
uint64_t srvid, uint32_t client_id,
|
||||
struct ctdb_req_control *c,
|
||||
TDB_DATA indata,
|
||||
TDB_DATA *outdata, uint32_t srcnode)
|
||||
TDB_DATA *outdata, uint32_t srcnode,
|
||||
bool *async_reply)
|
||||
{
|
||||
uint32_t opcode = c->opcode;
|
||||
uint64_t srvid = c->srvid;
|
||||
uint32_t client_id = c->client_id;
|
||||
|
||||
switch (opcode) {
|
||||
case CTDB_CONTROL_PROCESS_EXISTS: {
|
||||
CHECK_CONTROL_DATA_SIZE(sizeof(pid_t));
|
||||
@ -111,11 +115,6 @@ static int32_t ctdb_control_dispatch(struct ctdb_context *ctdb,
|
||||
case CTDB_CONTROL_PUSH_DB:
|
||||
return ctdb_control_push_db(ctdb, indata);
|
||||
|
||||
case CTDB_CONTROL_SET_RECMODE: {
|
||||
ctdb->recovery_mode = ((uint32_t *)(&indata.dptr[0]))[0];
|
||||
return 0;
|
||||
}
|
||||
|
||||
case CTDB_CONTROL_GET_RECMODE: {
|
||||
return ctdb->recovery_mode;
|
||||
}
|
||||
@ -221,6 +220,18 @@ static int32_t ctdb_control_dispatch(struct ctdb_context *ctdb,
|
||||
CHECK_CONTROL_DATA_SIZE(sizeof(uint32_t));
|
||||
return ctdb_ltdb_set_seqnum_frequency(ctdb, *(uint32_t *)indata.dptr);
|
||||
|
||||
case CTDB_CONTROL_FREEZE:
|
||||
CHECK_CONTROL_DATA_SIZE(0);
|
||||
return ctdb_control_freeze(ctdb, c, async_reply);
|
||||
|
||||
case CTDB_CONTROL_THAW:
|
||||
CHECK_CONTROL_DATA_SIZE(0);
|
||||
return ctdb_control_thaw(ctdb);
|
||||
|
||||
case CTDB_CONTROL_SET_RECMODE:
|
||||
CHECK_CONTROL_DATA_SIZE(sizeof(uint32_t));
|
||||
return ctdb_control_set_recmode(ctdb, indata);
|
||||
|
||||
default:
|
||||
DEBUG(0,(__location__ " Unknown CTDB control opcode %u\n", opcode));
|
||||
return -1;
|
||||
@ -267,21 +278,18 @@ void ctdb_request_control(struct ctdb_context *ctdb, struct ctdb_req_header *hdr
|
||||
struct ctdb_req_control *c = (struct ctdb_req_control *)hdr;
|
||||
TDB_DATA data, *outdata;
|
||||
int32_t status;
|
||||
bool async_reply = False;
|
||||
|
||||
data.dptr = &c->data[0];
|
||||
data.dsize = c->datalen;
|
||||
|
||||
outdata = talloc_zero(c, TDB_DATA);
|
||||
|
||||
if (c->opcode == CTDB_CONTROL_SET_RECMODE) {
|
||||
/* this function operates asynchronously */
|
||||
ctdb_control_set_recmode(ctdb, c, data);
|
||||
return;
|
||||
}
|
||||
status = ctdb_control_dispatch(ctdb, c, data, outdata, hdr->srcnode, &async_reply);
|
||||
|
||||
status = ctdb_control_dispatch(ctdb, c->opcode, c->srvid, c->client_id,
|
||||
data, outdata, hdr->srcnode);
|
||||
ctdb_request_control_reply(ctdb, c, outdata, status);
|
||||
if (!async_reply) {
|
||||
ctdb_request_control_reply(ctdb, c, outdata, status);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
262
ctdb/common/ctdb_freeze.c
Normal file
262
ctdb/common/ctdb_freeze.c
Normal file
@ -0,0 +1,262 @@
|
||||
/*
|
||||
ctdb freeze handling
|
||||
|
||||
Copyright (C) Andrew Tridgell 2007
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2 of the License, or (at your option) any later version.
|
||||
|
||||
This library 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
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#include "includes.h"
|
||||
#include "lib/events/events.h"
|
||||
#include "lib/tdb/include/tdb.h"
|
||||
#include "system/network.h"
|
||||
#include "system/filesys.h"
|
||||
#include "system/wait.h"
|
||||
#include "../include/ctdb_private.h"
|
||||
#include "lib/util/dlinklist.h"
|
||||
#include "db_wrap.h"
|
||||
|
||||
|
||||
/*
|
||||
lock all databases
|
||||
*/
|
||||
static int ctdb_lock_all_databases(struct ctdb_context *ctdb)
|
||||
{
|
||||
struct ctdb_db_context *ctdb_db;
|
||||
for (ctdb_db=ctdb->db_list;ctdb_db;ctdb_db=ctdb_db->next) {
|
||||
if (tdb_lockall(ctdb_db->ltdb->tdb) != 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
lock all databases - mark only
|
||||
*/
|
||||
static int ctdb_lock_all_databases_mark(struct ctdb_context *ctdb)
|
||||
{
|
||||
struct ctdb_db_context *ctdb_db;
|
||||
for (ctdb_db=ctdb->db_list;ctdb_db;ctdb_db=ctdb_db->next) {
|
||||
if (tdb_lockall_mark(ctdb_db->ltdb->tdb) != 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
lock all databases - unmark only
|
||||
*/
|
||||
static int ctdb_lock_all_databases_unmark(struct ctdb_context *ctdb)
|
||||
{
|
||||
struct ctdb_db_context *ctdb_db;
|
||||
for (ctdb_db=ctdb->db_list;ctdb_db;ctdb_db=ctdb_db->next) {
|
||||
if (tdb_lockall_unmark(ctdb_db->ltdb->tdb) != 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
a list of control requests waiting for a freeze lock child to get
|
||||
the database locks
|
||||
*/
|
||||
struct ctdb_freeze_waiter {
|
||||
struct ctdb_freeze_waiter *next, *prev;
|
||||
struct ctdb_context *ctdb;
|
||||
struct ctdb_req_control *c;
|
||||
int32_t status;
|
||||
};
|
||||
|
||||
/* a handle to a freeze lock child process */
|
||||
struct ctdb_freeze_handle {
|
||||
struct ctdb_context *ctdb;
|
||||
pid_t child;
|
||||
int fd;
|
||||
struct ctdb_freeze_waiter *waiters;
|
||||
};
|
||||
|
||||
/*
|
||||
destroy a freeze handle
|
||||
*/
|
||||
static int ctdb_freeze_handle_destructor(struct ctdb_freeze_handle *h)
|
||||
{
|
||||
if (h->ctdb->freeze_mode == CTDB_FREEZE_FROZEN) {
|
||||
ctdb_lock_all_databases_unmark(h->ctdb);
|
||||
}
|
||||
h->ctdb->freeze_mode = CTDB_FREEZE_NONE;
|
||||
kill(h->child, SIGKILL);
|
||||
waitpid(h->child, NULL, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
called when the child writes its status to us
|
||||
*/
|
||||
static void ctdb_freeze_lock_handler(struct event_context *ev, struct fd_event *fde,
|
||||
uint16_t flags, void *private_data)
|
||||
{
|
||||
struct ctdb_freeze_handle *h = talloc_get_type(private_data, struct ctdb_freeze_handle);
|
||||
int32_t status;
|
||||
struct ctdb_freeze_waiter *w;
|
||||
int ret;
|
||||
|
||||
if (read(h->fd, &status, sizeof(status)) != sizeof(status)) {
|
||||
DEBUG(0,("read error from freeze lock child\n"));
|
||||
status = -1;
|
||||
}
|
||||
|
||||
if (status == -1) {
|
||||
DEBUG(0,("Failed to get locks in ctdb_freeze_child\n"));
|
||||
/* we didn't get the locks - destroy the handle */
|
||||
talloc_free(h);
|
||||
return;
|
||||
}
|
||||
|
||||
ret = ctdb_lock_all_databases_mark(h->ctdb);
|
||||
if (ret == -1) {
|
||||
DEBUG(0,("Failed to mark locks in ctdb_freeze\n"));
|
||||
talloc_free(h);
|
||||
return;
|
||||
}
|
||||
|
||||
h->ctdb->freeze_mode = CTDB_FREEZE_FROZEN;
|
||||
|
||||
/* notify the waiters */
|
||||
while ((w = h->ctdb->freeze_handle->waiters)) {
|
||||
w->status = status;
|
||||
DLIST_REMOVE(h->ctdb->freeze_handle->waiters, w);
|
||||
talloc_free(w);
|
||||
}
|
||||
|
||||
talloc_free(fde);
|
||||
}
|
||||
|
||||
/*
|
||||
create a child which gets locks on all the open databases, then calls the callback telling the parent
|
||||
that it is done
|
||||
*/
|
||||
static struct ctdb_freeze_handle *ctdb_freeze_lock(struct ctdb_context *ctdb)
|
||||
{
|
||||
struct ctdb_freeze_handle *h;
|
||||
int fd[2];
|
||||
struct fd_event *fde;
|
||||
|
||||
h = talloc_zero(ctdb, struct ctdb_freeze_handle);
|
||||
CTDB_NO_MEMORY_VOID(ctdb, h);
|
||||
|
||||
h->ctdb = ctdb;
|
||||
|
||||
/* use socketpair() instead of pipe() so we have bi-directional fds */
|
||||
if (socketpair(AF_UNIX, SOCK_STREAM, 0, fd) != 0) {
|
||||
DEBUG(0,("Failed to create pipe for ctdb_freeze_lock\n"));
|
||||
talloc_free(h);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
h->child = fork();
|
||||
if (h->child == -1) {
|
||||
DEBUG(0,("Failed to fork child for ctdb_freeze_lock\n"));
|
||||
talloc_free(h);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (h->child == 0) {
|
||||
int ret;
|
||||
/* in the child */
|
||||
close(fd[0]);
|
||||
ret = ctdb_lock_all_databases(ctdb);
|
||||
if (ret != 0) {
|
||||
_exit(0);
|
||||
}
|
||||
write(fd[1], &ret, sizeof(ret));
|
||||
/* the read here means we will die if the parent exits */
|
||||
read(fd[1], &ret, sizeof(ret));
|
||||
_exit(0);
|
||||
}
|
||||
|
||||
talloc_set_destructor(h, ctdb_freeze_handle_destructor);
|
||||
|
||||
close(fd[1]);
|
||||
|
||||
h->fd = fd[0];
|
||||
|
||||
fde = event_add_fd(ctdb->ev, h, h->fd, EVENT_FD_READ|EVENT_FD_AUTOCLOSE,
|
||||
ctdb_freeze_lock_handler, h);
|
||||
if (fde == NULL) {
|
||||
DEBUG(0,("Failed to setup fd event for ctdb_freeze_lock\n"));
|
||||
close(fd[0]);
|
||||
talloc_free(h);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return h;
|
||||
}
|
||||
|
||||
/*
|
||||
destroy a waiter for a freeze mode change
|
||||
*/
|
||||
static int ctdb_freeze_waiter_destructor(struct ctdb_freeze_waiter *w)
|
||||
{
|
||||
DLIST_REMOVE(w->ctdb->freeze_handle->waiters, w);
|
||||
ctdb_request_control_reply(w->ctdb, w->c, NULL, w->status);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
freeze the databases
|
||||
*/
|
||||
int32_t ctdb_control_freeze(struct ctdb_context *ctdb, struct ctdb_req_control *c, bool *async_reply)
|
||||
{
|
||||
struct ctdb_freeze_waiter *w;
|
||||
|
||||
if (ctdb->freeze_mode == CTDB_FREEZE_FROZEN) {
|
||||
/* we're already frozen */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* if there isn't a freeze lock child then create one */
|
||||
if (!ctdb->freeze_handle) {
|
||||
ctdb->freeze_handle = ctdb_freeze_lock(ctdb);
|
||||
CTDB_NO_MEMORY(ctdb, ctdb->freeze_handle);
|
||||
ctdb->freeze_mode = CTDB_FREEZE_PENDING;
|
||||
}
|
||||
|
||||
/* add ourselves to list of waiters */
|
||||
w = talloc(ctdb->freeze_handle, struct ctdb_freeze_waiter);
|
||||
CTDB_NO_MEMORY(ctdb, w);
|
||||
w->ctdb = ctdb;
|
||||
w->c = talloc_steal(w, c);
|
||||
w->status = -1;
|
||||
talloc_set_destructor(w, ctdb_freeze_waiter_destructor);
|
||||
DLIST_ADD(ctdb->freeze_handle->waiters, w);
|
||||
|
||||
/* we won't reply till later */
|
||||
*async_reply = True;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
thaw the databases
|
||||
*/
|
||||
int32_t ctdb_control_thaw(struct ctdb_context *ctdb)
|
||||
{
|
||||
talloc_free(ctdb->freeze_handle);
|
||||
ctdb->freeze_handle = NULL;
|
||||
return 0;
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
ctdb_control protocol code
|
||||
ctdb recovery code
|
||||
|
||||
Copyright (C) Andrew Tridgell 2007
|
||||
Copyright (C) Ronnie Sahlberg 2007
|
||||
@ -368,231 +368,17 @@ int32_t ctdb_control_clear_db(struct ctdb_context *ctdb, TDB_DATA indata)
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
lock all databases
|
||||
*/
|
||||
static int ctdb_lock_all_databases(struct ctdb_context *ctdb)
|
||||
{
|
||||
struct ctdb_db_context *ctdb_db;
|
||||
for (ctdb_db=ctdb->db_list;ctdb_db;ctdb_db=ctdb_db->next) {
|
||||
if (tdb_lockall(ctdb_db->ltdb->tdb) != 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
lock all databases - mark only
|
||||
*/
|
||||
static int ctdb_lock_all_databases_mark(struct ctdb_context *ctdb)
|
||||
{
|
||||
struct ctdb_db_context *ctdb_db;
|
||||
for (ctdb_db=ctdb->db_list;ctdb_db;ctdb_db=ctdb_db->next) {
|
||||
if (tdb_lockall_mark(ctdb_db->ltdb->tdb) != 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
lock all databases - unmark only
|
||||
*/
|
||||
static int ctdb_lock_all_databases_unmark(struct ctdb_context *ctdb)
|
||||
{
|
||||
struct ctdb_db_context *ctdb_db;
|
||||
for (ctdb_db=ctdb->db_list;ctdb_db;ctdb_db=ctdb_db->next) {
|
||||
if (tdb_lockall_unmark(ctdb_db->ltdb->tdb) != 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
a list of control requests waiting for a recovery lock child to gets
|
||||
the database locks
|
||||
*/
|
||||
struct ctdb_recovery_waiter {
|
||||
struct ctdb_recovery_waiter *next, *prev;
|
||||
struct ctdb_context *ctdb;
|
||||
struct ctdb_req_control *c;
|
||||
int32_t status;
|
||||
};
|
||||
|
||||
/* a handle to a recovery lock child process */
|
||||
struct ctdb_recovery_handle {
|
||||
struct ctdb_context *ctdb;
|
||||
pid_t child;
|
||||
int fd;
|
||||
struct ctdb_recovery_waiter *waiters;
|
||||
};
|
||||
|
||||
/*
|
||||
destroy a recovery handle
|
||||
*/
|
||||
static int ctdb_recovery_handle_destructor(struct ctdb_recovery_handle *h)
|
||||
{
|
||||
if (h->ctdb->recovery_mode == CTDB_RECOVERY_ACTIVE) {
|
||||
ctdb_lock_all_databases_unmark(h->ctdb);
|
||||
}
|
||||
kill(h->child, SIGKILL);
|
||||
waitpid(h->child, NULL, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
called when the child writes its status to us
|
||||
*/
|
||||
static void ctdb_recovery_lock_handler(struct event_context *ev, struct fd_event *fde,
|
||||
uint16_t flags, void *private_data)
|
||||
{
|
||||
struct ctdb_recovery_handle *h = talloc_get_type(private_data, struct ctdb_recovery_handle);
|
||||
int32_t status;
|
||||
struct ctdb_recovery_waiter *w;
|
||||
int ret;
|
||||
|
||||
if (read(h->fd, &status, sizeof(status)) != sizeof(status)) {
|
||||
DEBUG(0,("read error from recovery lock child\n"));
|
||||
status = -1;
|
||||
}
|
||||
|
||||
if (status == -1) {
|
||||
DEBUG(0,("Failed to get locks in ctdb_recovery_child\n"));
|
||||
/* we didn't get the locks - destroy the handle */
|
||||
talloc_free(h);
|
||||
return;
|
||||
}
|
||||
|
||||
ret = ctdb_lock_all_databases_mark(h->ctdb);
|
||||
if (ret == -1) {
|
||||
DEBUG(0,("Failed to mark locks in ctdb_recovery\n"));
|
||||
talloc_free(h);
|
||||
return;
|
||||
}
|
||||
|
||||
h->ctdb->recovery_mode = CTDB_RECOVERY_ACTIVE;
|
||||
|
||||
/* notify the waiters */
|
||||
while ((w = h->ctdb->recovery_handle->waiters)) {
|
||||
w->status = status;
|
||||
DLIST_REMOVE(h->ctdb->recovery_handle->waiters, w);
|
||||
talloc_free(w);
|
||||
}
|
||||
|
||||
talloc_free(fde);
|
||||
}
|
||||
|
||||
/*
|
||||
create a child which gets locks on all the open databases, then calls the callback telling the parent
|
||||
that it is done
|
||||
*/
|
||||
static struct ctdb_recovery_handle *ctdb_recovery_lock(struct ctdb_context *ctdb)
|
||||
{
|
||||
struct ctdb_recovery_handle *h;
|
||||
int fd[2];
|
||||
struct fd_event *fde;
|
||||
|
||||
h = talloc_zero(ctdb, struct ctdb_recovery_handle);
|
||||
CTDB_NO_MEMORY_VOID(ctdb, h);
|
||||
|
||||
h->ctdb = ctdb;
|
||||
|
||||
/* use socketpair() instead of pipe() so we have bi-directional fds */
|
||||
if (socketpair(AF_UNIX, SOCK_STREAM, 0, fd) != 0) {
|
||||
DEBUG(0,("Failed to create pipe for ctdb_recovery_lock\n"));
|
||||
talloc_free(h);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
h->child = fork();
|
||||
if (h->child == -1) {
|
||||
DEBUG(0,("Failed to fork child for ctdb_recovery_lock\n"));
|
||||
talloc_free(h);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (h->child == 0) {
|
||||
int ret;
|
||||
/* in the child */
|
||||
close(fd[0]);
|
||||
ret = ctdb_lock_all_databases(ctdb);
|
||||
if (ret != 0) {
|
||||
_exit(0);
|
||||
}
|
||||
write(fd[1], &ret, sizeof(ret));
|
||||
/* the read here means we will die if the parent exits */
|
||||
read(fd[1], &ret, sizeof(ret));
|
||||
_exit(0);
|
||||
}
|
||||
|
||||
talloc_set_destructor(h, ctdb_recovery_handle_destructor);
|
||||
|
||||
close(fd[1]);
|
||||
|
||||
h->fd = fd[0];
|
||||
|
||||
fde = event_add_fd(ctdb->ev, h, h->fd, EVENT_FD_READ|EVENT_FD_AUTOCLOSE,
|
||||
ctdb_recovery_lock_handler, h);
|
||||
if (fde == NULL) {
|
||||
DEBUG(0,("Failed to setup fd event for ctdb_recovery_lock\n"));
|
||||
close(fd[0]);
|
||||
talloc_free(h);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return h;
|
||||
}
|
||||
|
||||
/*
|
||||
destroy a waiter for a recovery mode change
|
||||
*/
|
||||
static int ctdb_recovery_waiter_destructor(struct ctdb_recovery_waiter *w)
|
||||
{
|
||||
DLIST_REMOVE(w->ctdb->recovery_handle->waiters, w);
|
||||
ctdb_request_control_reply(w->ctdb, w->c, NULL, w->status);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
set the recovery mode
|
||||
*/
|
||||
void ctdb_control_set_recmode(struct ctdb_context *ctdb, struct ctdb_req_control *c, TDB_DATA data)
|
||||
int32_t ctdb_control_set_recmode(struct ctdb_context *ctdb, TDB_DATA indata)
|
||||
{
|
||||
uint32_t recmode = *(uint32_t *)data.dptr;
|
||||
struct ctdb_recovery_waiter *w;
|
||||
|
||||
if (recmode == CTDB_RECOVERY_NORMAL) {
|
||||
/* switching to normal mode is easy */
|
||||
talloc_free(ctdb->recovery_handle);
|
||||
ctdb->recovery_handle = NULL;
|
||||
ctdb->recovery_mode = CTDB_RECOVERY_NORMAL;
|
||||
ctdb_request_control_reply(ctdb, c, NULL, 0);
|
||||
return;
|
||||
uint32_t recmode = *(uint32_t *)indata.dptr;
|
||||
if (ctdb->freeze_mode != CTDB_FREEZE_FROZEN) {
|
||||
DEBUG(0,("Attempt to change recovery mode to %u when not frozen\n",
|
||||
recmode));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ctdb->recovery_mode == CTDB_RECOVERY_ACTIVE) {
|
||||
/* we're already active */
|
||||
ctdb_request_control_reply(ctdb, c, NULL, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
/* if there isn't a recovery lock child then create one */
|
||||
if (!ctdb->recovery_handle) {
|
||||
ctdb->recovery_handle = ctdb_recovery_lock(ctdb);
|
||||
CTDB_NO_MEMORY_VOID(ctdb, ctdb->recovery_handle);
|
||||
}
|
||||
|
||||
/* add ourselves to list of waiters */
|
||||
w = talloc(ctdb->recovery_handle, struct ctdb_recovery_waiter);
|
||||
CTDB_NO_MEMORY_VOID(ctdb, w);
|
||||
w->ctdb = ctdb;
|
||||
w->c = talloc_steal(w, c);
|
||||
w->status = -1;
|
||||
talloc_set_destructor(w, ctdb_recovery_waiter_destructor);
|
||||
DLIST_ADD(ctdb->recovery_handle->waiters, w);
|
||||
ctdb->recovery_mode = recmode;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
@ -57,11 +57,27 @@ static int set_recovery_mode(struct ctdb_context *ctdb, struct ctdb_node_map *no
|
||||
continue;
|
||||
}
|
||||
|
||||
if (rec_mode == CTDB_RECOVERY_ACTIVE) {
|
||||
ret = ctdb_ctrl_freeze(ctdb, timeval_current_ofs(5, 0), nodemap->nodes[j].vnn);
|
||||
if (ret != 0) {
|
||||
DEBUG(0, (__location__ " Unable to freeze node %u\n", nodemap->nodes[j].vnn));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
ret = ctdb_ctrl_setrecmode(ctdb, timeval_current_ofs(1, 0), nodemap->nodes[j].vnn, rec_mode);
|
||||
if (ret != 0) {
|
||||
DEBUG(0, (__location__ " Unable to set recmode on node %u\n", nodemap->nodes[j].vnn));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (rec_mode == CTDB_RECOVERY_NORMAL) {
|
||||
ret = ctdb_ctrl_thaw(ctdb, timeval_current_ofs(5, 0), nodemap->nodes[j].vnn);
|
||||
if (ret != 0) {
|
||||
DEBUG(0, (__location__ " Unable to thaw node %u\n", nodemap->nodes[j].vnn));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -323,4 +323,7 @@ int ctdb_dump_db(struct ctdb_db_context *ctdb_db, FILE *f);
|
||||
*/
|
||||
int ctdb_ctrl_getpid(struct ctdb_context *ctdb, struct timeval timeout, uint32_t destnode, uint32_t *pid);
|
||||
|
||||
int ctdb_ctrl_freeze(struct ctdb_context *ctdb, struct timeval timeout, uint32_t destnode);
|
||||
int ctdb_ctrl_thaw(struct ctdb_context *ctdb, struct timeval timeout, uint32_t destnode);
|
||||
|
||||
#endif
|
||||
|
@ -218,12 +218,14 @@ struct ctdb_write_record {
|
||||
unsigned char blob[1];
|
||||
};
|
||||
|
||||
enum ctdb_freeze_mode {CTDB_FREEZE_NONE, CTDB_FREEZE_PENDING, CTDB_FREEZE_FROZEN};
|
||||
|
||||
/* main state of the ctdb daemon */
|
||||
struct ctdb_context {
|
||||
struct event_context *ev;
|
||||
uint32_t recovery_mode;
|
||||
struct ctdb_recovery_handle *recovery_handle;
|
||||
enum ctdb_freeze_mode freeze_mode;
|
||||
struct ctdb_freeze_handle *freeze_handle;
|
||||
struct ctdb_address address;
|
||||
const char *name;
|
||||
const char *db_directory;
|
||||
@ -345,6 +347,8 @@ enum ctdb_controls {CTDB_CONTROL_PROCESS_EXISTS,
|
||||
CTDB_CONTROL_GET_PID,
|
||||
CTDB_CONTROL_GET_RECMASTER,
|
||||
CTDB_CONTROL_SET_RECMASTER,
|
||||
CTDB_CONTROL_FREEZE,
|
||||
CTDB_CONTROL_THAW,
|
||||
};
|
||||
|
||||
|
||||
@ -783,8 +787,11 @@ int32_t ctdb_control_push_db(struct ctdb_context *ctdb, TDB_DATA indata);
|
||||
int32_t ctdb_control_set_dmaster(struct ctdb_context *ctdb, TDB_DATA indata);
|
||||
int32_t ctdb_control_clear_db(struct ctdb_context *ctdb, TDB_DATA indata);
|
||||
|
||||
void ctdb_control_set_recmode(struct ctdb_context *ctdb, struct ctdb_req_control *c, TDB_DATA data);
|
||||
int32_t ctdb_control_set_recmode(struct ctdb_context *ctdb, TDB_DATA data);
|
||||
void ctdb_request_control_reply(struct ctdb_context *ctdb, struct ctdb_req_control *c,
|
||||
TDB_DATA *outdata, int32_t status);
|
||||
|
||||
int32_t ctdb_control_freeze(struct ctdb_context *ctdb, struct ctdb_req_control *c, bool *async_reply);
|
||||
int32_t ctdb_control_thaw(struct ctdb_context *ctdb);
|
||||
|
||||
#endif
|
||||
|
@ -56,6 +56,8 @@ static void usage(void)
|
||||
" setrecmaster <vnn> <master_vnn> set recovery master\n"
|
||||
" attach <dbname> attach a database\n"
|
||||
" getpid <vnn> get the pid of a ctdb daemon\n"
|
||||
" freeze <vnn|all> freeze a node\n"
|
||||
" thaw <vnn|all> thaw a node\n"
|
||||
);
|
||||
exit(1);
|
||||
}
|
||||
@ -799,6 +801,79 @@ static int control_debug(struct ctdb_context *ctdb, int argc, const char **argv)
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
freeze a node
|
||||
*/
|
||||
static int control_freeze(struct ctdb_context *ctdb, int argc, const char **argv)
|
||||
{
|
||||
int ret=0;
|
||||
uint32_t vnn, i;
|
||||
uint32_t *nodes;
|
||||
uint32_t num_nodes;
|
||||
|
||||
if (argc < 1) {
|
||||
usage();
|
||||
}
|
||||
|
||||
if (strcmp(argv[0], "all") != 0) {
|
||||
vnn = strtoul(argv[0], NULL, 0);
|
||||
ret = ctdb_ctrl_freeze(ctdb, timeval_current_ofs(5, 0), vnn);
|
||||
if (ret != 0) {
|
||||
printf("Unable to freeze node %u\n", vnn);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
nodes = ctdb_get_connected_nodes(ctdb, timeval_current_ofs(1, 0), ctdb, &num_nodes);
|
||||
CTDB_NO_MEMORY(ctdb, nodes);
|
||||
for (i=0;i<num_nodes;i++) {
|
||||
int res = ctdb_ctrl_freeze(ctdb, timeval_current_ofs(5, 0), nodes[i]);
|
||||
if (res != 0) {
|
||||
printf("Warning: Unable to freeze node %u\n", nodes[i]);
|
||||
}
|
||||
ret |= res;
|
||||
}
|
||||
talloc_free(nodes);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
thaw a node
|
||||
*/
|
||||
static int control_thaw(struct ctdb_context *ctdb, int argc, const char **argv)
|
||||
{
|
||||
int ret=0;
|
||||
uint32_t vnn, i;
|
||||
uint32_t *nodes;
|
||||
uint32_t num_nodes;
|
||||
|
||||
if (argc < 1) {
|
||||
usage();
|
||||
}
|
||||
|
||||
if (strcmp(argv[0], "all") != 0) {
|
||||
vnn = strtoul(argv[0], NULL, 0);
|
||||
ret = ctdb_ctrl_thaw(ctdb, timeval_current_ofs(5, 0), vnn);
|
||||
if (ret != 0) {
|
||||
printf("Unable to thaw node %u\n", vnn);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
nodes = ctdb_get_connected_nodes(ctdb, timeval_current_ofs(1, 0), ctdb, &num_nodes);
|
||||
CTDB_NO_MEMORY(ctdb, nodes);
|
||||
for (i=0;i<num_nodes;i++) {
|
||||
int res = ctdb_ctrl_thaw(ctdb, timeval_current_ofs(5, 0), nodes[i]);
|
||||
if (res != 0) {
|
||||
printf("Warning: Unable to thaw node %u\n", nodes[i]);
|
||||
}
|
||||
ret |= res;
|
||||
}
|
||||
talloc_free(nodes);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
attach to a database
|
||||
*/
|
||||
@ -886,6 +961,8 @@ int main(int argc, const char *argv[])
|
||||
{ "attach", control_attach },
|
||||
{ "dumpmemory", control_dumpmemory },
|
||||
{ "getpid", control_getpid },
|
||||
{ "freeze", control_freeze },
|
||||
{ "thaw", control_thaw },
|
||||
};
|
||||
|
||||
pc = poptGetContext(argv[0], argc, argv, popt_options, POPT_CONTEXT_KEEP_FIRST);
|
||||
|
Reference in New Issue
Block a user