Coverage Report

Created: 2026-05-24 06:47

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/samba/source3/lib/dbwrap/dbwrap_watch.c
Line
Count
Source
1
/*
2
   Unix SMB/CIFS implementation.
3
   Watch dbwrap record changes
4
   Copyright (C) Volker Lendecke 2012
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 "lib/util/server_id.h"
23
#include "dbwrap/dbwrap.h"
24
#include "dbwrap_watch.h"
25
#include "dbwrap_open.h"
26
#include "lib/util/util_tdb.h"
27
#include "lib/util/tevent_ntstatus.h"
28
#include "serverid.h"
29
#include "server_id_watch.h"
30
#include "lib/dbwrap/dbwrap_private.h"
31
32
struct dbwrap_watcher {
33
  /*
34
   * Process watching this record
35
   */
36
  struct server_id pid;
37
  /*
38
   * Individual instance inside the waiter, incremented each
39
   * time a watcher is created
40
   */
41
  uint64_t instance;
42
};
43
44
0
#define DBWRAP_WATCHER_BUF_LENGTH (SERVER_ID_BUF_LENGTH + sizeof(uint64_t))
45
0
#define DBWRAP_MAX_WATCHERS (INT32_MAX/DBWRAP_WATCHER_BUF_LENGTH)
46
47
/*
48
 * Watched records contain a header of:
49
 *
50
 * [uint32] num_records
51
 * 0 [DBWRAP_WATCHER_BUF_LENGTH]              \
52
 * 1 [DBWRAP_WATCHER_BUF_LENGTH]              |
53
 * ..                                         |- Array of watchers
54
 * (num_records-1)[DBWRAP_WATCHER_BUF_LENGTH] /
55
 *
56
 * [Remainder of record....]
57
 *
58
 * If this header is absent then this is a
59
 * fresh record of length zero (no watchers).
60
 */
61
62
static bool dbwrap_watch_rec_parse(
63
  TDB_DATA data,
64
  uint8_t **pwatchers,
65
  size_t *pnum_watchers,
66
  TDB_DATA *pdata)
67
0
{
68
0
  size_t num_watchers;
69
70
0
  if (data.dsize == 0) {
71
    /* Fresh record */
72
0
    if (pwatchers != NULL) {
73
0
      *pwatchers = NULL;
74
0
    }
75
0
    if (pnum_watchers != NULL) {
76
0
      *pnum_watchers = 0;
77
0
    }
78
0
    if (pdata != NULL) {
79
0
      *pdata = (TDB_DATA) { .dptr = NULL };
80
0
    }
81
0
    return true;
82
0
  }
83
84
0
  if (data.dsize < sizeof(uint32_t)) {
85
    /* Invalid record */
86
0
    return false;
87
0
  }
88
89
0
  num_watchers = IVAL(data.dptr, 0);
90
91
0
  data.dptr += sizeof(uint32_t);
92
0
  data.dsize -= sizeof(uint32_t);
93
94
0
  if (num_watchers > data.dsize/DBWRAP_WATCHER_BUF_LENGTH) {
95
    /* Invalid record */
96
0
    return false;
97
0
  }
98
99
0
  if (pwatchers != NULL) {
100
0
    *pwatchers = data.dptr;
101
0
  }
102
0
  if (pnum_watchers != NULL) {
103
0
    *pnum_watchers = num_watchers;
104
0
  }
105
0
  if (pdata != NULL) {
106
0
    size_t watchers_len = num_watchers * DBWRAP_WATCHER_BUF_LENGTH;
107
0
    *pdata = (TDB_DATA) {
108
0
      .dptr = data.dptr + watchers_len,
109
0
      .dsize = data.dsize - watchers_len
110
0
    };
111
0
  }
112
113
0
  return true;
114
0
}
115
116
static void dbwrap_watcher_get(struct dbwrap_watcher *w,
117
             const uint8_t buf[DBWRAP_WATCHER_BUF_LENGTH])
118
0
{
119
0
  server_id_get(&w->pid, buf);
120
0
  w->instance = BVAL(buf, SERVER_ID_BUF_LENGTH);
121
0
}
122
123
static void dbwrap_watcher_put(uint8_t buf[DBWRAP_WATCHER_BUF_LENGTH],
124
             const struct dbwrap_watcher *w)
125
0
{
126
0
  server_id_put(buf, w->pid);
127
0
  SBVAL(buf, SERVER_ID_BUF_LENGTH, w->instance);
128
0
}
129
130
static void dbwrap_watch_log_invalid_record(
131
  struct db_context *db, TDB_DATA key, TDB_DATA value)
132
0
{
133
0
  DBG_ERR("Found invalid record in %s\n", dbwrap_name(db));
134
0
  dump_data(1, key.dptr, key.dsize);
135
0
  dump_data(1, value.dptr, value.dsize);
136
0
}
137
138
struct db_watched_ctx {
139
  struct db_context *backend;
140
  struct messaging_context *msg;
141
};
142
143
struct db_watched_record {
144
  struct db_record *rec;
145
  struct server_id self;
146
  struct {
147
    struct db_record *rec;
148
    TDB_DATA initial_value;
149
    bool initial_valid;
150
  } backend;
151
  bool force_fini_store;
152
  struct dbwrap_watcher added;
153
  bool removed_first;
154
  struct {
155
    /*
156
     * The is the number of watcher records
157
     * parsed from backend.initial_value
158
     */
159
    size_t count;
160
    /*
161
     * This is the pointer to
162
     * the optentially first watcher record
163
     * parsed from backend.initial_value
164
     *
165
     * The pointer actually points to memory
166
     * in backend.initial_value.
167
     *
168
     * Note it might be NULL, if count is 0.
169
     */
170
    uint8_t *first;
171
    /*
172
     * This remembers if we already
173
     * notified the watchers.
174
     *
175
     * As we only need to do that once during:
176
     *   do_locked
177
     * or:
178
     *   between rec = fetch_locked
179
     *   and
180
     *   TALLOC_FREE(rec)
181
     */
182
    bool alerted;
183
  } watchers;
184
  struct {
185
    struct dbwrap_watcher watcher;
186
  } wakeup;
187
};
188
189
static struct db_watched_record *db_record_get_watched_record(struct db_record *rec)
190
0
{
191
  /*
192
   * we can't use wrec = talloc_get_type_abort() here!
193
   * because wrec is likely a stack variable in
194
   * dbwrap_watched_do_locked_fn()
195
   *
196
   * In order to have a least some protection
197
   * we verify the cross reference pointers
198
   * between rec and wrec
199
   */
200
0
  struct db_watched_record *wrec =
201
0
    (struct db_watched_record *)rec->private_data;
202
0
  SMB_ASSERT(wrec->rec == rec);
203
0
  return wrec;
204
0
}
205
206
static NTSTATUS dbwrap_watched_record_storev(
207
  struct db_watched_record *wrec,
208
  const TDB_DATA *dbufs, int num_dbufs, int flags);
209
static NTSTATUS dbwrap_watched_storev(struct db_record *rec,
210
              const TDB_DATA *dbufs, int num_dbufs,
211
              int flags);
212
static NTSTATUS dbwrap_watched_delete(struct db_record *rec);
213
static void dbwrap_watched_trigger_wakeup(struct messaging_context *msg_ctx,
214
            struct dbwrap_watcher *watcher);
215
static int db_watched_record_destructor(struct db_watched_record *wrec);
216
217
static void db_watched_record_init(struct db_context *db,
218
           struct messaging_context *msg_ctx,
219
           struct db_record *rec,
220
           struct db_watched_record *wrec,
221
           struct db_record *backend_rec,
222
           TDB_DATA backend_value)
223
0
{
224
0
  bool ok;
225
226
0
  *rec = (struct db_record) {
227
0
    .db = db,
228
0
    .key = dbwrap_record_get_key(backend_rec),
229
0
    .storev = dbwrap_watched_storev,
230
0
    .delete_rec = dbwrap_watched_delete,
231
0
    .private_data = wrec,
232
0
  };
233
234
0
  *wrec = (struct db_watched_record) {
235
0
    .rec = rec,
236
0
    .self = messaging_server_id(msg_ctx),
237
0
    .backend = {
238
0
      .rec = backend_rec,
239
0
      .initial_value = backend_value,
240
0
      .initial_valid = true,
241
0
    },
242
0
  };
243
244
0
  ok = dbwrap_watch_rec_parse(backend_value,
245
0
            &wrec->watchers.first,
246
0
            &wrec->watchers.count,
247
0
            &rec->value);
248
0
  if (!ok) {
249
0
    dbwrap_watch_log_invalid_record(rec->db, rec->key, backend_value);
250
    /* wipe invalid data */
251
0
    rec->value = (TDB_DATA) { .dptr = NULL, .dsize = 0 };
252
0
  }
253
0
}
254
255
static struct db_record *dbwrap_watched_fetch_locked(
256
  struct db_context *db, TALLOC_CTX *mem_ctx, TDB_DATA key)
257
0
{
258
0
  struct db_watched_ctx *ctx = talloc_get_type_abort(
259
0
    db->private_data, struct db_watched_ctx);
260
0
  struct db_record *rec = NULL;
261
0
  struct db_watched_record *wrec = NULL;
262
0
  struct db_record *backend_rec = NULL;
263
0
  TDB_DATA backend_value = { .dptr = NULL, };
264
265
0
  rec = talloc_zero(mem_ctx, struct db_record);
266
0
  if (rec == NULL) {
267
0
    return NULL;
268
0
  }
269
0
  wrec = talloc_zero(rec, struct db_watched_record);
270
0
  if (wrec == NULL) {
271
0
    TALLOC_FREE(rec);
272
0
    return NULL;
273
0
  }
274
275
0
  backend_rec = dbwrap_fetch_locked(ctx->backend, wrec, key);
276
0
  if (backend_rec == NULL) {
277
0
    TALLOC_FREE(rec);
278
0
    return NULL;
279
0
  }
280
0
  backend_value = dbwrap_record_get_value(backend_rec);
281
282
0
  db_watched_record_init(db, ctx->msg,
283
0
             rec, wrec,
284
0
             backend_rec, backend_value);
285
0
  rec->value_valid = true;
286
0
  talloc_set_destructor(wrec, db_watched_record_destructor);
287
288
0
  return rec;
289
0
}
290
291
struct db_watched_record_fini_state {
292
  struct db_watched_record *wrec;
293
  TALLOC_CTX *frame;
294
  TDB_DATA dbufs[2];
295
  int num_dbufs;
296
  bool ok;
297
};
298
299
static void db_watched_record_fini_fetcher(TDB_DATA key,
300
             TDB_DATA backend_value,
301
             void *private_data)
302
0
{
303
0
  struct db_watched_record_fini_state *state =
304
0
    (struct db_watched_record_fini_state *)private_data;
305
0
  struct db_watched_record *wrec = state->wrec;
306
0
  struct db_record *rec = wrec->rec;
307
0
  TDB_DATA value = {};
308
0
  bool ok;
309
0
  size_t copy_size;
310
311
  /*
312
   * We're within dbwrap_parse_record()
313
   * and backend_value directly points into
314
   * the mmap'ed tdb, so we need to copy the
315
   * parts we require.
316
   */
317
318
0
  ok = dbwrap_watch_rec_parse(backend_value, NULL, NULL, &value);
319
0
  if (!ok) {
320
0
    struct db_context *db = dbwrap_record_get_db(rec);
321
322
0
    dbwrap_watch_log_invalid_record(db, key, backend_value);
323
324
    /* wipe invalid data */
325
0
    value = (TDB_DATA) { .dptr = NULL, .dsize = 0 };
326
0
  }
327
328
0
  copy_size = MIN(rec->value.dsize, value.dsize);
329
0
  if (copy_size != 0) {
330
    /*
331
     * First reuse the buffer we already had
332
     * as much as we can.
333
     */
334
0
    memcpy(rec->value.dptr, value.dptr, copy_size);
335
0
    state->dbufs[state->num_dbufs++] = rec->value;
336
0
    value.dsize -= copy_size;
337
0
    value.dptr += copy_size;
338
0
  }
339
340
0
  if (value.dsize != 0) {
341
0
    uint8_t *p = NULL;
342
343
    /*
344
     * There's still new data left
345
     * allocate it on callers stackframe
346
     */
347
0
    p = talloc_memdup(state->frame, value.dptr, value.dsize);
348
0
    if (p == NULL) {
349
0
      DBG_WARNING("failed to allocate %zu bytes\n",
350
0
            value.dsize);
351
0
      return;
352
0
    }
353
354
0
    state->dbufs[state->num_dbufs++] = (TDB_DATA) {
355
0
      .dptr = p, .dsize = value.dsize,
356
0
    };
357
0
  }
358
359
0
  state->ok = true;
360
0
}
361
362
static void db_watched_record_fini(struct db_watched_record *wrec)
363
0
{
364
0
  struct db_watched_record_fini_state state = { .wrec = wrec, };
365
0
  struct db_context *backend = dbwrap_record_get_db(wrec->backend.rec);
366
0
  struct db_record *rec = wrec->rec;
367
0
  TDB_DATA key = dbwrap_record_get_key(wrec->backend.rec);
368
0
  NTSTATUS status;
369
370
0
  if (!wrec->force_fini_store) {
371
0
    return;
372
0
  }
373
374
0
  if (wrec->backend.initial_valid) {
375
0
    if (rec->value.dsize != 0) {
376
0
      state.dbufs[state.num_dbufs++] = rec->value;
377
0
    }
378
0
  } else {
379
    /*
380
     * We need to fetch the current
381
     * value from the backend again,
382
     * which may need to allocate memory
383
     * on the provided stackframe.
384
     */
385
386
0
    state.frame = talloc_stackframe();
387
388
0
    status = dbwrap_parse_record(backend, key,
389
0
        db_watched_record_fini_fetcher, &state);
390
0
    if (!NT_STATUS_IS_OK(status)) {
391
0
      DBG_WARNING("dbwrap_parse_record failed: %s\n",
392
0
            nt_errstr(status));
393
0
      TALLOC_FREE(state.frame);
394
0
      return;
395
0
    }
396
0
    if (!state.ok) {
397
0
      TALLOC_FREE(state.frame);
398
0
      return;
399
0
    }
400
0
  }
401
402
  /*
403
   * We don't want to wake up others just because
404
   * we added ourself as new watcher. But if we
405
   * removed outself from the first position
406
   * we need to alert the next one.
407
   */
408
0
  if (!wrec->removed_first) {
409
0
    dbwrap_watched_watch_skip_alerting(rec);
410
0
  }
411
412
0
  status = dbwrap_watched_record_storev(wrec, state.dbufs, state.num_dbufs, 0);
413
0
  TALLOC_FREE(state.frame);
414
0
  if (!NT_STATUS_IS_OK(status)) {
415
0
    DBG_WARNING("dbwrap_watched_record_storev failed: %s\n",
416
0
          nt_errstr(status));
417
0
    return;
418
0
  }
419
420
0
  return;
421
0
}
422
423
static int db_watched_record_destructor(struct db_watched_record *wrec)
424
0
{
425
0
  struct db_record *rec = wrec->rec;
426
0
  struct db_watched_ctx *ctx = talloc_get_type_abort(
427
0
    rec->db->private_data, struct db_watched_ctx);
428
429
0
  db_watched_record_fini(wrec);
430
0
  TALLOC_FREE(wrec->backend.rec);
431
0
  dbwrap_watched_trigger_wakeup(ctx->msg, &wrec->wakeup.watcher);
432
0
  return 0;
433
0
}
434
435
struct dbwrap_watched_do_locked_state {
436
  struct db_context *db;
437
  struct messaging_context *msg_ctx;
438
  struct db_watched_record *wrec;
439
  struct db_record *rec;
440
  void (*fn)(struct db_record *rec,
441
       TDB_DATA value,
442
       void *private_data);
443
  void *private_data;
444
};
445
446
static void dbwrap_watched_do_locked_fn(
447
  struct db_record *backend_rec,
448
  TDB_DATA backend_value,
449
  void *private_data)
450
0
{
451
0
  struct dbwrap_watched_do_locked_state *state =
452
0
    (struct dbwrap_watched_do_locked_state *)private_data;
453
454
0
  db_watched_record_init(state->db, state->msg_ctx,
455
0
             state->rec, state->wrec,
456
0
             backend_rec, backend_value);
457
458
0
  state->fn(state->rec, state->rec->value, state->private_data);
459
460
0
  db_watched_record_fini(state->wrec);
461
0
}
462
463
static NTSTATUS dbwrap_watched_do_locked(struct db_context *db, TDB_DATA key,
464
           void (*fn)(struct db_record *rec,
465
                TDB_DATA value,
466
                void *private_data),
467
           void *private_data)
468
0
{
469
0
  struct db_watched_ctx *ctx = talloc_get_type_abort(
470
0
    db->private_data, struct db_watched_ctx);
471
0
  struct db_watched_record wrec;
472
0
  struct db_record rec;
473
0
  struct dbwrap_watched_do_locked_state state = {
474
0
    .db = db, .msg_ctx = ctx->msg,
475
0
    .rec = &rec, .wrec = &wrec,
476
0
    .fn = fn, .private_data = private_data,
477
0
  };
478
0
  NTSTATUS status;
479
480
0
  status = dbwrap_do_locked(
481
0
    ctx->backend, key, dbwrap_watched_do_locked_fn, &state);
482
0
  if (!NT_STATUS_IS_OK(status)) {
483
0
    DBG_DEBUG("dbwrap_do_locked returned %s\n", nt_errstr(status));
484
0
    return status;
485
0
  }
486
487
0
  DBG_DEBUG("dbwrap_watched_do_locked_fn returned\n");
488
489
0
  dbwrap_watched_trigger_wakeup(state.msg_ctx, &wrec.wakeup.watcher);
490
491
0
  return NT_STATUS_OK;
492
0
}
493
494
static void dbwrap_watched_record_prepare_wakeup(
495
  struct db_watched_record *wrec)
496
0
{
497
  /*
498
   * Wakeup only needs to happen once (if at all)
499
   */
500
0
  if (wrec->watchers.alerted) {
501
    /* already done */
502
0
    return;
503
0
  }
504
0
  wrec->watchers.alerted = true;
505
506
0
  if (wrec->watchers.count == 0) {
507
0
    DBG_DEBUG("No watchers\n");
508
0
    return;
509
0
  }
510
511
0
  while (wrec->watchers.count != 0) {
512
0
    struct server_id_buf tmp;
513
0
    bool exists;
514
515
0
    dbwrap_watcher_get(&wrec->wakeup.watcher, wrec->watchers.first);
516
0
    exists = serverid_exists(&wrec->wakeup.watcher.pid);
517
0
    if (!exists) {
518
0
      DBG_DEBUG("Discard non-existing waiter %s:%"PRIu64"\n",
519
0
          server_id_str_buf(wrec->wakeup.watcher.pid, &tmp),
520
0
          wrec->wakeup.watcher.instance);
521
0
      wrec->watchers.first += DBWRAP_WATCHER_BUF_LENGTH;
522
0
      wrec->watchers.count -= 1;
523
0
      continue;
524
0
    }
525
526
    /*
527
     * We will only wakeup the first waiter, via
528
     * dbwrap_watched_trigger_wakeup(), but keep
529
     * all (including the first one) in the list that
530
     * will be flushed back to the backend record
531
     * again. Waiters are removing their entries
532
     * via dbwrap_watched_watch_remove_instance()
533
     * when they no longer want to monitor the record.
534
     */
535
0
    DBG_DEBUG("Will alert first waiter %s:%"PRIu64"\n",
536
0
        server_id_str_buf(wrec->wakeup.watcher.pid, &tmp),
537
0
        wrec->wakeup.watcher.instance);
538
0
    break;
539
0
  }
540
0
}
541
542
static void dbwrap_watched_trigger_wakeup(struct messaging_context *msg_ctx,
543
            struct dbwrap_watcher *watcher)
544
0
{
545
0
  struct server_id_buf tmp;
546
0
  uint8_t instance_buf[8];
547
0
  NTSTATUS status;
548
549
0
  if (watcher->instance == 0) {
550
0
    DBG_DEBUG("No one to wakeup\n");
551
0
    return;
552
0
  }
553
554
0
  DBG_DEBUG("Alerting %s:%"PRIu64"\n",
555
0
      server_id_str_buf(watcher->pid, &tmp),
556
0
      watcher->instance);
557
558
0
  SBVAL(instance_buf, 0, watcher->instance);
559
560
0
  status = messaging_send_buf(
561
0
    msg_ctx,
562
0
    watcher->pid,
563
0
    MSG_DBWRAP_MODIFIED,
564
0
    instance_buf,
565
0
    sizeof(instance_buf));
566
0
  if (!NT_STATUS_IS_OK(status)) {
567
0
    DBG_WARNING("messaging_send_buf to %s failed: %s - ignoring...\n",
568
0
          server_id_str_buf(watcher->pid, &tmp),
569
0
          nt_errstr(status));
570
0
  }
571
0
}
572
573
static NTSTATUS dbwrap_watched_record_storev(
574
  struct db_watched_record *wrec,
575
  const TDB_DATA *dbufs, int num_dbufs, int flags)
576
0
{
577
0
  uint8_t num_watchers_buf[4] = { 0 };
578
0
  uint8_t add_buf[DBWRAP_WATCHER_BUF_LENGTH];
579
0
  size_t num_store_watchers;
580
0
  TDB_DATA my_dbufs[num_dbufs+3];
581
0
  int num_my_dbufs = 0;
582
0
  NTSTATUS status;
583
0
  size_t add_count = 0;
584
585
0
  dbwrap_watched_record_prepare_wakeup(wrec);
586
587
0
  wrec->backend.initial_valid = false;
588
0
  wrec->force_fini_store = false;
589
590
0
  if (wrec->added.pid.pid != 0) {
591
0
    dbwrap_watcher_put(add_buf, &wrec->added);
592
0
    add_count = 1;
593
0
  }
594
595
0
  num_store_watchers = wrec->watchers.count + add_count;
596
0
  if (num_store_watchers == 0 && num_dbufs == 0) {
597
0
    status = dbwrap_record_delete(wrec->backend.rec);
598
0
    return status;
599
0
  }
600
0
  if (num_store_watchers >= DBWRAP_MAX_WATCHERS) {
601
0
    DBG_WARNING("Can't handle %zu watchers\n",
602
0
          num_store_watchers);
603
0
    return NT_STATUS_INSUFFICIENT_RESOURCES;
604
0
  }
605
606
0
  SIVAL(num_watchers_buf, 0, num_store_watchers);
607
608
0
  my_dbufs[num_my_dbufs++] = (TDB_DATA) {
609
0
    .dptr = num_watchers_buf, .dsize = sizeof(num_watchers_buf),
610
0
  };
611
0
  if (wrec->watchers.count != 0) {
612
0
    my_dbufs[num_my_dbufs++] = (TDB_DATA) {
613
0
      .dptr = wrec->watchers.first, .dsize = wrec->watchers.count * DBWRAP_WATCHER_BUF_LENGTH,
614
0
    };
615
0
  }
616
0
  if (add_count != 0) {
617
0
    my_dbufs[num_my_dbufs++] = (TDB_DATA) {
618
0
      .dptr = add_buf,
619
0
      .dsize = sizeof(add_buf),
620
0
    };
621
0
  }
622
0
  if (num_dbufs != 0) {
623
0
    memcpy(my_dbufs+num_my_dbufs, dbufs, num_dbufs * sizeof(*dbufs));
624
0
    num_my_dbufs += num_dbufs;
625
0
  }
626
627
0
  SMB_ASSERT(num_my_dbufs <= ARRAY_SIZE(my_dbufs));
628
629
0
  status = dbwrap_record_storev(
630
0
    wrec->backend.rec, my_dbufs, num_my_dbufs, flags);
631
0
  return status;
632
0
}
633
634
static NTSTATUS dbwrap_watched_storev(struct db_record *rec,
635
              const TDB_DATA *dbufs, int num_dbufs,
636
              int flags)
637
0
{
638
0
  struct db_watched_record *wrec = db_record_get_watched_record(rec);
639
640
0
  return dbwrap_watched_record_storev(wrec, dbufs, num_dbufs, flags);
641
0
}
642
643
static NTSTATUS dbwrap_watched_delete(struct db_record *rec)
644
0
{
645
0
  struct db_watched_record *wrec = db_record_get_watched_record(rec);
646
647
  /*
648
   * dbwrap_watched_record_storev() will figure out
649
   * if the record should be deleted or if there are still
650
   * watchers to be stored.
651
   */
652
0
  return dbwrap_watched_record_storev(wrec, NULL, 0, 0);
653
0
}
654
655
struct dbwrap_watched_traverse_state {
656
  struct db_context *db;
657
  int (*fn)(struct db_record *rec, void *private_data);
658
  void *private_data;
659
};
660
661
static int dbwrap_watched_traverse_fn(struct db_record *rec,
662
              void *private_data)
663
0
{
664
0
  struct dbwrap_watched_traverse_state *state = private_data;
665
0
  struct db_record prec = *rec;
666
0
  bool ok;
667
668
0
  ok = dbwrap_watch_rec_parse(rec->value, NULL, NULL, &prec.value);
669
0
  if (!ok) {
670
0
    return 0;
671
0
  }
672
0
  if (prec.value.dsize == 0) {
673
0
    return 0;
674
0
  }
675
0
  prec.value_valid = true;
676
0
  prec.db = state->db;
677
678
0
  return state->fn(&prec, state->private_data);
679
0
}
680
681
static int dbwrap_watched_traverse(struct db_context *db,
682
           int (*fn)(struct db_record *rec,
683
               void *private_data),
684
           void *private_data)
685
0
{
686
0
  struct db_watched_ctx *ctx = talloc_get_type_abort(
687
0
    db->private_data, struct db_watched_ctx);
688
0
  struct dbwrap_watched_traverse_state state = {
689
0
    .db = db, .fn = fn, .private_data = private_data };
690
0
  NTSTATUS status;
691
0
  int ret;
692
693
0
  status = dbwrap_traverse(
694
0
    ctx->backend, dbwrap_watched_traverse_fn, &state, &ret);
695
0
  if (!NT_STATUS_IS_OK(status)) {
696
0
    return -1;
697
0
  }
698
0
  return ret;
699
0
}
700
701
static int dbwrap_watched_traverse_read(struct db_context *db,
702
          int (*fn)(struct db_record *rec,
703
              void *private_data),
704
          void *private_data)
705
0
{
706
0
  struct db_watched_ctx *ctx = talloc_get_type_abort(
707
0
    db->private_data, struct db_watched_ctx);
708
0
  struct dbwrap_watched_traverse_state state = {
709
0
    .db = db, .fn = fn, .private_data = private_data };
710
0
  NTSTATUS status;
711
0
  int ret;
712
713
0
  status = dbwrap_traverse_read(
714
0
    ctx->backend, dbwrap_watched_traverse_fn, &state, &ret);
715
0
  if (!NT_STATUS_IS_OK(status)) {
716
0
    return -1;
717
0
  }
718
0
  return ret;
719
0
}
720
721
static int dbwrap_watched_get_seqnum(struct db_context *db)
722
0
{
723
0
  struct db_watched_ctx *ctx = talloc_get_type_abort(
724
0
    db->private_data, struct db_watched_ctx);
725
0
  return dbwrap_get_seqnum(ctx->backend);
726
0
}
727
728
static int dbwrap_watched_transaction_start(struct db_context *db)
729
0
{
730
0
  struct db_watched_ctx *ctx = talloc_get_type_abort(
731
0
    db->private_data, struct db_watched_ctx);
732
0
  return dbwrap_transaction_start(ctx->backend);
733
0
}
734
735
static int dbwrap_watched_transaction_commit(struct db_context *db)
736
0
{
737
0
  struct db_watched_ctx *ctx = talloc_get_type_abort(
738
0
    db->private_data, struct db_watched_ctx);
739
0
  return dbwrap_transaction_commit(ctx->backend);
740
0
}
741
742
static int dbwrap_watched_transaction_cancel(struct db_context *db)
743
0
{
744
0
  struct db_watched_ctx *ctx = talloc_get_type_abort(
745
0
    db->private_data, struct db_watched_ctx);
746
0
  return dbwrap_transaction_cancel(ctx->backend);
747
0
}
748
749
struct dbwrap_watched_parse_record_state {
750
  struct db_context *db;
751
  void (*parser)(TDB_DATA key, TDB_DATA data, void *private_data);
752
  void *private_data;
753
  bool ok;
754
};
755
756
static void dbwrap_watched_parse_record_parser(TDB_DATA key, TDB_DATA data,
757
                 void *private_data)
758
0
{
759
0
  struct dbwrap_watched_parse_record_state *state = private_data;
760
0
  TDB_DATA userdata;
761
762
0
  state->ok = dbwrap_watch_rec_parse(data, NULL, NULL, &userdata);
763
0
  if (!state->ok) {
764
0
    dbwrap_watch_log_invalid_record(state->db, key, data);
765
0
    return;
766
0
  }
767
768
0
  state->parser(key, userdata, state->private_data);
769
0
}
770
771
static NTSTATUS dbwrap_watched_parse_record(
772
  struct db_context *db, TDB_DATA key,
773
  void (*parser)(TDB_DATA key, TDB_DATA data, void *private_data),
774
  void *private_data)
775
0
{
776
0
  struct db_watched_ctx *ctx = talloc_get_type_abort(
777
0
    db->private_data, struct db_watched_ctx);
778
0
  struct dbwrap_watched_parse_record_state state = {
779
0
    .db = db,
780
0
    .parser = parser,
781
0
    .private_data = private_data,
782
0
  };
783
0
  NTSTATUS status;
784
785
0
  status = dbwrap_parse_record(
786
0
    ctx->backend, key, dbwrap_watched_parse_record_parser, &state);
787
0
  if (!NT_STATUS_IS_OK(status)) {
788
0
    return status;
789
0
  }
790
0
  if (!state.ok) {
791
0
    return NT_STATUS_NOT_FOUND;
792
0
  }
793
0
  return NT_STATUS_OK;
794
0
}
795
796
static void dbwrap_watched_parse_record_done(struct tevent_req *subreq);
797
798
static struct tevent_req *dbwrap_watched_parse_record_send(
799
  TALLOC_CTX *mem_ctx,
800
  struct tevent_context *ev,
801
  struct db_context *db,
802
  TDB_DATA key,
803
  void (*parser)(TDB_DATA key, TDB_DATA data, void *private_data),
804
  void *private_data,
805
  enum dbwrap_req_state *req_state)
806
0
{
807
0
  struct db_watched_ctx *ctx = talloc_get_type_abort(
808
0
    db->private_data, struct db_watched_ctx);
809
0
  struct tevent_req *req = NULL;
810
0
  struct tevent_req *subreq = NULL;
811
0
  struct dbwrap_watched_parse_record_state *state = NULL;
812
813
0
  req = tevent_req_create(mem_ctx, &state,
814
0
        struct dbwrap_watched_parse_record_state);
815
0
  if (req == NULL) {
816
0
    *req_state = DBWRAP_REQ_ERROR;
817
0
    return NULL;
818
0
  }
819
820
0
  *state = (struct dbwrap_watched_parse_record_state) {
821
0
    .parser = parser,
822
0
    .private_data = private_data,
823
0
    .ok = true,
824
0
  };
825
826
0
  subreq = dbwrap_parse_record_send(state,
827
0
            ev,
828
0
            ctx->backend,
829
0
            key,
830
0
            dbwrap_watched_parse_record_parser,
831
0
            state,
832
0
            req_state);
833
0
  if (tevent_req_nomem(subreq, req)) {
834
0
    *req_state = DBWRAP_REQ_ERROR;
835
0
    return tevent_req_post(req, ev);
836
0
  }
837
838
0
  tevent_req_set_callback(subreq, dbwrap_watched_parse_record_done, req);
839
0
  return req;
840
0
}
841
842
static void dbwrap_watched_parse_record_done(struct tevent_req *subreq)
843
0
{
844
0
  struct tevent_req *req = tevent_req_callback_data(
845
0
    subreq, struct tevent_req);
846
0
  struct dbwrap_watched_parse_record_state *state = tevent_req_data(
847
0
    req, struct dbwrap_watched_parse_record_state);
848
0
  NTSTATUS status;
849
850
0
  status = dbwrap_parse_record_recv(subreq);
851
0
  TALLOC_FREE(subreq);
852
0
  if (tevent_req_nterror(req, status)) {
853
0
    return;
854
0
  }
855
856
0
  if (!state->ok) {
857
0
    tevent_req_nterror(req, NT_STATUS_NOT_FOUND);
858
0
    return;
859
0
  }
860
861
0
  tevent_req_done(req);
862
0
  return;
863
0
}
864
865
static NTSTATUS dbwrap_watched_parse_record_recv(struct tevent_req *req)
866
0
{
867
0
  NTSTATUS status;
868
869
0
  if (tevent_req_is_nterror(req, &status)) {
870
0
    tevent_req_received(req);
871
0
    return status;
872
0
  }
873
874
0
  tevent_req_received(req);
875
0
  return NT_STATUS_OK;
876
0
}
877
878
static int dbwrap_watched_exists(struct db_context *db, TDB_DATA key)
879
0
{
880
0
  struct db_watched_ctx *ctx = talloc_get_type_abort(
881
0
    db->private_data, struct db_watched_ctx);
882
883
0
  return dbwrap_exists(ctx->backend, key);
884
0
}
885
886
static size_t dbwrap_watched_id(struct db_context *db, uint8_t *id,
887
        size_t idlen)
888
0
{
889
0
  struct db_watched_ctx *ctx = talloc_get_type_abort(
890
0
    db->private_data, struct db_watched_ctx);
891
892
0
  return dbwrap_db_id(ctx->backend, id, idlen);
893
0
}
894
895
struct db_context *db_open_watched(TALLOC_CTX *mem_ctx,
896
           struct db_context **backend,
897
           struct messaging_context *msg)
898
0
{
899
0
  struct db_context *db;
900
0
  struct db_watched_ctx *ctx;
901
902
0
  db = talloc_zero(mem_ctx, struct db_context);
903
0
  if (db == NULL) {
904
0
    return NULL;
905
0
  }
906
0
  ctx = talloc_zero(db, struct db_watched_ctx);
907
0
  if (ctx == NULL) {
908
0
    TALLOC_FREE(db);
909
0
    return NULL;
910
0
  }
911
0
  db->private_data = ctx;
912
913
0
  ctx->msg = msg;
914
915
0
  ctx->backend = talloc_move(ctx, backend);
916
0
  db->lock_order = ctx->backend->lock_order;
917
0
  ctx->backend->lock_order = DBWRAP_LOCK_ORDER_NONE;
918
919
0
  db->fetch_locked = dbwrap_watched_fetch_locked;
920
0
  db->do_locked = dbwrap_watched_do_locked;
921
0
  db->traverse = dbwrap_watched_traverse;
922
0
  db->traverse_read = dbwrap_watched_traverse_read;
923
0
  db->get_seqnum = dbwrap_watched_get_seqnum;
924
0
  db->transaction_start = dbwrap_watched_transaction_start;
925
0
  db->transaction_commit = dbwrap_watched_transaction_commit;
926
0
  db->transaction_cancel = dbwrap_watched_transaction_cancel;
927
0
  db->parse_record = dbwrap_watched_parse_record;
928
0
  db->parse_record_send = dbwrap_watched_parse_record_send;
929
0
  db->parse_record_recv = dbwrap_watched_parse_record_recv;
930
0
  db->exists = dbwrap_watched_exists;
931
0
  db->id = dbwrap_watched_id;
932
0
  db->name = dbwrap_name(ctx->backend);
933
934
0
  return db;
935
0
}
936
937
uint64_t dbwrap_watched_watch_add_instance(struct db_record *rec)
938
0
{
939
0
  struct db_watched_record *wrec = db_record_get_watched_record(rec);
940
0
  static uint64_t global_instance = 1;
941
942
0
  SMB_ASSERT(wrec->added.instance == 0);
943
944
0
  wrec->added = (struct dbwrap_watcher) {
945
0
    .pid = wrec->self,
946
0
    .instance = global_instance++,
947
0
  };
948
949
0
  wrec->force_fini_store = true;
950
951
0
  return wrec->added.instance;
952
0
}
953
954
void dbwrap_watched_watch_remove_instance(struct db_record *rec, uint64_t instance)
955
0
{
956
0
  struct db_watched_record *wrec = db_record_get_watched_record(rec);
957
0
  struct dbwrap_watcher clear_watcher = {
958
0
    .pid = wrec->self,
959
0
    .instance = instance,
960
0
  };
961
0
  size_t i;
962
0
  struct server_id_buf buf;
963
964
0
  if (instance == 0) {
965
0
    return;
966
0
  }
967
968
0
  if (wrec->added.instance == instance) {
969
0
    SMB_ASSERT(server_id_equal(&wrec->added.pid, &wrec->self));
970
0
    DBG_DEBUG("Watcher %s:%"PRIu64" reverted from adding\n",
971
0
        server_id_str_buf(clear_watcher.pid, &buf),
972
0
        clear_watcher.instance);
973
0
    ZERO_STRUCT(wrec->added);
974
0
  }
975
976
0
  for (i=0; i < wrec->watchers.count; i++) {
977
0
    struct dbwrap_watcher watcher;
978
0
    size_t off = i*DBWRAP_WATCHER_BUF_LENGTH;
979
0
    size_t next_off;
980
0
    size_t full_len;
981
0
    size_t move_len;
982
983
0
    dbwrap_watcher_get(&watcher, wrec->watchers.first + off);
984
985
0
    if (clear_watcher.instance != watcher.instance) {
986
0
      continue;
987
0
    }
988
0
    if (!server_id_equal(&clear_watcher.pid, &watcher.pid)) {
989
0
      continue;
990
0
    }
991
992
0
    wrec->force_fini_store = true;
993
994
0
    if (i == 0) {
995
0
      DBG_DEBUG("Watcher %s:%"PRIu64" removed from first position of %zu\n",
996
0
          server_id_str_buf(clear_watcher.pid, &buf),
997
0
          clear_watcher.instance,
998
0
          wrec->watchers.count);
999
0
      wrec->watchers.first += DBWRAP_WATCHER_BUF_LENGTH;
1000
0
      wrec->watchers.count -= 1;
1001
0
      wrec->removed_first = true;
1002
0
      return;
1003
0
    }
1004
0
    if (i == (wrec->watchers.count-1)) {
1005
0
      DBG_DEBUG("Watcher %s:%"PRIu64" removed from last position of %zu\n",
1006
0
          server_id_str_buf(clear_watcher.pid, &buf),
1007
0
          clear_watcher.instance,
1008
0
          wrec->watchers.count);
1009
0
      wrec->watchers.count -= 1;
1010
0
      return;
1011
0
    }
1012
1013
0
    DBG_DEBUG("Watcher %s:%"PRIu64" cleared at position %zu from %zu\n",
1014
0
        server_id_str_buf(clear_watcher.pid, &buf),
1015
0
        clear_watcher.instance, i+1,
1016
0
        wrec->watchers.count);
1017
1018
0
    next_off = off + DBWRAP_WATCHER_BUF_LENGTH;
1019
0
    full_len = wrec->watchers.count * DBWRAP_WATCHER_BUF_LENGTH;
1020
0
    move_len = full_len - next_off;
1021
0
    memmove(wrec->watchers.first + off,
1022
0
      wrec->watchers.first + next_off,
1023
0
      move_len);
1024
0
    wrec->watchers.count -= 1;
1025
0
    return;
1026
0
  }
1027
1028
0
  DBG_DEBUG("Watcher %s:%"PRIu64" not found in %zu watchers\n",
1029
0
      server_id_str_buf(clear_watcher.pid, &buf),
1030
0
      clear_watcher.instance,
1031
0
      wrec->watchers.count);
1032
0
  return;
1033
0
}
1034
1035
void dbwrap_watched_watch_skip_alerting(struct db_record *rec)
1036
0
{
1037
0
  struct db_watched_record *wrec = db_record_get_watched_record(rec);
1038
1039
0
  wrec->wakeup.watcher = (struct dbwrap_watcher) { .instance = 0, };
1040
0
  wrec->watchers.alerted = true;
1041
0
}
1042
1043
void dbwrap_watched_watch_reset_alerting(struct db_record *rec)
1044
0
{
1045
0
  struct db_watched_record *wrec = db_record_get_watched_record(rec);
1046
1047
0
  wrec->wakeup.watcher = (struct dbwrap_watcher) { .instance = 0, };
1048
0
  wrec->watchers.alerted = false;
1049
0
}
1050
1051
void dbwrap_watched_watch_force_alerting(struct db_record *rec)
1052
0
{
1053
0
  struct db_watched_record *wrec = db_record_get_watched_record(rec);
1054
1055
0
  dbwrap_watched_record_prepare_wakeup(wrec);
1056
0
}
1057
1058
struct dbwrap_watched_watch_state {
1059
  struct db_context *db;
1060
  TDB_DATA key;
1061
  struct dbwrap_watcher watcher;
1062
  struct server_id blocker;
1063
  bool blockerdead;
1064
};
1065
1066
static bool dbwrap_watched_msg_filter(struct messaging_rec *rec,
1067
              void *private_data);
1068
static void dbwrap_watched_watch_done(struct tevent_req *subreq);
1069
static void dbwrap_watched_watch_blocker_died(struct tevent_req *subreq);
1070
static int dbwrap_watched_watch_state_destructor(
1071
  struct dbwrap_watched_watch_state *state);
1072
1073
struct tevent_req *dbwrap_watched_watch_send(TALLOC_CTX *mem_ctx,
1074
               struct tevent_context *ev,
1075
               struct db_record *rec,
1076
               uint64_t resumed_instance,
1077
               struct server_id blocker)
1078
0
{
1079
0
  struct db_context *db = dbwrap_record_get_db(rec);
1080
0
  struct db_watched_ctx *ctx = talloc_get_type_abort(
1081
0
    db->private_data, struct db_watched_ctx);
1082
0
  struct db_watched_record *wrec = db_record_get_watched_record(rec);
1083
0
  struct tevent_req *req, *subreq;
1084
0
  struct dbwrap_watched_watch_state *state;
1085
0
  uint64_t instance;
1086
1087
0
  req = tevent_req_create(mem_ctx, &state,
1088
0
        struct dbwrap_watched_watch_state);
1089
0
  if (req == NULL) {
1090
0
    return NULL;
1091
0
  }
1092
0
  state->db = db;
1093
0
  state->blocker = blocker;
1094
1095
0
  if (ctx->msg == NULL) {
1096
0
    tevent_req_nterror(req, NT_STATUS_NOT_SUPPORTED);
1097
0
    return tevent_req_post(req, ev);
1098
0
  }
1099
1100
0
  if (resumed_instance == 0 && wrec->added.instance == 0) {
1101
    /*
1102
     * Adding a new instance
1103
     */
1104
0
    instance = dbwrap_watched_watch_add_instance(rec);
1105
0
  } else if (resumed_instance != 0 && wrec->added.instance == 0) {
1106
    /*
1107
     * Resuming an existing instance that was
1108
     * already present before do_locked started
1109
     */
1110
0
    instance = resumed_instance;
1111
0
  } else if (resumed_instance == wrec->added.instance) {
1112
    /*
1113
     * The caller used dbwrap_watched_watch_add_instance()
1114
     * already during this do_locked() invocation.
1115
     */
1116
0
    instance = resumed_instance;
1117
0
  } else {
1118
0
    tevent_req_nterror(req, NT_STATUS_REQUEST_NOT_ACCEPTED);
1119
0
    return tevent_req_post(req, ev);
1120
0
  }
1121
1122
0
  state->watcher = (struct dbwrap_watcher) {
1123
0
    .pid = messaging_server_id(ctx->msg),
1124
0
    .instance = instance,
1125
0
  };
1126
1127
0
  state->key = tdb_data_talloc_copy(state, rec->key);
1128
0
  if (tevent_req_nomem(state->key.dptr, req)) {
1129
0
    return tevent_req_post(req, ev);
1130
0
  }
1131
1132
0
  subreq = messaging_filtered_read_send(
1133
0
    state, ev, ctx->msg, dbwrap_watched_msg_filter, state);
1134
0
  if (tevent_req_nomem(subreq, req)) {
1135
0
    return tevent_req_post(req, ev);
1136
0
  }
1137
0
  tevent_req_set_callback(subreq, dbwrap_watched_watch_done, req);
1138
1139
0
  talloc_set_destructor(state, dbwrap_watched_watch_state_destructor);
1140
1141
0
  if (blocker.pid != 0) {
1142
0
    subreq = server_id_watch_send(state, ev, blocker);
1143
0
    if (tevent_req_nomem(subreq, req)) {
1144
0
      return tevent_req_post(req, ev);
1145
0
    }
1146
0
    tevent_req_set_callback(
1147
0
      subreq, dbwrap_watched_watch_blocker_died, req);
1148
0
  }
1149
1150
0
  return req;
1151
0
}
1152
1153
static void dbwrap_watched_watch_blocker_died(struct tevent_req *subreq)
1154
0
{
1155
0
  struct tevent_req *req = tevent_req_callback_data(
1156
0
    subreq, struct tevent_req);
1157
0
  struct dbwrap_watched_watch_state *state = tevent_req_data(
1158
0
    req, struct dbwrap_watched_watch_state);
1159
0
  int ret;
1160
1161
0
  ret = server_id_watch_recv(subreq, NULL);
1162
0
  TALLOC_FREE(subreq);
1163
0
  if (ret != 0) {
1164
0
    tevent_req_nterror(req, map_nt_error_from_unix(ret));
1165
0
    return;
1166
0
  }
1167
0
  state->blockerdead = true;
1168
0
  tevent_req_done(req);
1169
0
}
1170
1171
static void dbwrap_watched_watch_state_destructor_fn(
1172
  struct db_record *rec,
1173
  TDB_DATA value,
1174
  void *private_data)
1175
0
{
1176
0
  struct dbwrap_watched_watch_state *state = talloc_get_type_abort(
1177
0
    private_data, struct dbwrap_watched_watch_state);
1178
1179
  /*
1180
   * Here we just remove ourself from the in memory
1181
   * watchers array and let db_watched_record_fini()
1182
   * call dbwrap_watched_record_storev() to do the magic
1183
   * of writing back the modified in memory copy.
1184
   */
1185
0
  dbwrap_watched_watch_remove_instance(rec, state->watcher.instance);
1186
0
  return;
1187
0
}
1188
1189
static int dbwrap_watched_watch_state_destructor(
1190
  struct dbwrap_watched_watch_state *state)
1191
0
{
1192
0
  NTSTATUS status;
1193
1194
0
  status = dbwrap_do_locked(
1195
0
    state->db,
1196
0
    state->key,
1197
0
    dbwrap_watched_watch_state_destructor_fn,
1198
0
    state);
1199
0
  if (!NT_STATUS_IS_OK(status)) {
1200
0
    DBG_WARNING("dbwrap_do_locked failed: %s\n",
1201
0
          nt_errstr(status));
1202
0
  }
1203
0
  return 0;
1204
0
}
1205
1206
static bool dbwrap_watched_msg_filter(struct messaging_rec *rec,
1207
              void *private_data)
1208
0
{
1209
0
  struct dbwrap_watched_watch_state *state = talloc_get_type_abort(
1210
0
    private_data, struct dbwrap_watched_watch_state);
1211
0
  uint64_t instance;
1212
1213
0
  if (rec->msg_type != MSG_DBWRAP_MODIFIED) {
1214
0
    return false;
1215
0
  }
1216
0
  if (rec->num_fds != 0) {
1217
0
    return false;
1218
0
  }
1219
1220
0
  if (rec->buf.length != sizeof(instance)) {
1221
0
    DBG_DEBUG("Got size %zu, expected %zu\n",
1222
0
        rec->buf.length,
1223
0
        sizeof(instance));
1224
0
    return false;
1225
0
  }
1226
1227
0
  instance = BVAL(rec->buf.data, 0);
1228
1229
0
  if (instance != state->watcher.instance) {
1230
0
    DBG_DEBUG("Got instance %"PRIu64", expected %"PRIu64"\n",
1231
0
        instance,
1232
0
        state->watcher.instance);
1233
0
    return false;
1234
0
  }
1235
1236
0
  return true;
1237
0
}
1238
1239
static void dbwrap_watched_watch_done(struct tevent_req *subreq)
1240
0
{
1241
0
  struct tevent_req *req = tevent_req_callback_data(
1242
0
    subreq, struct tevent_req);
1243
0
  struct dbwrap_watched_watch_state *state = tevent_req_data(
1244
0
    req, struct dbwrap_watched_watch_state);
1245
0
  struct messaging_rec *rec;
1246
0
  int ret;
1247
1248
0
  ret = messaging_filtered_read_recv(subreq, state, &rec);
1249
0
  TALLOC_FREE(subreq);
1250
0
  if (ret != 0) {
1251
0
    tevent_req_nterror(req, map_nt_error_from_unix(ret));
1252
0
    return;
1253
0
  }
1254
0
  tevent_req_done(req);
1255
0
}
1256
1257
NTSTATUS dbwrap_watched_watch_recv(struct tevent_req *req,
1258
           uint64_t *pkeep_instance,
1259
           bool *blockerdead,
1260
           struct server_id *blocker)
1261
0
{
1262
0
  struct dbwrap_watched_watch_state *state = tevent_req_data(
1263
0
    req, struct dbwrap_watched_watch_state);
1264
0
  NTSTATUS status;
1265
1266
0
  if (tevent_req_is_nterror(req, &status)) {
1267
0
    tevent_req_received(req);
1268
0
    return status;
1269
0
  }
1270
0
  if (pkeep_instance != NULL) {
1271
0
    *pkeep_instance = state->watcher.instance;
1272
    /*
1273
     * No need to remove ourselves anymore,
1274
     * the caller will take care of removing itself.
1275
     */
1276
0
    talloc_set_destructor(state, NULL);
1277
0
  }
1278
0
  if (blockerdead != NULL) {
1279
0
    *blockerdead = state->blockerdead;
1280
0
  }
1281
0
  if (blocker != NULL) {
1282
0
    *blocker = state->blocker;
1283
0
  }
1284
0
  tevent_req_received(req);
1285
0
  return NT_STATUS_OK;
1286
0
}
1287