summaryrefslogtreecommitdiff
path: root/source3/lib/background.c
diff options
context:
space:
mode:
authorVolker Lendecke <vl@samba.org>2012-03-23 13:43:39 +0100
committerVolker Lendecke <vl@samba.org>2012-04-17 10:21:00 +0200
commit1e1b6f79b3ef4ac684df4f7dfbbf949e6c8976a9 (patch)
treefa318679f2a6324b34fb2f122162e4ba7c730948 /source3/lib/background.c
parenta49ac23a10bfe4098cb46b39d3152571a2562a38 (diff)
downloadsamba-1e1b6f79b3ef4ac684df4f7dfbbf949e6c8976a9.tar.gz
samba-1e1b6f79b3ef4ac684df4f7dfbbf949e6c8976a9.tar.bz2
samba-1e1b6f79b3ef4ac684df4f7dfbbf949e6c8976a9.zip
s3: Add infrastructure for background jobs
Diffstat (limited to 'source3/lib/background.c')
-rw-r--r--source3/lib/background.c233
1 files changed, 233 insertions, 0 deletions
diff --git a/source3/lib/background.c b/source3/lib/background.c
new file mode 100644
index 0000000000..aa2a77d59a
--- /dev/null
+++ b/source3/lib/background.c
@@ -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);
+}