xref: /illumos-gate/usr/src/lib/libinstzones/common/zones_exec.c (revision e71ca95ca6de23d33b54cb55cefdef30bc7c969b)
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 
28 
29 /*
30  * Module:	zones_exec.c
31  * Group:	libinstzones
32  * Description:	Provide "zones" execution interface for install
33  *		consolidation code
34  *
35  * Public Methods:
36  *
37  * z_ExecCmdArray - Execute a Unix command and return results and status
38  * _zexec - run a command with arguments on a specified zone
39  * _zexec_init_template - used by _zexec to establish contracts
40  * _z_zone_exec - Execute a Unix command in a specified zone and return results
41  * z_ExecCmdList - Execute a Unix command and return results and status
42  */
43 
44 /*
45  * System includes
46  */
47 
48 #include <stdio.h>
49 #include <stdlib.h>
50 #include <unistd.h>
51 #include <fcntl.h>
52 #include <ctype.h>
53 #include <sys/types.h>
54 #include <sys/param.h>
55 #include <string.h>
56 #include <strings.h>
57 #include <stdarg.h>
58 #include <limits.h>
59 #include <errno.h>
60 #include <signal.h>
61 #include <wait.h>
62 #include <stropts.h>
63 #include <libintl.h>
64 #include <locale.h>
65 #include <libcontract.h>
66 #include <sys/contract/process.h>
67 #include <sys/ctfs.h>
68 #include <assert.h>
69 
70 /*
71  * local includes
72  */
73 
74 #include "instzones_lib.h"
75 #include "zones_strings.h"
76 
77 /*
78  * Private structures
79  */
80 
81 /*
82  * Library Function Prototypes
83  */
84 
85 /*
86  * Local Function Prototypes
87  */
88 
89 /*
90  * global internal (private) declarations
91  */
92 
93 /*
94  * *****************************************************************************
95  * global external (public) functions
96  * *****************************************************************************
97  */
98 
99 /*
100  * Name:	z_ExecCmdArray
101  * Synopsis:	Execute Unix command and return results
102  * Description:	Execute a Unix command and return results and status
103  * Arguments:
104  *		r_status - [RO, *RW] - (int *)
105  *			Return (exit) status from Unix command:
106  *			== -1 : child terminated with a signal
107  *			!= -1 : lower 8-bit value child passed to exit()
108  *		r_results - [RO, *RW] - (char **)
109  *			Any output generated by the Unix command to stdout
110  *			and to stderr
111  *			== (char *)NULL if no output generated
112  *		a_inputFile - [RO, *RO] - (char *)
113  *			Pointer to character string representing file to be
114  *			used as "standard input" for the command.
115  *			== (char *)NULL to use "/dev/null" as standard input
116  *		a_cmd - [RO, *RO] - (char *)
117  *			Pointer to character string representing the full path
118  *			of the Unix command to execute
119  *		char **a_args - [RO, *RO] - (char **)
120  *			List of character strings representing the arguments
121  *			to be passed to the Unix command. The list must be
122  *			terminated with an element that is (char *)NULL
123  * Returns:	int
124  *			== 0 - Command executed
125  *				Look at r_status for results of Unix command
126  *			!= 0 - problems executing command
127  *				r_status and r_results have no meaning;
128  *				r_status will be -1
129  *				r_results will be NULL
130  * NOTE:    	Any results returned is placed in new storage for the
131  *		calling method. The caller must use 'free' to dispose
132  *		of the storage once the results are no longer needed.
133  * NOTE:	If 0 is returned, 'r_status' must be queried to
134  *		determine the results of the Unix command.
135  * NOTE:	The system "errno" value from immediately after waitpid() call
136  *		is preserved for the calling method to use to determine
137  *		the system reason why the operation failed.
138  */
139 
140 int
141 z_ExecCmdArray(int *r_status, char **r_results,
142     char *a_inputFile, char *a_cmd, char **a_args)
143 {
144 	char		*buffer;
145 	int		bufferIndex;
146 	int		bufferSize;
147 	int		ipipe[2] = {0, 0};
148 	int		lerrno;
149 	int		status;
150 	int		stdinfile = -1;
151 	pid_t		pid;
152 	pid_t		resultPid;
153 
154 	/* entry assertions */
155 
156 	assert(r_status != NULL);
157 	assert(a_cmd != NULL);
158 	assert(*a_cmd != '\0');
159 	assert(a_args != NULL);
160 
161 	/* reset return results buffer pointer */
162 
163 	if (r_results != (char **)NULL) {
164 		*r_results = (char *)NULL;
165 	}
166 
167 	*r_status = -1;
168 
169 	/*
170 	 * See if command exists
171 	 */
172 
173 	if (access(a_cmd, F_OK|X_OK) != 0) {
174 		return (-1);
175 	}
176 
177 	/*
178 	 * See if input file exists
179 	 */
180 
181 	if (a_inputFile != (char *)NULL) {
182 		stdinfile = open(a_inputFile, O_RDONLY);
183 	} else {
184 		stdinfile = open("/dev/null", O_RDONLY); /* stdin = /dev/null */
185 	}
186 
187 	if (stdinfile < 0) {
188 		return (-1);
189 	}
190 
191 	/*
192 	 * Create a pipe to be used to capture the command output
193 	 */
194 
195 	if (pipe(ipipe) != 0) {
196 		(void) close(stdinfile);
197 		return (-1);
198 	}
199 
200 
201 	bufferSize = PIPE_BUFFER_INCREMENT;
202 	bufferIndex = 0;
203 	buffer = calloc(1, bufferSize);
204 	if (buffer == (char *)NULL) {
205 		(void) close(stdinfile);
206 		return (-1);
207 	}
208 
209 	/* flush standard i/o before creating new process */
210 
211 	(void) fflush(stderr);
212 	(void) fflush(stdout);
213 
214 	/*
215 	 * create new process to execute command in;
216 	 * vfork() is being used to avoid duplicating the parents
217 	 * memory space - this means that the child process may
218 	 * not modify any of the parents memory including the
219 	 * standard i/o descriptors - all the child can do is
220 	 * adjust interrupts and open files as a prelude to a
221 	 * call to exec().
222 	 */
223 
224 	pid = vfork();
225 
226 	if (pid == 0) {
227 		/*
228 		 * This is the forked (child) process ======================
229 		 */
230 
231 		int	i;
232 
233 		/* reset any signals to default */
234 
235 		for (i = 0; i < NSIG; i++) {
236 			(void) sigset(i, SIG_DFL);
237 		}
238 
239 		/* assign stdin, stdout, stderr as appropriate */
240 
241 		(void) dup2(stdinfile, STDIN_FILENO);
242 		(void) close(ipipe[0]);		/* close out pipe reader side */
243 		(void) dup2(ipipe[1], STDOUT_FILENO);
244 		(void) dup2(ipipe[1], STDERR_FILENO);
245 
246 		/* Close all open files except standard i/o */
247 
248 		closefrom(3);
249 
250 		/* execute target executable */
251 
252 		(void) execvp(a_cmd, a_args);
253 		perror(a_cmd);	/* Emit error msg - ends up in callers buffer */
254 		_exit(0x00FE);
255 	} else if (pid == -1) {
256 		_z_program_error(ERR_FORK, strerror(errno));
257 		*r_status = -1;
258 		return (-1);
259 	}
260 
261 	/*
262 	 * This is the forking (parent) process ====================
263 	 */
264 
265 	(void) close(stdinfile);
266 	(void) close(ipipe[1]);		/* Close write side of pipe */
267 
268 	/*
269 	 * Spin reading data from the child into the buffer - when the read eofs
270 	 * the child has exited
271 	 */
272 
273 	for (;;) {
274 		ssize_t	bytesRead;
275 
276 		/* read as much child data as there is available buffer space */
277 
278 		bytesRead = read(ipipe[0], buffer + bufferIndex,
279 		    bufferSize - bufferIndex);
280 
281 		/* break out of read loop if end-of-file encountered */
282 
283 		if (bytesRead == 0) {
284 			break;
285 		}
286 
287 		/* if error, continue if recoverable, else break out of loop */
288 
289 		if (bytesRead == -1) {
290 			/* try again: EAGAIN - insufficient resources */
291 
292 			if (errno == EAGAIN) {
293 				continue;
294 			}
295 
296 			/* try again: EINTR - interrupted system call */
297 
298 			if (errno == EINTR) {
299 				continue;
300 			}
301 
302 			/* break out of loop - error not recoverable */
303 			break;
304 		}
305 
306 		/* at least 1 byte read: expand buffer if at end */
307 
308 		bufferIndex += bytesRead;
309 		if (bufferIndex >= bufferSize) {
310 			buffer = realloc(buffer,
311 			    bufferSize += PIPE_BUFFER_INCREMENT);
312 			(void) memset(buffer + bufferIndex, 0,
313 			    bufferSize - bufferIndex);
314 		}
315 	}
316 
317 	(void) close(ipipe[0]);		/* Close read side of pipe */
318 
319 	/* Get subprocess exit status */
320 
321 	for (;;) {
322 		resultPid = waitpid(pid, &status, 0L);
323 		lerrno = (resultPid == -1 ? errno : 0);
324 
325 		/* break loop if child process status reaped */
326 
327 		if (resultPid != -1) {
328 			break;
329 		}
330 
331 		/* break loop if not interrupted out of waitpid */
332 
333 		if (errno != EINTR) {
334 			break;
335 		}
336 	}
337 
338 	/*
339 	 * If the child process terminated due to a call to exit(), then
340 	 * set results equal to the 8-bit exit status of the child process;
341 	 * otherwise, set the exit status to "-1" indicating that the child
342 	 * exited via a signal.
343 	 */
344 
345 	*r_status = WIFEXITED(status) ? WEXITSTATUS(status) : -1;
346 
347 	/* return appropriate output */
348 
349 	if (!*buffer) {
350 		/* No contents in output buffer - discard */
351 		free(buffer);
352 	} else if (r_results == (char **)NULL) {
353 		/* Not requested to return results - discard */
354 		free(buffer);
355 	} else {
356 		/* have output and request to return: pass to calling method */
357 		*r_results = buffer;
358 	}
359 
360 	errno = lerrno;
361 	return (resultPid == -1 ? -1 : 0);
362 }
363 
364 /*
365  * Name:	_zexec
366  * Description:	run a command with arguments on a specified zone
367  * Arguments:	a_zoneName - pointer to string representing the name of the zone
368  *			to execute the specified command in
369  *		a_path - pointer to string representing the full path *in the
370  *			non-global zone named by a_zoneName* of the Unix command
371  *			to be executed
372  *		a_argv[] - Pointer to array of character strings representing
373  *			the arguments to be passed to the Unix command. The list
374  *			must be termianted with an element that is (char *)NULL
375  *		NOTE: a_argv[0] is the "command name" passed to the command
376  * Returns:	int
377  *			This function must be treated like a call to exec()
378  *			If the exec() is successful, the thread of control is
379  *			NOT returned, and the process will exit when completed.
380  *			If this function returns, it means the exec() could not
381  *			be done, or another fatal error occurred.
382  */
383 
384 int
385 _zexec(const char *a_zoneName, const char *a_path, char *a_argv[])
386 {
387 	zoneid_t zoneid;
388 	zone_state_t st;
389 	char **new_env = { NULL };
390 	priv_set_t *privset;
391 
392 	/* entry assertions */
393 
394 	assert(a_zoneName != NULL);
395 	assert(*a_zoneName != '\0');
396 	assert(a_path != NULL);
397 	assert(*a_path != '\0');
398 
399 	/* establish locale settings */
400 
401 	(void) setlocale(LC_ALL, "");
402 	(void) textdomain(TEXT_DOMAIN);
403 
404 	/* can only be invoked from within the global zone */
405 
406 	if (getzoneid() != GLOBAL_ZONEID) {
407 		_z_program_error(ERR_ZEXEC_NOT_IN_GZ, a_zoneName);
408 		return (-1);
409 	}
410 
411 	if (strcmp(a_zoneName, GLOBAL_ZONENAME) == 0) {
412 		_z_program_error(ERR_ZEXEC_GZUSED, a_zoneName);
413 		return (-1);
414 	}
415 
416 	/* get the state of the specified zone */
417 
418 	if (zone_get_state((char *)a_zoneName, &st) != Z_OK) {
419 		_z_program_error(ERR_ZEXEC_BADZONE, a_zoneName);
420 		return (-1);
421 	}
422 
423 	if (st < ZONE_STATE_INSTALLED) {
424 		_z_program_error(ERR_ZEXEC_BADSTATE, a_zoneName,
425 		    zone_state_str(st));
426 		return (-1);
427 	}
428 
429 	if (st != ZONE_STATE_RUNNING && st != ZONE_STATE_MOUNTED) {
430 		_z_program_error(ERR_ZEXEC_NOTRUNNING, a_zoneName,
431 		    zone_state_str(st));
432 		return (-1);
433 	}
434 
435 	/*
436 	 * In both console and non-console cases, we require all privs.
437 	 * In the console case, because we may need to startup zoneadmd.
438 	 * In the non-console case in order to do zone_enter(2), zonept()
439 	 * and other tasks.
440 	 *
441 	 * Future work: this solution is temporary.  Ultimately, we need to
442 	 * move to a flexible system which allows the global admin to
443 	 * designate that a particular user can zlogin (and probably zlogin
444 	 * -C) to a particular zone.  This all-root business we have now is
445 	 * quite sketchy.
446 	 */
447 
448 	if ((privset = priv_allocset()) == NULL) {
449 		_z_program_error(ERR_ZEXEC_PRIV_ALLOCSET, a_zoneName,
450 		    strerror(errno));
451 		return (-1);
452 	}
453 
454 	if (getppriv(PRIV_EFFECTIVE, privset) != 0) {
455 		_z_program_error(ERR_ZEXEC_GETPPRIV, a_zoneName,
456 		    strerror(errno));
457 		priv_freeset(privset);
458 		return (-1);
459 	}
460 
461 	if (priv_isfullset(privset) == B_FALSE) {
462 		_z_program_error(ERR_ZEXEC_PRIVS, a_zoneName);
463 		priv_freeset(privset);
464 		return (-1);
465 	}
466 	priv_freeset(privset);
467 
468 	if ((zoneid = getzoneidbyname(a_zoneName)) == -1) {
469 		_z_program_error(ERR_ZEXEC_NOZONEID, a_zoneName,
470 		    strerror(errno));
471 		return (-1);
472 	}
473 
474 	if ((new_env = _zexec_prep_env()) == NULL) {
475 		_z_program_error(ERR_ZEXEC_ASSEMBLE, a_zoneName);
476 		return (-1);
477 	}
478 
479 	/*
480 	 * In case any of stdin, stdout or stderr are streams,
481 	 * anchor them to prevent malicious I_POPs.
482 	 *
483 	 * Future work: use pipes to entirely eliminate FD leakage
484 	 * into the zone.
485 	 */
486 
487 	(void) ioctl(STDIN_FILENO, I_ANCHOR);
488 	(void) ioctl(STDOUT_FILENO, I_ANCHOR);
489 	(void) ioctl(STDERR_FILENO, I_ANCHOR);
490 
491 	if (zone_enter(zoneid) == -1) {
492 		int	lerrno = errno;
493 
494 		_z_program_error(ERR_ZEXEC_ZONEENTER, a_zoneName,
495 		    strerror(errno));
496 
497 		if (lerrno == EFAULT) {
498 			_z_program_error(ERR_ZEXEC_EFAULT, a_zoneName);
499 		}
500 
501 		free(new_env);
502 
503 		return (-1);
504 	}
505 
506 	(void) execve(a_path, &a_argv[0], new_env);
507 
508 	_z_program_error(ERR_ZEXEC_EXECFAILURE, a_zoneName, strerror(errno));
509 
510 	return (-1);
511 }
512 
513 /*
514  * Name:	_zexec_init_template
515  * Description:	used by _zexec to establish contracts
516  */
517 
518 int
519 _zexec_init_template(void)
520 {
521 	int fd;
522 	int err = 0;
523 
524 	fd = open64(CTFS_ROOT "/process/template", O_RDWR);
525 	if (fd == -1) {
526 		return (-1);
527 	}
528 
529 	/*
530 	 * zlogin doesn't do anything with the contract.
531 	 * Deliver no events, don't inherit, and allow it to be orphaned.
532 	 */
533 	err |= ct_tmpl_set_critical(fd, 0);
534 	err |= ct_tmpl_set_informative(fd, 0);
535 	err |= ct_pr_tmpl_set_fatal(fd, CT_PR_EV_HWERR);
536 	err |= ct_pr_tmpl_set_param(fd, CT_PR_PGRPONLY | CT_PR_REGENT);
537 	if (err || ct_tmpl_activate(fd)) {
538 		(void) close(fd);
539 		return (-1);
540 	}
541 
542 	return (fd);
543 }
544 
545 /*
546  * Helper routine for _zexec_prep_env below.
547  */
548 char *
549 _zexec_add_env(char *name, char *value)
550 {
551 	size_t sz = strlen(name) + strlen(value) + 1;
552 	char *str;
553 
554 	if ((str = malloc(sz)) == NULL)
555 		return (NULL);
556 
557 	(void) snprintf(str, sz, "%s%s", name, value);
558 	return (str);
559 }
560 
561 /*
562  * Prepare envp array for exec'd process.
563  */
564 char **
565 _zexec_prep_env()
566 {
567 	int e = 0, size = 1;
568 	char **new_env, *estr;
569 	char *term = getenv("TERM");
570 
571 	size++;	/* for $PATH */
572 	if (term != NULL)
573 		size++;
574 
575 	/*
576 	 * In failsafe mode we set $HOME
577 	 */
578 	size++;
579 
580 	/*
581 	 * In failsafe mode we set $SHELL, since login won't be around to do it.
582 	 */
583 	size++;
584 
585 	if ((new_env = malloc(sizeof (char *) * size)) == NULL)
586 		return (NULL);
587 
588 	if ((estr = _zexec_add_env("PATH=", ZONE_DEF_PATH)) == NULL) {
589 		free(new_env);
590 		return (NULL);
591 	}
592 	new_env[e++] = estr;
593 
594 	if (term != NULL) {
595 		if ((estr = _zexec_add_env("TERM=", term)) == NULL) {
596 			free(new_env);
597 			return (NULL);
598 		}
599 		new_env[e++] = estr;
600 	}
601 
602 	if ((estr = _zexec_add_env("HOME=", "/")) == NULL) {
603 		free(new_env);
604 		return (NULL);
605 	}
606 	new_env[e++] = estr;
607 
608 	if ((estr = _zexec_add_env("SHELL=", ZONE_FAILSAFESHELL)) == NULL) {
609 		free(new_env);
610 		return (NULL);
611 	}
612 	new_env[e++] = estr;
613 
614 	new_env[e++] = NULL;
615 
616 	return (new_env);
617 }
618 
619 /*
620  * Name:	_z_zone_exec
621  * Description:	Execute a Unix command in a specified zone and return results
622  * Arguments:
623  *		r_status - [RO, *RW] - (int *)
624  *			Return (exit) status from Unix command:
625  *			== -1 : child terminated with a signal
626  *			!= -1 : lower 8-bit value child passed to exit()
627  *		r_results - [RO, *RW] - (char **)
628  *			Any output generated by the Unix command to stdout
629  *			and to stderr
630  *			== (char *)NULL if no output generated
631  *		a_inputFile - [RO, *RO] - (char *)
632  *			Pointer to character string representing file to be
633  *			used as "standard input" for the command.
634  *			== (char *)NULL to use "/dev/null" as standard input
635  *		a_path - [RO, *RO] - (char *)
636  *			Pointer to character string representing the full path
637  *			*in the non-global zone named by a_zoneName*of the Unix
638  *			command to be executed
639  *		char **a_args - [RO, *RO] - (char **)
640  *			List of character strings representing the arguments
641  *			to be passed to the Unix command.
642  *			NOTE: The list must be terminated with an element that
643  *			----- is (char *)NULL
644  *			NOTE: a_argv[0] is the "command name" passed to the
645  *			----- command executed in the specified non-global zone
646  *		a_zoneName - pointer to string representing the name of the zone
647  *			to execute the specified command in
648  *		a_fds - Pointer to array of integers representing file
649  *			descriptors to remain open during the call - all
650  *			file descriptors above STDERR_FILENO not in this
651  *			list will be closed.
652  * Returns:	int
653  *			== 0 - Command executed
654  *				Look at r_status for results of Unix command
655  *			!= 0 - problems executing command
656  *				r_status and r_results have no meaning;
657  *				r_status will be -1
658  *				r_results will be NULL
659  *			The return (exit) code from the specified Unix command
660  *			Special return codes:
661  *			-1 : failure to exec process
662  *			-2 : could not create contract for greenline
663  *			-3 : fork() failed
664  *			-4 : could not open stdin source file
665  *			-5 : error from 'waitpid' other than EINTR
666  *			-6 : zones are not supported
667  *			-7 : interrupt received
668  * NOTE:	All file descriptores other than 0, 1 and 2 are closed except
669  *		for those file descriptors listed in the a_fds array.
670  */
671 
672 int
673 _z_zone_exec(int *r_status, char **r_results, char *a_inputFile,
674 	char *a_path, char *a_argv[], const char *a_zoneName, int *a_fds)
675 {
676 	struct sigaction	nact;
677 	struct sigaction	oact;
678 	char			*buffer;
679 	char			*thisZoneName;
680 	int			bufferIndex;
681 	int			bufferSize;
682 	int			exit_no;
683 	int			ipipe[2] = {0, 0};
684 	int			lerrno;
685 	int			n;
686 	int			status;
687 	int			stdinfile = -1;
688 	int			tmpl_fd;
689 	pid_t			child_pid;
690 	pid_t			result_pid;
691 	void			(*funcSighup)();
692 	void			(*funcSigint)();
693 
694 	/* entry assertions */
695 
696 	assert(a_path != (char *)NULL);
697 	assert(*a_path != '\0');
698 	assert(a_argv != (char **)NULL);
699 	assert(a_argv[0] != (char *)NULL);
700 	assert(*a_argv[0] != '\0');
701 	assert(a_zoneName != (char *)NULL);
702 
703 	/*
704 	 * if requested to execute in current zone name, directly execute
705 	 */
706 
707 	thisZoneName = z_get_zonename();
708 	status = (strcmp(a_zoneName, thisZoneName) == 0);
709 
710 	/* entry debugging info */
711 
712 	_z_echoDebug(DBG_ZONE_EXEC_CMD_ENTER, a_path, a_zoneName, thisZoneName);
713 	(void) free(thisZoneName);
714 	for (n = 0; a_argv[n]; n++) {
715 		_z_echoDebug(DBG_ARG, n, a_argv[n]);
716 	}
717 
718 	/* if this zone, just exec the command directly */
719 
720 	if (status != 0) {
721 		return (z_ExecCmdArray(r_status, r_results, a_inputFile,
722 		    a_path, a_argv));
723 	}
724 
725 	/* reset return results buffer pointer */
726 
727 	if (r_results != (char **)NULL) {
728 		*r_results = (char *)NULL;
729 	}
730 
731 	*r_status = -1;	/* -1 : failure to exec process */
732 
733 	/* if zones are not implemented, return TRUE */
734 
735 	if (!z_zones_are_implemented()) {
736 		return (-6);	/* -6 : zones are not supported */
737 	}
738 
739 	if ((tmpl_fd = _zexec_init_template()) == -1) {
740 		_z_program_error(ERR_CANNOT_CREATE_CONTRACT, strerror(errno));
741 		return (-2);	/* -2 : cannot create greenline contract */
742 	}
743 
744 	/*
745 	 * See if input file exists
746 	 */
747 
748 	if (a_inputFile != (char *)NULL) {
749 		stdinfile = open(a_inputFile, O_RDONLY);
750 	} else {
751 		stdinfile = open("/dev/null", O_RDONLY); /* stdin = /dev/null */
752 	}
753 
754 	if (stdinfile < 0) {
755 		return (-4);	/* -4 : could not open stdin source file */
756 	}
757 
758 	/*
759 	 * Create a pipe to be used to capture the command output
760 	 */
761 
762 	if (pipe(ipipe) != 0) {
763 		(void) close(stdinfile);
764 		return (-1);
765 	}
766 
767 	bufferSize = PIPE_BUFFER_INCREMENT;
768 	bufferIndex = 0;
769 	buffer = calloc(1, bufferSize);
770 	if (buffer == (char *)NULL) {
771 		(void) close(stdinfile);
772 		return (-1);
773 	}
774 
775 	/* flush standard i/o before creating new process */
776 
777 	(void) fflush(stderr);
778 	(void) fflush(stdout);
779 
780 	/*
781 	 * hold SIGINT/SIGHUP signals and reset signal received counter;
782 	 * after the fork1() the parent and child need to setup their respective
783 	 * interrupt handling and release the hold on the signals
784 	 */
785 
786 	(void) sighold(SIGINT);
787 	(void) sighold(SIGHUP);
788 
789 	_z_global_data._z_SigReceived = 0;	/* no signals received */
790 
791 	/*
792 	 * fork off a new process to execute command in;
793 	 * fork1() is used instead of vfork() so the child process can
794 	 * perform operations that would modify the parent process if
795 	 * vfork() were used
796 	 */
797 
798 	child_pid = fork1();
799 
800 	if (child_pid < 0) {
801 		/*
802 		 * *************************************************************
803 		 * fork failed!
804 		 * *************************************************************
805 		 */
806 
807 		(void) ct_tmpl_clear(tmpl_fd);
808 		(void) close(tmpl_fd);
809 		(void) free(buffer);
810 		_z_program_error(ERR_FORK, strerror(errno));
811 
812 		/* release hold on signals */
813 		(void) sigrelse(SIGHUP);
814 		(void) sigrelse(SIGINT);
815 
816 		return (-3);	/* -3 : fork() failed */
817 	}
818 
819 	if (child_pid == 0) {
820 		int	i;
821 
822 		/*
823 		 * *************************************************************
824 		 * This is the forked (child) process
825 		 * *************************************************************
826 		 */
827 
828 		(void) ct_tmpl_clear(tmpl_fd);
829 		(void) close(tmpl_fd);
830 
831 		/* reset any signals to default */
832 
833 		for (i = 0; i < NSIG; i++) {
834 			(void) sigset(i, SIG_DFL);
835 		}
836 
837 		/* assign stdin, stdout, stderr as appropriate */
838 
839 		(void) dup2(stdinfile, STDIN_FILENO);
840 		(void) close(ipipe[0]);		/* close out pipe reader side */
841 		(void) dup2(ipipe[1], STDOUT_FILENO);
842 		(void) dup2(ipipe[1], STDERR_FILENO);
843 
844 		/*
845 		 * close all file descriptors not in the a_fds list
846 		 */
847 
848 		(void) fdwalk(&_z_close_file_descriptors, (void *)a_fds);
849 
850 		/* release all held signals */
851 
852 		(void) sigrelse(SIGHUP);
853 		(void) sigrelse(SIGINT);
854 
855 		/* execute command in the specified non-global zone */
856 
857 		_exit(_zexec(a_zoneName, a_path, a_argv));
858 	}
859 
860 	/*
861 	 * *********************************************************************
862 	 * This is the forking (parent) process
863 	 * *********************************************************************
864 	 */
865 
866 	/* register child process i.d. so signal handlers can pass signal on */
867 
868 	_z_global_data._z_ChildProcessId = child_pid;
869 
870 	/*
871 	 * setup signal handlers for SIGINT and SIGHUP and release hold
872 	 */
873 
874 	/* hook SIGINT to _z_sig_trap() */
875 
876 	nact.sa_handler = _z_sig_trap;
877 	nact.sa_flags = SA_RESTART;
878 	(void) sigemptyset(&nact.sa_mask);
879 
880 	if (sigaction(SIGINT, &nact, &oact) < 0) {
881 		funcSigint = SIG_DFL;
882 	} else {
883 		funcSigint = oact.sa_handler;
884 	}
885 
886 	/* hook SIGHUP to _z_sig_trap() */
887 
888 	nact.sa_handler = _z_sig_trap;
889 	nact.sa_flags = SA_RESTART;
890 	(void) sigemptyset(&nact.sa_mask);
891 
892 	if (sigaction(SIGHUP, &nact, &oact) < 0) {
893 		funcSighup = SIG_DFL;
894 	} else {
895 		funcSighup = oact.sa_handler;
896 	}
897 
898 	/* release hold on signals */
899 
900 	(void) sigrelse(SIGHUP);
901 	(void) sigrelse(SIGINT);
902 
903 	(void) ct_tmpl_clear(tmpl_fd);
904 	(void) close(tmpl_fd);
905 
906 	(void) close(stdinfile);
907 	(void) close(ipipe[1]);		/* Close write side of pipe */
908 
909 	/*
910 	 * Spin reading data from the child into the buffer - when the read eofs
911 	 * the child has exited
912 	 */
913 
914 	for (;;) {
915 		ssize_t	bytesRead;
916 
917 		/* read as much child data as there is available buffer space */
918 
919 		bytesRead = read(ipipe[0], buffer + bufferIndex,
920 		    bufferSize - bufferIndex);
921 
922 		/* break out of read loop if end-of-file encountered */
923 
924 		if (bytesRead == 0) {
925 			break;
926 		}
927 
928 		/* if error, continue if recoverable, else break out of loop */
929 
930 		if (bytesRead == -1) {
931 			/* try again: EAGAIN - insufficient resources */
932 
933 			if (errno == EAGAIN) {
934 				continue;
935 			}
936 
937 			/* try again: EINTR - interrupted system call */
938 
939 			if (errno == EINTR) {
940 				continue;
941 			}
942 
943 			/* break out of loop - error not recoverable */
944 			break;
945 		}
946 
947 		/* at least 1 byte read: expand buffer if at end */
948 
949 		bufferIndex += bytesRead;
950 		if (bufferIndex >= bufferSize) {
951 			buffer = realloc(buffer,
952 			    bufferSize += PIPE_BUFFER_INCREMENT);
953 			(void) memset(buffer + bufferIndex, 0,
954 			    bufferSize - bufferIndex);
955 		}
956 	}
957 
958 	(void) close(ipipe[0]);		/* Close read side of pipe */
959 
960 	/*
961 	 * wait for the process to exit, reap child exit status
962 	 */
963 
964 	for (;;) {
965 		result_pid = waitpid(child_pid, &status, 0L);
966 		lerrno = (result_pid == -1 ? errno : 0);
967 
968 		/* break loop if child process status reaped */
969 
970 		if (result_pid != -1) {
971 			break;
972 		}
973 
974 		/* break loop if not interrupted out of waitpid */
975 
976 		if (errno != EINTR) {
977 			break;
978 		}
979 	}
980 
981 	/* reset child process i.d. so signal handlers do not pass signals on */
982 
983 	_z_global_data._z_ChildProcessId = -1;
984 
985 	/*
986 	 * If the child process terminated due to a call to exit(), then
987 	 * set results equal to the 8-bit exit status of the child process;
988 	 * otherwise, set the exit status to "-1" indicating that the child
989 	 * exited via a signal.
990 	 */
991 
992 	if (WIFEXITED(status)) {
993 		*r_status = WEXITSTATUS(status);
994 		if ((_z_global_data._z_SigReceived != 0) && (*r_status == 0)) {
995 			*r_status = 1;
996 		}
997 	} else {
998 		*r_status = -1;	/* -1 : failure to exec process */
999 	}
1000 
1001 	/* determine proper exit code */
1002 
1003 	if (result_pid == -1) {
1004 		exit_no = -5;	/* -5 : error from 'waitpid' other than EINTR */
1005 	} else if (_z_global_data._z_SigReceived != 0) {
1006 		exit_no = -7;	/* -7 : interrupt received */
1007 	} else {
1008 		exit_no = 0;
1009 	}
1010 
1011 	/* return appropriate output */
1012 
1013 	if (!*buffer) {
1014 		/* No contents in output buffer - discard */
1015 		free(buffer);
1016 	} else if (r_results == (char **)NULL) {
1017 		/* Not requested to return results - discard */
1018 		free(buffer);
1019 	} else {
1020 		/* have output and request to return: pass to calling method */
1021 		*r_results = buffer;
1022 	}
1023 
1024 	/*
1025 	 * reset signal handlers
1026 	 */
1027 
1028 	/* reset SIGINT */
1029 
1030 	nact.sa_handler = funcSigint;
1031 	nact.sa_flags = SA_RESTART;
1032 	(void) sigemptyset(&nact.sa_mask);
1033 
1034 	(void) sigaction(SIGINT, &nact, (struct sigaction *)NULL);
1035 
1036 	/* reset SIGHUP */
1037 
1038 	nact.sa_handler = funcSighup;
1039 	nact.sa_flags = SA_RESTART;
1040 	(void) sigemptyset(&nact.sa_mask);
1041 
1042 	(void) sigaction(SIGHUP, &nact, (struct sigaction *)NULL);
1043 
1044 	/*
1045 	 * if signal received during command execution, interrupt
1046 	 * this process now.
1047 	 */
1048 
1049 	if (_z_global_data._z_SigReceived != 0) {
1050 		(void) kill(getpid(), SIGINT);
1051 	}
1052 
1053 	/* set errno and return */
1054 
1055 	errno = lerrno;
1056 
1057 	return (exit_no);
1058 }
1059 
1060 /*
1061  * Name:	z_ExecCmdList
1062  * Synopsis:	Execute Unix command and return results
1063  * Description:	Execute a Unix command and return results and status
1064  * Arguments:
1065  *		r_status - [RO, *RW] - (int *)
1066  *			Return (exit) status from Unix command
1067  *		r_results - [RO, *RW] - (char **)
1068  *			Any output generated by the Unix command to stdout
1069  *			and to stderr
1070  *			== (char *)NULL if no output generated
1071  *		a_inputFile - [RO, *RO] - (char *)
1072  *			Pointer to character string representing file to be
1073  *			used as "standard input" for the command.
1074  *			== (char *)NULL to use "/dev/null" as standard input
1075  *		a_cmd - [RO, *RO] - (char *)
1076  *			Pointer to character string representing the full path
1077  *			of the Unix command to execute
1078  *		... - [RO] (?)
1079  *			Zero or more arguments to the Unix command
1080  *			The argument list must be ended with (void *)NULL
1081  * Returns:	int
1082  *			== 0 - Command executed
1083  *				Look at r_status for results of Unix command
1084  *			!= 0 - problems executing command
1085  *				r_status and r_results have no meaning
1086  * NOTE:    	Any results returned is placed in new storage for the
1087  *		calling method. The caller must use 'free' to dispose
1088  *		of the storage once the results are no longer needed.
1089  * NOTE:	If LU_SUCCESS is returned, 'r_status' must be queried to
1090  *		determine the results of the Unix command.
1091  */
1092 
1093 /*VARARGS*/
1094 int
1095 z_ExecCmdList(int *r_status, char **r_results,
1096 	char *a_inputFile, char *a_cmd, ...)
1097 {
1098 	va_list		ap;		/* references variable argument list */
1099 	char		*array[MAX_EXEC_CMD_ARGS+1];
1100 	int		argno = 0;
1101 
1102 	/*
1103 	 * Create argument array for exec system call
1104 	 */
1105 
1106 	bzero(array, sizeof (array));
1107 
1108 	va_start(ap, a_cmd);	/* Begin variable argument processing */
1109 
1110 	for (argno = 0; argno < MAX_EXEC_CMD_ARGS; argno++) {
1111 		array[argno] = va_arg(ap, char *);
1112 		if (array[argno] == (char *)NULL) {
1113 			break;
1114 		}
1115 	}
1116 
1117 	va_end(ap);
1118 	return (z_ExecCmdArray(r_status, r_results, a_inputFile,
1119 	    a_cmd, array));
1120 }
1121