-/* Copyright (c) 2010-2018 Dovecot authors, see the included COPYING file */
+/* Copyright (c) 2010-2019 Dovecot authors, see the included COPYING file */
#include "lib.h"
#include "array.h"
execvp_const(args[0], args);
}
-static bool client_exec_script(struct master_service_connection *conn)
+static bool
+parse_input(ARRAY_TYPE(const_string)* envs, const char *const **args_r,
+ struct master_service_connection* conn, ssize_t *output_r)
{
- ARRAY_TYPE(const_string) envs;
- const char *const *args;
string_t *input;
void *buf;
size_t prev_size, scanpos;
bool header_complete = FALSE, noreply = FALSE;
- ssize_t ret;
- int status;
- pid_t pid;
- net_set_nonblock(conn->fd, FALSE);
input = t_buffer_create(IO_BLOCK_SIZE);
/* Input contains:
buf = buffer_append_space_unsafe(input, IO_BLOCK_SIZE);
/* peek in socket input buffer */
- ret = recv(conn->fd, buf, IO_BLOCK_SIZE, MSG_PEEK);
- if (ret <= 0) {
+ *output_r = recv(conn->fd, buf, IO_BLOCK_SIZE, MSG_PEEK);
+ if (*output_r <= 0) {
buffer_set_used_size(input, prev_size);
if (strchr(str_c(input), '\n') != NULL)
script_verify_version(t_strcut(str_c(input), '\n'));
- if (ret < 0)
+ if (*output_r < 0)
i_fatal("recv(MSG_PEEK) failed: %m");
i_fatal("recv(MSG_PEEK) failed: disconnected");
/* scan for final \n\n */
pos = CONST_PTR_OFFSET(input->data, scanpos);
- end = CONST_PTR_OFFSET(input->data, prev_size + ret);
+ end = CONST_PTR_OFFSET(input->data, prev_size + *output_r);
for (; pos < end; pos++) {
if (pos[-1] == '\n' && pos[0] == '\n') {
header_complete = TRUE;
scanpos = pos - (const unsigned char *)input->data;
/* read data for real (up to and including \n\n) */
- ret = recv(conn->fd, buf, scanpos-prev_size, 0);
- if (prev_size+ret != scanpos) {
- if (ret < 0)
+ *output_r = recv(conn->fd, buf, scanpos-prev_size, 0);
+ if (prev_size+(*output_r) != scanpos) {
+ if (*output_r < 0)
i_fatal("recv() failed: %m");
- if (ret == 0)
+ if (*output_r == 0)
i_fatal("recv() failed: disconnected");
i_fatal("recv() failed: size of definitive recv() differs from peek");
}
/* drop the last two LFs */
buffer_set_used_size(input, scanpos-2);
- args = t_strsplit(str_c(input), "\n");
- script_verify_version(*args); args++;
- t_array_init(&envs, 16);
- if (*args != NULL) {
+ *args_r = t_strsplit(str_c(input), "\n");
+ script_verify_version(**args_r); (*args_r)++;
+ if (**args_r != NULL) {
const char *p;
- if (str_begins(*args, "alarm=")) {
+ if (str_begins(**args_r, "alarm=")) {
unsigned int seconds;
- if (str_to_uint(*args + 6, &seconds) < 0)
+ if (str_to_uint((**args_r) + 6, &seconds) < 0)
i_fatal("invalid alarm option");
alarm(seconds);
- args++;
+ (*args_r)++;
}
- while (str_begins(*args, "env_")) {
+ while (str_begins(**args_r, "env_")) {
const char *envname, *env;
- env = t_str_tabunescape(*args+4);
+ env = t_str_tabunescape((**args_r)+4);
p = strchr(env, '=');
if (p == NULL)
i_fatal("invalid environment variable");
- envname = t_strdup_until(*args+4, p);
+ envname = t_strdup_until((**args_r)+4, p);
if (str_array_find(accepted_envs, envname))
- array_push_back(&envs, &env);
- args++;
+ array_push_back(envs, &env);
+ (*args_r)++;
}
- if (strcmp(*args, "noreply") == 0) {
+ if (strcmp(**args_r, "noreply") == 0) {
noreply = TRUE;
}
- if (**args == '\0')
+ if (***args_r == '\0')
i_fatal("empty options");
- args++;
+ (*args_r)++;
}
- array_append_zero(&envs);
+ array_append_zero(envs);
+
+ return noreply;
+}
+
+static bool client_exec_script(struct master_service_connection *conn)
+{
+ ARRAY_TYPE(const_string) envs;
+ const char *const *args = NULL;
+ ssize_t ret;
+ int status;
+ pid_t pid;
+
+ t_array_init(&envs, 16);
+
+ net_set_nonblock(conn->fd, FALSE);
- if (noreply) {
- /* no need to fork and check exit status */
+ if (parse_input(&envs, &args, conn, &ret)) {
+ /* parse_input returns TRUE if noreply is set in the input.
+ * In that case there is no need to fork and check exit
+ * status */
exec_child(conn, args, array_front(&envs));
i_unreached();
}