dbwrap: Make dbwrap_db_id return size_t
[Samba.git] / source3 / lib / dbwrap / dbwrap_watch.c
blob11ade1948af639b6573f5a353de6db02f7bb33d4
1 /*
2 Unix SMB/CIFS implementation.
3 Watch dbwrap record changes
4 Copyright (C) Volker Lendecke 2012
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.
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.
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/>.
20 #include "includes.h"
21 #include "system/filesys.h"
22 #include "dbwrap/dbwrap.h"
23 #include "dbwrap_watch.h"
24 #include "dbwrap_open.h"
25 #include "lib/util/util_tdb.h"
26 #include "lib/util/tevent_ntstatus.h"
28 static struct db_context *dbwrap_record_watchers_db(void)
30 static struct db_context *watchers_db;
32 if (watchers_db == NULL) {
33 char *db_path = lock_path("dbwrap_watchers.tdb");
34 if (db_path == NULL) {
35 return NULL;
38 watchers_db = db_open(
39 NULL, db_path, 0,
40 TDB_CLEAR_IF_FIRST | TDB_INCOMPATIBLE_HASH,
41 O_RDWR|O_CREAT, 0600, DBWRAP_LOCK_ORDER_3,
42 DBWRAP_FLAG_NONE);
43 TALLOC_FREE(db_path);
45 return watchers_db;
48 static TDB_DATA dbwrap_record_watchers_key(TALLOC_CTX *mem_ctx,
49 struct db_context *db,
50 struct db_record *rec,
51 TDB_DATA *rec_key)
53 size_t db_id_len = dbwrap_db_id(db, NULL, 0);
54 uint8_t db_id[db_id_len];
55 TDB_DATA key, wkey;
57 dbwrap_db_id(db, db_id, db_id_len);
59 key = dbwrap_record_get_key(rec);
61 wkey.dsize = sizeof(uint32_t) + db_id_len + key.dsize;
62 wkey.dptr = talloc_array(mem_ctx, uint8_t, wkey.dsize);
63 if (wkey.dptr == NULL) {
64 return make_tdb_data(NULL, 0);
66 SIVAL(wkey.dptr, 0, db_id_len);
67 memcpy(wkey.dptr + sizeof(uint32_t), db_id, db_id_len);
68 memcpy(wkey.dptr + sizeof(uint32_t) + db_id_len, key.dptr, key.dsize);
70 if (rec_key != NULL) {
71 rec_key->dptr = wkey.dptr + sizeof(uint32_t) + db_id_len;
72 rec_key->dsize = key.dsize;
75 return wkey;
78 static bool dbwrap_record_watchers_key_parse(
79 TDB_DATA wkey, uint8_t **p_db_id, size_t *p_db_id_len, TDB_DATA *key)
81 size_t db_id_len;
83 if (wkey.dsize < sizeof(uint32_t)) {
84 DEBUG(1, ("Invalid watchers key\n"));
85 return false;
87 db_id_len = IVAL(wkey.dptr, 0);
88 if (db_id_len > (wkey.dsize - sizeof(uint32_t))) {
89 DEBUG(1, ("Invalid watchers key, wkey.dsize=%d, "
90 "db_id_len=%d\n", (int)wkey.dsize, (int)db_id_len));
91 return false;
93 *p_db_id = wkey.dptr + sizeof(uint32_t);
94 *p_db_id_len = db_id_len;
95 key->dptr = wkey.dptr + sizeof(uint32_t) + db_id_len;
96 key->dsize = wkey.dsize - sizeof(uint32_t) - db_id_len;
97 return true;
100 static NTSTATUS dbwrap_record_add_watcher(TDB_DATA w_key, struct server_id id)
102 struct TALLOC_CTX *frame = talloc_stackframe();
103 struct db_context *db;
104 struct db_record *rec;
105 TDB_DATA value;
106 struct server_id *ids;
107 size_t num_ids;
108 NTSTATUS status;
110 db = dbwrap_record_watchers_db();
111 if (db == NULL) {
112 status = map_nt_error_from_unix(errno);
113 goto fail;
115 rec = dbwrap_fetch_locked(db, talloc_tos(), w_key);
116 if (rec == NULL) {
117 status = map_nt_error_from_unix(errno);
118 goto fail;
120 value = dbwrap_record_get_value(rec);
122 if ((value.dsize % sizeof(struct server_id)) != 0) {
123 status = NT_STATUS_INTERNAL_DB_CORRUPTION;
124 goto fail;
127 ids = (struct server_id *)value.dptr;
128 num_ids = value.dsize / sizeof(struct server_id);
130 ids = talloc_array(talloc_tos(), struct server_id,
131 num_ids + 1);
132 if (ids == NULL) {
133 status = NT_STATUS_NO_MEMORY;
134 goto fail;
136 memcpy(ids, value.dptr, value.dsize);
137 ids[num_ids] = id;
138 num_ids += 1;
140 status = dbwrap_record_store(
141 rec, make_tdb_data((uint8_t *)ids, talloc_get_size(ids)), 0);
142 fail:
143 TALLOC_FREE(frame);
144 return status;
147 static NTSTATUS dbwrap_record_del_watcher(TDB_DATA w_key, struct server_id id)
149 struct TALLOC_CTX *frame = talloc_stackframe();
150 struct db_context *db;
151 struct db_record *rec;
152 struct server_id *ids;
153 size_t i, num_ids;
154 TDB_DATA value;
155 NTSTATUS status;
157 db = dbwrap_record_watchers_db();
158 if (db == NULL) {
159 status = map_nt_error_from_unix(errno);
160 goto fail;
162 rec = dbwrap_fetch_locked(db, talloc_tos(), w_key);
163 if (rec == NULL) {
164 status = map_nt_error_from_unix(errno);
165 goto fail;
167 value = dbwrap_record_get_value(rec);
169 if ((value.dsize % sizeof(struct server_id)) != 0) {
170 status = NT_STATUS_INTERNAL_DB_CORRUPTION;
171 goto fail;
174 ids = (struct server_id *)value.dptr;
175 num_ids = value.dsize / sizeof(struct server_id);
177 for (i=0; i<num_ids; i++) {
178 if (serverid_equal(&id, &ids[i])) {
179 ids[i] = ids[num_ids-1];
180 value.dsize -= sizeof(struct server_id);
181 break;
184 if (value.dsize == 0) {
185 status = dbwrap_record_delete(rec);
186 goto done;
188 status = dbwrap_record_store(rec, value, 0);
189 fail:
190 done:
191 TALLOC_FREE(frame);
192 return status;
195 static NTSTATUS dbwrap_record_get_watchers(struct db_context *db,
196 struct db_record *rec,
197 TALLOC_CTX *mem_ctx,
198 struct server_id **p_ids,
199 size_t *p_num_ids)
201 struct db_context *w_db;
202 TDB_DATA key = { 0, };
203 TDB_DATA value = { 0, };
204 struct server_id *ids;
205 NTSTATUS status;
207 key = dbwrap_record_watchers_key(talloc_tos(), db, rec, NULL);
208 if (key.dptr == NULL) {
209 status = NT_STATUS_NO_MEMORY;
210 goto fail;
212 w_db = dbwrap_record_watchers_db();
213 if (w_db == NULL) {
214 status = NT_STATUS_INTERNAL_ERROR;
215 goto fail;
217 status = dbwrap_fetch(w_db, mem_ctx, key, &value);
218 TALLOC_FREE(key.dptr);
219 if (!NT_STATUS_IS_OK(status)) {
220 goto fail;
222 if ((value.dsize % sizeof(struct server_id)) != 0) {
223 status = NT_STATUS_INTERNAL_DB_CORRUPTION;
224 goto fail;
226 ids = (struct server_id *)value.dptr;
227 *p_ids = talloc_move(mem_ctx, &ids);
228 *p_num_ids = value.dsize / sizeof(struct server_id);
229 return NT_STATUS_OK;
230 fail:
231 TALLOC_FREE(key.dptr);
232 TALLOC_FREE(value.dptr);
233 return status;
236 struct dbwrap_record_watch_state {
237 struct tevent_context *ev;
238 struct db_context *db;
239 struct tevent_req *req;
240 struct messaging_context *msg;
241 TDB_DATA key;
242 TDB_DATA w_key;
245 static bool dbwrap_record_watch_filter(struct messaging_rec *rec,
246 void *private_data);
247 static void dbwrap_record_watch_done(struct tevent_req *subreq);
248 static int dbwrap_record_watch_state_destructor(
249 struct dbwrap_record_watch_state *state);
251 struct tevent_req *dbwrap_record_watch_send(TALLOC_CTX *mem_ctx,
252 struct tevent_context *ev,
253 struct db_record *rec,
254 struct messaging_context *msg)
256 struct tevent_req *req, *subreq;
257 struct dbwrap_record_watch_state *state;
258 struct db_context *watchers_db;
259 NTSTATUS status;
261 req = tevent_req_create(mem_ctx, &state,
262 struct dbwrap_record_watch_state);
263 if (req == NULL) {
264 return NULL;
266 state->db = dbwrap_record_get_db(rec);
267 state->ev = ev;
268 state->req = req;
269 state->msg = msg;
271 watchers_db = dbwrap_record_watchers_db();
272 if (watchers_db == NULL) {
273 tevent_req_nterror(req, map_nt_error_from_unix(errno));
274 return tevent_req_post(req, ev);
277 state->w_key = dbwrap_record_watchers_key(state, state->db, rec,
278 &state->key);
279 if (tevent_req_nomem(state->w_key.dptr, req)) {
280 return tevent_req_post(req, ev);
283 subreq = messaging_filtered_read_send(
284 state, ev, state->msg, dbwrap_record_watch_filter, state);
285 if (tevent_req_nomem(subreq, req)) {
286 return tevent_req_post(req, ev);
288 tevent_req_set_callback(subreq, dbwrap_record_watch_done, req);
290 status = dbwrap_record_add_watcher(
291 state->w_key, messaging_server_id(state->msg));
292 if (tevent_req_nterror(req, status)) {
293 return tevent_req_post(req, ev);
295 talloc_set_destructor(state, dbwrap_record_watch_state_destructor);
297 return req;
300 static bool dbwrap_record_watch_filter(struct messaging_rec *rec,
301 void *private_data)
303 struct dbwrap_record_watch_state *state = talloc_get_type_abort(
304 private_data, struct dbwrap_record_watch_state);
306 if (rec->msg_type != MSG_DBWRAP_MODIFIED) {
307 return false;
309 if (rec->num_fds != 0) {
310 return false;
312 if (rec->buf.length != state->w_key.dsize) {
313 return false;
315 return memcmp(rec->buf.data, state->w_key.dptr, rec->buf.length) == 0;
318 static int dbwrap_record_watch_state_destructor(
319 struct dbwrap_record_watch_state *s)
321 if (s->msg != NULL) {
322 dbwrap_record_del_watcher(
323 s->w_key, messaging_server_id(s->msg));
325 return 0;
328 static void dbwrap_watch_record_stored(struct db_context *db,
329 struct db_record *rec,
330 void *private_data)
332 struct messaging_context *msg = talloc_get_type_abort(
333 private_data, struct messaging_context);
334 struct server_id *ids = NULL;
335 size_t num_ids = 0;
336 TDB_DATA w_key = { 0, };
337 NTSTATUS status;
338 uint32_t i;
340 status = dbwrap_record_get_watchers(db, rec, talloc_tos(),
341 &ids, &num_ids);
342 if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
343 goto done;
345 if (!NT_STATUS_IS_OK(status)) {
346 DEBUG(1, ("dbwrap_record_get_watchers failed: %s\n",
347 nt_errstr(status)));
348 goto done;
350 w_key = dbwrap_record_watchers_key(talloc_tos(), db, rec, NULL);
351 if (w_key.dptr == NULL) {
352 DEBUG(1, ("dbwrap_record_watchers_key failed\n"));
353 goto done;
356 for (i=0; i<num_ids; i++) {
357 status = messaging_send_buf(msg, ids[i], MSG_DBWRAP_MODIFIED,
358 w_key.dptr, w_key.dsize);
359 if (!NT_STATUS_IS_OK(status)) {
360 struct server_id_buf tmp;
361 DEBUG(1, ("messaging_send to %s failed: %s\n",
362 server_id_str_buf(ids[i], &tmp),
363 nt_errstr(status)));
366 done:
367 TALLOC_FREE(w_key.dptr);
368 TALLOC_FREE(ids);
369 return;
372 void dbwrap_watch_db(struct db_context *db, struct messaging_context *msg)
374 dbwrap_set_stored_callback(db, dbwrap_watch_record_stored, msg);
377 static void dbwrap_record_watch_done(struct tevent_req *subreq)
379 struct tevent_req *req = tevent_req_callback_data(
380 subreq, struct tevent_req);
381 struct messaging_rec *rec;
382 int ret;
384 ret = messaging_filtered_read_recv(subreq, talloc_tos(), &rec);
385 TALLOC_FREE(subreq);
386 if (ret != 0) {
387 tevent_req_nterror(req, map_nt_error_from_unix(ret));
388 return;
390 tevent_req_done(req);
393 NTSTATUS dbwrap_record_watch_recv(struct tevent_req *req,
394 TALLOC_CTX *mem_ctx,
395 struct db_record **prec)
397 struct dbwrap_record_watch_state *state = tevent_req_data(
398 req, struct dbwrap_record_watch_state);
399 NTSTATUS status;
400 struct db_record *rec;
402 if (tevent_req_is_nterror(req, &status)) {
403 return status;
405 if (prec == NULL) {
406 return NT_STATUS_OK;
408 rec = dbwrap_fetch_locked(state->db, mem_ctx, state->key);
409 if (rec == NULL) {
410 return NT_STATUS_INTERNAL_DB_ERROR;
412 *prec = rec;
413 return NT_STATUS_OK;
416 struct dbwrap_watchers_traverse_read_state {
417 int (*fn)(const uint8_t *db_id, size_t db_id_len, const TDB_DATA key,
418 const struct server_id *watchers, size_t num_watchers,
419 void *private_data);
420 void *private_data;
423 static int dbwrap_watchers_traverse_read_callback(
424 struct db_record *rec, void *private_data)
426 struct dbwrap_watchers_traverse_read_state *state =
427 (struct dbwrap_watchers_traverse_read_state *)private_data;
428 uint8_t *db_id;
429 size_t db_id_len;
430 TDB_DATA w_key, key, w_data;
431 int res;
433 w_key = dbwrap_record_get_key(rec);
434 w_data = dbwrap_record_get_value(rec);
436 if (!dbwrap_record_watchers_key_parse(w_key, &db_id, &db_id_len,
437 &key)) {
438 return 0;
440 if ((w_data.dsize % sizeof(struct server_id)) != 0) {
441 return 0;
443 res = state->fn(db_id, db_id_len, key,
444 (struct server_id *)w_data.dptr,
445 w_data.dsize / sizeof(struct server_id),
446 state->private_data);
447 return res;
450 void dbwrap_watchers_traverse_read(
451 int (*fn)(const uint8_t *db_id, size_t db_id_len, const TDB_DATA key,
452 const struct server_id *watchers, size_t num_watchers,
453 void *private_data),
454 void *private_data)
456 struct dbwrap_watchers_traverse_read_state state;
457 struct db_context *db;
459 db = dbwrap_record_watchers_db();
460 if (db == NULL) {
461 return;
463 state.fn = fn;
464 state.private_data = private_data;
465 dbwrap_traverse_read(db, dbwrap_watchers_traverse_read_callback,
466 &state, NULL);
469 static int dbwrap_wakeall_cb(const uint8_t *db_id, size_t db_id_len,
470 const TDB_DATA key,
471 const struct server_id *watchers,
472 size_t num_watchers,
473 void *private_data)
475 struct messaging_context *msg = talloc_get_type_abort(
476 private_data, struct messaging_context);
477 uint32_t i;
478 DATA_BLOB blob;
480 blob.data = key.dptr;
481 blob.length = key.dsize;
483 for (i=0; i<num_watchers; i++) {
484 messaging_send(msg, watchers[i], MSG_DBWRAP_MODIFIED, &blob);
486 return 0;
489 void dbwrap_watchers_wakeall(struct messaging_context *msg)
491 dbwrap_watchers_traverse_read(dbwrap_wakeall_cb, msg);