ldb: move struct ldb_debug_ops to ldb_private.h
[samba.git] / source3 / smbd / scavenger.c
1 /*
2    Unix SMB/CIFS implementation.
3    smbd scavenger daemon
4
5    Copyright (C) Gregor Beck                    2013
6
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 3 of the License, or
10    (at your option) any later version.
11
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    You should have received a copy of the GNU General Public License
18    along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include "includes.h"
22 #include "messages.h"
23 #include "serverid.h"
24 #include "smbd/globals.h"
25 #include "smbd/smbXsrv_open.h"
26 #include "smbd/scavenger.h"
27 #include "locking/share_mode_lock.h"
28 #include "locking/leases_db.h"
29 #include "locking/proto.h"
30 #include "librpc/gen_ndr/open_files.h"
31 #include "lib/util/server_id.h"
32 #include "lib/util/util_process.h"
33 #include "lib/util/sys_rw_data.h"
34
35 #undef DBGC_CLASS
36 #define DBGC_CLASS DBGC_SCAVENGER
37
38 struct smbd_scavenger_state {
39         struct tevent_context *ev;
40         struct messaging_context *msg;
41         struct server_id parent_id;
42         struct server_id *scavenger_id;
43         bool am_scavenger;
44 };
45
46 static struct smbd_scavenger_state *smbd_scavenger_state = NULL;
47
48 struct scavenger_message {
49         struct file_id file_id;
50         uint64_t open_persistent_id;
51         NTTIME until;
52 };
53
54 static int smbd_scavenger_main(struct smbd_scavenger_state *state)
55 {
56         struct server_id_buf tmp1, tmp2;
57
58         DEBUG(10, ("scavenger: %s started, parent: %s\n",
59                    server_id_str_buf(*state->scavenger_id, &tmp1),
60                    server_id_str_buf(state->parent_id, &tmp2)));
61
62         while (true) {
63                 TALLOC_CTX *frame = talloc_stackframe();
64                 int ret;
65
66                 ret = tevent_loop_once(state->ev);
67                 if (ret != 0) {
68                         DEBUG(2, ("tevent_loop_once failed: %s\n",
69                                   strerror(errno)));
70                         TALLOC_FREE(frame);
71                         return 1;
72                 }
73
74                 DEBUG(10, ("scavenger: %s event loop iteration\n",
75                            server_id_str_buf(*state->scavenger_id, &tmp1)));
76                 TALLOC_FREE(frame);
77         }
78
79         return 0;
80 }
81
82 static void smbd_scavenger_done(struct tevent_context *event_ctx, struct tevent_fd *fde,
83                                 uint16_t flags, void *private_data)
84 {
85         struct smbd_scavenger_state *state = talloc_get_type_abort(
86                 private_data, struct smbd_scavenger_state);
87         struct server_id_buf tmp;
88
89         DEBUG(2, ("scavenger: %s died\n",
90                   server_id_str_buf(*state->scavenger_id, &tmp)));
91
92         TALLOC_FREE(state->scavenger_id);
93 }
94
95 static void smbd_scavenger_parent_dead(struct tevent_context *event_ctx,
96                                        struct tevent_fd *fde,
97                                        uint16_t flags, void *private_data)
98 {
99         struct smbd_scavenger_state *state = talloc_get_type_abort(
100                 private_data, struct smbd_scavenger_state);
101         struct server_id_buf tmp1, tmp2;
102
103         DEBUG(2, ("scavenger: %s parent %s died\n",
104                   server_id_str_buf(*state->scavenger_id, &tmp1),
105                   server_id_str_buf(state->parent_id, &tmp2)));
106
107         exit_server_cleanly("smbd_scavenger_parent_dead");
108 }
109
110 static void scavenger_sig_term_handler(struct tevent_context *ev,
111                                        struct tevent_signal *se,
112                                        int signum,
113                                        int count,
114                                        void *siginfo,
115                                        void *private_data)
116 {
117         exit_server_cleanly("termination signal");
118 }
119
120 static void scavenger_setup_sig_term_handler(struct tevent_context *ev_ctx)
121 {
122         struct tevent_signal *se;
123
124         se = tevent_add_signal(ev_ctx,
125                                ev_ctx,
126                                SIGTERM, 0,
127                                scavenger_sig_term_handler,
128                                NULL);
129         if (se == NULL) {
130                 exit_server("failed to setup SIGTERM handler");
131         }
132 }
133
134 static bool smbd_scavenger_running(struct smbd_scavenger_state *state)
135 {
136         if (state->scavenger_id == NULL) {
137                 return false;
138         }
139
140         return serverid_exists(state->scavenger_id);
141 }
142
143 static int smbd_scavenger_server_id_destructor(struct server_id *id)
144 {
145         return 0;
146 }
147
148 static bool scavenger_say_hello(int fd, struct server_id self)
149 {
150         ssize_t ret;
151         struct server_id_buf tmp;
152
153         ret = write_data(fd, &self, sizeof(self));
154         if (ret == -1) {
155                 DEBUG(2, ("Failed to write to pipe: %s\n", strerror(errno)));
156                 return false;
157         }
158         if (ret < sizeof(self)) {
159                 DBG_WARNING("Could not write serverid\n");
160                 return false;
161         }
162
163         DEBUG(4, ("scavenger_say_hello: self[%s]\n",
164                   server_id_str_buf(self, &tmp)));
165         return true;
166 }
167
168 static bool scavenger_wait_hello(int fd, struct server_id *child)
169 {
170         struct server_id_buf tmp;
171         ssize_t ret;
172
173         ret = read_data(fd, child, sizeof(struct server_id));
174         if (ret == -1) {
175                 DEBUG(2, ("Failed to read from pipe: %s\n",
176                           strerror(errno)));
177                 return false;
178         }
179         if (ret < sizeof(struct server_id)) {
180                 DBG_WARNING("Could not read serverid\n");
181                 return false;
182         }
183
184         DEBUG(4, ("scavenger_say_hello: child[%s]\n",
185                   server_id_str_buf(*child, &tmp)));
186         return true;
187 }
188
189 static bool smbd_scavenger_start(struct smbd_scavenger_state *state)
190 {
191         struct server_id self = messaging_server_id(state->msg);
192         struct tevent_fd *fde = NULL;
193         int fds[2];
194         int ret;
195         bool ok;
196
197         SMB_ASSERT(server_id_equal(&state->parent_id, &self));
198
199         if (smbd_scavenger_running(state)) {
200                 struct server_id_buf tmp;
201                 DEBUG(10, ("scavenger %s already running\n",
202                            server_id_str_buf(*state->scavenger_id,
203                                              &tmp)));
204                 return true;
205         }
206
207         if (state->scavenger_id != NULL) {
208                 struct server_id_buf tmp;
209                 DEBUG(10, ("scavenger zombie %s, cleaning up\n",
210                            server_id_str_buf(*state->scavenger_id,
211                                              &tmp)));
212                 TALLOC_FREE(state->scavenger_id);
213         }
214
215         state->scavenger_id = talloc_zero(state, struct server_id);
216         if (state->scavenger_id == NULL) {
217                 DEBUG(2, ("Out of memory\n"));
218                 goto fail;
219         }
220         talloc_set_destructor(state->scavenger_id,
221                               smbd_scavenger_server_id_destructor);
222
223         ret = socketpair(AF_UNIX, SOCK_STREAM, 0, fds);
224         if (ret == -1) {
225                 DEBUG(2, ("socketpair failed: %s\n", strerror(errno)));
226                 goto fail;
227         }
228
229         smb_set_close_on_exec(fds[0]);
230         smb_set_close_on_exec(fds[1]);
231
232         ret = fork();
233         if (ret == -1) {
234                 int err = errno;
235                 close(fds[0]);
236                 close(fds[1]);
237                 DEBUG(0, ("fork failed: %s\n", strerror(err)));
238                 goto fail;
239         }
240
241         if (ret == 0) {
242                 /* child */
243
244                 NTSTATUS status;
245
246                 close(fds[0]);
247
248                 status = smbd_reinit_after_fork(state->msg, state->ev,
249                                                 true);
250                 if (!NT_STATUS_IS_OK(status)) {
251                         DEBUG(2, ("reinit_after_fork failed: %s\n",
252                                   nt_errstr(status)));
253                         exit_server("reinit_after_fork failed");
254                         return false;
255                 }
256
257                 process_set_title("smbd-scavenger", "scavenger");
258                 reopen_logs();
259
260                 state->am_scavenger = true;
261                 *state->scavenger_id = messaging_server_id(state->msg);
262
263                 scavenger_setup_sig_term_handler(state->ev);
264
265                 ok = scavenger_say_hello(fds[1], *state->scavenger_id);
266                 if (!ok) {
267                         DEBUG(2, ("scavenger_say_hello failed\n"));
268                         exit_server("scavenger_say_hello failed");
269                         return false;
270                 }
271
272                 fde = tevent_add_fd(state->ev, state->scavenger_id,
273                                     fds[1], TEVENT_FD_READ,
274                                     smbd_scavenger_parent_dead, state);
275                 if (fde == NULL) {
276                         DEBUG(2, ("tevent_add_fd(smbd_scavenger_parent_dead) "
277                                   "failed\n"));
278                         exit_server("tevent_add_fd(smbd_scavenger_parent_dead) "
279                                     "failed");
280                         return false;
281                 }
282                 tevent_fd_set_auto_close(fde);
283
284                 ret = smbd_scavenger_main(state);
285
286                 DEBUG(10, ("scavenger ended: %d\n", ret));
287                 exit_server_cleanly("scavenger ended");
288                 return false;
289         }
290
291         /* parent */
292         close(fds[1]);
293
294         ok = scavenger_wait_hello(fds[0], state->scavenger_id);
295         if (!ok) {
296                 close(fds[0]);
297                 goto fail;
298         }
299
300         fde = tevent_add_fd(state->ev, state->scavenger_id,
301                             fds[0], TEVENT_FD_READ,
302                             smbd_scavenger_done, state);
303         if (fde == NULL) {
304                 close(fds[0]);
305                 goto fail;
306         }
307         tevent_fd_set_auto_close(fde);
308
309         return true;
310 fail:
311         TALLOC_FREE(state->scavenger_id);
312         return false;
313 }
314
315 static void scavenger_add_timer(struct smbd_scavenger_state *state,
316                                 struct scavenger_message *msg);
317
318 static void smbd_scavenger_msg(struct messaging_context *msg_ctx,
319                                void *private_data,
320                                uint32_t msg_type,
321                                struct server_id src,
322                                DATA_BLOB *data)
323 {
324         struct smbd_scavenger_state *state =
325                 talloc_get_type_abort(private_data,
326                                       struct smbd_scavenger_state);
327         TALLOC_CTX *frame = talloc_stackframe();
328         struct server_id self = messaging_server_id(msg_ctx);
329         struct scavenger_message *msg = NULL;
330         struct server_id_buf tmp1, tmp2;
331
332         DEBUG(10, ("smbd_scavenger_msg: %s got message from %s\n",
333                    server_id_str_buf(self, &tmp1),
334                    server_id_str_buf(src, &tmp2)));
335
336         if (server_id_equal(&state->parent_id, &self)) {
337                 NTSTATUS status;
338
339                 if (!smbd_scavenger_running(state) &&
340                     !smbd_scavenger_start(state))
341                 {
342                         DEBUG(2, ("Failed to start scavenger\n"));
343                         goto done;
344                 }
345                 DEBUG(10, ("forwarding message to scavenger\n"));
346
347                 status = messaging_send(msg_ctx,
348                                         *state->scavenger_id, msg_type, data);
349                 if (!NT_STATUS_IS_OK(status)) {
350                         DEBUG(2, ("forwarding message to scavenger failed: "
351                                   "%s\n", nt_errstr(status)));
352                         goto done;
353                 }
354                 goto done;
355         }
356
357         if (!state->am_scavenger) {
358                 DEBUG(10, ("im not the scavenger: ignore message\n"));
359                 goto done;
360         }
361
362         if (!server_id_equal(&state->parent_id, &src)) {
363                 DEBUG(10, ("scavenger: ignore spurious message\n"));
364                 goto done;
365         }
366
367         DEBUG(10, ("scavenger: got a message\n"));
368         msg = (struct scavenger_message*)data->data;
369         scavenger_add_timer(state, msg);
370 done:
371         talloc_free(frame);
372 }
373
374 bool smbd_scavenger_init(TALLOC_CTX *mem_ctx,
375                          struct messaging_context *msg,
376                          struct tevent_context *ev)
377 {
378         struct smbd_scavenger_state *state;
379         NTSTATUS status;
380
381         if (smbd_scavenger_state) {
382                 DEBUG(10, ("smbd_scavenger_init called again\n"));
383                 return true;
384         }
385
386         state = talloc_zero(mem_ctx, struct smbd_scavenger_state);
387         if (state == NULL) {
388                 DEBUG(2, ("Out of memory\n"));
389                 return false;
390         }
391
392         state->msg = msg;
393         state->ev = ev;
394         state->parent_id = messaging_server_id(msg);
395
396         status = messaging_register(msg, state, MSG_SMB_SCAVENGER,
397                                     smbd_scavenger_msg);
398         if (!NT_STATUS_IS_OK(status)) {
399                 DEBUG(2, ("failed to register message handler: %s\n",
400                           nt_errstr(status)));
401                 goto fail;
402         }
403
404         smbd_scavenger_state = state;
405         return true;
406 fail:
407         talloc_free(state);
408         return false;
409 }
410
411 void scavenger_schedule_disconnected(struct files_struct *fsp)
412 {
413         NTSTATUS status;
414         struct server_id self = messaging_server_id(fsp->conn->sconn->msg_ctx);
415         struct timeval disconnect_time, until;
416         uint64_t timeout_usec;
417         struct scavenger_message msg;
418         DATA_BLOB msg_blob;
419         struct server_id_buf tmp;
420         struct file_id_buf idbuf;
421
422         if (fsp->op == NULL) {
423                 return;
424         }
425         nttime_to_timeval(&disconnect_time, fsp->op->global->disconnect_time);
426         timeout_usec = UINT64_C(1000) * fsp->op->global->durable_timeout_msec;
427         until = timeval_add(&disconnect_time,
428                             timeout_usec / 1000000,
429                             timeout_usec % 1000000);
430
431         ZERO_STRUCT(msg);
432         msg.file_id = fsp->file_id;
433         msg.open_persistent_id = fsp->op->global->open_persistent_id;
434         msg.until = timeval_to_nttime(&until);
435
436         DEBUG(10, ("smbd: %s mark file %s as disconnected at %s with timeout "
437                    "at %s in %fs\n",
438                    server_id_str_buf(self, &tmp),
439                    file_id_str_buf(fsp->file_id, &idbuf),
440                    timeval_string(talloc_tos(), &disconnect_time, true),
441                    timeval_string(talloc_tos(), &until, true),
442                    fsp->op->global->durable_timeout_msec/1000.0));
443
444         SMB_ASSERT(server_id_is_disconnected(&fsp->op->global->server_id));
445         SMB_ASSERT(!server_id_equal(&self, &smbd_scavenger_state->parent_id));
446         SMB_ASSERT(!smbd_scavenger_state->am_scavenger);
447
448         msg_blob = data_blob_const(&msg, sizeof(msg));
449         DEBUG(10, ("send message to scavenger\n"));
450
451         status = messaging_send(smbd_scavenger_state->msg,
452                                 smbd_scavenger_state->parent_id,
453                                 MSG_SMB_SCAVENGER,
454                                 &msg_blob);
455         if (!NT_STATUS_IS_OK(status)) {
456                 struct server_id_buf tmp1, tmp2;
457                 DEBUG(2, ("Failed to send message to parent smbd %s "
458                           "from %s: %s\n",
459                           server_id_str_buf(smbd_scavenger_state->parent_id,
460                                             &tmp1),
461                           server_id_str_buf(self, &tmp2),
462                           nt_errstr(status)));
463         }
464 }
465
466 struct scavenger_timer_context {
467         struct smbd_scavenger_state *state;
468         struct scavenger_message msg;
469 };
470
471 struct cleanup_disconnected_state {
472         struct file_id fid;
473         struct share_mode_lock *lck;
474         uint64_t open_persistent_id;
475         size_t num_disconnected;
476         bool found_connected;
477 };
478
479 static bool cleanup_disconnected_lease(struct share_mode_entry *e,
480                                        void *private_data)
481 {
482         struct cleanup_disconnected_state *state = private_data;
483         NTSTATUS status;
484
485         status = leases_db_del(&e->client_guid, &e->lease_key, &state->fid);
486
487         if (!NT_STATUS_IS_OK(status)) {
488                 DBG_DEBUG("leases_db_del failed: %s\n",
489                           nt_errstr(status));
490         }
491
492         return false;
493 }
494
495 static bool share_mode_find_connected_fn(
496         struct share_mode_entry *e,
497         bool *modified,
498         void *private_data)
499 {
500         struct cleanup_disconnected_state *state = private_data;
501         bool disconnected;
502
503         disconnected = server_id_is_disconnected(&e->pid);
504         if (!disconnected) {
505                 char *name = share_mode_filename(talloc_tos(), state->lck);
506                 struct file_id_buf tmp1;
507                 struct server_id_buf tmp2;
508                 DBG_INFO("file (file-id='%s', servicepath='%s', name='%s') "
509                          "is used by server %s ==> do not cleanup\n",
510                          file_id_str_buf(state->fid, &tmp1),
511                          share_mode_servicepath(state->lck),
512                          name,
513                          server_id_str_buf(e->pid, &tmp2));
514                 TALLOC_FREE(name);
515                 state->found_connected = true;
516                 return true;
517         }
518
519         if (state->open_persistent_id != e->share_file_id) {
520                 char *name = share_mode_filename(talloc_tos(), state->lck);
521                 struct file_id_buf tmp;
522                 DBG_INFO("entry for file "
523                          "(file-id='%s', servicepath='%s', name='%s') "
524                          "has share_file_id %"PRIu64" but expected "
525                          "%"PRIu64"==> do not cleanup\n",
526                          file_id_str_buf(state->fid, &tmp),
527                          share_mode_servicepath(state->lck),
528                          name,
529                          e->share_file_id,
530                          state->open_persistent_id);
531                 TALLOC_FREE(name);
532                 state->found_connected = true;
533                 return true;
534         }
535
536         state->num_disconnected += 1;
537
538         return false;
539 }
540
541 static bool cleanup_disconnected_share_mode_entry_fn(
542         struct share_mode_entry *e,
543         bool *modified,
544         void *private_data)
545 {
546         struct cleanup_disconnected_state *state = private_data;
547
548         bool disconnected;
549
550         disconnected = server_id_is_disconnected(&e->pid);
551         if (!disconnected) {
552                 char *name = share_mode_filename(talloc_tos(), state->lck);
553                 struct file_id_buf tmp1;
554                 struct server_id_buf tmp2;
555                 DBG_ERR("file (file-id='%s', servicepath='%s', name='%s') "
556                         "is used by server %s ==> internal error\n",
557                         file_id_str_buf(state->fid, &tmp1),
558                         share_mode_servicepath(state->lck),
559                         name,
560                         server_id_str_buf(e->pid, &tmp2));
561                 TALLOC_FREE(name);
562                 smb_panic(__location__);
563         }
564
565         /*
566          * Setting e->stale = true is
567          * the indication to delete the entry.
568          */
569         e->stale = true;
570         return false;
571 }
572
573 static bool share_mode_cleanup_disconnected(
574         struct file_id fid, uint64_t open_persistent_id)
575 {
576         struct cleanup_disconnected_state state = {
577                 .fid = fid,
578                 .open_persistent_id = open_persistent_id
579         };
580         bool ret = false;
581         TALLOC_CTX *frame = talloc_stackframe();
582         char *name = NULL;
583         struct file_id_buf idbuf;
584         bool ok;
585
586         state.lck = get_existing_share_mode_lock(frame, fid);
587         if (state.lck == NULL) {
588                 DBG_INFO("Could not fetch share mode entry for %s\n",
589                          file_id_str_buf(fid, &idbuf));
590                 goto done;
591         }
592         name = share_mode_filename(frame, state.lck);
593
594         ok = share_mode_forall_entries(
595                 state.lck, share_mode_find_connected_fn, &state);
596         if (!ok) {
597                 DBG_DEBUG("share_mode_forall_entries failed\n");
598                 goto done;
599         }
600         if (state.found_connected) {
601                 DBG_DEBUG("Found connected entry\n");
602                 goto done;
603         }
604
605         ok = share_mode_forall_leases(
606                 state.lck, cleanup_disconnected_lease, &state);
607         if (!ok) {
608                 DBG_DEBUG("failed to clean up leases associated "
609                           "with file (file-id='%s', servicepath='%s', "
610                           "name='%s') and open_persistent_id %"PRIu64" "
611                           "==> do not cleanup\n",
612                           file_id_str_buf(fid, &idbuf),
613                           share_mode_servicepath(state.lck),
614                           name,
615                           open_persistent_id);
616                 goto done;
617         }
618
619         ok = brl_cleanup_disconnected(fid, open_persistent_id);
620         if (!ok) {
621                 DBG_DEBUG("failed to clean up byte range locks associated "
622                           "with file (file-id='%s', servicepath='%s', "
623                           "name='%s') and open_persistent_id %"PRIu64" "
624                           "==> do not cleanup\n",
625                           file_id_str_buf(fid, &idbuf),
626                           share_mode_servicepath(state.lck),
627                           name,
628                           open_persistent_id);
629                 goto done;
630         }
631
632         DBG_DEBUG("cleaning up %zu entries for file "
633                   "(file-id='%s', servicepath='%s', name='%s') "
634                   "from open_persistent_id %"PRIu64"\n",
635                   state.num_disconnected,
636                   file_id_str_buf(fid, &idbuf),
637                   share_mode_servicepath(state.lck),
638                   name,
639                   open_persistent_id);
640
641         ok = share_mode_forall_entries(
642                 state.lck, cleanup_disconnected_share_mode_entry_fn, &state);
643         if (!ok) {
644                 DBG_DEBUG("failed to clean up %zu entries associated "
645                           "with file (file-id='%s', servicepath='%s', "
646                           "name='%s') and open_persistent_id %"PRIu64" "
647                           "==> do not cleanup\n",
648                           state.num_disconnected,
649                           file_id_str_buf(fid, &idbuf),
650                           share_mode_servicepath(state.lck),
651                           name,
652                           open_persistent_id);
653                 goto done;
654         }
655
656         ret = true;
657 done:
658         talloc_free(frame);
659         return ret;
660 }
661
662 static void scavenger_timer(struct tevent_context *ev,
663                             struct tevent_timer *te,
664                             struct timeval t, void *data)
665 {
666         struct scavenger_timer_context *ctx =
667                 talloc_get_type_abort(data, struct scavenger_timer_context);
668         struct file_id_buf idbuf;
669         NTSTATUS status;
670         bool ok;
671
672         DBG_DEBUG("do cleanup for file %s at %s\n",
673                   file_id_str_buf(ctx->msg.file_id, &idbuf),
674                   timeval_string(talloc_tos(), &t, true));
675
676         ok = share_mode_cleanup_disconnected(ctx->msg.file_id,
677                                              ctx->msg.open_persistent_id);
678         if (!ok) {
679                 DBG_WARNING("Failed to cleanup share modes and byte range "
680                             "locks for file %s open %"PRIu64"\n",
681                             file_id_str_buf(ctx->msg.file_id, &idbuf),
682                             ctx->msg.open_persistent_id);
683         }
684
685         status = smbXsrv_open_cleanup(ctx->msg.open_persistent_id);
686         if (!NT_STATUS_IS_OK(status)) {
687                 DBG_WARNING("Failed to cleanup open global for file %s open "
688                             "%"PRIu64": %s\n",
689                             file_id_str_buf(ctx->msg.file_id, &idbuf),
690                             ctx->msg.open_persistent_id,
691                             nt_errstr(status));
692         }
693 }
694
695 static void scavenger_add_timer(struct smbd_scavenger_state *state,
696                                 struct scavenger_message *msg)
697 {
698         struct tevent_timer *te;
699         struct scavenger_timer_context *ctx;
700         struct timeval until;
701         struct file_id_buf idbuf;
702
703         nttime_to_timeval(&until, msg->until);
704
705         DBG_DEBUG("schedule file %s for cleanup at %s\n",
706                   file_id_str_buf(msg->file_id, &idbuf),
707                   timeval_string(talloc_tos(), &until, true));
708
709         ctx = talloc_zero(state, struct scavenger_timer_context);
710         if (ctx == NULL) {
711                 DEBUG(2, ("Failed to talloc_zero(scavenger_timer_context)\n"));
712                 return;
713         }
714
715         ctx->state = state;
716         ctx->msg = *msg;
717
718         te = tevent_add_timer(state->ev,
719                               state,
720                               until,
721                               scavenger_timer,
722                               ctx);
723         if (te == NULL) {
724                 DEBUG(2, ("Failed to add scavenger_timer event\n"));
725                 talloc_free(ctx);
726                 return;
727         }
728
729         /* delete context after handler was running */
730         talloc_steal(te, ctx);
731 }