⛏️ index : haiku.git

/*
 * Copyright 2003-2007, Waldemar Kornewald <wkornew@gmx.net>
 * Distributed under the terms of the MIT License.
 */

/*!	\class KPPPReportManager
	\brief Manager for PPP reports and report requests.
*/

#include <KPPPReportManager.h>

#include <lock.h>
#include <util/AutoLock.h>

#include <KPPPUtils.h>


typedef struct report_sender_info {
	thread_id thread;
	ppp_report_packet report;
} report_sender_info;


static status_t
report_sender_thread(void *data)
{
	report_sender_info *info = static_cast<report_sender_info*>(data);
	KPPPReportManager::SendReport(info->thread, &info->report);
	delete info;
	return B_OK;
}


/*!	\brief Constructor.

	\param lock The BLocker that should be used by this report manager.
*/
KPPPReportManager::KPPPReportManager(mutex& lock)
	: fLock(lock)
{
}


//!	Deletes all report requests.
KPPPReportManager::~KPPPReportManager()
{
	for (int32 index = 0; index < fReportRequests.CountItems(); index++)
		delete fReportRequests.ItemAt(index);
}


/*!	\brief Send the given report message to the given thread.

	\param thread The report receiver.
	\param report The report message.

	\return \c false on error.
*/
bool
KPPPReportManager::SendReport(thread_id thread, const ppp_report_packet *report)
{
	if (!report)
		return false;

	if (thread == find_thread(NULL)) {
		report_sender_info *info = new report_sender_info;
		info->thread = thread;
		memcpy(&info->report, report, sizeof(ppp_report_packet));
		resume_thread(spawn_kernel_thread(report_sender_thread, "PPP: ReportSender",
			B_NORMAL_PRIORITY, info));
		return true;
	}

	send_data_with_timeout(thread, PPP_REPORT_CODE, &report, sizeof(report),
		PPP_REPORT_TIMEOUT);
	return true;
}


/*!	\brief Requests report messages of a given \a type.

	\param type The type of report.
	\param thread The receiver thread.
	\param flags Optional report flags. See \c ppp_report_flags for more information.
*/
void
KPPPReportManager::EnableReports(ppp_report_type type, thread_id thread,
	int32 flags)
{
	if (thread < 0 || type == PPP_ALL_REPORTS)
		return;

	MutexLocker locker(fLock);

	ppp_report_request *request = new ppp_report_request;
	request->type = type;
	request->thread = thread;
	request->flags = flags;

	fReportRequests.AddItem(request);
}


//!	Removes a report request.
void
KPPPReportManager::DisableReports(ppp_report_type type, thread_id thread)
{
	if (thread < 0)
		return;

	MutexLocker locker(fLock);

	ppp_report_request *request;

	for (int32 i = 0; i < fReportRequests.CountItems(); i++) {
		request = fReportRequests.ItemAt(i);

		if (request->thread != thread)
			continue;

		if (request->type == type || type == PPP_ALL_REPORTS)
			fReportRequests.RemoveItem(request);
	}

	// empty message queue
	while (has_data(thread)) {
		thread_id sender;
		receive_data(&sender, NULL, 0);
	}
}


//!	Returns if we report messages of a given \a type to the given \a thread.
bool
KPPPReportManager::DoesReport(ppp_report_type type, thread_id thread)
{
	if (thread < 0)
		return false;

	MutexLocker locker(fLock);

	ppp_report_request *request;

	for (int32 i = 0; i < fReportRequests.CountItems(); i++) {
		request = fReportRequests.ItemAt(i);

		if (request->thread == thread && request->type == type)
			return true;
	}

	return false;
}


/*!	\brief Send out report messages to all requestors.

	You may append additional data to the report messages. The data length may not be
	greater than \c PPP_REPORT_DATA_LIMIT.

	\param type The report type.
	\param code The report code belonging to the report type.
	\param data Additional data.
	\param length Length of the data.

	\return \c false on error.
*/
bool
KPPPReportManager::Report(ppp_report_type type, int32 code, void *data, int32 length)
{
	TRACE("KPPPReportManager: Report(type=%d code=%ld length=%ld) to %ld receivers\n",
		type, code, length, fReportRequests.CountItems());

	if (length > PPP_REPORT_DATA_LIMIT)
		return false;

	if (fReportRequests.CountItems() == 0)
		return true;

	if (!data)
		length = 0;

	MutexLocker locker(fLock);

	status_t result;
	thread_id me = find_thread(NULL);

	ppp_report_packet report;
	report.type = type;
	report.code = code;
	report.length = length;
	memcpy(report.data, data, length);

	ppp_report_request *request;

	for (int32 index = 0; index < fReportRequests.CountItems(); index++) {
		request = fReportRequests.ItemAt(index);

		// do not send to yourself
		if (request->thread == me)
			continue;

		result = send_data_with_timeout(request->thread, PPP_REPORT_CODE, &report,
			sizeof(report), PPP_REPORT_TIMEOUT);

#if DEBUG
		if (result == B_TIMED_OUT)
			TRACE("KPPPReportManager::Report(): timed out sending\n");
#endif

		if (result == B_BAD_THREAD_ID || result == B_NO_MEMORY
				|| request->flags & PPP_REMOVE_AFTER_REPORT) {
			fReportRequests.RemoveItem(request);
			--index;
			continue;
		}
	}

	return true;
}