diff options
Diffstat (limited to 'sal/osl/unx/process.c')
-rw-r--r-- | sal/osl/unx/process.c | 1536 |
1 files changed, 1536 insertions, 0 deletions
diff --git a/sal/osl/unx/process.c b/sal/osl/unx/process.c new file mode 100644 index 000000000000..300a1446e81a --- /dev/null +++ b/sal/osl/unx/process.c @@ -0,0 +1,1536 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 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); +} |