4 Copyright (C) Andrew Tridgell 2007
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, see <http://www.gnu.org/licenses/>.
20 #include "lib/events/events.h"
21 #include "lib/tdb/include/tdb.h"
22 #include "system/network.h"
23 #include "system/filesys.h"
24 #include "system/wait.h"
25 #include "../include/ctdb_private.h"
26 #include "lib/util/dlinklist.h"
32 static int ctdb_lock_all_databases(struct ctdb_context *ctdb)
34 struct ctdb_db_context *ctdb_db;
35 for (ctdb_db=ctdb->db_list;ctdb_db;ctdb_db=ctdb_db->next) {
36 if (tdb_lockall(ctdb_db->ltdb->tdb) != 0) {
44 a list of control requests waiting for a freeze lock child to get
47 struct ctdb_freeze_waiter {
48 struct ctdb_freeze_waiter *next, *prev;
49 struct ctdb_context *ctdb;
50 struct ctdb_req_control *c;
54 /* a handle to a freeze lock child process */
55 struct ctdb_freeze_handle {
56 struct ctdb_context *ctdb;
59 struct ctdb_freeze_waiter *waiters;
63 destroy a freeze handle
65 static int ctdb_freeze_handle_destructor(struct ctdb_freeze_handle *h)
67 h->ctdb->freeze_mode = CTDB_FREEZE_NONE;
68 kill(h->child, SIGKILL);
69 waitpid(h->child, NULL, 0);
74 called when the child writes its status to us
76 static void ctdb_freeze_lock_handler(struct event_context *ev, struct fd_event *fde,
77 uint16_t flags, void *private_data)
79 struct ctdb_freeze_handle *h = talloc_get_type(private_data, struct ctdb_freeze_handle);
81 struct ctdb_freeze_waiter *w;
83 if (h->ctdb->freeze_mode == CTDB_FREEZE_FROZEN) {
84 DEBUG(0,("freeze child died - unfreezing\n"));
89 if (read(h->fd, &status, sizeof(status)) != sizeof(status)) {
90 DEBUG(0,("read error from freeze lock child\n"));
95 DEBUG(0,("Failed to get locks in ctdb_freeze_child\n"));
96 /* we didn't get the locks - destroy the handle */
101 h->ctdb->freeze_mode = CTDB_FREEZE_FROZEN;
103 /* notify the waiters */
104 while ((w = h->ctdb->freeze_handle->waiters)) {
106 DLIST_REMOVE(h->ctdb->freeze_handle->waiters, w);
112 create a child which gets locks on all the open databases, then calls the callback telling the parent
115 static struct ctdb_freeze_handle *ctdb_freeze_lock(struct ctdb_context *ctdb)
117 struct ctdb_freeze_handle *h;
119 struct fd_event *fde;
121 h = talloc_zero(ctdb, struct ctdb_freeze_handle);
122 CTDB_NO_MEMORY_VOID(ctdb, h);
126 /* use socketpair() instead of pipe() so we have bi-directional fds */
127 if (socketpair(AF_UNIX, SOCK_STREAM, 0, fd) != 0) {
128 DEBUG(0,("Failed to create pipe for ctdb_freeze_lock\n"));
134 if (h->child == -1) {
135 DEBUG(0,("Failed to fork child for ctdb_freeze_lock\n"));
144 ret = ctdb_lock_all_databases(ctdb);
148 write(fd[1], &ret, sizeof(ret));
149 /* the read here means we will die if the parent exits */
150 read(fd[1], &ret, sizeof(ret));
154 talloc_set_destructor(h, ctdb_freeze_handle_destructor);
160 fde = event_add_fd(ctdb->ev, h, h->fd, EVENT_FD_READ|EVENT_FD_AUTOCLOSE,
161 ctdb_freeze_lock_handler, h);
163 DEBUG(0,("Failed to setup fd event for ctdb_freeze_lock\n"));
173 destroy a waiter for a freeze mode change
175 static int ctdb_freeze_waiter_destructor(struct ctdb_freeze_waiter *w)
177 DLIST_REMOVE(w->ctdb->freeze_handle->waiters, w);
178 ctdb_request_control_reply(w->ctdb, w->c, NULL, w->status, NULL);
183 start the freeze process
185 void ctdb_start_freeze(struct ctdb_context *ctdb)
187 if (ctdb->freeze_mode == CTDB_FREEZE_FROZEN) {
188 /* we're already frozen */
192 /* if there isn't a freeze lock child then create one */
193 if (!ctdb->freeze_handle) {
194 ctdb->freeze_handle = ctdb_freeze_lock(ctdb);
195 CTDB_NO_MEMORY_VOID(ctdb, ctdb->freeze_handle);
196 ctdb->freeze_mode = CTDB_FREEZE_PENDING;
203 int32_t ctdb_control_freeze(struct ctdb_context *ctdb, struct ctdb_req_control *c, bool *async_reply)
205 struct ctdb_freeze_waiter *w;
207 if (ctdb->freeze_mode == CTDB_FREEZE_FROZEN) {
208 /* we're already frozen */
212 ctdb_start_freeze(ctdb);
214 /* add ourselves to list of waiters */
215 w = talloc(ctdb->freeze_handle, struct ctdb_freeze_waiter);
216 CTDB_NO_MEMORY(ctdb, w);
218 w->c = talloc_steal(w, c);
220 talloc_set_destructor(w, ctdb_freeze_waiter_destructor);
221 DLIST_ADD(ctdb->freeze_handle->waiters, w);
223 /* we won't reply till later */
230 block until we are frozen, used during daemon startup
232 bool ctdb_blocking_freeze(struct ctdb_context *ctdb)
234 ctdb_start_freeze(ctdb);
236 /* block until frozen */
237 while (ctdb->freeze_mode == CTDB_FREEZE_PENDING) {
238 event_loop_once(ctdb->ev);
241 return ctdb->freeze_mode == CTDB_FREEZE_FROZEN;
249 int32_t ctdb_control_thaw(struct ctdb_context *ctdb)
251 talloc_free(ctdb->freeze_handle);
252 ctdb->freeze_handle = NULL;
253 ctdb_call_resend_all(ctdb);