xref: /titanic_50/usr/src/lib/libpkg/common/runcmd.c (revision 3c112a2b34403220c06c3e2fcac403358cfba168)
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 (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
28 /* All Rights Reserved */
29 
30 
31 
32 #include <stdio.h>
33 #include <errno.h>
34 #include <string.h>
35 #include <strings.h>
36 #include <signal.h>
37 #include <fcntl.h>
38 #include <stdlib.h>
39 #include <unistd.h>
40 #include <wait.h>
41 #include <sys/types.h>
42 #include "pkglib.h"
43 #include "pkglocale.h"
44 #include "pkglibmsgs.h"
45 
46 #ifndef _STDARG_H
47 #include "stdarg.h"
48 #endif
49 
50 /*
51  * Private definitions
52  */
53 
54 /* Maximum number of arguments to pkg_ExecCmdList */
55 
56 #define	MAX_EXEC_CMD_ARGS	100
57 
58 /* Size of buffer increments when reading from pipe */
59 
60 #define	PIPE_BUFFER_INCREMENT	256
61 
62 static char	errfile[L_tmpnam+1];
63 
64 /*
65  * This is the "argument array" definition that is returned by e_new_args and is
66  * used by e_add_args, e_free_args, etc.
67  */
68 
69 struct _argArray_t {
70 	long	_aaNumArgs;	/* number of arguments set */
71 	long	_aaMaxArgs;	/* number of arguments allocated */
72 	char	**_aaArgs;	/* actual arguments */
73 };
74 
75 typedef struct _argArray_t argArray_t;
76 
77 /*
78  * Private Methods
79  */
80 static void		e_free_args(argArray_t *a_args);
81 static argArray_t	*e_new_args(int initialCount);
82 /*PRINTFLIKE2*/
83 static boolean_t	e_add_arg(argArray_t *a_args, char *a_format, ...);
84 static int		e_get_argc(argArray_t *a_args);
85 static char		**e_get_argv(argArray_t *a_args);
86 
87 
88 /*
89  * Public Methods
90  */
91 
92 
93 void
94 rpterr(void)
95 {
96 	FILE	*fp;
97 	int	c;
98 
99 	if (errfile[0]) {
100 		if (fp = fopen(errfile, "r")) {
101 			while ((c = getc(fp)) != EOF)
102 				putc(c, stderr);
103 			(void) fclose(fp);
104 		}
105 		(void) unlink(errfile);
106 		errfile[0] = '\0';
107 	}
108 }
109 
110 void
111 ecleanup(void)
112 {
113 	if (errfile[0]) {
114 		(void) unlink(errfile);
115 		errfile[0] = NULL;
116 	}
117 }
118 
119 int
120 esystem(char *cmd, int ifd, int ofd)
121 {
122 	char	*perrfile;
123 	int	status = 0;
124 	pid_t	pid;
125 
126 	perrfile = tmpnam(NULL);
127 	if (perrfile == NULL) {
128 		progerr(
129 		    pkg_gt("unable to create temp error file, errno=%d"),
130 		    errno);
131 		return (-1);
132 	}
133 	(void) strlcpy(errfile, perrfile, sizeof (errfile));
134 
135 	/* flush standard i/o before creating new process */
136 
137 	(void) fflush(stderr);
138 	(void) fflush(stdout);
139 
140 	/*
141 	 * create new process to execute command in;
142 	 * vfork() is being used to avoid duplicating the parents
143 	 * memory space - this means that the child process may
144 	 * not modify any of the parents memory including the
145 	 * standard i/o descriptors - all the child can do is
146 	 * adjust interrupts and open files as a prelude to a
147 	 * call to exec().
148 	 */
149 
150 	pid = vfork();
151 	if (pid == 0) {
152 		/*
153 		 * this is the child process
154 		 */
155 		int	i;
156 
157 		/* reset any signals to default */
158 
159 		for (i = 0; i < NSIG; i++) {
160 			(void) sigset(i, SIG_DFL);
161 		}
162 
163 		if (ifd > 0) {
164 			(void) dup2(ifd, STDIN_FILENO);
165 		}
166 
167 		if (ofd >= 0 && ofd != STDOUT_FILENO) {
168 			(void) dup2(ofd, STDOUT_FILENO);
169 		}
170 
171 		i = open(errfile, O_WRONLY|O_CREAT|O_TRUNC, 0666);
172 		if (i >= 0) {
173 			dup2(i, STDERR_FILENO);
174 		}
175 
176 		/* Close all open files except standard i/o */
177 
178 		closefrom(3);
179 
180 		/* execute target executable */
181 
182 		execl("/sbin/sh", "/sbin/sh", "-c", cmd, NULL);
183 		progerr(pkg_gt("exec of <%s> failed, errno=%d"), cmd, errno);
184 		_exit(99);
185 	} else if (pid < 0) {
186 		/* fork failed! */
187 
188 		logerr(pkg_gt("bad vfork(), errno=%d"), errno);
189 		return (-1);
190 	}
191 
192 	/*
193 	 * this is the parent process
194 	 */
195 
196 	sighold(SIGINT);
197 	pid = waitpid(pid, &status, 0);
198 	sigrelse(SIGINT);
199 
200 	if (pid < 0) {
201 		return (-1); /* probably interrupted */
202 	}
203 
204 	switch (status & 0177) {
205 		case 0:
206 		case 0177:
207 			status = status >> 8;
208 			/*FALLTHROUGH*/
209 
210 		default:
211 			/* terminated by a signal */
212 			status = status & 0177;
213 	}
214 
215 	if (status == 0) {
216 		ecleanup();
217 	}
218 
219 	return (status);
220 }
221 
222 FILE *
223 epopen(char *cmd, char *mode)
224 {
225 	char	*buffer, *perrfile;
226 	FILE	*pp;
227 	size_t	len;
228 	size_t	alen;
229 
230 	if (errfile[0]) {
231 		/* cleanup previous errfile */
232 		unlink(errfile);
233 	}
234 
235 	perrfile = tmpnam(NULL);
236 	if (perrfile == NULL) {
237 		progerr(
238 		    pkg_gt("unable to create temp error file, errno=%d"),
239 		    errno);
240 		return ((FILE *)0);
241 	}
242 
243 	if (strlcpy(errfile, perrfile, sizeof (errfile)) > sizeof (errfile)) {
244 		progerr(pkg_gt("file name max length %d; name is too long: %s"),
245 						sizeof (errfile), perrfile);
246 		return ((FILE *)0);
247 	}
248 
249 	len = strlen(cmd)+6+strlen(errfile);
250 	buffer = (char *)calloc(len, sizeof (char));
251 	if (buffer == NULL) {
252 		progerr(pkg_gt("no memory in epopen(), errno=%d"), errno);
253 		return ((FILE *)0);
254 	}
255 
256 	if (strchr(cmd, '|')) {
257 		alen = snprintf(buffer, len, "(%s) 2>%s", cmd, errfile);
258 	} else {
259 		alen = snprintf(buffer, len, "%s 2>%s", cmd, errfile);
260 	}
261 
262 	if (alen > len) {
263 		progerr(pkg_gt("command max length %d; cmd is too long: %s"),
264 								len, cmd);
265 		return ((FILE *)0);
266 	}
267 
268 	pp = popen(buffer, mode);
269 
270 	free(buffer);
271 	return (pp);
272 }
273 
274 int
275 epclose(FILE *pp)
276 {
277 	int n;
278 
279 	n = pclose(pp);
280 	if (n == 0)
281 		ecleanup();
282 	return (n);
283 }
284 
285 /*
286  * Name:	e_ExecCmdArray
287  * Synopsis:	Execute Unix command and return results
288  * Description:	Execute a Unix command and return results and status
289  * Arguments:
290  *		r_status - [RO, *RW] - (int *)
291  *			Return (exit) status from Unix command:
292  *			== -1 : child terminated with a signal
293  *			!= -1 : lower 8-bit value child passed to exit()
294  *		r_results - [RO, *RW] - (char **)
295  *			Any output generated by the Unix command to stdout
296  *			and to stderr
297  *			== (char *)NULL if no output generated
298  *		a_inputFile - [RO, *RO] - (char *)
299  *			Pointer to character string representing file to be
300  *			used as "standard input" for the command.
301  *			== (char *)NULL to use "/dev/null" as standard input
302  *		a_cmd - [RO, *RO] - (char *)
303  *			Pointer to character string representing the full path
304  *			of the Unix command to execute
305  *		char **a_args - [RO, *RO] - (char **)
306  *			List of character strings representing the arguments
307  *			to be passed to the Unix command. The list must be
308  *			terminated with an element that is (char *)NULL
309  * Returns:	int
310  *			== 0 - Command executed
311  *				Look at r_status for results of Unix command
312  *			!= 0 - problems executing command
313  *				r_status and r_results have no meaning;
314  *				r_status will be -1
315  *				r_results will be NULL
316  * NOTE:    	Any results returned is placed in new storage for the
317  *		calling method. The caller must use 'free' to dispose
318  *		of the storage once the results are no longer needed.
319  * NOTE:	If 0 is returned, 'r_status' must be queried to
320  *		determine the results of the Unix command.
321  * NOTE:	The system "errno" value from immediately after waitpid() call
322  *		is preserved for the calling method to use to determine
323  *		the system reason why the operation failed.
324  */
325 
326 int
327 e_ExecCmdArray(int *r_status, char **r_results,
328 	char *a_inputFile, char *a_cmd, char **a_args)
329 {
330 	char		*buffer;
331 	int		bufferIndex;
332 	int		bufferSize;
333 	int		ipipe[2] = {0, 0};
334 	pid_t		pid;
335 	pid_t		resultPid;
336 	int		status;
337 	int		lerrno;
338 	int		stdinfile = -1;
339 
340 	/* reset return results buffer pointer */
341 
342 	if (r_results != (char **)NULL) {
343 		*r_results = (char *)NULL;
344 	}
345 
346 	*r_status = -1;
347 
348 	/*
349 	 * See if command exists
350 	 */
351 
352 	if (access(a_cmd, F_OK|X_OK) != 0) {
353 		return (-1);
354 	}
355 
356 	/*
357 	 * See if input file exists
358 	 */
359 
360 	if (a_inputFile != (char *)NULL) {
361 		stdinfile = open(a_inputFile, O_RDONLY);
362 	} else {
363 		stdinfile = open("/dev/null", O_RDONLY); /* stdin = /dev/null */
364 	}
365 
366 	if (stdinfile < 0) {
367 		return (-1);
368 	}
369 
370 	/*
371 	 * Create a pipe to be used to capture the command output
372 	 */
373 
374 	if (pipe(ipipe) != 0) {
375 		(void) close(stdinfile);
376 		return (-1);
377 	}
378 
379 
380 	bufferSize = PIPE_BUFFER_INCREMENT;
381 	bufferIndex = 0;
382 	buffer = calloc(1, bufferSize);
383 	if (buffer == (char *)NULL) {
384 		(void) close(stdinfile);
385 		return (-1);
386 	}
387 
388 	/* flush standard i/o before creating new process */
389 
390 	(void) fflush(stderr);
391 	(void) fflush(stdout);
392 
393 	/*
394 	 * create new process to execute command in;
395 	 * vfork() is being used to avoid duplicating the parents
396 	 * memory space - this means that the child process may
397 	 * not modify any of the parents memory including the
398 	 * standard i/o descriptors - all the child can do is
399 	 * adjust interrupts and open files as a prelude to a
400 	 * call to exec().
401 	 */
402 
403 	pid = vfork();
404 
405 	if (pid == 0) {
406 		/*
407 		 * This is the forked (child) process ======================
408 		 */
409 
410 		int	i;
411 
412 		/* reset any signals to default */
413 
414 		for (i = 0; i < NSIG; i++) {
415 			(void) sigset(i, SIG_DFL);
416 		}
417 
418 		/* assign stdin, stdout, stderr as appropriate */
419 
420 		(void) dup2(stdinfile, STDIN_FILENO);
421 		(void) close(ipipe[0]);		/* close out pipe reader side */
422 		(void) dup2(ipipe[1], STDOUT_FILENO);
423 		(void) dup2(ipipe[1], STDERR_FILENO);
424 
425 		/* Close all open files except standard i/o */
426 
427 		closefrom(3);
428 
429 		/* execute target executable */
430 
431 		(void) execvp(a_cmd, a_args);
432 		perror(a_cmd);	/* Emit error msg - ends up in callers buffer */
433 		_exit(0x00FE);
434 	}
435 
436 	/*
437 	 * This is the forking (parent) process ====================
438 	 */
439 
440 	(void) close(stdinfile);
441 	(void) close(ipipe[1]);		/* Close write side of pipe */
442 
443 	/*
444 	 * Spin reading data from the child into the buffer - when the read eofs
445 	 * the child has exited
446 	 */
447 
448 	for (;;) {
449 		ssize_t	bytesRead;
450 
451 		/* read as much child data as there is available buffer space */
452 
453 		bytesRead = read(ipipe[0], buffer + bufferIndex,
454 						bufferSize - bufferIndex);
455 
456 		/* break out of read loop if end-of-file encountered */
457 
458 		if (bytesRead == 0) {
459 			break;
460 		}
461 
462 		/* if error, continue if recoverable, else break out of loop */
463 
464 		if (bytesRead == -1) {
465 			/* try again: EAGAIN - insufficient resources */
466 
467 			if (errno == EAGAIN) {
468 				continue;
469 			}
470 
471 			/* try again: EINTR - interrupted system call */
472 
473 			if (errno == EINTR) {
474 				continue;
475 			}
476 
477 			/* break out of loop - error not recoverable */
478 			break;
479 		}
480 
481 		/* at least 1 byte read: expand buffer if at end */
482 
483 		bufferIndex += bytesRead;
484 		if (bufferIndex >= bufferSize) {
485 			buffer = realloc(buffer,
486 					bufferSize += PIPE_BUFFER_INCREMENT);
487 			(void) memset(buffer + bufferIndex, 0,
488 				bufferSize - bufferIndex);
489 		}
490 	}
491 
492 	(void) close(ipipe[0]);		/* Close read side of pipe */
493 
494 	/* Get subprocess exit status */
495 
496 	for (;;) {
497 		resultPid = waitpid(pid, &status, 0L);
498 		lerrno = (resultPid == -1 ? errno : 0);
499 
500 		/* break loop if child process status reaped */
501 
502 		if (resultPid != -1) {
503 			break;
504 		}
505 
506 		/* break loop if not interrupted out of waitpid */
507 
508 		if (errno != EINTR) {
509 			break;
510 		}
511 	}
512 
513 	/*
514 	 * If the child process terminated due to a call to exit(), then
515 	 * set results equal to the 8-bit exit status of the child process;
516 	 * otherwise, set the exit status to "-1" indicating that the child
517 	 * exited via a signal.
518 	 */
519 
520 	*r_status = WIFEXITED(status) ? WEXITSTATUS(status) : -1;
521 
522 	/* return appropriate output */
523 
524 	if (!*buffer) {
525 		/* No contents in output buffer - discard */
526 		free(buffer);
527 	} else if (r_results == (char **)NULL) {
528 		/* Not requested to return results - discard */
529 		free(buffer);
530 	} else {
531 		/* have output and request to return: pass to calling method */
532 		*r_results = buffer;
533 	}
534 
535 	errno = lerrno;
536 	return (resultPid == -1 ? -1 : 0);
537 }
538 
539 /*
540  * Name:	e_ExecCmdList
541  * Synopsis:	Execute Unix command and return results
542  * Description:	Execute a Unix command and return results and status
543  * Arguments:
544  *		r_status - [RO, *RW] - (int *)
545  *			Return (exit) status from Unix command
546  *		r_results - [RO, *RW] - (char **)
547  *			Any output generated by the Unix command to stdout
548  *			and to stderr
549  *			== (char *)NULL if no output generated
550  *		a_inputFile - [RO, *RO] - (char *)
551  *			Pointer to character string representing file to be
552  *			used as "standard input" for the command.
553  *			== (char *)NULL to use "/dev/null" as standard input
554  *		a_cmd - [RO, *RO] - (char *)
555  *			Pointer to character string representing the full path
556  *			of the Unix command to execute
557  *		... - [RO] (?)
558  *			Zero or more arguments to the Unix command
559  *			The argument list must be ended with (void *)NULL
560  * Returns:	int
561  *			== 0 - Command executed
562  *				Look at r_status for results of Unix command
563  *			!= 0 - problems executing command
564  *				r_status and r_results have no meaning
565  * NOTE:    	Any results returned is placed in new storage for the
566  *		calling method. The caller must use 'free' to dispose
567  *		of the storage once the results are no longer needed.
568  * NOTE:	If LU_SUCCESS is returned, 'r_status' must be queried to
569  *		determine the results of the Unix command.
570  */
571 
572 int
573 e_ExecCmdList(int *r_status, char **r_results,
574 	char *a_inputFile, char *a_cmd, ...)
575 {
576 	va_list		ap;		/* references variable argument list */
577 	char		*array[MAX_EXEC_CMD_ARGS+1];
578 	int		argno = 0;
579 
580 	/*
581 	 * Create argument array for exec system call
582 	 */
583 
584 	bzero(array, sizeof (array));
585 
586 	va_start(ap, a_cmd);	/* Begin variable argument processing */
587 
588 	for (argno = 0; argno < MAX_EXEC_CMD_ARGS; argno++) {
589 		array[argno] = va_arg(ap, char *);
590 		if (array[argno] == (char *)NULL) {
591 			break;
592 		}
593 	}
594 
595 	va_end(ap);
596 	return (e_ExecCmdArray(r_status, r_results, a_inputFile,
597 								a_cmd, array));
598 }
599 
600 /*
601  * Name:	e_new_args
602  * Description:	create a new argument array for use in exec() calls
603  * Arguments:	initialCount - [RO, *RO] - (int)
604  *			Initial number of elements to populate the
605  *			argument array with - use best guess
606  * Returns:	argArray_t *
607  *			Pointer to argument array that can be used in other
608  *			functions that accept it as an argument
609  *			== (argArray_t *)NULL - error
610  * NOTE: you must call e_free_args() when the returned argument array is
611  * no longer needed so that all storage used can be freed up.
612  */
613 
614 argArray_t *
615 e_new_args(int initialCount)
616 {
617 	argArray_t	*aa;
618 
619 	/* allocate new argument array structure */
620 
621 	aa = (argArray_t *)calloc(1, sizeof (argArray_t));
622 	if (aa == (argArray_t *)NULL) {
623 		progerr(ERR_MALLOC, strerror(errno), sizeof (argArray_t),
624 			"<argArray_t>");
625 		return ((argArray_t *)NULL);
626 	}
627 
628 	/* allocate initial argument array */
629 
630 	aa->_aaArgs = (char **)calloc(initialCount+1, sizeof (char *));
631 	if (aa->_aaArgs == (char **)NULL) {
632 		progerr(ERR_MALLOC, strerror(errno),
633 			(initialCount+1)*sizeof (char *), "<char **>");
634 		return ((argArray_t *)NULL);
635 	}
636 
637 	/* initialize argument indexes */
638 
639 	aa->_aaNumArgs = 0;
640 	aa->_aaMaxArgs = initialCount;
641 
642 	return (aa);
643 }
644 
645 /*
646  * Name:	e_add_arg
647  * Description:	add new argument to argument array for use in exec() calls
648  * Arguments:	a_args - [RO, *RW] - (argArray_t *)
649  *			Pointer to argument array (previously allocated via
650  *			a call to e_new_args) to add the argument to
651  *		a_format - [RO, *RO] - (char *)
652  *			Pointer to "printf" style format argument
653  *		... - [RO, *RO] - (varies)
654  *			Arguments as appropriate for format statement
655  * Returns:	boolean_t
656  *			B_TRUE - success
657  *			B_FALSE - failure
658  * Examples:
659  * - to add an argument that specifies a file descriptor:
660  *	int fd;
661  *	e_add_arg(aa, "/proc/self/fd/%d", fd);
662  * - to add a flag or other known text:
663  *	e_add_arg(aa, "-s")
664  * - to add random text:
665  *	char *random_text;
666  *	e_add_arg(aa, "%s", random_text);
667  */
668 
669 /*PRINTFLIKE2*/
670 boolean_t
671 e_add_arg(argArray_t *a_args, char *a_format, ...)
672 {
673 	char		*rstr = (char *)NULL;
674 	char		bfr[MAX_CANON];
675 	size_t		vres = 0;
676 	va_list		ap;
677 
678 	/*
679 	 * double argument array if array is full
680 	 */
681 
682 	if (a_args->_aaNumArgs >= a_args->_aaMaxArgs) {
683 		int	newMax;
684 		char	**newArgs;
685 
686 		newMax = a_args->_aaMaxArgs * 2;
687 		newArgs = (char **)realloc(a_args->_aaArgs,
688 			(newMax+1) * sizeof (char *));
689 		if (newArgs == (char **)NULL) {
690 			progerr(ERR_MALLOC, strerror(errno),
691 				((newMax+1) * sizeof (char *)), "<char **>");
692 			return (B_FALSE);
693 		}
694 		a_args->_aaArgs = newArgs;
695 		a_args->_aaMaxArgs = newMax;
696 	}
697 
698 	/* determine size of argument to add to list */
699 
700 	va_start(ap, a_format);
701 	vres = vsnprintf(bfr, sizeof (bfr), a_format, ap);
702 	va_end(ap);
703 
704 	/* if it fit in the built in buffer, use that */
705 	if (vres < sizeof (bfr)) {
706 		/* dup text already generated in bfr */
707 		rstr = strdup(bfr);
708 		if (rstr == (char *)NULL) {
709 			progerr(ERR_MALLOC, strerror(errno), vres+2,
710 				"<char *>");
711 			return (B_FALSE);
712 		}
713 	} else {
714 		/* allocate space for argument to add */
715 
716 		rstr = (char *)malloc(vres+2);
717 		if (rstr == (char *)NULL) {
718 			progerr(ERR_MALLOC, strerror(errno), vres+2,
719 				"<char *>");
720 			return (B_FALSE);
721 		}
722 
723 		/* generate argument to add */
724 
725 		va_start(ap, a_format);
726 		vres = vsnprintf(rstr, vres+1, a_format, ap);
727 		va_end(ap);
728 	}
729 
730 	/* add argument to the end of the argument array */
731 
732 	a_args->_aaArgs[a_args->_aaNumArgs++] = rstr;
733 	a_args->_aaArgs[a_args->_aaNumArgs] = (char *)NULL;
734 
735 	return (B_TRUE);
736 }
737 
738 /*
739  * Name:	e_get_argv
740  * Description:	return (char **)argv pointer from argument array
741  * Arguments:	a_args - [RO, *RW] - (argArray_t *)
742  *			Pointer to argument array (previously allocated via
743  *			a call to e_new_args) to return argv pointer for
744  * Returns:	char **
745  *			Pointer to (char **)argv pointer suitable for use
746  *			in an exec*() call
747  * NOTE: the actual character array is always terminated with a (char *)NULL
748  */
749 
750 char **
751 e_get_argv(argArray_t *a_args)
752 {
753 	return (a_args->_aaArgs);
754 }
755 
756 /*
757  * Name:	e_get_argc
758  * Description:	return (int) argc count from argument array
759  * Arguments:	a_args - [RO, *RW] - (argArray_t *)
760  *			Pointer to argument array (previously allocated via
761  *			a call to e_new_args) to return argc count for
762  * Returns:	int
763  *			Count of the number of arguments in the argument array
764  *			suitable for use in an exec*() call
765  */
766 
767 int
768 e_get_argc(argArray_t *a_args)
769 {
770 	return (a_args->_aaNumArgs);
771 }
772 
773 /*
774  * Name:	e_free_args
775  * Description:	free all storage contained in an argument array previously
776  *		allocated by a call to e_new_args
777  * Arguments:	a_args - [RO, *RW] - (argArray_t *)
778  *			Pointer to argument array (previously allocated via
779  *			a call to e_new_args) to free
780  * Returns:	void
781  * NOTE:	preserves errno (usually called right after e_execCmd*())
782  */
783 
784 void
785 e_free_args(argArray_t *a_args)
786 {
787 	int	i;
788 	int	lerrno = errno;
789 
790 	/* free all arguments in the argument array */
791 
792 	for (i = (a_args->_aaNumArgs-1); i >= 0; i--) {
793 		(void) free(a_args->_aaArgs[i]);
794 		a_args->_aaArgs[i] = (char *)NULL;
795 	}
796 
797 	/* free argument array */
798 
799 	(void) free(a_args->_aaArgs);
800 
801 	/* free argument array structure */
802 
803 	(void) free(a_args);
804 
805 	/* restore errno */
806 
807 	errno = lerrno;
808 }
809