xref: /illumos-gate/usr/src/lib/libpkg/common/runcmd.c (revision d66f292d0062ce8c43a0c493d881a24f3b991317)
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   * Public Methods
66   */
67  
68  
69  void
70  rpterr(void)
71  {
72  	FILE	*fp;
73  	int	c;
74  
75  	if (errfile[0]) {
76  		if (fp = fopen(errfile, "r")) {
77  			while ((c = getc(fp)) != EOF)
78  				(void) putc(c, stderr);
79  			(void) fclose(fp);
80  		}
81  		(void) unlink(errfile);
82  		errfile[0] = '\0';
83  	}
84  }
85  
86  void
87  ecleanup(void)
88  {
89  	if (errfile[0]) {
90  		(void) unlink(errfile);
91  		errfile[0] = NULL;
92  	}
93  }
94  
95  int
96  esystem(char *cmd, int ifd, int ofd)
97  {
98  	char	*perrfile;
99  	int	status = 0;
100  	pid_t	pid;
101  
102  	perrfile = tmpnam(NULL);
103  	if (perrfile == NULL) {
104  		progerr(
105  		    pkg_gt("unable to create temp error file, errno=%d"),
106  		    errno);
107  		return (-1);
108  	}
109  	(void) strlcpy(errfile, perrfile, sizeof (errfile));
110  
111  	/* flush standard i/o before creating new process */
112  
113  	(void) fflush(stderr);
114  	(void) fflush(stdout);
115  
116  	/*
117  	 * create new process to execute command in;
118  	 * vfork() is being used to avoid duplicating the parents
119  	 * memory space - this means that the child process may
120  	 * not modify any of the parents memory including the
121  	 * standard i/o descriptors - all the child can do is
122  	 * adjust interrupts and open files as a prelude to a
123  	 * call to exec().
124  	 */
125  
126  	pid = vfork();
127  	if (pid == 0) {
128  		/*
129  		 * this is the child process
130  		 */
131  		int	i;
132  
133  		/* reset any signals to default */
134  
135  		for (i = 0; i < NSIG; i++) {
136  			(void) sigset(i, SIG_DFL);
137  		}
138  
139  		if (ifd > 0) {
140  			(void) dup2(ifd, STDIN_FILENO);
141  		}
142  
143  		if (ofd >= 0 && ofd != STDOUT_FILENO) {
144  			(void) dup2(ofd, STDOUT_FILENO);
145  		}
146  
147  		i = open(errfile, O_WRONLY|O_CREAT|O_TRUNC, 0666);
148  		if (i >= 0) {
149  			(void) dup2(i, STDERR_FILENO);
150  		}
151  
152  		/* Close all open files except standard i/o */
153  
154  		closefrom(3);
155  
156  		/* execute target executable */
157  
158  		(void) execl("/sbin/sh", "/sbin/sh", "-c", cmd, NULL);
159  		progerr(pkg_gt("exec of <%s> failed, errno=%d"), cmd, errno);
160  		_exit(99);
161  	} else if (pid < 0) {
162  		/* fork failed! */
163  
164  		logerr(pkg_gt("bad vfork(), errno=%d"), errno);
165  		return (-1);
166  	}
167  
168  	/*
169  	 * this is the parent process
170  	 */
171  
172  	(void) sighold(SIGINT);
173  	pid = waitpid(pid, &status, 0);
174  	(void) sigrelse(SIGINT);
175  
176  	if (pid < 0) {
177  		return (-1); /* probably interrupted */
178  	}
179  
180  	switch (status & 0177) {
181  		case 0:
182  		case 0177:
183  			status = status >> 8;
184  			/*FALLTHROUGH*/
185  
186  		default:
187  			/* terminated by a signal */
188  			status = status & 0177;
189  	}
190  
191  	if (status == 0) {
192  		ecleanup();
193  	}
194  
195  	return (status);
196  }
197  
198  FILE *
199  epopen(char *cmd, char *mode)
200  {
201  	char	*buffer, *perrfile;
202  	FILE	*pp;
203  	size_t	len;
204  	size_t	alen;
205  
206  	if (errfile[0]) {
207  		/* cleanup previous errfile */
208  		(void) unlink(errfile);
209  	}
210  
211  	perrfile = tmpnam(NULL);
212  	if (perrfile == NULL) {
213  		progerr(
214  		    pkg_gt("unable to create temp error file, errno=%d"),
215  		    errno);
216  		return ((FILE *)0);
217  	}
218  
219  	if (strlcpy(errfile, perrfile, sizeof (errfile)) > sizeof (errfile)) {
220  		progerr(pkg_gt("file name max length %d; name is too long: %s"),
221  						sizeof (errfile), perrfile);
222  		return ((FILE *)0);
223  	}
224  
225  	len = strlen(cmd)+6+strlen(errfile);
226  	buffer = (char *)calloc(len, sizeof (char));
227  	if (buffer == NULL) {
228  		progerr(pkg_gt("no memory in epopen(), errno=%d"), errno);
229  		return ((FILE *)0);
230  	}
231  
232  	if (strchr(cmd, '|')) {
233  		alen = snprintf(buffer, len, "(%s) 2>%s", cmd, errfile);
234  	} else {
235  		alen = snprintf(buffer, len, "%s 2>%s", cmd, errfile);
236  	}
237  
238  	if (alen > len) {
239  		progerr(pkg_gt("command max length %d; cmd is too long: %s"),
240  								len, cmd);
241  		return ((FILE *)0);
242  	}
243  
244  	pp = popen(buffer, mode);
245  
246  	free(buffer);
247  	return (pp);
248  }
249  
250  int
251  epclose(FILE *pp)
252  {
253  	int n;
254  
255  	n = pclose(pp);
256  	if (n == 0)
257  		ecleanup();
258  	return (n);
259  }
260  
261  /*
262   * Name:	e_ExecCmdArray
263   * Synopsis:	Execute Unix command and return results
264   * Description:	Execute a Unix command and return results and status
265   * Arguments:
266   *		r_status - [RO, *RW] - (int *)
267   *			Return (exit) status from Unix command:
268   *			== -1 : child terminated with a signal
269   *			!= -1 : lower 8-bit value child passed to exit()
270   *		r_results - [RO, *RW] - (char **)
271   *			Any output generated by the Unix command to stdout
272   *			and to stderr
273   *			== (char *)NULL if no output generated
274   *		a_inputFile - [RO, *RO] - (char *)
275   *			Pointer to character string representing file to be
276   *			used as "standard input" for the command.
277   *			== (char *)NULL to use "/dev/null" as standard input
278   *		a_cmd - [RO, *RO] - (char *)
279   *			Pointer to character string representing the full path
280   *			of the Unix command to execute
281   *		char **a_args - [RO, *RO] - (char **)
282   *			List of character strings representing the arguments
283   *			to be passed to the Unix command. The list must be
284   *			terminated with an element that is (char *)NULL
285   * Returns:	int
286   *			== 0 - Command executed
287   *				Look at r_status for results of Unix command
288   *			!= 0 - problems executing command
289   *				r_status and r_results have no meaning;
290   *				r_status will be -1
291   *				r_results will be NULL
292   * NOTE:    	Any results returned is placed in new storage for the
293   *		calling method. The caller must use 'free' to dispose
294   *		of the storage once the results are no longer needed.
295   * NOTE:	If 0 is returned, 'r_status' must be queried to
296   *		determine the results of the Unix command.
297   * NOTE:	The system "errno" value from immediately after waitpid() call
298   *		is preserved for the calling method to use to determine
299   *		the system reason why the operation failed.
300   */
301  
302  int
303  e_ExecCmdArray(int *r_status, char **r_results,
304  	char *a_inputFile, char *a_cmd, char **a_args)
305  {
306  	char		*buffer;
307  	int		bufferIndex;
308  	int		bufferSize;
309  	int		ipipe[2] = {0, 0};
310  	pid_t		pid;
311  	pid_t		resultPid;
312  	int		status;
313  	int		lerrno;
314  	int		stdinfile = -1;
315  
316  	/* reset return results buffer pointer */
317  
318  	if (r_results != (char **)NULL) {
319  		*r_results = (char *)NULL;
320  	}
321  
322  	*r_status = -1;
323  
324  	/*
325  	 * See if command exists
326  	 */
327  
328  	if (access(a_cmd, F_OK|X_OK) != 0) {
329  		return (-1);
330  	}
331  
332  	/*
333  	 * See if input file exists
334  	 */
335  
336  	if (a_inputFile != (char *)NULL) {
337  		stdinfile = open(a_inputFile, O_RDONLY);
338  	} else {
339  		stdinfile = open("/dev/null", O_RDONLY); /* stdin = /dev/null */
340  	}
341  
342  	if (stdinfile < 0) {
343  		return (-1);
344  	}
345  
346  	/*
347  	 * Create a pipe to be used to capture the command output
348  	 */
349  
350  	if (pipe(ipipe) != 0) {
351  		(void) close(stdinfile);
352  		return (-1);
353  	}
354  
355  
356  	bufferSize = PIPE_BUFFER_INCREMENT;
357  	bufferIndex = 0;
358  	buffer = calloc(1, bufferSize);
359  	if (buffer == (char *)NULL) {
360  		(void) close(stdinfile);
361  		return (-1);
362  	}
363  
364  	/* flush standard i/o before creating new process */
365  
366  	(void) fflush(stderr);
367  	(void) fflush(stdout);
368  
369  	/*
370  	 * create new process to execute command in;
371  	 * vfork() is being used to avoid duplicating the parents
372  	 * memory space - this means that the child process may
373  	 * not modify any of the parents memory including the
374  	 * standard i/o descriptors - all the child can do is
375  	 * adjust interrupts and open files as a prelude to a
376  	 * call to exec().
377  	 */
378  
379  	pid = vfork();
380  
381  	if (pid == 0) {
382  		/*
383  		 * This is the forked (child) process ======================
384  		 */
385  
386  		int	i;
387  
388  		/* reset any signals to default */
389  
390  		for (i = 0; i < NSIG; i++) {
391  			(void) sigset(i, SIG_DFL);
392  		}
393  
394  		/* assign stdin, stdout, stderr as appropriate */
395  
396  		(void) dup2(stdinfile, STDIN_FILENO);
397  		(void) close(ipipe[0]);		/* close out pipe reader side */
398  		(void) dup2(ipipe[1], STDOUT_FILENO);
399  		(void) dup2(ipipe[1], STDERR_FILENO);
400  
401  		/* Close all open files except standard i/o */
402  
403  		closefrom(3);
404  
405  		/* execute target executable */
406  
407  		(void) execvp(a_cmd, a_args);
408  		perror(a_cmd);	/* Emit error msg - ends up in callers buffer */
409  		_exit(0x00FE);
410  	}
411  
412  	/*
413  	 * This is the forking (parent) process ====================
414  	 */
415  
416  	(void) close(stdinfile);
417  	(void) close(ipipe[1]);		/* Close write side of pipe */
418  
419  	/*
420  	 * Spin reading data from the child into the buffer - when the read eofs
421  	 * the child has exited
422  	 */
423  
424  	for (;;) {
425  		ssize_t	bytesRead;
426  
427  		/* read as much child data as there is available buffer space */
428  
429  		bytesRead = read(ipipe[0], buffer + bufferIndex,
430  						bufferSize - bufferIndex);
431  
432  		/* break out of read loop if end-of-file encountered */
433  
434  		if (bytesRead == 0) {
435  			break;
436  		}
437  
438  		/* if error, continue if recoverable, else break out of loop */
439  
440  		if (bytesRead == -1) {
441  			/* try again: EAGAIN - insufficient resources */
442  
443  			if (errno == EAGAIN) {
444  				continue;
445  			}
446  
447  			/* try again: EINTR - interrupted system call */
448  
449  			if (errno == EINTR) {
450  				continue;
451  			}
452  
453  			/* break out of loop - error not recoverable */
454  			break;
455  		}
456  
457  		/* at least 1 byte read: expand buffer if at end */
458  
459  		bufferIndex += bytesRead;
460  		if (bufferIndex >= bufferSize) {
461  			buffer = realloc(buffer,
462  					bufferSize += PIPE_BUFFER_INCREMENT);
463  			(void) memset(buffer + bufferIndex, 0,
464  				bufferSize - bufferIndex);
465  		}
466  	}
467  
468  	(void) close(ipipe[0]);		/* Close read side of pipe */
469  
470  	/* Get subprocess exit status */
471  
472  	for (;;) {
473  		resultPid = waitpid(pid, &status, 0L);
474  		lerrno = (resultPid == -1 ? errno : 0);
475  
476  		/* break loop if child process status reaped */
477  
478  		if (resultPid != -1) {
479  			break;
480  		}
481  
482  		/* break loop if not interrupted out of waitpid */
483  
484  		if (errno != EINTR) {
485  			break;
486  		}
487  	}
488  
489  	/*
490  	 * If the child process terminated due to a call to exit(), then
491  	 * set results equal to the 8-bit exit status of the child process;
492  	 * otherwise, set the exit status to "-1" indicating that the child
493  	 * exited via a signal.
494  	 */
495  
496  	*r_status = WIFEXITED(status) ? WEXITSTATUS(status) : -1;
497  
498  	/* return appropriate output */
499  
500  	if (!*buffer) {
501  		/* No contents in output buffer - discard */
502  		free(buffer);
503  	} else if (r_results == (char **)NULL) {
504  		/* Not requested to return results - discard */
505  		free(buffer);
506  	} else {
507  		/* have output and request to return: pass to calling method */
508  		*r_results = buffer;
509  	}
510  
511  	errno = lerrno;
512  	return (resultPid == -1 ? -1 : 0);
513  }
514  
515  /*
516   * Name:	e_ExecCmdList
517   * Synopsis:	Execute Unix command and return results
518   * Description:	Execute a Unix command and return results and status
519   * Arguments:
520   *		r_status - [RO, *RW] - (int *)
521   *			Return (exit) status from Unix command
522   *		r_results - [RO, *RW] - (char **)
523   *			Any output generated by the Unix command to stdout
524   *			and to stderr
525   *			== (char *)NULL if no output generated
526   *		a_inputFile - [RO, *RO] - (char *)
527   *			Pointer to character string representing file to be
528   *			used as "standard input" for the command.
529   *			== (char *)NULL to use "/dev/null" as standard input
530   *		a_cmd - [RO, *RO] - (char *)
531   *			Pointer to character string representing the full path
532   *			of the Unix command to execute
533   *		... - [RO] (?)
534   *			Zero or more arguments to the Unix command
535   *			The argument list must be ended with (void *)NULL
536   * Returns:	int
537   *			== 0 - Command executed
538   *				Look at r_status for results of Unix command
539   *			!= 0 - problems executing command
540   *				r_status and r_results have no meaning
541   * NOTE:    	Any results returned is placed in new storage for the
542   *		calling method. The caller must use 'free' to dispose
543   *		of the storage once the results are no longer needed.
544   * NOTE:	If LU_SUCCESS is returned, 'r_status' must be queried to
545   *		determine the results of the Unix command.
546   */
547  
548  int
549  e_ExecCmdList(int *r_status, char **r_results,
550  	char *a_inputFile, char *a_cmd, ...)
551  {
552  	va_list		ap;		/* references variable argument list */
553  	char		*array[MAX_EXEC_CMD_ARGS+1];
554  	int		argno = 0;
555  
556  	/*
557  	 * Create argument array for exec system call
558  	 */
559  
560  	bzero(array, sizeof (array));
561  
562  	va_start(ap, a_cmd);	/* Begin variable argument processing */
563  
564  	for (argno = 0; argno < MAX_EXEC_CMD_ARGS; argno++) {
565  		array[argno] = va_arg(ap, char *);
566  		if (array[argno] == (char *)NULL) {
567  			break;
568  		}
569  	}
570  
571  	va_end(ap);
572  	return (e_ExecCmdArray(r_status, r_results, a_inputFile,
573  								a_cmd, array));
574  }
575