Home | History | Annotate | Download | only in common
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License, Version 1.0 only
      6  * (the "License").  You may not use this file except in compliance
      7  * with the License.
      8  *
      9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
     10  * or http://www.opensolaris.org/os/licensing.
     11  * See the License for the specific language governing permissions
     12  * and limitations under the License.
     13  *
     14  * When distributing Covered Code, include this CDDL HEADER in each
     15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     16  * If applicable, add the following below this CDDL HEADER, with the
     17  * fields enclosed by brackets "[]" replaced with your own identifying
     18  * information: Portions Copyright [yyyy] [name of copyright owner]
     19  *
     20  * CDDL HEADER END
     21  */
     22 /*
     23  * Copyright 2003 Sun Microsystems, Inc.  All rights reserved.
     24  * Use is subject to license terms.
     25  */
     26 
     27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
     28 
     29 #include <stdio.h>
     30 #include <stdlib.h>
     31 #include <unistd.h>
     32 #include <sys/types.h>
     33 #include <poll.h>
     34 #include <sys/wait.h>
     35 #include <errno.h>
     36 #include <strings.h>
     37 #include <sys/stropts.h>
     38 #include "libfsmgt.h"
     39 
     40 #define	MASKVAL (POLLIN | POLLPRI | POLLRDNORM | POLLRDBAND)
     41 #define	STDOUT 1
     42 #define	STDERR 2
     43 
     44 /*
     45  * Public methods
     46  */
     47 
     48 /*
     49  * Method: cmd_execute_command
     50  *
     51  * Description: Executes the given command and returns the output written to
     52  * stdout and stderr in two separate file descriptors to be read by the caller.
     53  * It is recommended that the caller use the cmd_retrieve_string method or
     54  * another polling method to read from the file descriptors especially in the
     55  * case that the command output is expected to be lengthy.
     56  *
     57  * Parameters:
     58  *	- char *cmd - The command to execute.
     59  *	- int *output_filedes - The file descriptor to which the stdout output
     60  *	is written.
     61  *	- int *err_filedes -  The file descriptor to which the stderr output
     62  *	is written.
     63  *
     64  * Returns:
     65  *	- int - This value will always be zero.  This was intended to be the
     66  *	the exit status of the executed command, but in the case of the
     67  *	execution of a command with a large amount of output (ex: ls of a large
     68  *	directory) we can't wait for the exec'd command to exit.  This is
     69  *	because of the way that file descriptors work.  When the child process,
     70  *	or the process executing the command, writes of 'x' amount of data to
     71  *	a file desciptor (fd), the fd reaches a threshold and will lock and wait
     72  *	for a reader to read before writing anymore data.  In this case, we
     73  *	don't have a reader since the caller reads from the file descriptors,
     74  *	not the parent process.
     75  *	The result is that the parent process cannot be allowed to wait for the
     76  *	child process to exit.  Hence, cannot get the exit status of the
     77  *	executed command.
     78  */
     79 int
     80 cmd_execute_command(char *cmd, int *output_filedes, int *err_filedes) {
     81 	pid_t child_pid;
     82 	int output[2];
     83 	int error[2];
     84 	int ret_val;
     85 
     86 	if (pipe(output) == -1) {
     87 		return (errno);
     88 	}
     89 
     90 	if (pipe(error) == -1) {
     91 		return (errno);
     92 	}
     93 
     94 	if ((child_pid = fork()) == -1) {
     95 		return (errno);
     96 	}
     97 
     98 	if (child_pid == 0) {
     99 		/*
    100 		 * We are in the child.
    101 		 */
    102 
    103 		/*
    104 		 * Close the file descriptors we aren't using.
    105 		 */
    106 		close(output[0]);
    107 		close(error[0]);
    108 
    109 		/*
    110 		 * Close stdout and dup to output[1]
    111 		 */
    112 		if (close(STDOUT) == -1) {
    113 			exit(errno);
    114 		}
    115 
    116 		if (dup(output[1]) == -1) {
    117 			exit(errno);
    118 		}
    119 
    120 		close(output[1]);
    121 
    122 		/*
    123 		 * Close stderr and dup to error[1]
    124 		 */
    125 		if (close(STDERR) == -1) {
    126 			exit(errno);
    127 		}
    128 
    129 		if (dup(error[1]) == -1) {
    130 			exit(errno);
    131 		}
    132 
    133 		close(error[1]);
    134 
    135 		if (execl("/usr/bin/sh", "sh", "-c", cmd, (char *)0) == -1) {
    136 
    137 			exit(errno);
    138 		} else {
    139 			exit(0);
    140 		}
    141 	}
    142 
    143 	/*
    144 	 * We are in the parent
    145 	 */
    146 
    147 	/*
    148 	 * Close the file descriptors we aren't using.
    149 	 */
    150 	close(output[1]);
    151 	close(error[1]);
    152 
    153 	*output_filedes = output[0];
    154 	*err_filedes = error[0];
    155 
    156 	/*
    157 	 * Do not wait for the child process to exit.  Just return.
    158 	 */
    159 	ret_val = 0;
    160 	return (ret_val);
    161 
    162 } /* cmd_execute_command */
    163 
    164 /*
    165  * Method: cmd_execute_command_and_retrieve_string
    166  *
    167  * Description: Executes the given string and returns the output as it is
    168  * output as it is written to stdout and stderr in the return string.
    169  *
    170  * Parameters:
    171  *	- char *cmd - the command to execute.
    172  *	- int *errp - the error indicator.  This will be set to a non-zero
    173  *	upon error.
    174  *
    175  * Returns:
    176  *	char * - The output of the command to stderr and stdout.
    177  */
    178 char *
    179 cmd_execute_command_and_retrieve_string(char *cmd, int *errp) {
    180 	pid_t child_pid;
    181 	int output[2];
    182 	int err;
    183 	int status;
    184 	char *ret_val;
    185 
    186 	*errp = 0;
    187 	if (pipe(output) == -1) {
    188 		*errp = errno;
    189 		return (NULL);
    190 	}
    191 
    192 	if ((child_pid = fork()) == -1) {
    193 		*errp = errno;
    194 		return (NULL);
    195 	}
    196 
    197 	if (child_pid == 0) {
    198 		/*
    199 		 * We are in the child.
    200 		 */
    201 
    202 		/*
    203 		 * Close the unused file descriptor.
    204 		 */
    205 		close(output[0]);
    206 
    207 		/*
    208 		 * Close stdout and dup to output[1]
    209 		 */
    210 		if (close(STDOUT) == -1) {
    211 			*errp = errno;
    212 			exit(*errp);
    213 		}
    214 
    215 		if (dup(output[1]) == -1) {
    216 			*errp = errno;
    217 			exit(*errp);
    218 		}
    219 
    220 		/*
    221 		 * Close stderr and dup to output[1]
    222 		 */
    223 		if (close(STDERR) == -1) {
    224 			*errp = errno;
    225 			exit(*errp);
    226 		}
    227 
    228 		if (dup(output[1]) == -1) {
    229 			*errp = errno;
    230 			exit(*errp);
    231 		}
    232 
    233 		close(output[1]);
    234 
    235 		if (execl("/usr/bin/sh", "sh", "-c", cmd, (char *)0) == -1) {
    236 
    237 			*errp = errno;
    238 			exit(*errp);
    239 		} else {
    240 			exit(0);
    241 		}
    242 	}
    243 
    244 	/*
    245 	 * We are in the parent
    246 	 */
    247 
    248 	/*
    249 	 * Close the file descriptors we are not using.
    250 	 */
    251 	close(output[1]);
    252 
    253 	/*
    254 	 * Wait for the child process to exit.
    255 	 */
    256 	while ((wait(&status) != child_pid)) {
    257 		ret_val = cmd_retrieve_string(output[0], &err);
    258 	}
    259 
    260 	/*
    261 	 * Evaluate the wait status and set the evaluated value to
    262 	 * the value of errp.
    263 	 */
    264 	*errp = WEXITSTATUS(status);
    265 
    266 	ret_val = cmd_retrieve_string(output[0], &err);
    267 
    268 	/*
    269 	 * Caller must free space allocated for ret_val with free()
    270 	 */
    271 	return (ret_val);
    272 } /* cmd_execute_command_and_retrieve_string */
    273 
    274 /*
    275  * Method: cmd_retrieve_string
    276  *
    277  * Description: Returns the data written to the file descriptor passed in.
    278  *
    279  * Parameters:
    280  *	- int filedes - The file descriptor to be read.
    281  *	- int *errp - The error indicator.  This will be set to a non-zero
    282  *	value upon error.
    283  *
    284  * Returns:
    285  *	- char * - The data read from the file descriptor.
    286  */
    287 char *
    288 cmd_retrieve_string(int filedes, int *errp) {
    289 	int returned_value = 0;
    290 	int buffer_size = 1024;
    291 	int len;
    292 	char *ret_val;
    293 	char *buffer;
    294 	boolean_t stop_loop = B_FALSE;
    295 	struct pollfd pollfds[1];
    296 
    297 	*errp = 0;
    298 	/*
    299 	 * Read from the file descriptor passed into the function.  This
    300 	 * will read data written to the file descriptor on a FIFO basis.
    301 	 * Care must be taken to make sure to get all data from the file
    302 	 * descriptor.
    303 	 */
    304 
    305 	ret_val = (char *)calloc((size_t)1, (size_t)sizeof (char));
    306 	ret_val[0] = '\0';
    307 
    308 
    309 	/*
    310 	 * Set up the pollfd structure with appropriate information.
    311 	 */
    312 	pollfds[0].fd = filedes;
    313 	pollfds[0].events = MASKVAL;
    314 	pollfds[0].revents = 0;
    315 
    316 	while (stop_loop == B_FALSE) {
    317 		char *tmp_string;
    318 
    319 		switch (poll(pollfds, 1, INFTIM)) {
    320 			case -1:
    321 
    322 			case 0:
    323 				/*
    324 				 * Nothing to read yet so continue.
    325 				 */
    326 				continue;
    327 			default:
    328 				buffer = (char *)calloc(
    329 					(size_t)(buffer_size + 1),
    330 					(size_t)sizeof (char));
    331 
    332 				if (buffer == NULL) {
    333 					/*
    334 					 * Out of memory
    335 					 */
    336 					*errp = errno;
    337 					return (NULL);
    338 				}
    339 
    340 				/*
    341 				 * Call read to read from the filedesc.
    342 				 */
    343 				returned_value = read(filedes, buffer,
    344 					buffer_size);
    345 				if (returned_value <= 0) {
    346 					/*
    347 					 * Either we errored or didn't read any
    348 					 * bytes of data.
    349 					 * returned_value == -1 represents an
    350 					 * error.
    351 					 * returned value == 0 represents 0
    352 					 * bytes read.
    353 					 */
    354 					stop_loop = B_TRUE;
    355 					continue;
    356 				}
    357 
    358 				len = strlen(buffer);
    359 
    360 				/*
    361 				 * Allocate space for the new string.
    362 				 */
    363 				tmp_string =
    364 				(char *)calloc((size_t)(len+strlen(ret_val)+1),
    365 						(size_t)sizeof (char));
    366 
    367 				if (tmp_string == NULL) {
    368 					/*
    369 					 * Out of memory
    370 					 */
    371 
    372 					*errp = errno;
    373 					return (NULL);
    374 				}
    375 
    376 				/*
    377 				 * Concatenate the the new string in 'buffer'
    378 				 * with whatever is in the 'ret_val' buffer.
    379 				 */
    380 				snprintf(tmp_string, (size_t)(len +
    381 					strlen(ret_val) + 1), "%s%s",
    382 					ret_val, buffer);
    383 
    384 				(void) free(ret_val);
    385 				ret_val = strdup(tmp_string);
    386 
    387 				if (ret_val == NULL) {
    388 					/*
    389 					 * Out of memory
    390 					 */
    391 					*errp = errno;
    392 					return (NULL);
    393 				}
    394 				(void) free(tmp_string);
    395 				(void) free(buffer);
    396 
    397 		} /* switch (poll(pollfds, 1, INFTIM)) */
    398 
    399 	} /* while (stop_loop == B_FALSE) */
    400 
    401 	return (ret_val);
    402 } /* cmd_retrieve_string */
    403