- send tcp info to all connected nodes, not just vnnmap nodes
[sahlberg/ctdb.git] / 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 2 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, write to the Free Software
18    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20 #include "includes.h"
21 #include "lib/events/events.h"
22 #include "lib/tdb/include/tdb.h"
23 #include "system/network.h"
24 #include "system/filesys.h"
25 #include "system/wait.h"
26 #include "../include/ctdb_private.h"
27 #include "lib/util/dlinklist.h"
28 #include "db_wrap.h"
29
30
31 /*
32   lock all databases
33  */
34 static int ctdb_lock_all_databases(struct ctdb_context *ctdb)
35 {
36         struct ctdb_db_context *ctdb_db;
37         for (ctdb_db=ctdb->db_list;ctdb_db;ctdb_db=ctdb_db->next) {
38                 if (tdb_lockall(ctdb_db->ltdb->tdb) != 0) {
39                         return -1;
40                 }
41         }
42         return 0;
43 }
44
45 /*
46   a list of control requests waiting for a freeze lock child to get
47   the database locks
48  */
49 struct ctdb_freeze_waiter {
50         struct ctdb_freeze_waiter *next, *prev;
51         struct ctdb_context *ctdb;
52         struct ctdb_req_control *c;
53         int32_t status;
54 };
55
56 /* a handle to a freeze lock child process */
57 struct ctdb_freeze_handle {
58         struct ctdb_context *ctdb;
59         pid_t child;
60         int fd;
61         struct ctdb_freeze_waiter *waiters;
62 };
63
64 /*
65   destroy a freeze handle
66  */     
67 static int ctdb_freeze_handle_destructor(struct ctdb_freeze_handle *h)
68 {
69         h->ctdb->freeze_mode = CTDB_FREEZE_NONE;
70         kill(h->child, SIGKILL);
71         waitpid(h->child, NULL, 0);
72         return 0;
73 }
74
75 /*
76   called when the child writes its status to us
77  */
78 static void ctdb_freeze_lock_handler(struct event_context *ev, struct fd_event *fde, 
79                                        uint16_t flags, void *private_data)
80 {
81         struct ctdb_freeze_handle *h = talloc_get_type(private_data, struct ctdb_freeze_handle);
82         int32_t status;
83         struct ctdb_freeze_waiter *w;
84
85         if (h->ctdb->freeze_mode == CTDB_FREEZE_FROZEN) {
86                 DEBUG(0,("freeze child died - unfreezing\n"));
87                 talloc_free(h);
88                 return;
89         }
90
91         if (read(h->fd, &status, sizeof(status)) != sizeof(status)) {
92                 DEBUG(0,("read error from freeze lock child\n"));
93                 status = -1;
94         }
95
96         if (status == -1) {
97                 DEBUG(0,("Failed to get locks in ctdb_freeze_child\n"));
98                 /* we didn't get the locks - destroy the handle */
99                 talloc_free(h);
100                 return;
101         }
102
103         h->ctdb->freeze_mode = CTDB_FREEZE_FROZEN;
104
105         /* notify the waiters */
106         while ((w = h->ctdb->freeze_handle->waiters)) {
107                 w->status = status;
108                 DLIST_REMOVE(h->ctdb->freeze_handle->waiters, w);
109                 talloc_free(w);
110         }
111 }
112
113 /*
114   create a child which gets locks on all the open databases, then calls the callback telling the parent
115   that it is done
116  */
117 static struct ctdb_freeze_handle *ctdb_freeze_lock(struct ctdb_context *ctdb)
118 {
119         struct ctdb_freeze_handle *h;
120         int fd[2];
121         struct fd_event *fde;
122
123         h = talloc_zero(ctdb, struct ctdb_freeze_handle);
124         CTDB_NO_MEMORY_VOID(ctdb, h);
125
126         h->ctdb = ctdb;
127
128         /* use socketpair() instead of pipe() so we have bi-directional fds */
129         if (socketpair(AF_UNIX, SOCK_STREAM, 0, fd) != 0) {
130                 DEBUG(0,("Failed to create pipe for ctdb_freeze_lock\n"));
131                 talloc_free(h);
132                 return NULL;
133         }
134         
135         h->child = fork();
136         if (h->child == -1) {
137                 DEBUG(0,("Failed to fork child for ctdb_freeze_lock\n"));
138                 talloc_free(h);
139                 return NULL;
140         }
141
142         if (h->child == 0) {
143                 int ret;
144                 /* in the child */
145                 close(fd[0]);
146                 ret = ctdb_lock_all_databases(ctdb);
147                 if (ret != 0) {
148                         _exit(0);
149                 }
150                 write(fd[1], &ret, sizeof(ret));
151                 /* the read here means we will die if the parent exits */
152                 read(fd[1], &ret, sizeof(ret));
153                 _exit(0);
154         }
155
156         talloc_set_destructor(h, ctdb_freeze_handle_destructor);
157
158         close(fd[1]);
159
160         h->fd = fd[0];
161
162         fde = event_add_fd(ctdb->ev, h, h->fd, EVENT_FD_READ|EVENT_FD_AUTOCLOSE, 
163                            ctdb_freeze_lock_handler, h);
164         if (fde == NULL) {
165                 DEBUG(0,("Failed to setup fd event for ctdb_freeze_lock\n"));
166                 close(fd[0]);
167                 talloc_free(h);
168                 return NULL;
169         }
170
171         return h;
172 }
173
174 /*
175   destroy a waiter for a freeze mode change
176  */
177 static int ctdb_freeze_waiter_destructor(struct ctdb_freeze_waiter *w)
178 {
179         DLIST_REMOVE(w->ctdb->freeze_handle->waiters, w);
180         ctdb_request_control_reply(w->ctdb, w->c, NULL, w->status, NULL);
181         return 0;
182 }
183
184 /*
185   start the freeze process
186  */
187 void ctdb_start_freeze(struct ctdb_context *ctdb)
188 {
189         if (ctdb->freeze_mode == CTDB_FREEZE_FROZEN) {
190                 /* we're already frozen */
191                 return;
192         }
193
194         /* if there isn't a freeze lock child then create one */
195         if (!ctdb->freeze_handle) {
196                 ctdb->freeze_handle = ctdb_freeze_lock(ctdb);
197                 CTDB_NO_MEMORY_VOID(ctdb, ctdb->freeze_handle);
198                 ctdb->freeze_mode = CTDB_FREEZE_PENDING;
199         }
200 }
201
202 /*
203   freeze the databases
204  */
205 int32_t ctdb_control_freeze(struct ctdb_context *ctdb, struct ctdb_req_control *c, bool *async_reply)
206 {
207         struct ctdb_freeze_waiter *w;
208
209         if (ctdb->freeze_mode == CTDB_FREEZE_FROZEN) {
210                 /* we're already frozen */
211                 return 0;
212         }
213
214         ctdb_start_freeze(ctdb);
215
216         /* add ourselves to list of waiters */
217         w = talloc(ctdb->freeze_handle, struct ctdb_freeze_waiter);
218         CTDB_NO_MEMORY(ctdb, w);
219         w->ctdb   = ctdb;
220         w->c      = talloc_steal(w, c);
221         w->status = -1;
222         talloc_set_destructor(w, ctdb_freeze_waiter_destructor);
223         DLIST_ADD(ctdb->freeze_handle->waiters, w);
224
225         /* we won't reply till later */
226         *async_reply = True;
227         return 0;
228 }
229
230
231 /*
232   block until we are frozen, used during daemon startup
233  */
234 bool ctdb_blocking_freeze(struct ctdb_context *ctdb)
235 {
236         ctdb_start_freeze(ctdb);
237
238         /* block until frozen */
239         while (ctdb->freeze_mode == CTDB_FREEZE_PENDING) {
240                 event_loop_once(ctdb->ev);
241         }
242
243         return ctdb->freeze_mode == CTDB_FREEZE_FROZEN;
244 }
245
246
247
248 /*
249   thaw the databases
250  */
251 int32_t ctdb_control_thaw(struct ctdb_context *ctdb)
252 {
253         talloc_free(ctdb->freeze_handle);
254         ctdb->freeze_handle = NULL;
255         ctdb_call_resend_all(ctdb);
256         return 0;
257 }