Author: bonefish Date: 2010-12-16 02:49:52 +0100 (Thu, 16 Dec 2010) New Revision: 39862 Changeset: http://dev.haiku-os.org/changeset/39862 Modified: haiku/trunk/headers/private/kernel/ksystem_info.h haiku/trunk/headers/private/system/syscalls.h haiku/trunk/headers/private/system/system_info.h haiku/trunk/src/system/kernel/main.cpp haiku/trunk/src/system/kernel/system_info.cpp haiku/trunk/src/system/libroot/os/system_info.c Log: Implemented a generic system watching mechanism with a userland API. Currently only a few events can be watched (team creation/deletion/exec, thread creation/ deletion/name changes). The functions start_system_watching()/ stop_system_watching start/stop watching events. Modified: haiku/trunk/headers/private/kernel/ksystem_info.h =================================================================== --- haiku/trunk/headers/private/kernel/ksystem_info.h 2010-12-16 01:44:15 UTC (rev 39861) +++ haiku/trunk/headers/private/kernel/ksystem_info.h 2010-12-16 01:49:52 UTC (rev 39862) @@ -8,6 +8,7 @@ #include <OS.h> + struct kernel_args; @@ -15,15 +16,24 @@ extern "C" { #endif -extern status_t system_info_init(struct kernel_args *args); -extern uint32 get_haiku_revision(void); -extern status_t _user_get_system_info(system_info *userInfo, size_t size); -extern status_t _user_get_system_info_etc(int32 id, void *buffer, - size_t bufferSize); +status_t system_info_init(struct kernel_args *args); +status_t system_notifications_init(); +uint32 get_haiku_revision(void); +status_t _user_get_system_info(system_info *userInfo, size_t size); +status_t _user_get_system_info_etc(int32 id, void *buffer, + size_t bufferSize); + +status_t _user_start_system_watching(int32 object, uint32 flags, + port_id port, int32 token); +status_t _user_stop_system_watching(int32 object, uint32 flags, + port_id port, int32 token); + + #ifdef __cplusplus } #endif + #endif /* _KERNEL_SYSTEM_INFO_H */ Modified: haiku/trunk/headers/private/system/syscalls.h =================================================================== --- haiku/trunk/headers/private/system/syscalls.h 2010-12-16 01:44:15 UTC (rev 39861) +++ haiku/trunk/headers/private/system/syscalls.h 2010-12-16 01:49:52 UTC (rev 39862) @@ -174,6 +174,11 @@ extern status_t _kern_get_extended_team_info(team_id teamID, uint32 flags, void* buffer, size_t size, size_t* _sizeNeeded); +extern status_t _kern_start_system_watching(int32 object, uint32 flags, + port_id port, int32 token); +extern status_t _kern_stop_system_watching(int32 object, uint32 flags, + port_id port, int32 token); + extern status_t _kern_block_thread(uint32 flags, bigtime_t timeout); extern status_t _kern_unblock_thread(thread_id thread, status_t status); extern status_t _kern_unblock_threads(thread_id* threads, uint32 count, Modified: haiku/trunk/headers/private/system/system_info.h =================================================================== --- haiku/trunk/headers/private/system/system_info.h 2010-12-16 01:44:15 UTC (rev 39861) +++ haiku/trunk/headers/private/system/system_info.h 2010-12-16 01:49:52 UTC (rev 39862) @@ -1,5 +1,5 @@ /* - * Copyright 2008 Haiku, Inc. All rights reserved. + * Copyright 2008-2010, Haiku, Inc. All rights reserved. * Distributed under the terms of the MIT License. */ #ifndef _SYSTEM_INFO_H @@ -24,14 +24,56 @@ }; +enum { + // team creation or deletion; object == -1; either one also triggers on + // exec() + B_WATCH_SYSTEM_TEAM_CREATION = 0x01, + B_WATCH_SYSTEM_TEAM_DELETION = 0x02, + + // thread creation or deletion or property (name, priority) changes; + // object == team ID or -1 for all teams + B_WATCH_SYSTEM_THREAD_CREATION = 0x04, + B_WATCH_SYSTEM_THREAD_DELETION = 0x08, + B_WATCH_SYSTEM_THREAD_PROPERTIES = 0x10, + + B_WATCH_SYSTEM_ALL + = B_WATCH_SYSTEM_TEAM_CREATION + | B_WATCH_SYSTEM_TEAM_DELETION + | B_WATCH_SYSTEM_THREAD_CREATION + | B_WATCH_SYSTEM_THREAD_DELETION + | B_WATCH_SYSTEM_THREAD_PROPERTIES +}; + +enum { + // message what for the notification messages + B_SYSTEM_OBJECT_UPDATE = 'SOUP', + + // "opcode" values + B_TEAM_CREATED = 0, + B_TEAM_DELETED = 1, + B_TEAM_EXEC = 2, + B_THREAD_CREATED = 3, + B_THREAD_DELETED = 4, + B_THREAD_NAME_CHANGED = 5 +}; + + #ifdef __cplusplus extern "C" { #endif -extern status_t get_system_info_etc(int32 id, void *buffer, size_t bufferSize); +status_t get_system_info_etc(int32 id, void* buffer, size_t bufferSize); + +status_t start_system_watching(int32 object, uint32 flags, port_id port, + int32 token); +status_t stop_system_watching(int32 object, uint32 flags, port_id port, + int32 token); + + #ifdef __cplusplus } #endif + #endif /* _SYSTEM_INFO_H */ Modified: haiku/trunk/src/system/kernel/main.cpp =================================================================== --- haiku/trunk/src/system/kernel/main.cpp 2010-12-16 01:44:15 UTC (rev 39861) +++ haiku/trunk/src/system/kernel/main.cpp 2010-12-16 01:49:52 UTC (rev 39862) @@ -270,6 +270,9 @@ TRACE("init user mutex\n"); user_mutex_init(); + TRACE("init system notifications\n"); + system_notifications_init(); + TRACE("Init modules\n"); boot_splash_set_stage(BOOT_SPLASH_STAGE_1_INIT_MODULES); module_init_post_threads(); Modified: haiku/trunk/src/system/kernel/system_info.cpp =================================================================== --- haiku/trunk/src/system/kernel/system_info.cpp 2010-12-16 01:44:15 UTC (rev 39861) +++ haiku/trunk/src/system/kernel/system_info.cpp 2010-12-16 01:49:52 UTC (rev 39862) @@ -5,6 +5,7 @@ * Authors: * Stefano Ceccherini * Axel Dörfler, axeld@xxxxxxxxxxxxxxxx + * Ingo Weinhold, ingo_weinhold@xxxxxx */ @@ -17,16 +18,22 @@ #include <OS.h> #include <KernelExport.h> +#include <AutoDeleter.h> + #include <block_cache.h> #include <cpu.h> #include <debug.h> #include <kernel.h> +#include <lock.h> +#include <Notifications.h> +#include <messaging.h> #include <port.h> #include <real_time_clock.h> #include <sem.h> #include <smp.h> #include <team.h> #include <thread.h> +#include <util/AutoLock.h> #include <vm/vm.h> #include <vm/vm_page.h> @@ -66,6 +73,335 @@ } +// #pragma mark - user notifications + + +class SystemNotificationService : private NotificationListener { +public: + SystemNotificationService() + { + mutex_init(&fLock, "system notification service"); + } + + status_t Init() + { + status_t error = fTeamListeners.Init(); + if (error != B_OK) + return error; + + error = NotificationManager::Manager().AddListener("teams", + TEAM_ADDED | TEAM_REMOVED | TEAM_EXEC, *this); + if (error != B_OK) + return error; + + error = NotificationManager::Manager().AddListener("threads", + THREAD_ADDED | THREAD_REMOVED | TEAM_EXEC, *this); + if (error != B_OK) + return error; + + return B_OK; + } + + status_t StartListening(int32 object, uint32 flags, port_id port, + int32 token) + { + // check the parameters + if ((object < 0 && object != -1) || port < 0) + return B_BAD_VALUE; + + if ((flags & B_WATCH_SYSTEM_ALL) == 0 + || (flags & ~(uint32)B_WATCH_SYSTEM_ALL) != 0) { + return B_BAD_VALUE; + } + + MutexLocker locker(fLock); + + // maybe the listener already exists + ListenerList* listenerList; + Listener* listener = _FindListener(object, port, token, listenerList); + if (listener != NULL) { + // just add the new flags + listener->flags |= flags; + return B_OK; + } + + // create a new listener + listener = new(std::nothrow) Listener; + if (listener == NULL) + return B_NO_MEMORY; + ObjectDeleter<Listener> listenerDeleter(listener); + + listener->port = port; + listener->token = token; + listener->flags = flags; + + // if there's no list yet, create a new list + if (listenerList == NULL) { + listenerList = new(std::nothrow) ListenerList; + if (listenerList == NULL) + return B_NO_MEMORY; + + listenerList->object = object; + + fTeamListeners.Insert(listenerList); + } + + listener->list = listenerList; + listenerList->listeners.Add(listener); + listenerDeleter.Detach(); + + team_associate_data(listener); + + return B_OK; + } + + status_t StopListening(int32 object, uint32 flags, port_id port, + int32 token) + { + MutexLocker locker(fLock); + + // find the listener + ListenerList* listenerList; + Listener* listener = _FindListener(object, port, token, listenerList); + if (listener == NULL) + return B_ENTRY_NOT_FOUND; + + // clear the given flags + listener->flags &= ~flags; + + if (listener->flags != 0) + return B_OK; + + team_dissociate_data(listener); + _RemoveListener(listener); + + return B_OK; + } + +private: + struct ListenerList; + + struct Listener : AssociatedData { + DoublyLinkedListLink<Listener> listLink; + ListenerList* list; + port_id port; + int32 token; + uint32 flags; + + virtual void OwnerDeleted(AssociatedDataOwner* owner); + }; + + friend struct Listener; + + struct ListenerList { + typedef DoublyLinkedList<Listener, + DoublyLinkedListMemberGetLink<Listener, &Listener::listLink> > List; + + ListenerList* hashNext; + List listeners; + int32 object; + }; + + struct ListenerHashDefinition { + typedef int32 KeyType; + typedef ListenerList ValueType; + + size_t HashKey(int32 key) const + { + return key; + } + + size_t Hash(const ListenerList* value) const + { + return HashKey(value->object); + } + + bool Compare(int32 key, const ListenerList* value) const + { + return value->object == key; + } + + ListenerList*& GetLink(ListenerList* value) const + { + return value->hashNext; + } + }; + + typedef BOpenHashTable<ListenerHashDefinition> ListenerHash; + +private: + virtual void EventOccurred(NotificationService& service, + const KMessage* event) + { + MutexLocker locker(fLock); + + int32 eventCode; + int32 teamID; + if (event->FindInt32("event", &eventCode) != B_OK + || event->FindInt32("team", &teamID) != B_OK) { + return; + } + + int32 object; + uint32 opcode; + uint32 flags; + + // translate the event + if (event->What() == TEAM_MONITOR) { + switch (eventCode) { + case TEAM_ADDED: + opcode = B_TEAM_CREATED; + flags = B_WATCH_SYSTEM_TEAM_CREATION; + break; + case TEAM_REMOVED: + opcode = B_TEAM_DELETED; + flags = B_WATCH_SYSTEM_TEAM_DELETION; + break; + case TEAM_EXEC: + opcode = B_TEAM_EXEC; + flags = B_WATCH_SYSTEM_TEAM_CREATION + | B_WATCH_SYSTEM_TEAM_DELETION; + break; + default: + return; + } + + object = teamID; + } else if (event->What() == THREAD_MONITOR) { + if (event->FindInt32("thread", &object) != B_OK) + return; + + switch (eventCode) { + case THREAD_ADDED: + opcode = B_THREAD_CREATED; + flags = B_WATCH_SYSTEM_THREAD_CREATION; + break; + case THREAD_REMOVED: + opcode = B_THREAD_DELETED; + flags = B_WATCH_SYSTEM_THREAD_DELETION; + break; + case THREAD_NAME_CHANGED: + opcode = B_THREAD_NAME_CHANGED; + flags = B_WATCH_SYSTEM_THREAD_PROPERTIES; + break; + default: + return; + } + } else + return; + + // find matching listeners + messaging_target targets[kMaxMessagingTargetCount]; + int32 targetCount = 0; + + _AddTargets(fTeamListeners.Lookup(teamID), flags, targets, + targetCount, object, opcode); + _AddTargets(fTeamListeners.Lookup(-1), flags, targets, targetCount, + object, opcode); + + // send the message + if (targetCount > 0) + _SendMessage(targets, targetCount, teamID, opcode); + } + + void _AddTargets(ListenerList* listenerList, uint32 flags, + messaging_target* targets, int32& targetCount, int32 object, + uint32 opcode) + { + if (listenerList == NULL) + return; + + for (ListenerList::List::Iterator it + = listenerList->listeners.GetIterator(); + Listener* listener = it.Next();) { + if ((listener->flags & flags) == 0) + continue; + + // array is full -- need to flush it first + if (targetCount == kMaxMessagingTargetCount) { + _SendMessage(targets, targetCount, object, opcode); + targetCount = 0; + } + + // add the listener + targets[targetCount].port = listener->port; + targets[targetCount++].token = listener->token; + } + } + + void _SendMessage(messaging_target* targets, int32 targetCount, + int32 object, uint32 opcode) + { + // prepare the message + char buffer[128]; + KMessage message; + message.SetTo(buffer, sizeof(buffer), B_SYSTEM_OBJECT_UPDATE); + message.AddInt32("opcode", opcode); + if (opcode < B_THREAD_CREATED) + message.AddInt32("team", object); + else + message.AddInt32("thread", object); + + // send it + send_message(message.Buffer(), message.ContentSize(), targets, + targetCount); + } + + Listener* _FindListener(int32 object, port_id port, int32 token, + ListenerList*& _listenerList) + { + _listenerList = fTeamListeners.Lookup(object); + if (_listenerList == NULL) + return NULL; + + for (ListenerList::List::Iterator it + = _listenerList->listeners.GetIterator(); + Listener* listener = it.Next();) { + if (listener->port == port && listener->token == token) + return listener; + } + + return NULL; + } + + void _RemoveObsoleteListener(Listener* listener) + { + MutexLocker locker(fLock); + _RemoveListener(listener); + } + + void _RemoveListener(Listener* listener) + { + // no flags anymore -- remove the listener + ListenerList* listenerList = listener->list; + listenerList->listeners.Remove(listener); + listener->ReleaseReference(); + + if (listenerList->listeners.IsEmpty()) { + // no listeners in the list anymore -- remove the list from the hash + // table + fTeamListeners.Remove(listenerList); + delete listenerList; + } + } + +private: + static const int32 kMaxMessagingTargetCount = 8; + + mutex fLock; + ListenerHash fTeamListeners; +}; + +static SystemNotificationService sSystemNotificationService; + + +void +SystemNotificationService::Listener::OwnerDeleted(AssociatedDataOwner* owner) +{ + sSystemNotificationService._RemoveObsoleteListener(this); +} + + // #pragma mark - @@ -115,6 +451,21 @@ } +status_t +system_notifications_init() +{ + new (&sSystemNotificationService) SystemNotificationService; + + status_t error = sSystemNotificationService.Init(); + if (error != B_OK) { + panic("system_info_init(): Failed to init system notification service"); + return error; + } + + return B_OK; +} + + uint32 get_haiku_revision(void) { @@ -170,3 +521,20 @@ return B_BAD_VALUE; } } + + +status_t +_user_start_system_watching(int32 object, uint32 flags, port_id port, + int32 token) +{ + return sSystemNotificationService.StartListening(object, flags, port, + token); +} + + +status_t +_user_stop_system_watching(int32 object, uint32 flags, port_id port, + int32 token) +{ + return sSystemNotificationService.StopListening(object, flags, port, token); +} Modified: haiku/trunk/src/system/libroot/os/system_info.c =================================================================== --- haiku/trunk/src/system/libroot/os/system_info.c 2010-12-16 01:44:15 UTC (rev 39861) +++ haiku/trunk/src/system/libroot/os/system_info.c 2010-12-16 01:49:52 UTC (rev 39862) @@ -30,6 +30,20 @@ } +status_t +start_system_watching(int32 object, uint32 flags, port_id port, int32 token) +{ + return _kern_start_system_watching(object, flags, port, token); +} + + +status_t +stop_system_watching(int32 object, uint32 flags, port_id port, int32 token) +{ + return _kern_stop_system_watching(object, flags, port, token); +} + + int32 is_computer_on(void) {