VirtualBox

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

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

process-posix.cpp: added todos.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 16.7 KB
Line 
1/* $Id: process-posix.cpp 27747 2010-03-26 14:56:44Z 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_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 /** @todo OS/2: implement DETACHED (BACKGROUND stuff), see VbglR3Daemonize. */
179 /** @todo Try do the detach thing with posix spawn. */
180 if ( !(fFlags & (RTPROC_FLAGS_DAEMONIZE_DEPRECATED | RTPROC_FLAGS_DETACHED))
181 && uid == ~(uid_t)0
182 && gid == ~(gid_t)0
183 )
184 {
185 /* Spawn attributes. */
186 posix_spawnattr_t Attr;
187 rc = posix_spawnattr_init(&Attr);
188 if (!rc)
189 {
190# ifndef RT_OS_OS2 /* We don't need this on OS/2 and I don't recall if it's actually implemented. */
191 rc = posix_spawnattr_setflags(&Attr, POSIX_SPAWN_SETPGROUP);
192 Assert(rc == 0);
193 if (!rc)
194 {
195 rc = posix_spawnattr_setpgroup(&Attr, 0 /* pg == child pid */);
196 Assert(rc == 0);
197 }
198# endif
199
200 /* File changes. */
201 posix_spawn_file_actions_t FileActions;
202 posix_spawn_file_actions_t *pFileActions = NULL;
203 if (aStdFds[0] != -1 || aStdFds[1] != -1 || aStdFds[2] != -1)
204 {
205 rc = posix_spawn_file_actions_init(&FileActions);
206 if (!rc)
207 {
208 pFileActions = &FileActions;
209 for (int i = 0; i < 3; i++)
210 {
211 int fd = aStdFds[i];
212 if (fd == -2)
213 rc = posix_spawn_file_actions_addclose(&FileActions, i);
214 else if (fd >= 0 && fd != i)
215 {
216 rc = posix_spawn_file_actions_adddup2(&FileActions, fd, i);
217 if (!rc)
218 {
219 for (int j = i + 1; j < 3; j++)
220 if (aStdFds[j] == fd)
221 {
222 fd = -1;
223 break;
224 }
225 if (fd >= 0)
226 rc = posix_spawn_file_actions_addclose(&FileActions, fd);
227 }
228 }
229 if (rc)
230 break;
231 }
232 }
233 }
234
235 if (!rc)
236 rc = posix_spawn(&pid, pszExec, pFileActions, &Attr, (char * const *)papszArgs,
237 (char * const *)papszEnv);
238
239 /* cleanup */
240 int rc2 = posix_spawnattr_destroy(&Attr); Assert(rc2 == 0); NOREF(rc2);
241 if (pFileActions)
242 {
243 rc2 = posix_spawn_file_actions_destroy(pFileActions);
244 Assert(rc2 == 0);
245 }
246
247 /* return on success.*/
248 if (!rc)
249 {
250 if (phProcess)
251 *phProcess = pid;
252 return VINF_SUCCESS;
253 }
254 }
255 }
256 else
257#endif
258 {
259 pid = fork();
260 if (!pid)
261 {
262 setpgid(0, 0); /* see comment above */
263
264 /*
265 * Change group and user if requested.
266 */
267#if 1 /** @todo This needs more work, see suplib/hardening. */
268 if (gid != ~(gid_t)0)
269 {
270 if (setgid(gid))
271 exit(126);
272 }
273
274 if (uid != ~(uid_t)0)
275 {
276 if (setuid(uid))
277 exit(126);
278 }
279#endif
280
281 /*
282 * Apply changes to the standard file descriptor and stuff.
283 */
284 for (int i = 0; i < 3; i++)
285 {
286 int fd = aStdFds[i];
287 if (fd == -2)
288 close(i);
289 else if (fd >= 0)
290 {
291 int rc2 = dup2(fd, i);
292 if (rc2 != i)
293 exit(125);
294 for (int j = i + 1; j < 3; j++)
295 if (aStdFds[j] == fd)
296 {
297 fd = -1;
298 break;
299 }
300 if (fd >= 0)
301 close(fd);
302 }
303 }
304
305 /*
306 * Daemonize the process if requested.
307 */
308 if (fFlags & (RTPROC_FLAGS_DAEMONIZE_DEPRECATED | RTPROC_FLAGS_DETACHED))
309 {
310 rc = RTProcDaemonizeUsingFork(true /*fNoChDir*/,
311 !(fFlags & RTPROC_FLAGS_DAEMONIZE_DEPRECATED) /*fNoClose*/,
312 NULL /* pszPidFile */);
313 if (RT_FAILURE(rc))
314 {
315 /* parent */
316 AssertReleaseMsgFailed(("RTProcDaemonize returns %Rrc errno=%d\n", rc, errno));
317 exit(127);
318 }
319 /* daemonized child */
320 }
321
322 /*
323 * Finally, execute the requested program.
324 */
325 rc = execve(pszExec, (char * const *)papszArgs, (char * const *)papszEnv);
326 AssertReleaseMsgFailed(("execve returns %d errno=%d\n", rc, errno));
327 exit(127);
328 }
329 if (pid > 0)
330 {
331 if (phProcess)
332 *phProcess = pid;
333 return VINF_SUCCESS;
334 }
335 rc = errno;
336 }
337
338
339 return VERR_NOT_IMPLEMENTED;
340}
341
342
343RTR3DECL(int) RTProcWait(RTPROCESS Process, unsigned fFlags, PRTPROCSTATUS pProcStatus)
344{
345 int rc;
346 do rc = RTProcWaitNoResume(Process, fFlags, pProcStatus);
347 while (rc == VERR_INTERRUPTED);
348 return rc;
349}
350
351RTR3DECL(int) RTProcWaitNoResume(RTPROCESS Process, unsigned fFlags, PRTPROCSTATUS pProcStatus)
352{
353 /*
354 * Validate input.
355 */
356 if (Process <= 0)
357 {
358 AssertMsgFailed(("Invalid Process=%d\n", Process));
359 return VERR_INVALID_PARAMETER;
360 }
361 if (fFlags & ~(RTPROCWAIT_FLAGS_NOBLOCK | RTPROCWAIT_FLAGS_BLOCK))
362 {
363 AssertMsgFailed(("Invalid flags %#x\n", fFlags));
364 return VERR_INVALID_PARAMETER;
365 }
366
367 /*
368 * Performe the wait.
369 */
370 int iStatus = 0;
371 int rc = waitpid(Process, &iStatus, fFlags & RTPROCWAIT_FLAGS_NOBLOCK ? WNOHANG : 0);
372 if (rc > 0)
373 {
374 /*
375 * Fill in the status structure.
376 */
377 if (pProcStatus)
378 {
379 if (WIFEXITED(iStatus))
380 {
381 pProcStatus->enmReason = RTPROCEXITREASON_NORMAL;
382 pProcStatus->iStatus = WEXITSTATUS(iStatus);
383 }
384 else if (WIFSIGNALED(iStatus))
385 {
386 pProcStatus->enmReason = RTPROCEXITREASON_SIGNAL;
387 pProcStatus->iStatus = WTERMSIG(iStatus);
388 }
389 else
390 {
391 Assert(!WIFSTOPPED(iStatus));
392 pProcStatus->enmReason = RTPROCEXITREASON_ABEND;
393 pProcStatus->iStatus = iStatus;
394 }
395 }
396 return VINF_SUCCESS;
397 }
398
399 /*
400 * Child running?
401 */
402 if (!rc)
403 {
404 Assert(fFlags & RTPROCWAIT_FLAGS_NOBLOCK);
405 return VERR_PROCESS_RUNNING;
406 }
407
408 /*
409 * Figure out which error to return.
410 */
411 int iErr = errno;
412 if (iErr == ECHILD)
413 return VERR_PROCESS_NOT_FOUND;
414 return RTErrConvertFromErrno(iErr);
415}
416
417
418RTR3DECL(int) RTProcTerminate(RTPROCESS Process)
419{
420 if (!kill(Process, SIGKILL))
421 return VINF_SUCCESS;
422 return RTErrConvertFromErrno(errno);
423}
424
425
426RTR3DECL(uint64_t) RTProcGetAffinityMask()
427{
428 // @todo
429 return 1;
430}
431
432
433RTR3DECL(int) RTProcDaemonizeUsingFork(bool fNoChDir, bool fNoClose, const char *pszPidfile)
434{
435 /*
436 * Fork the child process in a new session and quit the parent.
437 *
438 * - fork once and create a new session (setsid). This will detach us
439 * from the controlling tty meaning that we won't receive the SIGHUP
440 * (or any other signal) sent to that session.
441 * - The SIGHUP signal is ignored because the session/parent may throw
442 * us one before we get to the setsid.
443 * - When the parent exit(0) we will become an orphan and re-parented to
444 * the init process.
445 * - Because of the sometimes unexpected semantics of assigning the
446 * controlling tty automagically when a session leader first opens a tty,
447 * we will fork() once more to get rid of the session leadership role.
448 */
449
450 /* We start off by opening the pidfile, so that we can fail straight away
451 * if it already exists. */
452 int fdPidfile = -1;
453 if (pszPidfile != NULL)
454 {
455 /* @note the exclusive create is not guaranteed on all file
456 * systems (e.g. NFSv2) */
457 if ((fdPidfile = open(pszPidfile, O_RDWR | O_CREAT | O_EXCL, 0644)) == -1)
458 return RTErrConvertFromErrno(errno);
459 }
460
461 /* Ignore SIGHUP straight away. */
462 struct sigaction OldSigAct;
463 struct sigaction SigAct;
464 memset(&SigAct, 0, sizeof(SigAct));
465 SigAct.sa_handler = SIG_IGN;
466 int rcSigAct = sigaction(SIGHUP, &SigAct, &OldSigAct);
467
468 /* First fork, to become independent process. */
469 pid_t pid = fork();
470 if (pid == -1)
471 return RTErrConvertFromErrno(errno);
472 if (pid != 0)
473 {
474 /* Parent exits, no longer necessary. The child gets reparented
475 * to the init process. */
476 exit(0);
477 }
478
479 /* Create new session, fix up the standard file descriptors and the
480 * current working directory. */
481 pid_t newpgid = setsid();
482 int SavedErrno = errno;
483 if (rcSigAct != -1)
484 sigaction(SIGHUP, &OldSigAct, NULL);
485 if (newpgid == -1)
486 return RTErrConvertFromErrno(SavedErrno);
487
488 if (!fNoClose)
489 {
490 /* Open stdin(0), stdout(1) and stderr(2) as /dev/null. */
491 int fd = open("/dev/null", O_RDWR);
492 if (fd == -1) /* paranoia */
493 {
494 close(STDIN_FILENO);
495 close(STDOUT_FILENO);
496 close(STDERR_FILENO);
497 fd = open("/dev/null", O_RDWR);
498 }
499 if (fd != -1)
500 {
501 dup2(fd, STDIN_FILENO);
502 dup2(fd, STDOUT_FILENO);
503 dup2(fd, STDERR_FILENO);
504 if (fd > 2)
505 close(fd);
506 }
507 }
508
509 if (!fNoChDir)
510 {
511 int rcChdir = chdir("/");
512 }
513
514 /* Second fork to lose session leader status. */
515 pid = fork();
516 if (pid == -1)
517 return RTErrConvertFromErrno(errno);
518
519 if (pid != 0)
520 {
521 /* Write the pid file, this is done in the parent, before exiting. */
522 if (fdPidfile != -1)
523 {
524 char szBuf[256];
525 size_t cbPid = RTStrPrintf(szBuf, sizeof(szBuf), "%d\n", pid);
526 int rcWrite = write(fdPidfile, szBuf, cbPid);
527 close(fdPidfile);
528 }
529 exit(0);
530 }
531
532 return VINF_SUCCESS;
533}
534
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