dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / lib / libfsmgt / common / cmd.c
blob341a29caec35a74d70022922d0a3d96572dc05b4
1 /*
2 * CDDL HEADER START
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.
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]
20 * CDDL HEADER END
23 * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 #pragma ident "%Z%%M% %I% %E% SMI"
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"
40 #define MASKVAL (POLLIN | POLLPRI | POLLRDNORM | POLLRDBAND)
41 #define STDOUT 1
42 #define STDERR 2
45 * Public methods
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.
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.
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.
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;
86 if (pipe(output) == -1) {
87 return (errno);
90 if (pipe(error) == -1) {
91 return (errno);
94 if ((child_pid = fork()) == -1) {
95 return (errno);
98 if (child_pid == 0) {
100 * We are in the child.
104 * Close the file descriptors we aren't using.
106 close(output[0]);
107 close(error[0]);
110 * Close stdout and dup to output[1]
112 if (close(STDOUT) == -1) {
113 exit(errno);
116 if (dup(output[1]) == -1) {
117 exit(errno);
120 close(output[1]);
123 * Close stderr and dup to error[1]
125 if (close(STDERR) == -1) {
126 exit(errno);
129 if (dup(error[1]) == -1) {
130 exit(errno);
133 close(error[1]);
135 if (execl("/usr/bin/sh", "sh", "-c", cmd, (char *)0) == -1) {
137 exit(errno);
138 } else {
139 exit(0);
144 * We are in the parent
148 * Close the file descriptors we aren't using.
150 close(output[1]);
151 close(error[1]);
153 *output_filedes = output[0];
154 *err_filedes = error[0];
157 * Do not wait for the child process to exit. Just return.
159 ret_val = 0;
160 return (ret_val);
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.
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.
175 * Returns:
176 * char * - The output of the command to stderr and stdout.
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;
186 *errp = 0;
187 if (pipe(output) == -1) {
188 *errp = errno;
189 return (NULL);
192 if ((child_pid = fork()) == -1) {
193 *errp = errno;
194 return (NULL);
197 if (child_pid == 0) {
199 * We are in the child.
203 * Close the unused file descriptor.
205 close(output[0]);
208 * Close stdout and dup to output[1]
210 if (close(STDOUT) == -1) {
211 *errp = errno;
212 exit(*errp);
215 if (dup(output[1]) == -1) {
216 *errp = errno;
217 exit(*errp);
221 * Close stderr and dup to output[1]
223 if (close(STDERR) == -1) {
224 *errp = errno;
225 exit(*errp);
228 if (dup(output[1]) == -1) {
229 *errp = errno;
230 exit(*errp);
233 close(output[1]);
235 if (execl("/usr/bin/sh", "sh", "-c", cmd, (char *)0) == -1) {
237 *errp = errno;
238 exit(*errp);
239 } else {
240 exit(0);
245 * We are in the parent
249 * Close the file descriptors we are not using.
251 close(output[1]);
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
262 * the value of errp.
264 *errp = WEXITSTATUS(status);
266 ret_val = cmd_retrieve_string(output[0], &err);
269 * Caller must free space allocated for ret_val with free()
271 return (ret_val);
272 } /* cmd_execute_command_and_retrieve_string */
275 * Method: cmd_retrieve_string
277 * Description: Returns the data written to the file descriptor passed in.
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.
284 * Returns:
285 * - char * - The data read from the file descriptor.
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];
297 *errp = 0;
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.
305 ret_val = (char *)calloc((size_t)1, (size_t)sizeof (char));
306 ret_val[0] = '\0';
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) {
317 char *tmp_string;
319 switch (poll(pollfds, 1, INFTIM)) {
320 case -1:
322 case 0:
324 * Nothing to read yet so continue.
326 continue;
327 default:
328 buffer = (char *)calloc(
329 (size_t)(buffer_size + 1),
330 (size_t)sizeof (char));
332 if (buffer == NULL) {
334 * Out of memory
336 *errp = errno;
337 return (NULL);
341 * Call read to read from the filedesc.
343 returned_value = read(filedes, buffer,
344 buffer_size);
345 if (returned_value <= 0) {
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.
354 stop_loop = B_TRUE;
355 continue;
358 len = strlen(buffer);
361 * Allocate space for the new string.
363 tmp_string =
364 (char *)calloc((size_t)(len+strlen(ret_val)+1),
365 (size_t)sizeof (char));
367 if (tmp_string == NULL) {
369 * Out of memory
372 *errp = errno;
373 return (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",
382 ret_val, buffer);
384 (void) free(ret_val);
385 ret_val = strdup(tmp_string);
387 if (ret_val == NULL) {
389 * Out of memory
391 *errp = errno;
392 return (NULL);
394 (void) free(tmp_string);
395 (void) free(buffer);
397 } /* switch (poll(pollfds, 1, INFTIM)) */
399 } /* while (stop_loop == B_FALSE) */
401 return (ret_val);
402 } /* cmd_retrieve_string */