diff options
author | Volker Lendecke <vl@samba.org> | 2012-03-23 13:43:39 +0100 |
---|---|---|
committer | Volker Lendecke <vl@samba.org> | 2012-04-17 10:21:00 +0200 |
commit | 1e1b6f79b3ef4ac684df4f7dfbbf949e6c8976a9 (patch) | |
tree | fa318679f2a6324b34fb2f122162e4ba7c730948 /source3/lib | |
parent | a49ac23a10bfe4098cb46b39d3152571a2562a38 (diff) | |
download | samba-1e1b6f79b3ef4ac684df4f7dfbbf949e6c8976a9.tar.gz samba-1e1b6f79b3ef4ac684df4f7dfbbf949e6c8976a9.tar.bz2 samba-1e1b6f79b3ef4ac684df4f7dfbbf949e6c8976a9.zip |
s3: Add infrastructure for background jobs
Diffstat (limited to 'source3/lib')
-rw-r--r-- | source3/lib/background.c | 233 | ||||
-rw-r--r-- | source3/lib/background.h | 39 |
2 files changed, 272 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); +} diff --git a/source3/lib/background.h b/source3/lib/background.h new file mode 100644 index 0000000000..ccfd62bfce --- /dev/null +++ b/source3/lib/background.h @@ -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 |