Blob Blame Raw
From 63576f12524f521c0cf08d42b279654885135a90 Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose@redhat.com>
Date: Tue, 30 Jan 2018 14:39:17 +0100
Subject: [PATCH 09/23] library: add _adcli_call_external_program()

Allow adcli to call an external program given by an absolute path name
and an array of options. stdin and stdout can be used if needed.

https://bugs.freedesktop.org/show_bug.cgi?id=100118

Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
---
 configure.ac        |  28 +++++++
 library/adprivate.h |   6 ++
 library/adutil.c    | 211 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 245 insertions(+)

diff --git a/configure.ac b/configure.ac
index 221d8ae..fe86638 100644
--- a/configure.ac
+++ b/configure.ac
@@ -263,6 +263,34 @@ AC_SUBST(LCOV)
 AC_SUBST(GCOV)
 AC_SUBST(GENHTML)
 
+AC_PATH_PROG(BIN_CAT, cat, no)
+if test "$BIN_CAT" = "no" ; then
+	AC_MSG_ERROR([cat is not available])
+else
+	AC_DEFINE_UNQUOTED(BIN_CAT, "$BIN_CAT", [path to cat, used in unit test])
+fi
+
+AC_PATH_PROG(BIN_TAC, tac, no)
+if test "$BIN_TAC" = "no" ; then
+	AC_MSG_ERROR([tac is not available])
+else
+	AC_DEFINE_UNQUOTED(BIN_TAC, "$BIN_TAC", [path to tac, used in unit test])
+fi
+
+AC_PATH_PROG(BIN_REV, rev, no)
+if test "$BIN_REV" = "no" ; then
+	AC_MSG_ERROR([rev is not available])
+else
+	AC_DEFINE_UNQUOTED(BIN_REV, "$BIN_REV", [path to rev, used in unit test])
+fi
+
+AC_PATH_PROG(BIN_ECHO, echo, no)
+if test "$BIN_ECHO" = "no" ; then
+	AC_MSG_ERROR([echo is not available])
+else
+	AC_DEFINE_UNQUOTED(BIN_ECHO, "$BIN_ECHO", [path to echo, used in unit test])
+fi
+
 # ---------------------------------------------------------------------
 
 ADCLI_LT_RELEASE=$ADCLI_CURRENT:$ADCLI_REVISION:$ADCLI_AGE
diff --git a/library/adprivate.h b/library/adprivate.h
index e99f9fc..7257c93 100644
--- a/library/adprivate.h
+++ b/library/adprivate.h
@@ -285,4 +285,10 @@ struct _adcli_attrs {
 
 bool             _adcli_check_nt_time_string_lifetime (const char *nt_time_string, unsigned int lifetime);
 
+adcli_result     _adcli_call_external_program     (const char *binary,
+                                                   char * const *argv,
+                                                   const char *stdin_data,
+                                                   uint8_t **stdout_data,
+                                                   size_t *stdout_data_len);
+
 #endif /* ADPRIVATE_H_ */
diff --git a/library/adutil.c b/library/adutil.c
index 829cdd9..a27bd68 100644
--- a/library/adutil.c
+++ b/library/adutil.c
@@ -36,6 +36,7 @@
 #include <unistd.h>
 #include <stdint.h>
 #include <time.h>
+#include <sys/wait.h>
 
 static adcli_message_func message_func = NULL;
 static char last_error[2048] = { 0, };
@@ -506,6 +507,161 @@ _adcli_check_nt_time_string_lifetime (const char *nt_time_string,
 	return false;
 }
 
+adcli_result
+_adcli_call_external_program (const char *binary, char * const *argv,
+                              const char *stdin_data,
+                              uint8_t **stdout_data, size_t *stdout_data_len)
+{
+	int ret;
+	int pipefd_to_child[2] = { -1, -1};
+	int pipefd_from_child[2] = { -1, -1};
+	pid_t child_pid = 0;
+	int err;
+	size_t len;
+	ssize_t rlen;
+	pid_t wret;
+	int status;
+	uint8_t read_buf[4096];
+	uint8_t *out;
+
+	errno = 0;
+	ret = access (binary, X_OK);
+	if (ret != 0) {
+		err = errno;
+		_adcli_err ("Cannot run [%s]: [%d][%s].", binary, err,
+		                                          strerror (err));
+		ret = ADCLI_ERR_FAIL;
+		goto done;
+	}
+
+	ret = pipe (pipefd_from_child);
+	if (ret == -1) {
+		err = errno;
+		_adcli_err ("pipe failed [%d][%s].", err, strerror (err));
+		ret = ADCLI_ERR_FAIL;
+		goto done;
+	}
+
+	ret = pipe (pipefd_to_child);
+	if (ret == -1) {
+		err = errno;
+		_adcli_err ("pipe failed [%d][%s].", err, strerror (err));
+		ret = ADCLI_ERR_FAIL;
+		goto done;
+	}
+
+	child_pid = fork ();
+
+	if (child_pid == 0) { /* child */
+		close (pipefd_to_child[1]);
+		ret = dup2 (pipefd_to_child[0], STDIN_FILENO);
+		if (ret == -1) {
+			err = errno;
+			_adcli_err ("dup2 failed [%d][%s].", err,
+			                                     strerror (err));
+			exit (EXIT_FAILURE);
+		}
+
+		close (pipefd_from_child[0]);
+		ret = dup2 (pipefd_from_child[1], STDOUT_FILENO);
+		if (ret == -1) {
+			err = errno;
+			_adcli_err ("dup2 failed [%d][%s].", err,
+			                                     strerror (err));
+			exit (EXIT_FAILURE);
+		}
+
+		execv (binary, argv);
+		_adcli_err ("Failed to run %s.", binary);
+		ret = ADCLI_ERR_FAIL;
+		goto done;
+	} else if (child_pid > 0) { /* parent */
+
+		if (stdin_data != NULL) {
+			len = strlen (stdin_data);
+			ret = write (pipefd_to_child[1], stdin_data, len);
+			if (ret != len) {
+				_adcli_err ("Failed to send computer account password "
+				            "to net command.");
+				ret = ADCLI_ERR_FAIL;
+				goto done;
+			}
+		}
+
+		close (pipefd_to_child[0]);
+		pipefd_to_child[0] = -1;
+		close (pipefd_to_child[1]);
+		pipefd_to_child[0] = -1;
+
+		if (stdout_data != NULL || stdout_data_len != NULL) {
+			rlen = read (pipefd_from_child[0], read_buf, sizeof (read_buf));
+			if (rlen < 0) {
+				ret = errno;
+				_adcli_err ("Failed to read from child [%d][%s].\n",
+				            ret, strerror (ret));
+				ret = ADCLI_ERR_FAIL;
+				goto done;
+			}
+
+			out = malloc (sizeof(uint8_t) * rlen);
+			if (out == NULL) {
+				_adcli_err ("Failed to allocate memory "
+				            "for child output.");
+				ret = ADCLI_ERR_FAIL;
+				goto done;
+			} else {
+				memcpy (out, read_buf, rlen);
+			}
+
+			if (stdout_data != NULL) {
+				*stdout_data = out;
+			} else {
+				free (out);
+			}
+
+			if (stdout_data_len != NULL) {
+				*stdout_data_len = rlen;
+			}
+		}
+
+	} else {
+		_adcli_err ("Cannot run net command.");
+		ret = ADCLI_ERR_FAIL;
+		goto done;
+	}
+
+	ret = ADCLI_SUCCESS;
+
+done:
+	if (pipefd_from_child[0] != -1) {
+		close (pipefd_from_child[0]);
+	}
+	if (pipefd_from_child[1] != -1) {
+		close (pipefd_from_child[1]);
+	}
+	if (pipefd_to_child[0] != -1) {
+		close (pipefd_to_child[0]);
+	}
+	if (pipefd_to_child[1] != -1) {
+		close (pipefd_to_child[1]);
+	}
+
+	if (child_pid > 0) {
+		wret = waitpid (child_pid, &status, 0);
+		if (wret == -1) {
+			_adcli_err ("No sure what happend to net command.");
+		} else {
+			if (WIFEXITED (status)) {
+				_adcli_err ("net command failed with %d.",
+				            WEXITSTATUS (status));
+			}
+		}
+	}
+
+	return ret;
+}
+
+
 #ifdef UTIL_TESTS
 
 #include "test.h"
@@ -620,6 +776,60 @@ test_bin_sid_to_str (void)
 	free (str);
 }
 
+static void
+test_call_external_program (void)
+{
+	adcli_result res;
+	char *argv[] = { NULL, NULL, NULL };
+	uint8_t *stdout_data;
+	size_t stdout_data_len;
+
+	argv[0] = "/does/not/exists";
+	res = _adcli_call_external_program (argv[0], argv, NULL, NULL, NULL);
+	assert (res == ADCLI_ERR_FAIL);
+
+#ifdef BIN_CAT
+	argv[0] = BIN_CAT;
+	res = _adcli_call_external_program (argv[0], argv, "Hello",
+	                                    &stdout_data, &stdout_data_len);
+	assert (res == ADCLI_SUCCESS);
+	assert (strncmp ("Hello", (char *) stdout_data, stdout_data_len) == 0);
+	free (stdout_data);
+
+	res = _adcli_call_external_program (argv[0], argv, "Hello",
+	                                    NULL, NULL);
+	assert (res == ADCLI_SUCCESS);
+#endif
+
+#ifdef BIN_REV
+	argv[0] = BIN_REV;
+	res = _adcli_call_external_program (argv[0], argv, "Hello\n",
+	                                    &stdout_data, &stdout_data_len);
+	assert (res == ADCLI_SUCCESS);
+	assert (strncmp ("olleH\n", (char *) stdout_data, stdout_data_len) == 0);
+	free (stdout_data);
+#endif
+
+#ifdef BIN_TAC
+	argv[0] = BIN_TAC;
+	res = _adcli_call_external_program (argv[0], argv, "Hello\nWorld\n",
+	                                    &stdout_data, &stdout_data_len);
+	assert (res == ADCLI_SUCCESS);
+	assert (strncmp ("World\nHello\n", (char *) stdout_data, stdout_data_len) == 0);
+	free (stdout_data);
+#endif
+
+#ifdef BIN_ECHO
+	argv[0] = BIN_ECHO;
+	argv[1] = "Hello";
+	res = _adcli_call_external_program (argv[0], argv, NULL,
+	                                    &stdout_data, &stdout_data_len);
+	assert (res == ADCLI_SUCCESS);
+	assert (strncmp ("Hello\n", (char *) stdout_data, stdout_data_len) == 0);
+	free (stdout_data);
+#endif
+}
+
 int
 main (int argc,
       char *argv[])
@@ -629,6 +839,7 @@ main (int argc,
 	test_func (test_strv_count, "/util/strv_count");
 	test_func (test_check_nt_time_string_lifetime, "/util/check_nt_time_string_lifetime");
 	test_func (test_bin_sid_to_str, "/util/bin_sid_to_str");
+	test_func (test_call_external_program, "/util/call_external_program");
 	return test_run (argc, argv);
 }
 
-- 
2.14.4