VirtualBox

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

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

process-creation-posix.cpp: posix_spawnattr_setsigmask doesn't take effect without setting the POSIX_SPAWN_SETSIGMASK flag. Check status codes. kLIBC actually implements posix_spawnattr_setflags and ignores flags it doesn't implement IIRC, so lose the #ifndef RT_OS_OS2 bits.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 25.1 KB
Line 
1/* $Id: process-creation-posix.cpp 44555 2013-02-05 23:56:23Z 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 /* Indicate that process group and signal mask are to be changed,
470 and that the child should use default signal actions. */
471 rc = posix_spawnattr_setflags(&Attr, POSIX_SPAWN_SETPGROUP | POSIX_SPAWN_SETSIGMASK | POSIX_SPAWN_SETSIGDEF);
472 Assert(rc == 0);
473
474 /* The child starts in its own process group. */
475 if (!rc)
476 {
477 rc = posix_spawnattr_setpgroup(&Attr, 0 /* pg == child pid */);
478 Assert(rc == 0);
479 }
480
481 /* Unmask all signals. */
482 if (!rc)
483 {
484 sigset_t SigMask;
485 sigemptyset(&SigMask);
486 rc = posix_spawnattr_setsigmask(&Attr, &SigMask); Assert(rc == 0);
487 }
488
489 /* File changes. */
490 posix_spawn_file_actions_t FileActions;
491 posix_spawn_file_actions_t *pFileActions = NULL;
492 if ((aStdFds[0] != -1 || aStdFds[1] != -1 || aStdFds[2] != -1) && !rc)
493 {
494 rc = posix_spawn_file_actions_init(&FileActions);
495 if (!rc)
496 {
497 pFileActions = &FileActions;
498 for (int i = 0; i < 3; i++)
499 {
500 int fd = aStdFds[i];
501 if (fd == -2)
502 rc = posix_spawn_file_actions_addclose(&FileActions, i);
503 else if (fd >= 0 && fd != i)
504 {
505 rc = posix_spawn_file_actions_adddup2(&FileActions, fd, i);
506 if (!rc)
507 {
508 for (int j = i + 1; j < 3; j++)
509 if (aStdFds[j] == fd)
510 {
511 fd = -1;
512 break;
513 }
514 if (fd >= 0)
515 rc = posix_spawn_file_actions_addclose(&FileActions, fd);
516 }
517 }
518 if (rc)
519 break;
520 }
521 }
522 }
523
524 if (!rc)
525 rc = posix_spawn(&pid, pszExec, pFileActions, &Attr, (char * const *)papszArgs,
526 (char * const *)papszEnv);
527
528 /* cleanup */
529 int rc2 = posix_spawnattr_destroy(&Attr); Assert(rc2 == 0); NOREF(rc2);
530 if (pFileActions)
531 {
532 rc2 = posix_spawn_file_actions_destroy(pFileActions);
533 Assert(rc2 == 0);
534 }
535
536 /* return on success.*/
537 if (!rc)
538 {
539 /* For a detached process this happens in the temp process, so
540 * it's not worth doing anything as this process must exit. */
541 if (fFlags & RTPROC_FLAGS_DETACHED)
542 _Exit(0);
543 if (phProcess)
544 *phProcess = pid;
545 return VINF_SUCCESS;
546 }
547 }
548 /* For a detached process this happens in the temp process, so
549 * it's not worth doing anything as this process must exit. */
550 if (fFlags & RTPROC_FLAGS_DETACHED)
551 _Exit(124);
552 }
553 else
554#endif
555 {
556#ifdef RT_OS_SOLARIS
557 int templateFd = rtSolarisContractPreFork();
558 if (templateFd == -1)
559 return VERR_OPEN_FAILED;
560#endif /* RT_OS_SOLARIS */
561 pid = fork();
562 if (!pid)
563 {
564#ifdef RT_OS_SOLARIS
565 rtSolarisContractPostForkChild(templateFd);
566#endif /* RT_OS_SOLARIS */
567 if (!(fFlags & RTPROC_FLAGS_DETACHED))
568 setpgid(0, 0); /* see comment above */
569
570 /*
571 * Change group and user if requested.
572 */
573#if 1 /** @todo This needs more work, see suplib/hardening. */
574 if (gid != ~(gid_t)0)
575 {
576 if (setgid(gid))
577 {
578 if (fFlags & RTPROC_FLAGS_DETACHED)
579 _Exit(126);
580 else
581 exit(126);
582 }
583 }
584
585 if (uid != ~(uid_t)0)
586 {
587 if (setuid(uid))
588 {
589 if (fFlags & RTPROC_FLAGS_DETACHED)
590 _Exit(126);
591 else
592 exit(126);
593 }
594 }
595#endif
596
597 /*
598 * Unset the signal mask.
599 */
600 sigset_t SigMask;
601 sigemptyset(&SigMask);
602 rc = sigprocmask(SIG_SETMASK, &SigMask, NULL);
603 Assert(rc == 0);
604
605 /*
606 * Apply changes to the standard file descriptor and stuff.
607 */
608 for (int i = 0; i < 3; i++)
609 {
610 int fd = aStdFds[i];
611 if (fd == -2)
612 close(i);
613 else if (fd >= 0)
614 {
615 int rc2 = dup2(fd, i);
616 if (rc2 != i)
617 {
618 if (fFlags & RTPROC_FLAGS_DETACHED)
619 _Exit(125);
620 else
621 exit(125);
622 }
623 for (int j = i + 1; j < 3; j++)
624 if (aStdFds[j] == fd)
625 {
626 fd = -1;
627 break;
628 }
629 if (fd >= 0)
630 close(fd);
631 }
632 }
633
634 /*
635 * Finally, execute the requested program.
636 */
637 rc = execve(pszExec, (char * const *)papszArgs, (char * const *)papszEnv);
638 if (errno == ENOEXEC)
639 {
640 /* This can happen when trying to start a shell script without the magic #!/bin/sh */
641 RTAssertMsg2Weak("Cannot execute this binary format!\n");
642 }
643 else
644 RTAssertMsg2Weak("execve returns %d errno=%d\n", rc, errno);
645 RTAssertReleasePanic();
646 if (fFlags & RTPROC_FLAGS_DETACHED)
647 _Exit(127);
648 else
649 exit(127);
650 }
651#ifdef RT_OS_SOLARIS
652 rtSolarisContractPostForkParent(templateFd, pid);
653#endif /* RT_OS_SOLARIS */
654 if (pid > 0)
655 {
656 /* For a detached process this happens in the temp process, so
657 * it's not worth doing anything as this process must exit. */
658 if (fFlags & RTPROC_FLAGS_DETACHED)
659 _Exit(0);
660 if (phProcess)
661 *phProcess = pid;
662 return VINF_SUCCESS;
663 }
664 /* For a detached process this happens in the temp process, so
665 * it's not worth doing anything as this process must exit. */
666 if (fFlags & RTPROC_FLAGS_DETACHED)
667 _Exit(124);
668 return RTErrConvertFromErrno(errno);
669 }
670
671 return VERR_NOT_IMPLEMENTED;
672}
673
674
675RTR3DECL(int) RTProcDaemonizeUsingFork(bool fNoChDir, bool fNoClose, const char *pszPidfile)
676{
677 /*
678 * Fork the child process in a new session and quit the parent.
679 *
680 * - fork once and create a new session (setsid). This will detach us
681 * from the controlling tty meaning that we won't receive the SIGHUP
682 * (or any other signal) sent to that session.
683 * - The SIGHUP signal is ignored because the session/parent may throw
684 * us one before we get to the setsid.
685 * - When the parent exit(0) we will become an orphan and re-parented to
686 * the init process.
687 * - Because of the sometimes unexpected semantics of assigning the
688 * controlling tty automagically when a session leader first opens a tty,
689 * we will fork() once more to get rid of the session leadership role.
690 */
691
692 /* We start off by opening the pidfile, so that we can fail straight away
693 * if it already exists. */
694 int fdPidfile = -1;
695 if (pszPidfile != NULL)
696 {
697 /* @note the exclusive create is not guaranteed on all file
698 * systems (e.g. NFSv2) */
699 if ((fdPidfile = open(pszPidfile, O_RDWR | O_CREAT | O_EXCL, 0644)) == -1)
700 return RTErrConvertFromErrno(errno);
701 }
702
703 /* Ignore SIGHUP straight away. */
704 struct sigaction OldSigAct;
705 struct sigaction SigAct;
706 memset(&SigAct, 0, sizeof(SigAct));
707 SigAct.sa_handler = SIG_IGN;
708 int rcSigAct = sigaction(SIGHUP, &SigAct, &OldSigAct);
709
710 /* First fork, to become independent process. */
711 pid_t pid = fork();
712 if (pid == -1)
713 {
714 if (fdPidfile != -1)
715 close(fdPidfile);
716 return RTErrConvertFromErrno(errno);
717 }
718 if (pid != 0)
719 {
720 /* Parent exits, no longer necessary. The child gets reparented
721 * to the init process. */
722 exit(0);
723 }
724
725 /* Create new session, fix up the standard file descriptors and the
726 * current working directory. */
727 /** @todo r=klaus the webservice uses this function and assumes that the
728 * contract id of the daemon is the same as that of the original process.
729 * Whenever this code is changed this must still remain possible. */
730 pid_t newpgid = setsid();
731 int SavedErrno = errno;
732 if (rcSigAct != -1)
733 sigaction(SIGHUP, &OldSigAct, NULL);
734 if (newpgid == -1)
735 {
736 if (fdPidfile != -1)
737 close(fdPidfile);
738 return RTErrConvertFromErrno(SavedErrno);
739 }
740
741 if (!fNoClose)
742 {
743 /* Open stdin(0), stdout(1) and stderr(2) as /dev/null. */
744 int fd = open("/dev/null", O_RDWR);
745 if (fd == -1) /* paranoia */
746 {
747 close(STDIN_FILENO);
748 close(STDOUT_FILENO);
749 close(STDERR_FILENO);
750 fd = open("/dev/null", O_RDWR);
751 }
752 if (fd != -1)
753 {
754 dup2(fd, STDIN_FILENO);
755 dup2(fd, STDOUT_FILENO);
756 dup2(fd, STDERR_FILENO);
757 if (fd > 2)
758 close(fd);
759 }
760 }
761
762 if (!fNoChDir)
763 {
764 int rcIgnored = chdir("/");
765 NOREF(rcIgnored);
766 }
767
768 /* Second fork to lose session leader status. */
769 pid = fork();
770 if (pid == -1)
771 {
772 if (fdPidfile != -1)
773 close(fdPidfile);
774 return RTErrConvertFromErrno(errno);
775 }
776
777 if (pid != 0)
778 {
779 /* Write the pid file, this is done in the parent, before exiting. */
780 if (fdPidfile != -1)
781 {
782 char szBuf[256];
783 size_t cbPid = RTStrPrintf(szBuf, sizeof(szBuf), "%d\n", pid);
784 ssize_t cbIgnored = write(fdPidfile, szBuf, cbPid); NOREF(cbIgnored);
785 close(fdPidfile);
786 }
787 exit(0);
788 }
789
790 if (fdPidfile != -1)
791 close(fdPidfile);
792
793 return VINF_SUCCESS;
794}
795
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