VirtualBox

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

Last change on this file since 11367 was 11337, checked in by vboxsync, 17 years ago

Runtime: new function for daemonizing, implemented on posix systems only so far.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 11.2 KB
Line 
1/* $Id: process-posix.cpp 11337 2008-08-11 15:15:13Z 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 <iprt/string.h>
57#include <iprt/assert.h>
58#include <iprt/err.h>
59#include <iprt/env.h>
60#include "internal/process.h"
61
62
63
64RTR3DECL(int) RTProcCreate(const char *pszExec, const char * const *papszArgs, RTENV Env, unsigned fFlags, PRTPROCESS pProcess)
65{
66 /*
67 * Validate input.
68 */
69 AssertPtrReturn(pszExec, VERR_INVALID_POINTER);
70 AssertReturn(*pszExec, VERR_INVALID_PARAMETER);
71 AssertReturn(!fFlags, VERR_INVALID_PARAMETER);
72 AssertReturn(Env != NIL_RTENV, VERR_INVALID_PARAMETER);
73 const char * const *papszEnv = RTEnvGetExecEnvP(Env);
74 AssertPtrReturn(papszEnv, VERR_INVALID_HANDLE);
75 /* later: path searching. */
76
77
78 /*
79 * Check for execute access to the file.
80 */
81 if (access(pszExec, X_OK))
82 {
83 int rc = RTErrConvertFromErrno(errno);
84 AssertMsgFailed(("'%s' %Vrc!\n", pszExec, rc));
85 return rc;
86 }
87
88#if 0
89 /*
90 * Squeeze gdb --args in front of what's being spawned.
91 */
92 unsigned cArgs = 0;
93 while (papszArgs[cArgs])
94 cArgs++;
95 cArgs += 3;
96 const char **papszArgsTmp = (const char **)alloca(cArgs * sizeof(char *));
97 papszArgsTmp[0] = "/usr/bin/gdb";
98 papszArgsTmp[1] = "--args";
99 papszArgsTmp[2] = pszExec;
100 for (unsigned i = 1; papszArgs[i]; i++)
101 papszArgsTmp[i + 2] = papszArgs[i];
102 papszArgsTmp[cArgs - 1] = NULL;
103 pszExec = papszArgsTmp[0];
104 papszArgs = papszArgsTmp;
105#endif
106
107 /*
108 * Spawn the child.
109 */
110 pid_t pid;
111#ifdef HAVE_POSIX_SPAWN
112 /** @todo check if it requires any of those two attributes, don't remember atm. */
113 int rc = posix_spawn(&pid, pszExec, NULL, NULL, (char * const *)papszArgs,
114 (char * const *)papszEnv);
115 if (!rc)
116 {
117 if (pProcess)
118 *pProcess = pid;
119 return VINF_SUCCESS;
120 }
121
122#else
123
124 pid = fork();
125 if (!pid)
126 {
127 int rc;
128 rc = execve(pszExec, (char * const *)papszArgs, (char * const *)papszEnv);
129 AssertReleaseMsgFailed(("execve returns %d errno=%d\n", rc, errno));
130 exit(127);
131 }
132 if (pid > 0)
133 {
134 if (pProcess)
135 *pProcess = pid;
136 return VINF_SUCCESS;
137 }
138 int rc = errno;
139#endif
140
141 /* failure, errno value in rc. */
142 AssertMsgFailed(("spawn/exec failed rc=%d\n", rc)); /* this migth be annoying... */
143 return RTErrConvertFromErrno(rc);
144}
145
146
147RTR3DECL(int) RTProcWait(RTPROCESS Process, unsigned fFlags, PRTPROCSTATUS pProcStatus)
148{
149 int rc;
150 do rc = RTProcWaitNoResume(Process, fFlags, pProcStatus);
151 while (rc == VERR_INTERRUPTED);
152 return rc;
153}
154
155RTR3DECL(int) RTProcWaitNoResume(RTPROCESS Process, unsigned fFlags, PRTPROCSTATUS pProcStatus)
156{
157 /*
158 * Validate input.
159 */
160 if (Process <= 0)
161 {
162 AssertMsgFailed(("Invalid Process=%d\n", Process));
163 return VERR_INVALID_PARAMETER;
164 }
165 if (fFlags & ~(RTPROCWAIT_FLAGS_NOBLOCK | RTPROCWAIT_FLAGS_BLOCK))
166 {
167 AssertMsgFailed(("Invalid flags %#x\n", fFlags));
168 return VERR_INVALID_PARAMETER;
169 }
170
171 /*
172 * Performe the wait.
173 */
174 int iStatus = 0;
175 int rc = waitpid(Process, &iStatus, fFlags & RTPROCWAIT_FLAGS_NOBLOCK ? WNOHANG : 0);
176 if (rc > 0)
177 {
178 /*
179 * Fill in the status structure.
180 */
181 if (pProcStatus)
182 {
183 if (WIFEXITED(iStatus))
184 {
185 pProcStatus->enmReason = RTPROCEXITREASON_NORMAL;
186 pProcStatus->iStatus = WEXITSTATUS(iStatus);
187 }
188 else if (WIFSIGNALED(iStatus))
189 {
190 pProcStatus->enmReason = RTPROCEXITREASON_SIGNAL;
191 pProcStatus->iStatus = WTERMSIG(iStatus);
192 }
193 else
194 {
195 Assert(!WIFSTOPPED(iStatus));
196 pProcStatus->enmReason = RTPROCEXITREASON_ABEND;
197 pProcStatus->iStatus = iStatus;
198 }
199 }
200 return VINF_SUCCESS;
201 }
202
203 /*
204 * Child running?
205 */
206 if (!rc)
207 {
208 Assert(fFlags & RTPROCWAIT_FLAGS_NOBLOCK);
209 return VERR_PROCESS_RUNNING;
210 }
211
212 /*
213 * Figure out which error to return.
214 */
215 int iErr = errno;
216 if (iErr == ECHILD)
217 return VERR_PROCESS_NOT_FOUND;
218 return RTErrConvertFromErrno(iErr);
219}
220
221
222RTR3DECL(int) RTProcTerminate(RTPROCESS Process)
223{
224 if (!kill(Process, SIGKILL))
225 return VINF_SUCCESS;
226 return RTErrConvertFromErrno(errno);
227}
228
229
230RTR3DECL(uint64_t) RTProcGetAffinityMask()
231{
232 // @todo
233 return 1;
234}
235
236
237RTR3DECL(char *) RTProcGetExecutableName(char *pszExecName, size_t cchExecName)
238{
239 /*
240 * I don't think there is a posix API for this, but
241 * because I'm lazy I'm not creating OS specific code
242 * files and code for this.
243 */
244#if defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD) || defined(RT_OS_SOLARIS)
245# ifdef RT_OS_LINUX
246 int cchLink = readlink("/proc/self/exe", pszExecName, cchExecName - 1);
247# elif defined(RT_OS_SOLARIS)
248 char szFileBuf[80];
249 RTStrPrintf(szFileBuf, sizeof(szFileBuf), "/proc/%ld/path/a.out", (long)getpid());
250 int cchLink = readlink(szFileBuf, pszExecName, cchExecName - 1);
251# else
252 int cchLink = readlink("/proc/curproc/file", pszExecName, cchExecName - 1);
253# endif
254 if (cchLink > 0 && (size_t)cchLink <= cchExecName - 1)
255 {
256 pszExecName[cchLink] = '\0';
257 return pszExecName;
258 }
259
260#elif defined(RT_OS_OS2) || defined(RT_OS_L4)
261 if (!_execname(pszExecName, cchExecName))
262 return pszExecName;
263
264#elif defined(RT_OS_DARWIN)
265 const char *pszImageName = _dyld_get_image_name(0);
266 if (pszImageName)
267 {
268 size_t cchImageName = strlen(pszImageName);
269 if (cchImageName < cchExecName)
270 return (char *)memcpy(pszExecName, pszImageName, cchImageName + 1);
271 }
272
273#else
274# error "Port me!"
275#endif
276 return NULL;
277}
278
279/**
280 * Daemonize the current process, making it a background process. The current
281 * process will exit if daemonizing is successful.
282 *
283 * @returns iprt status code.
284 * @param fNoChDir Pass false to change working directory to "/".
285 * @param fNoClose Pass false to redirect standard file streams to the null device.
286 * @param pszPidfile Path to a file to write the process id of the daemon
287 * process to. Daemonizing will fail if this file already
288 * exists or cannot be written. May be NULL.
289 */
290RTR3DECL(int) RTProcDaemonize(bool fNoChDir, bool fNoClose, const char *pszPidfile)
291{
292 /*
293 * Fork the child process in a new session and quit the parent.
294 *
295 * - fork once and create a new session (setsid). This will detach us
296 * from the controlling tty meaning that we won't receive the SIGHUP
297 * (or any other signal) sent to that session.
298 * - The SIGHUP signal is ignored because the session/parent may throw
299 * us one before we get to the setsid.
300 * - When the parent exit(0) we will become an orphan and re-parented to
301 * the init process.
302 * - Because of the sometimes unexpected semantics of assigning the
303 * controlling tty automagically when a session leader first opens a tty,
304 * we will fork() once more to get rid of the session leadership role.
305 */
306
307 /* We start off by opening the pidfile, so that we can fail straight away
308 * if it already exists. */
309 int fdPidfile = -1;
310 if (pszPidfile != NULL)
311 {
312 /* @note the exclusive create is not guaranteed on all file
313 * systems (e.g. NFSv2) */
314 if ((fdPidfile = open(pszPidfile, O_RDWR | O_CREAT | O_EXCL, 0644)) == -1)
315 return RTErrConvertFromErrno(errno);
316 }
317
318 /* Ignore SIGHUP straight away. */
319 struct sigaction OldSigAct;
320 struct sigaction SigAct;
321 memset(&SigAct, 0, sizeof(SigAct));
322 SigAct.sa_handler = SIG_IGN;
323 int rcSigAct = sigaction(SIGHUP, &SigAct, &OldSigAct);
324
325 /* First fork, to become independent process. */
326 pid_t pid = fork();
327 if (pid == -1)
328 return RTErrConvertFromErrno(errno);
329 if (pid != 0)
330 {
331 /* Parent exits, no longer necessary. Child creates gets reparented
332 * to the init process. */
333 exit(0);
334 }
335
336 /* Create new session, fix up the standard file descriptors and the
337 * current working directory. */
338 pid_t newpgid = setsid();
339 int SavedErrno = errno;
340 if (rcSigAct != -1)
341 sigaction(SIGHUP, &OldSigAct, NULL);
342 if (newpgid == -1)
343 return RTErrConvertFromErrno(SavedErrno);
344
345 if (!fNoClose)
346 {
347 /* Open stdin(0), stdout(1) and stderr(2) as /dev/null. */
348 int fd = open("/dev/null", O_RDWR);
349 if (fd == -1) /* paranoia */
350 {
351 close(STDIN_FILENO);
352 close(STDOUT_FILENO);
353 close(STDERR_FILENO);
354 fd = open("/dev/null", O_RDWR);
355 }
356 if (fd != -1)
357 {
358 dup2(fd, STDIN_FILENO);
359 dup2(fd, STDOUT_FILENO);
360 dup2(fd, STDERR_FILENO);
361 if (fd > 2)
362 close(fd);
363 }
364 }
365
366 if (!fNoChDir)
367 chdir("/");
368
369 /* Second fork to lose session leader status. */
370 pid = fork();
371 if (pid == -1)
372 return RTErrConvertFromErrno(errno);
373 if (pid != 0)
374 {
375 /* Write the pid file, this is done in the parent, before exiting. */
376 if (fdPidfile != -1)
377 {
378 char szBuf[256];
379 size_t cbPid = RTStrPrintf(szBuf, sizeof(szBuf), "%d\n", pid);
380 write(fdPidfile, szBuf, cbPid);
381 close(fdPidfile);
382 }
383 exit(0);
384 }
385
386 return VINF_SUCCESS;
387}
388
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