From: Timo Sirainen Date: Wed, 2 Jun 2010 17:46:26 +0000 (+0100) Subject: Added "script" service that can be used to easily execute programs via UNIX socket. X-Git-Tag: 2.0.beta6~69 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=cbfdc79faac95adc8a3f76ee2216d3fea0c19826;p=thirdparty%2Fdovecot%2Fcore.git Added "script" service that can be used to easily execute programs via UNIX socket. --HG-- branch : HEAD --- diff --git a/.hgignore b/.hgignore index 436b6f1c7a..bce7596164 100644 --- a/.hgignore +++ b/.hgignore @@ -86,6 +86,7 @@ src/util/imap-utf7 src/util/listview src/util/maildirlock src/util/rawlog +src/util/script src/util/script-login src/util/tcpwrap src/plugins/quota/rquota_xdr.c diff --git a/src/util/Makefile.am b/src/util/Makefile.am index 4f4bfdabb3..34df8b20d6 100644 --- a/src/util/Makefile.am +++ b/src/util/Makefile.am @@ -2,6 +2,7 @@ pkglibexecdir = $(libexecdir)/dovecot pkglibexec_PROGRAMS = \ rawlog \ + script \ script-login \ $(TCPWRAP_BIN) \ gdbhelper \ @@ -32,6 +33,11 @@ script_login_DEPENDENCIES = $(LIBDOVECOT_STORAGE) $(LIBDOVECOT_DEPS) script_login_SOURCES = \ script-login.c +script_LDADD = $(LIBDOVECOT) $(MODULE_LIBS) +script_DEPENDENCIES = $(LIBDOVECOT_DEPS) +script_SOURCES = \ + script.c + if TCPWRAPPERS TCPWRAP_BIN = tcpwrap tcpwrap_LDADD = $(LIBDOVECOT) $(MODULE_LIBS) $(LIBWRAP_LIBS) diff --git a/src/util/script.c b/src/util/script.c new file mode 100644 index 0000000000..0d0a85c425 --- /dev/null +++ b/src/util/script.c @@ -0,0 +1,125 @@ +/* Copyright (c) 2010 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "array.h" +#include "str.h" +#include "execv-const.h" +#include "master-interface.h" +#include "master-service.h" + +#include +#include + +#define SCRIPT_MAJOR_VERSION 1 +#define SCRIPT_READ_TIMEOUT_SECS 10 + +static ARRAY_TYPE(const_string) exec_args; + +static void script_verify_version(const char *line) +{ + if (line == NULL || + !version_string_verify(line, "script", SCRIPT_MAJOR_VERSION)) { + i_fatal("Client not compatible with this binary " + "(connecting to wrong socket?)"); + } +} + +static void client_connected(struct master_service_connection *conn) +{ + const unsigned char *end; + const char *const *args; + buffer_t *input; + void *buf; + unsigned int i, socket_count; + size_t prev_size; + ssize_t ret; + + net_set_nonblock(conn->fd, FALSE); + input = buffer_create_dynamic(pool_datastack_create(), IO_BLOCK_SIZE); + + /* Input contains: + + VERSION .. + arg 1 + arg 2 + ... + */ + alarm(SCRIPT_READ_TIMEOUT_SECS); + do { + prev_size = input->used; + buf = buffer_append_space_unsafe(input, IO_BLOCK_SIZE); + ret = read(conn->fd, buf, IO_BLOCK_SIZE); + if (ret <= 0) { + buffer_set_used_size(input, prev_size); + if (strchr(str_c(input), '\n') != NULL) + script_verify_version(t_strcut(str_c(input), '\t')); + + if (ret < 0) + i_fatal("read() failed: %m"); + else + i_fatal("read() failed: disconnected"); + } + buffer_set_used_size(input, prev_size + ret); + end = CONST_PTR_OFFSET(input->data, input->used); + } while (!(end[-1] == '\n' && (input->used == 1 || end[-2] == '\n'))); + + /* drop the last LF */ + buffer_set_used_size(input, input->used - 1); + + args = t_strsplit(str_c(input), "\n"); + script_verify_version(*args); + + for (args++; *args != NULL; args++) + array_append(&exec_args, args, 1); + (void)array_append_space(&exec_args); + + /* close all fds */ + socket_count = master_service_get_socket_count(master_service); + for (i = 0; i < socket_count; i++) { + if (close(MASTER_LISTEN_FD_FIRST + i) < 0) + i_error("close(listener) failed: %m"); + } + if (close(MASTER_STATUS_FD) < 0) + i_error("close(status) failed: %m"); + if (close(conn->fd) < 0) + i_error("close() failed: %m"); + + args = array_idx(&exec_args, 0); + execvp_const(args[0], args); +} + +int main(int argc, char *argv[]) +{ + const char *binary; + int i; + + master_service = master_service_init("script", 0, &argc, &argv, NULL); + if (master_getopt(master_service) > 0) + return FATAL_DEFAULT; + argc -= optind; + argv += optind; + + master_service_init_log(master_service, "script: "); + master_service_init_finish(master_service); + master_service_set_service_count(master_service, 1); + + if (argv[0] == NULL) + i_fatal("Missing script path"); + + if (argv[0][0] == '/') + binary = argv[0]; + else + binary = t_strconcat(PKG_LIBEXECDIR"/", argv[0], NULL); + + i_array_init(&exec_args, argc + 16); + array_append(&exec_args, &binary, 1); + for (i = 1; i < argc; i++) { + const char *arg = argv[i]; + + array_append(&exec_args, &arg, 1); + } + + master_service_run(master_service, client_connected); + master_service_deinit(&master_service); + return 0; +}