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