1/*
2 * Copyright 2003-2007, Waldemar Kornewald <wkornew@gmx.net>
3 * Distributed under the terms of the MIT License.
4 */
5
6/*!	\class KPPPReportManager
7	\brief Manager for PPP reports and report requests.
8*/
9
10#include <KPPPReportManager.h>
11
12#include <lock.h>
13#include <util/AutoLock.h>
14
15#include <KPPPUtils.h>
16
17
18typedef struct report_sender_info {
19	thread_id thread;
20	ppp_report_packet report;
21} report_sender_info;
22
23
24static status_t
25report_sender_thread(void *data)
26{
27	report_sender_info *info = static_cast<report_sender_info*>(data);
28	KPPPReportManager::SendReport(info->thread, &info->report);
29	delete info;
30	return B_OK;
31}
32
33
34/*!	\brief Constructor.
35
36	\param lock The BLocker that should be used by this report manager.
37*/
38KPPPReportManager::KPPPReportManager(mutex& lock)
39	: fLock(lock)
40{
41}
42
43
44//!	Deletes all report requests.
45KPPPReportManager::~KPPPReportManager()
46{
47	for (int32 index = 0; index < fReportRequests.CountItems(); index++)
48		delete fReportRequests.ItemAt(index);
49}
50
51
52/*!	\brief Send the given report message to the given thread.
53
54	\param thread The report receiver.
55	\param report The report message.
56
57	\return \c false on error.
58*/
59bool
60KPPPReportManager::SendReport(thread_id thread, const ppp_report_packet *report)
61{
62	if (!report)
63		return false;
64
65	if (thread == find_thread(NULL)) {
66		report_sender_info *info = new report_sender_info;
67		info->thread = thread;
68		memcpy(&info->report, report, sizeof(ppp_report_packet));
69		resume_thread(spawn_kernel_thread(report_sender_thread, "PPP: ReportSender",
70			B_NORMAL_PRIORITY, info));
71		return true;
72	}
73
74	send_data_with_timeout(thread, PPP_REPORT_CODE, &report, sizeof(report),
75		PPP_REPORT_TIMEOUT);
76	return true;
77}
78
79
80/*!	\brief Requests report messages of a given \a type.
81
82	\param type The type of report.
83	\param thread The receiver thread.
84	\param flags Optional report flags. See \c ppp_report_flags for more information.
85*/
86void
87KPPPReportManager::EnableReports(ppp_report_type type, thread_id thread,
88	int32 flags)
89{
90	if (thread < 0 || type == PPP_ALL_REPORTS)
91		return;
92
93	MutexLocker locker(fLock);
94
95	ppp_report_request *request = new ppp_report_request;
96	request->type = type;
97	request->thread = thread;
98	request->flags = flags;
99
100	fReportRequests.AddItem(request);
101}
102
103
104//!	Removes a report request.
105void
106KPPPReportManager::DisableReports(ppp_report_type type, thread_id thread)
107{
108	if (thread < 0)
109		return;
110
111	MutexLocker locker(fLock);
112
113	ppp_report_request *request;
114
115	for (int32 i = 0; i < fReportRequests.CountItems(); i++) {
116		request = fReportRequests.ItemAt(i);
117
118		if (request->thread != thread)
119			continue;
120
121		if (request->type == type || type == PPP_ALL_REPORTS)
122			fReportRequests.RemoveItem(request);
123	}
124
125	// empty message queue
126	while (has_data(thread)) {
127		thread_id sender;
128		receive_data(&sender, NULL, 0);
129	}
130}
131
132
133//!	Returns if we report messages of a given \a type to the given \a thread.
134bool
135KPPPReportManager::DoesReport(ppp_report_type type, thread_id thread)
136{
137	if (thread < 0)
138		return false;
139
140	MutexLocker locker(fLock);
141
142	ppp_report_request *request;
143
144	for (int32 i = 0; i < fReportRequests.CountItems(); i++) {
145		request = fReportRequests.ItemAt(i);
146
147		if (request->thread == thread && request->type == type)
148			return true;
149	}
150
151	return false;
152}
153
154
155/*!	\brief Send out report messages to all requestors.
156
157	You may append additional data to the report messages. The data length may not be
158	greater than \c PPP_REPORT_DATA_LIMIT.
159
160	\param type The report type.
161	\param code The report code belonging to the report type.
162	\param data Additional data.
163	\param length Length of the data.
164
165	\return \c false on error.
166*/
167bool
168KPPPReportManager::Report(ppp_report_type type, int32 code, void *data, int32 length)
169{
170	TRACE("KPPPReportManager: Report(type=%d code=%ld length=%ld) to %ld receivers\n",
171		type, code, length, fReportRequests.CountItems());
172
173	if (length > PPP_REPORT_DATA_LIMIT)
174		return false;
175
176	if (fReportRequests.CountItems() == 0)
177		return true;
178
179	if (!data)
180		length = 0;
181
182	MutexLocker locker(fLock);
183
184	status_t result;
185	thread_id me = find_thread(NULL);
186
187	ppp_report_packet report;
188	report.type = type;
189	report.code = code;
190	report.length = length;
191	memcpy(report.data, data, length);
192
193	ppp_report_request *request;
194
195	for (int32 index = 0; index < fReportRequests.CountItems(); index++) {
196		request = fReportRequests.ItemAt(index);
197
198		// do not send to yourself
199		if (request->thread == me)
200			continue;
201
202		result = send_data_with_timeout(request->thread, PPP_REPORT_CODE, &report,
203			sizeof(report), PPP_REPORT_TIMEOUT);
204
205#if DEBUG
206		if (result == B_TIMED_OUT)
207			TRACE("KPPPReportManager::Report(): timed out sending\n");
208#endif
209
210		if (result == B_BAD_THREAD_ID || result == B_NO_MEMORY
211				|| request->flags & PPP_REMOVE_AFTER_REPORT) {
212			fReportRequests.RemoveItem(request);
213			--index;
214			continue;
215		}
216	}
217
218	return true;
219}
220