VirtualBox

source: vbox/trunk/src/VBox/Runtime/r3/posix/process-creation-posix.cpp@ 62564

Last change on this file since 62564 was 62564, checked in by vboxsync, 8 years ago

IPRT: Mark unused parameters.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 33.2 KB
Line 
1/* $Id: process-creation-posix.cpp 62564 2016-07-26 14:43:03Z vboxsync $ */
2/** @file
3 * IPRT - Process Creation, POSIX.
4 */
5
6/*
7 * Copyright (C) 2006-2016 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*********************************************************************************************************************************
29* Header Files *
30*********************************************************************************************************************************/
31#define LOG_GROUP RTLOGGROUP_PROCESS
32#include <iprt/cdefs.h>
33
34#include <unistd.h>
35#include <stdlib.h>
36#include <errno.h>
37#include <sys/types.h>
38#include <sys/stat.h>
39#include <sys/wait.h>
40#include <fcntl.h>
41#include <signal.h>
42#include <grp.h>
43#include <pwd.h>
44#if defined(RT_OS_LINUX) || defined(RT_OS_SOLARIS)
45# include <crypt.h>
46# include <shadow.h>
47#endif
48
49#if defined(RT_OS_LINUX) || defined(RT_OS_OS2)
50/* While Solaris has posix_spawn() of course we don't want to use it as
51 * we need to have the child in a different process contract, no matter
52 * whether it is started detached or not. */
53# define HAVE_POSIX_SPAWN 1
54#endif
55#if defined(RT_OS_DARWIN) && defined(MAC_OS_X_VERSION_MIN_REQUIRED)
56# if MAC_OS_X_VERSION_MIN_REQUIRED >= 1050
57# define HAVE_POSIX_SPAWN 1
58# endif
59#endif
60#ifdef HAVE_POSIX_SPAWN
61# include <spawn.h>
62#endif
63
64#ifdef RT_OS_DARWIN
65# include <mach-o/dyld.h>
66#endif
67#ifdef RT_OS_SOLARIS
68# include <limits.h>
69# include <sys/ctfs.h>
70# include <sys/contract/process.h>
71# include <libcontract.h>
72#endif
73
74#ifndef RT_OS_SOLARIS
75# include <paths.h>
76#else
77# define _PATH_MAILDIR "/var/mail"
78# define _PATH_DEFPATH "/usr/bin:/bin"
79# define _PATH_STDPATH "/sbin:/usr/sbin:/bin:/usr/bin"
80#endif
81
82
83#include <iprt/process.h>
84#include "internal/iprt.h"
85
86#include <iprt/assert.h>
87#include <iprt/env.h>
88#include <iprt/err.h>
89#include <iprt/file.h>
90#include <iprt/path.h>
91#include <iprt/pipe.h>
92#include <iprt/socket.h>
93#include <iprt/string.h>
94#include <iprt/mem.h>
95#include "internal/process.h"
96
97
98/**
99 * Check the credentials and return the gid/uid of user.
100 *
101 * @param pszUser username
102 * @param pszPasswd password
103 * @param gid where to store the GID of the user
104 * @param uid where to store the UID of the user
105 * @returns IPRT status code
106 */
107static int rtCheckCredentials(const char *pszUser, const char *pszPasswd, gid_t *pGid, uid_t *pUid)
108{
109#if defined(RT_OS_LINUX)
110 struct passwd *pw;
111
112 pw = getpwnam(pszUser);
113 if (!pw)
114 return VERR_AUTHENTICATION_FAILURE;
115
116 if (!pszPasswd)
117 pszPasswd = "";
118
119 struct spwd *spwd;
120 /* works only if /etc/shadow is accessible */
121 spwd = getspnam(pszUser);
122 if (spwd)
123 pw->pw_passwd = spwd->sp_pwdp;
124
125 /* Default fCorrect=true if no password specified. In that case, pw->pw_passwd
126 * must be NULL (no password set for this user). Fail if a password is specified
127 * but the user does not have one assigned. */
128 int fCorrect = !pszPasswd || !*pszPasswd;
129 if (pw->pw_passwd && *pw->pw_passwd)
130 {
131 struct crypt_data *data = (struct crypt_data*)RTMemTmpAllocZ(sizeof(*data));
132 /* be reentrant */
133 char *pszEncPasswd = crypt_r(pszPasswd, pw->pw_passwd, data);
134 fCorrect = pszEncPasswd && !strcmp(pszEncPasswd, pw->pw_passwd);
135 RTMemTmpFree(data);
136 }
137 if (!fCorrect)
138 return VERR_AUTHENTICATION_FAILURE;
139
140 *pGid = pw->pw_gid;
141 *pUid = pw->pw_uid;
142 return VINF_SUCCESS;
143
144#elif defined(RT_OS_SOLARIS)
145 struct passwd *ppw, pw;
146 char szBuf[1024];
147
148 if (getpwnam_r(pszUser, &pw, szBuf, sizeof(szBuf), &ppw) != 0 || ppw == NULL)
149 return VERR_AUTHENTICATION_FAILURE;
150
151 if (!pszPasswd)
152 pszPasswd = "";
153
154 struct spwd spwd;
155 char szPwdBuf[1024];
156 /* works only if /etc/shadow is accessible */
157 if (getspnam_r(pszUser, &spwd, szPwdBuf, sizeof(szPwdBuf)) != NULL)
158 ppw->pw_passwd = spwd.sp_pwdp;
159
160 char *pszEncPasswd = crypt(pszPasswd, ppw->pw_passwd);
161 if (strcmp(pszEncPasswd, ppw->pw_passwd))
162 return VERR_AUTHENTICATION_FAILURE;
163
164 *pGid = ppw->pw_gid;
165 *pUid = ppw->pw_uid;
166 return VINF_SUCCESS;
167
168#else
169 NOREF(pszUser); NOREF(pszPasswd); NOREF(pGid); NOREF(pUid);
170 return VERR_AUTHENTICATION_FAILURE;
171#endif
172}
173
174
175#ifdef RT_OS_SOLARIS
176/** @todo the error reporting of the Solaris process contract code could be
177 * a lot better, but essentially it is not meant to run into errors after
178 * the debugging phase. */
179static int rtSolarisContractPreFork(void)
180{
181 int templateFd = open64(CTFS_ROOT "/process/template", O_RDWR);
182 if (templateFd < 0)
183 return -1;
184
185 /* Set template parameters and event sets. */
186 if (ct_pr_tmpl_set_param(templateFd, CT_PR_PGRPONLY))
187 {
188 close(templateFd);
189 return -1;
190 }
191 if (ct_pr_tmpl_set_fatal(templateFd, CT_PR_EV_HWERR))
192 {
193 close(templateFd);
194 return -1;
195 }
196 if (ct_tmpl_set_critical(templateFd, 0))
197 {
198 close(templateFd);
199 return -1;
200 }
201 if (ct_tmpl_set_informative(templateFd, CT_PR_EV_HWERR))
202 {
203 close(templateFd);
204 return -1;
205 }
206
207 /* Make this the active template for the process. */
208 if (ct_tmpl_activate(templateFd))
209 {
210 close(templateFd);
211 return -1;
212 }
213
214 return templateFd;
215}
216
217static void rtSolarisContractPostForkChild(int templateFd)
218{
219 if (templateFd == -1)
220 return;
221
222 /* Clear the active template. */
223 ct_tmpl_clear(templateFd);
224 close(templateFd);
225}
226
227static void rtSolarisContractPostForkParent(int templateFd, pid_t pid)
228{
229 if (templateFd == -1)
230 return;
231
232 /* Clear the active template. */
233 int cleared = ct_tmpl_clear(templateFd);
234 close(templateFd);
235
236 /* If the clearing failed or the fork failed there's nothing more to do. */
237 if (cleared || pid <= 0)
238 return;
239
240 /* Look up the contract which was created by this thread. */
241 int statFd = open64(CTFS_ROOT "/process/latest", O_RDONLY);
242 if (statFd == -1)
243 return;
244 ct_stathdl_t statHdl;
245 if (ct_status_read(statFd, CTD_COMMON, &statHdl))
246 {
247 close(statFd);
248 return;
249 }
250 ctid_t ctId = ct_status_get_id(statHdl);
251 ct_status_free(statHdl);
252 close(statFd);
253 if (ctId < 0)
254 return;
255
256 /* Abandon this contract we just created. */
257 char ctlPath[PATH_MAX];
258 size_t len = snprintf(ctlPath, sizeof(ctlPath),
259 CTFS_ROOT "/process/%ld/ctl", (long)ctId);
260 if (len >= sizeof(ctlPath))
261 return;
262 int ctlFd = open64(ctlPath, O_WRONLY);
263 if (statFd == -1)
264 return;
265 if (ct_ctl_abandon(ctlFd) < 0)
266 {
267 close(ctlFd);
268 return;
269 }
270 close(ctlFd);
271}
272
273#endif /* RT_OS_SOLARIS */
274
275
276RTR3DECL(int) RTProcCreate(const char *pszExec, const char * const *papszArgs, RTENV Env, unsigned fFlags, PRTPROCESS pProcess)
277{
278 return RTProcCreateEx(pszExec, papszArgs, Env, fFlags,
279 NULL, NULL, NULL, /* standard handles */
280 NULL /*pszAsUser*/, NULL /* pszPassword*/,
281 pProcess);
282}
283
284
285/**
286 * Adjust the profile environment after forking the child process and changing
287 * the UID.
288 *
289 * @returns IRPT status code.
290 * @param hEnvToUse The environment we're going to use with execve.
291 * @param fFlags The process creation flags.
292 * @param hEnv The environment passed in by the user.
293 */
294static int rtProcPosixAdjustProfileEnvFromChild(RTENV hEnvToUse, uint32_t fFlags, RTENV hEnv)
295{
296 int rc = VINF_SUCCESS;
297#ifdef RT_OS_DARWIN
298 if ( RT_SUCCESS(rc)
299 && (!(fFlags & RTPROC_FLAGS_ENV_CHANGE_RECORD) || RTEnvExistEx(hEnv, "TMPDIR")) )
300 {
301 char szValue[_4K];
302 size_t cbNeeded = confstr(_CS_DARWIN_USER_TEMP_DIR, szValue, sizeof(szValue));
303 if (cbNeeded > 0 && cbNeeded < sizeof(szValue))
304 {
305 char *pszTmp;
306 rc = RTStrCurrentCPToUtf8(&pszTmp, szValue);
307 if (RT_SUCCESS(rc))
308 {
309 rc = RTEnvSetEx(hEnvToUse, "TMPDIR", pszTmp);
310 RTStrFree(pszTmp);
311 }
312 }
313 else
314 rc = VERR_BUFFER_OVERFLOW;
315 }
316#else
317 RT_NOREF_PV(hEnvToUse); RT_NOREF_PV(fFlags); RT_NOREF_PV(hEnv);
318#endif
319 return rc;
320}
321
322
323/**
324 * Create a very very basic environment for a user.
325 *
326 * @returns IPRT status code.
327 * @param phEnvToUse Where to return the created environment.
328 * @param pszUser The user name for the profile.
329 */
330static int rtProcPosixCreateProfileEnv(PRTENV phEnvToUse, const char *pszUser)
331{
332 struct passwd Pwd;
333 struct passwd *pPwd = NULL;
334 char achBuf[_4K];
335 int rc;
336 errno = 0;
337 if (pszUser)
338 rc = getpwnam_r(pszUser, &Pwd, achBuf, sizeof(achBuf), &pPwd);
339 else
340 rc = getpwuid_r(getuid(), &Pwd, achBuf, sizeof(achBuf), &pPwd);
341 if (rc == 0 && pPwd)
342 {
343 char *pszDir;
344 rc = RTStrCurrentCPToUtf8(&pszDir, pPwd->pw_dir);
345 if (RT_SUCCESS(rc))
346 {
347 char *pszShell;
348 rc = RTStrCurrentCPToUtf8(&pszShell, pPwd->pw_shell);
349 if (RT_SUCCESS(rc))
350 {
351 char *pszUserFree = NULL;
352 if (!pszUser)
353 {
354 rc = RTStrCurrentCPToUtf8(&pszUserFree, pPwd->pw_name);
355 if (RT_SUCCESS(rc))
356 pszUser = pszUserFree;
357 }
358 if (RT_SUCCESS(rc))
359 {
360 rc = RTEnvCreate(phEnvToUse);
361 if (RT_SUCCESS(rc))
362 {
363 RTENV hEnvToUse = *phEnvToUse;
364
365 rc = RTEnvSetEx(hEnvToUse, "HOME", pszDir);
366 if (RT_SUCCESS(rc))
367 rc = RTEnvSetEx(hEnvToUse, "SHELL", pszShell);
368 if (RT_SUCCESS(rc))
369 rc = RTEnvSetEx(hEnvToUse, "USER", pszUser);
370 if (RT_SUCCESS(rc))
371 rc = RTEnvSetEx(hEnvToUse, "LOGNAME", pszUser);
372
373 if (RT_SUCCESS(rc))
374 rc = RTEnvSetEx(hEnvToUse, "PATH", pPwd->pw_uid == 0 ? _PATH_STDPATH : _PATH_DEFPATH);
375
376 if (RT_SUCCESS(rc))
377 {
378 RTStrPrintf(achBuf, sizeof(achBuf), "%s/%s", _PATH_MAILDIR, pszUser);
379 rc = RTEnvSetEx(hEnvToUse, "MAIL", achBuf);
380 }
381
382#ifdef RT_OS_DARWIN
383 if (RT_SUCCESS(rc) && !pszUserFree)
384 {
385 size_t cbNeeded = confstr(_CS_DARWIN_USER_TEMP_DIR, achBuf, sizeof(achBuf));
386 if (cbNeeded > 0 && cbNeeded < sizeof(achBuf))
387 {
388 char *pszTmp;
389 rc = RTStrCurrentCPToUtf8(&pszTmp, achBuf);
390 if (RT_SUCCESS(rc))
391 {
392 rc = RTEnvSetEx(hEnvToUse, "TMPDIR", pszTmp);
393 RTStrFree(pszTmp);
394 }
395 }
396 else
397 rc = VERR_BUFFER_OVERFLOW;
398 }
399#endif
400
401 /** @todo load /etc/environment, /etc/profile.env and ~/.pam_environment? */
402
403 if (RT_FAILURE(rc))
404 RTEnvDestroy(hEnvToUse);
405 }
406 RTStrFree(pszUserFree);
407 }
408 RTStrFree(pszShell);
409 }
410 RTStrFree(pszDir);
411 }
412 }
413 else
414 rc = errno ? RTErrConvertFromErrno(errno) : VERR_ACCESS_DENIED;
415 return rc;
416}
417
418
419/**
420 * RTPathTraverseList callback used by RTProcCreateEx to locate the executable.
421 */
422static DECLCALLBACK(int) rtPathFindExec(char const *pchPath, size_t cchPath, void *pvUser1, void *pvUser2)
423{
424 const char *pszExec = (const char *)pvUser1;
425 char *pszRealExec = (char *)pvUser2;
426 int rc = RTPathJoinEx(pszRealExec, RTPATH_MAX, pchPath, cchPath, pszExec, RTSTR_MAX);
427 if (RT_FAILURE(rc))
428 return rc;
429 if (!access(pszRealExec, X_OK))
430 return VINF_SUCCESS;
431 if ( errno == EACCES
432 || errno == EPERM)
433 return RTErrConvertFromErrno(errno);
434 return VERR_TRY_AGAIN;
435}
436
437/**
438 * Cleans up the environment on the way out.
439 */
440static int rtProcPosixCreateReturn(int rc, RTENV hEnvToUse, RTENV hEnv)
441{
442 if (hEnvToUse != hEnv)
443 RTEnvDestroy(hEnvToUse);
444 return rc;
445}
446
447
448RTR3DECL(int) RTProcCreateEx(const char *pszExec, const char * const *papszArgs, RTENV hEnv, uint32_t fFlags,
449 PCRTHANDLE phStdIn, PCRTHANDLE phStdOut, PCRTHANDLE phStdErr, const char *pszAsUser,
450 const char *pszPassword, PRTPROCESS phProcess)
451{
452 int rc;
453
454 /*
455 * Input validation
456 */
457 AssertPtrReturn(pszExec, VERR_INVALID_POINTER);
458 AssertReturn(*pszExec, VERR_INVALID_PARAMETER);
459 AssertReturn(!(fFlags & ~RTPROC_FLAGS_VALID_MASK), VERR_INVALID_PARAMETER);
460 AssertReturn(!(fFlags & RTPROC_FLAGS_DETACHED) || !phProcess, VERR_INVALID_PARAMETER);
461 AssertReturn(hEnv != NIL_RTENV, VERR_INVALID_PARAMETER);
462 AssertPtrReturn(papszArgs, VERR_INVALID_PARAMETER);
463 AssertPtrNullReturn(pszAsUser, VERR_INVALID_POINTER);
464 AssertReturn(!pszAsUser || *pszAsUser, VERR_INVALID_PARAMETER);
465 AssertReturn(!pszPassword || pszAsUser, VERR_INVALID_PARAMETER);
466 AssertPtrNullReturn(pszPassword, VERR_INVALID_POINTER);
467#if defined(RT_OS_OS2)
468 if (fFlags & RTPROC_FLAGS_DETACHED)
469 return VERR_PROC_DETACH_NOT_SUPPORTED;
470#endif
471
472 /*
473 * Get the file descriptors for the handles we've been passed.
474 */
475 PCRTHANDLE paHandles[3] = { phStdIn, phStdOut, phStdErr };
476 int aStdFds[3] = { -1, -1, -1 };
477 for (int i = 0; i < 3; i++)
478 {
479 if (paHandles[i])
480 {
481 AssertPtrReturn(paHandles[i], VERR_INVALID_POINTER);
482 switch (paHandles[i]->enmType)
483 {
484 case RTHANDLETYPE_FILE:
485 aStdFds[i] = paHandles[i]->u.hFile != NIL_RTFILE
486 ? (int)RTFileToNative(paHandles[i]->u.hFile)
487 : -2 /* close it */;
488 break;
489
490 case RTHANDLETYPE_PIPE:
491 aStdFds[i] = paHandles[i]->u.hPipe != NIL_RTPIPE
492 ? (int)RTPipeToNative(paHandles[i]->u.hPipe)
493 : -2 /* close it */;
494 break;
495
496 case RTHANDLETYPE_SOCKET:
497 aStdFds[i] = paHandles[i]->u.hSocket != NIL_RTSOCKET
498 ? (int)RTSocketToNative(paHandles[i]->u.hSocket)
499 : -2 /* close it */;
500 break;
501
502 default:
503 AssertMsgFailedReturn(("%d: %d\n", i, paHandles[i]->enmType), VERR_INVALID_PARAMETER);
504 }
505 /** @todo check the close-on-execness of these handles? */
506 }
507 }
508
509 for (int i = 0; i < 3; i++)
510 if (aStdFds[i] == i)
511 aStdFds[i] = -1;
512
513 for (int i = 0; i < 3; i++)
514 AssertMsgReturn(aStdFds[i] < 0 || aStdFds[i] > i,
515 ("%i := %i not possible because we're lazy\n", i, aStdFds[i]),
516 VERR_NOT_SUPPORTED);
517
518 /*
519 * Resolve the user id if specified.
520 */
521 uid_t uid = ~(uid_t)0;
522 gid_t gid = ~(gid_t)0;
523 if (pszAsUser)
524 {
525 rc = rtCheckCredentials(pszAsUser, pszPassword, &gid, &uid);
526 if (RT_FAILURE(rc))
527 return rc;
528 }
529
530 /*
531 * Create the child environment if either RTPROC_FLAGS_PROFILE or
532 * RTPROC_FLAGS_ENV_CHANGE_RECORD are in effect.
533 */
534 RTENV hEnvToUse = hEnv;
535 if ( (fFlags & (RTPROC_FLAGS_ENV_CHANGE_RECORD | RTPROC_FLAGS_PROFILE))
536 && ( (fFlags & RTPROC_FLAGS_ENV_CHANGE_RECORD)
537 || hEnv == RTENV_DEFAULT) )
538 {
539 if (fFlags & RTPROC_FLAGS_PROFILE)
540 rc = rtProcPosixCreateProfileEnv(&hEnvToUse, pszAsUser);
541 else
542 rc = RTEnvClone(&hEnvToUse, RTENV_DEFAULT);
543 if (RT_SUCCESS(rc))
544 {
545 if ((fFlags & RTPROC_FLAGS_ENV_CHANGE_RECORD) && hEnv != RTENV_DEFAULT)
546 rc = RTEnvApplyChanges(hEnvToUse, hEnv);
547 if (RT_FAILURE(rc))
548 RTEnvDestroy(hEnvToUse);
549 }
550 if (RT_FAILURE(rc))
551 return rc;
552 }
553
554 /*
555 * Check for execute access to the file.
556 */
557 char szRealExec[RTPATH_MAX];
558 if (access(pszExec, X_OK))
559 {
560 rc = errno;
561 if ( !(fFlags & RTPROC_FLAGS_SEARCH_PATH)
562 || rc != ENOENT
563 || RTPathHavePath(pszExec) )
564 rc = RTErrConvertFromErrno(rc);
565 else
566 {
567 /* search */
568 char *pszPath = RTEnvDupEx(hEnvToUse, "PATH");
569 rc = RTPathTraverseList(pszPath, ':', rtPathFindExec, (void *)pszExec, &szRealExec[0]);
570 RTStrFree(pszPath);
571 if (RT_SUCCESS(rc))
572 pszExec = szRealExec;
573 else
574 rc = rc == VERR_END_OF_STRING ? VERR_FILE_NOT_FOUND : rc;
575 }
576
577 if (RT_FAILURE(rc))
578 return rtProcPosixCreateReturn(rc, hEnvToUse, hEnv);
579 }
580
581 pid_t pid = -1;
582 const char * const *papszEnv = RTEnvGetExecEnvP(hEnvToUse);
583 AssertPtrReturn(papszEnv, rtProcPosixCreateReturn(VERR_INVALID_HANDLE, hEnvToUse, hEnv));
584
585
586 /*
587 * Take care of detaching the process.
588 *
589 * HACK ALERT! Put the process into a new process group with pgid = pid
590 * to make sure it differs from that of the parent process to ensure that
591 * the IPRT waitpid call doesn't race anyone (read XPCOM) doing group wide
592 * waits. setsid() includes the setpgid() functionality.
593 * 2010-10-11 XPCOM no longer waits for anything, but it cannot hurt.
594 */
595#ifndef RT_OS_OS2
596 if (fFlags & RTPROC_FLAGS_DETACHED)
597 {
598# ifdef RT_OS_SOLARIS
599 int templateFd = -1;
600 if (!(fFlags & RTPROC_FLAGS_SAME_CONTRACT))
601 {
602 templateFd = rtSolarisContractPreFork();
603 if (templateFd == -1)
604 return rtProcPosixCreateReturn(VERR_OPEN_FAILED, hEnvToUse, hEnv);
605 }
606# endif /* RT_OS_SOLARIS */
607 pid = fork();
608 if (!pid)
609 {
610# ifdef RT_OS_SOLARIS
611 if (!(fFlags & RTPROC_FLAGS_SAME_CONTRACT))
612 rtSolarisContractPostForkChild(templateFd);
613# endif
614 setsid(); /* see comment above */
615
616 pid = -1;
617 /* Child falls through to the actual spawn code below. */
618 }
619 else
620 {
621# ifdef RT_OS_SOLARIS
622 if (!(fFlags & RTPROC_FLAGS_SAME_CONTRACT))
623 rtSolarisContractPostForkParent(templateFd, pid);
624# endif
625 if (pid > 0)
626 {
627 /* Must wait for the temporary process to avoid a zombie. */
628 int status = 0;
629 pid_t pidChild = 0;
630
631 /* Restart if we get interrupted. */
632 do
633 {
634 pidChild = waitpid(pid, &status, 0);
635 } while ( pidChild == -1
636 && errno == EINTR);
637
638 /* Assume that something wasn't found. No detailed info. */
639 if (status)
640 return rtProcPosixCreateReturn(VERR_PROCESS_NOT_FOUND, hEnvToUse, hEnv);
641 if (phProcess)
642 *phProcess = 0;
643 return rtProcPosixCreateReturn(VINF_SUCCESS, hEnvToUse, hEnv);
644 }
645 return rtProcPosixCreateReturn(RTErrConvertFromErrno(errno), hEnvToUse, hEnv);
646 }
647 }
648#endif
649
650 /*
651 * Spawn the child.
652 *
653 * Any spawn code MUST not execute any atexit functions if it is for a
654 * detached process. It would lead to running the atexit functions which
655 * make only sense for the parent. libORBit e.g. gets confused by multiple
656 * execution. Remember, there was only a fork() so far, and until exec()
657 * is successfully run there is nothing which would prevent doing anything
658 * silly with the (duplicated) file descriptors.
659 */
660#ifdef HAVE_POSIX_SPAWN
661 /** @todo OS/2: implement DETACHED (BACKGROUND stuff), see VbglR3Daemonize. */
662 if ( uid == ~(uid_t)0
663 && gid == ~(gid_t)0)
664 {
665 /* Spawn attributes. */
666 posix_spawnattr_t Attr;
667 rc = posix_spawnattr_init(&Attr);
668 if (!rc)
669 {
670 /* Indicate that process group and signal mask are to be changed,
671 and that the child should use default signal actions. */
672 rc = posix_spawnattr_setflags(&Attr, POSIX_SPAWN_SETPGROUP | POSIX_SPAWN_SETSIGMASK | POSIX_SPAWN_SETSIGDEF);
673 Assert(rc == 0);
674
675 /* The child starts in its own process group. */
676 if (!rc)
677 {
678 rc = posix_spawnattr_setpgroup(&Attr, 0 /* pg == child pid */);
679 Assert(rc == 0);
680 }
681
682 /* Unmask all signals. */
683 if (!rc)
684 {
685 sigset_t SigMask;
686 sigemptyset(&SigMask);
687 rc = posix_spawnattr_setsigmask(&Attr, &SigMask); Assert(rc == 0);
688 }
689
690 /* File changes. */
691 posix_spawn_file_actions_t FileActions;
692 posix_spawn_file_actions_t *pFileActions = NULL;
693 if ((aStdFds[0] != -1 || aStdFds[1] != -1 || aStdFds[2] != -1) && !rc)
694 {
695 rc = posix_spawn_file_actions_init(&FileActions);
696 if (!rc)
697 {
698 pFileActions = &FileActions;
699 for (int i = 0; i < 3; i++)
700 {
701 int fd = aStdFds[i];
702 if (fd == -2)
703 rc = posix_spawn_file_actions_addclose(&FileActions, i);
704 else if (fd >= 0 && fd != i)
705 {
706 rc = posix_spawn_file_actions_adddup2(&FileActions, fd, i);
707 if (!rc)
708 {
709 for (int j = i + 1; j < 3; j++)
710 if (aStdFds[j] == fd)
711 {
712 fd = -1;
713 break;
714 }
715 if (fd >= 0)
716 rc = posix_spawn_file_actions_addclose(&FileActions, fd);
717 }
718 }
719 if (rc)
720 break;
721 }
722 }
723 }
724
725 if (!rc)
726 rc = posix_spawn(&pid, pszExec, pFileActions, &Attr, (char * const *)papszArgs,
727 (char * const *)papszEnv);
728
729 /* cleanup */
730 int rc2 = posix_spawnattr_destroy(&Attr); Assert(rc2 == 0); NOREF(rc2);
731 if (pFileActions)
732 {
733 rc2 = posix_spawn_file_actions_destroy(pFileActions);
734 Assert(rc2 == 0);
735 }
736
737 /* return on success.*/
738 if (!rc)
739 {
740 /* For a detached process this happens in the temp process, so
741 * it's not worth doing anything as this process must exit. */
742 if (fFlags & RTPROC_FLAGS_DETACHED)
743 _Exit(0);
744 if (phProcess)
745 *phProcess = pid;
746 return rtProcPosixCreateReturn(VINF_SUCCESS, hEnvToUse, hEnv);
747 }
748 }
749 /* For a detached process this happens in the temp process, so
750 * it's not worth doing anything as this process must exit. */
751 if (fFlags & RTPROC_FLAGS_DETACHED)
752 _Exit(124);
753 }
754 else
755#endif
756 {
757#ifdef RT_OS_SOLARIS
758 int templateFd = -1;
759 if (!(fFlags & RTPROC_FLAGS_SAME_CONTRACT))
760 {
761 templateFd = rtSolarisContractPreFork();
762 if (templateFd == -1)
763 return rtProcPosixCreateReturn(VERR_OPEN_FAILED, hEnvToUse, hEnv);
764 }
765#endif /* RT_OS_SOLARIS */
766 pid = fork();
767 if (!pid)
768 {
769#ifdef RT_OS_SOLARIS
770 if (!(fFlags & RTPROC_FLAGS_SAME_CONTRACT))
771 rtSolarisContractPostForkChild(templateFd);
772#endif /* RT_OS_SOLARIS */
773 if (!(fFlags & RTPROC_FLAGS_DETACHED))
774 setpgid(0, 0); /* see comment above */
775
776 /*
777 * Change group and user if requested.
778 */
779#if 1 /** @todo This needs more work, see suplib/hardening. */
780 if (pszAsUser)
781 {
782 int ret = initgroups(pszAsUser, gid);
783 if (ret)
784 {
785 if (fFlags & RTPROC_FLAGS_DETACHED)
786 _Exit(126);
787 else
788 exit(126);
789 }
790 }
791 if (gid != ~(gid_t)0)
792 {
793 if (setgid(gid))
794 {
795 if (fFlags & RTPROC_FLAGS_DETACHED)
796 _Exit(126);
797 else
798 exit(126);
799 }
800 }
801
802 if (uid != ~(uid_t)0)
803 {
804 if (setuid(uid))
805 {
806 if (fFlags & RTPROC_FLAGS_DETACHED)
807 _Exit(126);
808 else
809 exit(126);
810 }
811 }
812#endif
813
814 /*
815 * Some final profile environment tweaks, if running as user.
816 */
817 if ( (fFlags & RTPROC_FLAGS_PROFILE)
818 && pszAsUser
819 && ( (fFlags & RTPROC_FLAGS_ENV_CHANGE_RECORD)
820 || hEnv == RTENV_DEFAULT) )
821 {
822 rc = rtProcPosixAdjustProfileEnvFromChild(hEnvToUse, fFlags, hEnv);
823 papszEnv = RTEnvGetExecEnvP(hEnvToUse);
824 if (RT_FAILURE(rc) || !papszEnv)
825 {
826 if (fFlags & RTPROC_FLAGS_DETACHED)
827 _Exit(126);
828 else
829 exit(126);
830 }
831 }
832
833 /*
834 * Unset the signal mask.
835 */
836 sigset_t SigMask;
837 sigemptyset(&SigMask);
838 rc = sigprocmask(SIG_SETMASK, &SigMask, NULL);
839 Assert(rc == 0);
840
841 /*
842 * Apply changes to the standard file descriptor and stuff.
843 */
844 for (int i = 0; i < 3; i++)
845 {
846 int fd = aStdFds[i];
847 if (fd == -2)
848 close(i);
849 else if (fd >= 0)
850 {
851 int rc2 = dup2(fd, i);
852 if (rc2 != i)
853 {
854 if (fFlags & RTPROC_FLAGS_DETACHED)
855 _Exit(125);
856 else
857 exit(125);
858 }
859 for (int j = i + 1; j < 3; j++)
860 if (aStdFds[j] == fd)
861 {
862 fd = -1;
863 break;
864 }
865 if (fd >= 0)
866 close(fd);
867 }
868 }
869
870 /*
871 * Finally, execute the requested program.
872 */
873 rc = execve(pszExec, (char * const *)papszArgs, (char * const *)papszEnv);
874 if (errno == ENOEXEC)
875 {
876 /* This can happen when trying to start a shell script without the magic #!/bin/sh */
877 RTAssertMsg2Weak("Cannot execute this binary format!\n");
878 }
879 else
880 RTAssertMsg2Weak("execve returns %d errno=%d\n", rc, errno);
881 RTAssertReleasePanic();
882 if (fFlags & RTPROC_FLAGS_DETACHED)
883 _Exit(127);
884 else
885 exit(127);
886 }
887#ifdef RT_OS_SOLARIS
888 if (!(fFlags & RTPROC_FLAGS_SAME_CONTRACT))
889 rtSolarisContractPostForkParent(templateFd, pid);
890#endif /* RT_OS_SOLARIS */
891 if (pid > 0)
892 {
893 /* For a detached process this happens in the temp process, so
894 * it's not worth doing anything as this process must exit. */
895 if (fFlags & RTPROC_FLAGS_DETACHED)
896 _Exit(0);
897 if (phProcess)
898 *phProcess = pid;
899 return rtProcPosixCreateReturn(VINF_SUCCESS, hEnvToUse, hEnv);
900 }
901 /* For a detached process this happens in the temp process, so
902 * it's not worth doing anything as this process must exit. */
903 if (fFlags & RTPROC_FLAGS_DETACHED)
904 _Exit(124);
905 return rtProcPosixCreateReturn(RTErrConvertFromErrno(errno), hEnvToUse, hEnv);
906 }
907
908 return rtProcPosixCreateReturn(VERR_NOT_IMPLEMENTED, hEnvToUse, hEnv);
909}
910
911
912RTR3DECL(int) RTProcDaemonizeUsingFork(bool fNoChDir, bool fNoClose, const char *pszPidfile)
913{
914 /*
915 * Fork the child process in a new session and quit the parent.
916 *
917 * - fork once and create a new session (setsid). This will detach us
918 * from the controlling tty meaning that we won't receive the SIGHUP
919 * (or any other signal) sent to that session.
920 * - The SIGHUP signal is ignored because the session/parent may throw
921 * us one before we get to the setsid.
922 * - When the parent exit(0) we will become an orphan and re-parented to
923 * the init process.
924 * - Because of the sometimes unexpected semantics of assigning the
925 * controlling tty automagically when a session leader first opens a tty,
926 * we will fork() once more to get rid of the session leadership role.
927 */
928
929 /* We start off by opening the pidfile, so that we can fail straight away
930 * if it already exists. */
931 int fdPidfile = -1;
932 if (pszPidfile != NULL)
933 {
934 /* @note the exclusive create is not guaranteed on all file
935 * systems (e.g. NFSv2) */
936 if ((fdPidfile = open(pszPidfile, O_RDWR | O_CREAT | O_EXCL, 0644)) == -1)
937 return RTErrConvertFromErrno(errno);
938 }
939
940 /* Ignore SIGHUP straight away. */
941 struct sigaction OldSigAct;
942 struct sigaction SigAct;
943 memset(&SigAct, 0, sizeof(SigAct));
944 SigAct.sa_handler = SIG_IGN;
945 int rcSigAct = sigaction(SIGHUP, &SigAct, &OldSigAct);
946
947 /* First fork, to become independent process. */
948 pid_t pid = fork();
949 if (pid == -1)
950 {
951 if (fdPidfile != -1)
952 close(fdPidfile);
953 return RTErrConvertFromErrno(errno);
954 }
955 if (pid != 0)
956 {
957 /* Parent exits, no longer necessary. The child gets reparented
958 * to the init process. */
959 exit(0);
960 }
961
962 /* Create new session, fix up the standard file descriptors and the
963 * current working directory. */
964 /** @todo r=klaus the webservice uses this function and assumes that the
965 * contract id of the daemon is the same as that of the original process.
966 * Whenever this code is changed this must still remain possible. */
967 pid_t newpgid = setsid();
968 int SavedErrno = errno;
969 if (rcSigAct != -1)
970 sigaction(SIGHUP, &OldSigAct, NULL);
971 if (newpgid == -1)
972 {
973 if (fdPidfile != -1)
974 close(fdPidfile);
975 return RTErrConvertFromErrno(SavedErrno);
976 }
977
978 if (!fNoClose)
979 {
980 /* Open stdin(0), stdout(1) and stderr(2) as /dev/null. */
981 int fd = open("/dev/null", O_RDWR);
982 if (fd == -1) /* paranoia */
983 {
984 close(STDIN_FILENO);
985 close(STDOUT_FILENO);
986 close(STDERR_FILENO);
987 fd = open("/dev/null", O_RDWR);
988 }
989 if (fd != -1)
990 {
991 dup2(fd, STDIN_FILENO);
992 dup2(fd, STDOUT_FILENO);
993 dup2(fd, STDERR_FILENO);
994 if (fd > 2)
995 close(fd);
996 }
997 }
998
999 if (!fNoChDir)
1000 {
1001 int rcIgnored = chdir("/");
1002 NOREF(rcIgnored);
1003 }
1004
1005 /* Second fork to lose session leader status. */
1006 pid = fork();
1007 if (pid == -1)
1008 {
1009 if (fdPidfile != -1)
1010 close(fdPidfile);
1011 return RTErrConvertFromErrno(errno);
1012 }
1013
1014 if (pid != 0)
1015 {
1016 /* Write the pid file, this is done in the parent, before exiting. */
1017 if (fdPidfile != -1)
1018 {
1019 char szBuf[256];
1020 size_t cbPid = RTStrPrintf(szBuf, sizeof(szBuf), "%d\n", pid);
1021 ssize_t cbIgnored = write(fdPidfile, szBuf, cbPid); NOREF(cbIgnored);
1022 close(fdPidfile);
1023 }
1024 exit(0);
1025 }
1026
1027 if (fdPidfile != -1)
1028 close(fdPidfile);
1029
1030 return VINF_SUCCESS;
1031}
1032
Note: See TracBrowser for help on using the repository browser.

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