⛏️ index : haiku.git

/*
 * Copyright 2008-2013, Axel DΓΆrfler, axeld@pinc-software.de.
 * Distributed under the terms of the MIT License.
 */

/*! Provides the networking stack notification service. */

#include <net_notifications.h>

#include <kernel.h>
#include <generic_syscall.h>
#include <Notifications.h>
#include <util/KMessage.h>

//#define TRACE_NOTIFICATIONS
#ifdef TRACE_NOTIFICATIONS
#	define TRACE(x...) dprintf("\33[32mnet_notifications:\33[0m " x)
#else
#	define TRACE(x...) ;
#endif


class NetNotificationService : public DefaultUserNotificationService {
public:
								NetNotificationService();
	virtual						~NetNotificationService();

			void				Notify(const KMessage& event);

protected:
	virtual	void				LastReferenceReleased();
	virtual	void				FirstAdded();
	virtual	void				LastRemoved();
};


static NetNotificationService sNotificationService;


//	#pragma mark - NetNotificationService


NetNotificationService::NetNotificationService()
	:
	DefaultUserNotificationService("network")
{
}


NetNotificationService::~NetNotificationService()
{
}


void
NetNotificationService::Notify(const KMessage& event)
{
	uint32 opcode = event.GetInt32("opcode", 0);
	if (opcode == 0)
		return;

	TRACE("notify for %lx\n", opcode);

	DefaultUserNotificationService::Notify(event, opcode);
}


void
NetNotificationService::LastReferenceReleased()
{
	// don't delete us here
}


void
NetNotificationService::FirstAdded()
{
	// The reference counting doesn't work for us, as we'll have to
	// ensure our module stays loaded.
	module_info* dummy;
	get_module(NET_NOTIFICATIONS_MODULE_NAME, &dummy);
}


void
NetNotificationService::LastRemoved()
{
	// Give up the reference _AddListener()
	put_module(NET_NOTIFICATIONS_MODULE_NAME);
}


//	#pragma mark - User generic syscall


static status_t
net_notifications_control(const char *subsystem, uint32 function, void *buffer,
	size_t bufferSize)
{
	struct net_notifications_control control;
	if (bufferSize != sizeof(struct net_notifications_control)
		|| function != NET_NOTIFICATIONS_CONTROL_WATCHING)
		return B_BAD_VALUE;
	if (!IS_USER_ADDRESS(buffer) || user_memcpy(&control, buffer,
			sizeof(struct net_notifications_control)) < B_OK)
		return B_BAD_ADDRESS;

	if (control.flags != 0) {
		return sNotificationService.UpdateUserListener(control.flags,
			control.port, control.token);
	}

	return sNotificationService.RemoveUserListeners(control.port,
		control.token);
}


//	#pragma mark - exported module API


static status_t
send_notification(const KMessage* event)
{
	sNotificationService.Notify(*event);
	return B_OK;
}


static status_t
notifications_std_ops(int32 op, ...)
{
	switch (op) {
		case B_MODULE_INIT:
		{
			TRACE("init\n");

			new(&sNotificationService) NetNotificationService();
			status_t result = sNotificationService.Register();
			if (result != B_OK)
				return result;

			register_generic_syscall(NET_NOTIFICATIONS_SYSCALLS,
				net_notifications_control, 1, 0);
			return B_OK;
		}
		case B_MODULE_UNINIT:
			TRACE("uninit\n");

			unregister_generic_syscall(NET_NOTIFICATIONS_SYSCALLS, 1);

			// TODO: due to the way the locking in the notification
			// manager works, there's a potential race condition here
			// where someone attempts to add a listener right as
			// we're uninitializing. Needs to be looked at/resolved.
			sNotificationService.Unregister();

			// we need to release the reference that was acquired
			// on our behalf by the NotificationManager.
			sNotificationService.ReleaseReference();
			sNotificationService.~NetNotificationService();
			return B_OK;

		default:
			return B_ERROR;
	}
}


net_notifications_module_info sNotificationsModule = {
	{
		NET_NOTIFICATIONS_MODULE_NAME,
		0,
		notifications_std_ops
	},

	send_notification
};

module_info* modules[] = {
	(module_info*)&sNotificationsModule,
	NULL
};