/************************************************************************* * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * Copyright 2008 by Sun Microsystems, Inc. * * OpenOffice.org - a multi-platform office productivity suite * * $RCSfile: pipe.c,v $ * $Revision: 1.20 $ * * 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 * * for a copy of the LGPLv3 License. * ************************************************************************/ #include "system.h" #include #include /*#include */ #include #include #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; }