]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
Added "script" service that can be used to easily execute programs via UNIX socket.
authorTimo Sirainen <tss@iki.fi>
Wed, 2 Jun 2010 17:46:26 +0000 (18:46 +0100)
committerTimo Sirainen <tss@iki.fi>
Wed, 2 Jun 2010 17:46:26 +0000 (18:46 +0100)
--HG--
branch : HEAD

.hgignore
src/util/Makefile.am
src/util/script.c [new file with mode: 0644]

index 436b6f1c7a75076d37ce1aeacbddda27cb25291b..bce75961643fc7100e8a31fdd612266bb7780616 100644 (file)
--- 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
index 4f4bfdabb3b49e103c022c31f23e87dabdf1d839..34df8b20d6d50d779972a70dd69a977a88d46d8b 100644 (file)
@@ -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 (file)
index 0000000..0d0a85c
--- /dev/null
@@ -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 <stdlib.h>
+#include <unistd.h>
+
+#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 .. <lf>
+          arg 1 <lf>
+          arg 2 <lf>
+          ...
+          <lf> */
+       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;
+}