in main/sal/osl/unx/process.c [424:672]
static void ChildStatusProc(void *pData)
{
pid_t pid = -1;
int status = 0;
int channel[2];
ProcessData data;
ProcessData *pdata;
int stdOutput[2] = { -1, -1 }, stdInput[2] = { -1, -1 }, stdError[2] = { -1, -1 };
pdata = (ProcessData *)pData;
/* make a copy of our data, because forking will only copy
our local stack of the thread, so the process data will not be accessible
in our child process */
memcpy(&data, pData, sizeof(data));
if (socketpair(AF_UNIX, SOCK_STREAM, 0, channel) == -1)
status = errno;
fcntl(channel[0], F_SETFD, FD_CLOEXEC);
fcntl(channel[1], F_SETFD, FD_CLOEXEC);
/* Create redirected IO pipes */
if ( status == 0 && data.m_pInputWrite )
if (pipe( stdInput ) == -1)
status = errno;
if ( status == 0 && data.m_pOutputRead )
if (pipe( stdOutput ) == -1)
status = errno;
if ( status == 0 && data.m_pErrorRead )
if (pipe( stdError ) == -1)
status = errno;
if ( (status == 0) && ((pid = fork()) == 0) )
{
/* Child */
int chstatus = 0;
sal_Int32 nWrote;
if (channel[0] != -1) close(channel[0]);
if ((data.m_uid != (uid_t)-1) && ((data.m_uid != getuid()) || (data.m_gid != getgid())))
{
OSL_ASSERT(geteuid() == 0); /* must be root */
if (! INIT_GROUPS(data.m_name, data.m_gid) || (setuid(data.m_uid) != 0))
OSL_TRACE("Failed to change uid and guid, errno=%d (%s)\n", errno, strerror(errno));
#if defined(LINUX) || defined (FREEBSD)
unsetenv("HOME");
#else
putenv("HOME=");
#endif
}
if (data.m_pszDir)
chstatus = chdir(data.m_pszDir);
if (chstatus == 0 && ((data.m_uid == (uid_t)-1) || ((data.m_uid == getuid()) && (data.m_gid == getgid()))))
{
int i;
for (i = 0; data.m_pszEnv[i] != NULL; i++)
{
if (strchr(data.m_pszEnv[i], '=') == NULL)
{
unsetenv(data.m_pszEnv[i]); /*TODO: check error return*/
}
else
{
putenv(data.m_pszEnv[i]); /*TODO: check error return*/
}
}
OSL_TRACE("ChildStatusProc : starting '%s'",data.m_pszArgs[0]);
/* Connect std IO to pipe ends */
/* Write end of stdInput not used in child process */
if (stdInput[1] != -1) close( stdInput[1] );
/* Read end of stdOutput not used in child process */
if (stdOutput[0] != -1) close( stdOutput[0] );
/* Read end of stdError not used in child process */
if (stdError[0] != -1) close( stdError[0] );
/* Redirect pipe ends to std IO */
if ( stdInput[0] != STDIN_FILENO )
{
dup2( stdInput[0], STDIN_FILENO );
if (stdInput[0] != -1) close( stdInput[0] );
}
if ( stdOutput[1] != STDOUT_FILENO )
{
dup2( stdOutput[1], STDOUT_FILENO );
if (stdOutput[1] != -1) close( stdOutput[1] );
}
if ( stdError[1] != STDERR_FILENO )
{
dup2( stdError[1], STDERR_FILENO );
if (stdError[1] != -1) close( stdError[1] );
}
pid=execv(data.m_pszArgs[0], (sal_Char **)data.m_pszArgs);
}
OSL_TRACE("Failed to exec, errno=%d (%s)\n", errno, strerror(errno));
OSL_TRACE("ChildStatusProc : starting '%s' failed",data.m_pszArgs[0]);
/* if we reach here, something went wrong */
nWrote = write(channel[1], &errno, sizeof(errno));
if (nWrote != sizeof(errno))
OSL_TRACE("sendFdPipe : sending failed (%s)",strerror(errno));
if (channel[1] != -1) close(channel[1]);
_exit(255);
}
else
{ /* Parent */
int i = -1;
if (channel[1] != -1) close(channel[1]);
/* Close unused pipe ends */
if (stdInput[0] != -1) close( stdInput[0] );
if (stdOutput[1] != -1) close( stdOutput[1] );
if (stdError[1] != -1) close( stdError[1] );
if (pid > 0)
{
while (((i = read(channel[0], &status, sizeof(status))) < 0))
{
if (errno != EINTR)
break;
}
}
if (channel[0] != -1) close(channel[0]);
if ((pid > 0) && (i == 0))
{
pid_t child_pid;
osl_acquireMutex(ChildListMutex);
pdata->m_pProcImpl->m_pid = pid;
pdata->m_pProcImpl->m_pnext = ChildList;
ChildList = pdata->m_pProcImpl;
/* Store used pipe ends in data structure */
if ( pdata->m_pInputWrite )
*(pdata->m_pInputWrite) = osl_createFileHandleFromFD( stdInput[1] );
if ( pdata->m_pOutputRead )
*(pdata->m_pOutputRead) = osl_createFileHandleFromFD( stdOutput[0] );
if ( pdata->m_pErrorRead )
*(pdata->m_pErrorRead) = osl_createFileHandleFromFD( stdError[0] );
osl_releaseMutex(ChildListMutex);
osl_setCondition(pdata->m_started);
do
{
child_pid = waitpid(pid, &status, 0);
} while ( 0 > child_pid && EINTR == errno );
if ( child_pid < 0)
{
OSL_TRACE("Failed to wait for child process, errno=%d (%s)\n", errno, strerror(errno));
/*
We got an other error than EINTR. Anyway we have to wake up the
waiting thread under any circumstances */
child_pid = pid;
}
if ( child_pid > 0 )
{
oslProcessImpl* pChild;
osl_acquireMutex(ChildListMutex);
pChild = ChildList;
/* check if it is one of our child processes */
while (pChild != NULL)
{
if (pChild->m_pid == child_pid)
{
if (WIFEXITED(status))
pChild->m_status = WEXITSTATUS(status);
else
pChild->m_status = -1;
osl_setCondition(pChild->m_terminated);
}
pChild = pChild->m_pnext;
}
osl_releaseMutex(ChildListMutex);
}
}
else
{
OSL_TRACE("ChildStatusProc : starting '%s' failed",data.m_pszArgs[0]);
OSL_TRACE("Failed to launch child process, child reports errno=%d (%s)\n", status, strerror(status));
/* Close pipe ends */
if ( pdata->m_pInputWrite )
*pdata->m_pInputWrite = NULL;
if ( pdata->m_pOutputRead )
*pdata->m_pOutputRead = NULL;
if ( pdata->m_pErrorRead )
*pdata->m_pErrorRead = NULL;
if (stdInput[1] != -1) close( stdInput[1] );
if (stdOutput[0] != -1) close( stdOutput[0] );
if (stdError[0] != -1) close( stdError[0] );
//if pid > 0 then a process was created, even if it later failed
//e.g. bash searching for a command to execute, and we still
//need to clean it up to avoid "defunct" processes
if (pid > 0)
{
pid_t child_pid;
do
{
child_pid = waitpid(pid, &status, 0);
} while ( 0 > child_pid && EINTR == errno );
}
/* notify (and unblock) parent thread */
osl_setCondition(pdata->m_started);
}
}
}