# This file is part of bish-bosh. It is subject to the licence terms in the COPYRIGHT file found in the top-level directory of this distribution and at https://raw.githubusercontent.com/raphaelcohn/bish-bosh/master/COPYRIGHT. No part of bish-bosh, including this file, may be copied, modified, propagated, or distributed except according to the terms contained in the COPYRIGHT file.
# Copyright © 2014-2015 The developers of bish-bosh. See the COPYRIGHT file in the top-level directory of this distribution and at https://raw.githubusercontent.com/raphaelcohn/bish-bosh/master/COPYRIGHT.


core_usesIn bishbosh/connection read write handler background foreground clientId packetIdentifier
core_usesIn core children

core_dependency_requires '*' mkdir
bishbosh_connection_mkdir()
{
	local parentPath="$1"
	local folderName="$2"

	local folderPath="$parentPath"/"$folderName"
	mkdir -m 0700 -p "$folderPath"
	printf '%s' "$folderPath"
}

core_dependency_oneOf '*' mkfifo mknod
bishbosh_connection_mkfifo()
{
	local fifoName="$1"
	local fifoPath="$bishbosh_connection_fifoFolder"/"$fifoName"
	if core_compatibility_whichNoOutput mkfifo; then
		mkfifo -m 0600 "$fifoPath"
	elif core_compatibility_whichNoOutput mknod; then
		# Not compatible with BSD-derived mknod (but then mkfifo is only likely to be missing on embedded systems)
		mknod -m 0600 "$fifoPath" p
	fi
	printf '%s' "$fifoPath"
}

core_dependency_requires '*' rm
bishbosh_connection_cleanContentsOfFolderPath()
{
	set +f
	rm -rf "$1"/* 2>/dev/null
	set -f
}

bishbosh_connection_sourceScriptlets()
{
	local path="$1"
	mkdir -m 0755 -p "$path" 2>/dev/null
	
	local scriptletFilePath="$path"/rc
	if core_path_isReadableNonEmptyFilePath "$scriptletFilePath"; then
		. "$scriptletFilePath"
	fi
	
	local scriptletsFolderPath="$path"/rc.d
	if [ ! -d "$scriptletsFolderPath" ]; then
		return 0
	fi
	pushd "$scriptletsFolderPath"
		set +f
			for scriptletFilePath in *
			do
				set -f
				if ! core_path_isReadableNonEmptyFilePath "$scriptletFilePath"; then
					if [ "$scriptletFilePath" = '*' ]; then
						continue
					fi
					core_exitError "The client-id scriptlet file '$scriptletFilePath' is not a readable, non-empty file"
				fi
				
				. ./"$scriptletFilePath"
			done
		set -f
	popd
}

_bishbosh_connection_defaultFolderPath()
{
	local variableName="$1"
	local parentPath="$2"
	local folderName="$3"
	if core_variable_isUnset "$variableName"; then
		core_variable_setVariable "$variableName" "$(bishbosh_connection_mkdir "$parentPath" "$folderName")"
	fi
}

bishbosh_connection_obtainConnectionId()
{
	if core_variable_isUnset bishbosh_connection_nextId; then
		bishbosh_connection_id=0
		bishbosh_connection_nextId=0
	else
		bishbosh_connection_id=$bishbosh_connection_nextId
		bishbosh_connection_nextId=$((bishbosh_connection_nextId + 1))
	fi
}

core_dependency_requires '*' rmdir mkdir
bishbosh_connection_obtainLockForClientId()
{
	# This odd-looking design minimises the time between obtaining the lock (mkdir) and enabling clean-up on exit
	# (there's a slight chance that receiving SIGTERM will leave the lock behind if received between mkdir and assignment of the variable bishbosh_connection_clientIdLockFolderPath)
	bishbosh_connection_clientIdLockFolderPath=''
	_bishbosh_connection_write_removeClientIdLockFolder()
	{
		if [ -n "$bishbosh_connection_clientIdLockFolderPath" ]; then
			rmdir "$bishbosh_connection_clientIdLockFolderPath" 1>/dev/null 2>/dev/null || true
		fi
	}
	core_trap_addOnCleanUp _bishbosh_connection_write_removeClientIdLockFolder
	local lockPath="$bishbosh_lockPath"/servers/"$bishbosh_server"/ports/"$bishbosh_port"/client-ids/"_${bishbosh_clientId}"/lock
	if mkdir -m 0755 -p "$lockPath"; then
		bishbosh_connection_clientIdLockFolderPath="$lockPath"
	else
		core_exitError $core_commandLine_exitCode_TEMPFAIL "Another process has lock'd Client Id '$bishbosh_clientId' at path '$lockPath'"
	fi
}

bishbosh_connection_createFifos()
{
	bishbosh_connection_fifoFolder="$(bishbosh_connection_mkdir "$bishbosh_temporaryFolderPath"/"$bishbosh_connection_id" fifo)"
	bishbosh_connection_toServerFifo="$(bishbosh_connection_mkfifo to-server)"
	bishbosh_connection_fromServerFifo="$(bishbosh_connection_mkfifo from-server)"
	bishbosh_connection_fromHexConversionFifo="$(bishbosh_connection_mkfifo from-hex-conversion)"
}

bishbosh_connection_makeClientConnection()
{
	bishbosh_connection_validate_initialise
	
	if core_variable_isUnset bishbosh_connection_clientServersPath; then
		bishbosh_connection_clientServersPath="$bishbosh_clientPath"/servers/"$bishbosh_server"
		bishbosh_connection_sourceScriptlets "$bishbosh_connection_clientServersPath"
	fi
	
	if core_variable_isUnset bishbosh_connection_clientServersPortsPath; then
		bishbosh_connection_clientServersPortsPath="$bishbosh_connection_clientServersPath"/ports/"$bishbosh_port"
		bishbosh_connection_sourceScriptlets "$bishbosh_connection_clientServersPortsPath"
	fi
	
	bishbosh_connection_clientId_validateAndDefault
	
	if core_variable_isUnset bishbosh_connection_clientServersPortsClientIdsPath; then
		bishbosh_connection_clientServersPortsClientIdsPath="$bishbosh_connection_clientServersPortsPath"/client-ids/_"$bishbosh_clientId"
		bishbosh_connection_sourceScriptlets "$bishbosh_connection_clientServersPortsClientIdsPath"
	fi
	
	bishbosh_connection_obtainLockForClientId
	
	bishbosh_connection_obtainConnectionId
	
	bishbosh_connection_createFifos
	
	bishbosh_connection_packetIdentifier_initialise
	bishbosh_connection_write_initialise
	bishbosh_connection_read_initialise 'no'
	bishbosh_connection_ping_initialise
	bishbosh_connection_foreground_initialise
	
	# We can not open for writing using a file descriptor or redirection operator (>) a FIFO unless someone is already reading from it; we will be blocked
	# We can not open for reading using a file descriptor or redirection operator (<) a FIFO unless someone is already writing to it; we will be blocked
	
	# Background <"$bishbosh_connection_toServerFifo" >"$bishbosh_connection_fromServerFifo" UNLESS devtcp, in which case 3<>
	bishbosh_connection_background_client
	if [ "${bishbosh_backend_name}" = 'devtcp' ]; then
		bishbosh_connection_clientPid=-1
	else
		bishbosh_connection_foreground_recordBackgroundJob bishbosh_connection_clientPid
	fi
	
	# Background <"$bishbosh_connection_fromServerFifo" >"$bishbosh_connection_fromHexConversionFifo"
	bishbosh_connection_background_hexConversion
	bishbosh_connection_foreground_recordBackgroundJob bishbosh_connection_hexConversionPid
	
	# Background >"$bishbosh_connection_toServerFifo" <"$bishbosh_connection_fromHexConversionFifo"
	bishbosh_connection_background_processLoop
	bishbosh_connection_foreground_recordBackgroundJob bishbosh_connection_processLoopPid
	
	# Background process, if required, to interrupt processingLoopRead
	bishbosh_connection_background_processingLoopReadInterrupter
	
	# Foreground process to monitor child processes for exit
	bishbosh_connection_foreground_monitorForChildExit
}
