fixed some incorrect CTDB_NO_MEMORY*() calls found after fixing the
[obnox/samba/samba-obnox.git] / ctdb / server / ctdb_freeze.c
1 /* 
2    ctdb freeze handling
3
4    Copyright (C) Andrew Tridgell  2007
5
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.
10    
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.
15    
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/>.
18 */
19 #include "includes.h"
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"
27 #include "db_wrap.h"
28
29
30 /*
31   lock all databases
32  */
33 static int ctdb_lock_all_databases(struct ctdb_context *ctdb)
34 {
35         struct ctdb_db_context *ctdb_db;
36         for (ctdb_db=ctdb->db_list;ctdb_db;ctdb_db=ctdb_db->next) {
37                 if (tdb_lockall(ctdb_db->ltdb->tdb) != 0) {
38                         return -1;
39                 }
40         }
41         return 0;
42 }
43
44 /*
45   a list of control requests waiting for a freeze lock child to get
46   the database locks
47  */
48 struct ctdb_freeze_waiter {
49         struct ctdb_freeze_waiter *next, *prev;
50         struct ctdb_context *ctdb;
51         struct ctdb_req_control *c;
52         int32_t status;
53 };
54
55 /* a handle to a freeze lock child process */
56 struct ctdb_freeze_handle {
57         struct ctdb_context *ctdb;
58         pid_t child;
59         int fd;
60         struct ctdb_freeze_waiter *waiters;
61         bool transaction_started;
62         uint32_t transaction_id;
63 };
64
65 /*
66   destroy a freeze handle
67  */     
68 static int ctdb_freeze_handle_destructor(struct ctdb_freeze_handle *h)
69 {
70         h->ctdb->freeze_mode = CTDB_FREEZE_NONE;
71         kill(h->child, SIGKILL);
72         waitpid(h->child, NULL, 0);
73         return 0;
74 }
75
76 /*
77   called when the child writes its status to us
78  */
79 static void ctdb_freeze_lock_handler(struct event_context *ev, struct fd_event *fde, 
80                                        uint16_t flags, void *private_data)
81 {
82         struct ctdb_freeze_handle *h = talloc_get_type(private_data, struct ctdb_freeze_handle);
83         int32_t status;
84         struct ctdb_freeze_waiter *w;
85
86         if (h->ctdb->freeze_mode == CTDB_FREEZE_FROZEN) {
87                 DEBUG(DEBUG_INFO,("freeze child died - unfreezing\n"));
88                 if (h->ctdb->freeze_handle == h) {
89                         h->ctdb->freeze_handle = NULL;
90                 }
91                 talloc_free(h);
92                 return;
93         }
94
95         if (read(h->fd, &status, sizeof(status)) != sizeof(status)) {
96                 DEBUG(DEBUG_ERR,("read error from freeze lock child\n"));
97                 status = -1;
98         }
99
100         if (status == -1) {
101                 DEBUG(DEBUG_ERR,("Failed to get locks in ctdb_freeze_child\n"));
102                 /* we didn't get the locks - destroy the handle */
103                 talloc_free(h);
104                 return;
105         }
106
107         h->ctdb->freeze_mode = CTDB_FREEZE_FROZEN;
108
109         /* notify the waiters */
110         while ((w = h->ctdb->freeze_handle->waiters)) {
111                 w->status = status;
112                 DLIST_REMOVE(h->ctdb->freeze_handle->waiters, w);
113                 talloc_free(w);
114         }
115 }
116
117 /*
118   create a child which gets locks on all the open databases, then calls the callback telling the parent
119   that it is done
120  */
121 static struct ctdb_freeze_handle *ctdb_freeze_lock(struct ctdb_context *ctdb)
122 {
123         struct ctdb_freeze_handle *h;
124         int fd[2];
125         struct fd_event *fde;
126
127         h = talloc_zero(ctdb, struct ctdb_freeze_handle);
128         CTDB_NO_MEMORY_NULL(ctdb, h);
129
130         h->ctdb = ctdb;
131
132         /* use socketpair() instead of pipe() so we have bi-directional fds */
133         if (socketpair(AF_UNIX, SOCK_STREAM, 0, fd) != 0) {
134                 DEBUG(DEBUG_ERR,("Failed to create pipe for ctdb_freeze_lock\n"));
135                 talloc_free(h);
136                 return NULL;
137         }
138         
139         h->child = fork();
140         if (h->child == -1) {
141                 DEBUG(DEBUG_ERR,("Failed to fork child for ctdb_freeze_lock\n"));
142                 talloc_free(h);
143                 return NULL;
144         }
145
146         if (h->child == 0) {
147                 int ret;
148                 int count = 0;
149                 /* in the child */
150                 close(fd[0]);
151                 ret = ctdb_lock_all_databases(ctdb);
152                 if (ret != 0) {
153                         _exit(0);
154                 }
155
156                 alarm(30);
157
158                 while (count++ < 30) {
159                         ret = write(fd[1], &ret, sizeof(ret));
160                         if (ret == sizeof(ret)) {
161                                 break;
162                         }
163                         DEBUG(DEBUG_ERR, (__location__ " Failed to write to socket from freeze child. ret:%d errno:%u\n", ret, errno));
164                         sleep (1);
165                 }
166                 if (count >= 30) {
167                         DEBUG(DEBUG_ERR, (__location__ " Failed to write to socket from freeze child. Aborting freeze child\n"));
168                         _exit(0);
169                 }
170
171                 /* the read here means we will die if the parent exits */
172                 read(fd[1], &ret, sizeof(ret));
173                 _exit(0);
174         }
175
176         talloc_set_destructor(h, ctdb_freeze_handle_destructor);
177
178         close(fd[1]);
179
180         h->fd = fd[0];
181
182         fde = event_add_fd(ctdb->ev, h, h->fd, EVENT_FD_READ|EVENT_FD_AUTOCLOSE, 
183                            ctdb_freeze_lock_handler, h);
184         if (fde == NULL) {
185                 DEBUG(DEBUG_ERR,("Failed to setup fd event for ctdb_freeze_lock\n"));
186                 close(fd[0]);
187                 talloc_free(h);
188                 return NULL;
189         }
190
191         return h;
192 }
193
194 /*
195   destroy a waiter for a freeze mode change
196  */
197 static int ctdb_freeze_waiter_destructor(struct ctdb_freeze_waiter *w)
198 {
199         DLIST_REMOVE(w->ctdb->freeze_handle->waiters, w);
200         ctdb_request_control_reply(w->ctdb, w->c, NULL, w->status, NULL);
201         return 0;
202 }
203
204 /*
205   start the freeze process
206  */
207 void ctdb_start_freeze(struct ctdb_context *ctdb)
208 {
209         if (ctdb->freeze_mode == CTDB_FREEZE_FROZEN) {
210                 /* we're already frozen */
211                 return;
212         }
213
214         /* if there isn't a freeze lock child then create one */
215         if (!ctdb->freeze_handle) {
216                 ctdb->freeze_handle = ctdb_freeze_lock(ctdb);
217                 CTDB_NO_MEMORY_VOID(ctdb, ctdb->freeze_handle);
218                 ctdb->freeze_mode = CTDB_FREEZE_PENDING;
219         }
220 }
221
222 /*
223   freeze the databases
224  */
225 int32_t ctdb_control_freeze(struct ctdb_context *ctdb, struct ctdb_req_control *c, bool *async_reply)
226 {
227         struct ctdb_freeze_waiter *w;
228
229         if (ctdb->freeze_mode == CTDB_FREEZE_FROZEN) {
230                 /* we're already frozen */
231                 return 0;
232         }
233
234         ctdb_start_freeze(ctdb);
235
236         /* add ourselves to list of waiters */
237         w = talloc(ctdb->freeze_handle, struct ctdb_freeze_waiter);
238         CTDB_NO_MEMORY(ctdb, w);
239         w->ctdb   = ctdb;
240         w->c      = talloc_steal(w, c);
241         w->status = -1;
242         talloc_set_destructor(w, ctdb_freeze_waiter_destructor);
243         DLIST_ADD(ctdb->freeze_handle->waiters, w);
244
245         /* we won't reply till later */
246         *async_reply = True;
247         return 0;
248 }
249
250
251 /*
252   block until we are frozen, used during daemon startup
253  */
254 bool ctdb_blocking_freeze(struct ctdb_context *ctdb)
255 {
256         ctdb_start_freeze(ctdb);
257
258         /* block until frozen */
259         while (ctdb->freeze_mode == CTDB_FREEZE_PENDING) {
260                 event_loop_once(ctdb->ev);
261         }
262
263         return ctdb->freeze_mode == CTDB_FREEZE_FROZEN;
264 }
265
266
267
268 /*
269   thaw the databases
270  */
271 int32_t ctdb_control_thaw(struct ctdb_context *ctdb)
272 {
273         /* cancel any pending transactions */
274         if (ctdb->freeze_handle && ctdb->freeze_handle->transaction_started) {
275                 struct ctdb_db_context *ctdb_db;
276
277                 for (ctdb_db=ctdb->db_list;ctdb_db;ctdb_db=ctdb_db->next) {
278                         tdb_add_flags(ctdb_db->ltdb->tdb, TDB_NOLOCK);
279                         if (tdb_transaction_cancel(ctdb_db->ltdb->tdb) != 0) {
280                                 DEBUG(DEBUG_ERR,(__location__ " Failed to cancel transaction for db '%s'\n",
281                                          ctdb_db->db_name));
282                         }
283                         tdb_remove_flags(ctdb_db->ltdb->tdb, TDB_NOLOCK);
284                 }
285         }
286
287 #if 0
288         /* this hack can be used to get a copy of the databases at the end of a recovery */
289         system("mkdir -p /var/ctdb.saved; /usr/bin/rsync --delete -a /var/ctdb/ /var/ctdb.saved/$$ 2>&1 > /dev/null");
290 #endif
291
292 #if 0
293         /* and this one for local testing */
294         system("mkdir -p test.db.saved; /usr/bin/rsync --delete -a test.db/ test.db.saved/$$ 2>&1 > /dev/null");
295 #endif
296
297
298         talloc_free(ctdb->freeze_handle);
299         ctdb->freeze_handle = NULL;
300         ctdb_call_resend_all(ctdb);
301         return 0;
302 }
303
304
305 /*
306   start a transaction on all databases - used for recovery
307  */
308 int32_t ctdb_control_transaction_start(struct ctdb_context *ctdb, uint32_t id)
309 {
310         struct ctdb_db_context *ctdb_db;
311
312         if (ctdb->freeze_mode != CTDB_FREEZE_FROZEN) {
313                 DEBUG(DEBUG_ERR,(__location__ " Failed transaction_start while not frozen\n"));
314                 return -1;
315         }
316
317
318         for (ctdb_db=ctdb->db_list;ctdb_db;ctdb_db=ctdb_db->next) {
319                 int ret;
320
321                 tdb_add_flags(ctdb_db->ltdb->tdb, TDB_NOLOCK);
322
323                 if (ctdb->freeze_handle->transaction_started) {
324                         if (tdb_transaction_cancel(ctdb_db->ltdb->tdb) != 0) {
325                                 DEBUG(DEBUG_ERR,(__location__ " Failed to cancel transaction for db '%s'\n",
326                                          ctdb_db->db_name));
327                                 /* not a fatal error */
328                         }
329                 }
330
331                 ret = tdb_transaction_start(ctdb_db->ltdb->tdb);
332
333                 tdb_remove_flags(ctdb_db->ltdb->tdb, TDB_NOLOCK);
334
335                 if (ret != 0) {
336                         DEBUG(DEBUG_ERR,(__location__ " Failed to start transaction for db '%s'\n",
337                                  ctdb_db->db_name));
338                         return -1;
339                 }
340         }
341
342         ctdb->freeze_handle->transaction_started = true;
343         ctdb->freeze_handle->transaction_id = id;
344
345         return 0;
346 }
347
348 /*
349   commit transactions on all databases
350  */
351 int32_t ctdb_control_transaction_commit(struct ctdb_context *ctdb, uint32_t id)
352 {
353         struct ctdb_db_context *ctdb_db;
354
355         if (ctdb->freeze_mode != CTDB_FREEZE_FROZEN) {
356                 DEBUG(DEBUG_ERR,(__location__ " Failed transaction_start while not frozen\n"));
357                 return -1;
358         }
359
360         if (!ctdb->freeze_handle->transaction_started) {
361                 DEBUG(DEBUG_ERR,(__location__ " transaction not started\n"));
362                 return -1;
363         }
364
365         if (id != ctdb->freeze_handle->transaction_id) {
366                 DEBUG(DEBUG_ERR,(__location__ " incorrect transaction id 0x%x in commit\n", id));
367                 return -1;
368         }
369
370         for (ctdb_db=ctdb->db_list;ctdb_db;ctdb_db=ctdb_db->next) {
371                 tdb_add_flags(ctdb_db->ltdb->tdb, TDB_NOLOCK);
372                 if (tdb_transaction_commit(ctdb_db->ltdb->tdb) != 0) {
373                         DEBUG(DEBUG_ERR,(__location__ " Failed to commit transaction for db '%s'\n",
374                                  ctdb_db->db_name));
375                         /* this has to be fatal to maintain integrity - it should only
376                            happen if we run out of disk space */
377                         ctdb_fatal(ctdb, "Unable to commit transactions\n");
378                         return -1;
379                 }
380                 tdb_remove_flags(ctdb_db->ltdb->tdb, TDB_NOLOCK);
381         }
382
383         ctdb->freeze_handle->transaction_started = false;
384         ctdb->freeze_handle->transaction_id = 0;
385
386         return 0;
387 }
388
389 /*
390   wipe a database - only possible when in a frozen transaction
391  */
392 int32_t ctdb_control_wipe_database(struct ctdb_context *ctdb, TDB_DATA indata)
393 {
394         struct ctdb_control_wipe_database w = *(struct ctdb_control_wipe_database *)indata.dptr;
395         struct ctdb_db_context *ctdb_db;
396
397         if (ctdb->freeze_mode != CTDB_FREEZE_FROZEN) {
398                 DEBUG(DEBUG_ERR,(__location__ " Failed transaction_start while not frozen\n"));
399                 return -1;
400         }
401
402         if (!ctdb->freeze_handle->transaction_started) {
403                 DEBUG(DEBUG_ERR,(__location__ " transaction not started\n"));
404                 return -1;
405         }
406
407         if (w.transaction_id != ctdb->freeze_handle->transaction_id) {
408                 DEBUG(DEBUG_ERR,(__location__ " incorrect transaction id 0x%x in commit\n", w.transaction_id));
409                 return -1;
410         }
411
412         ctdb_db = find_ctdb_db(ctdb, w.db_id);
413         if (!ctdb_db) {
414                 DEBUG(DEBUG_ERR,(__location__ " Unknown db 0x%x\n", w.db_id));
415                 return -1;
416         }
417
418         if (tdb_wipe_all(ctdb_db->ltdb->tdb) != 0) {
419                 DEBUG(DEBUG_ERR,(__location__ " Failed to wipe database for db '%s'\n",
420                          ctdb_db->db_name));
421                 return -1;
422         }
423
424         return 0;
425 }