mirror of
https://github.com/stefanocasazza/ULib.git
synced 2025-09-28 19:05:55 +08:00
663 lines
21 KiB
C++
663 lines
21 KiB
C++
// ============================================================================
|
|
//
|
|
// = LIBRARY
|
|
// ULib - c++ library
|
|
//
|
|
// = FILENAME
|
|
// process.cpp
|
|
//
|
|
// = AUTHOR
|
|
// Stefano Casazza
|
|
//
|
|
// ============================================================================
|
|
|
|
#include <ulib/process.h>
|
|
#include <ulib/utility/interrupt.h>
|
|
|
|
#include <ulib/base/utility.h>
|
|
|
|
#ifdef _MSWINDOWS_
|
|
HANDLE UProcess::hFile[6];
|
|
HANDLE UProcess::hChildIn;
|
|
HANDLE UProcess::hChildOut;
|
|
HANDLE UProcess::hChildErr;
|
|
STARTUPINFO UProcess::aStartupInfo;
|
|
PROCESS_INFORMATION UProcess::aProcessInformation;
|
|
#elif defined(HAVE_POSIX_SPAWN)
|
|
# include <spawn.h>
|
|
#endif
|
|
|
|
int UProcess::filedes[6];
|
|
|
|
bool UProcess::fork()
|
|
{
|
|
U_TRACE_NO_PARAM(1, "UProcess::fork()")
|
|
|
|
U_CHECK_MEMORY
|
|
|
|
_pid = U_FORK();
|
|
|
|
if ((running = (_pid != -1)))
|
|
{
|
|
if (child()) u_setPid();
|
|
|
|
U_INTERNAL_DUMP("_pid = %u u_pid = %u %%P = %P running = %b", _pid, u_pid, running)
|
|
|
|
U_RETURN(true);
|
|
}
|
|
|
|
U_RETURN(false);
|
|
}
|
|
|
|
// inlining failed in call to 'UProcess::setStdInOutErr(bool, bool, bool)': call is unlikely and code size would grow
|
|
|
|
U_NO_EXPORT void UProcess::setStdInOutErr(bool fd_stdin, bool fd_stdout, bool fd_stderr)
|
|
{
|
|
U_TRACE(1, "UProcess::setStdInOutErr(%b,%b,%b)", fd_stdin, fd_stdout, fd_stderr) // problem with sanitize address
|
|
|
|
#ifdef _MSWINDOWS_
|
|
HANDLE hProcess = GetCurrentProcess();
|
|
#endif
|
|
|
|
// check if we write to STDIN
|
|
|
|
if (fd_stdin)
|
|
{
|
|
# ifdef _MSWINDOWS_
|
|
if (hFile[1]) // Created parent-output pipe...
|
|
{
|
|
hChildIn = hFile[0];
|
|
|
|
// Duplicating as inheritable child-input pipe
|
|
// --------------------------------------------------------------------------------------------------------------------------------------
|
|
// (void) U_SYSCALL(DuplicateHandle, "%p,%p,%p,%p,%lu,%b,%lu", hProcess, hFile[0], hProcess, &hChildIn, 0, TRUE, DUPLICATE_SAME_ACCESS);
|
|
// (void) U_SYSCALL( CloseHandle, "%p", hFile[0]);
|
|
// --------------------------------------------------------------------------------------------------------------------------------------
|
|
}
|
|
else
|
|
{
|
|
hChildIn = (HANDLE)_get_osfhandle(filedes[0]);
|
|
}
|
|
# else
|
|
U_INTERNAL_ASSERT_MAJOR(filedes[0], STDERR_FILENO)
|
|
|
|
# ifndef HAVE_DUP3
|
|
(void) U_SYSCALL(dup2, "%d,%d", filedes[0], STDIN_FILENO);
|
|
# else
|
|
(void) U_SYSCALL(dup3, "%d,%d,%d", filedes[0], STDIN_FILENO, O_CLOEXEC);
|
|
# endif
|
|
|
|
U_INTERNAL_ASSERT_EQUALS(::fcntl(STDIN_FILENO,F_GETFD,FD_CLOEXEC), 0)
|
|
# endif
|
|
}
|
|
|
|
// check if we read from STDOUT
|
|
|
|
if (fd_stdout)
|
|
{
|
|
# ifdef _MSWINDOWS_
|
|
if (hFile[2]) // Created parent-input pipe...
|
|
{
|
|
hChildOut = hFile[3];
|
|
|
|
// Duplicating as inheritable child-output pipe
|
|
// --------------------------------------------------------------------------------------------------------------------------------------
|
|
// (void) U_SYSCALL(DuplicateHandle, "%p,%p,%p,%p,%lu,%b,%lu", hProcess, hFile[3], hProcess, &hChildOut, 0, TRUE, DUPLICATE_SAME_ACCESS);
|
|
// (void) U_SYSCALL( CloseHandle, "%p", hFile[3]);
|
|
// --------------------------------------------------------------------------------------------------------------------------------------
|
|
}
|
|
else
|
|
{
|
|
hChildOut = (HANDLE)_get_osfhandle(filedes[3]);
|
|
}
|
|
# else
|
|
U_INTERNAL_ASSERT_MAJOR(filedes[3], STDOUT_FILENO)
|
|
|
|
# ifndef HAVE_DUP3
|
|
(void) U_SYSCALL(dup2, "%d,%d", filedes[3], STDOUT_FILENO);
|
|
# else
|
|
(void) U_SYSCALL(dup3, "%d,%d,%d", filedes[3], STDOUT_FILENO, O_CLOEXEC);
|
|
# endif
|
|
|
|
U_INTERNAL_ASSERT_EQUALS(::fcntl(STDOUT_FILENO,F_GETFD,FD_CLOEXEC), 0)
|
|
# endif
|
|
}
|
|
|
|
// check if we read from STDERR
|
|
|
|
if (fd_stderr)
|
|
{
|
|
# ifdef _MSWINDOWS_
|
|
if (hFile[4]) // Created parent-input pipe...
|
|
{
|
|
hChildErr = hFile[5];
|
|
|
|
// Duplicating as inheritable child-output pipe
|
|
// --------------------------------------------------------------------------------------------------------------------------------------
|
|
// (void) U_SYSCALL(DuplicateHandle, "%p,%p,%p,%p,%lu,%b,%lu", hProcess, hFile[5], hProcess, &hChildErr, 0, TRUE, DUPLICATE_SAME_ACCESS);
|
|
// (void) U_SYSCALL( CloseHandle, "%p", hFile[5]);
|
|
// --------------------------------------------------------------------------------------------------------------------------------------
|
|
}
|
|
else
|
|
{
|
|
hChildErr = (HANDLE)_get_osfhandle(filedes[5]);
|
|
}
|
|
# else
|
|
U_INTERNAL_ASSERT(filedes[5] >= STDIN_FILENO)
|
|
|
|
# ifndef HAVE_DUP3
|
|
(void) U_SYSCALL(dup2, "%d,%d", filedes[5], STDERR_FILENO);
|
|
# else
|
|
(void) U_SYSCALL(dup3, "%d,%d,%d", filedes[5], STDERR_FILENO, O_CLOEXEC);
|
|
# endif
|
|
|
|
U_INTERNAL_ASSERT_EQUALS(::fcntl(STDERR_FILENO,F_GETFD,FD_CLOEXEC), 0)
|
|
# endif
|
|
}
|
|
|
|
#ifdef _MSWINDOWS_
|
|
CloseHandle(hProcess);
|
|
#else
|
|
if (fd_stdin)
|
|
{
|
|
U_INTERNAL_DUMP("filedes[0,1] = { %d, %d }", filedes[0], filedes[1])
|
|
|
|
(void) U_SYSCALL(close, "%d", filedes[0]);
|
|
if (filedes[1] > STDERR_FILENO) (void) U_SYSCALL(close, "%d", filedes[1]);
|
|
}
|
|
|
|
if (fd_stdout)
|
|
{
|
|
U_INTERNAL_DUMP("filedes[2,3] = { %d, %d }", filedes[2], filedes[3])
|
|
|
|
(void) U_SYSCALL(close, "%d", filedes[3]);
|
|
if (filedes[2] > STDERR_FILENO) (void) U_SYSCALL(close, "%d", filedes[2]);
|
|
}
|
|
|
|
if (fd_stderr)
|
|
{
|
|
U_INTERNAL_DUMP("filedes[4,5] = { %d, %d }", filedes[4], filedes[5])
|
|
|
|
(void) U_SYSCALL(close, "%d", filedes[5]);
|
|
if (filedes[4] > STDERR_FILENO) (void) U_SYSCALL(close, "%d", filedes[4]);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#ifdef _MSWINDOWS_
|
|
void UProcess::pipe(int fdp)
|
|
{
|
|
U_TRACE(1, "UProcess::pipe(%d)", fdp)
|
|
|
|
U_INTERNAL_ASSERT_RANGE(STDIN_FILENO, fdp, STDERR_FILENO)
|
|
|
|
int fdn = (fdp * 2); // filedes[fdn] is for READING, filedes[fdn+1] is for WRITING
|
|
|
|
if (U_SYSCALL(CreatePipe, "%p,%p,%p,%lu", hFile+fdn, hFile+fdn+1, &sec_none, 0))
|
|
{
|
|
U_INTERNAL_DUMP("hFile[%d,%d] = { %p, %p }", fdn, fdn+1, hFile[fdn], hFile[fdn+1])
|
|
}
|
|
else
|
|
{
|
|
U_INTERNAL_DUMP("$R", "CreatePipe()")
|
|
}
|
|
}
|
|
#else
|
|
void UProcess::pipe(int fdp)
|
|
{
|
|
U_TRACE(1, "UProcess::pipe(%d)", fdp)
|
|
|
|
// pipe() creates a pair of file descriptors, pointing to a pipe inode, and places them in the array pointed to by fds.
|
|
|
|
U_INTERNAL_ASSERT_RANGE(STDIN_FILENO, fdp, STDERR_FILENO)
|
|
|
|
int* fds = filedes + (fdp * 2); // fds[0] is for READING, fds[1] is for WRITING
|
|
|
|
# ifndef HAVE_PIPE2
|
|
(void) U_SYSCALL(pipe, "%p", fds);
|
|
# else
|
|
(void) U_SYSCALL(pipe2, "%p,%d", fds, O_CLOEXEC);
|
|
# endif
|
|
|
|
U_INTERNAL_DUMP("filedes[%d,%d] = { %d, %d }", (fdp * 2), (fdp * 2) + 1, fds[0], fds[1])
|
|
}
|
|
#endif
|
|
|
|
#ifdef _MSWINDOWS_
|
|
static inline BOOL is_console(HANDLE h) { return h != INVALID_HANDLE_VALUE && ((ULONG_PTR)h & 3) == 3; }
|
|
|
|
pid_t UProcess::execute(const char* pathname, char* argv[], char* envp[], bool fd_stdin, bool fd_stdout, bool fd_stderr)
|
|
{
|
|
U_TRACE(1, "UProcess::execute(%S,%p,%p,%b,%b,%b)", pathname, argv, envp, fd_stdin, fd_stdout, fd_stderr)
|
|
|
|
U_INTERNAL_ASSERT_POINTER(argv)
|
|
U_DUMP_EXEC(argv, envp)
|
|
U_INTERNAL_ASSERT_EQUALS(strcmp(argv[0], u_basename(pathname, u__strlen(pathname, __PRETTY_FUNCTION__))), 0)
|
|
|
|
(void) U_SYSCALL(memset, "%p,%d,%lu", &aStartupInfo, 0, sizeof(STARTUPINFO));
|
|
(void) U_SYSCALL(memset, "%p,%d,%lu", &aProcessInformation, 0, sizeof(PROCESS_INFORMATION));
|
|
|
|
/**
|
|
* typedef struct _STARTUPINFO {
|
|
* DWORD cb; // Size of the structure, in bytes
|
|
* LPTSTR lpReserved;
|
|
* LPTSTR lpDesktop;
|
|
* LPTSTR lpTitle;
|
|
* DWORD dwX;
|
|
* DWORD dwY;
|
|
* DWORD dwXSize;
|
|
* DWORD dwYSize;
|
|
* DWORD dwXCountChars;
|
|
* DWORD dwYCountChars;
|
|
* DWORD dwFillAttribute;
|
|
* DWORD dwFlags;
|
|
* WORD wShowWindow;
|
|
* WORD cbReserved2;
|
|
* LPBYTE lpReserved2;
|
|
* HANDLE hStdInput;
|
|
* HANDLE hStdOutput;
|
|
* HANDLE hStdError;
|
|
* } STARTUPINFO, *LPSTARTUPINFO;
|
|
*/
|
|
|
|
aStartupInfo.cb = sizeof(STARTUPINFO);
|
|
aStartupInfo.dwFlags = STARTF_USESHOWWINDOW;
|
|
aStartupInfo.wShowWindow = SW_SHOWNORMAL;
|
|
|
|
// STARTF_USESTDHANDLES - Sets the standard input, standard output, and standard error handles for the process
|
|
// to the handles specified in the hStdInput, hStdOutput, and hStdError members of the STARTUPINFO structure.
|
|
// For this to work properly, the handles must be inheritable and the CreateProcess function's fInheritHandles
|
|
// parameter must be set to TRUE. If this value is not specified, the hStdInput, hStdOutput, and hStdError
|
|
// members of the STARTUPINFO structure are ignored
|
|
|
|
if (fd_stdin || fd_stdout || fd_stderr)
|
|
{
|
|
aStartupInfo.dwFlags |= STARTF_USESTDHANDLES; // Tell the new process to use our std handles
|
|
|
|
setStdInOutErr(fd_stdin, fd_stdout, fd_stderr);
|
|
}
|
|
|
|
aStartupInfo.hStdInput = (fd_stdin ? hChildIn : GetStdHandle(STD_INPUT_HANDLE));
|
|
aStartupInfo.hStdOutput = (fd_stdout ? hChildOut : GetStdHandle(STD_OUTPUT_HANDLE));
|
|
aStartupInfo.hStdError = (fd_stderr ? hChildErr : GetStdHandle(STD_ERROR_HANDLE));
|
|
|
|
U_INTERNAL_DUMP("hStdInput(%b) = %p hStdOutput(%b) = %p hStdError(%b) = %p", is_console(aStartupInfo.hStdInput), aStartupInfo.hStdInput,
|
|
is_console(aStartupInfo.hStdOutput), aStartupInfo.hStdOutput,
|
|
is_console(aStartupInfo.hStdError), aStartupInfo.hStdError)
|
|
|
|
char* w32_shell = (strncmp(argv[0], U_CONSTANT_TO_PARAM("sh.exe") == 0) ? (char*)pathname : 0);
|
|
|
|
U_INTERNAL_DUMP("w32_shell = %S", w32_shell)
|
|
|
|
int index = 0;
|
|
char buffer1[4096];
|
|
char* w32_cmd = (char*)pathname;
|
|
|
|
if (argv[1])
|
|
{
|
|
bool flag;
|
|
w32_cmd = buffer1;
|
|
int len = u__strlen(pathname, __PRETTY_FUNCTION__);
|
|
|
|
if (len)
|
|
{
|
|
index = len;
|
|
|
|
U_MEMCPY(w32_cmd, pathname, len);
|
|
}
|
|
|
|
for (int i = 1; argv[i]; ++i)
|
|
{
|
|
w32_cmd[index++] = ' ';
|
|
|
|
len = u__strlen(argv[i], __PRETTY_FUNCTION__);
|
|
|
|
if (len)
|
|
{
|
|
U_INTERNAL_ASSERT_MINOR(index+len+3, 4096)
|
|
|
|
flag = (strchr(argv[i], ' ') != 0);
|
|
|
|
if (flag) w32_cmd[index++] = '"';
|
|
|
|
U_MEMCPY(w32_cmd+index, argv[i], len);
|
|
|
|
index += len;
|
|
|
|
if (flag) w32_cmd[index++] = '"';
|
|
}
|
|
}
|
|
|
|
w32_cmd[index] = '\0';
|
|
}
|
|
|
|
U_INTERNAL_DUMP("w32_cmd(%d) = %.*S", index, index, w32_cmd)
|
|
|
|
char* w32_envp = 0;
|
|
char buffer2[32000] = { '\0' };
|
|
|
|
if (envp)
|
|
{
|
|
index = 0;
|
|
w32_envp = buffer2;
|
|
|
|
for (int len, i = 0; envp[i]; ++i, ++index)
|
|
{
|
|
len = u__strlen(envp[i], __PRETTY_FUNCTION__);
|
|
|
|
if (len)
|
|
{
|
|
U_INTERNAL_ASSERT_MINOR(index+len+1, 32000)
|
|
|
|
U_MEMCPY(w32_envp+index, envp[i], len);
|
|
|
|
index += len;
|
|
|
|
w32_envp[index] = '\0';
|
|
}
|
|
}
|
|
|
|
w32_envp[index+1] = '\0';
|
|
|
|
U_INTERNAL_DUMP("w32_envp(%d) = %#.*S", index, index+1, w32_envp)
|
|
}
|
|
|
|
pid_t pid;
|
|
BOOL fRet = U_SYSCALL(CreateProcessA, "%S,%S,%p,%p,%b,%lu,%p,%p,%p,%p",
|
|
w32_shell, // No module name (use command line)
|
|
w32_cmd, // Command line
|
|
&sec_none, // Default process security attributes
|
|
&sec_none, // Default thread security attributes
|
|
sec_none.bInheritHandle, // inherit handles from the parent
|
|
DETACHED_PROCESS, // the new process does not inherit its parent's console
|
|
w32_envp, // if NULL use the same environment as the parent
|
|
0, // Launch in the current directory
|
|
&aStartupInfo, // Startup Information
|
|
&aProcessInformation); // Process information stored upon return
|
|
|
|
if (fRet)
|
|
{
|
|
/**
|
|
* typedef struct _PROCESS_INFORMATION {
|
|
* HANDLE hProcess;
|
|
* HANDLE hThread;
|
|
* DWORD dwProcessId;
|
|
* DWORD dwThreadId;
|
|
* } PROCESS_INFORMATION;
|
|
*/
|
|
|
|
U_INTERNAL_DUMP("dwProcessId = %p hProcess = %p hThread = %p", aProcessInformation.dwProcessId,
|
|
aProcessInformation.hProcess,
|
|
aProcessInformation.hThread)
|
|
|
|
u_hProcess = aProcessInformation.hProcess;
|
|
|
|
pid = (pid_t) aProcessInformation.dwProcessId;
|
|
|
|
(void) U_SYSCALL(CloseHandle, "%p", aProcessInformation.hThread);
|
|
}
|
|
else
|
|
{
|
|
U_INTERNAL_DUMP("$R", "CreateProcess()")
|
|
|
|
pid = -1;
|
|
}
|
|
|
|
/* associate handle to filedes */
|
|
|
|
if (fd_stdin && hFile[1])
|
|
{
|
|
filedes[0] = _open_osfhandle((long)hChildIn, (_O_RDONLY | O_BINARY));
|
|
filedes[1] = _open_osfhandle((long)hFile[1], (_O_WRONLY | O_BINARY));
|
|
|
|
U_INTERNAL_DUMP("filedes[0,1] = { %d, %d }", filedes[0], filedes[1])
|
|
}
|
|
|
|
if (fd_stdout && hFile[2])
|
|
{
|
|
filedes[2] = _open_osfhandle((long)hFile[2], (_O_RDONLY | O_BINARY));
|
|
filedes[3] = _open_osfhandle((long)hChildOut, (_O_WRONLY | O_BINARY));
|
|
|
|
U_INTERNAL_DUMP("filedes[2,3] = { %d, %d }", filedes[2], filedes[3])
|
|
}
|
|
|
|
if (fd_stderr && hFile[4])
|
|
{
|
|
filedes[4] = _open_osfhandle((long)hFile[4], (_O_RDONLY | O_BINARY));
|
|
filedes[5] = _open_osfhandle((long)hChildErr, (_O_WRONLY | O_BINARY));
|
|
|
|
U_INTERNAL_DUMP("filedes[4,5] = { %d, %d }", filedes[4], filedes[5])
|
|
}
|
|
|
|
hChildIn = hChildOut = hChildErr = 0;
|
|
|
|
(void) U_SYSCALL(memset, "%p,%d,%lu", hFile, 0, sizeof(hFile));
|
|
|
|
U_RETURN(pid);
|
|
}
|
|
#else
|
|
pid_t UProcess::execute(const char* pathname, char* argv[], char* envp[], bool fd_stdin, bool fd_stdout, bool fd_stderr)
|
|
{
|
|
U_TRACE(1, "UProcess::execute(%S,%p,%p,%b,%b,%b)", pathname, argv, envp, fd_stdin, fd_stdout, fd_stderr)
|
|
|
|
U_INTERNAL_ASSERT_POINTER(argv)
|
|
U_DUMP_EXEC(argv, envp)
|
|
U_INTERNAL_ASSERT_EQUALS(strcmp(argv[0], u_basename(pathname, u__strlen(pathname, __PRETTY_FUNCTION__))), 0)
|
|
|
|
pid_t pid;
|
|
|
|
# ifdef HAVE_POSIX_SPAWN
|
|
posix_spawn_file_actions_t action;
|
|
|
|
(void) U_SYSCALL(posix_spawn_file_actions_init, "%p", &action);
|
|
|
|
// check if we write to STDIN
|
|
|
|
if (fd_stdin)
|
|
{
|
|
U_INTERNAL_ASSERT_MAJOR(filedes[0], STDERR_FILENO)
|
|
|
|
(void) U_SYSCALL(posix_spawn_file_actions_adddup2, "%p,%d,%d", &action, filedes[0], STDIN_FILENO);
|
|
|
|
(void) U_SYSCALL(posix_spawn_file_actions_addclose, "%p,%d", &action, filedes[0]);
|
|
if (filedes[1] > STDERR_FILENO) (void) U_SYSCALL(posix_spawn_file_actions_addclose, "%p,%d", &action, filedes[1]);
|
|
}
|
|
|
|
// check if we read from STDOUT
|
|
|
|
if (fd_stdout)
|
|
{
|
|
U_INTERNAL_ASSERT_MAJOR(filedes[3], STDOUT_FILENO)
|
|
|
|
(void) U_SYSCALL(posix_spawn_file_actions_adddup2, "%p,%d,%d", &action, filedes[3], STDOUT_FILENO);
|
|
|
|
(void) U_SYSCALL(posix_spawn_file_actions_addclose, "%p,%d", &action, filedes[3]);
|
|
if (filedes[2] > STDERR_FILENO) (void) U_SYSCALL(posix_spawn_file_actions_addclose, "%p,%d", &action, filedes[2]);
|
|
}
|
|
|
|
// check if we read from STDERR
|
|
|
|
if (fd_stderr)
|
|
{
|
|
U_INTERNAL_ASSERT(filedes[5] >= STDIN_FILENO)
|
|
|
|
(void) U_SYSCALL(posix_spawn_file_actions_adddup2, "%p,%d,%d", &action, filedes[5], STDERR_FILENO);
|
|
|
|
(void) U_SYSCALL(posix_spawn_file_actions_addclose, "%p,%d", &action, filedes[5]);
|
|
if (filedes[4] > STDERR_FILENO) (void) U_SYSCALL(posix_spawn_file_actions_addclose, "%p,%d", &action, filedes[4]);
|
|
}
|
|
|
|
(void) U_SYSCALL(posix_spawn, "%p,%S,%p,%p,%p,%p", &pid, pathname, &action, U_NULLPTR, argv, envp);
|
|
|
|
(void) U_SYSCALL(posix_spawn_file_actions_destroy, "%p", &action);
|
|
# else
|
|
pid = U_VFORK();
|
|
|
|
if (pid == 0) // child
|
|
{
|
|
setStdInOutErr(fd_stdin, fd_stdout, fd_stderr);
|
|
|
|
U_EXEC(pathname, argv, envp);
|
|
}
|
|
|
|
// parent
|
|
|
|
if (u_exec_failed) U_RETURN(-1);
|
|
# endif
|
|
|
|
U_RETURN(pid);
|
|
}
|
|
#endif
|
|
|
|
/**
|
|
* The wait() system call suspends execution of the calling process until one of its children terminates.
|
|
* The call wait(&status) is equivalent to:
|
|
*
|
|
* waitpid(-1, &status, 0);
|
|
*
|
|
* The waitpid() system call suspends execution of the calling process until a child specified by pid argument has changed state.
|
|
* By default, waitpid() waits only for terminated children, but this behavior is modifiable via the options argument, as described below.
|
|
*
|
|
* The value of pid can be:
|
|
*
|
|
* < -1 meaning wait for any child process whose process group ID is equal to the absolute value of pid.
|
|
* -1 meaning wait for any child process.
|
|
* 0 meaning wait for any child process whose process group ID is equal to that of the calling process.
|
|
* > 0 meaning wait for the child whose process ID is equal to the value of pid.
|
|
*
|
|
* The value of options is an OR of zero or more of the following constants:
|
|
*
|
|
* WNOHANG(1) return immediately if no child has exited.
|
|
* WUNTRACED(2) also return if a child has stopped (but not traced via ptrace(2)).
|
|
* Status for traced children which have stopped is provided even if this option is not specified.
|
|
* WCONTINUED(8) (since Linux 2.6.10) also return if a stopped child has been resumed by delivery of SIGCONT
|
|
*/
|
|
|
|
int UProcess::waitpid(pid_t pid, int* _status, int options)
|
|
{
|
|
U_TRACE(1, "UProcess::waitpid(%d,%p,%d)", pid, _status, options)
|
|
|
|
int result;
|
|
|
|
loop:
|
|
result = U_SYSCALL(waitpid, "%d,%p,%d", pid, _status, options);
|
|
|
|
if (result == -1 &&
|
|
errno == EINTR)
|
|
{
|
|
UInterrupt::checkForEventSignalPending();
|
|
|
|
if (UInterrupt::isSysCallToRestart()) goto loop;
|
|
}
|
|
|
|
// errno == ECHILD: if the process specified in pid does not exist or is not a child of the calling process...
|
|
|
|
#if DEBUG
|
|
if (_status) U_INTERNAL_DUMP("status = %d", *_status)
|
|
#endif
|
|
|
|
U_RETURN(result);
|
|
}
|
|
|
|
int UProcess::waitAll(int timeoutMS)
|
|
{
|
|
U_TRACE(1, "UProcess::waitAll(%d)", timeoutMS)
|
|
|
|
U_INTERNAL_DUMP("Call waitAll(%2D)")
|
|
|
|
#ifdef DEBUG
|
|
char buffer[128];
|
|
#endif
|
|
int lpid, result = U_FAILED_NONE;
|
|
|
|
if (timeoutMS) UInterrupt::setAlarm(timeoutMS);
|
|
|
|
while ((lpid = UProcess::waitpid(-1, &status, 0)) > 0)
|
|
{
|
|
if ((status == 0 && result == U_FAILED_ALL) || // (status == 0) -> success
|
|
(status != 0 && result == U_FAILED_NONE)) // (status != 0) -> failed
|
|
{
|
|
result = U_FAILED_SOME;
|
|
}
|
|
|
|
U_DUMP("result = %b status = %d, %S", result, status, exitInfo(buffer))
|
|
}
|
|
|
|
U_INTERNAL_DUMP("Return waitAll(%2D)")
|
|
|
|
if (timeoutMS)
|
|
{
|
|
if (lpid == -1 &&
|
|
errno == EINTR &&
|
|
UInterrupt::flag_alarm)
|
|
{
|
|
U_DEBUG("UProcess::waitAll(%d): alarm expired", timeoutMS);
|
|
|
|
U_RETURN(U_FAILED_ALARM);
|
|
}
|
|
|
|
UInterrupt::resetAlarm();
|
|
}
|
|
|
|
U_RETURN(result);
|
|
}
|
|
|
|
char* UProcess::exitInfo(char* buffer, int _status)
|
|
{
|
|
U_TRACE(0, "UProcess::exitInfo(%p,%d)", buffer, _status)
|
|
|
|
uint32_t n = 0;
|
|
|
|
if (WIFEXITED(_status))
|
|
{
|
|
n = u__snprintf(buffer, 128, U_CONSTANT_TO_PARAM("Exit %d"), WEXITSTATUS(_status));
|
|
}
|
|
else if (WIFSIGNALED(_status))
|
|
{
|
|
# ifndef WCOREDUMP
|
|
# define WCOREDUMP(status) ((status) & 0200) // settimo bit
|
|
# endif
|
|
n = u__snprintf(buffer, 128, U_CONSTANT_TO_PARAM("Signal %Y%s"), WTERMSIG(_status), (WCOREDUMP(_status) ? " - core dumped" : ""));
|
|
}
|
|
else if (WIFSTOPPED(_status))
|
|
{
|
|
n = u__snprintf(buffer, 128, U_CONSTANT_TO_PARAM("Signal %Y"), WSTOPSIG(_status));
|
|
}
|
|
# ifdef __clang__
|
|
# undef WIFCONTINUED // to avoid warning: equality comparison with extraneous parentheses...
|
|
# endif
|
|
# ifndef WIFCONTINUED
|
|
# define WIFCONTINUED(status) status == 0xffff
|
|
# endif
|
|
else if (WIFCONTINUED(_status))
|
|
{
|
|
U_MEMCPY(buffer, "SIGCONT", (n = U_CONSTANT_SIZE("SIGCONT")));
|
|
}
|
|
|
|
U_INTERNAL_ASSERT_MINOR(n, 128)
|
|
|
|
buffer[n] = '\0';
|
|
|
|
U_RETURN(buffer);
|
|
}
|
|
|
|
// DEBUG
|
|
|
|
#if defined(U_STDCPP_ENABLE) && defined(DEBUG)
|
|
const char* UProcess::dump(bool reset) const
|
|
{
|
|
*UObjectIO::os << "pid " << _pid << '\n'
|
|
<< "status " << status << '\n'
|
|
<< "running " << running;
|
|
|
|
if (reset)
|
|
{
|
|
UObjectIO::output();
|
|
|
|
return UObjectIO::buffer_output;
|
|
}
|
|
|
|
return U_NULLPTR;
|
|
}
|
|
#endif
|