Index: include/zbxexec.h
===================================================================
--- include/zbxexec.h	(revision 50496)
+++ include/zbxexec.h	(working copy)
@@ -21,6 +21,7 @@
 #define ZABBIX_ZBXEXEC_H
 
 int	zbx_execute(const char *command, char **buffer, char *error, size_t max_error_len, int timeout);
+int	zbx_execute_v(char **command, char **buffer, char *error, size_t max_error_len, int timeout);
 int	zbx_execute_nowait(const char *command);
 
 #endif
Index: src/zabbix_server/alerter/alerter.c
===================================================================
--- src/zabbix_server/alerter/alerter.c	(revision 50496)
+++ src/zabbix_server/alerter/alerter.c	(working copy)
@@ -87,28 +87,24 @@
 	}
 	else if (MEDIA_TYPE_EXEC == mediatype->type)
 	{
-		char	*cmd = NULL, *send_to, *subject, *message, *output = NULL;
+		char	*cmd_v[5] = {NULL, NULL, NULL, NULL, NULL};
+		char	*cmd = NULL, *output = NULL;
 		size_t	cmd_alloc = ZBX_KIBIBYTE, cmd_offset = 0;
+		size_t	cmd0_alloc = ZBX_KIBIBYTE, cmd0_offset = 0;
 
-		cmd = zbx_malloc(cmd, cmd_alloc);
+		cmd_v[0] = zbx_malloc(cmd_v[0], cmd0_alloc);
 
-		zbx_snprintf_alloc(&cmd, &cmd_alloc, &cmd_offset, "%s/%s",
+		zbx_snprintf_alloc(&cmd_v[0], &cmd0_alloc, &cmd0_offset, "%s/%s",
 				CONFIG_ALERT_SCRIPTS_PATH, mediatype->exec_path);
 
-		if (0 == access(cmd, X_OK))
+		cmd_v[1] = alert->sendto;
+		cmd_v[2] = alert->subject;
+		cmd_v[3] = alert->message;
+		cmd_v[4] = NULL;
+
+		if (0 == access(cmd_v[0], X_OK))
 		{
-			send_to = zbx_dyn_escape_string(alert->sendto, "\"\\");
-			subject = zbx_dyn_escape_string(alert->subject, "\"\\");
-			message = zbx_dyn_escape_string(alert->message, "\"\\");
-
-			zbx_snprintf_alloc(&cmd, &cmd_alloc, &cmd_offset, " \"%s\" \"%s\" \"%s\"",
-					send_to, subject, message);
-
-			zbx_free(send_to);
-			zbx_free(subject);
-			zbx_free(message);
-
-			if (SUCCEED == (res = zbx_execute(cmd, &output, error, max_error_len, ALARM_ACTION_TIMEOUT)))
+			if (SUCCEED == (res = zbx_execute_v(cmd_v, &output, error, max_error_len, ALARM_ACTION_TIMEOUT)))
 			{
 				zabbix_log(LOG_LEVEL_DEBUG, "%s output:\n%s", mediatype->exec_path, output);
 				zbx_free(output);
@@ -117,9 +113,15 @@
 				res = FAIL;
 		}
 		else
+		{
+			cmd = zbx_malloc(cmd, cmd_alloc);
+			zbx_snprintf_alloc(&cmd, &cmd_alloc, &cmd_offset, "%s '%s' '%s' '%s'",
+					cmd_v[0], cmd_v[1], cmd_v[2], cmd_v[3]);
 			zbx_snprintf(error, max_error_len, "%s: %s", cmd, zbx_strerror(errno));
+			zbx_free(cmd);
+		}
 
-		zbx_free(cmd);
+		zbx_free(cmd_v[0]);
 	}
 	else
 	{
Index: src/libs/zbxexec/execute.c
===================================================================
--- src/libs/zbxexec/execute.c	(revision 50496)
+++ src/libs/zbxexec/execute.c	(working copy)
@@ -184,6 +184,93 @@
 
 /******************************************************************************
  *                                                                            *
+ * Function: zbx_popen_v                                                      *
+ *                                                                            *
+ * Purpose: this function opens a process by creating a pipe, forking,        *
+ *          and invoking the shell                                            *
+ *          for avoiding command injection attack, this execute program       *
+ *          directly from execvp() without calling "/bin/sh"                  *
+ *                                                                            *
+ * Parameters: pid       - [OUT] child process PID                            *
+ *             command_v - [IN] a array of const pointer to a null-terminated *
+ *                         string containing a shell command args             *
+ *                                                                            *
+ * Return value: on success, reading file descriptor is returned. On error,   *
+ *               -1 is returned, and errno is set appropriately               *
+ *                                                                            *
+ * Author: Takanori Suzuki                                                    *
+ *                                                                            *
+ ******************************************************************************/
+static int	zbx_popen_v(pid_t *pid, char *const *command_v)
+{
+	const char	*__function_name = "zbx_popen_v";
+	int		fd[2];
+	char		*connected_cmd = NULL;
+	size_t  	connected_cmd_alloc = ZBX_KIBIBYTE, connected_cmd_offset = 0;
+	int		i = 0;
+
+	zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __function_name);
+
+	connected_cmd = zbx_malloc(connected_cmd, connected_cmd_alloc);
+	for (i = 0; NULL != command_v[i]; i++)
+	{
+		if (0 == i)
+			zbx_strcpy_alloc(&connected_cmd, &connected_cmd_alloc, &connected_cmd_offset, command_v[i]);
+		else
+		{
+			zbx_strcpy_alloc(&connected_cmd, &connected_cmd_alloc, &connected_cmd_offset, " '");
+			zbx_strcpy_alloc(&connected_cmd, &connected_cmd_alloc, &connected_cmd_offset, command_v[i]);
+			zbx_strcpy_alloc(&connected_cmd, &connected_cmd_alloc, &connected_cmd_offset, "'");
+		}
+	}
+	zabbix_log(LOG_LEVEL_DEBUG, "command:'%s'", connected_cmd);
+
+	if (-1 == pipe(fd))
+		return -1;
+
+	if (-1 == (*pid = zbx_fork()))
+	{
+		close(fd[0]);
+		close(fd[1]);
+		return -1;
+	}
+
+	if (0 != *pid)	/* parent process */
+	{
+		close(fd[1]);
+		zbx_free(connected_cmd);
+
+		zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%d", __function_name, fd[0]);
+
+		return fd[0];
+	}
+
+	/* child process */
+	close(fd[0]);
+	dup2(fd[1], STDOUT_FILENO);
+	dup2(fd[1], STDERR_FILENO);
+	close(fd[1]);
+
+	/* set the child as the process group leader, otherwise orphans may be left after timeout */
+	if (-1 == setpgid(0, 0))
+	{
+		zabbix_log(LOG_LEVEL_ERR, "%s(): failed to create a process group: %s",
+				__function_name, zbx_strerror(errno));
+		exit(EXIT_SUCCESS);
+	}
+
+	zabbix_log(LOG_LEVEL_DEBUG, "%s(): executing script", __function_name);
+
+	execvp(command_v[0], command_v);
+
+	/* execvp() returns only when an error occurs */
+	zabbix_log(LOG_LEVEL_WARNING, "execvp() failed for [%s]: %s", connected_cmd, zbx_strerror(errno));
+	zbx_free(connected_cmd);
+	exit(EXIT_SUCCESS);
+}
+
+/******************************************************************************
+ *                                                                            *
  * Function: zbx_waitpid                                                      *
  *                                                                            *
  * Purpose: this function waits for process to change state                   *
@@ -449,6 +536,99 @@
 
 /******************************************************************************
  *                                                                            *
+ * Function: zbx_execute_v                                                    *
+ *                                                                            *
+ * Purpose: this function executes a script and returns result from stdout    *
+ *          for avoiding command injection, it directly uses execvp() from    *
+ *          zbx_popen_v() without calling "/bin/sh"                           *
+ *          this is called only from server or proxy, so Windows is not       *
+ *          supported yet                                                     *
+ * Parameters: command_v     - [IN] command for execution in array            *
+ *             buffer        - [OUT] buffer for output, if NULL - ignored     *
+ *             error         - [OUT] error string if function fails           *
+ *             max_error_len - [IN] length of error buffer                    *
+ *                                                                            *
+ * Return value: SUCCEED if processed successfully, TIMEOUT_ERROR if          *
+ *               timeout occurred or FAIL otherwise                           *
+ *                                                                            *
+ * Author: Takanori Suzuki                                                    *
+ *                                                                            *
+ ******************************************************************************/
+int	zbx_execute_v(char **command_v, char **buffer, char *error, size_t max_error_len, int timeout)
+{
+	size_t			buf_size = PIPE_BUFFER_SIZE, offset = 0;
+	int			ret = FAIL;
+#ifndef _WINDOWS
+	pid_t			pid;
+	int			fd;
+
+	*error = '\0';
+
+	if (NULL != buffer)
+	{
+		*buffer = zbx_realloc(*buffer, buf_size);
+		**buffer = '\0';
+	}
+
+	alarm(timeout);
+
+	if (-1 != (fd = zbx_popen_v(&pid, command_v)))
+	{
+		int	rc = 0;
+		char	tmp_buf[PIPE_BUFFER_SIZE];
+
+		if (NULL != buffer)
+		{
+			while (0 < (rc = read(fd, tmp_buf, sizeof(tmp_buf) - 1)) &&
+					MAX_EXECUTE_OUTPUT_LEN > offset + rc)
+			{
+				tmp_buf[rc] = '\0';
+				zbx_strcpy_alloc(buffer, &buf_size, &offset, tmp_buf);
+			}
+		}
+
+		close(fd);
+
+		if (-1 == rc || -1 == zbx_waitpid(pid))
+		{
+			if (EINTR == errno)
+				ret = TIMEOUT_ERROR;
+			else
+				zbx_snprintf(error, max_error_len, "zbx_waitpid() failed: %s", zbx_strerror(errno));
+
+			/* kill the whole process group, pid must be the leader */
+			if (-1 == kill(-pid, SIGTERM))
+				zabbix_log(LOG_LEVEL_ERR, "failed to kill [%s]: %s", command_v[0], zbx_strerror(errno));
+
+			zbx_waitpid(pid);
+		}
+		else if (MAX_EXECUTE_OUTPUT_LEN <= offset + rc)
+		{
+			zabbix_log(LOG_LEVEL_ERR, "command output exceeded limit of %d KB",
+					MAX_EXECUTE_OUTPUT_LEN / ZBX_KIBIBYTE);
+		}
+		else
+			ret = SUCCEED;
+	}
+	else
+		zbx_strlcpy(error, zbx_strerror(errno), max_error_len);
+
+	alarm(0);
+
+	if (TIMEOUT_ERROR == ret)
+		zbx_strlcpy(error, "Timeout while executing a shell script.", max_error_len);
+	else if ('\0' != *error)
+		zabbix_log(LOG_LEVEL_WARNING, "%s", error);
+
+	if (SUCCEED != ret && NULL != buffer)
+		zbx_free(*buffer);
+
+#endif	/* not _WINDOWS */
+	return ret;
+}
+
+/******************************************************************************
+ *                                                                            *
  * Function: zbx_execute_nowait                                               *
  *                                                                            *
  * Purpose: this function executes a script in the background and             *
