VirtualBox

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

Last change on this file since 44549 was 44549, checked in by vboxsync, 12 years ago

Runtime/posix/RTProcCreateEx: unblock all signals for the new child process

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

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