VirtualBox

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

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

iprt: Adjustments to RTPipeReadBlocking and RTPipeWriteBlocking. RTPoll and RTProcCreateEx testcases.

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