Example #1
0
/**
 * @interface_method_impl{RTTIMENANOTSDATA, pfnBadCpuIndex}
 */
static DECLCALLBACK(uint64_t) rtTimeNanoTSInternalBadCpuIndex(PRTTIMENANOTSDATA pData, uint16_t idApic,
                                                              uint16_t iCpuSet, uint16_t iGipCpu)
{
# ifndef IN_RC
    AssertMsgFailed(("idApic=%#x iCpuSet=%#x iGipCpu=%#x\n", idApic, iCpuSet, iGipCpu));
    return RTTimeSystemNanoTS();
# else
    RTAssertReleasePanic();
    return 0;
# endif
}
Example #2
0
/**
 * Fallback function.
 */
static DECLCALLBACK(uint64_t) rtTimeNanoTSInternalFallback(PRTTIMENANOTSDATA pData)
{
    PSUPGLOBALINFOPAGE pGip = g_pSUPGlobalInfoPage;
    if (    pGip
        &&  pGip->u32Magic == SUPGLOBALINFOPAGE_MAGIC
        &&  (   pGip->u32Mode == SUPGIPMODE_INVARIANT_TSC
             || pGip->u32Mode == SUPGIPMODE_SYNC_TSC
             || pGip->u32Mode == SUPGIPMODE_ASYNC_TSC))
        return rtTimeNanoTSInternalRediscover(pData);
    NOREF(pData);
# ifndef IN_RC
    return RTTimeSystemNanoTS();
# else
    RTAssertReleasePanic();
    return 0;
# endif
}
RTR3DECL(int)   RTProcCreateEx(const char *pszExec, const char * const *papszArgs, RTENV hEnv, uint32_t fFlags,
                               PCRTHANDLE phStdIn, PCRTHANDLE phStdOut, PCRTHANDLE phStdErr, const char *pszAsUser,
                               const char *pszPassword, PRTPROCESS phProcess)
{
    int rc;

    /*
     * Input validation
     */
    AssertPtrReturn(pszExec, VERR_INVALID_POINTER);
    AssertReturn(*pszExec, VERR_INVALID_PARAMETER);
    AssertReturn(!(fFlags & ~RTPROC_FLAGS_VALID_MASK), VERR_INVALID_PARAMETER);
    AssertReturn(!(fFlags & RTPROC_FLAGS_DETACHED) || !phProcess, VERR_INVALID_PARAMETER);
    AssertReturn(hEnv != NIL_RTENV, VERR_INVALID_PARAMETER);
    AssertPtrReturn(papszArgs, VERR_INVALID_PARAMETER);
    AssertPtrNullReturn(pszAsUser, VERR_INVALID_POINTER);
    AssertReturn(!pszAsUser || *pszAsUser, VERR_INVALID_PARAMETER);
    AssertReturn(!pszPassword || pszAsUser, VERR_INVALID_PARAMETER);
    AssertPtrNullReturn(pszPassword, VERR_INVALID_POINTER);
#if defined(RT_OS_OS2)
    if (fFlags & RTPROC_FLAGS_DETACHED)
        return VERR_PROC_DETACH_NOT_SUPPORTED;
#endif

    /*
     * Get the file descriptors for the handles we've been passed.
     */
    PCRTHANDLE  paHandles[3] = { phStdIn, phStdOut, phStdErr };
    int         aStdFds[3]   = {      -1,       -1,       -1 };
    for (int i = 0; i < 3; i++)
    {
        if (paHandles[i])
        {
            AssertPtrReturn(paHandles[i], VERR_INVALID_POINTER);
            switch (paHandles[i]->enmType)
            {
                case RTHANDLETYPE_FILE:
                    aStdFds[i] = paHandles[i]->u.hFile != NIL_RTFILE
                               ? (int)RTFileToNative(paHandles[i]->u.hFile)
                               : -2 /* close it */;
                    break;

                case RTHANDLETYPE_PIPE:
                    aStdFds[i] = paHandles[i]->u.hPipe != NIL_RTPIPE
                               ? (int)RTPipeToNative(paHandles[i]->u.hPipe)
                               : -2 /* close it */;
                    break;

                case RTHANDLETYPE_SOCKET:
                    aStdFds[i] = paHandles[i]->u.hSocket != NIL_RTSOCKET
                               ? (int)RTSocketToNative(paHandles[i]->u.hSocket)
                               : -2 /* close it */;
                    break;

                default:
                    AssertMsgFailedReturn(("%d: %d\n", i, paHandles[i]->enmType), VERR_INVALID_PARAMETER);
            }
            /** @todo check the close-on-execness of these handles?  */
        }
    }

    for (int i = 0; i < 3; i++)
        if (aStdFds[i] == i)
            aStdFds[i] = -1;

    for (int i = 0; i < 3; i++)
        AssertMsgReturn(aStdFds[i] < 0 || aStdFds[i] > i,
                        ("%i := %i not possible because we're lazy\n", i, aStdFds[i]),
                        VERR_NOT_SUPPORTED);

    /*
     * Resolve the user id if specified.
     */
    uid_t uid = ~(uid_t)0;
    gid_t gid = ~(gid_t)0;
    if (pszAsUser)
    {
        rc = rtCheckCredentials(pszAsUser, pszPassword, &gid, &uid);
        if (RT_FAILURE(rc))
            return rc;
    }

    /*
     * Create the child environment if either RTPROC_FLAGS_PROFILE or
     * RTPROC_FLAGS_ENV_CHANGE_RECORD are in effect.
     */
    RTENV hEnvToUse = hEnv;
    if (   (fFlags & (RTPROC_FLAGS_ENV_CHANGE_RECORD | RTPROC_FLAGS_PROFILE))
        && (   (fFlags & RTPROC_FLAGS_ENV_CHANGE_RECORD)
            || hEnv == RTENV_DEFAULT) )
    {
        if (fFlags & RTPROC_FLAGS_PROFILE)
            rc = rtProcPosixCreateProfileEnv(&hEnvToUse, pszAsUser);
        else
            rc = RTEnvClone(&hEnvToUse, RTENV_DEFAULT);
        if (RT_SUCCESS(rc))
        {
            if ((fFlags & RTPROC_FLAGS_ENV_CHANGE_RECORD) && hEnv != RTENV_DEFAULT)
                rc = RTEnvApplyChanges(hEnvToUse, hEnv);
            if (RT_FAILURE(rc))
                RTEnvDestroy(hEnvToUse);
        }
        if (RT_FAILURE(rc))
            return rc;
    }

    /*
     * Check for execute access to the file.
     */
    char szRealExec[RTPATH_MAX];
    if (access(pszExec, X_OK))
    {
        rc = errno;
        if (   !(fFlags & RTPROC_FLAGS_SEARCH_PATH)
            || rc != ENOENT
            || RTPathHavePath(pszExec) )
            rc = RTErrConvertFromErrno(rc);
        else
        {
            /* search */
            char *pszPath = RTEnvDupEx(hEnvToUse, "PATH");
            rc = RTPathTraverseList(pszPath, ':', rtPathFindExec, (void *)pszExec, &szRealExec[0]);
            RTStrFree(pszPath);
            if (RT_SUCCESS(rc))
                pszExec = szRealExec;
            else
                rc = rc == VERR_END_OF_STRING ? VERR_FILE_NOT_FOUND : rc;
        }

        if (RT_FAILURE(rc))
            return rtProcPosixCreateReturn(rc, hEnvToUse, hEnv);
    }

    pid_t pid = -1;
    const char * const *papszEnv = RTEnvGetExecEnvP(hEnvToUse);
    AssertPtrReturn(papszEnv, rtProcPosixCreateReturn(VERR_INVALID_HANDLE, hEnvToUse, hEnv));


    /*
     * Take care of detaching the process.
     *
     * HACK ALERT! Put the process into a new process group with pgid = pid
     * to make sure it differs from that of the parent process to ensure that
     * the IPRT waitpid call doesn't race anyone (read XPCOM) doing group wide
     * waits. setsid() includes the setpgid() functionality.
     * 2010-10-11 XPCOM no longer waits for anything, but it cannot hurt.
     */
#ifndef RT_OS_OS2
    if (fFlags & RTPROC_FLAGS_DETACHED)
    {
# ifdef RT_OS_SOLARIS
        int templateFd = -1;
        if (!(fFlags & RTPROC_FLAGS_SAME_CONTRACT))
        {
            templateFd = rtSolarisContractPreFork();
            if (templateFd == -1)
                return rtProcPosixCreateReturn(VERR_OPEN_FAILED, hEnvToUse, hEnv);
        }
# endif /* RT_OS_SOLARIS */
        pid = fork();
        if (!pid)
        {
# ifdef RT_OS_SOLARIS
            if (!(fFlags & RTPROC_FLAGS_SAME_CONTRACT))
                rtSolarisContractPostForkChild(templateFd);
# endif
            setsid(); /* see comment above */

            pid = -1;
            /* Child falls through to the actual spawn code below. */
        }
        else
        {
# ifdef RT_OS_SOLARIS
            if (!(fFlags & RTPROC_FLAGS_SAME_CONTRACT))
                rtSolarisContractPostForkParent(templateFd, pid);
# endif
            if (pid > 0)
            {
                /* Must wait for the temporary process to avoid a zombie. */
                int status = 0;
                pid_t pidChild = 0;

                /* Restart if we get interrupted. */
                do
                {
                    pidChild = waitpid(pid, &status, 0);
                } while (   pidChild == -1
                         && errno == EINTR);

                /* Assume that something wasn't found. No detailed info. */
                if (status)
                    return rtProcPosixCreateReturn(VERR_PROCESS_NOT_FOUND, hEnvToUse, hEnv);
                if (phProcess)
                    *phProcess = 0;
                return rtProcPosixCreateReturn(VINF_SUCCESS, hEnvToUse, hEnv);
            }
            return rtProcPosixCreateReturn(RTErrConvertFromErrno(errno), hEnvToUse, hEnv);
        }
    }
#endif

    /*
     * Spawn the child.
     *
     * Any spawn code MUST not execute any atexit functions if it is for a
     * detached process. It would lead to running the atexit functions which
     * make only sense for the parent. libORBit e.g. gets confused by multiple
     * execution. Remember, there was only a fork() so far, and until exec()
     * is successfully run there is nothing which would prevent doing anything
     * silly with the (duplicated) file descriptors.
     */
#ifdef HAVE_POSIX_SPAWN
    /** @todo OS/2: implement DETACHED (BACKGROUND stuff), see VbglR3Daemonize.  */
    if (   uid == ~(uid_t)0
        && gid == ~(gid_t)0)
    {
        /* Spawn attributes. */
        posix_spawnattr_t Attr;
        rc = posix_spawnattr_init(&Attr);
        if (!rc)
        {
            /* Indicate that process group and signal mask are to be changed,
               and that the child should use default signal actions. */
            rc = posix_spawnattr_setflags(&Attr, POSIX_SPAWN_SETPGROUP | POSIX_SPAWN_SETSIGMASK | POSIX_SPAWN_SETSIGDEF);
            Assert(rc == 0);

            /* The child starts in its own process group. */
            if (!rc)
            {
                rc = posix_spawnattr_setpgroup(&Attr, 0 /* pg == child pid */);
                Assert(rc == 0);
            }

            /* Unmask all signals. */
            if (!rc)
            {
                sigset_t SigMask;
                sigemptyset(&SigMask);
                rc = posix_spawnattr_setsigmask(&Attr, &SigMask); Assert(rc == 0);
            }

            /* File changes. */
            posix_spawn_file_actions_t  FileActions;
            posix_spawn_file_actions_t *pFileActions = NULL;
            if ((aStdFds[0] != -1 || aStdFds[1] != -1 || aStdFds[2] != -1) && !rc)
            {
                rc = posix_spawn_file_actions_init(&FileActions);
                if (!rc)
                {
                    pFileActions = &FileActions;
                    for (int i = 0; i < 3; i++)
                    {
                        int fd = aStdFds[i];
                        if (fd == -2)
                            rc = posix_spawn_file_actions_addclose(&FileActions, i);
                        else if (fd >= 0 && fd != i)
                        {
                            rc = posix_spawn_file_actions_adddup2(&FileActions, fd, i);
                            if (!rc)
                            {
                                for (int j = i + 1; j < 3; j++)
                                    if (aStdFds[j] == fd)
                                    {
                                        fd = -1;
                                        break;
                                    }
                                if (fd >= 0)
                                    rc = posix_spawn_file_actions_addclose(&FileActions, fd);
                            }
                        }
                        if (rc)
                            break;
                    }
                }
            }

            if (!rc)
                rc = posix_spawn(&pid, pszExec, pFileActions, &Attr, (char * const *)papszArgs,
                                 (char * const *)papszEnv);

            /* cleanup */
            int rc2 = posix_spawnattr_destroy(&Attr); Assert(rc2 == 0); NOREF(rc2);
            if (pFileActions)
            {
                rc2 = posix_spawn_file_actions_destroy(pFileActions);
                Assert(rc2 == 0);
            }

            /* return on success.*/
            if (!rc)
            {
                /* For a detached process this happens in the temp process, so
                 * it's not worth doing anything as this process must exit. */
                if (fFlags & RTPROC_FLAGS_DETACHED)
                    _Exit(0);
                if (phProcess)
                    *phProcess = pid;
                return rtProcPosixCreateReturn(VINF_SUCCESS, hEnvToUse, hEnv);
            }
        }
        /* For a detached process this happens in the temp process, so
         * it's not worth doing anything as this process must exit. */
        if (fFlags & RTPROC_FLAGS_DETACHED)
            _Exit(124);
    }
    else
#endif
    {
#ifdef RT_OS_SOLARIS
        int templateFd = -1;
        if (!(fFlags & RTPROC_FLAGS_SAME_CONTRACT))
        {
            templateFd = rtSolarisContractPreFork();
            if (templateFd == -1)
                return rtProcPosixCreateReturn(VERR_OPEN_FAILED, hEnvToUse, hEnv);
        }
#endif /* RT_OS_SOLARIS */
        pid = fork();
        if (!pid)
        {
#ifdef RT_OS_SOLARIS
            if (!(fFlags & RTPROC_FLAGS_SAME_CONTRACT))
                rtSolarisContractPostForkChild(templateFd);
#endif /* RT_OS_SOLARIS */
            if (!(fFlags & RTPROC_FLAGS_DETACHED))
                setpgid(0, 0); /* see comment above */

            /*
             * Change group and user if requested.
             */
#if 1 /** @todo This needs more work, see suplib/hardening. */
            if (pszAsUser)
            {
                int ret = initgroups(pszAsUser, gid);
                if (ret)
                {
                    if (fFlags & RTPROC_FLAGS_DETACHED)
                        _Exit(126);
                    else
                        exit(126);
                }
            }
            if (gid != ~(gid_t)0)
            {
                if (setgid(gid))
                {
                    if (fFlags & RTPROC_FLAGS_DETACHED)
                        _Exit(126);
                    else
                        exit(126);
                }
            }

            if (uid != ~(uid_t)0)
            {
                if (setuid(uid))
                {
                    if (fFlags & RTPROC_FLAGS_DETACHED)
                        _Exit(126);
                    else
                        exit(126);
                }
            }
#endif

            /*
             * Some final profile environment tweaks, if running as user.
             */
            if (   (fFlags & RTPROC_FLAGS_PROFILE)
                && pszAsUser
                && (   (fFlags & RTPROC_FLAGS_ENV_CHANGE_RECORD)
                    || hEnv == RTENV_DEFAULT) )
            {
                rc = rtProcPosixAdjustProfileEnvFromChild(hEnvToUse, fFlags, hEnv);
                papszEnv = RTEnvGetExecEnvP(hEnvToUse);
                if (RT_FAILURE(rc) || !papszEnv)
                {
                    if (fFlags & RTPROC_FLAGS_DETACHED)
                        _Exit(126);
                    else
                        exit(126);
                }
            }

            /*
             * Unset the signal mask.
             */
            sigset_t SigMask;
            sigemptyset(&SigMask);
            rc = sigprocmask(SIG_SETMASK, &SigMask, NULL);
            Assert(rc == 0);

            /*
             * Apply changes to the standard file descriptor and stuff.
             */
            for (int i = 0; i < 3; i++)
            {
                int fd = aStdFds[i];
                if (fd == -2)
                    close(i);
                else if (fd >= 0)
                {
                    int rc2 = dup2(fd, i);
                    if (rc2 != i)
                    {
                        if (fFlags & RTPROC_FLAGS_DETACHED)
                            _Exit(125);
                        else
                            exit(125);
                    }
                    for (int j = i + 1; j < 3; j++)
                        if (aStdFds[j] == fd)
                        {
                            fd = -1;
                            break;
                        }
                    if (fd >= 0)
                        close(fd);
                }
            }

            /*
             * Finally, execute the requested program.
             */
            rc = execve(pszExec, (char * const *)papszArgs, (char * const *)papszEnv);
            if (errno == ENOEXEC)
            {
                /* This can happen when trying to start a shell script without the magic #!/bin/sh */
                RTAssertMsg2Weak("Cannot execute this binary format!\n");
            }
            else
                RTAssertMsg2Weak("execve returns %d errno=%d\n", rc, errno);
            RTAssertReleasePanic();
            if (fFlags & RTPROC_FLAGS_DETACHED)
                _Exit(127);
            else
                exit(127);
        }
#ifdef RT_OS_SOLARIS
        if (!(fFlags & RTPROC_FLAGS_SAME_CONTRACT))
            rtSolarisContractPostForkParent(templateFd, pid);
#endif /* RT_OS_SOLARIS */
        if (pid > 0)
        {
            /* For a detached process this happens in the temp process, so
             * it's not worth doing anything as this process must exit. */
            if (fFlags & RTPROC_FLAGS_DETACHED)
                _Exit(0);
            if (phProcess)
                *phProcess = pid;
            return rtProcPosixCreateReturn(VINF_SUCCESS, hEnvToUse, hEnv);
        }
        /* For a detached process this happens in the temp process, so
         * it's not worth doing anything as this process must exit. */
        if (fFlags & RTPROC_FLAGS_DETACHED)
            _Exit(124);
        return rtProcPosixCreateReturn(RTErrConvertFromErrno(errno), hEnvToUse, hEnv);
    }

    return rtProcPosixCreateReturn(VERR_NOT_IMPLEMENTED, hEnvToUse, hEnv);
}