VirtualBox

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

Last change on this file since 39562 was 39083, checked in by vboxsync, 13 years ago

IPRT: -Wunused-parameter.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 22.9 KB
Line 
1/* $Id: process-creation-posix.cpp 39083 2011-10-22 00:28:46Z vboxsync $ */
2/** @file
3 * IPRT - Process Creation, POSIX.
4 */
5
6/*
7 * Copyright (C) 2006-2010 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 <unistd.h>
33#include <stdlib.h>
34#include <errno.h>
35#include <sys/types.h>
36#include <sys/stat.h>
37#include <sys/wait.h>
38#include <fcntl.h>
39#include <signal.h>
40#if defined(RT_OS_LINUX) || defined(RT_OS_SOLARIS)
41# include <crypt.h>
42# include <pwd.h>
43# include <shadow.h>
44#endif
45#if defined(RT_OS_LINUX) || defined(RT_OS_OS2)
46/* While Solaris has posix_spawn() of course we don't want to use it as
47 * we need to have the child in a different process contract, no matter
48 * whether it is started detached or not. */
49# define HAVE_POSIX_SPAWN 1
50#endif
51#ifdef HAVE_POSIX_SPAWN
52# include <spawn.h>
53#endif
54#ifdef RT_OS_DARWIN
55# include <mach-o/dyld.h>
56#endif
57#ifdef RT_OS_SOLARIS
58# include <limits.h>
59# include <sys/ctfs.h>
60# include <sys/contract/process.h>
61# include <libcontract.h>
62#endif
63
64#include <iprt/process.h>
65#include "internal/iprt.h"
66
67#include <iprt/assert.h>
68#include <iprt/env.h>
69#include <iprt/err.h>
70#include <iprt/file.h>
71#include <iprt/pipe.h>
72#include <iprt/socket.h>
73#include <iprt/string.h>
74#include <iprt/mem.h>
75#include "internal/process.h"
76
77
78/**
79 * Check the credentials and return the gid/uid of user.
80 *
81 * @param pszUser username
82 * @param pszPasswd password
83 * @param gid where to store the GID of the user
84 * @param uid where to store the UID of the user
85 * @returns IPRT status code
86 */
87static int rtCheckCredentials(const char *pszUser, const char *pszPasswd, gid_t *pGid, uid_t *pUid)
88{
89#if defined(RT_OS_LINUX)
90 struct passwd *pw;
91
92 pw = getpwnam(pszUser);
93 if (!pw)
94 return VERR_PERMISSION_DENIED;
95
96 if (!pszPasswd)
97 pszPasswd = "";
98
99 struct spwd *spwd;
100 /* works only if /etc/shadow is accessible */
101 spwd = getspnam(pszUser);
102 if (spwd)
103 pw->pw_passwd = spwd->sp_pwdp;
104
105 /* be reentrant */
106 struct crypt_data *data = (struct crypt_data*)RTMemTmpAllocZ(sizeof(*data));
107 char *pszEncPasswd = crypt_r(pszPasswd, pw->pw_passwd, data);
108 int fCorrect = !strcmp(pszEncPasswd, pw->pw_passwd);
109 RTMemTmpFree(data);
110 if (!fCorrect)
111 return VERR_PERMISSION_DENIED;
112
113 *pGid = pw->pw_gid;
114 *pUid = 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 *pGid = ppw->pw_gid;
138 *pUid = ppw->pw_uid;
139 return VINF_SUCCESS;
140
141#else
142 NOREF(pszUser); NOREF(pszPasswd); NOREF(pGid); NOREF(pUid);
143 return VERR_PERMISSION_DENIED;
144#endif
145}
146
147
148#ifdef RT_OS_SOLARIS
149/** @todo the error reporting of the Solaris process contract code could be
150 * a lot better, but essentially it is not meant to run into errors after
151 * the debugging phase. */
152static int rtSolarisContractPreFork(void)
153{
154 int templateFd = open64(CTFS_ROOT "/process/template", O_RDWR);
155 if (templateFd < 0)
156 return -1;
157
158 /* Set template parameters and event sets. */
159 if (ct_pr_tmpl_set_param(templateFd, CT_PR_PGRPONLY))
160 {
161 close(templateFd);
162 return -1;
163 }
164 if (ct_pr_tmpl_set_fatal(templateFd, CT_PR_EV_HWERR))
165 {
166 close(templateFd);
167 return -1;
168 }
169 if (ct_tmpl_set_critical(templateFd, 0))
170 {
171 close(templateFd);
172 return -1;
173 }
174 if (ct_tmpl_set_informative(templateFd, CT_PR_EV_HWERR))
175 {
176 close(templateFd);
177 return -1;
178 }
179
180 /* Make this the active template for the process. */
181 if (ct_tmpl_activate(templateFd))
182 {
183 close(templateFd);
184 return -1;
185 }
186
187 return templateFd;
188}
189
190static void rtSolarisContractPostForkChild(int templateFd)
191{
192 if (templateFd == -1)
193 return;
194
195 /* Clear the active template. */
196 ct_tmpl_clear(templateFd);
197 close(templateFd);
198}
199
200static void rtSolarisContractPostForkParent(int templateFd, pid_t pid)
201{
202 if (templateFd == -1)
203 return;
204
205 /* Clear the active template. */
206 int cleared = ct_tmpl_clear(templateFd);
207 close(templateFd);
208
209 /* If the clearing failed or the fork failed there's nothing more to do. */
210 if (cleared || pid <= 0)
211 return;
212
213 /* Look up the contract which was created by this thread. */
214 int statFd = open64(CTFS_ROOT "/process/latest", O_RDONLY);
215 if (statFd == -1)
216 return;
217 ct_stathdl_t statHdl;
218 if (ct_status_read(statFd, CTD_COMMON, &statHdl))
219 {
220 close(statFd);
221 return;
222 }
223 ctid_t ctId = ct_status_get_id(statHdl);
224 ct_status_free(statHdl);
225 close(statFd);
226 if (ctId < 0)
227 return;
228
229 /* Abandon this contract we just created. */
230 char ctlPath[PATH_MAX];
231 size_t len = snprintf(ctlPath, sizeof(ctlPath),
232 CTFS_ROOT "/process/%d/ctl", ctId);
233 if (len >= sizeof(ctlPath))
234 return;
235 int ctlFd = open64(ctlPath, O_WRONLY);
236 if (statFd == -1)
237 return;
238 if (ct_ctl_abandon(ctlFd) < 0)
239 {
240 close(ctlFd);
241 return;
242 }
243 close(ctlFd);
244}
245
246#endif /* RT_OS_SOLARIS */
247
248
249RTR3DECL(int) RTProcCreate(const char *pszExec, const char * const *papszArgs, RTENV Env, unsigned fFlags, PRTPROCESS pProcess)
250{
251 return RTProcCreateEx(pszExec, papszArgs, Env, fFlags,
252 NULL, NULL, NULL, /* standard handles */
253 NULL /*pszAsUser*/, NULL /* pszPassword*/,
254 pProcess);
255}
256
257
258RTR3DECL(int) RTProcCreateEx(const char *pszExec, const char * const *papszArgs, RTENV hEnv, uint32_t fFlags,
259 PCRTHANDLE phStdIn, PCRTHANDLE phStdOut, PCRTHANDLE phStdErr, const char *pszAsUser,
260 const char *pszPassword, PRTPROCESS phProcess)
261{
262 int rc;
263
264 /*
265 * Input validation
266 */
267 AssertPtrReturn(pszExec, VERR_INVALID_POINTER);
268 AssertReturn(*pszExec, VERR_INVALID_PARAMETER);
269 AssertReturn(!(fFlags & ~(RTPROC_FLAGS_DETACHED | RTPROC_FLAGS_HIDDEN | RTPROC_FLAGS_SERVICE | RTPROC_FLAGS_SAME_CONTRACT | RTPROC_FLAGS_NO_PROFILE)), VERR_INVALID_PARAMETER);
270 AssertReturn(!(fFlags & RTPROC_FLAGS_DETACHED) || !phProcess, VERR_INVALID_PARAMETER);
271 AssertReturn(hEnv != NIL_RTENV, VERR_INVALID_PARAMETER);
272 const char * const *papszEnv = RTEnvGetExecEnvP(hEnv);
273 AssertPtrReturn(papszEnv, VERR_INVALID_HANDLE);
274 AssertPtrReturn(papszArgs, VERR_INVALID_PARAMETER);
275 /** @todo search the PATH (add flag for this). */
276 AssertPtrNullReturn(pszAsUser, VERR_INVALID_POINTER);
277 AssertReturn(!pszAsUser || *pszAsUser, VERR_INVALID_PARAMETER);
278 AssertReturn(!pszPassword || pszAsUser, VERR_INVALID_PARAMETER);
279 AssertPtrNullReturn(pszPassword, VERR_INVALID_POINTER);
280
281 /*
282 * Get the file descriptors for the handles we've been passed.
283 */
284 PCRTHANDLE paHandles[3] = { phStdIn, phStdOut, phStdErr };
285 int aStdFds[3] = { -1, -1, -1 };
286 for (int i = 0; i < 3; i++)
287 {
288 if (paHandles[i])
289 {
290 AssertPtrReturn(paHandles[i], VERR_INVALID_POINTER);
291 switch (paHandles[i]->enmType)
292 {
293 case RTHANDLETYPE_FILE:
294 aStdFds[i] = paHandles[i]->u.hFile != NIL_RTFILE
295 ? (int)RTFileToNative(paHandles[i]->u.hFile)
296 : -2 /* close it */;
297 break;
298
299 case RTHANDLETYPE_PIPE:
300 aStdFds[i] = paHandles[i]->u.hPipe != NIL_RTPIPE
301 ? (int)RTPipeToNative(paHandles[i]->u.hPipe)
302 : -2 /* close it */;
303 break;
304
305 case RTHANDLETYPE_SOCKET:
306 aStdFds[i] = paHandles[i]->u.hSocket != NIL_RTSOCKET
307 ? (int)RTSocketToNative(paHandles[i]->u.hSocket)
308 : -2 /* close it */;
309 break;
310
311 default:
312 AssertMsgFailedReturn(("%d: %d\n", i, paHandles[i]->enmType), VERR_INVALID_PARAMETER);
313 }
314 /** @todo check the close-on-execness of these handles? */
315 }
316 }
317
318 for (int i = 0; i < 3; i++)
319 if (aStdFds[i] == i)
320 aStdFds[i] = -1;
321
322 for (int i = 0; i < 3; i++)
323 AssertMsgReturn(aStdFds[i] < 0 || aStdFds[i] > i,
324 ("%i := %i not possible because we're lazy\n", i, aStdFds[i]),
325 VERR_NOT_SUPPORTED);
326
327 /*
328 * Resolve the user id if specified.
329 */
330 uid_t uid = ~(uid_t)0;
331 gid_t gid = ~(gid_t)0;
332 if (pszAsUser)
333 {
334 rc = rtCheckCredentials(pszAsUser, pszPassword, &gid, &uid);
335 if (RT_FAILURE(rc))
336 return rc;
337 }
338
339 /*
340 * Check for execute access to the file.
341 */
342 if (access(pszExec, X_OK))
343 return RTErrConvertFromErrno(errno);
344
345 pid_t pid = -1;
346
347 /*
348 * Take care of detaching the process.
349 *
350 * HACK ALERT! Put the process into a new process group with pgid = pid
351 * to make sure it differs from that of the parent process to ensure that
352 * the IPRT waitpid call doesn't race anyone (read XPCOM) doing group wide
353 * waits. setsid() includes the setpgid() functionality.
354 * 2010-10-11 XPCOM no longer waits for anything, but it cannot hurt.
355 */
356#ifndef RT_OS_OS2
357 if (fFlags & RTPROC_FLAGS_DETACHED)
358 {
359# ifdef RT_OS_SOLARIS
360 int templateFd = -1;
361 if (!(fFlags & RTPROC_FLAGS_SAME_CONTRACT))
362 {
363 templateFd = rtSolarisContractPreFork();
364 if (templateFd == -1)
365 return VERR_OPEN_FAILED;
366 }
367# endif /* RT_OS_SOLARIS */
368 pid = fork();
369 if (!pid)
370 {
371# ifdef RT_OS_SOLARIS
372 if (!(fFlags & RTPROC_FLAGS_SAME_CONTRACT))
373 rtSolarisContractPostForkChild(templateFd);
374# endif /* RT_OS_SOLARIS */
375 setsid(); /* see comment above */
376
377 pid = -1;
378 /* Child falls through to the actual spawn code below. */
379 }
380 else
381 {
382#ifdef RT_OS_SOLARIS
383 if (!(fFlags & RTPROC_FLAGS_SAME_CONTRACT))
384 rtSolarisContractPostForkParent(templateFd, pid);
385#endif /* RT_OS_SOLARIS */
386 if (pid > 0)
387 {
388 /* Must wait for the temporary process to avoid a zombie. */
389 int status = 0;
390 pid_t pidChild = 0;
391
392 /* Restart if we get interrupted. */
393 do
394 {
395 pidChild = waitpid(pid, &status, 0);
396 } while ( pidChild == -1
397 && errno == EINTR);
398
399 /* Assume that something wasn't found. No detailed info. */
400 if (status)
401 return VERR_PROCESS_NOT_FOUND;
402 if (phProcess)
403 *phProcess = 0;
404 return VINF_SUCCESS;
405 }
406 return RTErrConvertFromErrno(errno);
407 }
408 }
409#endif
410
411 /*
412 * Spawn the child.
413 *
414 * Any spawn code MUST not execute any atexit functions if it is for a
415 * detached process. It would lead to running the atexit functions which
416 * make only sense for the parent. libORBit e.g. gets confused by multiple
417 * execution. Remember, there was only a fork() so far, and until exec()
418 * is successfully run there is nothing which would prevent doing anything
419 * silly with the (duplicated) file descriptors.
420 */
421#ifdef HAVE_POSIX_SPAWN
422 /** @todo OS/2: implement DETACHED (BACKGROUND stuff), see VbglR3Daemonize. */
423 if ( uid == ~(uid_t)0
424 && gid == ~(gid_t)0)
425 {
426 /* Spawn attributes. */
427 posix_spawnattr_t Attr;
428 rc = posix_spawnattr_init(&Attr);
429 if (!rc)
430 {
431# ifndef RT_OS_OS2 /* We don't need this on OS/2 and I don't recall if it's actually implemented. */
432 rc = posix_spawnattr_setflags(&Attr, POSIX_SPAWN_SETPGROUP);
433 Assert(rc == 0);
434 if (!rc)
435 {
436 rc = posix_spawnattr_setpgroup(&Attr, 0 /* pg == child pid */);
437 Assert(rc == 0);
438 }
439# endif
440
441 /* File changes. */
442 posix_spawn_file_actions_t FileActions;
443 posix_spawn_file_actions_t *pFileActions = NULL;
444 if (aStdFds[0] != -1 || aStdFds[1] != -1 || aStdFds[2] != -1)
445 {
446 rc = posix_spawn_file_actions_init(&FileActions);
447 if (!rc)
448 {
449 pFileActions = &FileActions;
450 for (int i = 0; i < 3; i++)
451 {
452 int fd = aStdFds[i];
453 if (fd == -2)
454 rc = posix_spawn_file_actions_addclose(&FileActions, i);
455 else if (fd >= 0 && fd != i)
456 {
457 rc = posix_spawn_file_actions_adddup2(&FileActions, fd, i);
458 if (!rc)
459 {
460 for (int j = i + 1; j < 3; j++)
461 if (aStdFds[j] == fd)
462 {
463 fd = -1;
464 break;
465 }
466 if (fd >= 0)
467 rc = posix_spawn_file_actions_addclose(&FileActions, fd);
468 }
469 }
470 if (rc)
471 break;
472 }
473 }
474 }
475
476 if (!rc)
477 rc = posix_spawn(&pid, pszExec, pFileActions, &Attr, (char * const *)papszArgs,
478 (char * const *)papszEnv);
479
480 /* cleanup */
481 int rc2 = posix_spawnattr_destroy(&Attr); Assert(rc2 == 0); NOREF(rc2);
482 if (pFileActions)
483 {
484 rc2 = posix_spawn_file_actions_destroy(pFileActions);
485 Assert(rc2 == 0);
486 }
487
488 /* return on success.*/
489 if (!rc)
490 {
491 /* For a detached process this happens in the temp process, so
492 * it's not worth doing anything as this process must exit. */
493 if (fFlags & RTPROC_FLAGS_DETACHED)
494 _Exit(0);
495 if (phProcess)
496 *phProcess = pid;
497 return VINF_SUCCESS;
498 }
499 }
500 /* For a detached process this happens in the temp process, so
501 * it's not worth doing anything as this process must exit. */
502 if (fFlags & RTPROC_FLAGS_DETACHED)
503 _Exit(124);
504 }
505 else
506#endif
507 {
508#ifdef RT_OS_SOLARIS
509 int templateFd = rtSolarisContractPreFork();
510 if (templateFd == -1)
511 return VERR_OPEN_FAILED;
512#endif /* RT_OS_SOLARIS */
513 pid = fork();
514 if (!pid)
515 {
516#ifdef RT_OS_SOLARIS
517 rtSolarisContractPostForkChild(templateFd);
518#endif /* RT_OS_SOLARIS */
519 if (!(fFlags & RTPROC_FLAGS_DETACHED))
520 setpgid(0, 0); /* see comment above */
521
522 /*
523 * Change group and user if requested.
524 */
525#if 1 /** @todo This needs more work, see suplib/hardening. */
526 if (gid != ~(gid_t)0)
527 {
528 if (setgid(gid))
529 {
530 if (fFlags & RTPROC_FLAGS_DETACHED)
531 _Exit(126);
532 else
533 exit(126);
534 }
535 }
536
537 if (uid != ~(uid_t)0)
538 {
539 if (setuid(uid))
540 {
541 if (fFlags & RTPROC_FLAGS_DETACHED)
542 _Exit(126);
543 else
544 exit(126);
545 }
546 }
547#endif
548
549 /*
550 * Apply changes to the standard file descriptor and stuff.
551 */
552 for (int i = 0; i < 3; i++)
553 {
554 int fd = aStdFds[i];
555 if (fd == -2)
556 close(i);
557 else if (fd >= 0)
558 {
559 int rc2 = dup2(fd, i);
560 if (rc2 != i)
561 {
562 if (fFlags & RTPROC_FLAGS_DETACHED)
563 _Exit(125);
564 else
565 exit(125);
566 }
567 for (int j = i + 1; j < 3; j++)
568 if (aStdFds[j] == fd)
569 {
570 fd = -1;
571 break;
572 }
573 if (fd >= 0)
574 close(fd);
575 }
576 }
577
578 /*
579 * Finally, execute the requested program.
580 */
581 rc = execve(pszExec, (char * const *)papszArgs, (char * const *)papszEnv);
582 if (errno == ENOEXEC)
583 {
584 /* This can happen when trying to start a shell script without the magic #!/bin/sh */
585 RTAssertMsg2Weak("Cannot execute this binary format!\n");
586 }
587 else
588 RTAssertMsg2Weak("execve returns %d errno=%d\n", rc, errno);
589 RTAssertReleasePanic();
590 if (fFlags & RTPROC_FLAGS_DETACHED)
591 _Exit(127);
592 else
593 exit(127);
594 }
595#ifdef RT_OS_SOLARIS
596 rtSolarisContractPostForkParent(templateFd, pid);
597#endif /* RT_OS_SOLARIS */
598 if (pid > 0)
599 {
600 /* For a detached process this happens in the temp process, so
601 * it's not worth doing anything as this process must exit. */
602 if (fFlags & RTPROC_FLAGS_DETACHED)
603 _Exit(0);
604 if (phProcess)
605 *phProcess = pid;
606 return VINF_SUCCESS;
607 }
608 /* For a detached process this happens in the temp process, so
609 * it's not worth doing anything as this process must exit. */
610 if (fFlags & RTPROC_FLAGS_DETACHED)
611 _Exit(124);
612 return RTErrConvertFromErrno(errno);
613 }
614
615 return VERR_NOT_IMPLEMENTED;
616}
617
618
619RTR3DECL(int) RTProcDaemonizeUsingFork(bool fNoChDir, bool fNoClose, const char *pszPidfile)
620{
621 /*
622 * Fork the child process in a new session and quit the parent.
623 *
624 * - fork once and create a new session (setsid). This will detach us
625 * from the controlling tty meaning that we won't receive the SIGHUP
626 * (or any other signal) sent to that session.
627 * - The SIGHUP signal is ignored because the session/parent may throw
628 * us one before we get to the setsid.
629 * - When the parent exit(0) we will become an orphan and re-parented to
630 * the init process.
631 * - Because of the sometimes unexpected semantics of assigning the
632 * controlling tty automagically when a session leader first opens a tty,
633 * we will fork() once more to get rid of the session leadership role.
634 */
635
636 /* We start off by opening the pidfile, so that we can fail straight away
637 * if it already exists. */
638 int fdPidfile = -1;
639 if (pszPidfile != NULL)
640 {
641 /* @note the exclusive create is not guaranteed on all file
642 * systems (e.g. NFSv2) */
643 if ((fdPidfile = open(pszPidfile, O_RDWR | O_CREAT | O_EXCL, 0644)) == -1)
644 return RTErrConvertFromErrno(errno);
645 }
646
647 /* Ignore SIGHUP straight away. */
648 struct sigaction OldSigAct;
649 struct sigaction SigAct;
650 memset(&SigAct, 0, sizeof(SigAct));
651 SigAct.sa_handler = SIG_IGN;
652 int rcSigAct = sigaction(SIGHUP, &SigAct, &OldSigAct);
653
654 /* First fork, to become independent process. */
655 pid_t pid = fork();
656 if (pid == -1)
657 return RTErrConvertFromErrno(errno);
658 if (pid != 0)
659 {
660 /* Parent exits, no longer necessary. The child gets reparented
661 * to the init process. */
662 exit(0);
663 }
664
665 /* Create new session, fix up the standard file descriptors and the
666 * current working directory. */
667 /** @todo r=klaus the webservice uses this function and assumes that the
668 * contract id of the daemon is the same as that of the original process.
669 * Whenever this code is changed this must still remain possible. */
670 pid_t newpgid = setsid();
671 int SavedErrno = errno;
672 if (rcSigAct != -1)
673 sigaction(SIGHUP, &OldSigAct, NULL);
674 if (newpgid == -1)
675 return RTErrConvertFromErrno(SavedErrno);
676
677 if (!fNoClose)
678 {
679 /* Open stdin(0), stdout(1) and stderr(2) as /dev/null. */
680 int fd = open("/dev/null", O_RDWR);
681 if (fd == -1) /* paranoia */
682 {
683 close(STDIN_FILENO);
684 close(STDOUT_FILENO);
685 close(STDERR_FILENO);
686 fd = open("/dev/null", O_RDWR);
687 }
688 if (fd != -1)
689 {
690 dup2(fd, STDIN_FILENO);
691 dup2(fd, STDOUT_FILENO);
692 dup2(fd, STDERR_FILENO);
693 if (fd > 2)
694 close(fd);
695 }
696 }
697
698 if (!fNoChDir)
699 {
700 int rcIgnored = chdir("/");
701 NOREF(rcIgnored);
702 }
703
704 /* Second fork to lose session leader status. */
705 pid = fork();
706 if (pid == -1)
707 return RTErrConvertFromErrno(errno);
708
709 if (pid != 0)
710 {
711 /* Write the pid file, this is done in the parent, before exiting. */
712 if (fdPidfile != -1)
713 {
714 char szBuf[256];
715 size_t cbPid = RTStrPrintf(szBuf, sizeof(szBuf), "%d\n", pid);
716 ssize_t cbIgnored = write(fdPidfile, szBuf, cbPid); NOREF(cbIgnored);
717 close(fdPidfile);
718 }
719 exit(0);
720 }
721
722 return VINF_SUCCESS;
723}
724
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