VirtualBox

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

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

Implemented RTProcCreateEx for posix. The new features hasn't been tested yet (no testcase).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 19.2 KB
Line 
1/* $Id: process-posix.cpp 26802 2010-02-25 16:25:41Z vboxsync $ */
2/** @file
3 * IPRT - Process, POSIX.
4 */
5
6/*
7 * Copyright (C) 2006-2007 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/string.h>
64#include "internal/process.h"
65
66
67
68RTR3DECL(int) RTProcCreate(const char *pszExec, const char * const *papszArgs, RTENV Env, unsigned fFlags, PRTPROCESS pProcess)
69{
70#if 0
71 int rc;
72
73 /*
74 * Validate input.
75 */
76 AssertPtrReturn(pszExec, VERR_INVALID_POINTER);
77 AssertReturn(*pszExec, VERR_INVALID_PARAMETER);
78 AssertReturn(!(fFlags & ~RTPROC_FLAGS_DAEMONIZE), VERR_INVALID_PARAMETER);
79 AssertReturn(Env != NIL_RTENV, VERR_INVALID_PARAMETER);
80 const char * const *papszEnv = RTEnvGetExecEnvP(Env);
81 AssertPtrReturn(papszEnv, VERR_INVALID_HANDLE);
82 AssertPtrReturn(papszArgs, VERR_INVALID_PARAMETER);
83 /* later: path searching. */
84
85
86 /*
87 * Check for execute access to the file.
88 */
89 if (access(pszExec, X_OK))
90 {
91 rc = RTErrConvertFromErrno(errno);
92 AssertMsgFailed(("'%s' %Rrc!\n", pszExec, rc));
93 return rc;
94 }
95
96#if 0
97 /*
98 * Squeeze gdb --args in front of what's being spawned.
99 */
100 unsigned cArgs = 0;
101 while (papszArgs[cArgs])
102 cArgs++;
103 cArgs += 3;
104 const char **papszArgsTmp = (const char **)alloca(cArgs * sizeof(char *));
105 papszArgsTmp[0] = "/usr/bin/gdb";
106 papszArgsTmp[1] = "--args";
107 papszArgsTmp[2] = pszExec;
108 for (unsigned i = 1; papszArgs[i]; i++)
109 papszArgsTmp[i + 2] = papszArgs[i];
110 papszArgsTmp[cArgs - 1] = NULL;
111 pszExec = papszArgsTmp[0];
112 papszArgs = papszArgsTmp;
113#endif
114
115 /*
116 * Spawn the child.
117 *
118 * HACK ALERT! Put the process into a new process group with pgid = pid
119 * to make sure it differs from that of the parent process to ensure that
120 * the IPRT waipit call doesn't race anyone (read XPCOM) doing group wide
121 * waits.
122 */
123 pid_t pid;
124#ifdef HAVE_POSIX_SPAWN
125 if (!(fFlags & RTPROC_FLAGS_DAEMONIZE))
126 {
127 posix_spawnattr_t Attr;
128
129 rc = posix_spawnattr_init(&Attr);
130 if (!rc)
131 {
132# ifndef RT_OS_OS2 /* We don't need this on OS/2 and I don't recall if it's actually implemented. */
133 rc = posix_spawnattr_setflags(&Attr, POSIX_SPAWN_SETPGROUP);
134 Assert(rc == 0);
135 if (!rc)
136 {
137 rc = posix_spawnattr_setpgroup(&Attr, 0 /* pg == child pid */);
138 Assert(rc == 0);
139 }
140# endif
141 if (!rc)
142 {
143 rc = posix_spawn(&pid, pszExec, NULL, &Attr, (char * const *)papszArgs,
144 (char * const *)papszEnv);
145 if (!rc)
146 {
147 int rc2 = posix_spawnattr_destroy(&Attr); Assert(rc2 == 0); NOREF(rc2);
148 if (pProcess)
149 *pProcess = pid;
150 return VINF_SUCCESS;
151 }
152 }
153 int rc2 = posix_spawnattr_destroy(&Attr); Assert(rc2 == 0); NOREF(rc2);
154 }
155 }
156 else
157#endif
158 {
159 pid = fork();
160 if (!pid)
161 {
162 setpgid(0, 0); /* see comment above */
163
164 if (fFlags & RTPROC_FLAGS_DAEMONIZE)
165 {
166 rc = RTProcDaemonize(true /* fNoChDir */, false /* fNoClose */, NULL /* pszPidFile */);
167 AssertReleaseMsgFailed(("RTProcDaemonize returns %Rrc errno=%d\n", rc, errno));
168 exit(127);
169 }
170 rc = execve(pszExec, (char * const *)papszArgs, (char * const *)papszEnv);
171 AssertReleaseMsgFailed(("execve returns %d errno=%d\n", rc, errno));
172 exit(127);
173 }
174 if (pid > 0)
175 {
176 if (pProcess)
177 *pProcess = pid;
178 return VINF_SUCCESS;
179 }
180 rc = errno;
181 }
182
183 /* failure, errno value in rc. */
184 AssertMsgFailed(("spawn/exec failed rc=%d\n", rc)); /* this migth be annoying... */
185 return RTErrConvertFromErrno(rc);
186#else
187 return RTProcCreateEx(pszExec, papszArgs, Env, fFlags,
188 NULL, NULL, NULL, /* standard handles */
189 NULL /*pszAsUser*/,
190 pProcess);
191#endif
192}
193
194
195RTR3DECL(int) RTProcCreateEx(const char *pszExec, const char * const *papszArgs, RTENV hEnv, uint32_t fFlags,
196 PCRTHANDLE phStdIn, PCRTHANDLE phStdOut, PCRTHANDLE phStdErr, const char *pszAsUser,
197 PRTPROCESS phProcess)
198{
199 int rc;
200
201 /*
202 * Input validation
203 */
204 AssertPtrReturn(pszExec, VERR_INVALID_POINTER);
205 AssertReturn(*pszExec, VERR_INVALID_PARAMETER);
206 AssertReturn(!(fFlags & ~RTPROC_FLAGS_DAEMONIZE), VERR_INVALID_PARAMETER);
207 AssertReturn(hEnv != NIL_RTENV, VERR_INVALID_PARAMETER);
208 const char * const *papszEnv = RTEnvGetExecEnvP(hEnv);
209 AssertPtrReturn(papszEnv, VERR_INVALID_HANDLE);
210 AssertPtrReturn(papszArgs, VERR_INVALID_PARAMETER);
211 /** @todo search the PATH (add flag for this). */
212 AssertPtrNullReturn(pszAsUser, VERR_INVALID_POINTER);
213
214 /*
215 * Get the file descriptors for the handles we've been passed.
216 */
217 PCRTHANDLE paHandles[3] = { phStdIn, phStdOut, phStdErr };
218 int aStdFds[3] = { -1, -1, -1 };
219 for (int i = 0; i < 3; i++)
220 {
221 if (paHandles[i])
222 {
223 AssertPtrReturn(paHandles[i], VERR_INVALID_POINTER);
224 switch (paHandles[i]->enmType)
225 {
226 case RTHANDLETYPE_FILE:
227 aStdFds[i] = paHandles[i]->u.hFile != NIL_RTFILE
228 ? (int)RTFileToNative(paHandles[i]->u.hFile)
229 : -2 /* close it */;
230 break;
231
232 case RTHANDLETYPE_PIPE:
233 aStdFds[i] = paHandles[i]->u.hPipe != NIL_RTPIPE
234 ? (int)RTPipeToNative(paHandles[i]->u.hPipe)
235 : -2 /* close it */;
236 break;
237
238 case RTHANDLETYPE_SOCKET:
239 aStdFds[i] = paHandles[i]->u.hSocket != NIL_RTSOCKET
240 ? (int)paHandles[i]->u.hSocket //RTPipeToNative(paHandles[i]->u.hPipe)
241 : -2 /* close it */;
242 break;
243
244 default:
245 AssertMsgFailedReturn(("%d: %d\n", i, paHandles[i]->enmType), VERR_INVALID_PARAMETER);
246 }
247 /** @todo check the close-on-execness of these handles? */
248 }
249 }
250
251 for (int i = 0; i < 3; i++)
252 if (aStdFds[i] == i)
253 aStdFds[i] = -1;
254
255 for (int i = 0; i < 3; i++)
256 AssertMsgReturn(aStdFds[i] < 0 || aStdFds[i] > i,
257 ("%i := %i not possible because we're lazy\n", i, aStdFds[i]),
258 VERR_NOT_SUPPORTED);
259
260 /*
261 * Resolve the user id if specified.
262 */
263 uid_t uid = ~(uid_t)0;
264 gid_t gid = ~(gid_t)0;
265 if (pszAsUser)
266 {
267 AssertMsgFailed(("Implement get uid by name lookup\n"));
268 return VERR_NOT_IMPLEMENTED;
269 }
270
271 /*
272 * Check for execute access to the file.
273 */
274 if (access(pszExec, X_OK))
275 {
276 rc = RTErrConvertFromErrno(errno);
277 AssertMsgFailed(("'%s' %Rrc!\n", pszExec, rc));
278 return rc;
279 }
280
281 /*
282 * Spawn the child.
283 *
284 * HACK ALERT! Put the process into a new process group with pgid = pid
285 * to make sure it differs from that of the parent process to ensure that
286 * the IPRT waipit call doesn't race anyone (read XPCOM) doing group wide
287 * waits.
288 */
289 pid_t pid = -1;
290#ifdef HAVE_POSIX_SPAWN
291 if ( !(fFlags & RTPROC_FLAGS_DAEMONIZE)
292 && uid == ~(uid_t)0
293 && gid == ~(gid_t)0
294 )
295 {
296 /* Spawn attributes. */
297 posix_spawnattr_t Attr;
298 rc = posix_spawnattr_init(&Attr);
299 if (!rc)
300 {
301# ifndef RT_OS_OS2 /* We don't need this on OS/2 and I don't recall if it's actually implemented. */
302 rc = posix_spawnattr_setflags(&Attr, POSIX_SPAWN_SETPGROUP);
303 Assert(rc == 0);
304 if (!rc)
305 {
306 rc = posix_spawnattr_setpgroup(&Attr, 0 /* pg == child pid */);
307 Assert(rc == 0);
308 }
309# endif
310
311 /* File changes. */
312 posix_spawn_file_actions_t FileActions;
313 posix_spawn_file_actions_t *pFileActions = NULL;
314 if (aStdFds[0] != -1 || aStdFds[1] != -1 || aStdFds[2] != -1)
315 {
316 rc = posix_spawn_file_actions_init(&FileActions);
317 if (!rc)
318 {
319 pFileActions = &FileActions;
320 for (int i = 0; i < 3; i++)
321 {
322 if (aStdFds[i] == -2)
323 rc = posix_spawn_file_actions_addclose(&FileActions, i);
324 else if (aStdFds[i] >= 0 && aStdFds[i] != i)
325 {
326 rc = posix_spawn_file_actions_adddup2(&FileActions, aStdFds[i], i);
327 if (!rc)
328 rc = posix_spawn_file_actions_addclose(&FileActions, aStdFds[i]);
329 }
330 if (rc)
331 break;
332 }
333 }
334 }
335
336 if (!rc)
337 rc = posix_spawn(&pid, pszExec, NULL, &Attr, (char * const *)papszArgs,
338 (char * const *)papszEnv);
339
340 /* cleanup */
341 int rc2 = posix_spawnattr_destroy(&Attr); Assert(rc2 == 0); NOREF(rc2);
342 if (pFileActions)
343 {
344 rc2 = posix_spawn_file_actions_destroy(pFileActions);
345 Assert(rc2 == 0);
346 }
347
348 /* return on success.*/
349 if (!rc)
350 {
351 if (phProcess)
352 *phProcess = pid;
353 return VINF_SUCCESS;
354 }
355 }
356 }
357 else
358#endif
359 {
360 pid = fork();
361 if (!pid)
362 {
363 setpgid(0, 0); /* see comment above */
364
365 /*
366 * Change group and user if requested.
367 */
368#if 1 /** @todo This needs more work, see suplib/hardening. */
369 if (gid != ~(gid_t)0)
370 {
371 if (setgid(gid))
372 exit(127);
373 }
374
375 if (uid != ~(uid_t)0)
376 {
377 if (setuid(uid))
378 exit(127);
379 }
380#endif
381
382 /*
383 * Apply changes to the standard file descriptor and stuff.
384 */
385 for (int i = 0; i < 3; i++)
386 {
387 if (aStdFds[i] == -2)
388 close(aStdFds[i]);
389 else if (aStdFds[i] >= 0)
390 {
391 if (dup2(aStdFds[i], i) != i)
392 exit(127);
393 close(aStdFds[i]);
394 }
395 }
396
397 /*
398 * Daemonize the process if requested.
399 */
400 if (fFlags & RTPROC_FLAGS_DAEMONIZE)
401 {
402 rc = RTProcDaemonize(true /* fNoChDir */, false /* fNoClose */, NULL /* pszPidFile */);
403 AssertReleaseMsgFailed(("RTProcDaemonize returns %Rrc errno=%d\n", rc, errno));
404 exit(127);
405 }
406
407 /*
408 * Finally, execute the requested program.
409 */
410 rc = execve(pszExec, (char * const *)papszArgs, (char * const *)papszEnv);
411 AssertReleaseMsgFailed(("execve returns %d errno=%d\n", rc, errno));
412 exit(127);
413 }
414 if (pid > 0)
415 {
416 if (phProcess)
417 *phProcess = pid;
418 return VINF_SUCCESS;
419 }
420 rc = errno;
421 }
422
423
424 return VERR_NOT_IMPLEMENTED;
425}
426
427
428RTR3DECL(int) RTProcWait(RTPROCESS Process, unsigned fFlags, PRTPROCSTATUS pProcStatus)
429{
430 int rc;
431 do rc = RTProcWaitNoResume(Process, fFlags, pProcStatus);
432 while (rc == VERR_INTERRUPTED);
433 return rc;
434}
435
436RTR3DECL(int) RTProcWaitNoResume(RTPROCESS Process, unsigned fFlags, PRTPROCSTATUS pProcStatus)
437{
438 /*
439 * Validate input.
440 */
441 if (Process <= 0)
442 {
443 AssertMsgFailed(("Invalid Process=%d\n", Process));
444 return VERR_INVALID_PARAMETER;
445 }
446 if (fFlags & ~(RTPROCWAIT_FLAGS_NOBLOCK | RTPROCWAIT_FLAGS_BLOCK))
447 {
448 AssertMsgFailed(("Invalid flags %#x\n", fFlags));
449 return VERR_INVALID_PARAMETER;
450 }
451
452 /*
453 * Performe the wait.
454 */
455 int iStatus = 0;
456 int rc = waitpid(Process, &iStatus, fFlags & RTPROCWAIT_FLAGS_NOBLOCK ? WNOHANG : 0);
457 if (rc > 0)
458 {
459 /*
460 * Fill in the status structure.
461 */
462 if (pProcStatus)
463 {
464 if (WIFEXITED(iStatus))
465 {
466 pProcStatus->enmReason = RTPROCEXITREASON_NORMAL;
467 pProcStatus->iStatus = WEXITSTATUS(iStatus);
468 }
469 else if (WIFSIGNALED(iStatus))
470 {
471 pProcStatus->enmReason = RTPROCEXITREASON_SIGNAL;
472 pProcStatus->iStatus = WTERMSIG(iStatus);
473 }
474 else
475 {
476 Assert(!WIFSTOPPED(iStatus));
477 pProcStatus->enmReason = RTPROCEXITREASON_ABEND;
478 pProcStatus->iStatus = iStatus;
479 }
480 }
481 return VINF_SUCCESS;
482 }
483
484 /*
485 * Child running?
486 */
487 if (!rc)
488 {
489 Assert(fFlags & RTPROCWAIT_FLAGS_NOBLOCK);
490 return VERR_PROCESS_RUNNING;
491 }
492
493 /*
494 * Figure out which error to return.
495 */
496 int iErr = errno;
497 if (iErr == ECHILD)
498 return VERR_PROCESS_NOT_FOUND;
499 return RTErrConvertFromErrno(iErr);
500}
501
502
503RTR3DECL(int) RTProcTerminate(RTPROCESS Process)
504{
505 if (!kill(Process, SIGKILL))
506 return VINF_SUCCESS;
507 return RTErrConvertFromErrno(errno);
508}
509
510
511RTR3DECL(uint64_t) RTProcGetAffinityMask()
512{
513 // @todo
514 return 1;
515}
516
517
518/**
519 * Daemonize the current process, making it a background process. The current
520 * process will exit if daemonizing is successful.
521 *
522 * @returns iprt status code.
523 * @param fNoChDir Pass false to change working directory to "/".
524 * @param fNoClose Pass false to redirect standard file streams to the null device.
525 * @param pszPidfile Path to a file to write the process id of the daemon
526 * process to. Daemonizing will fail if this file already
527 * exists or cannot be written. May be NULL.
528 */
529RTR3DECL(int) RTProcDaemonize(bool fNoChDir, bool fNoClose, const char *pszPidfile)
530{
531 /*
532 * Fork the child process in a new session and quit the parent.
533 *
534 * - fork once and create a new session (setsid). This will detach us
535 * from the controlling tty meaning that we won't receive the SIGHUP
536 * (or any other signal) sent to that session.
537 * - The SIGHUP signal is ignored because the session/parent may throw
538 * us one before we get to the setsid.
539 * - When the parent exit(0) we will become an orphan and re-parented to
540 * the init process.
541 * - Because of the sometimes unexpected semantics of assigning the
542 * controlling tty automagically when a session leader first opens a tty,
543 * we will fork() once more to get rid of the session leadership role.
544 */
545
546 /* We start off by opening the pidfile, so that we can fail straight away
547 * if it already exists. */
548 int fdPidfile = -1;
549 if (pszPidfile != NULL)
550 {
551 /* @note the exclusive create is not guaranteed on all file
552 * systems (e.g. NFSv2) */
553 if ((fdPidfile = open(pszPidfile, O_RDWR | O_CREAT | O_EXCL, 0644)) == -1)
554 return RTErrConvertFromErrno(errno);
555 }
556
557 /* Ignore SIGHUP straight away. */
558 struct sigaction OldSigAct;
559 struct sigaction SigAct;
560 memset(&SigAct, 0, sizeof(SigAct));
561 SigAct.sa_handler = SIG_IGN;
562 int rcSigAct = sigaction(SIGHUP, &SigAct, &OldSigAct);
563
564 /* First fork, to become independent process. */
565 pid_t pid = fork();
566 if (pid == -1)
567 return RTErrConvertFromErrno(errno);
568 if (pid != 0)
569 {
570 /* Parent exits, no longer necessary. The child gets reparented
571 * to the init process. */
572 exit(0);
573 }
574
575 /* Create new session, fix up the standard file descriptors and the
576 * current working directory. */
577 pid_t newpgid = setsid();
578 int SavedErrno = errno;
579 if (rcSigAct != -1)
580 sigaction(SIGHUP, &OldSigAct, NULL);
581 if (newpgid == -1)
582 return RTErrConvertFromErrno(SavedErrno);
583
584 if (!fNoClose)
585 {
586 /* Open stdin(0), stdout(1) and stderr(2) as /dev/null. */
587 int fd = open("/dev/null", O_RDWR);
588 if (fd == -1) /* paranoia */
589 {
590 close(STDIN_FILENO);
591 close(STDOUT_FILENO);
592 close(STDERR_FILENO);
593 fd = open("/dev/null", O_RDWR);
594 }
595 if (fd != -1)
596 {
597 dup2(fd, STDIN_FILENO);
598 dup2(fd, STDOUT_FILENO);
599 dup2(fd, STDERR_FILENO);
600 if (fd > 2)
601 close(fd);
602 }
603 }
604
605 if (!fNoChDir)
606 int rcChdir = chdir("/");
607
608 /* Second fork to lose session leader status. */
609 pid = fork();
610 if (pid == -1)
611 return RTErrConvertFromErrno(errno);
612
613 if (pid != 0)
614 {
615 /* Write the pid file, this is done in the parent, before exiting. */
616 if (fdPidfile != -1)
617 {
618 char szBuf[256];
619 size_t cbPid = RTStrPrintf(szBuf, sizeof(szBuf), "%d\n", pid);
620 int rcWrite = write(fdPidfile, szBuf, cbPid);
621 close(fdPidfile);
622 }
623 exit(0);
624 }
625
626 return VINF_SUCCESS;
627}
628
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