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
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;
}
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;
+}
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));
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;
}
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;
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);
+ }
}
/*
--- /dev/null
+/*
+ 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;
+}
/*
- ctdb_control protocol code
+ ctdb recovery code
Copyright (C) Andrew Tridgell 2007
Copyright (C) Ronnie Sahlberg 2007
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;
- }
-
- 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);
+ 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;
}
-
- /* 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;
}
-
-
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;
*/
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
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;
CTDB_CONTROL_GET_PID,
CTDB_CONTROL_GET_RECMASTER,
CTDB_CONTROL_SET_RECMASTER,
+ CTDB_CONTROL_FREEZE,
+ CTDB_CONTROL_THAW,
};
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
" 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);
}
}
+/*
+ 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
*/
{ "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);