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/>.
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
) {
38 watchers_db
= db_open(
40 TDB_CLEAR_IF_FIRST
| TDB_INCOMPATIBLE_HASH
,
41 O_RDWR
|O_CREAT
, 0600, DBWRAP_LOCK_ORDER_3
,
48 static TDB_DATA
dbwrap_record_watchers_key(TALLOC_CTX
*mem_ctx
,
49 struct db_context
*db
,
50 struct db_record
*rec
,
53 size_t db_id_len
= dbwrap_db_id(db
, NULL
, 0);
54 uint8_t db_id
[db_id_len
];
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
;
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
)
83 if (wkey
.dsize
< sizeof(uint32_t)) {
84 DEBUG(1, ("Invalid watchers key\n"));
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
));
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
;
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
;
106 struct server_id
*ids
;
110 db
= dbwrap_record_watchers_db();
112 status
= map_nt_error_from_unix(errno
);
115 rec
= dbwrap_fetch_locked(db
, talloc_tos(), w_key
);
117 status
= map_nt_error_from_unix(errno
);
120 value
= dbwrap_record_get_value(rec
);
122 if ((value
.dsize
% sizeof(struct server_id
)) != 0) {
123 status
= NT_STATUS_INTERNAL_DB_CORRUPTION
;
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
,
133 status
= NT_STATUS_NO_MEMORY
;
136 memcpy(ids
, value
.dptr
, value
.dsize
);
140 status
= dbwrap_record_store(
141 rec
, make_tdb_data((uint8_t *)ids
, talloc_get_size(ids
)), 0);
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
;
157 db
= dbwrap_record_watchers_db();
159 status
= map_nt_error_from_unix(errno
);
162 rec
= dbwrap_fetch_locked(db
, talloc_tos(), w_key
);
164 status
= map_nt_error_from_unix(errno
);
167 value
= dbwrap_record_get_value(rec
);
169 if ((value
.dsize
% sizeof(struct server_id
)) != 0) {
170 status
= NT_STATUS_INTERNAL_DB_CORRUPTION
;
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
);
184 if (value
.dsize
== 0) {
185 status
= dbwrap_record_delete(rec
);
188 status
= dbwrap_record_store(rec
, value
, 0);
195 static NTSTATUS
dbwrap_record_get_watchers(struct db_context
*db
,
196 struct db_record
*rec
,
198 struct server_id
**p_ids
,
201 struct db_context
*w_db
;
202 TDB_DATA key
= { 0, };
203 TDB_DATA value
= { 0, };
204 struct server_id
*ids
;
207 key
= dbwrap_record_watchers_key(talloc_tos(), db
, rec
, NULL
);
208 if (key
.dptr
== NULL
) {
209 status
= NT_STATUS_NO_MEMORY
;
212 w_db
= dbwrap_record_watchers_db();
214 status
= NT_STATUS_INTERNAL_ERROR
;
217 status
= dbwrap_fetch(w_db
, mem_ctx
, key
, &value
);
218 TALLOC_FREE(key
.dptr
);
219 if (!NT_STATUS_IS_OK(status
)) {
222 if ((value
.dsize
% sizeof(struct server_id
)) != 0) {
223 status
= NT_STATUS_INTERNAL_DB_CORRUPTION
;
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
);
231 TALLOC_FREE(key
.dptr
);
232 TALLOC_FREE(value
.dptr
);
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
;
245 static bool dbwrap_record_watch_filter(struct messaging_rec
*rec
,
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
;
261 req
= tevent_req_create(mem_ctx
, &state
,
262 struct dbwrap_record_watch_state
);
266 state
->db
= dbwrap_record_get_db(rec
);
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
,
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
);
300 static bool dbwrap_record_watch_filter(struct messaging_rec
*rec
,
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
) {
309 if (rec
->num_fds
!= 0) {
312 if (rec
->buf
.length
!= state
->w_key
.dsize
) {
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
));
328 static void dbwrap_watch_record_stored(struct db_context
*db
,
329 struct db_record
*rec
,
332 struct messaging_context
*msg
= talloc_get_type_abort(
333 private_data
, struct messaging_context
);
334 struct server_id
*ids
= NULL
;
336 TDB_DATA w_key
= { 0, };
340 status
= dbwrap_record_get_watchers(db
, rec
, talloc_tos(),
342 if (NT_STATUS_EQUAL(status
, NT_STATUS_NOT_FOUND
)) {
345 if (!NT_STATUS_IS_OK(status
)) {
346 DEBUG(1, ("dbwrap_record_get_watchers failed: %s\n",
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"));
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
),
367 TALLOC_FREE(w_key
.dptr
);
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
;
384 ret
= messaging_filtered_read_recv(subreq
, talloc_tos(), &rec
);
387 tevent_req_nterror(req
, map_nt_error_from_unix(ret
));
390 tevent_req_done(req
);
393 NTSTATUS
dbwrap_record_watch_recv(struct tevent_req
*req
,
395 struct db_record
**prec
)
397 struct dbwrap_record_watch_state
*state
= tevent_req_data(
398 req
, struct dbwrap_record_watch_state
);
400 struct db_record
*rec
;
402 if (tevent_req_is_nterror(req
, &status
)) {
408 rec
= dbwrap_fetch_locked(state
->db
, mem_ctx
, state
->key
);
410 return NT_STATUS_INTERNAL_DB_ERROR
;
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
,
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
;
430 TDB_DATA w_key
, key
, w_data
;
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
,
440 if ((w_data
.dsize
% sizeof(struct server_id
)) != 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
);
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
,
456 struct dbwrap_watchers_traverse_read_state state
;
457 struct db_context
*db
;
459 db
= dbwrap_record_watchers_db();
464 state
.private_data
= private_data
;
465 dbwrap_traverse_read(db
, dbwrap_watchers_traverse_read_callback
,
469 static int dbwrap_wakeall_cb(const uint8_t *db_id
, size_t db_id_len
,
471 const struct server_id
*watchers
,
475 struct messaging_context
*msg
= talloc_get_type_abort(
476 private_data
, struct messaging_context
);
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
);
489 void dbwrap_watchers_wakeall(struct messaging_context
*msg
)
491 dbwrap_watchers_traverse_read(dbwrap_wakeall_cb
, msg
);