VirtualBox

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

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

Runtime/posix/process-creation: handle supplemental groups properly

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