/*******************************************************************************
 * Copyright (c) 2005, 2009 Intel Corporation.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *    Hoang M Nguyen, Intel - Initial API and Implementation
 *    IBM - Portions of old RAC code (brought over by Hoang M Nguyen)
 *    Spundun Bhatt (spundun@gmail.com), with the support and encouragement of the University of Southern California Information Sciences Institute Distributed Scalable Systems Division.
 *
 * $Id: TransportSupportNamedPipe_apple.c,v 1.1 2009/08/07 03:27:55 jwest Exp $
 *
 *******************************************************************************/

#ifdef __APPLE__  // Apple-specific

#include <stddef.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <dirent.h>

#include "tptp/TransportSupport.h"

#include "tptp/NoLog.h"



/**
 *********************************************************
 *
 * @brief
 *    read from the given named pipe
 *
 * @return
 *    The number of bytes that have been successfully read
 *    Negative - Error.
 *********************************************************/
int readFromNamedPipe(HANDLE handle,
						 char *buffer,
						 int offset,
						 int length,
						 int *bytesRead)
{
	ssize_t byte;
	int result;

	while (1) {
		byte = read(handle, buffer+offset, length-offset);
		if(byte == 0) {
			result = TPTP_PIPE_HAS_ENDED;
			*bytesRead=0;
		}
		else if(byte < 0)	{
			if (errno == EINTR) continue;
			
			result = TPTP_UNABLE_TO_READ_FROM_NAMED_PIPE;
			*bytesRead=0;
		}
		else {
			result = 1;
			*bytesRead = byte;
		}
		
		break;
	}
	
	return result;
}



/**
 *********************************************************
 *
 * @brief
 *    write to the given named pipe with given data
 *
 * @return
 *    The number of bytes successfully written
 *    Negative - Error
 *********************************************************/

int writeToNamedPipe(HANDLE handle,
					  char *buffer,
					  int offset,
					  int length,
					  int *bytesWritten) {
	int result;
	ssize_t byte;
	byte=write(handle, (unsigned char*)buffer+offset, length-offset);
	if(byte==0) {
		result=0;
		*bytesWritten=0;
	}
	else if(byte<0) {
		result=-1;
		*bytesWritten=0;
	}
	else {
		result=1;
		*bytesWritten=byte;
	}
	return *bytesWritten;
}


/**
 *********************************************************
 *
 * @brief
 *    Clean up a given pipe
 *
 * @return
 *    0 - Success
 *    nonzero - Error.
 *********************************************************/
int cleanPipeUp(HANDLE *pPipeHandle)
{
	if (*pPipeHandle >= 0)
	{
		close(*pPipeHandle);
		*pPipeHandle = -1 ;
	}

	return 0 ;
}


/**
 *********************************************************
 *
 * @brief
 *   Used by the creater of a named pipe to connect to a previously created named
 *   pipe for reading and waits for another application to open the pipe for writing.
 *
 * @note: This is a blocking call (i.e., the call will not return until someone else
 * has opened the pipe for writing.)
 *
 * @note: the two parameters are needed for platform-independent support
 *        (handle to be used in Windows and the name of the pipe for Linux)
 *
 * @param handle - the handle returned from a successful call to
 *                 ra_createNamedPipe()
 *
 * @param pPipeName - the full name of the pipe to connected to
 *
 * @return
 *    0 - Success
 *    nonzero - Error.
 *
 *********************************************************/
int connectToNamedPipe(HANDLE *pHandle, const char *pPipeName)
{
	int result = 0 ;

	/* Open the file */
	*pHandle = open(pPipeName, O_RDONLY);

	if(*pHandle < 0)
	{
		*pHandle = -1;
		result = -1 ;
	}

	return result;
}



/**
 *********************************************************
 *
 * @brief
 * Create the named pipe given its name
 *
 * @return
 *    the handle of the created pipe
 *    negative if error
 *********************************************************/
HANDLE createGeneralNamedPipe(const char *pNameSpace, const char *pPipeName, int createOption, int bInheritHandle)
{
	HANDLE result = (HANDLE) -1;
	char pFullName[1024];

	mode_t fcrtmask;   /* 203936 */

	/* Build the pipe name for the platform */
	strcpy(pFullName, pNameSpace);
	strcat(pFullName, pPipeName);

	/* destroy it first if it existed */
	destroyNamedPipe(pFullName) ;

	/* Set the file creation mask so all permissions can be set */
	fcrtmask = umask(0000);

	/* Ensure the RA_PIPE_NAMESPACE directory exists and that everyone can access it */
	mkdir(pNameSpace, 0777);

	/* go create the pipe */
	result = mkfifo(pFullName, 0666) ;

	if(result)
	{
		errno = TPTP_UNABLE_TO_CREATE_NAMED_PIPE ;
		result = INVALID_HANDLE_VALUE ;
	}
	else
	{
		/* Since mkfifo() may not return a valid file descriptor we will fill
		   in an initial value right now and the connectToNamedPipe() will
		   fill in the real descriptor.
		*/
		result = 0;
	}

	/* reset the file creation mask back to what it was */
	umask(fcrtmask);

	return result;
}




/**
 *********************************************************
 *
 * @brief
 * Opens a pipe with given option
 *
 * @return
 *    the handle of the open pipe
 *    negative if error
 *********************************************************/
HANDLE  openGeneralNamedPipe(const char *pNameSpace, const char *pPipeName, int openOption, int bInheritHandle) 
{
	HANDLE result;
	char  pFullName[1024];
	int    old_flags;
	BOOL   retry = TRUE;

	int  openMode = 0 ;

	if(pPipeName == NULL)
	{
		return (HANDLE) -1;
	}

	/* Build the pipe name for the platform */
	sprintf(pFullName, "%s%s", pNameSpace, pPipeName) ;

	switch(openOption) 
	{
		case TPTP_PIPE_READ_ONLY:      openMode = O_RDONLY ;  break ;
		case TPTP_PIPE_WRITE_ONLY:     openMode = O_WRONLY | O_NONBLOCK;  break ;
		/* NC here stands for non-critical.  It is used by logging, which would rather fail than loop for long periods. */
		case TPTP_PIPE_NC_WRITE_ONLY:  openMode = O_WRONLY | O_NONBLOCK; retry = FALSE;  break ;
		case TPTP_PIPE_DUPLEX:         openMode = O_RDWR ;    break ;
	}

	/* Open the file */
	do
	{
		result=open(pFullName, openMode);
		if(result < 0 )
		{
			/* If errno is ENXIO, it means our partner at the other end
			 * of the pipe isn't listening.  In that case we may want to
			 * retry.  Otherwise, it's an unrecoverable failure.
			 * 
			 * However, because the code to check for that caused a bug
			 * late in the release cycle, I backed out the check for this
			 * condition.  We should add something that checks for
			 * if (errno == ENXIO) and only retry in that case when
			 * we have more time for testing.
			 */
			result = TPTP_NO_NAMED_PIPE_READER;
			if ( retry )
			{
				Sleep(1000);
			}
		}
		else 
		{
			/* unset the O_NONBLOCK flag */
			old_flags = fcntl(result, F_GETFL);
			old_flags &= ~O_NONBLOCK;
			fcntl(result, F_SETFL,old_flags);
		}
	}
	while ( retry  && (result < 0) );
	
	return result;
}


/**
 *********************************************************
 *
 * @brief
 *    Called by the server pipe to disconnect from a pipe
 *
 * @note
 *    In Linux, disconnecting the named pipe is the same as closing it
 *
 *********************************************************/
int disconnectFromNamedPipe(HANDLE handle)
{
	return ( cleanPipeUp(&handle) );
}


/**
 *********************************************************
 *
 * @brief
 *    completely remove the named pipe from the system
 *
 * @return
 *    0  - Success
 *    nonzero - Error.
 *********************************************************/
int destroyNamedPipe(const char *pFullPipeName)
{
	return ( unlink(pFullPipeName) ) ;
}


#endif   // end-of-Apple-specific
