VirtualBox

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

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

iprt/RTProcCreateEx: restored assertion removed in r58933 and added a few more.

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