⛏️ index : haiku.git

// ServerConnection.cpp

#include "ServerConnection.h"

#include <AutoDeleter.h>
#include <ByteOrder.h>
#include <HashMap.h>

#include "Connection.h"
#include "ConnectionFactory.h"
#include "DebugSupport.h"
#include "ExtendedServerInfo.h"
#include "RequestConnection.h"
#include "ShareVolume.h"
#include "VolumeEvent.h"
#include "VolumeManager.h"

// VolumeMap
struct ServerConnection::VolumeMap : HashMap<HashKey32<int32>, ShareVolume*> {
};


// constructor
ServerConnection::ServerConnection(VolumeManager* volumeManager,
	ExtendedServerInfo* serverInfo)
	:
	BReferenceable(),
	RequestHandler(),
	fLock("server connection"),
	fVolumeManager(volumeManager),
	fServerInfo(serverInfo),
	fConnection(NULL),
	fVolumes(NULL),
	fConnectionBrokenEvent(NULL),
	fConnected(false)
{
	if (fServerInfo)
		fServerInfo->AcquireReference();
}

// destructor
ServerConnection::~ServerConnection()
{
PRINT(("ServerConnection::~ServerConnection()\n"))
	Close();
	delete fConnection;
	delete fVolumes;
	if (fConnectionBrokenEvent)
		fConnectionBrokenEvent->ReleaseReference();
	if (fServerInfo)
		fServerInfo->ReleaseReference();
}

// Init
status_t
ServerConnection::Init(vnode_id connectionBrokenTarget)
{
	if (!fServerInfo)
		RETURN_ERROR(B_BAD_VALUE);

	// create a connection broken event
	fConnectionBrokenEvent
		= new(std::nothrow) ConnectionBrokenEvent(connectionBrokenTarget);
	if (!fConnectionBrokenEvent)
		return B_NO_MEMORY;

	// get the server address
	const char* connectionMethod = fServerInfo->GetConnectionMethod();
	HashString server;
	status_t error = fServerInfo->GetAddress().GetString(&server, false);
	if (error != B_OK)
		RETURN_ERROR(error);

	// create the volume map
	fVolumes = new(std::nothrow) VolumeMap;
	if (!fVolumes)
		RETURN_ERROR(B_NO_MEMORY);
	error = fVolumes->InitCheck();
	if (error != B_OK)
		RETURN_ERROR(error);

	// establish the connection
	Connection* connection;
	ConnectionFactory factory;
	error = factory.CreateConnection(connectionMethod, server.GetString(),
		&connection);
	if (error != B_OK)
		RETURN_ERROR(error);

	// create a request connection
	fConnection = new(std::nothrow) RequestConnection(connection, this);
	if (!fConnection) {
		delete connection;
		RETURN_ERROR(B_NO_MEMORY);
	}
	error = fConnection->Init();
	if (error != B_OK)
		return error;

	// send an `init connection request'

	// prepare the request
	InitConnectionRequest request;
	request.bigEndian = B_HOST_IS_BENDIAN;

	// send the request
	Request* _reply;
	error = fConnection->SendRequest(&request, &_reply);
	if (error != B_OK)
		return error;
	ObjectDeleter<Request> replyDeleter(_reply);

	// everything OK?
	InitConnectionReply* reply = dynamic_cast<InitConnectionReply*>(_reply);
	if (!reply)
		return B_BAD_DATA;
	if (reply->error != B_OK)
		return reply->error;

	fConnected = true;
	return B_OK;
}

// Close
void
ServerConnection::Close()
{
	// mark the connection closed (events won't be delivered anymore)
	{
		AutoLocker<Locker> locker(fLock);
		fConnected = false;
	}

	if (fConnection)
		fConnection->Close();
}

// IsConnected
bool
ServerConnection::IsConnected()
{
	return fConnected;
}

// GetRequestConnection
RequestConnection*
ServerConnection::GetRequestConnection() const
{
	return fConnection;
}

// AddVolume
status_t
ServerConnection::AddVolume(ShareVolume* volume)
{
	if (!volume)
		return B_BAD_VALUE;

	AutoLocker<Locker> _(fLock);
	return fVolumes->Put(volume->GetID(), volume);
}

// RemoveVolume
void
ServerConnection::RemoveVolume(ShareVolume* volume)
{
	if (!volume)
		return;

	AutoLocker<Locker> _(fLock);
	fVolumes->Remove(volume->GetID());
}

// GetVolume
ShareVolume*
ServerConnection::GetVolume(int32 volumeID)
{
	AutoLocker<Locker> _(fLock);
	return fVolumes->Get(volumeID);
}

// VisitConnectionBrokenRequest
status_t
ServerConnection::VisitConnectionBrokenRequest(ConnectionBrokenRequest* request)
{
	AutoLocker<Locker> locker(fLock);
	if (fConnected) {
		fConnected = false;
		fVolumeManager->SendVolumeEvent(fConnectionBrokenEvent);
	}
	return B_OK;
}

// VisitNodeMonitoringRequest
status_t
ServerConnection::VisitNodeMonitoringRequest(NodeMonitoringRequest* request)
{
	AutoLocker<Locker> locker(fLock);
	if (fConnected) {
		if (ShareVolume* volume = GetVolume(request->volumeID)) {
			if (fVolumeManager->GetVolume(volume->GetRootID())) {
				locker.Unlock();
				if (request->opcode == B_DEVICE_UNMOUNTED)
					volume->SetUnmounting(true);
				else
					volume->ProcessNodeMonitoringRequest(request);
				volume->PutVolume();
			}
		}
	}
	return B_OK;
}