VirtualBox

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

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

build fix

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