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