VirtualBox

Changeset 92617 in vbox for trunk/src/VBox/Runtime/r3/posix


Ignore:
Timestamp:
Nov 29, 2021 1:04:10 AM (3 years ago)
Author:
vboxsync
svn:sync-xref-src-repo-rev:
148509
Message:

IPRT,RTProcCreatEx/posix: Run the login shell with a command dumping the environment to properly implement RTPROC_FLAGS_PROFILE. Tested on linux with sh, bash, zsh, ksh, ksh93, csh, tcsh, and tmux. bugref:10153

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Runtime/r3/posix/process-creation-posix.cpp

    r82968 r92617  
    112112# define _PATH_STDPATH "/sbin:/usr/sbin:/bin:/usr/bin"
    113113#endif
     114#ifndef _PATH_BSHELL
     115# define _PATH_BSHELL "/bin/sh"
     116#endif
    114117
    115118
     
    118121
    119122#include <iprt/assert.h>
     123#include <iprt/ctype.h>
    120124#include <iprt/env.h>
    121125#include <iprt/err.h>
     
    146150typedef RTPROCPAMARGS *PRTPROCPAMARGS;
    147151#endif
     152
     153
     154/*********************************************************************************************************************************
     155*   Global Variables                                                                                                             *
     156*********************************************************************************************************************************/
     157/** Environment dump marker used with CSH.   */
     158static const char g_szEnvMarkerBegin[] = "IPRT_EnvEnvEnv_Begin_EnvEnvEnv";
     159/** Environment dump marker used with CSH.   */
     160static const char g_szEnvMarkerEnd[]   = "IPRT_EnvEnvEnv_End_EnvEnvEnv";
     161
     162
     163/*********************************************************************************************************************************
     164*   Internal Functions                                                                                                           *
     165*********************************************************************************************************************************/
     166static 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);
    148169
    149170
     
    565586
    566587/**
    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 */
     602static 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 */
     699static 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 */
     823static 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.
    5681038 *
    5691039 * @returns IPRT status code.
    5701040 * @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
    5721046 */
    573 static int rtProcPosixCreateProfileEnv(PRTENV phEnvToUse, const char *pszUser)
     1047static int rtProcPosixCreateProfileEnv(PRTENV phEnvToUse, const char *pszAsUser, uid_t uid, gid_t gid, uint32_t fFlags)
    5741048{
    5751049    struct passwd   Pwd;
     
    5781052    int             rc;
    5791053    errno = 0;
    580     if (pszUser)
    581         rc = getpwnam_r(pszUser, &Pwd, achBuf, sizeof(achBuf), &pPwd);
     1054    if (pszAsUser)
     1055        rc = getpwnam_r(pszAsUser, &Pwd, achBuf, sizeof(achBuf), &pPwd);
    5821056    else
    5831057        rc = getpwuid_r(getuid(), &Pwd, achBuf, sizeof(achBuf), &pPwd);
     
    5881062        if (RT_SUCCESS(rc))
    5891063        {
     1064#if 0 /* Enable and modify this to test shells other that your login shell. */
     1065            pPwd->pw_shell = (char *)"/bin/tmux";
     1066#endif
    5901067            char *pszShell;
    5911068            rc = RTStrCurrentCPToUtf8(&pszShell, pPwd->pw_shell);
    5921069            if (RT_SUCCESS(rc))
    5931070            {
    594                 char *pszUserFree = NULL;
    595                 if (!pszUser)
     1071                char *pszAsUserFree = NULL;
     1072                if (!pszAsUser)
    5961073                {
    597                     rc = RTStrCurrentCPToUtf8(&pszUserFree, pPwd->pw_name);
     1074                    rc = RTStrCurrentCPToUtf8(&pszAsUserFree, pPwd->pw_name);
    5981075                    if (RT_SUCCESS(rc))
    599                         pszUser = pszUserFree;
     1076                        pszAsUser = pszAsUserFree;
    6001077                }
    6011078                if (RT_SUCCESS(rc))
     
    6101087                            rc = RTEnvSetEx(hEnvToUse, "SHELL", pszShell);
    6111088                        if (RT_SUCCESS(rc))
    612                             rc = RTEnvSetEx(hEnvToUse, "USER", pszUser);
     1089                            rc = RTEnvSetEx(hEnvToUse, "USER", pszAsUser);
    6131090                        if (RT_SUCCESS(rc))
    614                             rc = RTEnvSetEx(hEnvToUse, "LOGNAME", pszUser);
     1091                            rc = RTEnvSetEx(hEnvToUse, "LOGNAME", pszAsUser);
    6151092
    6161093                        if (RT_SUCCESS(rc))
     
    6191096                        if (RT_SUCCESS(rc))
    6201097                        {
    621                             RTStrPrintf(achBuf, sizeof(achBuf), "%s/%s", _PATH_MAILDIR, pszUser);
     1098                            RTStrPrintf(achBuf, sizeof(achBuf), "%s/%s", _PATH_MAILDIR, pszAsUser);
    6221099                            rc = RTEnvSetEx(hEnvToUse, "MAIL", achBuf);
    6231100                        }
    6241101
    6251102#ifdef RT_OS_DARWIN
    626                         if (RT_SUCCESS(rc) && !pszUserFree)
     1103                        if (RT_SUCCESS(rc) && !pszAsUserFree)
    6271104                        {
    6281105                            size_t cbNeeded = confstr(_CS_DARWIN_USER_TEMP_DIR, achBuf, sizeof(achBuf));
     
    6411118                        }
    6421119#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                        }
    6451131
    6461132                        if (RT_FAILURE(rc))
    6471133                            RTEnvDestroy(hEnvToUse);
    6481134                    }
    649                     RTStrFree(pszUserFree);
     1135                    RTStrFree(pszAsUserFree);
    6501136                }
    6511137                RTStrFree(pszShell);
     
    6781164}
    6791165
    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 
    6901166
    6911167RTR3DECL(int)   RTProcCreateEx(const char *pszExec, const char * const *papszArgs, RTENV hEnv, uint32_t fFlags,
     
    7831259    {
    7841260        if (fFlags & RTPROC_FLAGS_PROFILE)
    785             rc = rtProcPosixCreateProfileEnv(&hEnvToUse, pszAsUser);
     1261            rc = rtProcPosixCreateProfileEnv(&hEnvToUse, pszAsUser, uid, gid, fFlags);
    7861262        else
    7871263            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         }
    7951264        if (RT_FAILURE(rc))
    7961265            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        }
    7971276    }
    7981277
     
    8011280     */
    8021281    char szRealExec[RTPATH_MAX];
    803     if (access(pszExec, X_OK))
     1282    if (access(pszExec, X_OK) == 0)
     1283        rc = VINF_SUCCESS;
     1284    else
    8041285    {
    8051286        rc = errno;
     
    8191300                rc = rc == VERR_END_OF_STRING ? VERR_FILE_NOT_FOUND : rc;
    8201301        }
    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 */
     1340static 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     */
    8261359    pid_t pid = -1;
    827     const char * const *papszEnv = RTEnvGetExecEnvP(hEnvToUse);
    828     AssertPtrReturn(papszEnv, rtProcPosixCreateReturn(VERR_INVALID_HANDLE, hEnvToUse, hEnv));
    829 
    8301360
    8311361    /*
     
    8471377            templateFd = rtSolarisContractPreFork();
    8481378            if (templateFd == -1)
    849                 return rtProcPosixCreateReturn(VERR_OPEN_FAILED, hEnvToUse, hEnv);
     1379                return VERR_OPEN_FAILED;
    8501380        }
    8511381# endif /* RT_OS_SOLARIS */
     
    8831413                /* Assume that something wasn't found. No detailed info. */
    8841414                if (status)
    885                     return rtProcPosixCreateReturn(VERR_PROCESS_NOT_FOUND, hEnvToUse, hEnv);
     1415                    return VERR_PROCESS_NOT_FOUND;
    8861416                if (phProcess)
    8871417                    *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);
    8911421        }
    8921422    }
     
    9031433     * silly with the (duplicated) file descriptors.
    9041434     */
     1435    int rc;
    9051436#ifdef HAVE_POSIX_SPAWN
    9061437    /** @todo OS/2: implement DETACHED (BACKGROUND stuff), see VbglR3Daemonize.  */
     
    9361467            posix_spawn_file_actions_t  FileActions;
    9371468            posix_spawn_file_actions_t *pFileActions = NULL;
    938             if ((aStdFds[0] != -1 || aStdFds[1] != -1 || aStdFds[2] != -1) && !rc)
     1469            if (!rc && cRedirFds > 0)
    9391470            {
    9401471                rc = posix_spawn_file_actions_init(&FileActions);
     
    9421473                {
    9431474                    pFileActions = &FileActions;
    944                     for (int i = 0; i < 3; i++)
     1475                    for (unsigned i = 0; i < cRedirFds; i++)
    9451476                    {
    946                         int fd = aStdFds[i];
     1477                        int fd = paRedirFds[i];
    9471478                        if (fd == -2)
    9481479                            rc = posix_spawn_file_actions_addclose(&FileActions, i);
    949                         else if (fd >= 0 && fd != i)
     1480                        else if (fd >= 0 && fd != (int)i)
    9501481                        {
    9511482                            rc = posix_spawn_file_actions_adddup2(&FileActions, fd, i);
    9521483                            if (!rc)
    9531484                            {
    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)
    9561487                                    {
    9571488                                        fd = -1;
     
    9891520                if (phProcess)
    9901521                    *phProcess = pid;
    991                 return rtProcPosixCreateReturn(VINF_SUCCESS, hEnvToUse, hEnv);
     1522                return VINF_SUCCESS;
    9921523            }
    9931524        }
     
    10061537            templateFd = rtSolarisContractPreFork();
    10071538            if (templateFd == -1)
    1008                 return rtProcPosixCreateReturn(VERR_OPEN_FAILED, hEnvToUse, hEnv);
     1539                return VERR_OPEN_FAILED;
    10091540        }
    10101541#endif /* RT_OS_SOLARIS */
     
    10871618             * Apply changes to the standard file descriptor and stuff.
    10881619             */
    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];
    10921623                if (fd == -2)
    10931624                    close(i);
     
    10951626                {
    10961627                    int rc2 = dup2(fd, i);
    1097                     if (rc2 != i)
     1628                    if (rc2 != (int)i)
    10981629                    {
    10991630                        if (fFlags & RTPROC_FLAGS_DETACHED)
     
    11021633                            exit(125);
    11031634                    }
    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)
    11061637                        {
    11071638                            fd = -1;
     
    11421673            if (phProcess)
    11431674                *phProcess = pid;
    1144             return rtProcPosixCreateReturn(VINF_SUCCESS, hEnvToUse, hEnv);
     1675            return VINF_SUCCESS;
    11451676        }
    11461677        /* For a detached process this happens in the temp process, so
     
    11481679        if (fFlags & RTPROC_FLAGS_DETACHED)
    11491680            _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;
    11541685}
    11551686
Note: See TracChangeset for help on using the changeset viewer.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette