2 Unix SMB/CIFS implementation.
5 Copyright (C) Gregor Beck 2013
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.
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.
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/>.
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"
36 #define DBGC_CLASS DBGC_SCAVENGER
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
;
46 static struct smbd_scavenger_state
*smbd_scavenger_state
= NULL
;
48 struct scavenger_message
{
49 struct file_id file_id
;
50 uint64_t open_persistent_id
;
54 static int smbd_scavenger_main(struct smbd_scavenger_state
*state
)
56 struct server_id_buf tmp1
, tmp2
;
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
)));
63 TALLOC_CTX
*frame
= talloc_stackframe();
66 ret
= tevent_loop_once(state
->ev
);
68 DEBUG(2, ("tevent_loop_once failed: %s\n",
74 DEBUG(10, ("scavenger: %s event loop iteration\n",
75 server_id_str_buf(*state
->scavenger_id
, &tmp1
)));
82 static void smbd_scavenger_done(struct tevent_context
*event_ctx
, struct tevent_fd
*fde
,
83 uint16_t flags
, void *private_data
)
85 struct smbd_scavenger_state
*state
= talloc_get_type_abort(
86 private_data
, struct smbd_scavenger_state
);
87 struct server_id_buf tmp
;
89 DEBUG(2, ("scavenger: %s died\n",
90 server_id_str_buf(*state
->scavenger_id
, &tmp
)));
92 TALLOC_FREE(state
->scavenger_id
);
95 static void smbd_scavenger_parent_dead(struct tevent_context
*event_ctx
,
96 struct tevent_fd
*fde
,
97 uint16_t flags
, void *private_data
)
99 struct smbd_scavenger_state
*state
= talloc_get_type_abort(
100 private_data
, struct smbd_scavenger_state
);
101 struct server_id_buf tmp1
, tmp2
;
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
)));
107 exit_server("smbd_scavenger_parent_dead");
110 static void scavenger_sig_term_handler(struct tevent_context
*ev
,
111 struct tevent_signal
*se
,
117 exit_server_cleanly("termination signal");
120 static void scavenger_setup_sig_term_handler(struct tevent_context
*ev_ctx
)
122 struct tevent_signal
*se
;
124 se
= tevent_add_signal(ev_ctx
,
127 scavenger_sig_term_handler
,
130 exit_server("failed to setup SIGTERM handler");
134 static bool smbd_scavenger_running(struct smbd_scavenger_state
*state
)
136 if (state
->scavenger_id
== NULL
) {
140 return serverid_exists(state
->scavenger_id
);
143 static int smbd_scavenger_server_id_destructor(struct server_id
*id
)
148 static bool scavenger_say_hello(int fd
, struct server_id self
)
151 struct server_id_buf tmp
;
153 ret
= write_data(fd
, &self
, sizeof(self
));
155 DEBUG(2, ("Failed to write to pipe: %s\n", strerror(errno
)));
158 if (ret
< sizeof(self
)) {
159 DBG_WARNING("Could not write serverid\n");
163 DEBUG(4, ("scavenger_say_hello: self[%s]\n",
164 server_id_str_buf(self
, &tmp
)));
168 static bool scavenger_wait_hello(int fd
, struct server_id
*child
)
170 struct server_id_buf tmp
;
173 ret
= read_data(fd
, child
, sizeof(struct server_id
));
175 DEBUG(2, ("Failed to read from pipe: %s\n",
179 if (ret
< sizeof(struct server_id
)) {
180 DBG_WARNING("Could not read serverid\n");
184 DEBUG(4, ("scavenger_say_hello: child[%s]\n",
185 server_id_str_buf(*child
, &tmp
)));
189 static bool smbd_scavenger_start(struct smbd_scavenger_state
*state
)
191 struct server_id self
= messaging_server_id(state
->msg
);
192 struct tevent_fd
*fde
= NULL
;
197 SMB_ASSERT(server_id_equal(&state
->parent_id
, &self
));
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
,
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
,
212 TALLOC_FREE(state
->scavenger_id
);
215 state
->scavenger_id
= talloc_zero(state
, struct server_id
);
216 if (state
->scavenger_id
== NULL
) {
217 DEBUG(2, ("Out of memory\n"));
220 talloc_set_destructor(state
->scavenger_id
,
221 smbd_scavenger_server_id_destructor
);
223 ret
= socketpair(AF_UNIX
, SOCK_STREAM
, 0, fds
);
225 DEBUG(2, ("socketpair failed: %s", strerror(errno
)));
229 smb_set_close_on_exec(fds
[0]);
230 smb_set_close_on_exec(fds
[1]);
237 DEBUG(0, ("fork failed: %s", strerror(err
)));
248 status
= smbd_reinit_after_fork(state
->msg
, state
->ev
,
250 if (!NT_STATUS_IS_OK(status
)) {
251 DEBUG(2, ("reinit_after_fork failed: %s\n",
253 exit_server("reinit_after_fork failed");
257 process_set_title("smbd-scavenger", "scavenger");
260 state
->am_scavenger
= true;
261 *state
->scavenger_id
= messaging_server_id(state
->msg
);
263 scavenger_setup_sig_term_handler(state
->ev
);
265 ok
= scavenger_say_hello(fds
[1], *state
->scavenger_id
);
267 DEBUG(2, ("scavenger_say_hello failed\n"));
268 exit_server("scavenger_say_hello failed");
272 fde
= tevent_add_fd(state
->ev
, state
->scavenger_id
,
273 fds
[1], TEVENT_FD_READ
,
274 smbd_scavenger_parent_dead
, state
);
276 DEBUG(2, ("tevent_add_fd(smbd_scavenger_parent_dead) "
278 exit_server("tevent_add_fd(smbd_scavenger_parent_dead) "
282 tevent_fd_set_auto_close(fde
);
284 ret
= smbd_scavenger_main(state
);
286 DEBUG(10, ("scavenger ended: %d\n", ret
));
287 exit_server_cleanly("scavenger ended");
294 ok
= scavenger_wait_hello(fds
[0], state
->scavenger_id
);
300 fde
= tevent_add_fd(state
->ev
, state
->scavenger_id
,
301 fds
[0], TEVENT_FD_READ
,
302 smbd_scavenger_done
, state
);
307 tevent_fd_set_auto_close(fde
);
311 TALLOC_FREE(state
->scavenger_id
);
315 static void scavenger_add_timer(struct smbd_scavenger_state
*state
,
316 struct scavenger_message
*msg
);
318 static void smbd_scavenger_msg(struct messaging_context
*msg_ctx
,
321 struct server_id src
,
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
;
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
)));
336 if (server_id_equal(&state
->parent_id
, &self
)) {
339 if (!smbd_scavenger_running(state
) &&
340 !smbd_scavenger_start(state
))
342 DEBUG(2, ("Failed to start scavenger\n"));
345 DEBUG(10, ("forwarding message to scavenger\n"));
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
)));
357 if (!state
->am_scavenger
) {
358 DEBUG(10, ("im not the scavenger: ignore message\n"));
362 if (!server_id_equal(&state
->parent_id
, &src
)) {
363 DEBUG(10, ("scavenger: ignore spurious message\n"));
367 DEBUG(10, ("scavenger: got a message\n"));
368 msg
= (struct scavenger_message
*)data
->data
;
369 scavenger_add_timer(state
, msg
);
374 bool smbd_scavenger_init(TALLOC_CTX
*mem_ctx
,
375 struct messaging_context
*msg
,
376 struct tevent_context
*ev
)
378 struct smbd_scavenger_state
*state
;
381 if (smbd_scavenger_state
) {
382 DEBUG(10, ("smbd_scavenger_init called again\n"));
386 state
= talloc_zero(mem_ctx
, struct smbd_scavenger_state
);
388 DEBUG(2, ("Out of memory\n"));
394 state
->parent_id
= messaging_server_id(msg
);
396 status
= messaging_register(msg
, state
, MSG_SMB_SCAVENGER
,
398 if (!NT_STATUS_IS_OK(status
)) {
399 DEBUG(2, ("failed to register message handler: %s\n",
404 smbd_scavenger_state
= state
;
411 void scavenger_schedule_disconnected(struct files_struct
*fsp
)
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
;
419 struct server_id_buf tmp
;
420 struct file_id_buf idbuf
;
422 if (fsp
->op
== NULL
) {
425 nttime_to_timeval(&disconnect_time
, fsp
->op
->global
->disconnect_time
);
426 timeout_usec
= 1000 * fsp
->op
->global
->durable_timeout_msec
;
427 until
= timeval_add(&disconnect_time
,
428 timeout_usec
/ 1000000,
429 timeout_usec
% 1000000);
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
);
436 DEBUG(10, ("smbd: %s mark file %s as disconnected at %s with timeout "
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));
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
);
448 msg_blob
= data_blob_const(&msg
, sizeof(msg
));
449 DEBUG(10, ("send message to scavenger\n"));
451 status
= messaging_send(smbd_scavenger_state
->msg
,
452 smbd_scavenger_state
->parent_id
,
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 "
459 server_id_str_buf(smbd_scavenger_state
->parent_id
,
461 server_id_str_buf(self
, &tmp2
),
466 struct scavenger_timer_context
{
467 struct smbd_scavenger_state
*state
;
468 struct scavenger_message msg
;
471 struct cleanup_disconnected_state
{
473 struct share_mode_lock
*lck
;
474 uint64_t open_persistent_id
;
475 size_t num_disconnected
;
476 bool found_connected
;
479 static bool cleanup_disconnected_lease(struct share_mode_entry
*e
,
482 struct cleanup_disconnected_state
*state
= private_data
;
485 status
= leases_db_del(&e
->client_guid
, &e
->lease_key
, &state
->fid
);
487 if (!NT_STATUS_IS_OK(status
)) {
488 DBG_DEBUG("leases_db_del failed: %s\n",
495 static bool share_mode_find_connected_fn(
496 struct share_mode_entry
*e
,
500 struct cleanup_disconnected_state
*state
= private_data
;
503 disconnected
= server_id_is_disconnected(&e
->pid
);
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
),
513 server_id_str_buf(e
->pid
, &tmp2
));
515 state
->found_connected
= true;
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
),
530 state
->open_persistent_id
);
532 state
->found_connected
= true;
536 state
->num_disconnected
+= 1;
541 static bool cleanup_disconnected_share_mode_entry_fn(
542 struct share_mode_entry
*e
,
546 struct cleanup_disconnected_state
*state
= private_data
;
550 disconnected
= server_id_is_disconnected(&e
->pid
);
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
),
560 server_id_str_buf(e
->pid
, &tmp2
));
562 smb_panic(__location__
);
566 * Setting e->stale = true is
567 * the indication to delete the entry.
573 static bool share_mode_cleanup_disconnected(
574 struct file_id fid
, uint64_t open_persistent_id
)
576 struct cleanup_disconnected_state state
= {
578 .open_persistent_id
= open_persistent_id
581 TALLOC_CTX
*frame
= talloc_stackframe();
583 struct file_id_buf idbuf
;
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
));
592 name
= share_mode_filename(frame
, state
.lck
);
594 ok
= share_mode_forall_entries(
595 state
.lck
, share_mode_find_connected_fn
, &state
);
597 DBG_DEBUG("share_mode_forall_entries failed\n");
600 if (state
.found_connected
) {
601 DBG_DEBUG("Found connected entry\n");
605 ok
= share_mode_forall_leases(
606 state
.lck
, cleanup_disconnected_lease
, &state
);
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
),
619 ok
= brl_cleanup_disconnected(fid
, open_persistent_id
);
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
),
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
),
641 ok
= share_mode_forall_entries(
642 state
.lck
, cleanup_disconnected_share_mode_entry_fn
, &state
);
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
),
662 static void scavenger_timer(struct tevent_context
*ev
,
663 struct tevent_timer
*te
,
664 struct timeval t
, void *data
)
666 struct scavenger_timer_context
*ctx
=
667 talloc_get_type_abort(data
, struct scavenger_timer_context
);
668 struct file_id_buf idbuf
;
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));
676 ok
= share_mode_cleanup_disconnected(ctx
->msg
.file_id
,
677 ctx
->msg
.open_persistent_id
);
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
);
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 "
689 file_id_str_buf(ctx
->msg
.file_id
, &idbuf
),
690 ctx
->msg
.open_persistent_id
,
695 static void scavenger_add_timer(struct smbd_scavenger_state
*state
,
696 struct scavenger_message
*msg
)
698 struct tevent_timer
*te
;
699 struct scavenger_timer_context
*ctx
;
700 struct timeval until
;
701 struct file_id_buf idbuf
;
703 nttime_to_timeval(&until
, msg
->until
);
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));
709 ctx
= talloc_zero(state
, struct scavenger_timer_context
);
711 DEBUG(2, ("Failed to talloc_zero(scavenger_timer_context)\n"));
718 te
= tevent_add_timer(state
->ev
,
724 DEBUG(2, ("Failed to add scavenger_timer event\n"));
729 /* delete context after handler was running */
730 talloc_steal(te
, ctx
);