VirtualBox

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

最後變更 在這個檔案從44528是 44528,由 vboxsync 提交於 12 年 前

header (C) fixes

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette