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
z_ExecCmdArray(int * r_status,char ** r_results,char * a_inputFile,char * a_cmd,char ** a_args)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
_zexec(const char * a_zoneName,const char * a_path,char * a_argv[])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
_zexec_init_template(void)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 *
_zexec_add_env(char * name,char * value)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 **
_zexec_prep_env()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
_z_zone_exec(int * r_status,char ** r_results,char * a_inputFile,char * a_path,char * a_argv[],const char * a_zoneName,int * a_fds)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
z_ExecCmdList(int * r_status,char ** r_results,char * a_inputFile,char * a_cmd,...)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