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