VirtualBox

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

Last change on this file since 55998 was 55530, checked in by vboxsync, 10 years ago

RTProcCreate,RTProcCreateEx: Added RTPROC_FLAGS_UNQUOTED_ARGS for allowing working around for windows programs with their own brilliant ideas about command line parsing.

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