diff options
| -rw-r--r-- | source3/Makefile.in | 1 | ||||
| -rw-r--r-- | source3/lib/background.c | 233 | ||||
| -rw-r--r-- | source3/lib/background.h | 39 | ||||
| -rwxr-xr-x | source3/wscript_build | 1 | 
4 files changed, 274 insertions, 0 deletions
diff --git a/source3/Makefile.in b/source3/Makefile.in index 21d3bf96c7..63b21506d1 100644 --- a/source3/Makefile.in +++ b/source3/Makefile.in @@ -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 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 diff --git a/source3/wscript_build b/source3/wscript_build index a51946eb64..a10ceb15d1 100755 --- a/source3/wscript_build +++ b/source3/wscript_build @@ -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  | 
