1/*
2 * Copyright 2005, Ingo Weinhold, bonefish@users.sf.net. All rights reserved.
3 * Distributed under the terms of the MIT License.
4 */
5
6#include <map>
7#include <new>
8
9#include <string.h>
10
11#include <Autolock.h>
12
13#include <syscalls.h>
14
15#include "Debug.h"
16#include "MessageDeliverer.h"
17#include "MessagingService.h"
18
19using std::map;
20using std::nothrow;
21
22// sService -- the singleton instance
23MessagingService *MessagingService::sService = NULL;
24
25/*!	\class MessagingArea
26	\brief Represents an area of the messaging service shared between kernel
27		   and registrar.
28
29	The main purpose of the class is to retrieve (and remove) commands from
30	the area.
31*/
32
33// constructor
34MessagingArea::MessagingArea()
35{
36}
37
38// destructor
39MessagingArea::~MessagingArea()
40{
41	if (fID >= 0)
42		delete_area(fID);
43}
44
45// Create
46status_t
47MessagingArea::Create(area_id kernelAreaID, sem_id lockSem, sem_id counterSem,
48	MessagingArea *&_area)
49{
50	// allocate the object on the heap
51	MessagingArea *area = new(nothrow) MessagingArea;
52	if (!area)
53		return B_NO_MEMORY;
54
55	// clone the kernel area
56	area_id areaID = clone_area("messaging", (void**)&area->fHeader,
57		B_ANY_ADDRESS, B_READ_AREA | B_WRITE_AREA, kernelAreaID);
58	if (areaID < 0) {
59		delete area;
60		return areaID;
61	}
62
63	// finish the initialization of the object
64	area->fID = areaID;
65	area->fSize = area->fHeader->size;
66	area->fLockSem = lockSem;
67	area->fCounterSem = counterSem;
68	area->fNextArea = NULL;
69
70	_area = area;
71	return B_OK;
72}
73
74// Lock
75bool
76MessagingArea::Lock()
77{
78	// benaphore-like locking
79	if (atomic_add(&fHeader->lock_counter, 1) == 0)
80		return true;
81
82	return (acquire_sem(fLockSem) == B_OK);
83}
84
85// Unlock
86void
87MessagingArea::Unlock()
88{
89	if (atomic_add(&fHeader->lock_counter, -1) > 1)
90		release_sem(fLockSem);
91}
92
93// ID
94area_id
95MessagingArea::ID() const
96{
97	return fID;
98}
99
100// Size
101int32
102MessagingArea::Size() const
103{
104	return fSize;
105}
106
107// CountCommands
108int32
109MessagingArea::CountCommands() const
110{
111	return fHeader->command_count;
112}
113
114// PopCommand
115const messaging_command *
116MessagingArea::PopCommand()
117{
118	if (fHeader->command_count == 0)
119		return NULL;
120
121	// get the command
122	messaging_command *command
123		= (messaging_command*)((char*)fHeader + fHeader->first_command);
124
125	// remove it from the area
126	// (as long as the area is still locked, noone will overwrite the contents)
127	if (--fHeader->command_count == 0)
128		fHeader->first_command = fHeader->last_command = 0;
129	else
130		fHeader->first_command = command->next_command;
131
132	return command;
133}
134
135// Discard
136void
137MessagingArea::Discard()
138{
139	fHeader->size = 0;
140}
141
142// NextKernelAreaID
143area_id
144MessagingArea::NextKernelAreaID() const
145{
146	return fHeader->next_kernel_area;
147}
148
149// SetNextArea
150void
151MessagingArea::SetNextArea(MessagingArea *area)
152{
153	fNextArea = area;
154}
155
156// NextArea
157MessagingArea *
158MessagingArea::NextArea() const
159{
160	return fNextArea;
161}
162
163
164// #pragma mark -
165
166// constructor
167MessagingCommandHandler::MessagingCommandHandler()
168{
169}
170
171// destructor
172MessagingCommandHandler::~MessagingCommandHandler()
173{
174}
175
176
177// #pragma mark -
178
179// DefaultSendCommandHandler
180class MessagingService::DefaultSendCommandHandler
181	: public MessagingCommandHandler {
182
183	virtual void HandleMessagingCommand(uint32 _command, const void *data,
184		int32 dataSize)
185	{
186		const messaging_command_send_message *sendData
187			= (const messaging_command_send_message*)data;
188		const void *messageData = (uint8*)data
189			+ sizeof(messaging_command_send_message)
190			+ sizeof(messaging_target) * sendData->target_count;
191
192		DefaultMessagingTargetSet set(sendData->targets,
193			sendData->target_count);
194		MessageDeliverer::Default()->DeliverMessage(messageData,
195			sendData->message_size, set);
196	}
197};
198
199// CommandHandlerMap
200struct MessagingService::CommandHandlerMap
201	: map<uint32, MessagingCommandHandler*> {
202};
203
204
205/*! \class MessagingService
206	\brief Userland implementation of the kernel -> userland messaging service.
207
208	This service provides a way for the kernel to send BMessages (usually
209	notification (e.g. node monitoring) messages) to userland applications.
210
211	The kernel could write the messages directly to the respective target ports,
212	but this has the disadvantage, that a message needs to be dropped, if the
213	port is full at the moment of sending. By transferring the message to the
214	registrar, it is possible to use the MessageDeliverer which retries sending
215	messages on full ports.
216
217	The message transfer is implemented via areas shared between kernel
218	and registrar. By default one area is used as a ring buffer. The kernel
219	adds messages to it, the registrar removes them. If the area is full, the
220	kernel creates a new one and adds it to the area list.
221
222	While the service is called `messaging service' and we were speaking of
223	`messages' being passed through the areas, the service is actually more
224	general. In fact `commands' are passed through the areas. Currently the
225	only implemented command type is to send a message, but it is very easy
226	to add further command types (e.g. one for alerting the user in case of
227	errors).
228
229	The MessagingService maintains a mapping of command types to command
230	handlers (MessagingCommandHandler, which perform the actual processing
231	of the commands), that can be altered via
232	MessagingService::SetCommandHandler().
233*/
234
235// constructor
236MessagingService::MessagingService()
237	: fLock("messaging service"),
238	  fLockSem(-1),
239	  fCounterSem(-1),
240	  fFirstArea(NULL),
241	  fCommandHandlers(NULL),
242	  fCommandProcessor(-1),
243	  fTerminating(false)
244{
245}
246
247// destructor
248MessagingService::~MessagingService()
249{
250	fTerminating = true;
251
252	if (fLockSem >= 0)
253		delete_sem(fLockSem);
254	if (fCounterSem >= 0)
255		delete_sem(fCounterSem);
256
257	if (fCommandProcessor >= 0) {
258		int32 result;
259		wait_for_thread(fCommandProcessor, &result);
260	}
261
262	delete fCommandHandlers;
263
264	delete fFirstArea;
265}
266
267// Init
268status_t
269MessagingService::Init()
270{
271	// create the semaphores
272	fLockSem = create_sem(0, "messaging lock");
273	if (fLockSem < 0)
274		return fLockSem;
275
276	fCounterSem = create_sem(0, "messaging counter");
277	if (fCounterSem < 0)
278		return fCounterSem;
279
280	// create the command handler map
281	fCommandHandlers = new(nothrow) CommandHandlerMap;
282	if (!fCommandHandlers)
283		return B_NO_MEMORY;
284
285	// spawn the command processor
286	fCommandProcessor = spawn_thread(MessagingService::_CommandProcessorEntry,
287		"messaging command processor", B_DISPLAY_PRIORITY, this);
288	if (fCommandProcessor < 0)
289		return fCommandProcessor;
290
291	// register with the kernel
292	area_id areaID = _kern_register_messaging_service(fLockSem, fCounterSem);
293	if (areaID < 0)
294		return areaID;
295
296	// create the area
297	status_t error = MessagingArea::Create(areaID, fLockSem, fCounterSem,
298		fFirstArea);
299	if (error != B_OK) {
300		_kern_unregister_messaging_service();
301		return error;
302	}
303
304	// resume the command processor
305	resume_thread(fCommandProcessor);
306
307	// install the default send message command handler
308	MessagingCommandHandler *handler = new(nothrow) DefaultSendCommandHandler;
309	if (!handler)
310		return B_NO_MEMORY;
311	SetCommandHandler(MESSAGING_COMMAND_SEND_MESSAGE, handler);
312
313	return B_OK;
314}
315
316// CreateDefault
317status_t
318MessagingService::CreateDefault()
319{
320	if (sService)
321		return B_OK;
322
323	// create the service
324	MessagingService *service = new(nothrow) MessagingService;
325	if (!service)
326		return B_NO_MEMORY;
327
328	// init it
329	status_t error = service->Init();
330	if (error != B_OK) {
331		delete service;
332		return error;
333	}
334
335	sService = service;
336	return B_OK;
337}
338
339// DeleteDefault
340void
341MessagingService::DeleteDefault()
342{
343	if (sService) {
344		delete sService;
345		sService = NULL;
346	}
347}
348
349// Default
350MessagingService *
351MessagingService::Default()
352{
353	return sService;
354}
355
356// SetCommandHandler
357void
358MessagingService::SetCommandHandler(uint32 command,
359	MessagingCommandHandler *handler)
360{
361	BAutolock _(fLock);
362
363	if (handler) {
364		(*fCommandHandlers)[command] = handler;
365	} else {
366		// no handler: remove and existing entry
367		CommandHandlerMap::iterator it = fCommandHandlers->find(command);
368		if (it != fCommandHandlers->end())
369			fCommandHandlers->erase(it);
370	}
371}
372
373// _GetCommandHandler
374MessagingCommandHandler *
375MessagingService::_GetCommandHandler(uint32 command) const
376{
377	BAutolock _(fLock);
378
379	CommandHandlerMap::iterator it = fCommandHandlers->find(command);
380	return (it != fCommandHandlers->end() ? it->second : NULL);
381}
382
383// _CommandProcessorEntry
384int32
385MessagingService::_CommandProcessorEntry(void *data)
386{
387	return ((MessagingService*)data)->_CommandProcessor();
388}
389
390// _CommandProcessor
391int32
392MessagingService::_CommandProcessor()
393{
394	bool commandWaiting = false;
395	while (!fTerminating) {
396		// wait for the next command
397		if (!commandWaiting) {
398			status_t error = acquire_sem(fCounterSem);
399			if (error != B_OK)
400				continue;
401		} else
402			commandWaiting = false;
403
404		// get it from the first area
405		MessagingArea *area = fFirstArea;
406		area->Lock();
407		while (area->CountCommands() > 0) {
408			const messaging_command *command = area->PopCommand();
409			if (!command) {
410				// something's seriously wrong
411				ERROR("MessagingService::_CommandProcessor(): area %p (%"
412					B_PRId32 ") has command count %" B_PRId32 ", but doesn't "
413					"return any more commands.", area, area->ID(),
414					area->CountCommands());
415				break;
416			}
417PRINT("MessagingService::_CommandProcessor(): got command %" B_PRIu32 "\n",
418command->command);
419
420			// dispatch the command
421			MessagingCommandHandler *handler
422				= _GetCommandHandler(command->command);
423			if (handler) {
424				handler->HandleMessagingCommand(command->command, command->data,
425					command->size - sizeof(messaging_command));
426			} else {
427				WARNING("MessagingService::_CommandProcessor(): No handler "
428					"found for command %" B_PRIu32 "\n", command->command);
429			}
430		}
431
432		// there is a new area we don't know yet
433		if (!area->NextArea() && area->NextKernelAreaID() >= 0) {
434			// create it
435			MessagingArea *nextArea;
436			status_t error = MessagingArea::Create(area->NextKernelAreaID(),
437				fLockSem, fCounterSem, nextArea);
438			if (error == B_OK) {
439				area->SetNextArea(nextArea);
440				commandWaiting = true;
441			} else {
442				// Bad, but what can we do?
443				ERROR("MessagingService::_CommandProcessor(): Failed to clone "
444					"kernel area %" B_PRId32 ": %s\n", area->NextKernelAreaID(),
445					strerror(error));
446			}
447
448		}
449
450		// if the current area is empty and there is a next one, we discard the
451		// current one
452		if (area->NextArea() && area->CountCommands() == 0) {
453			fFirstArea = area->NextArea();
454			area->Discard();
455			area->Unlock();
456			delete area;
457		} else {
458			area->Unlock();
459		}
460	}
461
462	return 0;
463}
464
465