blob: a5f560e66f1fced8a69e00da0d41719256615d06 [file] [log] [blame]
/**************************************************************
*
* 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);
}