OpenSolaris_b135/lib/libfsmgt/common/cmd.c

Compare this file to the similar file:
Show the results in this format:

/*
 * 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 */