Changeset 92617 in vbox for trunk/src/VBox/Runtime/r3/posix
- Timestamp:
- Nov 29, 2021 1:04:10 AM (3 years ago)
- svn:sync-xref-src-repo-rev:
- 148509
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Runtime/r3/posix/process-creation-posix.cpp
r82968 r92617 112 112 # define _PATH_STDPATH "/sbin:/usr/sbin:/bin:/usr/bin" 113 113 #endif 114 #ifndef _PATH_BSHELL 115 # define _PATH_BSHELL "/bin/sh" 116 #endif 114 117 115 118 … … 118 121 119 122 #include <iprt/assert.h> 123 #include <iprt/ctype.h> 120 124 #include <iprt/env.h> 121 125 #include <iprt/err.h> … … 146 150 typedef RTPROCPAMARGS *PRTPROCPAMARGS; 147 151 #endif 152 153 154 /********************************************************************************************************************************* 155 * Global Variables * 156 *********************************************************************************************************************************/ 157 /** Environment dump marker used with CSH. */ 158 static const char g_szEnvMarkerBegin[] = "IPRT_EnvEnvEnv_Begin_EnvEnvEnv"; 159 /** Environment dump marker used with CSH. */ 160 static const char g_szEnvMarkerEnd[] = "IPRT_EnvEnvEnv_End_EnvEnvEnv"; 161 162 163 /********************************************************************************************************************************* 164 * Internal Functions * 165 *********************************************************************************************************************************/ 166 static int rtProcPosixCreateInner(const char *pszExec, const char * const *papszArgs, RTENV hEnv, RTENV hEnvToUse, 167 uint32_t fFlags, const char *pszAsUser, uid_t uid, gid_t gid, 168 unsigned cRedirFds, int *paRedirFds, PRTPROCESS phProcess); 148 169 149 170 … … 565 586 566 587 /** 567 * Create a very very basic environment for a user. 588 * Undos quoting and escape sequences and looks for stop characters. 589 * 590 * @returns Where to continue scanning in @a pszString. This points to the 591 * next character after the stop character, but for the zero terminator 592 * it points to the terminator character. 593 * @param pszString The string to undo quoting and escaping for. 594 * This is both input and output as the work is 595 * done in place. 596 * @param pfStoppedOnEqual Where to return whether we stopped work on a 597 * plain equal characater or not. If this is NULL, 598 * then the equal character is not a stop 599 * character, then only newline and the zero 600 * terminator are. 601 */ 602 static char *rtProcPosixProfileEnvUnquoteAndUnescapeString(char *pszString, bool *pfStoppedOnEqual) 603 { 604 if (pfStoppedOnEqual) 605 *pfStoppedOnEqual = false; 606 607 enum { kPlain, kSingleQ, kDoubleQ } enmState = kPlain; 608 char *pszDst = pszString; 609 for (;;) 610 { 611 char ch = *pszString++; 612 switch (ch) 613 { 614 default: 615 *pszDst++ = ch; 616 break; 617 618 case '\\': 619 { 620 char ch2; 621 if ( enmState == kSingleQ 622 || (ch2 = *pszString) == '\0' 623 || (enmState == kDoubleQ && strchr("\\$`\"\n", ch2) == NULL) ) 624 *pszDst++ = ch; 625 else 626 { 627 *pszDst++ = ch2; 628 pszString++; 629 } 630 break; 631 } 632 633 case '"': 634 if (enmState == kSingleQ) 635 *pszDst++ = ch; 636 else 637 enmState = enmState == kPlain ? kDoubleQ : kPlain; 638 break; 639 640 case '\'': 641 if (enmState == kDoubleQ) 642 *pszDst++ = ch; 643 else 644 enmState = enmState == kPlain ? kSingleQ : kPlain; 645 break; 646 647 case '\n': 648 if (enmState == kPlain) 649 { 650 *pszDst = '\0'; 651 return pszString; 652 } 653 *pszDst++ = ch; 654 break; 655 656 case '=': 657 if (enmState == kPlain && pfStoppedOnEqual) 658 { 659 *pszDst = '\0'; 660 *pfStoppedOnEqual = true; 661 return pszString; 662 } 663 *pszDst++ = ch; 664 break; 665 666 case '\0': 667 Assert(enmState == kPlain); 668 *pszDst = '\0'; 669 return pszString - 1; 670 } 671 } 672 } 673 674 675 /** 676 * Worker for rtProcPosixProfileEnvRunAndHarvest that parses the environment 677 * dump and loads it into hEnvToUse. 678 * 679 * @note This isn't entirely correct should any of the profile setup scripts 680 * unset any of the environment variables in the basic initial 681 * enviornment, but since that's unlikely and it's very convenient to 682 * have something half sensible as a basis if don't don't grok the dump 683 * entirely and would skip central stuff like PATH or HOME. 684 * 685 * @returns IPRT status code. 686 * @retval -VERR_PARSE_ERROR (positive, e.g. warning) if we run into trouble. 687 * @retval -VERR_INVALID_UTF8_ENCODING (positive, e.g. warning) if there are 688 * invalid UTF-8 in the environment. This isn't unlikely if the 689 * profile doesn't use UTF-8. This is unfortunately not something we 690 * can guess to accurately up front, so we don't do any guessing and 691 * hope everyone is sensible and use UTF-8. 692 * @param hEnvToUse The basic environment to extend with what we manage 693 * to parse here. 694 * @param pszEnvDump The environment dump to parse. Nominally in Bourne 695 * shell 'export -p' format. 696 * @param fWithMarkers Whether there are markers around the dump (C shell, 697 * tmux) or not. 698 */ 699 static int rtProcPosixProfileEnvHarvest(RTENV hEnvToUse, char *pszEnvDump, bool fWithMarkers) 700 { 701 LogFunc(("**** pszEnvDump start ****\n%s**** pszEnvDump end ****\n", pszEnvDump)); 702 703 /* 704 * Clip dump at markers if we're using them (C shell). 705 */ 706 if (fWithMarkers) 707 { 708 char *pszStart = strstr(pszEnvDump, g_szEnvMarkerBegin); 709 AssertReturn(pszStart, -VERR_PARSE_ERROR); 710 pszStart += sizeof(g_szEnvMarkerBegin) - 1; 711 if (*pszStart == '\n') 712 pszStart++; 713 pszEnvDump = pszStart; 714 715 char *pszEnd = strstr(pszStart, g_szEnvMarkerEnd); 716 AssertReturn(pszEnd, -VERR_PARSE_ERROR); 717 *pszEnd = '\0'; 718 } 719 720 /* 721 * Since we're using /bin/sh -c "export -p" for all the dumping, we should 722 * always get lines on the format: 723 * export VAR1="Value 1" 724 * export VAR2=Value2 725 * 726 * However, just in case something goes wrong, like bash doesn't think it 727 * needs to be posixly correct, try deal with the alternative where 728 * "declare -x " replaces the "export". 729 */ 730 const char *pszPrefix; 731 if ( strncmp(pszEnvDump, RT_STR_TUPLE("export")) == 0 732 && RT_C_IS_BLANK(pszEnvDump[6])) 733 pszPrefix = "export "; 734 else if ( strncmp(pszEnvDump, RT_STR_TUPLE("declare")) == 0 735 && RT_C_IS_BLANK(pszEnvDump[7]) 736 && pszEnvDump[8] == '-') 737 pszPrefix = "declare -x "; /* We only need to care about the non-array, non-function lines. */ 738 else 739 AssertFailedReturn(-VERR_PARSE_ERROR); 740 size_t const cchPrefix = strlen(pszPrefix); 741 742 /* 743 * Process the lines, ignoring stuff which we don't grok. 744 * The shell should quote problematic characters. Bash double quotes stuff 745 * by default, whereas almquist's shell does it as needed and only the value 746 * side. 747 */ 748 int rc = VINF_SUCCESS; 749 while (pszEnvDump && *pszEnvDump != '\0') 750 { 751 /* 752 * Skip the prefixing command. 753 */ 754 if ( cchPrefix == 0 755 || strncmp(pszEnvDump, pszPrefix, cchPrefix) == 0) 756 { 757 pszEnvDump += cchPrefix; 758 while (RT_C_IS_BLANK(*pszEnvDump)) 759 pszEnvDump++; 760 } 761 else 762 { 763 /* Oops, must find our bearings for some reason... */ 764 pszEnvDump = strchr(pszEnvDump, '\n'); 765 rc = -VERR_PARSE_ERROR; 766 continue; 767 } 768 769 /* 770 * Parse out the variable name using typical bourne shell escaping 771 * and quoting rules. 772 */ 773 /** @todo We should throw away lines that aren't propertly quoted, now we 774 * just continue and use what we found. */ 775 const char *pszVar = pszEnvDump; 776 bool fStoppedOnPlainEqual = false; 777 pszEnvDump = rtProcPosixProfileEnvUnquoteAndUnescapeString(pszEnvDump, &fStoppedOnPlainEqual); 778 const char *pszValue = pszEnvDump; 779 if (fStoppedOnPlainEqual) 780 pszEnvDump = rtProcPosixProfileEnvUnquoteAndUnescapeString(pszEnvDump, NULL /*pfStoppedOnPlainEqual*/); 781 else 782 pszValue = ""; 783 784 /* 785 * Add them if valid UTF-8, otherwise we simply drop them for now. 786 * The whole codeset stuff goes seriously wonky here as the environment 787 * we're harvesting probably contains it's own LC_CTYPE or LANG variables, 788 * so ignore the problem for now. 789 */ 790 if ( RTStrIsValidEncoding(pszVar) 791 && RTStrIsValidEncoding(pszValue)) 792 { 793 int rc2 = RTEnvSetEx(hEnvToUse, pszVar, pszValue); 794 AssertRCReturn(rc2, rc2); 795 } 796 else if (rc == VINF_SUCCESS) 797 rc = -VERR_INVALID_UTF8_ENCODING; 798 } 799 800 return rc; 801 } 802 803 804 /** 805 * Runs the user's shell in login mode with some environment dumping logic and 806 * harvests the dump, putting it into hEnvToUse. 807 * 808 * This is a bit hairy, esp. with regards to codesets. 809 * 810 * @returns IPRT status code. Not all error statuses will be returned and the 811 * caller should just continue with whatever is in hEnvToUse. 812 * @param hEnvToUse On input this is the basic user environment, on success 813 * in is fleshed out with stuff from the login shell dump. 814 * @param pszAsUser The user name for the profile. NULL if the current 815 * user. 816 * @param uid The UID corrsponding to @a pszAsUser, ~0 if NULL. 817 * @param gid The GID corrsponding to @a pszAsUser, ~0 if NULL. 818 * @param pszShell The login shell. This is a writable string to avoid 819 * needing to make a copy of it when examining the path 820 * part, instead we make a temporary change to it which is 821 * always reverted before returning. 822 */ 823 static int rtProcPosixProfileEnvRunAndHarvest(RTENV hEnvToUse, const char *pszAsUser, uid_t uid, gid_t gid, char *pszShell) 824 { 825 LogFlowFunc(("pszAsUser=%s uid=%u gid=%u pszShell=%s; hEnvToUse contains %u variables on entry\n", 826 pszAsUser, uid, gid, pszShell, RTEnvCountEx(hEnvToUse) )); 827 828 /* 829 * The three standard handles should be pointed to /dev/null, the 3rd handle 830 * is used to dump the environment. 831 */ 832 RTPIPE hPipeR, hPipeW; 833 int rc = RTPipeCreate(&hPipeR, &hPipeW, 0); 834 if (RT_SUCCESS(rc)) 835 { 836 RTFILE hFileNull; 837 rc = RTFileOpenBitBucket(&hFileNull, RTFILE_O_READWRITE); 838 if (RT_SUCCESS(rc)) 839 { 840 int aRedirFds[4]; 841 aRedirFds[0] = aRedirFds[1] = aRedirFds[2] = RTFileToNative(hFileNull); 842 aRedirFds[3] = RTPipeToNative(hPipeW); 843 844 /* 845 * Allocate a buffer for receiving the environment dump. 846 * 847 * This is fixed sized for simplicity and safety (creative user script 848 * shouldn't be allowed to exhaust our memory or such, after all we're 849 * most likely running with root privileges in this code path). 850 */ 851 size_t const cbEnvDump = _64K; 852 char * const pszEnvDump = (char *)RTMemTmpAllocZ(cbEnvDump); 853 if (pszEnvDump) 854 { 855 /* 856 * Our default approach is using /bin/sh: 857 */ 858 const char *pszExec = _PATH_BSHELL; 859 const char *apszArgs[8]; 860 apszArgs[0] = "-sh"; /* First arg must start with a dash for login shells. */ 861 apszArgs[1] = "-c"; 862 apszArgs[2] = "POSIXLY_CORRECT=1;export -p >&3"; 863 apszArgs[3] = NULL; 864 865 /* 866 * But see if we can trust the shell to be a real usable shell. 867 * This would be great as different shell typically has different profile setup 868 * files and we'll endup with the wrong enviornment if we use a different shell. 869 */ 870 char szDashShell[32]; 871 char szExportArg[128]; 872 bool fWithMarkers = false; 873 const char *pszShellNm = RTPathFilename(pszShell); 874 if ( pszShellNm 875 && access(pszShellNm, X_OK)) 876 { 877 /* 878 * First the check that it's a known bin directory: 879 */ 880 size_t const cchShellPath = pszShellNm - pszShell; 881 char const chSaved = pszShell[cchShellPath - 1]; 882 pszShell[cchShellPath - 1] = '\0'; 883 if ( RTPathCompare(pszShell, "/bin") == 0 884 || RTPathCompare(pszShell, "/usr/bin") == 0 885 || RTPathCompare(pszShell, "/usr/local/bin") == 0) 886 { 887 /* 888 * Then see if we recognize the shell name. 889 */ 890 RTStrCopy(&szDashShell[1], sizeof(szDashShell) - 1, pszShellNm); 891 szDashShell[0] = '-'; 892 if ( strcmp(pszShellNm, "bash") == 0 893 || strcmp(pszShellNm, "ksh") == 0 894 || strcmp(pszShellNm, "ksh93") == 0 895 || strcmp(pszShellNm, "zsh") == 0) 896 { 897 pszExec = pszShell; 898 apszArgs[0] = szDashShell; 899 900 /* Use /bin/sh for doing the environment dumping so we get the same kind 901 of output from everyone and can limit our parsing + testing efforts. */ 902 RTStrPrintf(szExportArg, sizeof(szExportArg), 903 "%s -c 'POSIXLY_CORRECT=1;export -p >&3'", _PATH_BSHELL); 904 apszArgs[2] = szExportArg; 905 } 906 /* C shell is very annoying in that it closes fd 3 without regard to what 907 we might have put there, so we must use stdout here but with markers so 908 we can find the dump. 909 Seems tmux have similar issues as it doesn't work above, but works fine here. */ 910 else if ( strcmp(pszShellNm, "csh") == 0 911 || strcmp(pszShellNm, "tcsh") == 0 912 || strcmp(pszShellNm, "tmux") == 0) 913 { 914 pszExec = pszShell; 915 apszArgs[0] = szDashShell; 916 917 fWithMarkers = true; 918 size_t cch = RTStrPrintf(szExportArg, sizeof(szExportArg), 919 "%s -c 'set -e;POSIXLY_CORRECT=1;echo %s;export -p;echo %s'", 920 _PATH_BSHELL, g_szEnvMarkerBegin, g_szEnvMarkerEnd); 921 Assert(cch < sizeof(szExportArg) - 1); RT_NOREF(cch); 922 apszArgs[2] = szExportArg; 923 924 aRedirFds[1] = aRedirFds[3]; 925 aRedirFds[3] = -1; 926 } 927 } 928 pszShell[cchShellPath - 1] = chSaved; 929 } 930 931 /* 932 * Create the process and wait for the output. 933 */ 934 LogFunc(("Executing '%s': '%s', '%s', '%s'\n", pszExec, apszArgs[0], apszArgs[1], apszArgs[2])); 935 RTPROCESS hProcess = NIL_RTPROCESS; 936 rc = rtProcPosixCreateInner(pszExec, apszArgs, hEnvToUse, hEnvToUse, 0 /*fFlags*/, 937 pszAsUser, uid, gid, RT_ELEMENTS(aRedirFds), aRedirFds, &hProcess); 938 if (RT_SUCCESS(rc)) 939 { 940 RTPipeClose(hPipeW); 941 hPipeW = NIL_RTPIPE; 942 943 size_t offEnvDump = 0; 944 uint64_t const msStart = RTTimeMilliTS(); 945 for (;;) 946 { 947 size_t cbRead = 0; 948 if (offEnvDump < cbEnvDump - 1) 949 { 950 rc = RTPipeRead(hPipeR, &pszEnvDump[offEnvDump], cbEnvDump - 1 - offEnvDump, &cbRead); 951 if (RT_SUCCESS(rc)) 952 offEnvDump += cbRead; 953 else 954 { 955 LogFlowFunc(("Breaking out of read loop: %Rrc\n", rc)); 956 if (rc == VERR_BROKEN_PIPE) 957 rc = VINF_SUCCESS; 958 break; 959 } 960 pszEnvDump[offEnvDump] = '\0'; 961 } 962 else 963 { 964 LogFunc(("Too much data in environment dump, dropping it\n")); 965 rc = VERR_TOO_MUCH_DATA; 966 break; 967 } 968 969 /* Do the timout check. */ 970 uint64_t const cMsElapsed = RTTimeMilliTS() - msStart; 971 if (cMsElapsed >= RT_MS_15SEC) 972 { 973 LogFunc(("Timed out after %RU64 ms\n", cMsElapsed)); 974 rc = VERR_TIMEOUT; 975 break; 976 } 977 978 /* If we got no data in above wait for more to become ready. */ 979 if (!cbRead) 980 RTPipeSelectOne(hPipeR, RT_MS_15SEC - cMsElapsed); 981 } 982 983 /* 984 * Kill the process and wait for it to avoid leaving zombies behind. 985 */ 986 /** @todo do we check the exit code? */ 987 int rc2 = RTProcWait(hProcess, RTPROCWAIT_FLAGS_NOBLOCK, NULL); 988 if (RT_SUCCESS(rc2)) 989 LogFlowFunc(("First RTProcWait succeeded\n")); 990 else 991 { 992 LogFunc(("First RTProcWait failed (%Rrc), terminating and doing a blocking wait\n", rc2)); 993 RTProcTerminate(hProcess); 994 RTProcWait(hProcess, RTPROCWAIT_FLAGS_BLOCK, NULL); 995 } 996 997 /* 998 * Parse the result. 999 */ 1000 if (RT_SUCCESS(rc)) 1001 rc = rtProcPosixProfileEnvHarvest(hEnvToUse, pszEnvDump, fWithMarkers); 1002 else 1003 { 1004 LogFunc(("Ignoring rc=%Rrc from the pipe read loop and continues with basic environment\n", rc)); 1005 rc = -rc; 1006 } 1007 } 1008 else 1009 LogFunc(("Failed to create process '%s': %Rrc\n", pszExec, rc)); 1010 RTMemTmpFree(pszEnvDump); 1011 } 1012 else 1013 { 1014 LogFunc(("Failed to allocate %#zx bytes for the dump\n", cbEnvDump)); 1015 rc = VERR_NO_TMP_MEMORY; 1016 } 1017 RTFileClose(hFileNull); 1018 } 1019 else 1020 LogFunc(("Failed to open /dev/null: %Rrc\n", rc)); 1021 RTPipeClose(hPipeR); 1022 RTPipeClose(hPipeW); 1023 } 1024 else 1025 LogFunc(("Failed to create pipe: %Rrc\n", rc)); 1026 LogFlowFunc(("returns %Rrc (hEnvToUse contains %u variables now)\n", rc, RTEnvCountEx(hEnvToUse))); 1027 return rc; 1028 } 1029 1030 1031 /** 1032 * Create an environment for the given user. 1033 * 1034 * This starts by creating a very basic environment and then tries to do it 1035 * properly by running the user's shell in login mode with some environment 1036 * dumping attached. The latter may fail and we'll ignore that for now and move 1037 * ahead with the very basic environment. 568 1038 * 569 1039 * @returns IPRT status code. 570 1040 * @param phEnvToUse Where to return the created environment. 571 * @param pszUser The user name for the profile. 1041 * @param pszAsUser The user name for the profile. NULL if the current 1042 * user. 1043 * @param uid The UID corrsponding to @a pszAsUser, ~0 if NULL. 1044 * @param gid The GID corrsponding to @a pszAsUser, ~0 if NULL. 1045 * @param fFlags RTPROC_FLAGS_XXX 572 1046 */ 573 static int rtProcPosixCreateProfileEnv(PRTENV phEnvToUse, const char *psz User)1047 static int rtProcPosixCreateProfileEnv(PRTENV phEnvToUse, const char *pszAsUser, uid_t uid, gid_t gid, uint32_t fFlags) 574 1048 { 575 1049 struct passwd Pwd; … … 578 1052 int rc; 579 1053 errno = 0; 580 if (psz User)581 rc = getpwnam_r(psz User, &Pwd, achBuf, sizeof(achBuf), &pPwd);1054 if (pszAsUser) 1055 rc = getpwnam_r(pszAsUser, &Pwd, achBuf, sizeof(achBuf), &pPwd); 582 1056 else 583 1057 rc = getpwuid_r(getuid(), &Pwd, achBuf, sizeof(achBuf), &pPwd); … … 588 1062 if (RT_SUCCESS(rc)) 589 1063 { 1064 #if 0 /* Enable and modify this to test shells other that your login shell. */ 1065 pPwd->pw_shell = (char *)"/bin/tmux"; 1066 #endif 590 1067 char *pszShell; 591 1068 rc = RTStrCurrentCPToUtf8(&pszShell, pPwd->pw_shell); 592 1069 if (RT_SUCCESS(rc)) 593 1070 { 594 char *psz UserFree = NULL;595 if (!psz User)1071 char *pszAsUserFree = NULL; 1072 if (!pszAsUser) 596 1073 { 597 rc = RTStrCurrentCPToUtf8(&psz UserFree, pPwd->pw_name);1074 rc = RTStrCurrentCPToUtf8(&pszAsUserFree, pPwd->pw_name); 598 1075 if (RT_SUCCESS(rc)) 599 psz User = pszUserFree;1076 pszAsUser = pszAsUserFree; 600 1077 } 601 1078 if (RT_SUCCESS(rc)) … … 610 1087 rc = RTEnvSetEx(hEnvToUse, "SHELL", pszShell); 611 1088 if (RT_SUCCESS(rc)) 612 rc = RTEnvSetEx(hEnvToUse, "USER", psz User);1089 rc = RTEnvSetEx(hEnvToUse, "USER", pszAsUser); 613 1090 if (RT_SUCCESS(rc)) 614 rc = RTEnvSetEx(hEnvToUse, "LOGNAME", psz User);1091 rc = RTEnvSetEx(hEnvToUse, "LOGNAME", pszAsUser); 615 1092 616 1093 if (RT_SUCCESS(rc)) … … 619 1096 if (RT_SUCCESS(rc)) 620 1097 { 621 RTStrPrintf(achBuf, sizeof(achBuf), "%s/%s", _PATH_MAILDIR, psz User);1098 RTStrPrintf(achBuf, sizeof(achBuf), "%s/%s", _PATH_MAILDIR, pszAsUser); 622 1099 rc = RTEnvSetEx(hEnvToUse, "MAIL", achBuf); 623 1100 } 624 1101 625 1102 #ifdef RT_OS_DARWIN 626 if (RT_SUCCESS(rc) && !psz UserFree)1103 if (RT_SUCCESS(rc) && !pszAsUserFree) 627 1104 { 628 1105 size_t cbNeeded = confstr(_CS_DARWIN_USER_TEMP_DIR, achBuf, sizeof(achBuf)); … … 641 1118 } 642 1119 #endif 643 644 /** @todo load /etc/environment, /etc/profile.env and ~/.pam_environment? */ 1120 if (RT_SUCCESS(rc)) 1121 { 1122 /* 1123 * Now comes the fun part where we need to try run a shell in login mode 1124 * and harvest its final environment to get the proper environment for 1125 * the user. We ignore some failures here so buggy login scrips and 1126 * other weird stuff won't trip us up too badly. 1127 */ 1128 if (!(fFlags & RTPROC_FLAGS_ONLY_BASIC_PROFILE)) 1129 rc = rtProcPosixProfileEnvRunAndHarvest(hEnvToUse, pszAsUser, uid, gid, pszShell); 1130 } 645 1131 646 1132 if (RT_FAILURE(rc)) 647 1133 RTEnvDestroy(hEnvToUse); 648 1134 } 649 RTStrFree(psz UserFree);1135 RTStrFree(pszAsUserFree); 650 1136 } 651 1137 RTStrFree(pszShell); … … 678 1164 } 679 1165 680 /**681 * Cleans up the environment on the way out.682 */683 static int rtProcPosixCreateReturn(int rc, RTENV hEnvToUse, RTENV hEnv)684 {685 if (hEnvToUse != hEnv)686 RTEnvDestroy(hEnvToUse);687 return rc;688 }689 690 1166 691 1167 RTR3DECL(int) RTProcCreateEx(const char *pszExec, const char * const *papszArgs, RTENV hEnv, uint32_t fFlags, … … 783 1259 { 784 1260 if (fFlags & RTPROC_FLAGS_PROFILE) 785 rc = rtProcPosixCreateProfileEnv(&hEnvToUse, pszAsUser );1261 rc = rtProcPosixCreateProfileEnv(&hEnvToUse, pszAsUser, uid, gid, fFlags); 786 1262 else 787 1263 rc = RTEnvClone(&hEnvToUse, RTENV_DEFAULT); 788 if (RT_SUCCESS(rc))789 {790 if ((fFlags & RTPROC_FLAGS_ENV_CHANGE_RECORD) && hEnv != RTENV_DEFAULT)791 rc = RTEnvApplyChanges(hEnvToUse, hEnv);792 if (RT_FAILURE(rc))793 RTEnvDestroy(hEnvToUse);794 }795 1264 if (RT_FAILURE(rc)) 796 1265 return rc; 1266 1267 if ((fFlags & RTPROC_FLAGS_ENV_CHANGE_RECORD) && hEnv != RTENV_DEFAULT) 1268 { 1269 rc = RTEnvApplyChanges(hEnvToUse, hEnv); 1270 if (RT_FAILURE(rc)) 1271 { 1272 RTEnvDestroy(hEnvToUse); 1273 return rc; 1274 } 1275 } 797 1276 } 798 1277 … … 801 1280 */ 802 1281 char szRealExec[RTPATH_MAX]; 803 if (access(pszExec, X_OK)) 1282 if (access(pszExec, X_OK) == 0) 1283 rc = VINF_SUCCESS; 1284 else 804 1285 { 805 1286 rc = errno; … … 819 1300 rc = rc == VERR_END_OF_STRING ? VERR_FILE_NOT_FOUND : rc; 820 1301 } 821 822 if (RT_FAILURE(rc)) 823 return rtProcPosixCreateReturn(rc, hEnvToUse, hEnv); 824 } 825 1302 } 1303 if (RT_SUCCESS(rc)) 1304 { 1305 /* 1306 * The rest of the process creation is reused internally by 1307 * rtProcPosixCreateProfileEnv. 1308 */ 1309 rc = rtProcPosixCreateInner(pszExec, papszArgs, hEnv, hEnvToUse, fFlags, pszAsUser, uid, gid, 1310 RT_ELEMENTS(aStdFds), aStdFds, phProcess); 1311 } 1312 if (hEnvToUse != hEnv) 1313 RTEnvDestroy(hEnvToUse); 1314 return rc; 1315 } 1316 1317 1318 /** 1319 * The inner 2nd half of RTProcCreateEx. 1320 * 1321 * This is also used by rtProcPosixCreateProfileEnv(). 1322 * 1323 * @returns IPRT status code. 1324 * @param pszExec The executable to run (absolute path, X_OK). 1325 * @param papszArgs The arguments. 1326 * @param hEnv The original enviornment request, needed for adjustments 1327 * if starting as different user. 1328 * @param hEnvToUse The environment we should use. 1329 * @param fFlags The process creation flags, RTPROC_FLAGS_XXX. 1330 * @param pszAsUser The user to start the process as, if requested. 1331 * @param uid The UID corrsponding to @a pszAsUser, ~0 if NULL. 1332 * @param gid The GID corrsponding to @a pszAsUser, ~0 if NULL. 1333 * @param cRedirFds Number of redirection file descriptors. 1334 * @param paRedirFds Pointer to redirection file descriptors. Entries 1335 * containing -1 are not modified (inherit from parent), 1336 * -2 indicates that the descriptor should be closed in the 1337 * child. 1338 * @param phProcess Where to return the process ID on success. 1339 */ 1340 static int rtProcPosixCreateInner(const char *pszExec, const char * const *papszArgs, RTENV hEnv, RTENV hEnvToUse, 1341 uint32_t fFlags, const char *pszAsUser, uid_t uid, gid_t gid, 1342 unsigned cRedirFds, int *paRedirFds, PRTPROCESS phProcess) 1343 { 1344 /* 1345 * Get the environment block. 1346 */ 1347 const char * const *papszEnv = RTEnvGetExecEnvP(hEnvToUse); 1348 AssertPtrReturn(papszEnv, VERR_INVALID_HANDLE); 1349 1350 /* 1351 * Optimize the redirections. 1352 */ 1353 while (cRedirFds > 0 && paRedirFds[cRedirFds - 1] == -1) 1354 cRedirFds--; 1355 1356 /* 1357 * Child PID. 1358 */ 826 1359 pid_t pid = -1; 827 const char * const *papszEnv = RTEnvGetExecEnvP(hEnvToUse);828 AssertPtrReturn(papszEnv, rtProcPosixCreateReturn(VERR_INVALID_HANDLE, hEnvToUse, hEnv));829 830 1360 831 1361 /* … … 847 1377 templateFd = rtSolarisContractPreFork(); 848 1378 if (templateFd == -1) 849 return rtProcPosixCreateReturn(VERR_OPEN_FAILED, hEnvToUse, hEnv);1379 return VERR_OPEN_FAILED; 850 1380 } 851 1381 # endif /* RT_OS_SOLARIS */ … … 883 1413 /* Assume that something wasn't found. No detailed info. */ 884 1414 if (status) 885 return rtProcPosixCreateReturn(VERR_PROCESS_NOT_FOUND, hEnvToUse, hEnv);1415 return VERR_PROCESS_NOT_FOUND; 886 1416 if (phProcess) 887 1417 *phProcess = 0; 888 return rtProcPosixCreateReturn(VINF_SUCCESS, hEnvToUse, hEnv);889 } 890 return rtProcPosixCreateReturn(RTErrConvertFromErrno(errno), hEnvToUse, hEnv);1418 return VINF_SUCCESS; 1419 } 1420 return RTErrConvertFromErrno(errno); 891 1421 } 892 1422 } … … 903 1433 * silly with the (duplicated) file descriptors. 904 1434 */ 1435 int rc; 905 1436 #ifdef HAVE_POSIX_SPAWN 906 1437 /** @todo OS/2: implement DETACHED (BACKGROUND stuff), see VbglR3Daemonize. */ … … 936 1467 posix_spawn_file_actions_t FileActions; 937 1468 posix_spawn_file_actions_t *pFileActions = NULL; 938 if ( (aStdFds[0] != -1 || aStdFds[1] != -1 || aStdFds[2] != -1) && !rc)1469 if (!rc && cRedirFds > 0) 939 1470 { 940 1471 rc = posix_spawn_file_actions_init(&FileActions); … … 942 1473 { 943 1474 pFileActions = &FileActions; 944 for ( int i = 0; i < 3; i++)1475 for (unsigned i = 0; i < cRedirFds; i++) 945 1476 { 946 int fd = aStdFds[i];1477 int fd = paRedirFds[i]; 947 1478 if (fd == -2) 948 1479 rc = posix_spawn_file_actions_addclose(&FileActions, i); 949 else if (fd >= 0 && fd != i)1480 else if (fd >= 0 && fd != (int)i) 950 1481 { 951 1482 rc = posix_spawn_file_actions_adddup2(&FileActions, fd, i); 952 1483 if (!rc) 953 1484 { 954 for ( int j = i + 1; j < 3; j++)955 if ( aStdFds[j] == fd)1485 for (unsigned j = i + 1; j < cRedirFds; j++) 1486 if (paRedirFds[j] == fd) 956 1487 { 957 1488 fd = -1; … … 989 1520 if (phProcess) 990 1521 *phProcess = pid; 991 return rtProcPosixCreateReturn(VINF_SUCCESS, hEnvToUse, hEnv);1522 return VINF_SUCCESS; 992 1523 } 993 1524 } … … 1006 1537 templateFd = rtSolarisContractPreFork(); 1007 1538 if (templateFd == -1) 1008 return rtProcPosixCreateReturn(VERR_OPEN_FAILED, hEnvToUse, hEnv);1539 return VERR_OPEN_FAILED; 1009 1540 } 1010 1541 #endif /* RT_OS_SOLARIS */ … … 1087 1618 * Apply changes to the standard file descriptor and stuff. 1088 1619 */ 1089 for ( int i = 0; i < 3; i++)1090 { 1091 int fd = aStdFds[i];1620 for (unsigned i = 0; i < cRedirFds; i++) 1621 { 1622 int fd = paRedirFds[i]; 1092 1623 if (fd == -2) 1093 1624 close(i); … … 1095 1626 { 1096 1627 int rc2 = dup2(fd, i); 1097 if (rc2 != i)1628 if (rc2 != (int)i) 1098 1629 { 1099 1630 if (fFlags & RTPROC_FLAGS_DETACHED) … … 1102 1633 exit(125); 1103 1634 } 1104 for ( int j = i + 1; j < 3; j++)1105 if ( aStdFds[j] == fd)1635 for (unsigned j = i + 1; j < cRedirFds; j++) 1636 if (paRedirFds[j] == fd) 1106 1637 { 1107 1638 fd = -1; … … 1142 1673 if (phProcess) 1143 1674 *phProcess = pid; 1144 return rtProcPosixCreateReturn(VINF_SUCCESS, hEnvToUse, hEnv);1675 return VINF_SUCCESS; 1145 1676 } 1146 1677 /* For a detached process this happens in the temp process, so … … 1148 1679 if (fFlags & RTPROC_FLAGS_DETACHED) 1149 1680 _Exit(124); 1150 return rtProcPosixCreateReturn(RTErrConvertFromErrno(errno), hEnvToUse, hEnv);1151 } 1152 1153 return rtProcPosixCreateReturn(VERR_NOT_IMPLEMENTED, hEnvToUse, hEnv);1681 return RTErrConvertFromErrno(errno); 1682 } 1683 1684 return VERR_NOT_IMPLEMENTED; 1154 1685 } 1155 1686
Note:
See TracChangeset
for help on using the changeset viewer.