VirtualBox

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

Last change on this file since 86186 was 82968, checked in by vboxsync, 5 years ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 42.3 KB
Line 
1/* $Id: process-creation-posix.cpp 82968 2020-02-04 10:35:17Z vboxsync $ */
2/** @file
3 * IPRT - Process Creation, POSIX.
4 */
5
6/*
7 * Copyright (C) 2006-2020 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#ifdef RT_OS_LINUX
34# define IPRT_WITH_DYNAMIC_CRYPT_R
35#endif
36#if (defined(RT_OS_LINUX) || defined(RT_OS_OS2)) && !defined(_GNU_SOURCE)
37# define _GNU_SOURCE
38#endif
39
40#ifdef RT_OS_OS2
41# define crypt unistd_crypt
42# define setkey unistd_setkey
43# define encrypt unistd_encrypt
44# include <unistd.h>
45# undef crypt
46# undef setkey
47# undef encrypt
48#else
49# include <unistd.h>
50#endif
51#include <stdlib.h>
52#include <errno.h>
53#include <sys/types.h>
54#include <sys/stat.h>
55#include <sys/wait.h>
56#include <fcntl.h>
57#include <signal.h>
58#include <grp.h>
59#include <pwd.h>
60#if defined(RT_OS_LINUX) || defined(RT_OS_OS2) || defined(RT_OS_SOLARIS)
61# include <crypt.h>
62#endif
63#if defined(RT_OS_LINUX) || defined(RT_OS_SOLARIS)
64# include <shadow.h>
65#endif
66
67#if defined(RT_OS_LINUX) || defined(RT_OS_OS2)
68/* While Solaris has posix_spawn() of course we don't want to use it as
69 * we need to have the child in a different process contract, no matter
70 * whether it is started detached or not. */
71# define HAVE_POSIX_SPAWN 1
72#endif
73#if defined(RT_OS_DARWIN) && defined(MAC_OS_X_VERSION_MIN_REQUIRED)
74# if MAC_OS_X_VERSION_MIN_REQUIRED >= 1050
75# define HAVE_POSIX_SPAWN 1
76# endif
77#endif
78#ifdef HAVE_POSIX_SPAWN
79# include <spawn.h>
80#endif
81
82#if !defined(IPRT_USE_PAM) && ( defined(RT_OS_DARWIN) || defined(RT_OS_FREEBSD) || defined(RT_OS_NETBSD) || defined(RT_OS_OPENBSD) )
83# define IPRT_USE_PAM
84#endif
85#ifdef IPRT_USE_PAM
86# ifdef RT_OS_DARWIN
87# include <mach-o/dyld.h>
88# define IPRT_LIBPAM_FILE "libpam.dylib"
89# define IPRT_PAM_SERVICE_NAME "login" /** @todo we've been abusing 'login' here, probably not needed? */
90# else
91# define IPRT_LIBPAM_FILE "libpam.so"
92# define IPRT_PAM_SERVICE_NAME "iprt-as-user"
93# endif
94# include <security/pam_appl.h>
95# include <stdlib.h>
96# include <dlfcn.h>
97# include <iprt/asm.h>
98#endif
99
100#ifdef RT_OS_SOLARIS
101# include <limits.h>
102# include <sys/ctfs.h>
103# include <sys/contract/process.h>
104# include <libcontract.h>
105#endif
106
107#ifndef RT_OS_SOLARIS
108# include <paths.h>
109#else
110# define _PATH_MAILDIR "/var/mail"
111# define _PATH_DEFPATH "/usr/bin:/bin"
112# define _PATH_STDPATH "/sbin:/usr/sbin:/bin:/usr/bin"
113#endif
114
115
116#include <iprt/process.h>
117#include "internal/iprt.h"
118
119#include <iprt/assert.h>
120#include <iprt/env.h>
121#include <iprt/err.h>
122#include <iprt/file.h>
123#ifdef IPRT_WITH_DYNAMIC_CRYPT_R
124# include <iprt/ldr.h>
125#endif
126#include <iprt/log.h>
127#include <iprt/path.h>
128#include <iprt/pipe.h>
129#include <iprt/socket.h>
130#include <iprt/string.h>
131#include <iprt/mem.h>
132#include "internal/process.h"
133
134
135/*********************************************************************************************************************************
136* Structures and Typedefs *
137*********************************************************************************************************************************/
138#ifdef IPRT_USE_PAM
139/** For passing info between rtCheckCredentials and rtPamConv. */
140typedef struct RTPROCPAMARGS
141{
142 const char *pszUser;
143 const char *pszPassword;
144} RTPROCPAMARGS;
145/** Pointer to rtPamConv argument package. */
146typedef RTPROCPAMARGS *PRTPROCPAMARGS;
147#endif
148
149
150#ifdef IPRT_USE_PAM
151/**
152 * Worker for rtCheckCredentials that feeds password and maybe username to PAM.
153 *
154 * @returns PAM status.
155 * @param cMessages Number of messages.
156 * @param papMessages Message vector.
157 * @param ppaResponses Where to put our responses.
158 * @param pvAppData Pointer to RTPROCPAMARGS.
159 */
160static int rtPamConv(int cMessages, const struct pam_message **papMessages, struct pam_response **ppaResponses, void *pvAppData)
161{
162 LogFlow(("rtPamConv: cMessages=%d\n", cMessages));
163 PRTPROCPAMARGS pArgs = (PRTPROCPAMARGS)pvAppData;
164 AssertPtrReturn(pArgs, PAM_CONV_ERR);
165
166 struct pam_response *paResponses = (struct pam_response *)calloc(cMessages, sizeof(paResponses[0]));
167 AssertReturn(paResponses, PAM_CONV_ERR);
168 for (int i = 0; i < cMessages; i++)
169 {
170 LogFlow(("rtPamConv: #%d: msg_style=%d msg=%s\n", i, papMessages[i]->msg_style, papMessages[i]->msg));
171
172 paResponses[i].resp_retcode = 0;
173 if (papMessages[i]->msg_style == PAM_PROMPT_ECHO_OFF)
174 paResponses[i].resp = strdup(pArgs->pszPassword);
175 else if (papMessages[i]->msg_style == PAM_PROMPT_ECHO_ON)
176 paResponses[i].resp = strdup(pArgs->pszUser);
177 else
178 {
179 paResponses[i].resp = NULL;
180 continue;
181 }
182 if (paResponses[i].resp == NULL)
183 {
184 while (i-- > 0)
185 free(paResponses[i].resp);
186 free(paResponses);
187 LogFlow(("rtPamConv: out of memory\n"));
188 return PAM_CONV_ERR;
189 }
190 }
191
192 *ppaResponses = paResponses;
193 return PAM_SUCCESS;
194}
195#endif /* IPRT_USE_PAM */
196
197
198#if defined(IPRT_WITH_DYNAMIC_CRYPT_R) && !defined(IPRT_USE_PAM)
199/** Pointer to crypt_r(). */
200typedef char *(*PFNCRYPTR)(const char *, const char *, struct crypt_data *);
201
202/**
203 * Wrapper for resolving and calling crypt_r dynamcially.
204 *
205 * The reason for this is that fedora 30+ wants to use libxcrypt rather than the
206 * glibc libcrypt. The two libraries has different crypt_data sizes and layout,
207 * so we allocate a 256KB data block to be on the safe size (caller does this).
208 */
209static char *rtProcDynamicCryptR(const char *pszKey, const char *pszSalt, struct crypt_data *pData)
210{
211 static PFNCRYPTR volatile s_pfnCryptR = NULL;
212 PFNCRYPTR pfnCryptR = s_pfnCryptR;
213 if (pfnCryptR)
214 return pfnCryptR(pszKey, pszSalt, pData);
215
216 pfnCryptR = (PFNCRYPTR)(uintptr_t)RTLdrGetSystemSymbolEx("libcrypt.so", "crypt_r", RTLDRLOAD_FLAGS_SO_VER_RANGE(1, 6));
217 if (!pfnCryptR)
218 pfnCryptR = (PFNCRYPTR)(uintptr_t)RTLdrGetSystemSymbolEx("libxcrypt.so", "crypt_r", RTLDRLOAD_FLAGS_SO_VER_RANGE(1, 32));
219 if (pfnCryptR)
220 {
221 s_pfnCryptR = pfnCryptR;
222 return pfnCryptR(pszKey, pszSalt, pData);
223 }
224
225 LogRel(("IPRT/RTProc: Unable to locate crypt_r!\n"));
226 return NULL;
227}
228#endif /* IPRT_WITH_DYNAMIC_CRYPT_R */
229
230
231/**
232 * Check the credentials and return the gid/uid of user.
233 *
234 * @param pszUser username
235 * @param pszPasswd password
236 * @param gid where to store the GID of the user
237 * @param uid where to store the UID of the user
238 * @returns IPRT status code
239 */
240static int rtCheckCredentials(const char *pszUser, const char *pszPasswd, gid_t *pGid, uid_t *pUid)
241{
242#ifdef IPRT_USE_PAM
243 RTLogPrintf("rtCheckCredentials\n");
244
245 /*
246 * Resolve user to UID and GID.
247 */
248 char szBuf[_4K];
249 struct passwd Pw;
250 struct passwd *pPw;
251 if (getpwnam_r(pszUser, &Pw, szBuf, sizeof(szBuf), &pPw) != 0)
252 return VERR_AUTHENTICATION_FAILURE;
253 if (!pPw)
254 return VERR_AUTHENTICATION_FAILURE;
255
256 *pUid = pPw->pw_uid;
257 *pGid = pPw->pw_gid;
258
259 /*
260 * Use PAM for the authentication.
261 * Note! libpam.2.dylib was introduced with 10.6.x (OpenPAM).
262 */
263 void *hModPam = dlopen(IPRT_LIBPAM_FILE, RTLD_LAZY | RTLD_GLOBAL);
264 if (hModPam)
265 {
266 int (*pfnPamStart)(const char *, const char *, struct pam_conv *, pam_handle_t **);
267 int (*pfnPamAuthenticate)(pam_handle_t *, int);
268 int (*pfnPamAcctMgmt)(pam_handle_t *, int);
269 int (*pfnPamSetItem)(pam_handle_t *, int, const void *);
270 int (*pfnPamEnd)(pam_handle_t *, int);
271 *(void **)&pfnPamStart = dlsym(hModPam, "pam_start");
272 *(void **)&pfnPamAuthenticate = dlsym(hModPam, "pam_authenticate");
273 *(void **)&pfnPamAcctMgmt = dlsym(hModPam, "pam_acct_mgmt");
274 *(void **)&pfnPamSetItem = dlsym(hModPam, "pam_set_item");
275 *(void **)&pfnPamEnd = dlsym(hModPam, "pam_end");
276 ASMCompilerBarrier();
277 if ( pfnPamStart
278 && pfnPamAuthenticate
279 && pfnPamAcctMgmt
280 && pfnPamSetItem
281 && pfnPamEnd)
282 {
283# define pam_start pfnPamStart
284# define pam_authenticate pfnPamAuthenticate
285# define pam_acct_mgmt pfnPamAcctMgmt
286# define pam_set_item pfnPamSetItem
287# define pam_end pfnPamEnd
288
289 /* Do the PAM stuff. */
290 pam_handle_t *hPam = NULL;
291 RTPROCPAMARGS PamConvArgs = { pszUser, pszPasswd };
292 struct pam_conv PamConversation;
293 RT_ZERO(PamConversation);
294 PamConversation.appdata_ptr = &PamConvArgs;
295 PamConversation.conv = rtPamConv;
296 int rc = pam_start(IPRT_PAM_SERVICE_NAME, pszUser, &PamConversation, &hPam);
297 if (rc == PAM_SUCCESS)
298 {
299 rc = pam_set_item(hPam, PAM_RUSER, pszUser);
300 if (rc == PAM_SUCCESS)
301 rc = pam_authenticate(hPam, 0);
302 if (rc == PAM_SUCCESS)
303 {
304 rc = pam_acct_mgmt(hPam, 0);
305 if ( rc == PAM_SUCCESS
306 || rc == PAM_AUTHINFO_UNAVAIL /*??*/)
307 {
308 pam_end(hPam, PAM_SUCCESS);
309 dlclose(hModPam);
310 return VINF_SUCCESS;
311 }
312 Log(("rtCheckCredentials: pam_acct_mgmt -> %d\n", rc));
313 }
314 else
315 Log(("rtCheckCredentials: pam_authenticate -> %d\n", rc));
316 pam_end(hPam, rc);
317 }
318 else
319 Log(("rtCheckCredentials: pam_start -> %d\n", rc));
320 }
321 else
322 Log(("rtCheckCredentials: failed to resolve symbols: %p %p %p %p %p\n",
323 pfnPamStart, pfnPamAuthenticate, pfnPamAcctMgmt, pfnPamSetItem, pfnPamEnd));
324 dlclose(hModPam);
325 }
326 else
327 Log(("rtCheckCredentials: Loading " IPRT_LIBPAM_FILE " failed\n"));
328 return VERR_AUTHENTICATION_FAILURE;
329
330#else
331 /*
332 * Lookup the user in /etc/passwd first.
333 *
334 * Note! On FreeBSD and OS/2 the root user will open /etc/shadow here, so
335 * the getspnam_r step is not necessary.
336 */
337 struct passwd Pwd;
338 char szBuf[_4K];
339 struct passwd *pPwd = NULL;
340 if (getpwnam_r(pszUser, &Pwd, szBuf, sizeof(szBuf), &pPwd) != 0)
341 return VERR_AUTHENTICATION_FAILURE;
342 if (pPwd == NULL)
343 return VERR_AUTHENTICATION_FAILURE;
344
345# if defined(RT_OS_LINUX) || defined(RT_OS_SOLARIS)
346 /*
347 * Ditto for /etc/shadow and replace pw_passwd from above if we can access it:
348 */
349 struct spwd ShwPwd;
350 char szBuf2[_4K];
351# if defined(RT_OS_LINUX)
352 struct spwd *pShwPwd = NULL;
353 if (getspnam_r(pszUser, &ShwPwd, szBuf2, sizeof(szBuf2), &pShwPwd) != 0)
354 pShwPwd = NULL;
355# else
356 struct spwd *pShwPwd = getspnam_r(pszUser, &ShwPwd, szBuf2, sizeof(szBuf2));
357# endif
358 if (pShwPwd != NULL)
359 pPwd->pw_passwd = pShwPwd->sp_pwdp;
360# endif
361
362 /*
363 * Encrypt the passed in password and see if it matches.
364 */
365# if !defined(RT_OS_LINUX)
366 int rc;
367# else
368 /* Default fCorrect=true if no password specified. In that case, pPwd->pw_passwd
369 must be NULL (no password set for this user). Fail if a password is specified
370 but the user does not have one assigned. */
371 int rc = !pszPasswd || !*pszPasswd ? VINF_SUCCESS : VERR_AUTHENTICATION_FAILURE;
372 if (pPwd->pw_passwd && *pPwd->pw_passwd)
373# endif
374 {
375# if defined(RT_OS_LINUX) || defined(RT_OS_OS2)
376# ifdef IPRT_WITH_DYNAMIC_CRYPT_R
377 size_t const cbCryptData = RT_MAX(sizeof(struct crypt_data) * 2, _256K);
378# else
379 size_t const cbCryptData = sizeof(struct crypt_data);
380# endif
381 struct crypt_data *pCryptData = (struct crypt_data *)RTMemTmpAllocZ(cbCryptData);
382 if (pCryptData)
383 {
384# ifdef IPRT_WITH_DYNAMIC_CRYPT_R
385 char *pszEncPasswd = rtProcDynamicCryptR(pszPasswd, pPwd->pw_passwd, pCryptData);
386# else
387 char *pszEncPasswd = crypt_r(pszPasswd, pPwd->pw_passwd, pCryptData);
388# endif
389 rc = pszEncPasswd && !strcmp(pszEncPasswd, pPwd->pw_passwd) ? VINF_SUCCESS : VERR_AUTHENTICATION_FAILURE;
390 RTMemWipeThoroughly(pCryptData, cbCryptData, 3);
391 RTMemTmpFree(pCryptData);
392 }
393 else
394 rc = VERR_NO_TMP_MEMORY;
395# else
396 char *pszEncPasswd = crypt(pszPasswd, pPwd->pw_passwd);
397 rc = strcmp(pszEncPasswd, pPwd->pw_passwd) == 0 ? VINF_SUCCESS : VERR_AUTHENTICATION_FAILURE;
398# endif
399 }
400
401 /*
402 * Return GID and UID on success. Always wipe stack buffers.
403 */
404 if (RT_SUCCESS(rc))
405 {
406 *pGid = pPwd->pw_gid;
407 *pUid = pPwd->pw_uid;
408 }
409 RTMemWipeThoroughly(szBuf, sizeof(szBuf), 3);
410# if defined(RT_OS_LINUX) || defined(RT_OS_SOLARIS)
411 RTMemWipeThoroughly(szBuf2, sizeof(szBuf2), 3);
412# endif
413 return rc;
414#endif
415}
416
417#ifdef RT_OS_SOLARIS
418
419/** @todo the error reporting of the Solaris process contract code could be
420 * a lot better, but essentially it is not meant to run into errors after
421 * the debugging phase. */
422static int rtSolarisContractPreFork(void)
423{
424 int templateFd = open64(CTFS_ROOT "/process/template", O_RDWR);
425 if (templateFd < 0)
426 return -1;
427
428 /* Set template parameters and event sets. */
429 if (ct_pr_tmpl_set_param(templateFd, CT_PR_PGRPONLY))
430 {
431 close(templateFd);
432 return -1;
433 }
434 if (ct_pr_tmpl_set_fatal(templateFd, CT_PR_EV_HWERR))
435 {
436 close(templateFd);
437 return -1;
438 }
439 if (ct_tmpl_set_critical(templateFd, 0))
440 {
441 close(templateFd);
442 return -1;
443 }
444 if (ct_tmpl_set_informative(templateFd, CT_PR_EV_HWERR))
445 {
446 close(templateFd);
447 return -1;
448 }
449
450 /* Make this the active template for the process. */
451 if (ct_tmpl_activate(templateFd))
452 {
453 close(templateFd);
454 return -1;
455 }
456
457 return templateFd;
458}
459
460static void rtSolarisContractPostForkChild(int templateFd)
461{
462 if (templateFd == -1)
463 return;
464
465 /* Clear the active template. */
466 ct_tmpl_clear(templateFd);
467 close(templateFd);
468}
469
470static void rtSolarisContractPostForkParent(int templateFd, pid_t pid)
471{
472 if (templateFd == -1)
473 return;
474
475 /* Clear the active template. */
476 int cleared = ct_tmpl_clear(templateFd);
477 close(templateFd);
478
479 /* If the clearing failed or the fork failed there's nothing more to do. */
480 if (cleared || pid <= 0)
481 return;
482
483 /* Look up the contract which was created by this thread. */
484 int statFd = open64(CTFS_ROOT "/process/latest", O_RDONLY);
485 if (statFd == -1)
486 return;
487 ct_stathdl_t statHdl;
488 if (ct_status_read(statFd, CTD_COMMON, &statHdl))
489 {
490 close(statFd);
491 return;
492 }
493 ctid_t ctId = ct_status_get_id(statHdl);
494 ct_status_free(statHdl);
495 close(statFd);
496 if (ctId < 0)
497 return;
498
499 /* Abandon this contract we just created. */
500 char ctlPath[PATH_MAX];
501 size_t len = snprintf(ctlPath, sizeof(ctlPath),
502 CTFS_ROOT "/process/%ld/ctl", (long)ctId);
503 if (len >= sizeof(ctlPath))
504 return;
505 int ctlFd = open64(ctlPath, O_WRONLY);
506 if (statFd == -1)
507 return;
508 if (ct_ctl_abandon(ctlFd) < 0)
509 {
510 close(ctlFd);
511 return;
512 }
513 close(ctlFd);
514}
515
516#endif /* RT_OS_SOLARIS */
517
518
519RTR3DECL(int) RTProcCreate(const char *pszExec, const char * const *papszArgs, RTENV Env, unsigned fFlags, PRTPROCESS pProcess)
520{
521 return RTProcCreateEx(pszExec, papszArgs, Env, fFlags,
522 NULL, NULL, NULL, /* standard handles */
523 NULL /*pszAsUser*/, NULL /* pszPassword*/, NULL /*pvExtraData*/,
524 pProcess);
525}
526
527
528/**
529 * Adjust the profile environment after forking the child process and changing
530 * the UID.
531 *
532 * @returns IRPT status code.
533 * @param hEnvToUse The environment we're going to use with execve.
534 * @param fFlags The process creation flags.
535 * @param hEnv The environment passed in by the user.
536 */
537static int rtProcPosixAdjustProfileEnvFromChild(RTENV hEnvToUse, uint32_t fFlags, RTENV hEnv)
538{
539 int rc = VINF_SUCCESS;
540#ifdef RT_OS_DARWIN
541 if ( RT_SUCCESS(rc)
542 && (!(fFlags & RTPROC_FLAGS_ENV_CHANGE_RECORD) || RTEnvExistEx(hEnv, "TMPDIR")) )
543 {
544 char szValue[_4K];
545 size_t cbNeeded = confstr(_CS_DARWIN_USER_TEMP_DIR, szValue, sizeof(szValue));
546 if (cbNeeded > 0 && cbNeeded < sizeof(szValue))
547 {
548 char *pszTmp;
549 rc = RTStrCurrentCPToUtf8(&pszTmp, szValue);
550 if (RT_SUCCESS(rc))
551 {
552 rc = RTEnvSetEx(hEnvToUse, "TMPDIR", pszTmp);
553 RTStrFree(pszTmp);
554 }
555 }
556 else
557 rc = VERR_BUFFER_OVERFLOW;
558 }
559#else
560 RT_NOREF_PV(hEnvToUse); RT_NOREF_PV(fFlags); RT_NOREF_PV(hEnv);
561#endif
562 return rc;
563}
564
565
566/**
567 * Create a very very basic environment for a user.
568 *
569 * @returns IPRT status code.
570 * @param phEnvToUse Where to return the created environment.
571 * @param pszUser The user name for the profile.
572 */
573static int rtProcPosixCreateProfileEnv(PRTENV phEnvToUse, const char *pszUser)
574{
575 struct passwd Pwd;
576 struct passwd *pPwd = NULL;
577 char achBuf[_4K];
578 int rc;
579 errno = 0;
580 if (pszUser)
581 rc = getpwnam_r(pszUser, &Pwd, achBuf, sizeof(achBuf), &pPwd);
582 else
583 rc = getpwuid_r(getuid(), &Pwd, achBuf, sizeof(achBuf), &pPwd);
584 if (rc == 0 && pPwd)
585 {
586 char *pszDir;
587 rc = RTStrCurrentCPToUtf8(&pszDir, pPwd->pw_dir);
588 if (RT_SUCCESS(rc))
589 {
590 char *pszShell;
591 rc = RTStrCurrentCPToUtf8(&pszShell, pPwd->pw_shell);
592 if (RT_SUCCESS(rc))
593 {
594 char *pszUserFree = NULL;
595 if (!pszUser)
596 {
597 rc = RTStrCurrentCPToUtf8(&pszUserFree, pPwd->pw_name);
598 if (RT_SUCCESS(rc))
599 pszUser = pszUserFree;
600 }
601 if (RT_SUCCESS(rc))
602 {
603 rc = RTEnvCreate(phEnvToUse);
604 if (RT_SUCCESS(rc))
605 {
606 RTENV hEnvToUse = *phEnvToUse;
607
608 rc = RTEnvSetEx(hEnvToUse, "HOME", pszDir);
609 if (RT_SUCCESS(rc))
610 rc = RTEnvSetEx(hEnvToUse, "SHELL", pszShell);
611 if (RT_SUCCESS(rc))
612 rc = RTEnvSetEx(hEnvToUse, "USER", pszUser);
613 if (RT_SUCCESS(rc))
614 rc = RTEnvSetEx(hEnvToUse, "LOGNAME", pszUser);
615
616 if (RT_SUCCESS(rc))
617 rc = RTEnvSetEx(hEnvToUse, "PATH", pPwd->pw_uid == 0 ? _PATH_STDPATH : _PATH_DEFPATH);
618
619 if (RT_SUCCESS(rc))
620 {
621 RTStrPrintf(achBuf, sizeof(achBuf), "%s/%s", _PATH_MAILDIR, pszUser);
622 rc = RTEnvSetEx(hEnvToUse, "MAIL", achBuf);
623 }
624
625#ifdef RT_OS_DARWIN
626 if (RT_SUCCESS(rc) && !pszUserFree)
627 {
628 size_t cbNeeded = confstr(_CS_DARWIN_USER_TEMP_DIR, achBuf, sizeof(achBuf));
629 if (cbNeeded > 0 && cbNeeded < sizeof(achBuf))
630 {
631 char *pszTmp;
632 rc = RTStrCurrentCPToUtf8(&pszTmp, achBuf);
633 if (RT_SUCCESS(rc))
634 {
635 rc = RTEnvSetEx(hEnvToUse, "TMPDIR", pszTmp);
636 RTStrFree(pszTmp);
637 }
638 }
639 else
640 rc = VERR_BUFFER_OVERFLOW;
641 }
642#endif
643
644 /** @todo load /etc/environment, /etc/profile.env and ~/.pam_environment? */
645
646 if (RT_FAILURE(rc))
647 RTEnvDestroy(hEnvToUse);
648 }
649 RTStrFree(pszUserFree);
650 }
651 RTStrFree(pszShell);
652 }
653 RTStrFree(pszDir);
654 }
655 }
656 else
657 rc = errno ? RTErrConvertFromErrno(errno) : VERR_ACCESS_DENIED;
658 return rc;
659}
660
661
662/**
663 * RTPathTraverseList callback used by RTProcCreateEx to locate the executable.
664 */
665static DECLCALLBACK(int) rtPathFindExec(char const *pchPath, size_t cchPath, void *pvUser1, void *pvUser2)
666{
667 const char *pszExec = (const char *)pvUser1;
668 char *pszRealExec = (char *)pvUser2;
669 int rc = RTPathJoinEx(pszRealExec, RTPATH_MAX, pchPath, cchPath, pszExec, RTSTR_MAX);
670 if (RT_FAILURE(rc))
671 return rc;
672 if (!access(pszRealExec, X_OK))
673 return VINF_SUCCESS;
674 if ( errno == EACCES
675 || errno == EPERM)
676 return RTErrConvertFromErrno(errno);
677 return VERR_TRY_AGAIN;
678}
679
680/**
681 * Cleans up the environment on the way out.
682 */
683static int rtProcPosixCreateReturn(int rc, RTENV hEnvToUse, RTENV hEnv)
684{
685 if (hEnvToUse != hEnv)
686 RTEnvDestroy(hEnvToUse);
687 return rc;
688}
689
690
691RTR3DECL(int) RTProcCreateEx(const char *pszExec, const char * const *papszArgs, RTENV hEnv, uint32_t fFlags,
692 PCRTHANDLE phStdIn, PCRTHANDLE phStdOut, PCRTHANDLE phStdErr, const char *pszAsUser,
693 const char *pszPassword, void *pvExtraData, PRTPROCESS phProcess)
694{
695 int rc;
696 LogFlow(("RTProcCreateEx: pszExec=%s pszAsUser=%s\n", pszExec, pszAsUser));
697
698 /*
699 * Input validation
700 */
701 AssertPtrReturn(pszExec, VERR_INVALID_POINTER);
702 AssertReturn(*pszExec, VERR_INVALID_PARAMETER);
703 AssertReturn(!(fFlags & ~RTPROC_FLAGS_VALID_MASK), VERR_INVALID_PARAMETER);
704 AssertReturn(!(fFlags & RTPROC_FLAGS_DETACHED) || !phProcess, VERR_INVALID_PARAMETER);
705 AssertReturn(hEnv != NIL_RTENV, VERR_INVALID_PARAMETER);
706 AssertPtrReturn(papszArgs, VERR_INVALID_PARAMETER);
707 AssertPtrNullReturn(pszAsUser, VERR_INVALID_POINTER);
708 AssertReturn(!pszAsUser || *pszAsUser, VERR_INVALID_PARAMETER);
709 AssertReturn(!pszPassword || pszAsUser, VERR_INVALID_PARAMETER);
710 AssertPtrNullReturn(pszPassword, VERR_INVALID_POINTER);
711#if defined(RT_OS_OS2)
712 if (fFlags & RTPROC_FLAGS_DETACHED)
713 return VERR_PROC_DETACH_NOT_SUPPORTED;
714#endif
715 AssertReturn(pvExtraData == NULL || (fFlags & RTPROC_FLAGS_DESIRED_SESSION_ID), VERR_INVALID_PARAMETER);
716
717 /*
718 * Get the file descriptors for the handles we've been passed.
719 */
720 PCRTHANDLE paHandles[3] = { phStdIn, phStdOut, phStdErr };
721 int aStdFds[3] = { -1, -1, -1 };
722 for (int i = 0; i < 3; i++)
723 {
724 if (paHandles[i])
725 {
726 AssertPtrReturn(paHandles[i], VERR_INVALID_POINTER);
727 switch (paHandles[i]->enmType)
728 {
729 case RTHANDLETYPE_FILE:
730 aStdFds[i] = paHandles[i]->u.hFile != NIL_RTFILE
731 ? (int)RTFileToNative(paHandles[i]->u.hFile)
732 : -2 /* close it */;
733 break;
734
735 case RTHANDLETYPE_PIPE:
736 aStdFds[i] = paHandles[i]->u.hPipe != NIL_RTPIPE
737 ? (int)RTPipeToNative(paHandles[i]->u.hPipe)
738 : -2 /* close it */;
739 break;
740
741 case RTHANDLETYPE_SOCKET:
742 aStdFds[i] = paHandles[i]->u.hSocket != NIL_RTSOCKET
743 ? (int)RTSocketToNative(paHandles[i]->u.hSocket)
744 : -2 /* close it */;
745 break;
746
747 default:
748 AssertMsgFailedReturn(("%d: %d\n", i, paHandles[i]->enmType), VERR_INVALID_PARAMETER);
749 }
750 /** @todo check the close-on-execness of these handles? */
751 }
752 }
753
754 for (int i = 0; i < 3; i++)
755 if (aStdFds[i] == i)
756 aStdFds[i] = -1;
757
758 for (int i = 0; i < 3; i++)
759 AssertMsgReturn(aStdFds[i] < 0 || aStdFds[i] > i,
760 ("%i := %i not possible because we're lazy\n", i, aStdFds[i]),
761 VERR_NOT_SUPPORTED);
762
763 /*
764 * Resolve the user id if specified.
765 */
766 uid_t uid = ~(uid_t)0;
767 gid_t gid = ~(gid_t)0;
768 if (pszAsUser)
769 {
770 rc = rtCheckCredentials(pszAsUser, pszPassword, &gid, &uid);
771 if (RT_FAILURE(rc))
772 return rc;
773 }
774
775 /*
776 * Create the child environment if either RTPROC_FLAGS_PROFILE or
777 * RTPROC_FLAGS_ENV_CHANGE_RECORD are in effect.
778 */
779 RTENV hEnvToUse = hEnv;
780 if ( (fFlags & (RTPROC_FLAGS_ENV_CHANGE_RECORD | RTPROC_FLAGS_PROFILE))
781 && ( (fFlags & RTPROC_FLAGS_ENV_CHANGE_RECORD)
782 || hEnv == RTENV_DEFAULT) )
783 {
784 if (fFlags & RTPROC_FLAGS_PROFILE)
785 rc = rtProcPosixCreateProfileEnv(&hEnvToUse, pszAsUser);
786 else
787 rc = RTEnvClone(&hEnvToUse, RTENV_DEFAULT);
788 if (RT_SUCCESS(rc))
789 {
790 if ((fFlags & RTPROC_FLAGS_ENV_CHANGE_RECORD) && hEnv != RTENV_DEFAULT)
791 rc = RTEnvApplyChanges(hEnvToUse, hEnv);
792 if (RT_FAILURE(rc))
793 RTEnvDestroy(hEnvToUse);
794 }
795 if (RT_FAILURE(rc))
796 return rc;
797 }
798
799 /*
800 * Check for execute access to the file.
801 */
802 char szRealExec[RTPATH_MAX];
803 if (access(pszExec, X_OK))
804 {
805 rc = errno;
806 if ( !(fFlags & RTPROC_FLAGS_SEARCH_PATH)
807 || rc != ENOENT
808 || RTPathHavePath(pszExec) )
809 rc = RTErrConvertFromErrno(rc);
810 else
811 {
812 /* search */
813 char *pszPath = RTEnvDupEx(hEnvToUse, "PATH");
814 rc = RTPathTraverseList(pszPath, ':', rtPathFindExec, (void *)pszExec, &szRealExec[0]);
815 RTStrFree(pszPath);
816 if (RT_SUCCESS(rc))
817 pszExec = szRealExec;
818 else
819 rc = rc == VERR_END_OF_STRING ? VERR_FILE_NOT_FOUND : rc;
820 }
821
822 if (RT_FAILURE(rc))
823 return rtProcPosixCreateReturn(rc, hEnvToUse, hEnv);
824 }
825
826 pid_t pid = -1;
827 const char * const *papszEnv = RTEnvGetExecEnvP(hEnvToUse);
828 AssertPtrReturn(papszEnv, rtProcPosixCreateReturn(VERR_INVALID_HANDLE, hEnvToUse, hEnv));
829
830
831 /*
832 * Take care of detaching the process.
833 *
834 * HACK ALERT! Put the process into a new process group with pgid = pid
835 * to make sure it differs from that of the parent process to ensure that
836 * the IPRT waitpid call doesn't race anyone (read XPCOM) doing group wide
837 * waits. setsid() includes the setpgid() functionality.
838 * 2010-10-11 XPCOM no longer waits for anything, but it cannot hurt.
839 */
840#ifndef RT_OS_OS2
841 if (fFlags & RTPROC_FLAGS_DETACHED)
842 {
843# ifdef RT_OS_SOLARIS
844 int templateFd = -1;
845 if (!(fFlags & RTPROC_FLAGS_SAME_CONTRACT))
846 {
847 templateFd = rtSolarisContractPreFork();
848 if (templateFd == -1)
849 return rtProcPosixCreateReturn(VERR_OPEN_FAILED, hEnvToUse, hEnv);
850 }
851# endif /* RT_OS_SOLARIS */
852 pid = fork();
853 if (!pid)
854 {
855# ifdef RT_OS_SOLARIS
856 if (!(fFlags & RTPROC_FLAGS_SAME_CONTRACT))
857 rtSolarisContractPostForkChild(templateFd);
858# endif
859 setsid(); /* see comment above */
860
861 pid = -1;
862 /* Child falls through to the actual spawn code below. */
863 }
864 else
865 {
866# ifdef RT_OS_SOLARIS
867 if (!(fFlags & RTPROC_FLAGS_SAME_CONTRACT))
868 rtSolarisContractPostForkParent(templateFd, pid);
869# endif
870 if (pid > 0)
871 {
872 /* Must wait for the temporary process to avoid a zombie. */
873 int status = 0;
874 pid_t pidChild = 0;
875
876 /* Restart if we get interrupted. */
877 do
878 {
879 pidChild = waitpid(pid, &status, 0);
880 } while ( pidChild == -1
881 && errno == EINTR);
882
883 /* Assume that something wasn't found. No detailed info. */
884 if (status)
885 return rtProcPosixCreateReturn(VERR_PROCESS_NOT_FOUND, hEnvToUse, hEnv);
886 if (phProcess)
887 *phProcess = 0;
888 return rtProcPosixCreateReturn(VINF_SUCCESS, hEnvToUse, hEnv);
889 }
890 return rtProcPosixCreateReturn(RTErrConvertFromErrno(errno), hEnvToUse, hEnv);
891 }
892 }
893#endif
894
895 /*
896 * Spawn the child.
897 *
898 * Any spawn code MUST not execute any atexit functions if it is for a
899 * detached process. It would lead to running the atexit functions which
900 * make only sense for the parent. libORBit e.g. gets confused by multiple
901 * execution. Remember, there was only a fork() so far, and until exec()
902 * is successfully run there is nothing which would prevent doing anything
903 * silly with the (duplicated) file descriptors.
904 */
905#ifdef HAVE_POSIX_SPAWN
906 /** @todo OS/2: implement DETACHED (BACKGROUND stuff), see VbglR3Daemonize. */
907 if ( uid == ~(uid_t)0
908 && gid == ~(gid_t)0)
909 {
910 /* Spawn attributes. */
911 posix_spawnattr_t Attr;
912 rc = posix_spawnattr_init(&Attr);
913 if (!rc)
914 {
915 /* Indicate that process group and signal mask are to be changed,
916 and that the child should use default signal actions. */
917 rc = posix_spawnattr_setflags(&Attr, POSIX_SPAWN_SETPGROUP | POSIX_SPAWN_SETSIGMASK | POSIX_SPAWN_SETSIGDEF);
918 Assert(rc == 0);
919
920 /* The child starts in its own process group. */
921 if (!rc)
922 {
923 rc = posix_spawnattr_setpgroup(&Attr, 0 /* pg == child pid */);
924 Assert(rc == 0);
925 }
926
927 /* Unmask all signals. */
928 if (!rc)
929 {
930 sigset_t SigMask;
931 sigemptyset(&SigMask);
932 rc = posix_spawnattr_setsigmask(&Attr, &SigMask); Assert(rc == 0);
933 }
934
935 /* File changes. */
936 posix_spawn_file_actions_t FileActions;
937 posix_spawn_file_actions_t *pFileActions = NULL;
938 if ((aStdFds[0] != -1 || aStdFds[1] != -1 || aStdFds[2] != -1) && !rc)
939 {
940 rc = posix_spawn_file_actions_init(&FileActions);
941 if (!rc)
942 {
943 pFileActions = &FileActions;
944 for (int i = 0; i < 3; i++)
945 {
946 int fd = aStdFds[i];
947 if (fd == -2)
948 rc = posix_spawn_file_actions_addclose(&FileActions, i);
949 else if (fd >= 0 && fd != i)
950 {
951 rc = posix_spawn_file_actions_adddup2(&FileActions, fd, i);
952 if (!rc)
953 {
954 for (int j = i + 1; j < 3; j++)
955 if (aStdFds[j] == fd)
956 {
957 fd = -1;
958 break;
959 }
960 if (fd >= 0)
961 rc = posix_spawn_file_actions_addclose(&FileActions, fd);
962 }
963 }
964 if (rc)
965 break;
966 }
967 }
968 }
969
970 if (!rc)
971 rc = posix_spawn(&pid, pszExec, pFileActions, &Attr, (char * const *)papszArgs,
972 (char * const *)papszEnv);
973
974 /* cleanup */
975 int rc2 = posix_spawnattr_destroy(&Attr); Assert(rc2 == 0); NOREF(rc2);
976 if (pFileActions)
977 {
978 rc2 = posix_spawn_file_actions_destroy(pFileActions);
979 Assert(rc2 == 0);
980 }
981
982 /* return on success.*/
983 if (!rc)
984 {
985 /* For a detached process this happens in the temp process, so
986 * it's not worth doing anything as this process must exit. */
987 if (fFlags & RTPROC_FLAGS_DETACHED)
988 _Exit(0);
989 if (phProcess)
990 *phProcess = pid;
991 return rtProcPosixCreateReturn(VINF_SUCCESS, hEnvToUse, hEnv);
992 }
993 }
994 /* For a detached process this happens in the temp process, so
995 * it's not worth doing anything as this process must exit. */
996 if (fFlags & RTPROC_FLAGS_DETACHED)
997 _Exit(124);
998 }
999 else
1000#endif
1001 {
1002#ifdef RT_OS_SOLARIS
1003 int templateFd = -1;
1004 if (!(fFlags & RTPROC_FLAGS_SAME_CONTRACT))
1005 {
1006 templateFd = rtSolarisContractPreFork();
1007 if (templateFd == -1)
1008 return rtProcPosixCreateReturn(VERR_OPEN_FAILED, hEnvToUse, hEnv);
1009 }
1010#endif /* RT_OS_SOLARIS */
1011 pid = fork();
1012 if (!pid)
1013 {
1014#ifdef RT_OS_SOLARIS
1015 if (!(fFlags & RTPROC_FLAGS_SAME_CONTRACT))
1016 rtSolarisContractPostForkChild(templateFd);
1017#endif /* RT_OS_SOLARIS */
1018 if (!(fFlags & RTPROC_FLAGS_DETACHED))
1019 setpgid(0, 0); /* see comment above */
1020
1021 /*
1022 * Change group and user if requested.
1023 */
1024#if 1 /** @todo This needs more work, see suplib/hardening. */
1025 if (pszAsUser)
1026 {
1027 int ret = initgroups(pszAsUser, gid);
1028 if (ret)
1029 {
1030 if (fFlags & RTPROC_FLAGS_DETACHED)
1031 _Exit(126);
1032 else
1033 exit(126);
1034 }
1035 }
1036 if (gid != ~(gid_t)0)
1037 {
1038 if (setgid(gid))
1039 {
1040 if (fFlags & RTPROC_FLAGS_DETACHED)
1041 _Exit(126);
1042 else
1043 exit(126);
1044 }
1045 }
1046
1047 if (uid != ~(uid_t)0)
1048 {
1049 if (setuid(uid))
1050 {
1051 if (fFlags & RTPROC_FLAGS_DETACHED)
1052 _Exit(126);
1053 else
1054 exit(126);
1055 }
1056 }
1057#endif
1058
1059 /*
1060 * Some final profile environment tweaks, if running as user.
1061 */
1062 if ( (fFlags & RTPROC_FLAGS_PROFILE)
1063 && pszAsUser
1064 && ( (fFlags & RTPROC_FLAGS_ENV_CHANGE_RECORD)
1065 || hEnv == RTENV_DEFAULT) )
1066 {
1067 rc = rtProcPosixAdjustProfileEnvFromChild(hEnvToUse, fFlags, hEnv);
1068 papszEnv = RTEnvGetExecEnvP(hEnvToUse);
1069 if (RT_FAILURE(rc) || !papszEnv)
1070 {
1071 if (fFlags & RTPROC_FLAGS_DETACHED)
1072 _Exit(126);
1073 else
1074 exit(126);
1075 }
1076 }
1077
1078 /*
1079 * Unset the signal mask.
1080 */
1081 sigset_t SigMask;
1082 sigemptyset(&SigMask);
1083 rc = sigprocmask(SIG_SETMASK, &SigMask, NULL);
1084 Assert(rc == 0);
1085
1086 /*
1087 * Apply changes to the standard file descriptor and stuff.
1088 */
1089 for (int i = 0; i < 3; i++)
1090 {
1091 int fd = aStdFds[i];
1092 if (fd == -2)
1093 close(i);
1094 else if (fd >= 0)
1095 {
1096 int rc2 = dup2(fd, i);
1097 if (rc2 != i)
1098 {
1099 if (fFlags & RTPROC_FLAGS_DETACHED)
1100 _Exit(125);
1101 else
1102 exit(125);
1103 }
1104 for (int j = i + 1; j < 3; j++)
1105 if (aStdFds[j] == fd)
1106 {
1107 fd = -1;
1108 break;
1109 }
1110 if (fd >= 0)
1111 close(fd);
1112 }
1113 }
1114
1115 /*
1116 * Finally, execute the requested program.
1117 */
1118 rc = execve(pszExec, (char * const *)papszArgs, (char * const *)papszEnv);
1119 if (errno == ENOEXEC)
1120 {
1121 /* This can happen when trying to start a shell script without the magic #!/bin/sh */
1122 RTAssertMsg2Weak("Cannot execute this binary format!\n");
1123 }
1124 else
1125 RTAssertMsg2Weak("execve returns %d errno=%d\n", rc, errno);
1126 RTAssertReleasePanic();
1127 if (fFlags & RTPROC_FLAGS_DETACHED)
1128 _Exit(127);
1129 else
1130 exit(127);
1131 }
1132#ifdef RT_OS_SOLARIS
1133 if (!(fFlags & RTPROC_FLAGS_SAME_CONTRACT))
1134 rtSolarisContractPostForkParent(templateFd, pid);
1135#endif /* RT_OS_SOLARIS */
1136 if (pid > 0)
1137 {
1138 /* For a detached process this happens in the temp process, so
1139 * it's not worth doing anything as this process must exit. */
1140 if (fFlags & RTPROC_FLAGS_DETACHED)
1141 _Exit(0);
1142 if (phProcess)
1143 *phProcess = pid;
1144 return rtProcPosixCreateReturn(VINF_SUCCESS, hEnvToUse, hEnv);
1145 }
1146 /* For a detached process this happens in the temp process, so
1147 * it's not worth doing anything as this process must exit. */
1148 if (fFlags & RTPROC_FLAGS_DETACHED)
1149 _Exit(124);
1150 return rtProcPosixCreateReturn(RTErrConvertFromErrno(errno), hEnvToUse, hEnv);
1151 }
1152
1153 return rtProcPosixCreateReturn(VERR_NOT_IMPLEMENTED, hEnvToUse, hEnv);
1154}
1155
1156
1157RTR3DECL(int) RTProcDaemonizeUsingFork(bool fNoChDir, bool fNoClose, const char *pszPidfile)
1158{
1159 /*
1160 * Fork the child process in a new session and quit the parent.
1161 *
1162 * - fork once and create a new session (setsid). This will detach us
1163 * from the controlling tty meaning that we won't receive the SIGHUP
1164 * (or any other signal) sent to that session.
1165 * - The SIGHUP signal is ignored because the session/parent may throw
1166 * us one before we get to the setsid.
1167 * - When the parent exit(0) we will become an orphan and re-parented to
1168 * the init process.
1169 * - Because of the sometimes unexpected semantics of assigning the
1170 * controlling tty automagically when a session leader first opens a tty,
1171 * we will fork() once more to get rid of the session leadership role.
1172 */
1173
1174 /* We start off by opening the pidfile, so that we can fail straight away
1175 * if it already exists. */
1176 int fdPidfile = -1;
1177 if (pszPidfile != NULL)
1178 {
1179 /* @note the exclusive create is not guaranteed on all file
1180 * systems (e.g. NFSv2) */
1181 if ((fdPidfile = open(pszPidfile, O_RDWR | O_CREAT | O_EXCL, 0644)) == -1)
1182 return RTErrConvertFromErrno(errno);
1183 }
1184
1185 /* Ignore SIGHUP straight away. */
1186 struct sigaction OldSigAct;
1187 struct sigaction SigAct;
1188 memset(&SigAct, 0, sizeof(SigAct));
1189 SigAct.sa_handler = SIG_IGN;
1190 int rcSigAct = sigaction(SIGHUP, &SigAct, &OldSigAct);
1191
1192 /* First fork, to become independent process. */
1193 pid_t pid = fork();
1194 if (pid == -1)
1195 {
1196 if (fdPidfile != -1)
1197 close(fdPidfile);
1198 return RTErrConvertFromErrno(errno);
1199 }
1200 if (pid != 0)
1201 {
1202 /* Parent exits, no longer necessary. The child gets reparented
1203 * to the init process. */
1204 exit(0);
1205 }
1206
1207 /* Create new session, fix up the standard file descriptors and the
1208 * current working directory. */
1209 /** @todo r=klaus the webservice uses this function and assumes that the
1210 * contract id of the daemon is the same as that of the original process.
1211 * Whenever this code is changed this must still remain possible. */
1212 pid_t newpgid = setsid();
1213 int SavedErrno = errno;
1214 if (rcSigAct != -1)
1215 sigaction(SIGHUP, &OldSigAct, NULL);
1216 if (newpgid == -1)
1217 {
1218 if (fdPidfile != -1)
1219 close(fdPidfile);
1220 return RTErrConvertFromErrno(SavedErrno);
1221 }
1222
1223 if (!fNoClose)
1224 {
1225 /* Open stdin(0), stdout(1) and stderr(2) as /dev/null. */
1226 int fd = open("/dev/null", O_RDWR);
1227 if (fd == -1) /* paranoia */
1228 {
1229 close(STDIN_FILENO);
1230 close(STDOUT_FILENO);
1231 close(STDERR_FILENO);
1232 fd = open("/dev/null", O_RDWR);
1233 }
1234 if (fd != -1)
1235 {
1236 dup2(fd, STDIN_FILENO);
1237 dup2(fd, STDOUT_FILENO);
1238 dup2(fd, STDERR_FILENO);
1239 if (fd > 2)
1240 close(fd);
1241 }
1242 }
1243
1244 if (!fNoChDir)
1245 {
1246 int rcIgnored = chdir("/");
1247 NOREF(rcIgnored);
1248 }
1249
1250 /* Second fork to lose session leader status. */
1251 pid = fork();
1252 if (pid == -1)
1253 {
1254 if (fdPidfile != -1)
1255 close(fdPidfile);
1256 return RTErrConvertFromErrno(errno);
1257 }
1258
1259 if (pid != 0)
1260 {
1261 /* Write the pid file, this is done in the parent, before exiting. */
1262 if (fdPidfile != -1)
1263 {
1264 char szBuf[256];
1265 size_t cbPid = RTStrPrintf(szBuf, sizeof(szBuf), "%d\n", pid);
1266 ssize_t cbIgnored = write(fdPidfile, szBuf, cbPid); NOREF(cbIgnored);
1267 close(fdPidfile);
1268 }
1269 exit(0);
1270 }
1271
1272 if (fdPidfile != -1)
1273 close(fdPidfile);
1274
1275 return VINF_SUCCESS;
1276}
1277
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