VirtualBox

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

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

Use posix_spawn on mac as well, its (mostly) there since 10.5.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 25.2 KB
Line 
1/* $Id: process-creation-posix.cpp 44556 2013-02-06 00:06:34Z 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 = rtSolarisContractPreFork();
567 if (templateFd == -1)
568 return VERR_OPEN_FAILED;
569#endif /* RT_OS_SOLARIS */
570 pid = fork();
571 if (!pid)
572 {
573#ifdef RT_OS_SOLARIS
574 rtSolarisContractPostForkChild(templateFd);
575#endif /* RT_OS_SOLARIS */
576 if (!(fFlags & RTPROC_FLAGS_DETACHED))
577 setpgid(0, 0); /* see comment above */
578
579 /*
580 * Change group and user if requested.
581 */
582#if 1 /** @todo This needs more work, see suplib/hardening. */
583 if (gid != ~(gid_t)0)
584 {
585 if (setgid(gid))
586 {
587 if (fFlags & RTPROC_FLAGS_DETACHED)
588 _Exit(126);
589 else
590 exit(126);
591 }
592 }
593
594 if (uid != ~(uid_t)0)
595 {
596 if (setuid(uid))
597 {
598 if (fFlags & RTPROC_FLAGS_DETACHED)
599 _Exit(126);
600 else
601 exit(126);
602 }
603 }
604#endif
605
606 /*
607 * Unset the signal mask.
608 */
609 sigset_t SigMask;
610 sigemptyset(&SigMask);
611 rc = sigprocmask(SIG_SETMASK, &SigMask, NULL);
612 Assert(rc == 0);
613
614 /*
615 * Apply changes to the standard file descriptor and stuff.
616 */
617 for (int i = 0; i < 3; i++)
618 {
619 int fd = aStdFds[i];
620 if (fd == -2)
621 close(i);
622 else if (fd >= 0)
623 {
624 int rc2 = dup2(fd, i);
625 if (rc2 != i)
626 {
627 if (fFlags & RTPROC_FLAGS_DETACHED)
628 _Exit(125);
629 else
630 exit(125);
631 }
632 for (int j = i + 1; j < 3; j++)
633 if (aStdFds[j] == fd)
634 {
635 fd = -1;
636 break;
637 }
638 if (fd >= 0)
639 close(fd);
640 }
641 }
642
643 /*
644 * Finally, execute the requested program.
645 */
646 rc = execve(pszExec, (char * const *)papszArgs, (char * const *)papszEnv);
647 if (errno == ENOEXEC)
648 {
649 /* This can happen when trying to start a shell script without the magic #!/bin/sh */
650 RTAssertMsg2Weak("Cannot execute this binary format!\n");
651 }
652 else
653 RTAssertMsg2Weak("execve returns %d errno=%d\n", rc, errno);
654 RTAssertReleasePanic();
655 if (fFlags & RTPROC_FLAGS_DETACHED)
656 _Exit(127);
657 else
658 exit(127);
659 }
660#ifdef RT_OS_SOLARIS
661 rtSolarisContractPostForkParent(templateFd, pid);
662#endif /* RT_OS_SOLARIS */
663 if (pid > 0)
664 {
665 /* For a detached process this happens in the temp process, so
666 * it's not worth doing anything as this process must exit. */
667 if (fFlags & RTPROC_FLAGS_DETACHED)
668 _Exit(0);
669 if (phProcess)
670 *phProcess = pid;
671 return VINF_SUCCESS;
672 }
673 /* For a detached process this happens in the temp process, so
674 * it's not worth doing anything as this process must exit. */
675 if (fFlags & RTPROC_FLAGS_DETACHED)
676 _Exit(124);
677 return RTErrConvertFromErrno(errno);
678 }
679
680 return VERR_NOT_IMPLEMENTED;
681}
682
683
684RTR3DECL(int) RTProcDaemonizeUsingFork(bool fNoChDir, bool fNoClose, const char *pszPidfile)
685{
686 /*
687 * Fork the child process in a new session and quit the parent.
688 *
689 * - fork once and create a new session (setsid). This will detach us
690 * from the controlling tty meaning that we won't receive the SIGHUP
691 * (or any other signal) sent to that session.
692 * - The SIGHUP signal is ignored because the session/parent may throw
693 * us one before we get to the setsid.
694 * - When the parent exit(0) we will become an orphan and re-parented to
695 * the init process.
696 * - Because of the sometimes unexpected semantics of assigning the
697 * controlling tty automagically when a session leader first opens a tty,
698 * we will fork() once more to get rid of the session leadership role.
699 */
700
701 /* We start off by opening the pidfile, so that we can fail straight away
702 * if it already exists. */
703 int fdPidfile = -1;
704 if (pszPidfile != NULL)
705 {
706 /* @note the exclusive create is not guaranteed on all file
707 * systems (e.g. NFSv2) */
708 if ((fdPidfile = open(pszPidfile, O_RDWR | O_CREAT | O_EXCL, 0644)) == -1)
709 return RTErrConvertFromErrno(errno);
710 }
711
712 /* Ignore SIGHUP straight away. */
713 struct sigaction OldSigAct;
714 struct sigaction SigAct;
715 memset(&SigAct, 0, sizeof(SigAct));
716 SigAct.sa_handler = SIG_IGN;
717 int rcSigAct = sigaction(SIGHUP, &SigAct, &OldSigAct);
718
719 /* First fork, to become independent process. */
720 pid_t pid = fork();
721 if (pid == -1)
722 {
723 if (fdPidfile != -1)
724 close(fdPidfile);
725 return RTErrConvertFromErrno(errno);
726 }
727 if (pid != 0)
728 {
729 /* Parent exits, no longer necessary. The child gets reparented
730 * to the init process. */
731 exit(0);
732 }
733
734 /* Create new session, fix up the standard file descriptors and the
735 * current working directory. */
736 /** @todo r=klaus the webservice uses this function and assumes that the
737 * contract id of the daemon is the same as that of the original process.
738 * Whenever this code is changed this must still remain possible. */
739 pid_t newpgid = setsid();
740 int SavedErrno = errno;
741 if (rcSigAct != -1)
742 sigaction(SIGHUP, &OldSigAct, NULL);
743 if (newpgid == -1)
744 {
745 if (fdPidfile != -1)
746 close(fdPidfile);
747 return RTErrConvertFromErrno(SavedErrno);
748 }
749
750 if (!fNoClose)
751 {
752 /* Open stdin(0), stdout(1) and stderr(2) as /dev/null. */
753 int fd = open("/dev/null", O_RDWR);
754 if (fd == -1) /* paranoia */
755 {
756 close(STDIN_FILENO);
757 close(STDOUT_FILENO);
758 close(STDERR_FILENO);
759 fd = open("/dev/null", O_RDWR);
760 }
761 if (fd != -1)
762 {
763 dup2(fd, STDIN_FILENO);
764 dup2(fd, STDOUT_FILENO);
765 dup2(fd, STDERR_FILENO);
766 if (fd > 2)
767 close(fd);
768 }
769 }
770
771 if (!fNoChDir)
772 {
773 int rcIgnored = chdir("/");
774 NOREF(rcIgnored);
775 }
776
777 /* Second fork to lose session leader status. */
778 pid = fork();
779 if (pid == -1)
780 {
781 if (fdPidfile != -1)
782 close(fdPidfile);
783 return RTErrConvertFromErrno(errno);
784 }
785
786 if (pid != 0)
787 {
788 /* Write the pid file, this is done in the parent, before exiting. */
789 if (fdPidfile != -1)
790 {
791 char szBuf[256];
792 size_t cbPid = RTStrPrintf(szBuf, sizeof(szBuf), "%d\n", pid);
793 ssize_t cbIgnored = write(fdPidfile, szBuf, cbPid); NOREF(cbIgnored);
794 close(fdPidfile);
795 }
796 exit(0);
797 }
798
799 if (fdPidfile != -1)
800 close(fdPidfile);
801
802 return VINF_SUCCESS;
803}
804
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