From 8abf673c508db9d8bbdaaa5bbee50dedd3a95faf Mon Sep 17 00:00:00 2001 From: Sumit Bose Date: Tue, 30 Jan 2018 14:39:17 +0100 Subject: [PATCH 3/9] 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 --- 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 #include #include +#include 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