1/*
2 * Copyright 2003-2007, Waldemar Kornewald <wkornew@gmx.net>
3 * Distributed under the terms of the MIT License.
4 */
5
6/*!	\class PPPInterfaceListener
7	\brief This class simplifies the process of monitoring PPP interfaces.
8
9	PPPInterfaceListener converts all kernel report messages from the PPP stack
10	into BMessage objects and forwards them to the target BHandler.\n
11	The following values are added to each BMessage as int32 values:
12		- "interface" [\c int32] (optional): the interface ID of the affected interface
13		- "type" [\c int32]: the report type
14		- "code" [\c int32]: the report code
15*/
16
17#include "PPPInterfaceListener.h"
18
19#include "PPPInterface.h"
20
21#include <Messenger.h>
22#include <Handler.h>
23#include <LockerHelper.h>
24#include <KPPPUtils.h>
25
26
27static const int32 kCodeQuitReportThread = 'QUIT';
28
29
30// Creates a BMessage for each report and send it to the target BHandler.
31static
32status_t
33report_thread(void *data)
34{
35	PPPInterfaceListener *listener = static_cast<PPPInterfaceListener*>(data);
36
37	ppp_report_packet report;
38	ppp_interface_id *interfaceID;
39	int32 code;
40	thread_id sender;
41	BMessage message;
42
43	while (true) {
44		code = receive_data(&sender, &report, sizeof(report));
45
46		if (code == kCodeQuitReportThread)
47			break;
48		else if (code != PPP_REPORT_CODE)
49			continue;
50
51		BMessenger messenger(listener->Target());
52		if (messenger.IsValid()) {
53			message.MakeEmpty();
54			message.what = PPP_REPORT_MESSAGE;
55			message.AddInt32("type", report.type);
56			message.AddInt32("code", report.code);
57
58			if (report.length >= sizeof(ppp_interface_id)
59					&& ((report.type == PPP_MANAGER_REPORT
60							&& report.code == PPP_REPORT_INTERFACE_CREATED)
61						|| report.type >= PPP_INTERFACE_REPORT_TYPE_MIN)) {
62				interfaceID = reinterpret_cast<ppp_interface_id*>(report.data);
63				message.AddInt32("interface", static_cast<int32>(*interfaceID));
64			}
65
66			// We might cause a dead-lock. Thus, abort if we cannot send.
67			messenger.SendMessage(&message, (BHandler*) NULL, 100000);
68		}
69	}
70
71	return B_OK;
72}
73
74
75/*!	\brief Constructs a new listener that sends report messages to \a target.
76
77	\param target The target BHandler which should receive report messages.
78*/
79PPPInterfaceListener::PPPInterfaceListener(BHandler *target)
80	: fTarget(target),
81	fIsWatching(false),
82	fInterface(PPP_UNDEFINED_INTERFACE_ID)
83{
84	Construct();
85}
86
87
88//!	Copy constructor.
89PPPInterfaceListener::PPPInterfaceListener(const PPPInterfaceListener& copy)
90	: fTarget(copy.Target()),
91	fIsWatching(false),
92	fInterface(PPP_UNDEFINED_INTERFACE_ID)
93{
94	Construct();
95}
96
97
98//!	Removes all report message requests.
99PPPInterfaceListener::~PPPInterfaceListener()
100{
101	// disable all report messages
102	StopWatchingInterface();
103	StopWatchingManager();
104
105	// tell thread to quit
106	send_data(fReportThread, kCodeQuitReportThread, NULL, 0);
107	int32 tmp;
108	wait_for_thread(fReportThread, &tmp);
109}
110
111
112/*!	\brief Returns whether the listener was constructed correctly.
113
114	\return
115		- \c B_OK: No errors.
116		- \c B_ERROR: Some not defined error occured (like missing PPP stack).
117		- any other value: see \c PPPManager::InitCheck()
118
119	\sa PPPManager::InitCheck()
120*/
121status_t
122PPPInterfaceListener::InitCheck() const
123{
124	if (fReportThread < 0)
125		return B_ERROR;
126
127	return Manager().InitCheck();
128}
129
130
131//!	Changes the target BHandler for the report messages (may be \c NULL).
132void
133PPPInterfaceListener::SetTarget(BHandler *target)
134{
135	fTarget = target;
136}
137
138
139/*!	\brief Changes the interface being monitored.
140
141	This unregisters the old interface from the watch-list.
142
143	\param ID The ID of the interface you want to watch.
144
145	\return \c true on sucess, \c false on failure.
146*/
147bool
148PPPInterfaceListener::WatchInterface(ppp_interface_id ID)
149{
150	if (ID == fInterface)
151		return true;
152
153	StopWatchingInterface();
154
155	if (ID == PPP_UNDEFINED_INTERFACE_ID)
156		return true;
157
158	// enable reports
159	PPPInterface interface(ID);
160	if (interface.InitCheck() != B_OK)
161		return false;
162
163	if (!interface.EnableReports(PPP_CONNECTION_REPORT, fReportThread, PPP_NO_FLAGS))
164		return false;
165
166	fIsWatching = true;
167	fInterface = ID;
168
169	return true;
170}
171
172
173//!	Enables interface creation messages from the PPP manager.
174void
175PPPInterfaceListener::WatchManager()
176{
177	Manager().EnableReports(PPP_MANAGER_REPORT, fReportThread, PPP_NO_FLAGS);
178}
179
180
181/*!	\brief Stops watching the interface.
182
183	Beware that this does not disable the PPP manager's report messages.
184*/
185void
186PPPInterfaceListener::StopWatchingInterface()
187{
188	if (!fIsWatching)
189		return;
190
191	PPPInterface interface(fInterface);
192	interface.DisableReports(PPP_ALL_REPORTS, fReportThread);
193
194	fIsWatching = false;
195	fInterface = PPP_UNDEFINED_INTERFACE_ID;
196}
197
198
199//!	Disables interface creation messages from the PPP manager.
200void
201PPPInterfaceListener::StopWatchingManager()
202{
203	Manager().DisableReports(PPP_ALL_REPORTS, fReportThread);
204}
205
206
207void
208PPPInterfaceListener::Construct()
209{
210	fReportThread = spawn_thread(report_thread, "report_thread", B_NORMAL_PRIORITY,
211		this);
212	resume_thread(fReportThread);
213}
214