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
cmd_execute_command(char * cmd,int * output_filedes,int * err_filedes)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 *
cmd_execute_command_and_retrieve_string(char * cmd,int * errp)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 *
cmd_retrieve_string(int filedes,int * errp)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