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