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/>.
25 #include "smbd/globals.h"
26 #include "smbd/scavenger.h"
27 #include "locking/proto.h"
28 #include "lib/util/util_process.h"
29 #include "lib/sys_rw.h"
32 #define DBGC_CLASS DBGC_SCAVENGER
34 struct smbd_scavenger_state
{
35 struct tevent_context
*ev
;
36 struct messaging_context
*msg
;
37 struct server_id parent_id
;
38 struct server_id
*scavenger_id
;
42 static struct smbd_scavenger_state
*smbd_scavenger_state
= NULL
;
44 struct scavenger_message
{
45 struct file_id file_id
;
46 uint64_t open_persistent_id
;
50 static int smbd_scavenger_main(struct smbd_scavenger_state
*state
)
52 DEBUG(10, ("scavenger: %s started, parent: %s\n",
53 server_id_str(talloc_tos(), state
->scavenger_id
),
54 server_id_str(talloc_tos(), &state
->parent_id
)));
57 TALLOC_CTX
*frame
= talloc_stackframe();
60 ret
= tevent_loop_once(state
->ev
);
62 DEBUG(2, ("tevent_loop_once failed: %s\n",
68 DEBUG(10, ("scavenger: %s event loop iteration\n",
69 server_id_str(talloc_tos(), state
->scavenger_id
)));
76 static void smbd_scavenger_done(struct tevent_context
*event_ctx
, struct tevent_fd
*fde
,
77 uint16_t flags
, void *private_data
)
79 struct smbd_scavenger_state
*state
= talloc_get_type_abort(
80 private_data
, struct smbd_scavenger_state
);
82 DEBUG(2, ("scavenger: %s died\n",
83 server_id_str(talloc_tos(), state
->scavenger_id
)));
85 TALLOC_FREE(state
->scavenger_id
);
88 static void smbd_scavenger_parent_dead(struct tevent_context
*event_ctx
,
89 struct tevent_fd
*fde
,
90 uint16_t flags
, void *private_data
)
92 struct smbd_scavenger_state
*state
= talloc_get_type_abort(
93 private_data
, struct smbd_scavenger_state
);
95 DEBUG(2, ("scavenger: %s parent %s died\n",
96 server_id_str(talloc_tos(), state
->scavenger_id
),
97 server_id_str(talloc_tos(), &state
->parent_id
)));
99 exit_server("smbd_scavenger_parent_dead");
102 static void scavenger_sig_term_handler(struct tevent_context
*ev
,
103 struct tevent_signal
*se
,
109 exit_server_cleanly("termination signal");
112 static void scavenger_setup_sig_term_handler(struct tevent_context
*ev_ctx
)
114 struct tevent_signal
*se
;
116 se
= tevent_add_signal(ev_ctx
,
119 scavenger_sig_term_handler
,
122 exit_server("failed to setup SIGTERM handler");
126 static bool smbd_scavenger_running(struct smbd_scavenger_state
*state
)
128 if (state
->scavenger_id
== NULL
) {
132 return serverid_exists(state
->scavenger_id
);
135 static int smbd_scavenger_server_id_destructor(struct server_id
*id
)
137 serverid_deregister(*id
);
141 static bool scavenger_say_hello(int fd
, struct server_id self
)
143 const uint8_t *msg
= (const uint8_t *)&self
;
144 size_t remaining
= sizeof(self
);
147 while (remaining
> 0) {
150 ret
= sys_write(fd
, msg
+ ofs
, remaining
);
152 DEBUG(2, ("Failed to write to pipe: %s\n",
159 DEBUG(4, ("scavenger_say_hello: self[%s]\n",
160 server_id_str(talloc_tos(), &self
)));
164 static bool scavenger_wait_hello(int fd
, struct server_id
*child
)
166 uint8_t *msg
= (uint8_t *)child
;
167 size_t remaining
= sizeof(*child
);
170 while (remaining
> 0) {
173 ret
= sys_read(fd
, msg
+ ofs
, remaining
);
175 DEBUG(2, ("Failed to read from pipe: %s\n",
182 DEBUG(4, ("scavenger_say_hello: child[%s]\n",
183 server_id_str(talloc_tos(), child
)));
187 static bool smbd_scavenger_start(struct smbd_scavenger_state
*state
)
189 struct server_id self
= messaging_server_id(state
->msg
);
190 struct tevent_fd
*fde
= NULL
;
196 SMB_ASSERT(server_id_equal(&state
->parent_id
, &self
));
198 if (smbd_scavenger_running(state
)) {
199 DEBUG(10, ("scavenger %s already running\n",
200 server_id_str(talloc_tos(),
201 state
->scavenger_id
)));
205 if (state
->scavenger_id
!= NULL
) {
206 DEBUG(10, ("scavenger zombie %s, cleaning up\n",
207 server_id_str(talloc_tos(),
208 state
->scavenger_id
)));
209 TALLOC_FREE(state
->scavenger_id
);
212 state
->scavenger_id
= talloc_zero(state
, struct server_id
);
213 if (state
->scavenger_id
== NULL
) {
214 DEBUG(2, ("Out of memory\n"));
217 talloc_set_destructor(state
->scavenger_id
,
218 smbd_scavenger_server_id_destructor
);
220 ret
= socketpair(AF_UNIX
, SOCK_STREAM
, 0, fds
);
222 DEBUG(2, ("socketpair failed: %s", strerror(errno
)));
226 smb_set_close_on_exec(fds
[0]);
227 smb_set_close_on_exec(fds
[1]);
229 unique_id
= serverid_get_random_unique_id();
236 DEBUG(0, ("fork failed: %s", strerror(err
)));
249 set_my_unique_id(unique_id
);
251 status
= reinit_after_fork(state
->msg
, state
->ev
, true);
252 if (!NT_STATUS_IS_OK(status
)) {
253 DEBUG(2, ("reinit_after_fork failed: %s\n",
255 exit_server("reinit_after_fork failed");
259 prctl_set_comment("smbd-scavenger");
261 state
->am_scavenger
= true;
262 *state
->scavenger_id
= messaging_server_id(state
->msg
);
264 scavenger_setup_sig_term_handler(state
->ev
);
266 serverid_register(*state
->scavenger_id
, FLAG_MSG_GENERAL
);
268 ok
= scavenger_say_hello(fds
[1], *state
->scavenger_id
);
270 DEBUG(2, ("scavenger_say_hello failed\n"));
271 exit_server("scavenger_say_hello failed");
275 fde
= tevent_add_fd(state
->ev
, state
->scavenger_id
,
276 fds
[1], TEVENT_FD_READ
,
277 smbd_scavenger_parent_dead
, state
);
279 DEBUG(2, ("tevent_add_fd(smbd_scavenger_parent_dead) "
281 exit_server("tevent_add_fd(smbd_scavenger_parent_dead) "
285 tevent_fd_set_auto_close(fde
);
287 ret
= smbd_scavenger_main(state
);
289 DEBUG(10, ("scavenger ended: %d\n", ret
));
290 exit_server_cleanly("scavenger ended");
297 ok
= scavenger_wait_hello(fds
[0], state
->scavenger_id
);
303 fde
= tevent_add_fd(state
->ev
, state
->scavenger_id
,
304 fds
[0], TEVENT_FD_READ
,
305 smbd_scavenger_done
, state
);
310 tevent_fd_set_auto_close(fde
);
314 TALLOC_FREE(state
->scavenger_id
);
318 static void scavenger_add_timer(struct smbd_scavenger_state
*state
,
319 struct scavenger_message
*msg
);
321 static void smbd_scavenger_msg(struct messaging_context
*msg_ctx
,
324 struct server_id src
,
327 struct smbd_scavenger_state
*state
=
328 talloc_get_type_abort(private_data
,
329 struct smbd_scavenger_state
);
330 TALLOC_CTX
*frame
= talloc_stackframe();
331 struct server_id self
= messaging_server_id(msg_ctx
);
332 struct scavenger_message
*msg
= NULL
;
334 DEBUG(10, ("smbd_scavenger_msg: %s got message from %s\n",
335 server_id_str(talloc_tos(), &self
),
336 server_id_str(talloc_tos(), &src
)));
338 if (server_id_equal(&state
->parent_id
, &self
)) {
341 if (!smbd_scavenger_running(state
) &&
342 !smbd_scavenger_start(state
))
344 DEBUG(2, ("Failed to start scavenger\n"));
347 DEBUG(10, ("forwarding message to scavenger\n"));
349 status
= messaging_send(msg_ctx
,
350 *state
->scavenger_id
, msg_type
, data
);
351 if (!NT_STATUS_IS_OK(status
)) {
352 DEBUG(2, ("forwarding message to scavenger failed: "
353 "%s\n", nt_errstr(status
)));
359 if (!state
->am_scavenger
) {
360 DEBUG(10, ("im not the scavenger: ignore message\n"));
364 if (!server_id_equal(&state
->parent_id
, &src
)) {
365 DEBUG(10, ("scavenger: ignore spurious message\n"));
369 DEBUG(10, ("scavenger: got a message\n"));
370 msg
= (struct scavenger_message
*)data
->data
;
371 scavenger_add_timer(state
, msg
);
376 bool smbd_scavenger_init(TALLOC_CTX
*mem_ctx
,
377 struct messaging_context
*msg
,
378 struct tevent_context
*ev
)
380 struct smbd_scavenger_state
*state
;
383 if (smbd_scavenger_state
) {
384 DEBUG(10, ("smbd_scavenger_init called again\n"));
388 state
= talloc_zero(mem_ctx
, struct smbd_scavenger_state
);
390 DEBUG(2, ("Out of memory\n"));
396 state
->parent_id
= messaging_server_id(msg
);
398 status
= messaging_register(msg
, state
, MSG_SMB_SCAVENGER
,
400 if (!NT_STATUS_IS_OK(status
)) {
401 DEBUG(2, ("failed to register message handler: %s\n",
406 smbd_scavenger_state
= state
;
413 void scavenger_schedule_disconnected(struct files_struct
*fsp
)
416 struct server_id self
= messaging_server_id(fsp
->conn
->sconn
->msg_ctx
);
417 struct timeval disconnect_time
, until
;
418 uint64_t timeout_usec
;
419 struct scavenger_message msg
;
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(talloc_tos(), &self
),
439 file_id_string_tos(&fsp
->file_id
),
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 DEBUG(2, ("Failed to send message to parent smbd %s "
458 server_id_str(talloc_tos(),
459 &smbd_scavenger_state
->parent_id
),
460 server_id_str(talloc_tos(), &self
),
465 struct scavenger_timer_context
{
466 struct smbd_scavenger_state
*state
;
467 struct scavenger_message msg
;
470 static void scavenger_timer(struct tevent_context
*ev
,
471 struct tevent_timer
*te
,
472 struct timeval t
, void *data
)
474 struct scavenger_timer_context
*ctx
=
475 talloc_get_type_abort(data
, struct scavenger_timer_context
);
479 DEBUG(10, ("scavenger: do cleanup for file %s at %s\n",
480 file_id_string_tos(&ctx
->msg
.file_id
),
481 timeval_string(talloc_tos(), &t
, true)));
483 ok
= share_mode_cleanup_disconnected(ctx
->msg
.file_id
,
484 ctx
->msg
.open_persistent_id
);
486 DEBUG(2, ("Failed to cleanup share modes and byte range locks "
487 "for file %s open %llu\n",
488 file_id_string_tos(&ctx
->msg
.file_id
),
489 (unsigned long long)ctx
->msg
.open_persistent_id
));
492 status
= smbXsrv_open_cleanup(ctx
->msg
.open_persistent_id
);
493 if (!NT_STATUS_IS_OK(status
)) {
494 DEBUG(2, ("Failed to cleanup open global for file %s open %llu:"
495 " %s\n", file_id_string_tos(&ctx
->msg
.file_id
),
496 (unsigned long long)ctx
->msg
.open_persistent_id
,
501 static void scavenger_add_timer(struct smbd_scavenger_state
*state
,
502 struct scavenger_message
*msg
)
504 struct tevent_timer
*te
;
505 struct scavenger_timer_context
*ctx
;
506 struct timeval until
;
508 nttime_to_timeval(&until
, msg
->until
);
510 DEBUG(10, ("scavenger: schedule file %s for cleanup at %s\n",
511 file_id_string_tos(&msg
->file_id
),
512 timeval_string(talloc_tos(), &until
, true)));
514 ctx
= talloc_zero(state
, struct scavenger_timer_context
);
516 DEBUG(2, ("Failed to talloc_zero(scavenger_timer_context)\n"));
523 te
= tevent_add_timer(state
->ev
,
529 DEBUG(2, ("Failed to add scavenger_timer event\n"));
534 /* delete context after handler was running */
535 talloc_steal(te
, ctx
);