s3-ctdb: Make use of CTDB_CONTROL_CHECK_SRVIDS
[samba.git] / source3 / lib / serverid.c
1 /*
2    Unix SMB/CIFS implementation.
3    Implementation of a reliable server_exists()
4    Copyright (C) Volker Lendecke 2010
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
20 #include "includes.h"
21 #include "system/filesys.h"
22 #include "serverid.h"
23 #include "util_tdb.h"
24 #include "dbwrap/dbwrap.h"
25 #include "dbwrap/dbwrap_open.h"
26 #include "lib/util/tdb_wrap.h"
27 #include "lib/param/param.h"
28 #include "ctdbd_conn.h"
29 #include "messages.h"
30
31 struct serverid_key {
32         pid_t pid;
33         uint32_t task_id;
34         uint32_t vnn;
35 };
36
37 struct serverid_data {
38         uint64_t unique_id;
39         uint32_t msg_flags;
40 };
41
42 bool serverid_parent_init(TALLOC_CTX *mem_ctx)
43 {
44         struct tdb_wrap *db;
45         struct loadparm_context *lp_ctx;
46
47         lp_ctx = loadparm_init_s3(mem_ctx, loadparm_s3_context());
48         if (lp_ctx == NULL) {
49                 DEBUG(0, ("loadparm_init_s3 failed\n"));
50                 return false;
51         }
52
53         /*
54          * Open the tdb in the parent process (smbd) so that our
55          * CLEAR_IF_FIRST optimization in tdb_reopen_all can properly
56          * work.
57          */
58
59         db = tdb_wrap_open(mem_ctx, lock_path("serverid.tdb"),
60                            0, TDB_DEFAULT|TDB_CLEAR_IF_FIRST|TDB_INCOMPATIBLE_HASH, O_RDWR|O_CREAT,
61                            0644, lp_ctx);
62         talloc_unlink(mem_ctx, lp_ctx);
63         if (db == NULL) {
64                 DEBUG(1, ("could not open serverid.tdb: %s\n",
65                           strerror(errno)));
66                 return false;
67         }
68         return true;
69 }
70
71 static struct db_context *serverid_db(void)
72 {
73         static struct db_context *db;
74
75         if (db != NULL) {
76                 return db;
77         }
78         db = db_open(NULL, lock_path("serverid.tdb"), 0,
79                      TDB_DEFAULT|TDB_CLEAR_IF_FIRST|TDB_INCOMPATIBLE_HASH, O_RDWR|O_CREAT, 0644);
80         return db;
81 }
82
83 static void serverid_fill_key(const struct server_id *id,
84                               struct serverid_key *key)
85 {
86         ZERO_STRUCTP(key);
87         key->pid = id->pid;
88         key->task_id = id->task_id;
89         key->vnn = id->vnn;
90 }
91
92 bool serverid_register(const struct server_id id, uint32_t msg_flags)
93 {
94         struct db_context *db;
95         struct serverid_key key;
96         struct serverid_data data;
97         struct db_record *rec;
98         TDB_DATA tdbkey, tdbdata;
99         NTSTATUS status;
100         bool ret = false;
101
102         db = serverid_db();
103         if (db == NULL) {
104                 return false;
105         }
106
107         serverid_fill_key(&id, &key);
108         tdbkey = make_tdb_data((uint8_t *)&key, sizeof(key));
109
110         rec = dbwrap_fetch_locked(db, talloc_tos(), tdbkey);
111         if (rec == NULL) {
112                 DEBUG(1, ("Could not fetch_lock serverid.tdb record\n"));
113                 return false;
114         }
115
116         ZERO_STRUCT(data);
117         data.unique_id = id.unique_id;
118         data.msg_flags = msg_flags;
119
120         tdbdata = make_tdb_data((uint8_t *)&data, sizeof(data));
121         status = dbwrap_record_store(rec, tdbdata, 0);
122         if (!NT_STATUS_IS_OK(status)) {
123                 DEBUG(1, ("Storing serverid.tdb record failed: %s\n",
124                           nt_errstr(status)));
125                 goto done;
126         }
127 #ifdef HAVE_CTDB_CONTROL_CHECK_SRVIDS_DECL
128         if (lp_clustering()) {
129                 register_with_ctdbd(messaging_ctdbd_connection(), id.unique_id);
130         }
131 #endif
132         ret = true;
133 done:
134         TALLOC_FREE(rec);
135         return ret;
136 }
137
138 bool serverid_register_msg_flags(const struct server_id id, bool do_reg,
139                                  uint32_t msg_flags)
140 {
141         struct db_context *db;
142         struct serverid_key key;
143         struct serverid_data *data;
144         struct db_record *rec;
145         TDB_DATA tdbkey;
146         TDB_DATA value;
147         NTSTATUS status;
148         bool ret = false;
149
150         db = serverid_db();
151         if (db == NULL) {
152                 return false;
153         }
154
155         serverid_fill_key(&id, &key);
156         tdbkey = make_tdb_data((uint8_t *)&key, sizeof(key));
157
158         rec = dbwrap_fetch_locked(db, talloc_tos(), tdbkey);
159         if (rec == NULL) {
160                 DEBUG(1, ("Could not fetch_lock serverid.tdb record\n"));
161                 return false;
162         }
163
164         value = dbwrap_record_get_value(rec);
165
166         if (value.dsize != sizeof(struct serverid_data)) {
167                 DEBUG(1, ("serverid record has unexpected size %d "
168                           "(wanted %d)\n", (int)value.dsize,
169                           (int)sizeof(struct serverid_data)));
170                 goto done;
171         }
172
173         data = (struct serverid_data *)value.dptr;
174
175         if (do_reg) {
176                 data->msg_flags |= msg_flags;
177         } else {
178                 data->msg_flags &= ~msg_flags;
179         }
180
181         status = dbwrap_record_store(rec, value, 0);
182         if (!NT_STATUS_IS_OK(status)) {
183                 DEBUG(1, ("Storing serverid.tdb record failed: %s\n",
184                           nt_errstr(status)));
185                 goto done;
186         }
187         ret = true;
188 done:
189         TALLOC_FREE(rec);
190         return ret;
191 }
192
193 bool serverid_deregister(struct server_id id)
194 {
195         struct db_context *db;
196         struct serverid_key key;
197         struct db_record *rec;
198         TDB_DATA tdbkey;
199         NTSTATUS status;
200         bool ret = false;
201
202         db = serverid_db();
203         if (db == NULL) {
204                 return false;
205         }
206
207         serverid_fill_key(&id, &key);
208         tdbkey = make_tdb_data((uint8_t *)&key, sizeof(key));
209
210         rec = dbwrap_fetch_locked(db, talloc_tos(), tdbkey);
211         if (rec == NULL) {
212                 DEBUG(1, ("Could not fetch_lock serverid.tdb record\n"));
213                 return false;
214         }
215
216         status = dbwrap_record_delete(rec);
217         if (!NT_STATUS_IS_OK(status)) {
218                 DEBUG(1, ("Deleting serverid.tdb record failed: %s\n",
219                           nt_errstr(status)));
220                 goto done;
221         }
222         ret = true;
223 done:
224         TALLOC_FREE(rec);
225         return ret;
226 }
227
228 struct serverid_exists_state {
229         const struct server_id *id;
230         bool exists;
231 };
232
233 static int server_exists_parse(TDB_DATA key, TDB_DATA data, void *priv)
234 {
235         struct serverid_exists_state *state =
236                 (struct serverid_exists_state *)priv;
237
238         if (data.dsize != sizeof(struct serverid_data)) {
239                 return -1;
240         }
241
242         /*
243          * Use memcmp, not direct compare. data.dptr might not be
244          * aligned.
245          */
246         state->exists = (memcmp(&state->id->unique_id, data.dptr,
247                                 sizeof(state->id->unique_id)) == 0);
248         return 0;
249 }
250
251 bool serverid_exists(const struct server_id *id)
252 {
253         struct db_context *db;
254         struct serverid_exists_state state;
255         struct serverid_key key;
256         TDB_DATA tdbkey;
257
258         if (procid_is_me(id)) {
259                 return true;
260         }
261
262         if (!process_exists(*id)) {
263                 return false;
264         }
265
266         db = serverid_db();
267         if (db == NULL) {
268                 return false;
269         }
270
271         serverid_fill_key(id, &key);
272         tdbkey = make_tdb_data((uint8_t *)&key, sizeof(key));
273
274         state.id = id;
275         state.exists = false;
276
277         if (dbwrap_parse_record(db, tdbkey, server_exists_parse, &state) != 0) {
278                 return false;
279         }
280         return state.exists;
281 }
282
283 bool serverids_exist(const struct server_id *ids, int num_ids, bool *results)
284 {
285         struct db_context *db;
286         int i;
287
288 #ifdef HAVE_CTDB_CONTROL_CHECK_SRVIDS_DECL
289         if (lp_clustering()) {
290                 return ctdb_serverids_exist(messaging_ctdbd_connection(),
291                                             ids, num_ids, results);
292         }
293 #endif
294         if (!processes_exist(ids, num_ids, results)) {
295                 return false;
296         }
297
298         db = serverid_db();
299         if (db == NULL) {
300                 return false;
301         }
302
303         for (i=0; i<num_ids; i++) {
304                 struct serverid_exists_state state;
305                 struct serverid_key key;
306                 TDB_DATA tdbkey;
307
308                 if (!results[i]) {
309                         continue;
310                 }
311
312                 serverid_fill_key(&ids[i], &key);
313                 tdbkey = make_tdb_data((uint8_t *)&key, sizeof(key));
314
315                 state.id = &ids[i];
316                 state.exists = false;
317                 dbwrap_parse_record(db, tdbkey, server_exists_parse, &state);
318                 results[i] = state.exists;
319         }
320         return true;
321 }
322
323 static bool serverid_rec_parse(const struct db_record *rec,
324                                struct server_id *id, uint32_t *msg_flags)
325 {
326         struct serverid_key key;
327         struct serverid_data data;
328         TDB_DATA tdbkey;
329         TDB_DATA tdbdata;
330
331         tdbkey = dbwrap_record_get_key(rec);
332         tdbdata = dbwrap_record_get_value(rec);
333
334         if (tdbkey.dsize != sizeof(key)) {
335                 DEBUG(1, ("Found invalid key length %d in serverid.tdb\n",
336                           (int)tdbkey.dsize));
337                 return false;
338         }
339         if (tdbdata.dsize != sizeof(data)) {
340                 DEBUG(1, ("Found invalid value length %d in serverid.tdb\n",
341                           (int)tdbdata.dsize));
342                 return false;
343         }
344
345         memcpy(&key, tdbkey.dptr, sizeof(key));
346         memcpy(&data, tdbdata.dptr, sizeof(data));
347
348         id->pid = key.pid;
349         id->task_id = key.task_id;
350         id->vnn = key.vnn;
351         id->unique_id = data.unique_id;
352         *msg_flags = data.msg_flags;
353         return true;
354 }
355
356 struct serverid_traverse_read_state {
357         int (*fn)(const struct server_id *id, uint32_t msg_flags,
358                   void *private_data);
359         void *private_data;
360 };
361
362 static int serverid_traverse_read_fn(struct db_record *rec, void *private_data)
363 {
364         struct serverid_traverse_read_state *state =
365                 (struct serverid_traverse_read_state *)private_data;
366         struct server_id id;
367         uint32_t msg_flags;
368
369         if (!serverid_rec_parse(rec, &id, &msg_flags)) {
370                 return 0;
371         }
372         return state->fn(&id, msg_flags,state->private_data);
373 }
374
375 bool serverid_traverse_read(int (*fn)(const struct server_id *id,
376                                       uint32_t msg_flags, void *private_data),
377                             void *private_data)
378 {
379         struct db_context *db;
380         struct serverid_traverse_read_state state;
381         NTSTATUS status;
382
383         db = serverid_db();
384         if (db == NULL) {
385                 return false;
386         }
387         state.fn = fn;
388         state.private_data = private_data;
389
390         status = dbwrap_traverse_read(db, serverid_traverse_read_fn, &state,
391                                       NULL);
392         return NT_STATUS_IS_OK(status);
393 }
394
395 struct serverid_traverse_state {
396         int (*fn)(struct db_record *rec, const struct server_id *id,
397                   uint32_t msg_flags, void *private_data);
398         void *private_data;
399 };
400
401 static int serverid_traverse_fn(struct db_record *rec, void *private_data)
402 {
403         struct serverid_traverse_state *state =
404                 (struct serverid_traverse_state *)private_data;
405         struct server_id id;
406         uint32_t msg_flags;
407
408         if (!serverid_rec_parse(rec, &id, &msg_flags)) {
409                 return 0;
410         }
411         return state->fn(rec, &id, msg_flags, state->private_data);
412 }
413
414 bool serverid_traverse(int (*fn)(struct db_record *rec,
415                                  const struct server_id *id,
416                                  uint32_t msg_flags, void *private_data),
417                             void *private_data)
418 {
419         struct db_context *db;
420         struct serverid_traverse_state state;
421         NTSTATUS status;
422
423         db = serverid_db();
424         if (db == NULL) {
425                 return false;
426         }
427         state.fn = fn;
428         state.private_data = private_data;
429
430         status = dbwrap_traverse(db, serverid_traverse_fn, &state, NULL);
431         return NT_STATUS_IS_OK(status);
432 }