blob: ebc61c5969ef1e89b93ba20b92821e772693ee80 [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.
*
*************************************************************/
#include "system.h"
#include "pipeimpl.h"
#include <osl/pipe.h>
#include <osl/diagnose.h>
#include <osl/thread.h>
#include <osl/mutex.h>
#include <osl/semaphor.h>
#include <osl/conditn.h>
#include <osl/interlck.h>
#include <osl/process.h>
#include <rtl/alloc.h>
#include <rtl/memory.h>
#define PIPESYSTEM "\\\\.\\pipe\\"
#define PIPEPREFIX "OSL_PIPE_"
typedef struct
{
sal_uInt32 m_Size;
sal_uInt32 m_ReadPos;
sal_uInt32 m_WritePos;
BYTE m_Data[1];
} oslPipeBuffer;
/*****************************************************************************/
/* oslPipeImpl */
/*****************************************************************************/
struct oslPipeImpl {
oslInterlockedCount m_Reference;
HANDLE m_File;
HANDLE m_NamedObject;
PSECURITY_ATTRIBUTES m_Security;
HANDLE m_ReadEvent;
HANDLE m_WriteEvent;
HANDLE m_AcceptEvent;
rtl_uString* m_Name;
oslPipeError m_Error;
sal_Bool m_bClosed;
};
/*****************************************************************************/
/* osl_create/destroy-PipeImpl */
/*****************************************************************************/
static oslInterlockedCount nPipes = 0;
oslPipe __osl_createPipeImpl(void)
{
oslPipe pPipe;
pPipe = (oslPipe) rtl_allocateZeroMemory(sizeof(struct oslPipeImpl));
pPipe->m_bClosed = sal_False;
pPipe->m_Reference = 0;
pPipe->m_Name = NULL;
pPipe->m_File = INVALID_HANDLE_VALUE;
pPipe->m_NamedObject = INVALID_HANDLE_VALUE;
pPipe->m_ReadEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
pPipe->m_WriteEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
pPipe->m_AcceptEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
return pPipe;
}
void __osl_destroyPipeImpl(oslPipe pPipe)
{
if (pPipe != NULL)
{
if ( pPipe->m_NamedObject != INVALID_HANDLE_VALUE && pPipe->m_NamedObject != NULL )
CloseHandle( pPipe->m_NamedObject );
if (pPipe->m_Security != NULL)
{
rtl_freeMemory(pPipe->m_Security->lpSecurityDescriptor);
rtl_freeMemory(pPipe->m_Security);
}
CloseHandle(pPipe->m_ReadEvent);
CloseHandle(pPipe->m_WriteEvent);
CloseHandle(pPipe->m_AcceptEvent);
if (pPipe->m_Name)
rtl_uString_release(pPipe->m_Name);
rtl_freeMemory(pPipe);
}
}
/*****************************************************************************/
/* osl_createPipe */
/*****************************************************************************/
oslPipe SAL_CALL osl_createPipe(rtl_uString *strPipeName, oslPipeOptions Options,
oslSecurity Security)
{
rtl_uString* name = NULL;
rtl_uString* path = NULL;
rtl_uString* temp = NULL;
oslPipe pPipe;
PSECURITY_ATTRIBUTES pSecAttr = NULL;
rtl_uString_newFromAscii(&path, PIPESYSTEM);
rtl_uString_newFromAscii(&name, PIPEPREFIX);
if ( /*IS_NT &&*/ Security)
{
rtl_uString *Ident = NULL;
rtl_uString *Delim = NULL;
OSL_VERIFY(osl_getUserIdent(Security, &Ident));
rtl_uString_newFromAscii(&Delim, "_");
rtl_uString_newConcat(&temp, name, Ident);
rtl_uString_newConcat(&name, temp, Delim);
rtl_uString_release(Ident);
rtl_uString_release(Delim);
}
else
{
if (Options & osl_Pipe_CREATE)
{
PSECURITY_DESCRIPTOR pSecDesc;
pSecDesc = (PSECURITY_DESCRIPTOR) rtl_allocateMemory(SECURITY_DESCRIPTOR_MIN_LENGTH);
/* add a NULL disc. ACL to the security descriptor */
OSL_VERIFY(InitializeSecurityDescriptor(pSecDesc, SECURITY_DESCRIPTOR_REVISION));
OSL_VERIFY(SetSecurityDescriptorDacl(pSecDesc, TRUE, (PACL) NULL, FALSE));
pSecAttr = rtl_allocateMemory(sizeof(SECURITY_ATTRIBUTES));
pSecAttr->nLength = sizeof(SECURITY_ATTRIBUTES);
pSecAttr->lpSecurityDescriptor = pSecDesc;
pSecAttr->bInheritHandle = TRUE;
}
}
rtl_uString_assign(&temp, name);
rtl_uString_newConcat(&name, temp, strPipeName);
/* alloc memory */
pPipe= __osl_createPipeImpl();
osl_incrementInterlockedCount(&(pPipe->m_Reference));
/* build system pipe name */
rtl_uString_assign(&temp, path);
rtl_uString_newConcat(&path, temp, name);
rtl_uString_release(temp);
temp = NULL;
if (Options & osl_Pipe_CREATE)
{
SetLastError( ERROR_SUCCESS );
if ( IS_NT )
pPipe->m_NamedObject = CreateMutexW( NULL, FALSE, name->buffer );
else
{
LPSTR pszTempBuffer = NULL;
int nCharsNeeded;
nCharsNeeded = WideCharToMultiByte( CP_ACP, 0, name->buffer, name->length, NULL, 0, NULL, NULL );
pszTempBuffer = alloca( nCharsNeeded * sizeof(CHAR) );
nCharsNeeded = WideCharToMultiByte( CP_ACP, 0, name->buffer, name->length, pszTempBuffer, nCharsNeeded, NULL, NULL );
pszTempBuffer[nCharsNeeded-1] = 0;
pPipe->m_NamedObject = CreateMutexA( NULL, FALSE, pszTempBuffer );
}
if ( pPipe->m_NamedObject != INVALID_HANDLE_VALUE && pPipe->m_NamedObject != NULL )
{
if ( GetLastError() != ERROR_ALREADY_EXISTS )
{
pPipe->m_Security = pSecAttr;
rtl_uString_assign(&pPipe->m_Name, name);
if (IS_NT)
{
/* try to open system pipe */
pPipe->m_File = CreateNamedPipeW(
path->buffer,
PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
PIPE_WAIT | PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE,
PIPE_UNLIMITED_INSTANCES,
4096, 4096,
NMPWAIT_WAIT_FOREVER,
pPipe->m_Security);
if (pPipe->m_File != INVALID_HANDLE_VALUE)
{
rtl_uString_release( name );
rtl_uString_release( path );
return pPipe;
}
}
else /* Win 9x */
{
LPSTR pszTempBuffer = NULL;
int nCharsNeeded;
nCharsNeeded = WideCharToMultiByte( CP_ACP, 0, path->buffer, path->length, NULL, 0, NULL, NULL );
pszTempBuffer = alloca( nCharsNeeded * sizeof(CHAR) );
nCharsNeeded = WideCharToMultiByte( CP_ACP, 0, path->buffer, path->length, pszTempBuffer, nCharsNeeded, NULL, NULL );
pszTempBuffer[nCharsNeeded-1] = 0;
pPipe->m_File = CreateSimplePipe( pszTempBuffer );
if ( IsValidHandle(pPipe->m_File) )
{
rtl_uString_release( name );
rtl_uString_release( path );
return pPipe;
}
}
}
else
{
CloseHandle( pPipe->m_NamedObject );
pPipe->m_NamedObject = INVALID_HANDLE_VALUE;
}
}
}
else
{
if (IS_NT)
{
BOOL fPipeAvailable;
do
{
/* free instance should be available first */
fPipeAvailable = WaitNamedPipeW(path->buffer, NMPWAIT_WAIT_FOREVER);
/* first try to open system pipe */
if ( fPipeAvailable )
{
pPipe->m_File = CreateFileW(
path->buffer,
GENERIC_READ|GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
NULL);
if ( pPipe->m_File != INVALID_HANDLE_VALUE )
{
// We got it !
rtl_uString_release( name );
rtl_uString_release( path );
return (pPipe);
}
else
{
// Pipe instance maybe catched by another client -> try again
}
}
} while ( fPipeAvailable );
}
else /* Win 9x */
{
LPSTR pszTempBuffer = NULL;
int nCharsNeeded;
nCharsNeeded = WideCharToMultiByte( CP_ACP, 0, path->buffer, path->length, NULL, 0, NULL, NULL );
pszTempBuffer = alloca( nCharsNeeded * sizeof(CHAR) );
nCharsNeeded = WideCharToMultiByte( CP_ACP, 0, path->buffer, path->length, pszTempBuffer, nCharsNeeded, NULL, NULL );
pszTempBuffer[nCharsNeeded-1] = 0;
pPipe->m_File = OpenSimplePipe( pszTempBuffer );
if ( IsValidHandle(pPipe->m_File) )
{
// We got it !
rtl_uString_release( name );
rtl_uString_release( path );
return (pPipe);
}
}
}
/* if we reach here something went wrong */
__osl_destroyPipeImpl(pPipe);
return NULL;
}
void SAL_CALL osl_acquirePipe( oslPipe pPipe )
{
osl_incrementInterlockedCount( &(pPipe->m_Reference) );
}
void SAL_CALL osl_releasePipe( oslPipe pPipe )
{
// OSL_ASSERT( pPipe );
if( 0 == pPipe )
return;
if( 0 == osl_decrementInterlockedCount( &(pPipe->m_Reference) ) )
{
if( ! pPipe->m_bClosed )
osl_closePipe( pPipe );
__osl_destroyPipeImpl( pPipe );
}
}
void SAL_CALL osl_closePipe( oslPipe pPipe )
{
if( pPipe && ! pPipe->m_bClosed )
{
pPipe->m_bClosed = sal_True;
if (IS_NT)
{
/* if we have a system pipe close it */
if (pPipe->m_File != INVALID_HANDLE_VALUE)
{
/* FlushFileBuffers(pPipe->m_File); */
DisconnectNamedPipe(pPipe->m_File);
CloseHandle(pPipe->m_File);
}
}
else
{
CloseSimplePipe( pPipe->m_File );
}
}
}
/*****************************************************************************/
/* osl_acceptPipe */
/*****************************************************************************/
oslPipe SAL_CALL osl_acceptPipe(oslPipe pPipe)
{
oslPipe pAcceptedPipe = NULL;
HANDLE Event;
OVERLAPPED os;
OSL_ASSERT(pPipe);
if (IS_NT)
{
DWORD nBytesTransfered;
rtl_uString* path = NULL;
rtl_uString* temp = NULL;
OSL_ASSERT (pPipe->m_File != INVALID_HANDLE_VALUE);
Event = pPipe->m_AcceptEvent;
rtl_zeroMemory(&os, sizeof(OVERLAPPED));
os.hEvent = pPipe->m_AcceptEvent;
ResetEvent(pPipe->m_AcceptEvent);
if ( !ConnectNamedPipe(pPipe->m_File, &os))
{
switch ( GetLastError() )
{
case ERROR_PIPE_CONNECTED: // Client already connected to pipe
case ERROR_NO_DATA: // Client was connected but has already closed pipe end
// should only appear in nonblocking mode but in fact does
// in blocking asynchronous mode.
break;
case ERROR_PIPE_LISTENING: // Only for nonblocking mode but see ERROR_NO_DATA
case ERROR_IO_PENDING: // This is normal if not client is connected yet
case ERROR_MORE_DATA: // Should not happen
// blocking call to accept
if( !GetOverlappedResult( pPipe->m_File, &os, &nBytesTransfered, TRUE ) )
{
// Possible error could be that between ConnectNamedPipe and GetOverlappedResult a connect
// took place.
switch ( GetLastError() )
{
case ERROR_PIPE_CONNECTED: // Pipe was already connected
case ERROR_NO_DATA: // Pipe was connected but client has already closed -> ver fast client ;-)
break; // Everything's fine !!!
default:
// Something went wrong
return 0;
}
}
break;
default: // All other error say that somethings going wrong.
return 0;
}
}
pAcceptedPipe = __osl_createPipeImpl();
OSL_ASSERT(pAcceptedPipe);
osl_incrementInterlockedCount(&(pAcceptedPipe->m_Reference));
rtl_uString_assign(&pAcceptedPipe->m_Name, pPipe->m_Name);
pAcceptedPipe->m_File = pPipe->m_File;
rtl_uString_newFromAscii(&temp, PIPESYSTEM);
rtl_uString_newConcat(&path, temp, pPipe->m_Name);
rtl_uString_release(temp);
// prepare for next accept
pPipe->m_File =
CreateNamedPipeW(path->buffer,
PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
PIPE_WAIT | PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE,
PIPE_UNLIMITED_INSTANCES,
4096, 4096,
NMPWAIT_WAIT_FOREVER,
pAcceptedPipe->m_Security);
rtl_uString_release( path );
}
else /* Win9x */
{
pAcceptedPipe = __osl_createPipeImpl();
OSL_ASSERT(pAcceptedPipe);
osl_incrementInterlockedCount(&(pAcceptedPipe->m_Reference));
rtl_uString_assign(&pAcceptedPipe->m_Name, pPipe->m_Name);
pAcceptedPipe->m_File = pPipe->m_File;
pAcceptedPipe->m_File = AcceptSimplePipeConnection( pPipe->m_File );
}
return pAcceptedPipe;
}
/*****************************************************************************/
/* osl_receivePipe */
/*****************************************************************************/
sal_Int32 SAL_CALL osl_receivePipe(oslPipe pPipe,
void* pBuffer,
sal_Int32 BytesToRead)
{
DWORD nBytes;
OSL_ASSERT(pPipe);
/* if we have a system pipe use it */
if ( IS_NT /*pPipe->m_File != INVALID_HANDLE_VALUE*/)
{
OVERLAPPED os;
rtl_zeroMemory(&os,sizeof(OVERLAPPED));
os.hEvent = pPipe->m_ReadEvent;
ResetEvent(pPipe->m_ReadEvent);
if (! ReadFile(pPipe->m_File, pBuffer, BytesToRead, &nBytes, &os) &&
((GetLastError() != ERROR_IO_PENDING) ||
! GetOverlappedResult(pPipe->m_File, &os, &nBytes, TRUE)))
{
DWORD lastError = GetLastError();
if (lastError == ERROR_MORE_DATA)
nBytes = BytesToRead;
else
{
if (lastError == ERROR_PIPE_NOT_CONNECTED)
nBytes = 0;
else
nBytes = (DWORD) -1;
pPipe->m_Error = osl_Pipe_E_ConnectionAbort;
}
}
}
else
{
BOOL fSuccess = ReadSimplePipe( pPipe->m_File, pBuffer, BytesToRead, &nBytes, TRUE );
if ( !fSuccess )
{
nBytes = 0;
pPipe->m_Error = osl_Pipe_E_ConnectionAbort;
}
}
return (nBytes);
}
/*****************************************************************************/
/* osl_sendPipe */
/*****************************************************************************/
sal_Int32 SAL_CALL osl_sendPipe(oslPipe pPipe,
const void* pBuffer,
sal_Int32 BytesToSend)
{
DWORD nBytes;
OSL_ASSERT(pPipe);
if (IS_NT/*pPipe->m_File != INVALID_HANDLE_VALUE*/)
{
OVERLAPPED os;
rtl_zeroMemory(&os, sizeof(OVERLAPPED));
os.hEvent = pPipe->m_WriteEvent;
ResetEvent(pPipe->m_WriteEvent);
if (! WriteFile(pPipe->m_File, pBuffer, BytesToSend, &nBytes, &os) &&
((GetLastError() != ERROR_IO_PENDING) ||
! GetOverlappedResult(pPipe->m_File, &os, &nBytes, TRUE)))
{
if (GetLastError() == ERROR_PIPE_NOT_CONNECTED)
nBytes = 0;
else
nBytes = (DWORD) -1;
pPipe->m_Error = osl_Pipe_E_ConnectionAbort;
}
}
else
{
BOOL fSuccess = WriteSimplePipe( pPipe->m_File, pBuffer, BytesToSend, &nBytes, TRUE );
if ( !fSuccess )
{
nBytes = 0;
pPipe->m_Error = osl_Pipe_E_ConnectionAbort;
}
}
return (nBytes);
}
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;
}
/*****************************************************************************/
/* osl_getLastPipeError */
/*****************************************************************************/
oslPipeError SAL_CALL osl_getLastPipeError(oslPipe pPipe)
{
oslPipeError Error;
if (pPipe != NULL)
{
Error = pPipe->m_Error;
pPipe->m_Error = osl_Pipe_E_None;
}
else
Error = osl_Pipe_E_NotFound;
return (Error);
}