s3:smbd: add a scavenger process for disconnected durable handles
[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
23 #include "messages.h"
24 #include "serverid.h"
25 #include "smbd/globals.h"
26 #include "smbd/scavenger.h"
27 #include "locking/proto.h"
28 #include "lib/util/util_process.h"
29
30 #undef DBGC_CLASS
31 #define DBGC_CLASS DBGC_SCAVENGER
32
33 struct smbd_scavenger_state {
34         struct tevent_context *ev;
35         struct messaging_context *msg;
36         struct server_id parent_id;
37         struct server_id *scavenger_id;
38         bool am_scavenger;
39 };
40
41 static struct smbd_scavenger_state *smbd_scavenger_state = NULL;
42
43 struct scavenger_message {
44         struct file_id file_id;
45         uint64_t open_persistent_id;
46         NTTIME until;
47 };
48
49 static int smbd_scavenger_main(struct smbd_scavenger_state *state)
50 {
51         DEBUG(10, ("scavenger: %s started, parent: %s\n",
52                    server_id_str(talloc_tos(), state->scavenger_id),
53                    server_id_str(talloc_tos(), &state->parent_id)));
54
55         while (true) {
56                 TALLOC_CTX *frame = talloc_stackframe();
57                 int ret;
58
59                 ret = tevent_loop_once(state->ev);
60                 if (ret != 0) {
61                         DEBUG(2, ("tevent_loop_once failed: %s\n",
62                                   strerror(errno)));
63                         TALLOC_FREE(frame);
64                         return 1;
65                 }
66
67                 DEBUG(10, ("scavenger: %s event loop iteration\n",
68                            server_id_str(talloc_tos(), state->scavenger_id)));
69                 TALLOC_FREE(frame);
70         }
71
72         return 0;
73 }
74
75 static void smbd_scavenger_done(struct tevent_context *event_ctx, struct tevent_fd *fde,
76                                 uint16_t flags, void *private_data)
77 {
78         struct smbd_scavenger_state *state = talloc_get_type_abort(
79                 private_data, struct smbd_scavenger_state);
80
81         DEBUG(2, ("scavenger: %s died\n",
82                   server_id_str(talloc_tos(), state->scavenger_id)));
83
84         TALLOC_FREE(state->scavenger_id);
85 }
86
87 static void smbd_scavenger_parent_dead(struct tevent_context *event_ctx,
88                                        struct tevent_fd *fde,
89                                        uint16_t flags, void *private_data)
90 {
91         struct smbd_scavenger_state *state = talloc_get_type_abort(
92                 private_data, struct smbd_scavenger_state);
93
94         DEBUG(2, ("scavenger: %s parent %s died\n",
95                   server_id_str(talloc_tos(), state->scavenger_id),
96                   server_id_str(talloc_tos(), &state->parent_id)));
97
98         exit_server("smbd_scavenger_parent_dead");
99 }
100
101 static void scavenger_sig_term_handler(struct tevent_context *ev,
102                                        struct tevent_signal *se,
103                                        int signum,
104                                        int count,
105                                        void *siginfo,
106                                        void *private_data)
107 {
108         exit_server_cleanly("termination signal");
109 }
110
111 static void scavenger_setup_sig_term_handler(struct tevent_context *ev_ctx)
112 {
113         struct tevent_signal *se;
114
115         se = tevent_add_signal(ev_ctx,
116                                ev_ctx,
117                                SIGTERM, 0,
118                                scavenger_sig_term_handler,
119                                NULL);
120         if (se == NULL) {
121                 exit_server("failed to setup SIGTERM handler");
122         }
123 }
124
125 static bool smbd_scavenger_running(struct smbd_scavenger_state *state)
126 {
127         if (state->scavenger_id == NULL) {
128                 return false;
129         }
130
131         return serverid_exists(state->scavenger_id);
132 }
133
134 static int smbd_scavenger_server_id_destructor(struct server_id *id)
135 {
136         serverid_deregister(*id);
137         return 0;
138 }
139
140 static bool scavenger_say_hello(int fd, struct server_id self)
141 {
142         const uint8_t *msg = (const uint8_t *)&self;
143         size_t remaining = sizeof(self);
144         size_t ofs = 0;
145
146         while (remaining > 0) {
147                 ssize_t ret;
148
149                 ret = sys_write(fd, msg + ofs, remaining);
150                 if (ret == -1) {
151                         DEBUG(2, ("Failed to write to pipe: %s\n",
152                                   strerror(errno)));
153                         return false;
154                 }
155                 remaining -= ret;
156         }
157
158         DEBUG(4, ("scavenger_say_hello: self[%s]\n",
159                   server_id_str(talloc_tos(), &self)));
160         return true;
161 }
162
163 static bool scavenger_wait_hello(int fd, struct server_id *child)
164 {
165         uint8_t *msg = (uint8_t *)child;
166         size_t remaining = sizeof(*child);
167         size_t ofs = 0;
168
169         while (remaining > 0) {
170                 ssize_t ret;
171
172                 ret = sys_read(fd, msg + ofs, remaining);
173                 if (ret == -1) {
174                         DEBUG(2, ("Failed to read from pipe: %s\n",
175                                   strerror(errno)));
176                         return false;
177                 }
178                 remaining -= ret;
179         }
180
181         DEBUG(4, ("scavenger_say_hello: child[%s]\n",
182                   server_id_str(talloc_tos(), child)));
183         return true;
184 }
185
186 static bool smbd_scavenger_start(struct smbd_scavenger_state *state)
187 {
188         struct server_id self = messaging_server_id(state->msg);
189         struct tevent_fd *fde = NULL;
190         int fds[2];
191         int ret;
192         uint64_t unique_id;
193         bool ok;
194
195         SMB_ASSERT(server_id_equal(&state->parent_id, &self));
196
197         if (smbd_scavenger_running(state)) {
198                 DEBUG(10, ("scavenger %s already running\n",
199                            server_id_str(talloc_tos(),
200                                          state->scavenger_id)));
201                 return true;
202         }
203
204         if (state->scavenger_id != NULL) {
205                 DEBUG(10, ("scavenger zombie %s, cleaning up\n",
206                            server_id_str(talloc_tos(),
207                                          state->scavenger_id)));
208                 TALLOC_FREE(state->scavenger_id);
209         }
210
211         state->scavenger_id = talloc_zero(state, struct server_id);
212         if (state->scavenger_id == NULL) {
213                 DEBUG(2, ("Out of memory\n"));
214                 goto fail;
215         }
216         talloc_set_destructor(state->scavenger_id,
217                               smbd_scavenger_server_id_destructor);
218
219         ret = socketpair(AF_UNIX, SOCK_STREAM, 0, fds);
220         if (ret == -1) {
221                 DEBUG(2, ("socketpair failed: %s", strerror(errno)));
222                 goto fail;
223         }
224
225         smb_set_close_on_exec(fds[0]);
226         smb_set_close_on_exec(fds[1]);
227
228         unique_id = serverid_get_random_unique_id();
229
230         ret = fork();
231         if (ret == -1) {
232                 int err = errno;
233                 close(fds[0]);
234                 close(fds[1]);
235                 DEBUG(0, ("fork failed: %s", strerror(err)));
236                 goto fail;
237         }
238
239         if (ret == 0) {
240                 /* child */
241
242                 NTSTATUS status;
243
244                 close(fds[0]);
245
246                 am_parent = NULL;
247
248                 set_my_unique_id(unique_id);
249
250                 status = reinit_after_fork(state->msg, state->ev, true);
251                 if (!NT_STATUS_IS_OK(status)) {
252                         DEBUG(2, ("reinit_after_fork failed: %s\n",
253                                   nt_errstr(status)));
254                         exit_server("reinit_after_fork failed");
255                         return false;
256                 }
257
258                 prctl_set_comment("smbd-scavenger");
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                 serverid_register(*state->scavenger_id, FLAG_MSG_GENERAL);
266
267                 ok = scavenger_say_hello(fds[1], *state->scavenger_id);
268                 if (!ok) {
269                         DEBUG(2, ("scavenger_say_hello failed\n"));
270                         exit_server("scavenger_say_hello failed");
271                         return false;
272                 }
273
274                 fde = tevent_add_fd(state->ev, state->scavenger_id,
275                                     fds[1], TEVENT_FD_READ,
276                                     smbd_scavenger_parent_dead, state);
277                 if (fde == NULL) {
278                         DEBUG(2, ("tevent_add_fd(smbd_scavenger_parent_dead) "
279                                   "failed\n"));
280                         exit_server("tevent_add_fd(smbd_scavenger_parent_dead) "
281                                     "failed");
282                         return false;
283                 }
284                 tevent_fd_set_auto_close(fde);
285
286                 ret = smbd_scavenger_main(state);
287
288                 DEBUG(10, ("scavenger ended: %d\n", ret));
289                 exit_server_cleanly("scavenger ended");
290                 return false;
291         }
292
293         /* parent */
294         close(fds[1]);
295
296         ok = scavenger_wait_hello(fds[0], state->scavenger_id);
297         if (!ok) {
298                 close(fds[0]);
299                 goto fail;
300         }
301
302         fde = tevent_add_fd(state->ev, state->scavenger_id,
303                             fds[0], TEVENT_FD_READ,
304                             smbd_scavenger_done, state);
305         if (fde == NULL) {
306                 close(fds[0]);
307                 goto fail;
308         }
309         tevent_fd_set_auto_close(fde);
310
311         return true;
312 fail:
313         TALLOC_FREE(state->scavenger_id);
314         return false;
315 }
316
317 static void scavenger_add_timer(struct smbd_scavenger_state *state,
318                                 struct scavenger_message *msg);
319
320 static void smbd_scavenger_msg(struct messaging_context *msg_ctx,
321                                void *private_data,
322                                uint32_t msg_type,
323                                struct server_id src,
324                                DATA_BLOB *data)
325 {
326         struct smbd_scavenger_state *state =
327                 talloc_get_type_abort(private_data,
328                                       struct smbd_scavenger_state);
329         TALLOC_CTX *frame = talloc_stackframe();
330         struct server_id self = messaging_server_id(msg_ctx);
331         struct scavenger_message *msg = NULL;
332
333         DEBUG(10, ("smbd_scavenger_msg: %s got message from %s\n",
334                    server_id_str(talloc_tos(), &self),
335                    server_id_str(talloc_tos(), &src)));
336
337         if (server_id_equal(&state->parent_id, &self)) {
338                 NTSTATUS status;
339
340                 if (!smbd_scavenger_running(state) &&
341                     !smbd_scavenger_start(state))
342                 {
343                         DEBUG(2, ("Failed to start scavenger\n"));
344                         goto done;
345                 }
346                 DEBUG(10, ("forwarding message to scavenger\n"));
347
348                 status = messaging_send(msg_ctx,
349                                         *state->scavenger_id, msg_type, data);
350                 if (!NT_STATUS_IS_OK(status)) {
351                         DEBUG(2, ("forwarding message to scavenger failed: "
352                                   "%s\n", nt_errstr(status)));
353                         goto done;
354                 }
355                 goto done;
356         }
357
358         if (!state->am_scavenger) {
359                 DEBUG(10, ("im not the scavenger: ignore message\n"));
360                 goto done;
361         }
362
363         if (!server_id_equal(&state->parent_id, &src)) {
364                 DEBUG(10, ("scavenger: ignore spurious message\n"));
365                 goto done;
366         }
367
368         DEBUG(10, ("scavenger: got a message\n"));
369         msg = (struct scavenger_message*)data->data;
370         scavenger_add_timer(state, msg);
371 done:
372         talloc_free(frame);
373 }
374
375 bool smbd_scavenger_init(TALLOC_CTX *mem_ctx,
376                          struct messaging_context *msg,
377                          struct tevent_context *ev)
378 {
379         struct smbd_scavenger_state *state;
380         NTSTATUS status;
381
382         if (smbd_scavenger_state) {
383                 DEBUG(10, ("smbd_scavenger_init called again\n"));
384                 return true;
385         }
386
387         state = talloc_zero(mem_ctx, struct smbd_scavenger_state);
388         if (state == NULL) {
389                 DEBUG(2, ("Out of memory\n"));
390                 return false;
391         }
392
393         state->msg = msg;
394         state->ev = ev;
395         state->parent_id = messaging_server_id(msg);
396
397         status = messaging_register(msg, state, MSG_SMB_SCAVENGER,
398                                     smbd_scavenger_msg);
399         if (!NT_STATUS_IS_OK(status)) {
400                 DEBUG(2, ("failed to register message handler: %s\n",
401                           nt_errstr(status)));
402                 goto fail;
403         }
404
405         smbd_scavenger_state = state;
406         return true;
407 fail:
408         talloc_free(state);
409         return false;
410 }
411
412 void scavenger_schedule_disconnected(struct files_struct *fsp)
413 {
414         NTSTATUS status;
415         struct server_id self = messaging_server_id(fsp->conn->sconn->msg_ctx);
416         struct timeval disconnect_time, until;
417         uint64_t timeout_usec;
418         struct scavenger_message msg;
419         DATA_BLOB msg_blob;
420
421         nttime_to_timeval(&disconnect_time, fsp->op->global->disconnect_time);
422         timeout_usec = 1000 * fsp->op->global->durable_timeout_msec;
423         until = timeval_add(&disconnect_time,
424                             timeout_usec / 1000000,
425                             timeout_usec % 1000000);
426
427         ZERO_STRUCT(msg);
428         msg.file_id = fsp->file_id;
429         msg.open_persistent_id = fsp->op->global->open_persistent_id;
430         msg.until = timeval_to_nttime(&until);
431
432         DEBUG(10, ("smbd: %s mark file %s as disconnected at %s with timeout "
433                    "at %s in %fs\n",
434                    server_id_str(talloc_tos(), &self),
435                    file_id_string_tos(&fsp->file_id),
436                    timeval_string(talloc_tos(), &disconnect_time, true),
437                    timeval_string(talloc_tos(), &until, true),
438                    fsp->op->global->durable_timeout_msec/1000.0));
439
440         SMB_ASSERT(server_id_is_disconnected(&fsp->op->global->server_id));
441         SMB_ASSERT(!server_id_equal(&self, &smbd_scavenger_state->parent_id));
442         SMB_ASSERT(!smbd_scavenger_state->am_scavenger);
443
444         msg_blob = data_blob_const(&msg, sizeof(msg));
445         DEBUG(10, ("send message to scavenger\n"));
446
447         status = messaging_send(smbd_scavenger_state->msg,
448                                 smbd_scavenger_state->parent_id,
449                                 MSG_SMB_SCAVENGER,
450                                 &msg_blob);
451         if (!NT_STATUS_IS_OK(status)) {
452                 DEBUG(2, ("Failed to send message to parent smbd %s "
453                           "from %s: %s\n",
454                           server_id_str(talloc_tos(),
455                                         &smbd_scavenger_state->parent_id),
456                           server_id_str(talloc_tos(), &self),
457                           nt_errstr(status)));
458         }
459 }
460
461 struct scavenger_timer_context {
462         struct smbd_scavenger_state *state;
463         struct scavenger_message msg;
464 };
465
466 static void scavenger_timer(struct tevent_context *ev,
467                             struct tevent_timer *te,
468                             struct timeval t, void *data)
469 {
470         struct scavenger_timer_context *ctx =
471                 talloc_get_type_abort(data, struct scavenger_timer_context);
472         NTSTATUS status;
473         bool ok;
474
475         DEBUG(10, ("scavenger: do cleanup for file %s at %s\n",
476                   file_id_string_tos(&ctx->msg.file_id),
477                   timeval_string(talloc_tos(), &t, true)));
478
479         ok = share_mode_cleanup_disconnected(ctx->msg.file_id,
480                                              ctx->msg.open_persistent_id);
481         if (!ok) {
482                 DEBUG(2, ("Failed to cleanup share modes and byte range locks "
483                           "for file %s open %lu\n",
484                           file_id_string_tos(&ctx->msg.file_id),
485                           ctx->msg.open_persistent_id));
486         }
487
488         status = smbXsrv_open_cleanup(ctx->msg.open_persistent_id);
489         if (!NT_STATUS_IS_OK(status)) {
490                 DEBUG(2, ("Failed to cleanup open global for file %s open %lu:"
491                           " %s\n", file_id_string_tos(&ctx->msg.file_id),
492                           ctx->msg.open_persistent_id, nt_errstr(status)));
493         }
494 }
495
496 static void scavenger_add_timer(struct smbd_scavenger_state *state,
497                                 struct scavenger_message *msg)
498 {
499         struct tevent_timer *te;
500         struct scavenger_timer_context *ctx;
501         struct timeval until;
502
503         nttime_to_timeval(&until, msg->until);
504
505         DEBUG(10, ("scavenger: schedule file %s for cleanup at %s\n",
506                    file_id_string_tos(&msg->file_id),
507                    timeval_string(talloc_tos(), &until, true)));
508
509         ctx = talloc_zero(state, struct scavenger_timer_context);
510         if (ctx == NULL) {
511                 DEBUG(2, ("Failed to talloc_zero(scavenger_timer_context)\n"));
512                 return;
513         }
514
515         ctx->state = state;
516         ctx->msg = *msg;
517
518         te = tevent_add_timer(state->ev,
519                               state,
520                               until,
521                               scavenger_timer,
522                               ctx);
523         if (te == NULL) {
524                 DEBUG(2, ("Failed to add scavenger_timer event\n"));
525                 talloc_free(ctx);
526                 return;
527         }
528
529         /* delete context after handler was running */
530         talloc_steal(te, ctx);
531 }