From 5f2abaf7df37f5e2e6cab5dffeed209e89759e07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Axel=20D=C3=B6rfler?= Date: Sun, 25 Oct 2015 14:22:00 +0200 Subject: [PATCH] launch_daemon: Added network_available event & condition. * Not yet tested. --- src/servers/launch/Conditions.cpp | 36 ++++++ src/servers/launch/Events.cpp | 99 ++++++++++++++++ src/servers/launch/Jamfile | 3 +- src/servers/launch/NetworkWatcher.cpp | 165 ++++++++++++++++++++++++++ src/servers/launch/NetworkWatcher.h | 47 ++++++++ 5 files changed, 349 insertions(+), 1 deletion(-) create mode 100644 src/servers/launch/NetworkWatcher.cpp create mode 100644 src/servers/launch/NetworkWatcher.h diff --git a/src/servers/launch/Conditions.cpp b/src/servers/launch/Conditions.cpp index 0231112876..66816197ca 100644 --- a/src/servers/launch/Conditions.cpp +++ b/src/servers/launch/Conditions.cpp @@ -13,6 +13,7 @@ #include #include +#include "NetworkWatcher.h" #include "Utility.h" @@ -102,6 +103,15 @@ private: }; +class NetworkAvailableCondition : public Condition { +public: + virtual bool Test(ConditionContext& context) const; + virtual bool IsConstant(ConditionContext& context) const; + + virtual BString ToString() const; +}; + + static Condition* create_condition(const char* name, const BMessage& args) { @@ -118,6 +128,8 @@ create_condition(const char* name, const BMessage& args) return new ReadOnlyCondition(args); if (strcmp(name, "file_exists") == 0) return new FileExistsCondition(args); + if (strcmp(name, "network_available") == 0) + return new NetworkAvailableCondition(); return NULL; } @@ -445,6 +457,30 @@ FileExistsCondition::ToString() const } +// #pragma mark - network_available + + +bool +NetworkAvailableCondition::Test(ConditionContext& context) const +{ + return NetworkWatcher::NetworkAvailable(false); +} + + +bool +NetworkAvailableCondition::IsConstant(ConditionContext& context) const +{ + return false; +} + + +BString +NetworkAvailableCondition::ToString() const +{ + return "network_available"; +} + + // #pragma mark - diff --git a/src/servers/launch/Events.cpp b/src/servers/launch/Events.cpp index e623aa4fc6..400fb3566b 100644 --- a/src/servers/launch/Events.cpp +++ b/src/servers/launch/Events.cpp @@ -17,6 +17,7 @@ #include "BaseJob.h" #include "LaunchDaemon.h" +#include "NetworkWatcher.h" #include "Utility.h" #include "VolumeWatcher.h" @@ -67,6 +68,16 @@ public: }; +class StickyEvent : public Event { +public: + StickyEvent(Event* parent); + virtual ~StickyEvent(); + + virtual void ResetSticky(); + virtual void ResetTrigger(); +}; + + class DemandEvent : public Event { public: DemandEvent(Event* parent); @@ -132,6 +143,20 @@ public: }; +class NetworkAvailableEvent : public StickyEvent, public NetworkListener { +public: + NetworkAvailableEvent(Event* parent, + const BMessage& args); + + virtual status_t Register(EventRegistrator& registrator); + virtual void Unregister(EventRegistrator& registrator); + + virtual BString ToString() const; + + virtual void NetworkAvailabilityChanged(bool available); +}; + + static Event* create_event(Event* parent, const char* name, const BMessenger* target, const BMessage& args) @@ -149,6 +174,8 @@ create_event(Event* parent, const char* name, const BMessenger* target, return new FileCreatedEvent(parent, args); if (strcmp(name, "volume_mounted") == 0) return new VolumeMountedEvent(parent, args); + if (strcmp(name, "network_available") == 0) + return new NetworkAvailableEvent(parent, args); return new ExternalEvent(parent, name, args); } @@ -389,6 +416,35 @@ OrEvent::ToString() const } +// #pragma mark - StickyEvent + + +StickyEvent::StickyEvent(Event* parent) + : + Event(parent) +{ +} + + +StickyEvent::~StickyEvent() +{ +} + + +void +StickyEvent::ResetSticky() +{ + Event::ResetTrigger(); +} + + +void +StickyEvent::ResetTrigger() +{ + // This is a sticky event; we don't reset the trigger here +} + + // #pragma mark - demand @@ -576,6 +632,49 @@ VolumeMountedEvent::VolumeUnmounted(dev_t device) // #pragma mark - +NetworkAvailableEvent::NetworkAvailableEvent(Event* parent, + const BMessage& args) + : + StickyEvent(parent) +{ +} + + +status_t +NetworkAvailableEvent::Register(EventRegistrator& registrator) +{ + NetworkWatcher::Register(this); + return B_OK; +} + + +void +NetworkAvailableEvent::Unregister(EventRegistrator& registrator) +{ + NetworkWatcher::Unregister(this); +} + + +BString +NetworkAvailableEvent::ToString() const +{ + return "network_available"; +} + + +void +NetworkAvailableEvent::NetworkAvailabilityChanged(bool available) +{ + if (available) + Trigger(); + else + ResetSticky(); +} + + +// #pragma mark - + + /*static*/ Event* Events::FromMessage(const BMessenger& target, const BMessage& message) { diff --git a/src/servers/launch/Jamfile b/src/servers/launch/Jamfile index e7e219894d..a6e7a3d57f 100644 --- a/src/servers/launch/Jamfile +++ b/src/servers/launch/Jamfile @@ -13,6 +13,7 @@ Server launch_daemon Conditions.cpp Events.cpp Job.cpp + NetworkWatcher.cpp SettingsParser.cpp Target.cpp Utility.cpp @@ -25,7 +26,7 @@ Server launch_daemon InitSharedMemoryDirectoryJob.cpp InitTemporaryDirectoryJob.cpp : - be libshared.a libmultiuser_utils.a [ TargetLibstdc++ ] + be network bnetapi libshared.a libmultiuser_utils.a [ TargetLibstdc++ ] : LaunchDaemon.rdef ; diff --git a/src/servers/launch/NetworkWatcher.cpp b/src/servers/launch/NetworkWatcher.cpp new file mode 100644 index 0000000000..442ef02a98 --- /dev/null +++ b/src/servers/launch/NetworkWatcher.cpp @@ -0,0 +1,165 @@ +/* + * Copyright 2015, Axel Dörfler, axeld@pinc-software.de. + * Distributed under the terms of the MIT License. + */ + + +//! The backbone of the NetworkAvailable event, and condition. + + +#include "NetworkWatcher.h" + +#include +#include +#include +#include +#include + +#include "Utility.h" + + +static const bigtime_t kNetworkUpdateInterval = 1000000; + // Update network availability every second + +static BLocker sLocker("network watcher"); +static NetworkWatcher* sWatcher; + +static bool sLastNetworkAvailable; +static bigtime_t sLastNetworkUpdate; + + +NetworkListener::~NetworkListener() +{ +} + + +// #pragma mark - + + +NetworkWatcher::NetworkWatcher() + : + BHandler("network watcher"), + fAvailable(false) +{ + if (be_app->Lock()) { + be_app->AddHandler(this); + + start_watching_network(B_WATCH_NETWORK_INTERFACE_CHANGES + | B_WATCH_NETWORK_LINK_CHANGES, this); + be_app->Unlock(); + } +} + + +NetworkWatcher::~NetworkWatcher() +{ + if (be_app->Lock()) { + stop_watching_network(this); + + be_app->RemoveHandler(this); + be_app->Unlock(); + } +} + + +void +NetworkWatcher::AddListener(NetworkListener* listener) +{ + BAutolock lock(sLocker); + fListeners.AddItem(listener); + + if (fListeners.CountItems() == 1) + UpdateAvailability(); +} + + +void +NetworkWatcher::RemoveListener(NetworkListener* listener) +{ + BAutolock lock(sLocker); + fListeners.RemoveItem(listener); +} + + +int32 +NetworkWatcher::CountListeners() const +{ + BAutolock lock(sLocker); + return fListeners.CountItems(); +} + + +void +NetworkWatcher::MessageReceived(BMessage* message) +{ + switch (message->what) { + case B_NETWORK_MONITOR: + UpdateAvailability(); + break; + } +} + + +/*static*/ void +NetworkWatcher::Register(NetworkListener* listener) +{ + BAutolock lock(sLocker); + if (sWatcher == NULL) + sWatcher = new NetworkWatcher(); + + sWatcher->AddListener(listener); +} + + +/*static*/ void +NetworkWatcher::Unregister(NetworkListener* listener) +{ + BAutolock lock(sLocker); + sWatcher->RemoveListener(listener); + + if (sWatcher->CountListeners() == 0) + delete sWatcher; +} + + +/*static*/ bool +NetworkWatcher::NetworkAvailable(bool immediate) +{ + if (!immediate + && system_time() - sLastNetworkUpdate < kNetworkUpdateInterval) { + return sLastNetworkAvailable; + } + + bool isAvailable = false; + + BNetworkRoster& roster = BNetworkRoster::Default(); + BNetworkInterface interface; + uint32 cookie = 0; + while (roster.GetNextInterface(&cookie, interface) == B_OK) { + uint32 flags = interface.Flags(); + if ((flags & (IFF_LOOPBACK | IFF_CONFIGURING | IFF_UP | IFF_LINK)) + == (IFF_UP | IFF_LINK)) { + isAvailable = true; + break; + } + } + + sLastNetworkAvailable = isAvailable; + sLastNetworkUpdate = system_time(); + return isAvailable; +} + + +void +NetworkWatcher::UpdateAvailability() +{ + bool isAvailable = NetworkAvailable(true); + if (isAvailable != fAvailable) { + fAvailable = isAvailable; + + BAutolock lock(sLocker); + for (int32 i = 0; i < fListeners.CountItems(); i++) { + fListeners.ItemAt(i)->NetworkAvailabilityChanged(fAvailable); + } + } +} diff --git a/src/servers/launch/NetworkWatcher.h b/src/servers/launch/NetworkWatcher.h new file mode 100644 index 0000000000..e2a6ff66cf --- /dev/null +++ b/src/servers/launch/NetworkWatcher.h @@ -0,0 +1,47 @@ +/* + * Copyright 2015, Axel Dörfler, axeld@pinc-software.de. + * Distributed under the terms of the MIT License. + */ +#ifndef NETWORK_WATCHER_H +#define NETWORK_WATCHER_H + + +#include +#include + + +class NetworkListener { +public: + virtual ~NetworkListener(); + + virtual void NetworkAvailabilityChanged(bool available) = 0; +}; + + +class NetworkWatcher : public BHandler { +public: + NetworkWatcher(); + virtual ~NetworkWatcher(); + + void AddListener(NetworkListener* listener); + void RemoveListener(NetworkListener* listener); + int32 CountListeners() const; + + virtual void MessageReceived(BMessage* message); + + static void Register(NetworkListener* listener); + static void Unregister(NetworkListener* listener); + + static bool NetworkAvailable(bool immediate); + +protected: + void UpdateAvailability(); + +protected: + BObjectList + fListeners; + bool fAvailable; +}; + + +#endif // NETWORK_WATCHER_H