diff --git a/headers/private/kernel/ksystem_info.h b/headers/private/kernel/ksystem_info.h index adf9ad76ba..48c1b85c3f 100644 --- a/headers/private/kernel/ksystem_info.h +++ b/headers/private/kernel/ksystem_info.h @@ -8,6 +8,7 @@ #include + struct kernel_args; @@ -15,15 +16,24 @@ struct kernel_args; 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 */ diff --git a/headers/private/system/syscalls.h b/headers/private/system/syscalls.h index 1217c0375f..a09a82e26e 100644 --- a/headers/private/system/syscalls.h +++ b/headers/private/system/syscalls.h @@ -174,6 +174,11 @@ extern status_t _kern_get_team_usage_info(team_id team, int32 who, 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, diff --git a/headers/private/system/system_info.h b/headers/private/system/system_info.h index 574fef5396..0a8843d359 100644 --- a/headers/private/system/system_info.h +++ b/headers/private/system/system_info.h @@ -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 @@ struct system_memory_info { }; +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 */ diff --git a/src/system/kernel/main.cpp b/src/system/kernel/main.cpp index b56c334557..eca43c7398 100644 --- a/src/system/kernel/main.cpp +++ b/src/system/kernel/main.cpp @@ -270,6 +270,9 @@ main2(void *unused) 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(); diff --git a/src/system/kernel/system_info.cpp b/src/system/kernel/system_info.cpp index 3070eae2d2..74d19cd77d 100644 --- a/src/system/kernel/system_info.cpp +++ b/src/system/kernel/system_info.cpp @@ -5,6 +5,7 @@ * Authors: * Stefano Ceccherini * Axel Dörfler, axeld@pinc-software.de + * Ingo Weinhold, ingo_weinhold@gmx.de */ @@ -17,16 +18,22 @@ #include #include +#include + #include #include #include #include +#include +#include +#include #include #include #include #include #include #include +#include #include #include @@ -66,6 +73,335 @@ dump_info(int argc, char **argv) } +// #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 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 listLink; + ListenerList* list; + port_id port; + int32 token; + uint32 flags; + + virtual void OwnerDeleted(AssociatedDataOwner* owner); + }; + + friend struct Listener; + + struct ListenerList { + typedef DoublyLinkedList > 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 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 @@ system_info_init(struct kernel_args *args) } +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 @@ _user_get_system_info_etc(int32 id, void* userInfo, size_t size) 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); +} diff --git a/src/system/libroot/os/system_info.c b/src/system/libroot/os/system_info.c index 3af55a1c72..9b67661157 100644 --- a/src/system/libroot/os/system_info.c +++ b/src/system/libroot/os/system_info.c @@ -30,6 +30,20 @@ get_system_info_etc(int32 id, void *info, size_t size) } +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) {