1/*
2 * Copyright 2001-2015 Haiku, Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 *		Ingo Weinhold (bonefish@users.sf.net)
7 */
8
9
10#include <Messenger.h>
11
12#include <new>
13#include <stdio.h>
14#include <strings.h>
15
16#include <Application.h>
17#include <AutoLocker.h>
18#include <Handler.h>
19#include <Looper.h>
20#include <Message.h>
21#include <OS.h>
22#include <Roster.h>
23
24#include <AppMisc.h>
25#include <LaunchRoster.h>
26#include <LooperList.h>
27#include <MessagePrivate.h>
28#include <MessageUtils.h>
29#include <TokenSpace.h>
30
31
32// debugging
33//#define DBG(x) x
34#define DBG(x)
35#define OUT	printf
36
37using BPrivate::gDefaultTokens;
38using BPrivate::gLooperList;
39using BPrivate::BLooperList;
40
41
42BMessenger::BMessenger()
43	:
44	fPort(-1),
45	fHandlerToken(B_NULL_TOKEN),
46	fTeam(-1)
47{
48}
49
50
51BMessenger::BMessenger(const char* signature, team_id team, status_t* result)
52	:
53	fPort(-1),
54	fHandlerToken(B_NULL_TOKEN),
55	fTeam(-1)
56{
57	_InitData(signature, team, result);
58}
59
60
61BMessenger::BMessenger(const BHandler* handler, const BLooper* looper,
62	status_t* _result)
63	:
64	fPort(-1),
65	fHandlerToken(B_NULL_TOKEN),
66	fTeam(-1)
67{
68	_InitData(handler, looper, _result);
69}
70
71
72BMessenger::BMessenger(const BMessenger& other)
73	:
74	fPort(other.fPort),
75	fHandlerToken(other.fHandlerToken),
76	fTeam(other.fTeam)
77{
78}
79
80
81BMessenger::~BMessenger()
82{
83}
84
85
86//	#pragma mark - Target
87
88
89bool
90BMessenger::IsTargetLocal() const
91{
92	return BPrivate::current_team() == fTeam;
93}
94
95
96BHandler*
97BMessenger::Target(BLooper** _looper) const
98{
99	BHandler* handler = NULL;
100	if (IsTargetLocal()
101		&& (fHandlerToken > B_NULL_TOKEN
102			|| fHandlerToken == B_PREFERRED_TOKEN)) {
103		gDefaultTokens.GetToken(fHandlerToken, B_HANDLER_TOKEN,
104			(void**)&handler);
105		if (_looper)
106			*_looper = BPrivate::gLooperList.LooperForPort(fPort);
107	} else if (_looper)
108		*_looper = NULL;
109
110	return handler;
111}
112
113
114bool
115BMessenger::LockTarget() const
116{
117	BLooper* looper = NULL;
118	Target(&looper);
119	if (looper != NULL && looper->Lock()) {
120		if (looper->fMsgPort == fPort)
121			return true;
122
123		looper->Unlock();
124		return false;
125	}
126
127	return false;
128}
129
130
131status_t
132BMessenger::LockTargetWithTimeout(bigtime_t timeout) const
133{
134	BLooper* looper = NULL;
135	Target(&looper);
136	if (looper == NULL)
137		return B_BAD_VALUE;
138
139	status_t result = looper->LockWithTimeout(timeout);
140
141	if (result == B_OK && looper->fMsgPort != fPort) {
142		looper->Unlock();
143		return B_BAD_PORT_ID;
144	}
145
146	return result;
147}
148
149
150//	#pragma mark - Message sending
151
152
153status_t
154BMessenger::SendMessage(uint32 command, BHandler* replyTo) const
155{
156	BMessage message(command);
157	return SendMessage(&message, replyTo);
158}
159
160
161status_t
162BMessenger::SendMessage(BMessage* message, BHandler* replyTo,
163	bigtime_t timeout) const
164{
165	DBG(OUT("BMessenger::SendMessage2(%.4s)\n", (char*)&message->what));
166
167	status_t result = message != NULL ? B_OK : B_BAD_VALUE;
168	if (result == B_OK) {
169		BMessenger replyMessenger(replyTo);
170		result = SendMessage(message, replyMessenger, timeout);
171	}
172
173	DBG(OUT("BMessenger::SendMessage2() done: %lx\n", result));
174
175	return result;
176}
177
178
179status_t
180BMessenger::SendMessage(BMessage* message, BMessenger replyTo,
181	bigtime_t timeout) const
182{
183	if (message == NULL)
184		return B_BAD_VALUE;
185
186	return BMessage::Private(message).SendMessage(fPort, fTeam, fHandlerToken,
187		timeout, false, replyTo);
188}
189
190
191status_t
192BMessenger::SendMessage(uint32 command, BMessage* reply) const
193{
194	BMessage message(command);
195
196	return SendMessage(&message, reply);
197}
198
199
200status_t
201BMessenger::SendMessage(BMessage* message, BMessage* reply,
202	bigtime_t deliveryTimeout, bigtime_t replyTimeout) const
203{
204	if (message == NULL || reply == NULL)
205		return B_BAD_VALUE;
206
207	status_t result = BMessage::Private(message).SendMessage(fPort, fTeam,
208		fHandlerToken, reply, deliveryTimeout, replyTimeout);
209
210	// map this result for now
211	if (result == B_BAD_TEAM_ID)
212		result = B_BAD_PORT_ID;
213
214	return result;
215}
216
217
218//	#pragma mark - Operators and misc
219
220
221status_t
222BMessenger::SetTo(const char* signature, team_id team)
223{
224	status_t result = B_OK;
225	_InitData(signature, team, &result);
226
227	return result;
228}
229
230
231status_t
232BMessenger::SetTo(const BHandler* handler, const BLooper* looper)
233{
234	status_t result = B_OK;
235	_InitData(handler, looper, &result);
236
237	return result;
238}
239
240
241BMessenger&
242BMessenger::operator=(const BMessenger& other)
243{
244	if (this != &other) {
245		fPort = other.fPort;
246		fHandlerToken = other.fHandlerToken;
247		fTeam = other.fTeam;
248	}
249
250	return *this;
251}
252
253
254bool
255BMessenger::operator==(const BMessenger& other) const
256{
257	// Note: The fTeam fields are not compared.
258	return fPort == other.fPort && fHandlerToken == other.fHandlerToken;
259}
260
261
262bool
263BMessenger::IsValid() const
264{
265	port_info info;
266	return fPort >= 0 && get_port_info(fPort, &info) == B_OK;
267}
268
269
270team_id
271BMessenger::Team() const
272{
273	return fTeam;
274}
275
276
277uint32
278BMessenger::HashValue() const
279{
280	return fPort * 19 + fHandlerToken;
281}
282
283
284//	#pragma mark - Private or reserved
285
286
287/*!	Sets the messenger's team, target looper port and handler token.
288
289	To target the preferred handler, use \c B_PREFERRED_TOKEN as token.
290
291	\param team The target's team.
292	\param port The target looper port.
293	\param token The target handler token.
294*/
295void
296BMessenger::_SetTo(team_id team, port_id port, int32 token)
297{
298	fTeam = team;
299	fPort = port;
300	fHandlerToken = token;
301}
302
303
304/*!	Initializes the BMessenger object's data given the signature and/or
305	team ID of a target.
306
307	When only a signature is given, and multiple instances of the application
308	are running it is undeterminate which one is chosen as the target. In case
309	only a team ID is passed, the target application is identified uniquely.
310	If both are supplied, the application identified by the team ID must have
311	a matching signature, otherwise the initilization fails.
312
313	\param signature The target application's signature. May be \c NULL.
314	\param team The target application's team ID. May be < 0.
315	\param result An optional pointer to a pre-allocated status_t into which
316		   the result of the initialization is written.
317*/
318void
319BMessenger::_InitData(const char* signature, team_id team, status_t* _result)
320{
321	status_t result = B_OK;
322
323	// get an app_info
324	app_info info;
325	if (team < 0) {
326		// no team ID given
327		if (signature != NULL) {
328			// Try existing launch communication data first
329			BMessage data;
330			if (BLaunchRoster().GetData(signature, data) == B_OK) {
331				info.port = data.GetInt32("port", -1);
332				team = data.GetInt32("team", -5);
333			}
334			if (info.port < 0) {
335				result = be_roster->GetAppInfo(signature, &info);
336				team = info.team;
337				// B_ERROR means that no application with the given signature
338				// is running. But we are supposed to return B_BAD_VALUE.
339				if (result == B_ERROR)
340					result = B_BAD_VALUE;
341			} else
342				info.flags = 0;
343		} else
344			result = B_BAD_TYPE;
345	} else {
346		// a team ID is given
347		result = be_roster->GetRunningAppInfo(team, &info);
348		// Compare the returned signature with the supplied one.
349		if (result == B_OK && signature != NULL
350			&& strcasecmp(signature, info.signature) != 0) {
351			result = B_MISMATCHED_VALUES;
352		}
353	}
354	// check whether the app flags say B_ARGV_ONLY
355	if (result == B_OK && (info.flags & B_ARGV_ONLY) != 0) {
356		result = B_BAD_TYPE;
357		// Set the team ID nevertheless -- that's what Be's implementation
358		// does. Don't know, if that is a bug, but at least it doesn't harm.
359		fTeam = team;
360	}
361	// init our members
362	if (result == B_OK) {
363		fTeam = team;
364		fPort = info.port;
365		fHandlerToken = B_PREFERRED_TOKEN;
366	}
367
368	// return the result
369	if (_result != NULL)
370		*_result = result;
371}
372
373
374/*!	Initializes the BMessenger to target the local BHandler and/or BLooper.
375
376	When a \c NULL handler is supplied, the preferred handler in the given
377	looper is targeted. If no looper is supplied the looper the given handler
378	belongs to is used -- that means in particular, that the handler must
379	already belong to a looper. If both are supplied the handler must actually
380	belong to looper.
381
382	\param handler The target handler. May be \c NULL.
383	\param looper The target looper. May be \c NULL.
384	\param result An optional pointer to a pre-allocated status_t into which
385	       the result of the initialization is written.
386*/
387void
388BMessenger::_InitData(const BHandler* handler, const BLooper* looper,
389	status_t* _result)
390{
391	status_t result = (handler != NULL || looper != NULL) ? B_OK : B_BAD_VALUE;
392	if (result == B_OK) {
393		if (handler != NULL) {
394			// BHandler is given, check/retrieve the looper.
395			if (looper != NULL) {
396				if (handler->Looper() != looper)
397					result = B_MISMATCHED_VALUES;
398			} else {
399				looper = handler->Looper();
400				if (looper == NULL)
401					result = B_MISMATCHED_VALUES;
402			}
403		}
404
405		// set port, token,...
406		if (result == B_OK) {
407			AutoLocker<BLooperList> locker(gLooperList);
408			if (locker.IsLocked() && gLooperList.IsLooperValid(looper)) {
409				fPort = looper->fMsgPort;
410				fHandlerToken = handler != NULL
411					? _get_object_token_(handler)
412					: B_PREFERRED_TOKEN;
413				fTeam = looper->Team();
414			} else
415				result = B_BAD_VALUE;
416		}
417	}
418
419	if (_result != NULL)
420		*_result = result;
421}
422
423
424//	#pragma mark - Operator functions
425
426
427bool
428operator<(const BMessenger& _a, const BMessenger& _b)
429{
430	BMessenger::Private a(const_cast<BMessenger&>(_a));
431	BMessenger::Private b(const_cast<BMessenger&>(_b));
432
433	// significance:
434	// 1. fPort
435	// 2. fHandlerToken
436	// 3. fPreferredTarget
437	// fTeam is insignificant
438	return (a.Port() < b.Port()
439			|| (a.Port() == b.Port()
440				&& (a.Token() < b.Token()
441					|| (a.Token() == b.Token()
442						&& !a.IsPreferredTarget()
443						&& b.IsPreferredTarget()))));
444}
445
446
447bool
448operator!=(const BMessenger& a, const BMessenger& b)
449{
450	return !(a == b);
451}
452