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
,
57 dbwrap_db_id(db
, &db_id
, &db_id_len
);
58 key
= dbwrap_record_get_key(rec
);
60 wkey
.dsize
= sizeof(uint32_t) + db_id_len
+ key
.dsize
;
61 wkey
.dptr
= talloc_array(mem_ctx
, uint8_t, wkey
.dsize
);
62 if (wkey
.dptr
== NULL
) {
63 return make_tdb_data(NULL
, 0);
65 SIVAL(wkey
.dptr
, 0, db_id_len
);
66 memcpy(wkey
.dptr
+ sizeof(uint32_t), db_id
, db_id_len
);
67 memcpy(wkey
.dptr
+ sizeof(uint32_t) + db_id_len
, key
.dptr
, key
.dsize
);
69 if (rec_key
!= NULL
) {
70 rec_key
->dptr
= wkey
.dptr
+ sizeof(uint32_t) + db_id_len
;
71 rec_key
->dsize
= key
.dsize
;
77 static bool dbwrap_record_watchers_key_parse(
78 TDB_DATA wkey
, uint8_t **p_db_id
, size_t *p_db_id_len
, TDB_DATA
*key
)
82 if (wkey
.dsize
< sizeof(uint32_t)) {
83 DEBUG(1, ("Invalid watchers key\n"));
86 db_id_len
= IVAL(wkey
.dptr
, 0);
87 if (db_id_len
> (wkey
.dsize
- sizeof(uint32_t))) {
88 DEBUG(1, ("Invalid watchers key, wkey.dsize=%d, "
89 "db_id_len=%d\n", (int)wkey
.dsize
, (int)db_id_len
));
92 *p_db_id
= wkey
.dptr
+ sizeof(uint32_t);
93 *p_db_id_len
= db_id_len
;
94 key
->dptr
= wkey
.dptr
+ sizeof(uint32_t) + db_id_len
;
95 key
->dsize
= wkey
.dsize
- sizeof(uint32_t) - db_id_len
;
99 static NTSTATUS
dbwrap_record_add_watcher(TDB_DATA w_key
, struct server_id id
)
101 struct TALLOC_CTX
*frame
= talloc_stackframe();
102 struct db_context
*db
;
103 struct db_record
*rec
;
105 struct server_id
*ids
;
109 db
= dbwrap_record_watchers_db();
111 status
= map_nt_error_from_unix(errno
);
114 rec
= dbwrap_fetch_locked(db
, talloc_tos(), w_key
);
116 status
= map_nt_error_from_unix(errno
);
119 value
= dbwrap_record_get_value(rec
);
121 if ((value
.dsize
% sizeof(struct server_id
)) != 0) {
122 status
= NT_STATUS_INTERNAL_DB_CORRUPTION
;
126 ids
= (struct server_id
*)value
.dptr
;
127 num_ids
= value
.dsize
/ sizeof(struct server_id
);
129 ids
= talloc_array(talloc_tos(), struct server_id
,
132 status
= NT_STATUS_NO_MEMORY
;
135 memcpy(ids
, value
.dptr
, value
.dsize
);
139 status
= dbwrap_record_store(
140 rec
, make_tdb_data((uint8_t *)ids
, talloc_get_size(ids
)), 0);
146 static NTSTATUS
dbwrap_record_del_watcher(TDB_DATA w_key
, struct server_id id
)
148 struct TALLOC_CTX
*frame
= talloc_stackframe();
149 struct db_context
*db
;
150 struct db_record
*rec
;
151 struct server_id
*ids
;
156 db
= dbwrap_record_watchers_db();
158 status
= map_nt_error_from_unix(errno
);
161 rec
= dbwrap_fetch_locked(db
, talloc_tos(), w_key
);
163 status
= map_nt_error_from_unix(errno
);
166 value
= dbwrap_record_get_value(rec
);
168 if ((value
.dsize
% sizeof(struct server_id
)) != 0) {
169 status
= NT_STATUS_INTERNAL_DB_CORRUPTION
;
173 ids
= (struct server_id
*)value
.dptr
;
174 num_ids
= value
.dsize
/ sizeof(struct server_id
);
176 for (i
=0; i
<num_ids
; i
++) {
177 if (serverid_equal(&id
, &ids
[i
])) {
178 ids
[i
] = ids
[num_ids
-1];
179 value
.dsize
-= sizeof(struct server_id
);
183 if (value
.dsize
== 0) {
184 status
= dbwrap_record_delete(rec
);
187 status
= dbwrap_record_store(rec
, value
, 0);
194 static NTSTATUS
dbwrap_record_get_watchers(struct db_context
*db
,
195 struct db_record
*rec
,
197 struct server_id
**p_ids
,
200 struct db_context
*w_db
;
201 TDB_DATA key
= { 0, };
202 TDB_DATA value
= { 0, };
203 struct server_id
*ids
;
206 key
= dbwrap_record_watchers_key(talloc_tos(), db
, rec
, NULL
);
207 if (key
.dptr
== NULL
) {
208 status
= NT_STATUS_NO_MEMORY
;
211 w_db
= dbwrap_record_watchers_db();
213 status
= NT_STATUS_INTERNAL_ERROR
;
216 status
= dbwrap_fetch(w_db
, mem_ctx
, key
, &value
);
217 TALLOC_FREE(key
.dptr
);
218 if (!NT_STATUS_IS_OK(status
)) {
221 if ((value
.dsize
% sizeof(struct server_id
)) != 0) {
222 status
= NT_STATUS_INTERNAL_DB_CORRUPTION
;
225 ids
= (struct server_id
*)value
.dptr
;
226 *p_ids
= talloc_move(mem_ctx
, &ids
);
227 *p_num_ids
= value
.dsize
/ sizeof(struct server_id
);
230 TALLOC_FREE(key
.dptr
);
231 TALLOC_FREE(value
.dptr
);
235 struct dbwrap_record_watch_state
{
236 struct tevent_context
*ev
;
237 struct db_context
*db
;
238 struct tevent_req
*req
;
239 struct messaging_context
*msg
;
244 static bool dbwrap_record_watch_filter(struct messaging_rec
*rec
,
246 static void dbwrap_record_watch_done(struct tevent_req
*subreq
);
247 static int dbwrap_record_watch_state_destructor(
248 struct dbwrap_record_watch_state
*state
);
250 struct tevent_req
*dbwrap_record_watch_send(TALLOC_CTX
*mem_ctx
,
251 struct tevent_context
*ev
,
252 struct db_record
*rec
,
253 struct messaging_context
*msg
)
255 struct tevent_req
*req
, *subreq
;
256 struct dbwrap_record_watch_state
*state
;
257 struct db_context
*watchers_db
;
260 req
= tevent_req_create(mem_ctx
, &state
,
261 struct dbwrap_record_watch_state
);
265 state
->db
= dbwrap_record_get_db(rec
);
270 watchers_db
= dbwrap_record_watchers_db();
271 if (watchers_db
== NULL
) {
272 tevent_req_nterror(req
, map_nt_error_from_unix(errno
));
273 return tevent_req_post(req
, ev
);
276 state
->w_key
= dbwrap_record_watchers_key(state
, state
->db
, rec
,
278 if (tevent_req_nomem(state
->w_key
.dptr
, req
)) {
279 return tevent_req_post(req
, ev
);
282 subreq
= messaging_filtered_read_send(
283 state
, ev
, state
->msg
, dbwrap_record_watch_filter
, state
);
284 if (tevent_req_nomem(subreq
, req
)) {
285 return tevent_req_post(req
, ev
);
287 tevent_req_set_callback(subreq
, dbwrap_record_watch_done
, req
);
289 status
= dbwrap_record_add_watcher(
290 state
->w_key
, messaging_server_id(state
->msg
));
291 if (tevent_req_nterror(req
, status
)) {
292 return tevent_req_post(req
, ev
);
294 talloc_set_destructor(state
, dbwrap_record_watch_state_destructor
);
299 static bool dbwrap_record_watch_filter(struct messaging_rec
*rec
,
302 struct dbwrap_record_watch_state
*state
= talloc_get_type_abort(
303 private_data
, struct dbwrap_record_watch_state
);
305 if (rec
->msg_type
!= MSG_DBWRAP_MODIFIED
) {
308 if (rec
->num_fds
!= 0) {
311 if (rec
->buf
.length
!= state
->w_key
.dsize
) {
314 return memcmp(rec
->buf
.data
, state
->w_key
.dptr
, rec
->buf
.length
) == 0;
317 static int dbwrap_record_watch_state_destructor(
318 struct dbwrap_record_watch_state
*s
)
320 if (s
->msg
!= NULL
) {
321 dbwrap_record_del_watcher(
322 s
->w_key
, messaging_server_id(s
->msg
));
327 static void dbwrap_watch_record_stored(struct db_context
*db
,
328 struct db_record
*rec
,
331 struct messaging_context
*msg
= talloc_get_type_abort(
332 private_data
, struct messaging_context
);
333 struct server_id
*ids
= NULL
;
335 TDB_DATA w_key
= { 0, };
339 status
= dbwrap_record_get_watchers(db
, rec
, talloc_tos(),
341 if (NT_STATUS_EQUAL(status
, NT_STATUS_NOT_FOUND
)) {
344 if (!NT_STATUS_IS_OK(status
)) {
345 DEBUG(1, ("dbwrap_record_get_watchers failed: %s\n",
349 w_key
= dbwrap_record_watchers_key(talloc_tos(), db
, rec
, NULL
);
350 if (w_key
.dptr
== NULL
) {
351 DEBUG(1, ("dbwrap_record_watchers_key failed\n"));
355 for (i
=0; i
<num_ids
; i
++) {
356 status
= messaging_send_buf(msg
, ids
[i
], MSG_DBWRAP_MODIFIED
,
357 w_key
.dptr
, w_key
.dsize
);
358 if (!NT_STATUS_IS_OK(status
)) {
359 char *str
= procid_str_static(&ids
[i
]);
360 DEBUG(1, ("messaging_send to %s failed: %s\n",
361 str
, nt_errstr(status
)));
366 TALLOC_FREE(w_key
.dptr
);
371 void dbwrap_watch_db(struct db_context
*db
, struct messaging_context
*msg
)
373 dbwrap_set_stored_callback(db
, dbwrap_watch_record_stored
, msg
);
376 static void dbwrap_record_watch_done(struct tevent_req
*subreq
)
378 struct tevent_req
*req
= tevent_req_callback_data(
379 subreq
, struct tevent_req
);
380 struct messaging_rec
*rec
;
383 ret
= messaging_filtered_read_recv(subreq
, talloc_tos(), &rec
);
386 tevent_req_nterror(req
, map_nt_error_from_unix(ret
));
389 tevent_req_done(req
);
392 NTSTATUS
dbwrap_record_watch_recv(struct tevent_req
*req
,
394 struct db_record
**prec
)
396 struct dbwrap_record_watch_state
*state
= tevent_req_data(
397 req
, struct dbwrap_record_watch_state
);
399 struct db_record
*rec
;
401 if (tevent_req_is_nterror(req
, &status
)) {
407 rec
= dbwrap_fetch_locked(state
->db
, mem_ctx
, state
->key
);
409 return NT_STATUS_INTERNAL_DB_ERROR
;
415 struct dbwrap_watchers_traverse_read_state
{
416 int (*fn
)(const uint8_t *db_id
, size_t db_id_len
, const TDB_DATA key
,
417 const struct server_id
*watchers
, size_t num_watchers
,
422 static int dbwrap_watchers_traverse_read_callback(
423 struct db_record
*rec
, void *private_data
)
425 struct dbwrap_watchers_traverse_read_state
*state
=
426 (struct dbwrap_watchers_traverse_read_state
*)private_data
;
429 TDB_DATA w_key
, key
, w_data
;
432 w_key
= dbwrap_record_get_key(rec
);
433 w_data
= dbwrap_record_get_value(rec
);
435 if (!dbwrap_record_watchers_key_parse(w_key
, &db_id
, &db_id_len
,
439 if ((w_data
.dsize
% sizeof(struct server_id
)) != 0) {
442 res
= state
->fn(db_id
, db_id_len
, key
,
443 (struct server_id
*)w_data
.dptr
,
444 w_data
.dsize
/ sizeof(struct server_id
),
445 state
->private_data
);
449 void dbwrap_watchers_traverse_read(
450 int (*fn
)(const uint8_t *db_id
, size_t db_id_len
, const TDB_DATA key
,
451 const struct server_id
*watchers
, size_t num_watchers
,
455 struct dbwrap_watchers_traverse_read_state state
;
456 struct db_context
*db
;
458 db
= dbwrap_record_watchers_db();
463 state
.private_data
= private_data
;
464 dbwrap_traverse_read(db
, dbwrap_watchers_traverse_read_callback
,
468 static int dbwrap_wakeall_cb(const uint8_t *db_id
, size_t db_id_len
,
470 const struct server_id
*watchers
,
474 struct messaging_context
*msg
= talloc_get_type_abort(
475 private_data
, struct messaging_context
);
479 blob
.data
= key
.dptr
;
480 blob
.length
= key
.dsize
;
482 for (i
=0; i
<num_watchers
; i
++) {
483 messaging_send(msg
, watchers
[i
], MSG_DBWRAP_MODIFIED
, &blob
);
488 void dbwrap_watchers_wakeall(struct messaging_context
*msg
)
490 dbwrap_watchers_traverse_read(dbwrap_wakeall_cb
, msg
);