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
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.
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]
23 * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 #pragma ident "%Z%%M% %I% %E% SMI"
32 #include <sys/types.h>
37 #include <sys/stropts.h>
40 #define MASKVAL (POLLIN | POLLPRI | POLLRDNORM | POLLRDBAND)
49 * Method: cmd_execute_command
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.
58 * - char *cmd - The command to execute.
59 * - int *output_filedes - The file descriptor to which the stdout output
61 * - int *err_filedes - The file descriptor to which the stderr output
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
80 cmd_execute_command(char *cmd
, int *output_filedes
, int *err_filedes
) {
86 if (pipe(output
) == -1) {
90 if (pipe(error
) == -1) {
94 if ((child_pid
= fork()) == -1) {
100 * We are in the child.
104 * Close the file descriptors we aren't using.
110 * Close stdout and dup to output[1]
112 if (close(STDOUT
) == -1) {
116 if (dup(output
[1]) == -1) {
123 * Close stderr and dup to error[1]
125 if (close(STDERR
) == -1) {
129 if (dup(error
[1]) == -1) {
135 if (execl("/usr/bin/sh", "sh", "-c", cmd
, (char *)0) == -1) {
144 * We are in the parent
148 * Close the file descriptors we aren't using.
153 *output_filedes
= output
[0];
154 *err_filedes
= error
[0];
157 * Do not wait for the child process to exit. Just return.
162 } /* cmd_execute_command */
165 * Method: cmd_execute_command_and_retrieve_string
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.
171 * - char *cmd - the command to execute.
172 * - int *errp - the error indicator. This will be set to a non-zero
176 * char * - The output of the command to stderr and stdout.
179 cmd_execute_command_and_retrieve_string(char *cmd
, int *errp
) {
187 if (pipe(output
) == -1) {
192 if ((child_pid
= fork()) == -1) {
197 if (child_pid
== 0) {
199 * We are in the child.
203 * Close the unused file descriptor.
208 * Close stdout and dup to output[1]
210 if (close(STDOUT
) == -1) {
215 if (dup(output
[1]) == -1) {
221 * Close stderr and dup to output[1]
223 if (close(STDERR
) == -1) {
228 if (dup(output
[1]) == -1) {
235 if (execl("/usr/bin/sh", "sh", "-c", cmd
, (char *)0) == -1) {
245 * We are in the parent
249 * Close the file descriptors we are not using.
254 * Wait for the child process to exit.
256 while ((wait(&status
) != child_pid
)) {
257 ret_val
= cmd_retrieve_string(output
[0], &err
);
261 * Evaluate the wait status and set the evaluated value to
264 *errp
= WEXITSTATUS(status
);
266 ret_val
= cmd_retrieve_string(output
[0], &err
);
269 * Caller must free space allocated for ret_val with free()
272 } /* cmd_execute_command_and_retrieve_string */
275 * Method: cmd_retrieve_string
277 * Description: Returns the data written to the file descriptor passed in.
280 * - int filedes - The file descriptor to be read.
281 * - int *errp - The error indicator. This will be set to a non-zero
285 * - char * - The data read from the file descriptor.
288 cmd_retrieve_string(int filedes
, int *errp
) {
289 int returned_value
= 0;
290 int buffer_size
= 1024;
294 boolean_t stop_loop
= B_FALSE
;
295 struct pollfd pollfds
[1];
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
305 ret_val
= (char *)calloc((size_t)1, (size_t)sizeof (char));
310 * Set up the pollfd structure with appropriate information.
312 pollfds
[0].fd
= filedes
;
313 pollfds
[0].events
= MASKVAL
;
314 pollfds
[0].revents
= 0;
316 while (stop_loop
== B_FALSE
) {
319 switch (poll(pollfds
, 1, INFTIM
)) {
324 * Nothing to read yet so continue.
328 buffer
= (char *)calloc(
329 (size_t)(buffer_size
+ 1),
330 (size_t)sizeof (char));
332 if (buffer
== NULL
) {
341 * Call read to read from the filedesc.
343 returned_value
= read(filedes
, buffer
,
345 if (returned_value
<= 0) {
347 * Either we errored or didn't read any
349 * returned_value == -1 represents an
351 * returned value == 0 represents 0
358 len
= strlen(buffer
);
361 * Allocate space for the new string.
364 (char *)calloc((size_t)(len
+strlen(ret_val
)+1),
365 (size_t)sizeof (char));
367 if (tmp_string
== NULL
) {
377 * Concatenate the the new string in 'buffer'
378 * with whatever is in the 'ret_val' buffer.
380 snprintf(tmp_string
, (size_t)(len
+
381 strlen(ret_val
) + 1), "%s%s",
384 (void) free(ret_val
);
385 ret_val
= strdup(tmp_string
);
387 if (ret_val
== NULL
) {
394 (void) free(tmp_string
);
397 } /* switch (poll(pollfds, 1, INFTIM)) */
399 } /* while (stop_loop == B_FALSE) */
402 } /* cmd_retrieve_string */