xref: /illumos-gate/usr/src/lib/libpkg/common/runcmd.c (revision 23294c7da48c2eb5222bccedbefb1e06cf5c4df3)
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