summaryrefslogtreecommitdiff
path: root/usr/src/lib/libfsmgt/common/cmd.c
diff options
context:
space:
mode:
authorstevel@tonic-gate <none@none>2005-06-14 00:00:00 -0700
committerstevel@tonic-gate <none@none>2005-06-14 00:00:00 -0700
commit7c478bd95313f5f23a4c958a745db2134aa03244 (patch)
treec871e58545497667cbb4b0a4f2daf204743e1fe7 /usr/src/lib/libfsmgt/common/cmd.c
downloadillumos-joyent-7c478bd95313f5f23a4c958a745db2134aa03244.tar.gz
OpenSolaris Launch
Diffstat (limited to 'usr/src/lib/libfsmgt/common/cmd.c')
-rw-r--r--usr/src/lib/libfsmgt/common/cmd.c402
1 files changed, 402 insertions, 0 deletions
diff --git a/usr/src/lib/libfsmgt/common/cmd.c b/usr/src/lib/libfsmgt/common/cmd.c
new file mode 100644
index 0000000000..341a29caec
--- /dev/null
+++ b/usr/src/lib/libfsmgt/common/cmd.c
@@ -0,0 +1,402 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <poll.h>
+#include <sys/wait.h>
+#include <errno.h>
+#include <strings.h>
+#include <sys/stropts.h>
+#include "libfsmgt.h"
+
+#define MASKVAL (POLLIN | POLLPRI | POLLRDNORM | POLLRDBAND)
+#define STDOUT 1
+#define STDERR 2
+
+/*
+ * Public methods
+ */
+
+/*
+ * Method: cmd_execute_command
+ *
+ * Description: Executes the given command and returns the output written to
+ * stdout and stderr in two separate file descriptors to be read by the caller.
+ * It is recommended that the caller use the cmd_retrieve_string method or
+ * another polling method to read from the file descriptors especially in the
+ * case that the command output is expected to be lengthy.
+ *
+ * Parameters:
+ * - char *cmd - The command to execute.
+ * - int *output_filedes - The file descriptor to which the stdout output
+ * is written.
+ * - int *err_filedes - The file descriptor to which the stderr output
+ * is written.
+ *
+ * Returns:
+ * - int - This value will always be zero. This was intended to be the
+ * the exit status of the executed command, but in the case of the
+ * execution of a command with a large amount of output (ex: ls of a large
+ * directory) we can't wait for the exec'd command to exit. This is
+ * because of the way that file descriptors work. When the child process,
+ * or the process executing the command, writes of 'x' amount of data to
+ * a file desciptor (fd), the fd reaches a threshold and will lock and wait
+ * for a reader to read before writing anymore data. In this case, we
+ * don't have a reader since the caller reads from the file descriptors,
+ * not the parent process.
+ * The result is that the parent process cannot be allowed to wait for the
+ * child process to exit. Hence, cannot get the exit status of the
+ * executed command.
+ */
+int
+cmd_execute_command(char *cmd, int *output_filedes, int *err_filedes) {
+ pid_t child_pid;
+ int output[2];
+ int error[2];
+ int ret_val;
+
+ if (pipe(output) == -1) {
+ return (errno);
+ }
+
+ if (pipe(error) == -1) {
+ return (errno);
+ }
+
+ if ((child_pid = fork()) == -1) {
+ return (errno);
+ }
+
+ if (child_pid == 0) {
+ /*
+ * We are in the child.
+ */
+
+ /*
+ * Close the file descriptors we aren't using.
+ */
+ close(output[0]);
+ close(error[0]);
+
+ /*
+ * Close stdout and dup to output[1]
+ */
+ if (close(STDOUT) == -1) {
+ exit(errno);
+ }
+
+ if (dup(output[1]) == -1) {
+ exit(errno);
+ }
+
+ close(output[1]);
+
+ /*
+ * Close stderr and dup to error[1]
+ */
+ if (close(STDERR) == -1) {
+ exit(errno);
+ }
+
+ if (dup(error[1]) == -1) {
+ exit(errno);
+ }
+
+ close(error[1]);
+
+ if (execl("/usr/bin/sh", "sh", "-c", cmd, (char *)0) == -1) {
+
+ exit(errno);
+ } else {
+ exit(0);
+ }
+ }
+
+ /*
+ * We are in the parent
+ */
+
+ /*
+ * Close the file descriptors we aren't using.
+ */
+ close(output[1]);
+ close(error[1]);
+
+ *output_filedes = output[0];
+ *err_filedes = error[0];
+
+ /*
+ * Do not wait for the child process to exit. Just return.
+ */
+ ret_val = 0;
+ return (ret_val);
+
+} /* cmd_execute_command */
+
+/*
+ * Method: cmd_execute_command_and_retrieve_string
+ *
+ * Description: Executes the given string and returns the output as it is
+ * output as it is written to stdout and stderr in the return string.
+ *
+ * Parameters:
+ * - char *cmd - the command to execute.
+ * - int *errp - the error indicator. This will be set to a non-zero
+ * upon error.
+ *
+ * Returns:
+ * char * - The output of the command to stderr and stdout.
+ */
+char *
+cmd_execute_command_and_retrieve_string(char *cmd, int *errp) {
+ pid_t child_pid;
+ int output[2];
+ int err;
+ int status;
+ char *ret_val;
+
+ *errp = 0;
+ if (pipe(output) == -1) {
+ *errp = errno;
+ return (NULL);
+ }
+
+ if ((child_pid = fork()) == -1) {
+ *errp = errno;
+ return (NULL);
+ }
+
+ if (child_pid == 0) {
+ /*
+ * We are in the child.
+ */
+
+ /*
+ * Close the unused file descriptor.
+ */
+ close(output[0]);
+
+ /*
+ * Close stdout and dup to output[1]
+ */
+ if (close(STDOUT) == -1) {
+ *errp = errno;
+ exit(*errp);
+ }
+
+ if (dup(output[1]) == -1) {
+ *errp = errno;
+ exit(*errp);
+ }
+
+ /*
+ * Close stderr and dup to output[1]
+ */
+ if (close(STDERR) == -1) {
+ *errp = errno;
+ exit(*errp);
+ }
+
+ if (dup(output[1]) == -1) {
+ *errp = errno;
+ exit(*errp);
+ }
+
+ close(output[1]);
+
+ if (execl("/usr/bin/sh", "sh", "-c", cmd, (char *)0) == -1) {
+
+ *errp = errno;
+ exit(*errp);
+ } else {
+ exit(0);
+ }
+ }
+
+ /*
+ * We are in the parent
+ */
+
+ /*
+ * Close the file descriptors we are not using.
+ */
+ close(output[1]);
+
+ /*
+ * Wait for the child process to exit.
+ */
+ while ((wait(&status) != child_pid)) {
+ ret_val = cmd_retrieve_string(output[0], &err);
+ }
+
+ /*
+ * Evaluate the wait status and set the evaluated value to
+ * the value of errp.
+ */
+ *errp = WEXITSTATUS(status);
+
+ ret_val = cmd_retrieve_string(output[0], &err);
+
+ /*
+ * Caller must free space allocated for ret_val with free()
+ */
+ return (ret_val);
+} /* cmd_execute_command_and_retrieve_string */
+
+/*
+ * Method: cmd_retrieve_string
+ *
+ * Description: Returns the data written to the file descriptor passed in.
+ *
+ * Parameters:
+ * - int filedes - The file descriptor to be read.
+ * - int *errp - The error indicator. This will be set to a non-zero
+ * value upon error.
+ *
+ * Returns:
+ * - char * - The data read from the file descriptor.
+ */
+char *
+cmd_retrieve_string(int filedes, int *errp) {
+ int returned_value = 0;
+ int buffer_size = 1024;
+ int len;
+ char *ret_val;
+ char *buffer;
+ boolean_t stop_loop = B_FALSE;
+ struct pollfd pollfds[1];
+
+ *errp = 0;
+ /*
+ * Read from the file descriptor passed into the function. This
+ * will read data written to the file descriptor on a FIFO basis.
+ * Care must be taken to make sure to get all data from the file
+ * descriptor.
+ */
+
+ ret_val = (char *)calloc((size_t)1, (size_t)sizeof (char));
+ ret_val[0] = '\0';
+
+
+ /*
+ * Set up the pollfd structure with appropriate information.
+ */
+ pollfds[0].fd = filedes;
+ pollfds[0].events = MASKVAL;
+ pollfds[0].revents = 0;
+
+ while (stop_loop == B_FALSE) {
+ char *tmp_string;
+
+ switch (poll(pollfds, 1, INFTIM)) {
+ case -1:
+
+ case 0:
+ /*
+ * Nothing to read yet so continue.
+ */
+ continue;
+ default:
+ buffer = (char *)calloc(
+ (size_t)(buffer_size + 1),
+ (size_t)sizeof (char));
+
+ if (buffer == NULL) {
+ /*
+ * Out of memory
+ */
+ *errp = errno;
+ return (NULL);
+ }
+
+ /*
+ * Call read to read from the filedesc.
+ */
+ returned_value = read(filedes, buffer,
+ buffer_size);
+ if (returned_value <= 0) {
+ /*
+ * Either we errored or didn't read any
+ * bytes of data.
+ * returned_value == -1 represents an
+ * error.
+ * returned value == 0 represents 0
+ * bytes read.
+ */
+ stop_loop = B_TRUE;
+ continue;
+ }
+
+ len = strlen(buffer);
+
+ /*
+ * Allocate space for the new string.
+ */
+ tmp_string =
+ (char *)calloc((size_t)(len+strlen(ret_val)+1),
+ (size_t)sizeof (char));
+
+ if (tmp_string == NULL) {
+ /*
+ * Out of memory
+ */
+
+ *errp = errno;
+ return (NULL);
+ }
+
+ /*
+ * Concatenate the the new string in 'buffer'
+ * with whatever is in the 'ret_val' buffer.
+ */
+ snprintf(tmp_string, (size_t)(len +
+ strlen(ret_val) + 1), "%s%s",
+ ret_val, buffer);
+
+ (void) free(ret_val);
+ ret_val = strdup(tmp_string);
+
+ if (ret_val == NULL) {
+ /*
+ * Out of memory
+ */
+ *errp = errno;
+ return (NULL);
+ }
+ (void) free(tmp_string);
+ (void) free(buffer);
+
+ } /* switch (poll(pollfds, 1, INFTIM)) */
+
+ } /* while (stop_loop == B_FALSE) */
+
+ return (ret_val);
+} /* cmd_retrieve_string */