s3: Add infrastructure for background jobs
authorVolker Lendecke <vl@samba.org>
Fri, 23 Mar 2012 12:43:39 +0000 (13:43 +0100)
committerVolker Lendecke <vl@samba.org>
Tue, 17 Apr 2012 08:21:00 +0000 (10:21 +0200)
source3/Makefile.in
source3/lib/background.c [new file with mode: 0644]
source3/lib/background.h [new file with mode: 0644]
source3/wscript_build

index 21d3bf96c7d92761d260edfa8dd3a0d17353d235..63b21506d1362c380763a15d8a7cca7556121507 100644 (file)
@@ -942,6 +942,7 @@ SMBD_OBJ_SRV = smbd/server_reload.o \
               lib/sysquotas_xfs.o lib/sysquotas_4A.o \
               lib/sysquotas_nfs.o \
               lib/smbd_shim.o \
+              lib/background.o \
               smbd/fake_file.o \
               smbd/quotas.o smbd/ntquotas.o $(AFS_OBJ) smbd/msdfs.o \
               $(AFS_SETTOKEN_OBJ) smbd/aio.o smbd/statvfs.o \
diff --git a/source3/lib/background.c b/source3/lib/background.c
new file mode 100644 (file)
index 0000000..aa2a77d
--- /dev/null
@@ -0,0 +1,233 @@
+/*
+   Unix SMB/CIFS implementation.
+   Regular background jobs as forked helpers
+   Copyright (C) Volker Lendecke 2012
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "includes.h"
+#include "lib/util/tevent_ntstatus.h"
+#include "lib/async_req/async_sock.h"
+#include "include/messages.h"
+#include "background.h"
+
+struct background_job_state {
+       struct tevent_context *ev;
+       struct messaging_context *msg;
+       uint32_t *trigger_msgs;
+       size_t num_trigger_msgs;
+       bool parent_longlived;
+       int (*fn)(void *private_data);
+       void *private_data;
+
+       struct tevent_req *wakeup_req;
+       int pipe_fd;
+};
+
+static int background_job_state_destructor(struct background_job_state *s);
+static void background_job_waited(struct tevent_req *subreq);
+static void background_job_done(struct tevent_req *subreq);
+static void background_job_trigger(
+       struct messaging_context *msg, void *private_data, uint32_t msg_type,
+       struct server_id server_id, DATA_BLOB *data);
+
+struct tevent_req *background_job_send(TALLOC_CTX *mem_ctx,
+                                      struct tevent_context *ev,
+                                      struct messaging_context *msg,
+                                      uint32_t *trigger_msgs,
+                                      size_t num_trigger_msgs,
+                                      time_t initial_wait_sec,
+                                      int (*fn)(void *private_data),
+                                      void *private_data)
+{
+       struct tevent_req *req, *subreq;
+       struct background_job_state *state;
+       size_t i;
+
+       req = tevent_req_create(mem_ctx, &state,
+                               struct background_job_state);
+       if (req == NULL) {
+               return NULL;
+       }
+
+       state->ev = ev;
+       state->msg = msg;
+
+       if (num_trigger_msgs != 0) {
+               state->trigger_msgs = (uint32_t *)talloc_memdup(
+                       state, trigger_msgs,
+                       sizeof(uint32_t) * num_trigger_msgs);
+               if (tevent_req_nomem(state->trigger_msgs, req)) {
+                       return tevent_req_post(req, ev);
+               }
+               state->num_trigger_msgs = num_trigger_msgs;
+       }
+
+       state->fn = fn;
+       state->private_data = private_data;
+
+       state->pipe_fd = -1;
+       talloc_set_destructor(state, background_job_state_destructor);
+
+       for (i=0; i<num_trigger_msgs; i++) {
+               NTSTATUS status;
+               status = messaging_register(msg, state, trigger_msgs[i],
+                                           background_job_trigger);
+               if (tevent_req_nterror(req, status)) {
+                       return tevent_req_post(req, ev);
+               }
+       }
+
+       subreq = tevent_wakeup_send(
+               state, state->ev, timeval_current_ofs(initial_wait_sec, 0));
+       if (tevent_req_nomem(subreq, req)) {
+               return tevent_req_post(req, ev);
+       }
+       tevent_req_set_callback(subreq, background_job_waited, req);
+       state->wakeup_req = subreq;
+       return req;
+}
+
+static int background_job_state_destructor(struct background_job_state *state)
+{
+       size_t i;
+       if (state->pipe_fd != -1) {
+               close(state->pipe_fd);
+               state->pipe_fd = -1;
+       }
+       for (i=0; i<state->num_trigger_msgs; i++) {
+               messaging_deregister(state->msg, state->trigger_msgs[i],
+                                    state);
+       }
+       return 0;
+}
+
+static void background_job_trigger(
+       struct messaging_context *msg, void *private_data, uint32_t msg_type,
+       struct server_id server_id, DATA_BLOB *data)
+{
+       struct background_job_state *state = talloc_get_type_abort(
+               private_data, struct background_job_state);
+
+       if (state->wakeup_req != NULL) {
+               tevent_req_set_endtime(state->wakeup_req, state->ev,
+                                      timeval_zero());
+       }
+}
+
+static void background_job_waited(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct background_job_state *state = tevent_req_data(
+               req, struct background_job_state);
+       int fds[2];
+       int res;
+       bool ret;
+
+       ret = tevent_wakeup_recv(subreq);
+       TALLOC_FREE(subreq);
+       state->wakeup_req = NULL;
+       if (!ret) {
+               tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
+               return;
+       }
+
+       res = pipe(fds);
+       if (res == -1) {
+               tevent_req_nterror(req, map_nt_error_from_unix(errno));
+               return;
+       }
+
+       res = fork();
+       if (res == -1) {
+               int err = errno;
+               close(fds[0]);
+               close(fds[1]);
+               tevent_req_nterror(req, map_nt_error_from_unix(err));
+               return;
+       }
+
+       if (res == 0) {
+               /* child */
+
+               NTSTATUS status;
+               ssize_t written;
+
+               close(fds[0]);
+
+               status = reinit_after_fork(state->msg, state->ev, true);
+               if (NT_STATUS_IS_OK(status)) {
+                       res = state->fn(state->private_data);
+               } else {
+                       res = -1;
+               }
+               written = write(fds[1], &res, sizeof(res));
+               if (written == -1) {
+                       _exit(1);
+               }
+               _exit(0);
+       }
+
+       /* parent */
+
+       close(fds[1]);
+       state->pipe_fd = fds[0];
+
+       subreq = read_packet_send(state, state->ev, state->pipe_fd,
+                                 sizeof(int), NULL, NULL);
+       if (tevent_req_nomem(subreq, req)) {
+               return;
+       }
+       tevent_req_set_callback(subreq, background_job_done, req);
+}
+
+static void background_job_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct background_job_state *state = tevent_req_data(
+               req, struct background_job_state);
+       ssize_t ret;
+       uint8_t *buf;
+       int err;
+       int wait_secs;
+
+       ret = read_packet_recv(subreq, talloc_tos(), &buf, &err);
+       TALLOC_FREE(subreq);
+       if (ret == -1) {
+               tevent_req_nterror(req, map_nt_error_from_unix(err));
+               return;
+       }
+       close(state->pipe_fd);
+       state->pipe_fd = -1;
+       memcpy(&wait_secs, buf, sizeof(wait_secs));
+       if (wait_secs == -1) {
+               tevent_req_done(req);
+               return;
+       }
+       subreq = tevent_wakeup_send(
+               state, state->ev, timeval_current_ofs(wait_secs, 0));
+       if (tevent_req_nomem(subreq, req)) {
+               return;
+       }
+       tevent_req_set_callback(subreq, background_job_waited, req);
+       state->wakeup_req = subreq;
+}
+
+NTSTATUS background_job_recv(struct tevent_req *req)
+{
+       return tevent_req_simple_recv_ntstatus(req);
+}
diff --git a/source3/lib/background.h b/source3/lib/background.h
new file mode 100644 (file)
index 0000000..ccfd62b
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+   Unix SMB/CIFS implementation.
+   Regular background jobs as forked helpers
+   Copyright (C) Volker Lendecke 2012
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _LIB_BACKGROUND_H_
+#define _LIB_BACKGROUND_H_
+
+/*
+ * From a parent process regularly fork a process and execute fn(). fn()
+ * returns the number of seconds to wait before it is run next time. Returning
+ * -1 means stop the job.
+ */
+
+struct tevent_req *background_job_send(TALLOC_CTX *mem_ctx,
+                                      struct tevent_context *ev,
+                                      struct messaging_context *msg,
+                                      uint32_t *trigger_msgs,
+                                      size_t num_trigger_msgs,
+                                      time_t initial_wait_sec,
+                                      int (*fn)(void *private_data),
+                                      void *private_data);
+NTSTATUS background_job_recv(struct tevent_req *req);
+
+#endif
index a51946eb644477573e5a550b6a7742e864786982..a10ceb15d199bf2ae0b9faf3de1a05d723a23a06 100755 (executable)
@@ -360,6 +360,7 @@ SMBD_SRC_SRV = '''smbd/server_reload.c smbd/files.c smbd/connection.c
                lib/sysquotas.c lib/sysquotas_linux.c
                lib/sysquotas_xfs.c lib/sysquotas_4A.c
                lib/sysquotas_nfs.c
+               lib/background.c
                smbd/fake_file.c
                smbd/quotas.c smbd/ntquotas.c smbd/msdfs.c
                smbd/aio.c smbd/statvfs.c