51f6d984e76b4fd36e3f7449747c5d78bf3d3838
[samba.git] / source4 / cluster / ctdb / ctdb_cluster.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    ctdb clustering hooks
5
6    Copyright (C) Andrew Tridgell 2006
7    
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 3 of the License, or
11    (at your option) any later version.
12    
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17    
18    You should have received a copy of the GNU General Public License
19    along with this program.  If not, see <http://www.gnu.org/licenses/>.
20 */
21
22 #include "includes.h"
23 #include "lib/events/events.h"
24 #include "cluster/cluster.h"
25 #include "system/filesys.h"
26 #include "cluster/cluster_private.h"
27 #include "../tdb/include/tdb.h"
28 #include "include/ctdb.h"
29 #include "tdb_wrap.h"
30 #include "../lib/util/dlinklist.h"
31 #include "param/param.h"
32 #include "librpc/gen_ndr/security.h"
33
34 /* a linked list of messaging handlers, allowing incoming messages
35    to be directed to the right messaging context */
36 struct cluster_messaging_list {
37         struct cluster_messaging_list *next, *prev;
38         struct cluster_state *state;
39         struct messaging_context *msg;
40         struct server_id server;
41         cluster_message_fn_t handler;
42 };
43
44 struct cluster_state {
45         struct ctdb_context *ctdb;
46         struct cluster_messaging_list *list;
47         uint32_t vnn;
48 };
49
50
51
52 /*
53   return a server_id for a ctdb node
54 */
55 static struct server_id ctdb_id(struct cluster_ops *ops, uint64_t id, uint32_t id2)
56 {
57         struct cluster_state *state = (struct cluster_state *)ops->private;
58         struct ctdb_context *ctdb = state->ctdb;
59         struct server_id server_id;
60         server_id.node = ctdb_get_vnn(ctdb);
61         server_id.id = id;
62         server_id.id2 = id2;
63         return server_id;
64 }
65
66
67 /*
68   return a server_id as a string
69 */
70 static const char *ctdb_id_string(struct cluster_ops *ops, 
71                                   TALLOC_CTX *mem_ctx, struct server_id id)
72 {
73         return talloc_asprintf(mem_ctx, "%u.%llu.%u", id.node, (unsigned long long)id.id, id.id2);
74 }
75
76 /*
77   this is an interim method for subsystems that have not yet been
78   converted to use the ctdb api. It opens a shared database in the
79   cluster temporary area, using TDB_CLEAR_IF_FIRST which relies on
80   correct operation of fcntl locks on the shared fileystem.
81 */
82 static struct tdb_wrap *ctdb_tdb_tmp_open(struct cluster_ops *ops,
83                                           TALLOC_CTX *mem_ctx, 
84                                           struct loadparm_context *lp_ctx,
85                                           const char *dbname, int flags)
86 {
87         const char *dir = lp_parm_string(lp_ctx, NULL, "ctdb", "shared data");
88         char *path;
89         struct tdb_wrap *w;
90         if (dir == NULL) {
91                 DEBUG(0,("ERROR: You must set 'ctdb:shared data' to a cluster shared path\n"));
92                 return NULL;
93         }
94         path = talloc_asprintf(mem_ctx, "%s/%s", dir, dbname);
95         w = tdb_wrap_open(mem_ctx, path, 0,  
96                           flags | TDB_CLEAR_IF_FIRST,
97                           O_RDWR|O_CREAT, 0600);
98         talloc_free(path);
99         return w;
100 }
101
102 /*
103   get at the ctdb handle
104 */
105 static void *ctdb_backend_handle(struct cluster_ops *ops)
106 {
107         struct cluster_state *state = (struct cluster_state *)ops->private;
108         return (void *)state->ctdb;
109 }
110
111 struct ctdb_handler_state {
112         struct cluster_state *state;
113         cluster_message_fn_t handler;
114         struct messaging_context *msg;
115 };
116
117 /*
118   dispatch incoming ctdb messages
119 */
120 static void ctdb_message_handler(struct ctdb_context *ctdb, uint64_t srvid, 
121                                  TDB_DATA data, void *private)
122 {
123         struct ctdb_handler_state *s = talloc_get_type(private, 
124                                                        struct ctdb_handler_state);
125         DATA_BLOB blob;
126         blob.data = data.dptr;
127         blob.length = data.dsize;
128         s->handler(s->msg, blob);
129 }
130
131 static int ctdb_handler_destructor(struct ctdb_handler_state *s)
132 {
133         /* XXX - tell ctdb to de-register the message handler */
134         return 0;
135 }
136
137 /*
138   setup a handler for ctdb messages
139 */
140 static NTSTATUS ctdb_message_init(struct cluster_ops *ops,
141                                   struct messaging_context *msg, 
142                                   struct server_id server,
143                                   cluster_message_fn_t handler)
144 {
145         struct cluster_state *state = (struct cluster_state *)ops->private;
146         struct ctdb_handler_state *h;
147         int ret;
148
149         h = talloc(msg, struct ctdb_handler_state);
150         NT_STATUS_HAVE_NO_MEMORY(h);
151
152         h->state = state;
153         h->handler = handler;
154         h->msg = msg;
155
156         talloc_set_destructor(h, ctdb_handler_destructor);
157
158         /* setup a message handler */
159         ret = ctdb_set_message_handler(state->ctdb, server.id, 
160                                        ctdb_message_handler, h);
161         if (ret == -1) {
162                 DEBUG(0,("ctdb_set_message_handler failed - %s\n", 
163                          ctdb_errstr(state->ctdb)));
164                 exit(1);
165         }
166
167         return NT_STATUS_OK;
168 }
169
170 /*
171   send a ctdb message to another node
172 */
173 static NTSTATUS ctdb_message_send(struct cluster_ops *ops,
174                                   struct server_id server, DATA_BLOB *data)
175 {
176         struct cluster_state *state = (struct cluster_state *)ops->private;
177         struct ctdb_context *ctdb = state->ctdb;
178         TDB_DATA tdata;
179         int ret;
180
181         tdata.dptr = data->data;
182         tdata.dsize = data->length;
183
184         ret = ctdb_send_message(ctdb, server.node, server.id, tdata);
185         if (ret != 0) {
186                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
187         }
188         return NT_STATUS_OK;
189 }
190
191 static struct cluster_ops cluster_ctdb_ops = {
192         .cluster_id           = ctdb_id,
193         .cluster_id_string    = ctdb_id_string,
194         .cluster_tdb_tmp_open = ctdb_tdb_tmp_open,
195         .backend_handle       = ctdb_backend_handle,
196         .message_init         = ctdb_message_init,
197         .message_send         = ctdb_message_send,
198         .private           = NULL
199 };
200
201 /* initialise ctdb */
202 void cluster_ctdb_init(struct loadparm_context *lp_ctx, 
203                        struct event_context *ev, const char *model)
204 {
205         struct cluster_state *state;
206         int ret;
207
208         if (!lp_parm_bool(lp_ctx, NULL, "ctdb", "enable", false)) {
209                 return;
210         }
211
212         state = talloc(ev, struct cluster_state);
213         if (state == NULL) goto failed;
214
215         state->ctdb = ctdb_init(ev);
216         if (state->ctdb == NULL) goto failed;
217
218         ret = ctdb_socket_connect(state->ctdb);
219         if (ret == -1) {
220                 DEBUG(0,(__location__ " Failed to connect to ctdb socket\n"));
221                 goto failed;
222         }
223
224         /* get our vnn */
225         state->vnn = ctdb_ctrl_getvnn(state->ctdb, timeval_zero(), CTDB_CURRENT_NODE);
226         if (state->vnn == (uint32_t)-1) {
227                 DEBUG(0,(__location__ " Failed to get ctdb vnn\n"));
228                 goto failed;
229         }
230
231         state->list = NULL;
232
233         cluster_ctdb_ops.private = state;
234
235         cluster_set_ops(&cluster_ctdb_ops);
236
237 #if 0
238         /* nasty hack for now ... */
239         {
240                 void brl_ctdb_init_ops(void);
241                 brl_ctdb_init_ops();
242         }
243 #endif
244
245         return;
246         
247 failed:
248         DEBUG(0,("cluster_ctdb_init failed\n"));
249         talloc_free(state);
250 }