Changeset 33602 in vbox for trunk/src/VBox/Runtime/r3/posix/process-posix.cpp
- Timestamp:
- Oct 29, 2010 12:39:54 PM (15 years ago)
- svn:sync-xref-src-repo-rev:
- 67213
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Runtime/r3/posix/process-posix.cpp
r33135 r33602 37 37 #include <sys/stat.h> 38 38 #include <sys/wait.h> 39 #include <fcntl.h>40 39 #include <signal.h> 41 #if defined(RT_OS_LINUX) || defined(RT_OS_SOLARIS)42 # include <crypt.h>43 # include <pwd.h>44 # include <shadow.h>45 #endif46 #if defined(RT_OS_LINUX) || defined(RT_OS_OS2)47 /* While Solaris has posix_spawn() of course we don't want to use it as48 * we need to have the child in a different process contract, no matter49 * whether it is started detached or not. */50 # define HAVE_POSIX_SPAWN 151 #endif52 #ifdef HAVE_POSIX_SPAWN53 # include <spawn.h>54 #endif55 #ifdef RT_OS_DARWIN56 # include <mach-o/dyld.h>57 #endif58 #ifdef RT_OS_SOLARIS59 # include <limits.h>60 # include <sys/ctfs.h>61 # include <sys/contract/process.h>62 # include <libcontract.h>63 #endif64 40 65 41 #include <iprt/process.h> … … 77 53 78 54 79 /**80 * Check the credentials and return the gid/uid of user.81 *82 * @param pszUser username83 * @param pszPasswd password84 * @param gid where to store the GID of the user85 * @param uid where to store the UID of the user86 * @returns IPRT status code87 */88 static int rtCheckCredentials(const char *pszUser, const char *pszPasswd, gid_t *gid, uid_t *uid)89 {90 #if defined(RT_OS_LINUX)91 struct passwd *pw;92 93 pw = getpwnam(pszUser);94 if (!pw)95 return VERR_PERMISSION_DENIED;96 97 if (!pszPasswd)98 pszPasswd = "";99 100 struct spwd *spwd;101 /* works only if /etc/shadow is accessible */102 spwd = getspnam(pszUser);103 if (spwd)104 pw->pw_passwd = spwd->sp_pwdp;105 106 /* be reentrant */107 struct crypt_data *data = (struct crypt_data*)RTMemTmpAllocZ(sizeof(*data));108 char *pszEncPasswd = crypt_r(pszPasswd, pw->pw_passwd, data);109 if (strcmp(pszEncPasswd, pw->pw_passwd))110 return VERR_PERMISSION_DENIED;111 RTMemTmpFree(data);112 113 *gid = pw->pw_gid;114 *uid = pw->pw_uid;115 return VINF_SUCCESS;116 117 #elif defined(RT_OS_SOLARIS)118 struct passwd *ppw, pw;119 char szBuf[1024];120 121 if (getpwnam_r(pszUser, &pw, szBuf, sizeof(szBuf), &ppw) != 0 || ppw == NULL)122 return VERR_PERMISSION_DENIED;123 124 if (!pszPasswd)125 pszPasswd = "";126 127 struct spwd spwd;128 char szPwdBuf[1024];129 /* works only if /etc/shadow is accessible */130 if (getspnam_r(pszUser, &spwd, szPwdBuf, sizeof(szPwdBuf)) != NULL)131 ppw->pw_passwd = spwd.sp_pwdp;132 133 char *pszEncPasswd = crypt(pszPasswd, ppw->pw_passwd);134 if (strcmp(pszEncPasswd, ppw->pw_passwd))135 return VERR_PERMISSION_DENIED;136 137 *gid = ppw->pw_gid;138 *uid = ppw->pw_uid;139 return VINF_SUCCESS;140 141 #else142 return VERR_PERMISSION_DENIED;143 #endif144 }145 146 147 #ifdef RT_OS_SOLARIS148 /** @todo the error reporting of the Solaris process contract code could be149 * a lot better, but essentially it is not meant to run into errors after150 * the debugging phase. */151 static int rtSolarisContractPreFork(void)152 {153 int templateFd = open64(CTFS_ROOT "/process/template", O_RDWR);154 if (templateFd < 0)155 return -1;156 157 /* Set template parameters and event sets. */158 if (ct_pr_tmpl_set_param(templateFd, CT_PR_PGRPONLY))159 {160 close(templateFd);161 return -1;162 }163 if (ct_pr_tmpl_set_fatal(templateFd, CT_PR_EV_HWERR))164 {165 close(templateFd);166 return -1;167 }168 if (ct_tmpl_set_critical(templateFd, 0))169 {170 close(templateFd);171 return -1;172 }173 if (ct_tmpl_set_informative(templateFd, CT_PR_EV_HWERR))174 {175 close(templateFd);176 return -1;177 }178 179 /* Make this the active template for the process. */180 if (ct_tmpl_activate(templateFd))181 {182 close(templateFd);183 return -1;184 }185 186 return templateFd;187 }188 189 static void rtSolarisContractPostForkChild(int templateFd)190 {191 if (templateFd == -1)192 return;193 194 /* Clear the active template. */195 ct_tmpl_clear(templateFd);196 close(templateFd);197 }198 199 static void rtSolarisContractPostForkParent(int templateFd, pid_t pid)200 {201 if (templateFd == -1)202 return;203 204 /* Clear the active template. */205 int cleared = ct_tmpl_clear(templateFd);206 close(templateFd);207 208 /* If the clearing failed or the fork failed there's nothing more to do. */209 if (cleared || pid <= 0)210 return;211 212 /* Look up the contract which was created by this thread. */213 int statFd = open64(CTFS_ROOT "/process/latest", O_RDONLY);214 if (statFd == -1)215 return;216 ct_stathdl_t statHdl;217 if (ct_status_read(statFd, CTD_COMMON, &statHdl))218 {219 close(statFd);220 return;221 }222 ctid_t ctId = ct_status_get_id(statHdl);223 ct_status_free(statHdl);224 close(statFd);225 if (ctId < 0)226 return;227 228 /* Abandon this contract we just created. */229 char ctlPath[PATH_MAX];230 size_t len = snprintf(ctlPath, sizeof(ctlPath),231 CTFS_ROOT "/process/%d/ctl", ctId);232 if (len >= sizeof(ctlPath))233 return;234 int ctlFd = open64(ctlPath, O_WRONLY);235 if (statFd == -1)236 return;237 if (ct_ctl_abandon(ctlFd) < 0)238 {239 close(ctlFd);240 return;241 }242 close(ctlFd);243 }244 245 #endif /* RT_OS_SOLARIS */246 247 248 RTR3DECL(int) RTProcCreate(const char *pszExec, const char * const *papszArgs, RTENV Env, unsigned fFlags, PRTPROCESS pProcess)249 {250 return RTProcCreateEx(pszExec, papszArgs, Env, fFlags,251 NULL, NULL, NULL, /* standard handles */252 NULL /*pszAsUser*/, NULL /* pszPassword*/,253 pProcess);254 }255 256 257 RTR3DECL(int) RTProcCreateEx(const char *pszExec, const char * const *papszArgs, RTENV hEnv, uint32_t fFlags,258 PCRTHANDLE phStdIn, PCRTHANDLE phStdOut, PCRTHANDLE phStdErr, const char *pszAsUser,259 const char *pszPassword, PRTPROCESS phProcess)260 {261 int rc;262 263 /*264 * Input validation265 */266 AssertPtrReturn(pszExec, VERR_INVALID_POINTER);267 AssertReturn(*pszExec, VERR_INVALID_PARAMETER);268 AssertReturn(!(fFlags & ~(RTPROC_FLAGS_DETACHED | RTPROC_FLAGS_SERVICE)), VERR_INVALID_PARAMETER);269 AssertReturn(!(fFlags & RTPROC_FLAGS_DETACHED) || !phProcess, VERR_INVALID_PARAMETER);270 AssertReturn(hEnv != NIL_RTENV, VERR_INVALID_PARAMETER);271 const char * const *papszEnv = RTEnvGetExecEnvP(hEnv);272 AssertPtrReturn(papszEnv, VERR_INVALID_HANDLE);273 AssertPtrReturn(papszArgs, VERR_INVALID_PARAMETER);274 /** @todo search the PATH (add flag for this). */275 AssertPtrNullReturn(pszAsUser, VERR_INVALID_POINTER);276 AssertReturn(!pszAsUser || *pszAsUser, VERR_INVALID_PARAMETER);277 AssertReturn(!pszPassword || pszAsUser, VERR_INVALID_PARAMETER);278 AssertPtrNullReturn(pszPassword, VERR_INVALID_POINTER);279 280 /*281 * Get the file descriptors for the handles we've been passed.282 */283 PCRTHANDLE paHandles[3] = { phStdIn, phStdOut, phStdErr };284 int aStdFds[3] = { -1, -1, -1 };285 for (int i = 0; i < 3; i++)286 {287 if (paHandles[i])288 {289 AssertPtrReturn(paHandles[i], VERR_INVALID_POINTER);290 switch (paHandles[i]->enmType)291 {292 case RTHANDLETYPE_FILE:293 aStdFds[i] = paHandles[i]->u.hFile != NIL_RTFILE294 ? (int)RTFileToNative(paHandles[i]->u.hFile)295 : -2 /* close it */;296 break;297 298 case RTHANDLETYPE_PIPE:299 aStdFds[i] = paHandles[i]->u.hPipe != NIL_RTPIPE300 ? (int)RTPipeToNative(paHandles[i]->u.hPipe)301 : -2 /* close it */;302 break;303 304 case RTHANDLETYPE_SOCKET:305 aStdFds[i] = paHandles[i]->u.hSocket != NIL_RTSOCKET306 ? (int)RTSocketToNative(paHandles[i]->u.hSocket)307 : -2 /* close it */;308 break;309 310 default:311 AssertMsgFailedReturn(("%d: %d\n", i, paHandles[i]->enmType), VERR_INVALID_PARAMETER);312 }313 /** @todo check the close-on-execness of these handles? */314 }315 }316 317 for (int i = 0; i < 3; i++)318 if (aStdFds[i] == i)319 aStdFds[i] = -1;320 321 for (int i = 0; i < 3; i++)322 AssertMsgReturn(aStdFds[i] < 0 || aStdFds[i] > i,323 ("%i := %i not possible because we're lazy\n", i, aStdFds[i]),324 VERR_NOT_SUPPORTED);325 326 /*327 * Resolve the user id if specified.328 */329 uid_t uid = ~(uid_t)0;330 gid_t gid = ~(gid_t)0;331 if (pszAsUser)332 {333 rc = rtCheckCredentials(pszAsUser, pszPassword, &gid, &uid);334 if (RT_FAILURE(rc))335 return rc;336 }337 338 /*339 * Check for execute access to the file.340 */341 if (access(pszExec, X_OK))342 {343 rc = RTErrConvertFromErrno(errno);344 AssertMsgFailed(("'%s' %Rrc!\n", pszExec, rc));345 return rc;346 }347 348 pid_t pid = -1;349 350 /*351 * Take care of detaching the process.352 *353 * HACK ALERT! Put the process into a new process group with pgid = pid354 * to make sure it differs from that of the parent process to ensure that355 * the IPRT waitpid call doesn't race anyone (read XPCOM) doing group wide356 * waits. setsid() includes the setpgid() functionality.357 * 2010-10-11 XPCOM no longer waits for anything, but it cannot hurt.358 */359 #ifndef RT_OS_OS2360 if (fFlags & RTPROC_FLAGS_DETACHED)361 {362 # ifdef RT_OS_SOLARIS363 int templateFd = rtSolarisContractPreFork();364 if (templateFd == -1)365 return VERR_OPEN_FAILED;366 # endif /* RT_OS_SOLARIS */367 pid = fork();368 if (!pid)369 {370 # ifdef RT_OS_SOLARIS371 rtSolarisContractPostForkChild(templateFd);372 # endif /* RT_OS_SOLARIS */373 setsid(); /* see comment above */374 375 pid = -1;376 /* Child falls through to the actual spawn code below. */377 }378 else379 {380 #ifdef RT_OS_SOLARIS381 rtSolarisContractPostForkParent(templateFd, pid);382 #endif /* RT_OS_SOLARIS */383 if (pid > 0)384 {385 /* Must wait for the temporary process to avoid a zombie. */386 int status = 0;387 pid_t pidChild = 0;388 389 /* Restart if we get interrupted. */390 do391 {392 pidChild = waitpid(pid, &status, 0);393 } while ( pidChild == -1394 && errno == EINTR);395 396 /* Assume that something wasn't found. No detailed info. */397 if (status)398 return VERR_PROCESS_NOT_FOUND;399 if (phProcess)400 *phProcess = 0;401 return VINF_SUCCESS;402 }403 return RTErrConvertFromErrno(errno);404 }405 }406 #endif407 408 /*409 * Spawn the child.410 *411 * Any spawn code MUST not execute any atexit functions if it is for a412 * detached process. It would lead to running the atexit functions which413 * make only sense for the parent. libORBit e.g. gets confused by multiple414 * execution. Remember, there was only a fork() so far, and until exec()415 * is successfully run there is nothing which would prevent doing anything416 * silly with the (duplicated) file descriptors.417 */418 #ifdef HAVE_POSIX_SPAWN419 /** @todo OS/2: implement DETACHED (BACKGROUND stuff), see VbglR3Daemonize. */420 if ( uid == ~(uid_t)0421 && gid == ~(gid_t)0)422 {423 /* Spawn attributes. */424 posix_spawnattr_t Attr;425 rc = posix_spawnattr_init(&Attr);426 if (!rc)427 {428 # ifndef RT_OS_OS2 /* We don't need this on OS/2 and I don't recall if it's actually implemented. */429 rc = posix_spawnattr_setflags(&Attr, POSIX_SPAWN_SETPGROUP);430 Assert(rc == 0);431 if (!rc)432 {433 rc = posix_spawnattr_setpgroup(&Attr, 0 /* pg == child pid */);434 Assert(rc == 0);435 }436 # endif437 438 /* File changes. */439 posix_spawn_file_actions_t FileActions;440 posix_spawn_file_actions_t *pFileActions = NULL;441 if (aStdFds[0] != -1 || aStdFds[1] != -1 || aStdFds[2] != -1)442 {443 rc = posix_spawn_file_actions_init(&FileActions);444 if (!rc)445 {446 pFileActions = &FileActions;447 for (int i = 0; i < 3; i++)448 {449 int fd = aStdFds[i];450 if (fd == -2)451 rc = posix_spawn_file_actions_addclose(&FileActions, i);452 else if (fd >= 0 && fd != i)453 {454 rc = posix_spawn_file_actions_adddup2(&FileActions, fd, i);455 if (!rc)456 {457 for (int j = i + 1; j < 3; j++)458 if (aStdFds[j] == fd)459 {460 fd = -1;461 break;462 }463 if (fd >= 0)464 rc = posix_spawn_file_actions_addclose(&FileActions, fd);465 }466 }467 if (rc)468 break;469 }470 }471 }472 473 if (!rc)474 rc = posix_spawn(&pid, pszExec, pFileActions, &Attr, (char * const *)papszArgs,475 (char * const *)papszEnv);476 477 /* cleanup */478 int rc2 = posix_spawnattr_destroy(&Attr); Assert(rc2 == 0); NOREF(rc2);479 if (pFileActions)480 {481 rc2 = posix_spawn_file_actions_destroy(pFileActions);482 Assert(rc2 == 0);483 }484 485 /* return on success.*/486 if (!rc)487 {488 /* For a detached process this happens in the temp process, so489 * it's not worth doing anything as this process must exit. */490 if (fFlags & RTPROC_FLAGS_DETACHED)491 _Exit(0);492 if (phProcess)493 *phProcess = pid;494 return VINF_SUCCESS;495 }496 }497 /* For a detached process this happens in the temp process, so498 * it's not worth doing anything as this process must exit. */499 if (fFlags & RTPROC_FLAGS_DETACHED)500 _Exit(124);501 }502 else503 #endif504 {505 #ifdef RT_OS_SOLARIS506 int templateFd = rtSolarisContractPreFork();507 if (templateFd == -1)508 return VERR_OPEN_FAILED;509 #endif /* RT_OS_SOLARIS */510 pid = fork();511 if (!pid)512 {513 #ifdef RT_OS_SOLARIS514 rtSolarisContractPostForkChild(templateFd);515 #endif /* RT_OS_SOLARIS */516 if (!(fFlags & RTPROC_FLAGS_DETACHED))517 setpgid(0, 0); /* see comment above */518 519 /*520 * Change group and user if requested.521 */522 #if 1 /** @todo This needs more work, see suplib/hardening. */523 if (gid != ~(gid_t)0)524 {525 if (setgid(gid))526 {527 if (fFlags & RTPROC_FLAGS_DETACHED)528 _Exit(126);529 else530 exit(126);531 }532 }533 534 if (uid != ~(uid_t)0)535 {536 if (setuid(uid))537 {538 if (fFlags & RTPROC_FLAGS_DETACHED)539 _Exit(126);540 else541 exit(126);542 }543 }544 #endif545 546 /*547 * Apply changes to the standard file descriptor and stuff.548 */549 for (int i = 0; i < 3; i++)550 {551 int fd = aStdFds[i];552 if (fd == -2)553 close(i);554 else if (fd >= 0)555 {556 int rc2 = dup2(fd, i);557 if (rc2 != i)558 {559 if (fFlags & RTPROC_FLAGS_DETACHED)560 _Exit(125);561 else562 exit(125);563 }564 for (int j = i + 1; j < 3; j++)565 if (aStdFds[j] == fd)566 {567 fd = -1;568 break;569 }570 if (fd >= 0)571 close(fd);572 }573 }574 575 /*576 * Finally, execute the requested program.577 */578 rc = execve(pszExec, (char * const *)papszArgs, (char * const *)papszEnv);579 if (errno == ENOEXEC)580 {581 /* This can happen when trying to start a shell script without the magic #!/bin/sh */582 RTAssertMsg2Weak("Cannot execute this binary format!\n");583 }584 else585 RTAssertMsg2Weak("execve returns %d errno=%d\n", rc, errno);586 RTAssertReleasePanic();587 if (fFlags & RTPROC_FLAGS_DETACHED)588 _Exit(127);589 else590 exit(127);591 }592 #ifdef RT_OS_SOLARIS593 rtSolarisContractPostForkParent(templateFd, pid);594 #endif /* RT_OS_SOLARIS */595 if (pid > 0)596 {597 /* For a detached process this happens in the temp process, so598 * it's not worth doing anything as this process must exit. */599 if (fFlags & RTPROC_FLAGS_DETACHED)600 _Exit(0);601 if (phProcess)602 *phProcess = pid;603 return VINF_SUCCESS;604 }605 /* For a detached process this happens in the temp process, so606 * it's not worth doing anything as this process must exit. */607 if (fFlags & RTPROC_FLAGS_DETACHED)608 _Exit(124);609 return RTErrConvertFromErrno(errno);610 }611 612 return VERR_NOT_IMPLEMENTED;613 }614 615 616 55 RTR3DECL(int) RTProcWait(RTPROCESS Process, unsigned fFlags, PRTPROCSTATUS pProcStatus) 617 56 { … … 621 60 return rc; 622 61 } 62 623 63 624 64 RTR3DECL(int) RTProcWaitNoResume(RTPROCESS Process, unsigned fFlags, PRTPROCSTATUS pProcStatus) … … 697 137 698 138 699 RTR3DECL(uint64_t) RTProcGetAffinityMask( )139 RTR3DECL(uint64_t) RTProcGetAffinityMask(void) 700 140 { 701 // @todo141 /// @todo 702 142 return 1; 703 143 } 704 144 705 706 RTR3DECL(int) RTProcDaemonizeUsingFork(bool fNoChDir, bool fNoClose, const char *pszPidfile)707 {708 /*709 * Fork the child process in a new session and quit the parent.710 *711 * - fork once and create a new session (setsid). This will detach us712 * from the controlling tty meaning that we won't receive the SIGHUP713 * (or any other signal) sent to that session.714 * - The SIGHUP signal is ignored because the session/parent may throw715 * us one before we get to the setsid.716 * - When the parent exit(0) we will become an orphan and re-parented to717 * the init process.718 * - Because of the sometimes unexpected semantics of assigning the719 * controlling tty automagically when a session leader first opens a tty,720 * we will fork() once more to get rid of the session leadership role.721 */722 723 /* We start off by opening the pidfile, so that we can fail straight away724 * if it already exists. */725 int fdPidfile = -1;726 if (pszPidfile != NULL)727 {728 /* @note the exclusive create is not guaranteed on all file729 * systems (e.g. NFSv2) */730 if ((fdPidfile = open(pszPidfile, O_RDWR | O_CREAT | O_EXCL, 0644)) == -1)731 return RTErrConvertFromErrno(errno);732 }733 734 /* Ignore SIGHUP straight away. */735 struct sigaction OldSigAct;736 struct sigaction SigAct;737 memset(&SigAct, 0, sizeof(SigAct));738 SigAct.sa_handler = SIG_IGN;739 int rcSigAct = sigaction(SIGHUP, &SigAct, &OldSigAct);740 741 /* First fork, to become independent process. */742 pid_t pid = fork();743 if (pid == -1)744 return RTErrConvertFromErrno(errno);745 if (pid != 0)746 {747 /* Parent exits, no longer necessary. The child gets reparented748 * to the init process. */749 exit(0);750 }751 752 /* Create new session, fix up the standard file descriptors and the753 * current working directory. */754 pid_t newpgid = setsid();755 int SavedErrno = errno;756 if (rcSigAct != -1)757 sigaction(SIGHUP, &OldSigAct, NULL);758 if (newpgid == -1)759 return RTErrConvertFromErrno(SavedErrno);760 761 if (!fNoClose)762 {763 /* Open stdin(0), stdout(1) and stderr(2) as /dev/null. */764 int fd = open("/dev/null", O_RDWR);765 if (fd == -1) /* paranoia */766 {767 close(STDIN_FILENO);768 close(STDOUT_FILENO);769 close(STDERR_FILENO);770 fd = open("/dev/null", O_RDWR);771 }772 if (fd != -1)773 {774 dup2(fd, STDIN_FILENO);775 dup2(fd, STDOUT_FILENO);776 dup2(fd, STDERR_FILENO);777 if (fd > 2)778 close(fd);779 }780 }781 782 if (!fNoChDir)783 {784 int rcChdir = chdir("/");785 }786 787 /* Second fork to lose session leader status. */788 pid = fork();789 if (pid == -1)790 return RTErrConvertFromErrno(errno);791 792 if (pid != 0)793 {794 /* Write the pid file, this is done in the parent, before exiting. */795 if (fdPidfile != -1)796 {797 char szBuf[256];798 size_t cbPid = RTStrPrintf(szBuf, sizeof(szBuf), "%d\n", pid);799 int rcWrite = write(fdPidfile, szBuf, cbPid);800 close(fdPidfile);801 }802 exit(0);803 }804 805 return VINF_SUCCESS;806 }807
Note:
See TracChangeset
for help on using the changeset viewer.