VirtualBox

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

Last change on this file since 27743 was 27743, checked in by vboxsync, 15 years ago

IPRT,*: Renamed RTProcDaemonize to RTProcDaemonizeUsingFork. Added a new RTPRocDaemonize that is portable. RTProcCreate* got a new flag RTPROC_FLAGS_DETACHED, while RTPROC_FLAGS_DAEMONIZE got renamed to RTPROC_FLAGS_DAEMONIZE_DEPRECATED.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 16.5 KB
Line 
1/* $Id: process-posix.cpp 27743 2010-03-26 14:50:55Z vboxsync $ */
2/** @file
3 * IPRT - Process, POSIX.
4 */
5
6/*
7 * Copyright (C) 2006-2010 Sun Microsystems, Inc.
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.215389.xyz. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 *
26 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
27 * Clara, CA 95054 USA or visit http://www.sun.com if you need
28 * additional information or have any questions.
29 */
30
31
32
33/*******************************************************************************
34* Header Files *
35*******************************************************************************/
36#define LOG_GROUP RTLOGGROUP_PROCESS
37#include <unistd.h>
38#include <stdlib.h>
39#include <errno.h>
40#include <sys/types.h>
41#include <sys/stat.h>
42#include <sys/wait.h>
43#include <fcntl.h>
44#include <signal.h>
45#if defined(RT_OS_LINUX) || defined(RT_OS_OS2)
46# define HAVE_POSIX_SPAWN 1
47#endif
48#ifdef HAVE_POSIX_SPAWN
49# include <spawn.h>
50#endif
51#ifdef RT_OS_DARWIN
52# include <mach-o/dyld.h>
53#endif
54
55#include <iprt/process.h>
56#include "internal/iprt.h"
57
58#include <iprt/assert.h>
59#include <iprt/env.h>
60#include <iprt/err.h>
61#include <iprt/file.h>
62#include <iprt/pipe.h>
63#include <iprt/socket.h>
64#include <iprt/string.h>
65#include "internal/process.h"
66
67
68
69RTR3DECL(int) RTProcCreate(const char *pszExec, const char * const *papszArgs, RTENV Env, unsigned fFlags, PRTPROCESS pProcess)
70{
71 return RTProcCreateEx(pszExec, papszArgs, Env, fFlags,
72 NULL, NULL, NULL, /* standard handles */
73 NULL /*pszAsUser*/, NULL /* pszPassword*/,
74 pProcess);
75}
76
77
78RTR3DECL(int) RTProcCreateEx(const char *pszExec, const char * const *papszArgs, RTENV hEnv, uint32_t fFlags,
79 PCRTHANDLE phStdIn, PCRTHANDLE phStdOut, PCRTHANDLE phStdErr, const char *pszAsUser,
80 const char *pszPassword, PRTPROCESS phProcess)
81{
82 int rc;
83
84 /*
85 * Input validation
86 */
87 AssertPtrReturn(pszExec, VERR_INVALID_POINTER);
88 AssertReturn(*pszExec, VERR_INVALID_PARAMETER);
89 AssertReturn(!(fFlags & ~(RTPROC_FLAGS_DAEMONIZE_DEPRECATED | RTPROC_FLAGS_DETACHED)), VERR_INVALID_PARAMETER);
90 AssertReturn(!(fFlags & RTPROC_FLAGS_DETACHED) || !phProcess, VERR_INVALID_PARAMETER);
91 AssertReturn(hEnv != NIL_RTENV, VERR_INVALID_PARAMETER);
92 const char * const *papszEnv = RTEnvGetExecEnvP(hEnv);
93 AssertPtrReturn(papszEnv, VERR_INVALID_HANDLE);
94 AssertPtrReturn(papszArgs, VERR_INVALID_PARAMETER);
95 /** @todo search the PATH (add flag for this). */
96 AssertPtrNullReturn(pszAsUser, VERR_INVALID_POINTER);
97 AssertReturn(!pszAsUser || *pszAsUser, VERR_INVALID_PARAMETER);
98 AssertReturn(!pszPassword || pszAsUser, VERR_INVALID_PARAMETER);
99 AssertPtrNullReturn(pszPassword, VERR_INVALID_POINTER);
100
101 /*
102 * Get the file descriptors for the handles we've been passed.
103 */
104 PCRTHANDLE paHandles[3] = { phStdIn, phStdOut, phStdErr };
105 int aStdFds[3] = { -1, -1, -1 };
106 for (int i = 0; i < 3; i++)
107 {
108 if (paHandles[i])
109 {
110 AssertPtrReturn(paHandles[i], VERR_INVALID_POINTER);
111 switch (paHandles[i]->enmType)
112 {
113 case RTHANDLETYPE_FILE:
114 aStdFds[i] = paHandles[i]->u.hFile != NIL_RTFILE
115 ? (int)RTFileToNative(paHandles[i]->u.hFile)
116 : -2 /* close it */;
117 break;
118
119 case RTHANDLETYPE_PIPE:
120 aStdFds[i] = paHandles[i]->u.hPipe != NIL_RTPIPE
121 ? (int)RTPipeToNative(paHandles[i]->u.hPipe)
122 : -2 /* close it */;
123 break;
124
125 case RTHANDLETYPE_SOCKET:
126 aStdFds[i] = paHandles[i]->u.hSocket != NIL_RTSOCKET
127 ? (int)RTSocketToNative(paHandles[i]->u.hSocket)
128 : -2 /* close it */;
129 break;
130
131 default:
132 AssertMsgFailedReturn(("%d: %d\n", i, paHandles[i]->enmType), VERR_INVALID_PARAMETER);
133 }
134 /** @todo check the close-on-execness of these handles? */
135 }
136 }
137
138 for (int i = 0; i < 3; i++)
139 if (aStdFds[i] == i)
140 aStdFds[i] = -1;
141
142 for (int i = 0; i < 3; i++)
143 AssertMsgReturn(aStdFds[i] < 0 || aStdFds[i] > i,
144 ("%i := %i not possible because we're lazy\n", i, aStdFds[i]),
145 VERR_NOT_SUPPORTED);
146
147 /*
148 * Resolve the user id if specified.
149 */
150 uid_t uid = ~(uid_t)0;
151 gid_t gid = ~(gid_t)0;
152 if (pszAsUser)
153 {
154 AssertMsgFailed(("Implement get uid by name lookup\n"));
155 return VERR_NOT_IMPLEMENTED;
156 }
157
158 /*
159 * Check for execute access to the file.
160 */
161 if (access(pszExec, X_OK))
162 {
163 rc = RTErrConvertFromErrno(errno);
164 AssertMsgFailed(("'%s' %Rrc!\n", pszExec, rc));
165 return rc;
166 }
167
168 /*
169 * Spawn the child.
170 *
171 * HACK ALERT! Put the process into a new process group with pgid = pid
172 * to make sure it differs from that of the parent process to ensure that
173 * the IPRT waipit call doesn't race anyone (read XPCOM) doing group wide
174 * waits.
175 */
176 pid_t pid = -1;
177#ifdef HAVE_POSIX_SPAWN
178 if ( !(fFlags & (RTPROC_FLAGS_DAEMONIZE_DEPRECATED | RTPROC_FLAGS_DETACHED))
179 && uid == ~(uid_t)0
180 && gid == ~(gid_t)0
181 )
182 {
183 /* Spawn attributes. */
184 posix_spawnattr_t Attr;
185 rc = posix_spawnattr_init(&Attr);
186 if (!rc)
187 {
188# ifndef RT_OS_OS2 /* We don't need this on OS/2 and I don't recall if it's actually implemented. */
189 rc = posix_spawnattr_setflags(&Attr, POSIX_SPAWN_SETPGROUP);
190 Assert(rc == 0);
191 if (!rc)
192 {
193 rc = posix_spawnattr_setpgroup(&Attr, 0 /* pg == child pid */);
194 Assert(rc == 0);
195 }
196# endif
197
198 /* File changes. */
199 posix_spawn_file_actions_t FileActions;
200 posix_spawn_file_actions_t *pFileActions = NULL;
201 if (aStdFds[0] != -1 || aStdFds[1] != -1 || aStdFds[2] != -1)
202 {
203 rc = posix_spawn_file_actions_init(&FileActions);
204 if (!rc)
205 {
206 pFileActions = &FileActions;
207 for (int i = 0; i < 3; i++)
208 {
209 int fd = aStdFds[i];
210 if (fd == -2)
211 rc = posix_spawn_file_actions_addclose(&FileActions, i);
212 else if (fd >= 0 && fd != i)
213 {
214 rc = posix_spawn_file_actions_adddup2(&FileActions, fd, i);
215 if (!rc)
216 {
217 for (int j = i + 1; j < 3; j++)
218 if (aStdFds[j] == fd)
219 {
220 fd = -1;
221 break;
222 }
223 if (fd >= 0)
224 rc = posix_spawn_file_actions_addclose(&FileActions, fd);
225 }
226 }
227 if (rc)
228 break;
229 }
230 }
231 }
232
233 if (!rc)
234 rc = posix_spawn(&pid, pszExec, pFileActions, &Attr, (char * const *)papszArgs,
235 (char * const *)papszEnv);
236
237 /* cleanup */
238 int rc2 = posix_spawnattr_destroy(&Attr); Assert(rc2 == 0); NOREF(rc2);
239 if (pFileActions)
240 {
241 rc2 = posix_spawn_file_actions_destroy(pFileActions);
242 Assert(rc2 == 0);
243 }
244
245 /* return on success.*/
246 if (!rc)
247 {
248 if (phProcess)
249 *phProcess = pid;
250 return VINF_SUCCESS;
251 }
252 }
253 }
254 else
255#endif
256 {
257 pid = fork();
258 if (!pid)
259 {
260 setpgid(0, 0); /* see comment above */
261
262 /*
263 * Change group and user if requested.
264 */
265#if 1 /** @todo This needs more work, see suplib/hardening. */
266 if (gid != ~(gid_t)0)
267 {
268 if (setgid(gid))
269 exit(126);
270 }
271
272 if (uid != ~(uid_t)0)
273 {
274 if (setuid(uid))
275 exit(126);
276 }
277#endif
278
279 /*
280 * Apply changes to the standard file descriptor and stuff.
281 */
282 for (int i = 0; i < 3; i++)
283 {
284 int fd = aStdFds[i];
285 if (fd == -2)
286 close(i);
287 else if (fd >= 0)
288 {
289 int rc2 = dup2(fd, i);
290 if (rc2 != i)
291 exit(125);
292 for (int j = i + 1; j < 3; j++)
293 if (aStdFds[j] == fd)
294 {
295 fd = -1;
296 break;
297 }
298 if (fd >= 0)
299 close(fd);
300 }
301 }
302
303 /*
304 * Daemonize the process if requested.
305 */
306 if (fFlags & (RTPROC_FLAGS_DAEMONIZE_DEPRECATED | RTPROC_FLAGS_DETACHED))
307 {
308 rc = RTProcDaemonizeUsingFork(true /*fNoChDir*/,
309 !(fFlags & RTPROC_FLAGS_DAEMONIZE_DEPRECATED) /*fNoClose*/,
310 NULL /* pszPidFile */);
311 if (RT_FAILURE(rc))
312 {
313 /* parent */
314 AssertReleaseMsgFailed(("RTProcDaemonize returns %Rrc errno=%d\n", rc, errno));
315 exit(127);
316 }
317 /* daemonized child */
318 }
319
320 /*
321 * Finally, execute the requested program.
322 */
323 rc = execve(pszExec, (char * const *)papszArgs, (char * const *)papszEnv);
324 AssertReleaseMsgFailed(("execve returns %d errno=%d\n", rc, errno));
325 exit(127);
326 }
327 if (pid > 0)
328 {
329 if (phProcess)
330 *phProcess = pid;
331 return VINF_SUCCESS;
332 }
333 rc = errno;
334 }
335
336
337 return VERR_NOT_IMPLEMENTED;
338}
339
340
341RTR3DECL(int) RTProcWait(RTPROCESS Process, unsigned fFlags, PRTPROCSTATUS pProcStatus)
342{
343 int rc;
344 do rc = RTProcWaitNoResume(Process, fFlags, pProcStatus);
345 while (rc == VERR_INTERRUPTED);
346 return rc;
347}
348
349RTR3DECL(int) RTProcWaitNoResume(RTPROCESS Process, unsigned fFlags, PRTPROCSTATUS pProcStatus)
350{
351 /*
352 * Validate input.
353 */
354 if (Process <= 0)
355 {
356 AssertMsgFailed(("Invalid Process=%d\n", Process));
357 return VERR_INVALID_PARAMETER;
358 }
359 if (fFlags & ~(RTPROCWAIT_FLAGS_NOBLOCK | RTPROCWAIT_FLAGS_BLOCK))
360 {
361 AssertMsgFailed(("Invalid flags %#x\n", fFlags));
362 return VERR_INVALID_PARAMETER;
363 }
364
365 /*
366 * Performe the wait.
367 */
368 int iStatus = 0;
369 int rc = waitpid(Process, &iStatus, fFlags & RTPROCWAIT_FLAGS_NOBLOCK ? WNOHANG : 0);
370 if (rc > 0)
371 {
372 /*
373 * Fill in the status structure.
374 */
375 if (pProcStatus)
376 {
377 if (WIFEXITED(iStatus))
378 {
379 pProcStatus->enmReason = RTPROCEXITREASON_NORMAL;
380 pProcStatus->iStatus = WEXITSTATUS(iStatus);
381 }
382 else if (WIFSIGNALED(iStatus))
383 {
384 pProcStatus->enmReason = RTPROCEXITREASON_SIGNAL;
385 pProcStatus->iStatus = WTERMSIG(iStatus);
386 }
387 else
388 {
389 Assert(!WIFSTOPPED(iStatus));
390 pProcStatus->enmReason = RTPROCEXITREASON_ABEND;
391 pProcStatus->iStatus = iStatus;
392 }
393 }
394 return VINF_SUCCESS;
395 }
396
397 /*
398 * Child running?
399 */
400 if (!rc)
401 {
402 Assert(fFlags & RTPROCWAIT_FLAGS_NOBLOCK);
403 return VERR_PROCESS_RUNNING;
404 }
405
406 /*
407 * Figure out which error to return.
408 */
409 int iErr = errno;
410 if (iErr == ECHILD)
411 return VERR_PROCESS_NOT_FOUND;
412 return RTErrConvertFromErrno(iErr);
413}
414
415
416RTR3DECL(int) RTProcTerminate(RTPROCESS Process)
417{
418 if (!kill(Process, SIGKILL))
419 return VINF_SUCCESS;
420 return RTErrConvertFromErrno(errno);
421}
422
423
424RTR3DECL(uint64_t) RTProcGetAffinityMask()
425{
426 // @todo
427 return 1;
428}
429
430
431RTR3DECL(int) RTProcDaemonizeUsingFork(bool fNoChDir, bool fNoClose, const char *pszPidfile)
432{
433 /*
434 * Fork the child process in a new session and quit the parent.
435 *
436 * - fork once and create a new session (setsid). This will detach us
437 * from the controlling tty meaning that we won't receive the SIGHUP
438 * (or any other signal) sent to that session.
439 * - The SIGHUP signal is ignored because the session/parent may throw
440 * us one before we get to the setsid.
441 * - When the parent exit(0) we will become an orphan and re-parented to
442 * the init process.
443 * - Because of the sometimes unexpected semantics of assigning the
444 * controlling tty automagically when a session leader first opens a tty,
445 * we will fork() once more to get rid of the session leadership role.
446 */
447
448 /* We start off by opening the pidfile, so that we can fail straight away
449 * if it already exists. */
450 int fdPidfile = -1;
451 if (pszPidfile != NULL)
452 {
453 /* @note the exclusive create is not guaranteed on all file
454 * systems (e.g. NFSv2) */
455 if ((fdPidfile = open(pszPidfile, O_RDWR | O_CREAT | O_EXCL, 0644)) == -1)
456 return RTErrConvertFromErrno(errno);
457 }
458
459 /* Ignore SIGHUP straight away. */
460 struct sigaction OldSigAct;
461 struct sigaction SigAct;
462 memset(&SigAct, 0, sizeof(SigAct));
463 SigAct.sa_handler = SIG_IGN;
464 int rcSigAct = sigaction(SIGHUP, &SigAct, &OldSigAct);
465
466 /* First fork, to become independent process. */
467 pid_t pid = fork();
468 if (pid == -1)
469 return RTErrConvertFromErrno(errno);
470 if (pid != 0)
471 {
472 /* Parent exits, no longer necessary. The child gets reparented
473 * to the init process. */
474 exit(0);
475 }
476
477 /* Create new session, fix up the standard file descriptors and the
478 * current working directory. */
479 pid_t newpgid = setsid();
480 int SavedErrno = errno;
481 if (rcSigAct != -1)
482 sigaction(SIGHUP, &OldSigAct, NULL);
483 if (newpgid == -1)
484 return RTErrConvertFromErrno(SavedErrno);
485
486 if (!fNoClose)
487 {
488 /* Open stdin(0), stdout(1) and stderr(2) as /dev/null. */
489 int fd = open("/dev/null", O_RDWR);
490 if (fd == -1) /* paranoia */
491 {
492 close(STDIN_FILENO);
493 close(STDOUT_FILENO);
494 close(STDERR_FILENO);
495 fd = open("/dev/null", O_RDWR);
496 }
497 if (fd != -1)
498 {
499 dup2(fd, STDIN_FILENO);
500 dup2(fd, STDOUT_FILENO);
501 dup2(fd, STDERR_FILENO);
502 if (fd > 2)
503 close(fd);
504 }
505 }
506
507 if (!fNoChDir)
508 {
509 int rcChdir = chdir("/");
510 }
511
512 /* Second fork to lose session leader status. */
513 pid = fork();
514 if (pid == -1)
515 return RTErrConvertFromErrno(errno);
516
517 if (pid != 0)
518 {
519 /* Write the pid file, this is done in the parent, before exiting. */
520 if (fdPidfile != -1)
521 {
522 char szBuf[256];
523 size_t cbPid = RTStrPrintf(szBuf, sizeof(szBuf), "%d\n", pid);
524 int rcWrite = write(fdPidfile, szBuf, cbPid);
525 close(fdPidfile);
526 }
527 exit(0);
528 }
529
530 return VINF_SUCCESS;
531}
532
Note: See TracBrowser for help on using the repository browser.

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