/**************************************************************
 * 
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 * 
 *************************************************************/




/*
 *   ToDo:
 *      - cleanup of process status things
 *      - cleanup of process spawning
 *      - cleanup of resource transfer
 */

#if defined(SOLARIS)
  // The procfs may only be used without LFS in 32bits.
# ifdef _FILE_OFFSET_BITS     
#   undef   _FILE_OFFSET_BITS
# endif
#endif


#ifdef FREEBSD
#include <machine/param.h>
#endif

#include "system.h"
#if defined(SOLARIS)
# include <sys/procfs.h>
#endif
#include <osl/diagnose.h>
#include <osl/mutex.h>

#ifndef _OSL_CONDITN_H_
#include <osl/conditn.h>
#endif
#include <osl/thread.h>
#include <osl/file.h>
#include <osl/signal.h>
#include <rtl/alloc.h>

#include <grp.h>

#include "procimpl.h"
#include "sockimpl.h"
#include "secimpl.h"


#define MAX_ARGS        255
#define MAX_ENVS        255

#if defined(MACOSX) || defined(IORESOURCE_TRANSFER_BSD)
#define CONTROLLEN (sizeof(struct cmsghdr) + sizeof(int))
#endif

/* implemented in file.c */
extern oslFileError FileURLToPath( char *, size_t, rtl_uString* );
extern oslFileHandle osl_createFileHandleFromFD( int fd );

/******************************************************************************
 *
 *                  Data Type Definition
 *
 ******************************************************************************/

typedef struct {
	int  m_hPipe;
	int  m_hConn;
	sal_Char m_Name[PATH_MAX + 1];
} Pipe;

typedef struct {
	const sal_Char*  m_pszArgs[MAX_ARGS + 1];
	oslProcessOption m_options;
	const sal_Char*  m_pszDir;
	sal_Char*        m_pszEnv[MAX_ENVS + 1];
	uid_t            m_uid;
	gid_t 		     m_gid;
	sal_Char* 		 m_name;
	oslCondition     m_started;
	oslProcessImpl*  m_pProcImpl;
	oslFileHandle	 *m_pInputWrite;
	oslFileHandle	 *m_pOutputRead;
	oslFileHandle	 *m_pErrorRead;
} ProcessData;

typedef struct _oslPipeImpl {
    int      m_Socket;
    sal_Char m_Name[PATH_MAX + 1];
} oslPipeImpl;


/******************************************************************************
 *
 *                  Function Declarations
 *
 *****************************************************************************/

oslProcessError SAL_CALL osl_psz_executeProcess(sal_Char *pszImageName,
                                                sal_Char *pszArguments[],
                                                oslProcessOption Options,
                                                oslSecurity Security,
                                                sal_Char *pszDirectory,
                                                sal_Char *pszEnvironments[],
                                                oslProcess *pProcess,
												oslFileHandle *pInputWrite,
												oslFileHandle *pOutputRead,
												oslFileHandle *pErrorRead );

												
oslProcessError SAL_CALL osl_searchPath_impl(
	const sal_Char* pszName, 
	const sal_Char* pszPath, 
	sal_Char Separator, 
	sal_Char *pszBuffer, 
	sal_uInt32 Max);


sal_Bool osl_getFullPath(const sal_Char* pszFilename, sal_Char* pszPath, sal_uInt32 MaxLen);

static oslProcessImpl* ChildList;
static oslMutex        ChildListMutex;

/******************************************************************************
 Deprecated
 Old and buggy implementation of osl_searchPath used only by
 osl_psz_executeProcess.
 A new implemenation is in file_path_helper.cxx
 *****************************************************************************/

oslProcessError SAL_CALL osl_searchPath_impl(const sal_Char* pszName, const sal_Char* pszPath,
			       sal_Char Separator, sal_Char *pszBuffer, sal_uInt32 Max)
{
	sal_Char path[PATH_MAX + 1];
	sal_Char *pchr;

    path[0] = '\0';
    
	OSL_ASSERT(pszName != NULL);

	if ( pszName == 0 )
	{
		return osl_Process_E_NotFound;
	}

	if (pszPath == NULL)
		pszPath = "PATH";

	if (Separator == '\0')
		Separator = ':';


	if ( (pchr = getenv(pszPath)) != 0 )
	{
		sal_Char *pstr;

		while (*pchr != '\0')
		{
			pstr = path;

			while ((*pchr != '\0') && (*pchr != Separator))
				*pstr++ = *pchr++;

			if ((pstr > path) && ((*(pstr - 1) != '/')))
				*pstr++ = '/';

			*pstr = '\0';

			strcat(path, pszName);

			if (access(path, 0) == 0)
			{
                char szRealPathBuf[PATH_MAX] = "";
                
				if( NULL == realpath(path, szRealPathBuf) || (strlen(szRealPathBuf) >= (sal_uInt32)Max))
					return osl_Process_E_Unknown;

				strcpy(pszBuffer, path);

				return osl_Process_E_None;
			}

			if (*pchr == Separator)
				pchr++;
		}
	}

	return osl_Process_E_NotFound;
}

/******************************************************************************
 *
 *                  New io resource transfer functions
 *
 *****************************************************************************/


/**********************************************
 sendFdPipe
 *********************************************/
 
static sal_Bool sendFdPipe(int PipeFD, int SocketFD)
{
    sal_Bool bRet = sal_False;

    struct iovec	iov[1];
    struct msghdr	msg;
    char			buf[2];	/* send_fd()/recv_fd() 2-byte protocol */
    int nSend;
    int RetCode=0;
    
#if defined(IOCHANNEL_TRANSFER_BSD)

    OSL_TRACE("IOCHANNEL_TRANSFER_BSD send");
/*      OSL_TRACE("sending fd %i\n",SocketFD); */
    
    iov[0].iov_base = buf;
    iov[0].iov_len  = sizeof(buf);
    msg.msg_iov     = iov;
    msg.msg_iovlen  = 1;
    msg.msg_name    = NULL;
    msg.msg_namelen = 0;

    msg.msg_accrights    = (caddr_t) &SocketFD;	/* addr of descriptor */
    msg.msg_accrightslen = sizeof(int);		/* pass 1 descriptor */
    buf[1] = 0;								/* zero status means OK */
    buf[0] = 0;								/* null byte flag to recv_fd() */
        
#else
    
    struct cmsghdr* cmptr = (struct cmsghdr*)malloc(CONTROLLEN);

    OSL_TRACE("!!!!!! IOCHANNEL_TRANSFER_BSD_RENO send");
/*      OSL_TRACE("sending fd %i\n",SocketFD); */

    iov[0].iov_base = buf;
    iov[0].iov_len = sizeof(buf);
    msg.msg_iov = iov;
    msg.msg_iovlen = 1;
    msg.msg_name = NULL;
    msg.msg_namelen = 0;
    msg.msg_control = (caddr_t) cmptr;
    msg.msg_controllen = CONTROLLEN;

    cmptr->cmsg_level = SOL_SOCKET;
    cmptr->cmsg_type = SCM_RIGHTS;
    cmptr->cmsg_len = CONTROLLEN;
    memcpy(CMSG_DATA(cmptr), &SocketFD, sizeof(int));

#endif
    
    if ( ( nSend = sendmsg(PipeFD, &msg, 0) ) > 0 )
    {
        bRet = sal_True;
        OSL_TRACE("sendFdPipe : send '%i' bytes\n",nSend);

    }
    else
    {
        OSL_TRACE("sendFdPipe : sending failed (%s)",strerror(errno));
    }

    nSend=read(PipeFD,&RetCode,sizeof(RetCode));

    if ( nSend > 0 && RetCode == 1 )
    {
        OSL_TRACE("sendFdPipe : resource was received\n");
    }
    else
    {
        OSL_TRACE("sendFdPipe : resource wasn't received\n");        
    }    
    
#if defined(IOCHANNEL_TRANSFER_BSD_RENO)
    free(cmptr);
#endif

    return bRet;
}

/**********************************************
 receiveFdPipe
 *********************************************/
 
static oslSocket receiveFdPipe(int PipeFD)
{
    oslSocket pSocket = 0;
    struct msghdr msghdr;
    struct iovec iov[1];
    char buffer[2];
    sal_Int32 nRead;
    int newfd=-1;
    int nRetCode=0;
/*      char *ptr; */

#if defined(IOCHANNEL_TRANSFER_BSD)
    
    OSL_TRACE("IOCHANNEL_TRANSFER_BSD receive\n");
    
    iov[0].iov_base = buffer;
    iov[0].iov_len = sizeof(buffer);
    msghdr.msg_name = NULL;
    msghdr.msg_namelen = 0;
    msghdr.msg_iov = iov;
    msghdr.msg_iovlen = 1;
    msghdr.msg_accrights = (caddr_t) &newfd; /* addr of descriptor   */
    msghdr.msg_accrightslen = sizeof(int);	 /* receive 1 descriptor */
    
#else
    struct cmsghdr* cmptr = (struct cmsghdr*)malloc(CONTROLLEN);

    OSL_TRACE(" !!!! IOCHANNEL_TRANSFER_BSD_RENO receive");
    
    iov[0].iov_base = buffer;
    iov[0].iov_len = sizeof(buffer);
    msghdr.msg_name = NULL;
    msghdr.msg_namelen = 0;
    msghdr.msg_iov = iov;
    msghdr.msg_iovlen = 1;

    msghdr.msg_control = (caddr_t) cmptr;
    msghdr.msg_controllen = CONTROLLEN;

#endif
    

#if defined(IOCHANNEL_TRANSFER_BSD)
    
    if ( ( nRead = recvmsg(PipeFD, &msghdr, 0) ) > 0 )
    {
        OSL_TRACE("receiveFdPipe : received '%i' bytes\n",nRead);
    }
#else

    if ( ( ( nRead = recvmsg(PipeFD, &msghdr, 0) ) > 0 ) &&
         ( msghdr.msg_controllen == CONTROLLEN ) )
    {
        OSL_TRACE("receiveFdPipe : received '%i' bytes\n",nRead);
        memcpy(&newfd, CMSG_DATA(cmptr), sizeof(int));
    }
#endif
    else
    {
        OSL_TRACE("receiveFdPipe : receiving failed (%s)",strerror(errno));
    }
    
    if ( newfd >= 0 )
    {
        pSocket = __osl_createSocketImpl(newfd);
        nRetCode=1;        
        OSL_TRACE("received fd %i\n",newfd);
    }

    OSL_TRACE("receiveFdPipe : writing back %i",nRetCode);
    nRead=write(PipeFD,&nRetCode,sizeof(nRetCode));

#if defined(IOCHANNEL_TRANSFER_BSD_RENO)
    free(cmptr);
#endif

    return pSocket;
}

/**********************************************
 osl_sendResourcePipe
 *********************************************/
 
sal_Bool osl_sendResourcePipe(oslPipe pPipe, oslSocket pSocket)
{
    sal_Bool bRet = sal_False;

    if ( pSocket == 0 || pPipe == 0 )
    {
        return sal_False;
    }

    bRet = sendFdPipe(pPipe->m_Socket,pSocket->m_Socket);

    return bRet;
}

/**********************************************
 osl_receiveResourcePipe
 *********************************************/
 
oslSocket osl_receiveResourcePipe(oslPipe pPipe)
{
    oslSocket pSocket=0;

    if ( pPipe ==  0 )
    {
        return 0;
    }

    pSocket = receiveFdPipe(pPipe->m_Socket);

    return (oslSocket) pSocket;
}



/******************************************************************************
 *
 *                  Functions for starting a process
 *
 *****************************************************************************/

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

/**********************************************
 osl_executeProcess_WithRedirectedIO
 *********************************************/
 
oslProcessError SAL_CALL osl_executeProcess_WithRedirectedIO(
											rtl_uString *ustrImageName,
											rtl_uString *ustrArguments[],
                                            sal_uInt32   nArguments,
											oslProcessOption Options,
											oslSecurity Security,
											rtl_uString *ustrWorkDir,
											rtl_uString *ustrEnvironment[],
                                            sal_uInt32   nEnvironmentVars,
											oslProcess *pProcess,
											oslFileHandle	*pInputWrite,
											oslFileHandle	*pOutputRead,
											oslFileHandle	*pErrorRead
											)
{

    oslProcessError Error;
    sal_Char* pszWorkDir=0;
    sal_Char** pArguments=0;
    sal_Char** pEnvironment=0;
    unsigned int idx;
    
    char szImagePath[PATH_MAX] = "";
    char szWorkDir[PATH_MAX] = "";

    if ( ustrImageName && ustrImageName->length )
    {
        FileURLToPath( szImagePath, PATH_MAX, ustrImageName );
    }

    if ( ustrWorkDir != 0 && ustrWorkDir->length )
    {
        FileURLToPath( szWorkDir, PATH_MAX, ustrWorkDir );
        pszWorkDir = szWorkDir;
    }

    if ( pArguments == 0 && nArguments > 0 )
    {
        pArguments = (sal_Char**) malloc( ( nArguments + 2 ) * sizeof(sal_Char*) );
    }


    for ( idx = 0 ; idx < nArguments ; ++idx )
    {
        rtl_String* strArg =0;


        rtl_uString2String( &strArg,
                            rtl_uString_getStr(ustrArguments[idx]),
                            rtl_uString_getLength(ustrArguments[idx]),
                            osl_getThreadTextEncoding(),
                            OUSTRING_TO_OSTRING_CVTFLAGS );

        pArguments[idx]=strdup(rtl_string_getStr(strArg));
        rtl_string_release(strArg);
		pArguments[idx+1]=0;
    }

    for ( idx = 0 ; idx < nEnvironmentVars ; ++idx )
    {
        rtl_String* strEnv=0;

        if ( pEnvironment == 0 )
        {
            pEnvironment = (sal_Char**) malloc( ( nEnvironmentVars + 2 ) * sizeof(sal_Char*) );
        }

        rtl_uString2String( &strEnv,
                            rtl_uString_getStr(ustrEnvironment[idx]),
                            rtl_uString_getLength(ustrEnvironment[idx]),
                            osl_getThreadTextEncoding(),
                            OUSTRING_TO_OSTRING_CVTFLAGS );

        pEnvironment[idx]=strdup(rtl_string_getStr(strEnv));
        rtl_string_release(strEnv);
        pEnvironment[idx+1]=0;
    }


    Error = osl_psz_executeProcess(szImagePath,
                                   pArguments,
                                   Options,
                                   Security,
                                   pszWorkDir,
                                   pEnvironment,
                                   pProcess,
								   pInputWrite,
								   pOutputRead,
								   pErrorRead
								   );

    if ( pArguments != 0 )
    {
        for ( idx = 0 ; idx < nArguments ; ++idx )
        {
            if ( pArguments[idx] != 0 )
            {
                free(pArguments[idx]);
            }
        }
        free(pArguments);
    }

    if ( pEnvironment != 0 )
    {
        for ( idx = 0 ; idx < nEnvironmentVars ; ++idx )
        {
            if ( pEnvironment[idx] != 0 )
            {
                free(pEnvironment[idx]);
            }
        }
        free(pEnvironment);
    }

    return Error;
}

/**********************************************
 osl_executeProcess
 *********************************************/
 
oslProcessError SAL_CALL osl_executeProcess(
											rtl_uString *ustrImageName,
											rtl_uString *ustrArguments[],
                                            sal_uInt32   nArguments,
											oslProcessOption Options,
											oslSecurity Security,
											rtl_uString *ustrWorkDir,
											rtl_uString *ustrEnvironment[],
                                            sal_uInt32   nEnvironmentVars,
											oslProcess *pProcess
											)
{
	return osl_executeProcess_WithRedirectedIO(
		ustrImageName,
		ustrArguments,
		nArguments,
		Options,
		Security,
		ustrWorkDir,
		ustrEnvironment,
		nEnvironmentVars,
		pProcess,
		NULL,
		NULL,
		NULL
		);
}

/**********************************************
 osl_psz_executeProcess
 *********************************************/
 
oslProcessError SAL_CALL osl_psz_executeProcess(sal_Char *pszImageName,
                                                sal_Char *pszArguments[],
                                                oslProcessOption Options,
                                                oslSecurity Security,
                                                sal_Char *pszDirectory,
                                                sal_Char *pszEnvironments[],
                                                oslProcess *pProcess,
												oslFileHandle	*pInputWrite,
												oslFileHandle	*pOutputRead,
												oslFileHandle	*pErrorRead
												)
{
    int     i;
	sal_Char    path[PATH_MAX + 1];
	ProcessData Data;
	oslThread hThread;

    path[0] = '\0';
    
    memset(&Data,0,sizeof(ProcessData));
	Data.m_pInputWrite = pInputWrite;
	Data.m_pOutputRead = pOutputRead;
	Data.m_pErrorRead = pErrorRead;

	if (pszImageName == NULL)
		pszImageName = pszArguments[0];

	OSL_ASSERT(pszImageName != NULL);

	if ( pszImageName == 0 )
	{
		return osl_Process_E_NotFound;
	}

	if ((Options & osl_Process_SEARCHPATH) &&
		(osl_searchPath_impl(pszImageName, NULL, '\0', path, sizeof(path)) == osl_Process_E_None))
		pszImageName = path;

	Data.m_pszArgs[0] = strdup(pszImageName);
	Data.m_pszArgs[1] = 0;

    if ( pszArguments != 0 )
    {
        for (i = 0; ((i + 2) < MAX_ARGS) && (pszArguments[i] != NULL); i++)
            Data.m_pszArgs[i+1] = strdup(pszArguments[i]);
        Data.m_pszArgs[i+2] = NULL;
    }

	Data.m_options = Options;
	Data.m_pszDir  = (pszDirectory != NULL) ? strdup(pszDirectory) : NULL;

	if (pszEnvironments != NULL)
	{
		for (i = 0; ((i + 1) < MAX_ENVS) &&  (pszEnvironments[i] != NULL); i++)
			Data.m_pszEnv[i] = strdup(pszEnvironments[i]);
	 	Data.m_pszEnv[i+1] = NULL;
	}
	else
	 	Data.m_pszEnv[0] = NULL;

	if (Security != NULL)
	{
	    Data.m_uid  = ((oslSecurityImpl*)Security)->m_pPasswd.pw_uid;
	    Data.m_gid  = ((oslSecurityImpl*)Security)->m_pPasswd.pw_gid;
		Data.m_name = ((oslSecurityImpl*)Security)->m_pPasswd.pw_name;
	}
	else
		Data.m_uid = (uid_t)-1;

	Data.m_pProcImpl = (oslProcessImpl*) malloc(sizeof(oslProcessImpl));
	Data.m_pProcImpl->m_pid = 0;
	Data.m_pProcImpl->m_terminated = osl_createCondition();
	Data.m_pProcImpl->m_pnext = NULL;

	if (ChildListMutex == NULL)
		ChildListMutex = osl_createMutex();

	Data.m_started = osl_createCondition();

	hThread = osl_createThread(ChildStatusProc, &Data);

	osl_waitCondition(Data.m_started, NULL);
    osl_destroyCondition(Data.m_started);

    for (i = 0; Data.m_pszArgs[i] != NULL; i++)
		  free((void *)Data.m_pszArgs[i]);

    for (i = 0; Data.m_pszEnv[i] != NULL; i++)
		  free((void *)Data.m_pszEnv[i]);

    if ( Data.m_pszDir != 0 )
    {
		free((void *)Data.m_pszDir);
    }

	osl_destroyThread(hThread);

	if (Data.m_pProcImpl->m_pid != 0)
	{
		*pProcess = Data.m_pProcImpl;

	 	if (Options & osl_Process_WAIT)
			osl_joinProcess(*pProcess);

		return osl_Process_E_None;
    }

	osl_destroyCondition(Data.m_pProcImpl->m_terminated);
	free(Data.m_pProcImpl);

	return osl_Process_E_Unknown;
}


/******************************************************************************
 *
 *                  Functions for processes
 *
 *****************************************************************************/


/**********************************************
 osl_terminateProcess
 *********************************************/
 
oslProcessError SAL_CALL osl_terminateProcess(oslProcess Process)
{
    if (Process == NULL)
	    return osl_Process_E_Unknown;

	if (kill(((oslProcessImpl*)Process)->m_pid, SIGKILL) != 0)
	{
		switch (errno)
		{
			case EPERM:
				return osl_Process_E_NoPermission;

			case ESRCH:
			    return osl_Process_E_NotFound;

			default:
			    return osl_Process_E_Unknown;
		}
	}

	return osl_Process_E_None;
}

/**********************************************
 osl_getProcess
 *********************************************/
 
oslProcess SAL_CALL osl_getProcess(oslProcessIdentifier Ident)
{
	oslProcessImpl *pProcImpl;

	if (kill(Ident, 0) != -1)
	{
		oslProcessImpl* pChild;

		if (ChildListMutex == NULL)
			ChildListMutex = osl_createMutex();

		osl_acquireMutex(ChildListMutex);

		pChild = ChildList;

		/* check if it is one of our child processes */
		while (pChild != NULL)
		{
			if (Ident == (sal_uInt32) pChild->m_pid)
				break;

			pChild = pChild->m_pnext;
		}

		pProcImpl = (oslProcessImpl*) malloc(sizeof(oslProcessImpl));
		pProcImpl->m_pid        = Ident;
		pProcImpl->m_terminated = osl_createCondition();

		if (pChild != NULL)
		{
			/* process is a child so insert into list */
			pProcImpl->m_pnext = pChild->m_pnext;
			pChild->m_pnext = pProcImpl;

			pProcImpl->m_status = pChild->m_status;

			if (osl_checkCondition(pChild->m_terminated))
				osl_setCondition(pProcImpl->m_terminated);
		}
		else
			pProcImpl->m_pnext = NULL;

		osl_releaseMutex(ChildListMutex);
	}
	else
		pProcImpl = NULL;

	return (pProcImpl);
}

/**********************************************
 osl_freeProcessHandle
 *********************************************/
 
void SAL_CALL osl_freeProcessHandle(oslProcess Process)
{
    if (Process != NULL)
	{
		oslProcessImpl *pChild, *pPrev = NULL;

		OSL_ASSERT(ChildListMutex != NULL);

		if ( ChildListMutex == 0 )
		{
			return;
		}

		osl_acquireMutex(ChildListMutex);

		pChild = ChildList;

		/* remove process from child list */
		while (pChild != NULL)
		{
			if (pChild == (oslProcessImpl*)Process)
			{
				if (pPrev != NULL)
					pPrev->m_pnext = pChild->m_pnext;
				else
					ChildList = pChild->m_pnext;

				break;
			}

			pPrev  = pChild;
			pChild = pChild->m_pnext;
		}

		osl_releaseMutex(ChildListMutex);

		osl_destroyCondition(((oslProcessImpl*)Process)->m_terminated);

		free(Process);
	}
}

#if defined(LINUX)
struct osl_procStat
{
   /* from 'stat' */
	pid_t pid;                /* pid */
	char command[16];         /* 'argv[0]' */ /* mfe: it all right char comm[16] in kernel! */
	char state;               /* state (running, stopped, ...) */
	pid_t ppid;               /* parent pid */
	pid_t pgrp;               /* parent group */
	int session;              /* session ID */
	int tty;                  /* no of tty */
	pid_t tpgid;              /* group of process owning the tty */
	unsigned long flags;      /* flags dunno */
	unsigned long minflt;     /* minor page faults */
	unsigned long cminflt;    /* minor page faults with children */
	unsigned long majflt;     /* major page faults */
	unsigned long cmajflt;    /* major page faults with children */
	unsigned long utime;      /* no of jiffies in user mode */
	unsigned long stime;      /* no of jiffies in kernel mode */
	unsigned long cutime;     /* no of jiffies in user mode with children */
	unsigned long cstime;     /* no of jiffies in kernel mode with children */
	unsigned long priority;   /* nice value + 15 (kernel scheduling prio)*/
	long nice;                /* nice value */
	long timeout;             /* no of jiffies of next process timeout */
	long itrealvalue;         /* no jiffies before next SIGALRM */
	unsigned long starttime;  /* process started this no of jiffies after boot */
	unsigned long vsize;      /* virtual memory size (in bytes) */
	long rss;                 /* resident set size (in pages) */
	unsigned long rss_rlim;   /* rss limit (in bytes) */
	unsigned long startcode;   /* address above program text can run */
	unsigned long endcode;    /* address below program text can run */
	unsigned long startstack; /* address of start of stack */
	unsigned long kstkesp;    /* current value of 'esp' (stack pointer) */
	unsigned long kstkeip;    /* current value of 'eip' (instruction pointer) */
	/* mfe: Linux > 2.1.7x have more signals (88) */
/*#ifdef LINUX */
	char signal[24];          /* pending signals */
	char blocked[24];         /* blocked signals */
	char sigignore[24];       /* ignored signals */
	char sigcatch[24];        /* catched signals */
/*#else*/
/*	long long signal;*/
/*	long long blocked;*/
/*	long long sigignore;*/
/*	long long sigcatch;*/
/*#endif */
	unsigned long wchan;      /* 'channel' the process is waiting in */
	unsigned long nswap;      /* ? */
	unsigned long cnswap;     /* ? */

	/* from 'status' */
	int ruid;                 /* real uid */
	int euid;                 /* effective uid */
	int suid;                 /* saved uid */
	int fuid;                 /* file access uid */
	int rgid;                 /* real gid */
	int egid;                 /* effective gid */
	int sgid;                 /* saved gid */
	int fgid;                 /* file access gid */
	unsigned long vm_size;    /* like vsize but on kb */
	unsigned long vm_lock;    /* locked pages in kb */
	unsigned long vm_rss;     /* like rss but in kb */
	unsigned long vm_data;    /* data size */
	unsigned long vm_stack;   /* stack size */
	unsigned long vm_exe;     /* executable size */
	unsigned long vm_lib;     /* library size */
};

/**********************************************
 osl_getProcStat
 *********************************************/
 
sal_Bool osl_getProcStat(pid_t pid, struct osl_procStat* procstat)
{
	int fd = 0;
	sal_Bool bRet = sal_False;
	char name[PATH_MAX + 1];
	snprintf(name, sizeof(name), "/proc/%u/stat", pid);

	if ((fd = open(name,O_RDONLY)) >=0 )
	{
		char* tmp=0;
		char prstatbuf[512];
		memset(prstatbuf,0,512);
		bRet = read(fd,prstatbuf,511) == 511;

		close(fd);
		/*printf("%s\n\n",prstatbuf);*/

		if (!bRet)
		    return sal_False;

		tmp = strrchr(prstatbuf, ')');
		*tmp = '\0';
		memset(procstat->command, 0, sizeof(procstat->command));

		sscanf(prstatbuf, "%d (%15c", &procstat->pid, procstat->command);
		sscanf(tmp + 2,
			   "%c"
			   "%i %i %i %i %i"
			   "%lu %lu %lu %lu %lu"
			   "%lu %lu %lu %lu"
			   "%lu %li %li %li"
			   "%lu %lu %li %lu"
			   "%lu %lu %lu %lu %lu"
			   "%s %s %s %s"
			   "%lu %lu %lu",
			   &procstat->state,
			   &procstat->ppid,      &procstat->pgrp,    &procstat->session,    &procstat->tty,         &procstat->tpgid,
			   &procstat->flags,     &procstat->minflt,  &procstat->cminflt,    &procstat->majflt,      &procstat->cmajflt,
			   &procstat->utime,     &procstat->stime,   &procstat->cutime,     &procstat->cstime,
			   &procstat->priority,  &procstat->nice,    &procstat->timeout,    &procstat->itrealvalue,
			   &procstat->starttime, &procstat->vsize,   &procstat->rss,        &procstat->rss_rlim,
			   &procstat->startcode, &procstat->endcode, &procstat->startstack, &procstat->kstkesp,     &procstat->kstkeip,
			   procstat->signal,     procstat->blocked,  procstat->sigignore,   procstat->sigcatch,
			   &procstat->wchan,     &procstat->nswap,   &procstat->cnswap
			);
	}
    return bRet;
}

/**********************************************
 osl_getProcStatus
 *********************************************/
 
sal_Bool osl_getProcStatus(pid_t pid, struct osl_procStat* procstat)
{
	int fd = 0;
	char name[PATH_MAX + 1];
	snprintf(name, sizeof(name), "/proc/%u/status", pid);

    sal_Bool bRet = sal_False;

	if ((fd = open(name,O_RDONLY)) >=0 )
	{
		char* tmp=0;
		char prstatusbuf[512];
		memset(prstatusbuf,0,512);
		bRet = read(fd,prstatusbuf,511) == 511;

		close(fd);

		/*		printf("\n\n%s\n\n",prstatusbuf);*/

		if (!bRet)
		    return sal_False;

		tmp = strstr(prstatusbuf,"Uid:");
		if(tmp)
		{
			sscanf(tmp,"Uid:\t%d\t%d\t%d\t%d",
				   &procstat->ruid, &procstat->euid, &procstat->suid, &procstat->fuid
				);
		}


		tmp = strstr(prstatusbuf,"Gid:");
		if(tmp)
		{
			sscanf(tmp,"Gid:\t%d\t%d\t%d\t%d",
				   &procstat->rgid, &procstat->egid, &procstat->sgid, &procstat->fgid
				);
		}

		tmp = strstr(prstatusbuf,"VmSize:");
		if(tmp)
		{
			sscanf(tmp,
				   "VmSize: %lu kB\n"
				   "VmLck: %lu kB\n"
				   "VmRSS: %lu kB\n"
				   "VmData: %lu kB\n"
				   "VmStk: %lu kB\n"
				   "VmExe: %lu kB\n"
				   "VmLib: %lu kB\n",
				   &procstat->vm_size, &procstat->vm_lock, &procstat->vm_rss, &procstat->vm_data,
				   &procstat->vm_stack, &procstat->vm_exe, &procstat->vm_lib
				);
		}

		tmp = strstr(prstatusbuf,"SigPnd:");
		if(tmp)
		{
			sscanf(tmp, "SigPnd: %s SigBlk: %s SigIgn: %s %*s %s",
				   procstat->signal, procstat->blocked, procstat->sigignore, procstat->sigcatch
				);
		}
	}
	return bRet;
}

#endif

/**********************************************
 osl_getProcessInfo
 *********************************************/
 
oslProcessError SAL_CALL osl_getProcessInfo(oslProcess Process, oslProcessData Fields, oslProcessInfo* pInfo)
{
	pid_t   pid;

    if (Process == NULL)
		pid = getpid();
	else
		pid = ((oslProcessImpl*)Process)->m_pid;

	if (! pInfo || (pInfo->Size != sizeof(oslProcessInfo)))
		return osl_Process_E_Unknown;

	pInfo->Fields = 0;

	if (Fields & osl_Process_IDENTIFIER)
	{
		pInfo->Ident  = pid;
		pInfo->Fields |= osl_Process_IDENTIFIER;
	}

	if (Fields & osl_Process_EXITCODE)
	{
		if ((Process != NULL) &&
			osl_checkCondition(((oslProcessImpl*)Process)->m_terminated))
		{
			pInfo->Code = ((oslProcessImpl*)Process)->m_status;
			pInfo->Fields |= osl_Process_EXITCODE;
		}
	}

	if (Fields & (osl_Process_HEAPUSAGE | osl_Process_CPUTIMES))
	{

#if defined(SOLARIS)

		int  fd;
		sal_Char name[PATH_MAX + 1];

		snprintf(name, sizeof(name), "/proc/%u", pid);

		if ((fd = open(name, O_RDONLY)) >= 0)
		{
			prstatus_t prstatus;

			if (ioctl(fd, PIOCSTATUS, &prstatus) >= 0)
			{
				if (Fields & osl_Process_CPUTIMES)
				{
					pInfo->UserTime.Seconds   = prstatus.pr_utime.tv_sec;
					pInfo->UserTime.Nanosec   = prstatus.pr_utime.tv_nsec;
					pInfo->SystemTime.Seconds = prstatus.pr_stime.tv_sec;
					pInfo->SystemTime.Nanosec = prstatus.pr_stime.tv_nsec;

					pInfo->Fields |= osl_Process_CPUTIMES;
				}

				if (Fields & osl_Process_HEAPUSAGE)
				{
					pInfo->HeapUsage = prstatus.pr_brksize;

					pInfo->Fields |= osl_Process_HEAPUSAGE;
				}

				close(fd);

				return (pInfo->Fields == Fields) ? osl_Process_E_None : osl_Process_E_Unknown;
			}
			else
				close(fd);
		}

#elif defined(HPUX)

		struct pst_status prstatus;

		if (pstat_getproc(&prstatus, sizeof(prstatus), (size_t)0, pid) == 1)
		{
			if (Fields & osl_Process_CPUTIMES)
			{
				pInfo->UserTime.Seconds   = prstatus.pst_utime;
				pInfo->UserTime.Nanosec   = 500000L;
				pInfo->SystemTime.Seconds = prstatus.pst_stime;
				pInfo->SystemTime.Nanosec = 500000L;

				pInfo->Fields |= osl_Process_CPUTIMES;
			}

			if (Fields & osl_Process_HEAPUSAGE)
			{
				pInfo->HeapUsage = prstatus.pst_vdsize*PAGESIZE;

				pInfo->Fields |= osl_Process_HEAPUSAGE;
			}

			return (pInfo->Fields == Fields) ? osl_Process_E_None : osl_Process_E_Unknown;
		}

#elif defined(LINUX)

		if ( (Fields & osl_Process_CPUTIMES) || (Fields & osl_Process_HEAPUSAGE) )
		{
    		struct osl_procStat procstat;
    		memset(&procstat,0,sizeof(procstat));

    		if ( (Fields & osl_Process_CPUTIMES) && osl_getProcStat(pid, &procstat) )
    		{
    			/*
    			 *  mfe:
    			 *  We calculate only time of the process proper.
    			 *  Threads are processes, we do not consider their time here!
    			 *  (For this, cutime and cstime should be used, it seems not
    			 *   to work in 2.0.36)
    			 */

                long clktck;
                unsigned long hz;
                unsigned long userseconds;
                unsigned long systemseconds;

                clktck = sysconf(_SC_CLK_TCK);
                if (clktck < 0) {
                    return osl_Process_E_Unknown;
                }
                hz = (unsigned long) clktck;

                userseconds = procstat.utime/hz;
                systemseconds = procstat.stime/hz;

     			pInfo->UserTime.Seconds   = userseconds;
    			pInfo->UserTime.Nanosec   = procstat.utime - (userseconds * hz);
    			pInfo->SystemTime.Seconds = systemseconds;
    			pInfo->SystemTime.Nanosec = procstat.stime - (systemseconds * hz);
    
    			pInfo->Fields |= osl_Process_CPUTIMES;
    		}

    		if ( (Fields & osl_Process_HEAPUSAGE) && osl_getProcStatus(pid, &procstat) )
    		{
    			/*
    			 *  mfe:
    			 *  vm_data (found in status) shows the size of the data segment
    			 *  it a rough approximation of the core heap size
    			 */
    			pInfo->HeapUsage = procstat.vm_data*1024;

    			pInfo->Fields |= osl_Process_HEAPUSAGE;
    		}
    	}

		return (pInfo->Fields == Fields) ? osl_Process_E_None : osl_Process_E_Unknown;
#endif

	}

	return (pInfo->Fields == Fields) ? osl_Process_E_None : osl_Process_E_Unknown;
}


/***********************************************
 helper function for osl_joinProcessWithTimeout
 **********************************************/

static int is_timeout(const struct timeval* tend)
{
	struct timeval tcurrent;
	gettimeofday(&tcurrent, NULL);				
	return (tcurrent.tv_sec >= tend->tv_sec);
}

/**********************************************
 kill(pid, 0) is usefull for checking if a 
 process is still alive, but remember that 
 kill even returns 0 if the process is already
 a zombie.
 *********************************************/
 
static int is_process_dead(pid_t pid)
{		
	return ((-1 == kill(pid, 0)) && (ESRCH == errno));
}

/**********************************************
 osl_joinProcessWithTimeout
 *********************************************/
 
oslProcessError SAL_CALL osl_joinProcessWithTimeout(oslProcess Process, const TimeValue* pTimeout)
{
	oslProcessImpl* pChild    = ChildList;
	oslProcessError osl_error = osl_Process_E_None;
		
    OSL_PRECOND(Process, "osl_joinProcess: Invalid parameter");
    OSL_ASSERT(ChildListMutex);
    
    if (NULL == Process || 0 == ChildListMutex)
        return osl_Process_E_Unknown;
            	 	
	osl_acquireMutex(ChildListMutex);
	
	/* check if process is a child of ours */
	while (pChild != NULL)
	{
		if (pChild == (oslProcessImpl*)Process)
			break;

		pChild = pChild->m_pnext;
	}

	osl_releaseMutex(ChildListMutex);
        
	if (pChild != NULL)
	{
		oslConditionResult cond_res = osl_waitCondition(pChild->m_terminated, pTimeout);
		
		if (osl_cond_result_timeout == cond_res)
		    osl_error = osl_Process_E_TimedOut;
		else if (osl_cond_result_ok != cond_res)
		    osl_error = osl_Process_E_Unknown;		    
	}
	else /* alien process; StatusThread will not be able
	   		to set the condition terminated */
	{
		pid_t pid = ((oslProcessImpl*)Process)->m_pid;
		
		if (pTimeout)
		{
			int timeout = 0;
			struct timeval tend;		
				
			gettimeofday(&tend, NULL);									
			
			tend.tv_sec += pTimeout->Seconds;			
												
			while (!is_process_dead(pid) && ((timeout = is_timeout(&tend)) == 0))
				sleep(1);
				
			if (timeout)
				osl_error = osl_Process_E_TimedOut;
		}
		else /* infinite */
		{
			while (!is_process_dead(pid))
				sleep(1);
		}				
	}
	return osl_error;	
}

/**********************************************
 osl_joinProcess
 *********************************************/

oslProcessError SAL_CALL osl_joinProcess(oslProcess Process)
{
    return osl_joinProcessWithTimeout(Process, NULL);        
}
