diff options
Diffstat (limited to 'sal/osl/unx/pipe.c')
-rw-r--r-- | sal/osl/unx/pipe.c | 592 |
1 files changed, 592 insertions, 0 deletions
diff --git a/sal/osl/unx/pipe.c b/sal/osl/unx/pipe.c new file mode 100644 index 000000000000..ede4cd7e074f --- /dev/null +++ b/sal/osl/unx/pipe.c @@ -0,0 +1,592 @@ +/************************************************************************* + * + * 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. + * + ************************************************************************/ + + +#include "system.h" + +#include <osl/pipe.h> +#include <osl/diagnose.h> +/*#include <osl/signal.h>*/ +#include <osl/thread.h> +#include <osl/interlck.h> + +#include "sockimpl.h" + +#define PIPEDEFAULTPATH "/tmp" +#define PIPEALTERNATEPATH "/var/tmp" + +#define PIPENAMEMASK "OSL_PIPE_%s" +#define SECPIPENAMEMASK "OSL_PIPE_%s_%s" + +sal_Bool SAL_CALL osl_psz_getUserIdent(oslSecurity Security, sal_Char *pszIdent, sal_uInt32 nMax); +oslPipe SAL_CALL osl_psz_createPipe(const sal_Char *pszPipeName, oslPipeOptions Options, oslSecurity Security); + +/*#define DEBUG_OSL_PIPE*/ +/*#define TRACE_OSL_PIPE*/ + + +/*****************************************************************************/ +/* enum oslPipeError */ +/*****************************************************************************/ + +static struct +{ + int errcode; + oslPipeError error; +} PipeError[]= { + { 0, osl_Pipe_E_None }, /* no error */ + { EPROTOTYPE, osl_Pipe_E_NoProtocol }, /* Protocol wrong type for socket */ + { ENOPROTOOPT, osl_Pipe_E_NoProtocol }, /* Protocol not available */ + { EPROTONOSUPPORT, osl_Pipe_E_NoProtocol }, /* Protocol not supported */ + { ESOCKTNOSUPPORT, osl_Pipe_E_NoProtocol }, /* Socket type not supported */ + { EPFNOSUPPORT, osl_Pipe_E_NoProtocol }, /* Protocol family not supported */ + { EAFNOSUPPORT, osl_Pipe_E_NoProtocol }, /* Address family not supported by */ + /* protocol family */ + { ENETRESET, osl_Pipe_E_NetworkReset }, /* Network dropped connection because */ + /* of reset */ + { ECONNABORTED, osl_Pipe_E_ConnectionAbort }, /* Software caused connection abort */ + { ECONNRESET, osl_Pipe_E_ConnectionReset }, /* Connection reset by peer */ + { ENOBUFS, osl_Pipe_E_NoBufferSpace }, /* No buffer space available */ + { ETIMEDOUT, osl_Pipe_E_TimedOut }, /* Connection timed out */ + { ECONNREFUSED, osl_Pipe_E_ConnectionRefused }, /* Connection refused */ + { -1, osl_Pipe_E_invalidError } +}; + + +/* map */ +/* mfe: NOT USED + static int osl_NativeFromPipeError(oslPipeError errorCode) + { + int i = 0; + + while ((PipeError[i].error != osl_Pipe_E_invalidError) && + (PipeError[i].error != errorCode)) i++; + + return PipeError[i].errcode; + + } +*/ + +/* reverse map */ +static oslPipeError osl_PipeErrorFromNative(int nativeType) +{ + int i = 0; + + while ((PipeError[i].error != osl_Pipe_E_invalidError) && + (PipeError[i].errcode != nativeType)) i++; + + return PipeError[i].error; +} + + +/* macros */ +#define ERROR_TO_NATIVE(x) osl_NativeFromPipeError(x) +#define ERROR_FROM_NATIVE(y) osl_PipeErrorFromNative(y) + + +/*****************************************************************************/ +/* osl_create/destroy-PipeImpl */ +/*****************************************************************************/ + +oslPipe __osl_createPipeImpl() +{ + oslPipe pPipeImpl; + + pPipeImpl = (oslPipe)calloc(1, sizeof(struct oslPipeImpl)); + pPipeImpl->m_nRefCount =1; + pPipeImpl->m_bClosed = sal_False; +#if defined(LINUX) + pPipeImpl->m_bIsInShutdown = sal_False; + pPipeImpl->m_bIsAccepting = sal_False; +#endif + return pPipeImpl; +} + +void __osl_destroyPipeImpl(oslPipe pImpl) +{ + if (pImpl != NULL) + free(pImpl); +} + + +/*****************************************************************************/ +/* osl_createPipe */ +/*****************************************************************************/ +oslPipe SAL_CALL osl_createPipe(rtl_uString *ustrPipeName, oslPipeOptions Options, oslSecurity Security) +{ + oslPipe pPipe=0; + rtl_String* strPipeName=0; + sal_Char* pszPipeName=0; + + if ( ustrPipeName != 0 ) + { + rtl_uString2String( &strPipeName, + rtl_uString_getStr(ustrPipeName), + rtl_uString_getLength(ustrPipeName), + osl_getThreadTextEncoding(), + OUSTRING_TO_OSTRING_CVTFLAGS ); + pszPipeName = rtl_string_getStr(strPipeName); + pPipe = osl_psz_createPipe(pszPipeName, Options, Security); + + if ( strPipeName != 0 ) + { + rtl_string_release(strPipeName); + } + } + + return pPipe; + +} + +oslPipe SAL_CALL osl_psz_createPipe(const sal_Char *pszPipeName, oslPipeOptions Options, + oslSecurity Security) +{ + int Flags; + size_t len; + struct sockaddr_un addr; + + sal_Char name[PATH_MAX + 1]; + oslPipe pPipe; + + if (access(PIPEDEFAULTPATH, R_OK|W_OK) == 0) + { + strncpy(name, PIPEDEFAULTPATH, sizeof(name)); + } + else + { + strncpy(name, PIPEALTERNATEPATH, sizeof(name)); + } + + + strncat(name, "/", sizeof(name)); + + if (Security) + { + sal_Char Ident[256]; + + Ident[0] = '\0'; + + OSL_VERIFY(osl_psz_getUserIdent(Security, Ident, sizeof(Ident))); + + snprintf(&name[strlen(name)], sizeof(name), SECPIPENAMEMASK, Ident, pszPipeName); + } + else + { + snprintf(&name[strlen(name)], sizeof(name), PIPENAMEMASK, pszPipeName); + } + + + /* alloc memory */ + pPipe= __osl_createPipeImpl(); + + /* create socket */ + pPipe->m_Socket = socket(AF_UNIX, SOCK_STREAM, 0); + if ( pPipe->m_Socket < 0 ) + { + OSL_TRACE("osl_createPipe socket failed. Errno: %d; %s\n",errno, strerror(errno)); + __osl_destroyPipeImpl(pPipe); + return NULL; + } + +/* OSL_TRACE("osl_createPipe : new Pipe on fd %i\n",pPipe->m_Socket);*/ + + /* set close-on-exec flag */ + if ((Flags = fcntl(pPipe->m_Socket, F_GETFD, 0)) != -1) + { + Flags |= FD_CLOEXEC; + if (fcntl(pPipe->m_Socket, F_SETFD, Flags) == -1) + { + OSL_TRACE("osl_createPipe failed changing socket flags. Errno: %d; %s\n",errno,strerror(errno)); + } + } + + memset(&addr, 0, sizeof(addr)); + + OSL_TRACE("osl_createPipe : Pipe Name '%s'",name); + + addr.sun_family = AF_UNIX; + strncpy(addr.sun_path, name, sizeof(addr.sun_path)); +#if defined(FREEBSD) + len = SUN_LEN(&addr); +#else + len = sizeof(addr); +#endif + + if ( Options & osl_Pipe_CREATE ) + { + struct stat status; + + /* check if there exists an orphan filesystem entry */ + if ( ( stat(name, &status) == 0) && + ( S_ISSOCK(status.st_mode) || S_ISFIFO(status.st_mode) ) ) + { + if ( connect(pPipe->m_Socket,(struct sockaddr *)&addr,len) >= 0 ) + { + OSL_TRACE("osl_createPipe : Pipe already in use. Errno: %d; %s\n",errno,strerror(errno)); + close (pPipe->m_Socket); + __osl_destroyPipeImpl(pPipe); + return NULL; + } + + unlink(name); + } + + /* ok, fs clean */ + if ( bind(pPipe->m_Socket, (struct sockaddr *)&addr, len) < 0 ) + { + OSL_TRACE("osl_createPipe : failed to bind socket. Errno: %d; %s\n",errno,strerror(errno)); + close (pPipe->m_Socket); + __osl_destroyPipeImpl(pPipe); + return NULL; + } + + /* Only give access to all if no security handle was specified, otherwise security + depends on umask */ + + if ( !Security ) + chmod(name,S_IRWXU | S_IRWXG |S_IRWXO); + + + strncpy(pPipe->m_Name, name, sizeof(pPipe->m_Name)); + + if ( listen(pPipe->m_Socket, 5) < 0 ) + { + OSL_TRACE("osl_createPipe failed to listen. Errno: %d; %s\n",errno,strerror(errno)); + unlink(name); /* remove filesystem entry */ + close (pPipe->m_Socket); + __osl_destroyPipeImpl(pPipe); + return NULL; + } + + return (pPipe); + } + else + { /* osl_pipe_OPEN */ + if ( access(name, F_OK) != -1 ) + { + if ( connect( pPipe->m_Socket, (struct sockaddr *)&addr, len) >= 0 ) + { + return (pPipe); + } + + OSL_TRACE("osl_createPipe failed to connect. Errno: %d; %s\n",errno,strerror(errno)); + } + + close (pPipe->m_Socket); + __osl_destroyPipeImpl(pPipe); + return NULL; + } +} + +void SAL_CALL osl_acquirePipe( oslPipe pPipe ) +{ + osl_incrementInterlockedCount( &(pPipe->m_nRefCount) ); +} + +void SAL_CALL osl_releasePipe( oslPipe pPipe ) +{ + + if( 0 == pPipe ) + return; + + if( 0 == osl_decrementInterlockedCount( &(pPipe->m_nRefCount) ) ) + { + if( ! pPipe->m_bClosed ) + osl_closePipe( pPipe ); + + __osl_destroyPipeImpl( pPipe ); + } +} + +void SAL_CALL osl_closePipe( oslPipe pPipe ) +{ + int nRet; +#if defined(LINUX) + size_t len; + struct sockaddr_un addr; + int fd; +#endif + int ConnFD; + + if( ! pPipe ) + { + return; + } + + if( pPipe->m_bClosed ) + { + return; + } + + ConnFD = pPipe->m_Socket; + + /* + Thread does not return from accept on linux, so + connect to the accepting pipe + */ +#if defined(LINUX) + if ( pPipe->m_bIsAccepting ) + { + pPipe->m_bIsInShutdown = sal_True; + pPipe->m_Socket = -1; + fd = socket(AF_UNIX, SOCK_STREAM, 0); + memset(&addr, 0, sizeof(addr)); + + OSL_TRACE("osl_destroyPipe : Pipe Name '%s'",pPipe->m_Name); + + addr.sun_family = AF_UNIX; + strncpy(addr.sun_path, pPipe->m_Name, sizeof(addr.sun_path)); + len = sizeof(addr); + + nRet = connect( fd, (struct sockaddr *)&addr, len); +#if OSL_DEBUG_LEVEL > 1 + if ( nRet < 0 ) + { + perror("connect in osl_destroyPipe"); + } +#endif /* OSL_DEBUG_LEVEL */ + close(fd); + } +#endif /* LINUX */ + + + nRet = shutdown(ConnFD, 2); + if ( nRet < 0 ) + { + OSL_TRACE("shutdown in destroyPipe failed : '%s'\n",strerror(errno)); + } + + nRet = close(ConnFD); + if ( nRet < 0 ) + { + OSL_TRACE("close in destroyPipe failed : '%s'\n",strerror(errno)); + } + /* remove filesystem entry */ + if ( strlen(pPipe->m_Name) > 0 ) + { + unlink(pPipe->m_Name); + } + pPipe->m_bClosed = sal_True; + +/* OSL_TRACE("Out osl_destroyPipe"); */ +} + + +/*****************************************************************************/ +/* osl_acceptPipe */ +/*****************************************************************************/ +oslPipe SAL_CALL osl_acceptPipe(oslPipe pPipe) +{ + int s, flags; + oslPipe pAcceptedPipe; + + OSL_ASSERT(pPipe); + if ( pPipe == 0 ) + { + return NULL; + } + + OSL_ASSERT(strlen(pPipe->m_Name) > 0); + +#if defined(LINUX) + pPipe->m_bIsAccepting = sal_True; +#endif + + s = accept(pPipe->m_Socket, NULL, NULL); + +#if defined(LINUX) + pPipe->m_bIsAccepting = sal_False; +#endif + + if (s < 0) + { + OSL_TRACE("osl_acceptPipe : accept error '%s'", strerror(errno)); + return NULL; + } + +#if defined(LINUX) + if ( pPipe->m_bIsInShutdown ) + { + close(s); + return NULL; + } +#endif /* LINUX */ + else + { + /* alloc memory */ + pAcceptedPipe= __osl_createPipeImpl(); + + OSL_ASSERT(pAcceptedPipe); + if(pAcceptedPipe==NULL) + { + close(s); + return NULL; + } + + /* set close-on-exec flag */ + if (!((flags = fcntl(s, F_GETFD, 0)) < 0)) + { + flags |= FD_CLOEXEC; + if (fcntl(s, F_SETFD, flags) < 0) + { + OSL_TRACE("osl_acceptPipe: error changing socket flags. " + "Errno: %d; %s",errno,strerror(errno)); + } + } + + pAcceptedPipe->m_Socket = s; + } + + return pAcceptedPipe; +} + +/*****************************************************************************/ +/* osl_receivePipe */ +/*****************************************************************************/ +sal_Int32 SAL_CALL osl_receivePipe(oslPipe pPipe, + void* pBuffer, + sal_Int32 BytesToRead) +{ + int nRet = 0; + + OSL_ASSERT(pPipe); + + if ( pPipe == 0 ) + { + OSL_TRACE("osl_receivePipe : Invalid socket"); + errno=EINVAL; + return -1; + } + + nRet = recv(pPipe->m_Socket, + (sal_Char*)pBuffer, + BytesToRead, 0); + + if ( nRet <= 0 ) + { + OSL_TRACE("osl_receivePipe failed : %i '%s'",nRet,strerror(errno)); + } + + return nRet; +} + + +/*****************************************************************************/ +/* osl_sendPipe */ +/*****************************************************************************/ +sal_Int32 SAL_CALL osl_sendPipe(oslPipe pPipe, + const void* pBuffer, + sal_Int32 BytesToSend) +{ + int nRet=0; + + OSL_ASSERT(pPipe); + + if ( pPipe == 0 ) + { + OSL_TRACE("osl_sendPipe : Invalid socket"); + errno=EINVAL; + return -1; + } + + nRet = send(pPipe->m_Socket, + (sal_Char*)pBuffer, + BytesToSend, 0); + + + if ( nRet <= 0 ) + { + OSL_TRACE("osl_sendPipe failed : %i '%s'",nRet,strerror(errno)); + } + + return nRet; +} + + +/*****************************************************************************/ +/* osl_getLastPipeError */ +/*****************************************************************************/ +oslPipeError SAL_CALL osl_getLastPipeError(oslPipe pPipe) +{ + (void) pPipe; /* unused */ + return ERROR_FROM_NATIVE(errno); +} + + +sal_Int32 SAL_CALL osl_writePipe( oslPipe pPipe, const void *pBuffer , sal_Int32 n ) +{ + /* loop until all desired bytes were send or an error occured */ + sal_Int32 BytesSend= 0; + sal_Int32 BytesToSend= n; + + OSL_ASSERT(pPipe); + while (BytesToSend > 0) + { + sal_Int32 RetVal; + + RetVal= osl_sendPipe(pPipe, pBuffer, BytesToSend); + + /* error occured? */ + if(RetVal <= 0) + { + break; + } + + BytesToSend -= RetVal; + BytesSend += RetVal; + pBuffer= (sal_Char*)pBuffer + RetVal; + } + + return BytesSend; +} + +sal_Int32 SAL_CALL osl_readPipe( oslPipe pPipe, void *pBuffer , sal_Int32 n ) +{ + /* loop until all desired bytes were read or an error occured */ + sal_Int32 BytesRead= 0; + sal_Int32 BytesToRead= n; + + OSL_ASSERT( pPipe ); + while (BytesToRead > 0) + { + sal_Int32 RetVal; + RetVal= osl_receivePipe(pPipe, pBuffer, BytesToRead); + + /* error occured? */ + if(RetVal <= 0) + { + break; + } + + BytesToRead -= RetVal; + BytesRead += RetVal; + pBuffer= (sal_Char*)pBuffer + RetVal; + } + return BytesRead; +} + + |