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