static void ChildStatusProc()

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);
		}
	}
}