Auto hotspot implementation
This commit is contained in:
+13
-1
@@ -9,6 +9,8 @@ set(CXX_STANDARD 20)
|
||||
|
||||
include(FindPkgConfig)
|
||||
|
||||
find_package(sdbus-c++ REQUIRED)
|
||||
|
||||
|
||||
if(DEFINED ENV{GITHUB_ACTIONS})
|
||||
message(STATUS "Building in GitHub Actions environment")
|
||||
@@ -144,6 +146,8 @@ else()
|
||||
endif()
|
||||
|
||||
set (PIPEDAL_SOURCES
|
||||
DBusToLv2Log.cpp DBusToLv2Log.hpp
|
||||
HotspotManager.cpp HotspotManager.hpp
|
||||
UpdateResults.cpp UpdateResults.hpp
|
||||
UpdaterSecurity.hpp
|
||||
Updater.cpp Updater.hpp
|
||||
@@ -252,6 +256,7 @@ set (PIPEDAL_INCLUDES
|
||||
)
|
||||
|
||||
set(PIPEDAL_LIBS libpipedald zip
|
||||
PiPedalCommon
|
||||
icui18n
|
||||
icuuc
|
||||
icudata
|
||||
@@ -290,6 +295,13 @@ target_link_libraries(pipedald PRIVATE PiPedalCommon
|
||||
)
|
||||
|
||||
#################################
|
||||
add_executable(hotspotManagerTest
|
||||
hotspotManagerTestMain.cpp
|
||||
HotspotManager.cpp HotspotManager.hpp Lv2Log.cpp Lv2Log.hpp)
|
||||
|
||||
target_link_libraries(hotspotManagerTest PRIVATE ${PIPEDAL_LIBS})
|
||||
|
||||
|
||||
add_executable(pipedaltest testMain.cpp
|
||||
|
||||
InvertingMutexTest.cpp
|
||||
@@ -313,7 +325,7 @@ add_executable(pipedaltest testMain.cpp
|
||||
MemDebug.cpp
|
||||
MemDebug.hpp
|
||||
)
|
||||
target_link_libraries(pipedaltest PRIVATE PiPedalCommon)
|
||||
target_link_libraries(pipedaltest PRIVATE ${PIPEDAL_LIBS})
|
||||
target_include_directories(pipedaltest PRIVATE ${PIPEDAL_INCLUDES}
|
||||
)
|
||||
|
||||
|
||||
@@ -1,3 +1,22 @@
|
||||
// Copyright (c) 2022-2024 Robin Davies
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
// this software and associated documentation files (the "Software"), to deal in
|
||||
// the Software without restriction, including without limitation the rights to
|
||||
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
// the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
// subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
#include "Storage.hpp"
|
||||
#include "CommandLineParser.hpp"
|
||||
#include <filesystem>
|
||||
|
||||
+99
-5
@@ -22,6 +22,8 @@
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include "CommandLineParser.hpp"
|
||||
#include "SystemConfigFile.hpp"
|
||||
|
||||
#include <filesystem>
|
||||
#include <stdlib.h>
|
||||
#include "WriteTemplateFile.hpp"
|
||||
@@ -42,6 +44,7 @@
|
||||
#include <grp.h>
|
||||
#include "ofstream_synced.hpp"
|
||||
|
||||
#define P2PD_DISABLED
|
||||
|
||||
#if JACK_HOST
|
||||
#define INSTALL_JACK_SERVICE 1
|
||||
@@ -64,6 +67,7 @@ namespace fs = std::filesystem;
|
||||
#define JACK_SERVICE_ACCOUNT_NAME "jack"
|
||||
#define AUDIO_SERVICE_GROUP_NAME "audio"
|
||||
#define JACK_SERVICE_GROUP_NAME AUDIO_SERVICE_GROUP_NAME
|
||||
#define NETDEV_GROUP_NAME "netdev"
|
||||
|
||||
#define SYSTEMCTL_BIN "/usr/bin/systemctl"
|
||||
#define GROUPADD_BIN "/usr/sbin/groupadd"
|
||||
@@ -151,6 +155,86 @@ void DisableService()
|
||||
#endif
|
||||
}
|
||||
|
||||
static void RestartAvahiService()
|
||||
{
|
||||
silentSysExec(SS(SYSTEMCTL_BIN << " restart avahi-daemon.service").c_str());
|
||||
}
|
||||
static void AvahiInstall()
|
||||
{
|
||||
// disable IPv6 mdns broadcasts. Avahi broadcasts link-local IPV6 addresses which are unusually difficult to deal with.
|
||||
|
||||
try
|
||||
{
|
||||
std::filesystem::path avahiConfig("/etc/avahi/avahi-daemon.conf");
|
||||
SystemConfigFile avahi(avahiConfig);
|
||||
|
||||
bool changed = avahi.RemoveUndoableActions();
|
||||
int line = avahi.GetLineThatStartsWith("use-ipv6=yes");
|
||||
if (line != -1)
|
||||
{
|
||||
avahi.UndoableReplaceLine(line, "use-ipv6=no");
|
||||
changed = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (avahi.GetLineThatStartsWith("use-ipv6=no") == -1)
|
||||
{
|
||||
line = avahi.GetLineThatStartsWith("[server]");
|
||||
if (line == 1)
|
||||
{
|
||||
throw std::runtime_error("Unable to find [server] section.");
|
||||
}
|
||||
{
|
||||
// increment to end of section.
|
||||
while (line < avahi.GetLineCount())
|
||||
{
|
||||
const auto &txt = avahi.Get(line);
|
||||
if (txt.empty())
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (txt.starts_with("[")) // start of next section.
|
||||
{
|
||||
break;
|
||||
}
|
||||
++line;
|
||||
}
|
||||
}
|
||||
avahi.UndoableAddLine(avahi.GetLineCount(), "use-ipv6=no");
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
if (changed)
|
||||
{
|
||||
avahi.Save();
|
||||
RestartAvahiService();
|
||||
}
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
cout << "Warning: Unabled to disable Ipv6 mDNS announcements. " << e.what() << endl;
|
||||
}
|
||||
}
|
||||
|
||||
static void AvahiUninstall()
|
||||
{
|
||||
try
|
||||
{
|
||||
std::filesystem::path avahiConfig("/etc/avahi/avahi-daemon.conf");
|
||||
SystemConfigFile avahi(avahiConfig);
|
||||
|
||||
if (avahi.RemoveUndoableActions())
|
||||
{
|
||||
avahi.Save();
|
||||
RestartAvahiService();
|
||||
}
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
cout << " Warning: Unable to restore Avahi Daemon configuration. " << e.what() << endl;
|
||||
}
|
||||
}
|
||||
|
||||
void StopService(bool excludeShutdownService = false)
|
||||
{
|
||||
if (sysExec(SYSTEMCTL_BIN " stop " PIPEDALD_SERVICE ".service") != EXIT_SUCCESS)
|
||||
@@ -526,6 +610,8 @@ void Uninstall()
|
||||
{
|
||||
}
|
||||
UninstallPamEnv();
|
||||
AvahiUninstall();
|
||||
|
||||
// UninstallLimits();
|
||||
sysExec(SYSTEMCTL_BIN " daemon-reload");
|
||||
}
|
||||
@@ -676,7 +762,8 @@ static void FixPermissions()
|
||||
struct passwd *passwd;
|
||||
if ((passwd = getpwnam("pipedal_d")) == nullptr)
|
||||
{
|
||||
cout << "Error: " << "User 'pipedal_d' does not exist." << endl;
|
||||
cout << "Error: "
|
||||
<< "User 'pipedal_d' does not exist." << endl;
|
||||
return;
|
||||
}
|
||||
uid = passwd->pw_uid;
|
||||
@@ -790,7 +877,6 @@ void InstallPgpKey()
|
||||
{
|
||||
cout << "Error: Failed to create update keyring." << endl;
|
||||
}
|
||||
|
||||
}
|
||||
{
|
||||
std::ostringstream ss;
|
||||
@@ -800,7 +886,6 @@ void InstallPgpKey()
|
||||
{
|
||||
cout << "Error: Failed to add update key." << endl;
|
||||
}
|
||||
|
||||
}
|
||||
{
|
||||
std::stringstream ss;
|
||||
@@ -825,6 +910,7 @@ void Install(const fs::path &programPrefix, const std::string endpointAddress)
|
||||
throw std::runtime_error("Failed to create pipedald service group.");
|
||||
}
|
||||
// defensively disable wifi p2p if some leftover config file left it enabled.
|
||||
#ifdef P2PD_DISABLED
|
||||
try
|
||||
{
|
||||
if (IsP2pServiceEnabled())
|
||||
@@ -838,6 +924,7 @@ void Install(const fs::path &programPrefix, const std::string endpointAddress)
|
||||
catch (const std::exception &)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
InstallAudioService();
|
||||
auto endpos = endpointAddress.find_last_of(':');
|
||||
@@ -888,6 +975,9 @@ void Install(const fs::path &programPrefix, const std::string endpointAddress)
|
||||
|
||||
// Add to audio groups.
|
||||
sysExec(USERMOD_BIN " -a -G " AUDIO_SERVICE_GROUP_NAME " " SERVICE_ACCOUNT_NAME);
|
||||
// add to netdev group
|
||||
sysExec(USERMOD_BIN " -a -G " NETDEV_GROUP_NAME " " SERVICE_ACCOUNT_NAME);
|
||||
|
||||
|
||||
// create and configure /var directory.
|
||||
|
||||
@@ -1007,12 +1097,16 @@ void Install(const fs::path &programPrefix, const std::string endpointAddress)
|
||||
sysExec(SYSTEMCTL_BIN " daemon-reload");
|
||||
|
||||
FixPermissions();
|
||||
RestartService(false);
|
||||
|
||||
StopService(false);
|
||||
AvahiInstall();
|
||||
InstallPgpKey();
|
||||
StartService(false);
|
||||
|
||||
EnableService();
|
||||
|
||||
// Restart WiFi Direct if neccessary.
|
||||
OnWifiReinstall();
|
||||
InstallPgpKey();
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
// Copyright (c) 2024 Robin Davies
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
// this software and associated documentation files (the "Software"), to deal in
|
||||
// the Software without restriction, including without limitation the rights to
|
||||
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
// the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
// subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
#include "DBusToLv2Log.hpp"
|
||||
#include "DBusLog.hpp"
|
||||
#include "Lv2Log.hpp"
|
||||
|
||||
using namespace pipedal;
|
||||
|
||||
class DBus2ToLv2Logger : public IDBusLogger
|
||||
{
|
||||
|
||||
public:
|
||||
virtual void LogError(const std::string &message) override
|
||||
{
|
||||
Lv2Log::error(message);
|
||||
}
|
||||
virtual void LogWarning(const std::string &message) override
|
||||
{
|
||||
Lv2Log::warning(message);
|
||||
}
|
||||
virtual void LogInfo(const std::string &message) override
|
||||
{
|
||||
Lv2Log::info(message);
|
||||
}
|
||||
virtual void LogDebug(const std::string &message) override
|
||||
{
|
||||
Lv2Log::debug(message);
|
||||
}
|
||||
virtual void LogTrace(const std::string &message) override
|
||||
{
|
||||
Lv2Log::debug(message);
|
||||
}
|
||||
};
|
||||
|
||||
void pipedal::DbusLogToLv2Log()
|
||||
{
|
||||
SetDBusLogger(std::make_unique<DBus2ToLv2Logger>());
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
// Copyright (c) 2024 Robin Davies
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
// this software and associated documentation files (the "Software"), to deal in
|
||||
// the Software without restriction, including without limitation the rights to
|
||||
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
// the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
// subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace pipedal {
|
||||
void DbusLogToLv2Log();
|
||||
}
|
||||
@@ -0,0 +1,851 @@
|
||||
// Copyright (c) 2022 Robin Davies
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
// this software and associated documentation files (the "Software"), to deal in
|
||||
// the Software without restriction, including without limitation the rights to
|
||||
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
// the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
// subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
#include "HotspotManager.hpp"
|
||||
#include <thread>
|
||||
#include "Lv2Log.hpp"
|
||||
#include "DBusLog.hpp"
|
||||
#include "DBusEvent.hpp"
|
||||
#include "DBusDispatcher.hpp"
|
||||
#include "NetworkManagerInterfaces.hpp"
|
||||
#include "ss.hpp"
|
||||
#include "WifiConfigSettings.hpp"
|
||||
#include "ServiceConfiguration.hpp"
|
||||
#include <unordered_set>
|
||||
#include <mutex>
|
||||
|
||||
using namespace pipedal;
|
||||
using namespace dbus::networkmanager;
|
||||
|
||||
namespace pipedal::impl
|
||||
{
|
||||
|
||||
constexpr std::string PIPEDAL_HOTSPOT_NAME = "PiPedal Hotspot";
|
||||
|
||||
class HotspotManagerImpl : public HotspotManager
|
||||
{
|
||||
|
||||
public:
|
||||
using ssid_t = WifiConfigSettings::ssid_t;
|
||||
|
||||
HotspotManagerImpl();
|
||||
virtual ~HotspotManagerImpl() noexcept;
|
||||
|
||||
virtual void Open() override;
|
||||
virtual void Reload() override;
|
||||
virtual void Close() override;
|
||||
|
||||
virtual PostHandle Post(PostCallback&&fn) override;
|
||||
virtual PostHandle PostDelayed(const clock::duration&delay,PostCallback&&fn) override;
|
||||
virtual bool CancelPost(PostHandle handle) override;
|
||||
|
||||
virtual void SetNetworkChangingListener(NetworkChangingListener &&listener) override;
|
||||
|
||||
|
||||
private:
|
||||
enum class State
|
||||
{
|
||||
Initial,
|
||||
WaitingForNetworkManager,
|
||||
Disabled,
|
||||
Monitoring,
|
||||
HotspotConnecting,
|
||||
HotspotConnected,
|
||||
Error,
|
||||
Closed
|
||||
};
|
||||
void SetState(State state);
|
||||
State state = State::Initial;
|
||||
|
||||
void onClose();
|
||||
void onError(const std::string &message);
|
||||
void onInitialize();
|
||||
void onDevicesChanged();
|
||||
void onDisconnect();
|
||||
void onReload();
|
||||
void onDisableHotspot();
|
||||
|
||||
void DisableHotspot();
|
||||
void EnableHotspot();
|
||||
void StartHotspot();
|
||||
void StopHotspot();
|
||||
void onStartMonitoring();
|
||||
void onAccessPointChanged();
|
||||
void onAccessPointsChanged();
|
||||
void CancelAccessPointsChangedTimer();
|
||||
void WaitForNetworkManager();
|
||||
void StartWaitForNetworkManagerTimer();
|
||||
void CancelWaitForNetworkManagerTimer();
|
||||
void UpdateNetworkManagerStatus();
|
||||
void CancelDeviceChangedTimer();
|
||||
void ReleaseNetworkManager();
|
||||
void OnEthernetStateChanged(uint32_t state);
|
||||
void OnWlanStateChanged(uint32_t state);
|
||||
std::vector<ssid_t> GetAutoConnectSsids();
|
||||
std::vector<ssid_t> GetKnownVisibleAccessPoints();
|
||||
|
||||
void FireNetworkChanging();
|
||||
|
||||
void MaybeStartHotspot();
|
||||
|
||||
void StartScanTimer();
|
||||
void StopScanTimer();
|
||||
void ScanNow();
|
||||
|
||||
Connection::ptr FindExistingConnection();
|
||||
|
||||
Device::ptr GetDevice(uint32_t nmDeviceType);
|
||||
std::atomic<bool> closed;
|
||||
DBusDispatcher dbusDispatcher;
|
||||
|
||||
std::unique_ptr<std::thread> thread;
|
||||
void ThreadProc();
|
||||
WifiConfigSettings wifiConfigSettings;
|
||||
NetworkManager::ptr networkManager;
|
||||
Device::ptr ethernetDevice;
|
||||
Device::ptr wlanDevice;
|
||||
DeviceWireless::ptr wlanWirelessDevice;
|
||||
ActiveConnection::ptr activeConnection;
|
||||
|
||||
std::recursive_mutex networkChangingListenerMutex;
|
||||
NetworkChangingListener networkChangingListener;
|
||||
|
||||
bool ethernetConnected = true;
|
||||
bool wlanConnected = true;
|
||||
bool isAutoWlanConnectionVisible = true;
|
||||
|
||||
DBusEventHandle onDeviceAddedHandle = INVALID_DBUS_EVENT_HANDLE;
|
||||
DBusEventHandle onDeviceRemovedHandle = INVALID_DBUS_EVENT_HANDLE;
|
||||
DBusDispatcher::PostHandle devicesChangedTimerHandle = 0;
|
||||
DBusDispatcher::PostHandle networkManagerTimerHandle = 0;
|
||||
DBusDispatcher::PostHandle accessPointsChangedTimerHandle = 0;
|
||||
DBusDispatcher::PostHandle scanTimerHandle = 0;
|
||||
};
|
||||
}
|
||||
using namespace pipedal::impl;
|
||||
|
||||
HotspotManagerImpl::HotspotManagerImpl()
|
||||
{
|
||||
SetDBusLogLevel(DBusLogLevel::None);
|
||||
}
|
||||
|
||||
void HotspotManagerImpl::Open()
|
||||
{
|
||||
|
||||
dbusDispatcher.Run();
|
||||
dbusDispatcher.Post(
|
||||
[this]()
|
||||
{
|
||||
UpdateNetworkManagerStatus(); // waits for valid network manager state, and calls onInitialize when ready.
|
||||
});
|
||||
}
|
||||
|
||||
void HotspotManagerImpl::onClose()
|
||||
{
|
||||
onDisableHotspot();
|
||||
ReleaseNetworkManager();
|
||||
|
||||
Lv2Log::debug("HotspotManager: state=Closed");
|
||||
SetState(State::Closed);
|
||||
}
|
||||
void HotspotManagerImpl::onError(const std::string &message)
|
||||
{
|
||||
Lv2Log::error(message);
|
||||
ReleaseNetworkManager();
|
||||
Lv2Log::debug("HotspotManager: state=Error");
|
||||
|
||||
SetState(State::Error);
|
||||
}
|
||||
|
||||
void HotspotManagerImpl::SetState(State state)
|
||||
{
|
||||
this->state = state;
|
||||
}
|
||||
void HotspotManagerImpl::onInitialize()
|
||||
{
|
||||
if (closed)
|
||||
return;
|
||||
if (state != State::Initial && state != State::WaitingForNetworkManager)
|
||||
{
|
||||
onError("Invalid state (onInitialize).");
|
||||
return;
|
||||
}
|
||||
try
|
||||
{
|
||||
wifiConfigSettings.Load();
|
||||
|
||||
if (wifiConfigSettings.valid_ && wifiConfigSettings.enable_)
|
||||
{
|
||||
onStartMonitoring();
|
||||
}
|
||||
else
|
||||
{
|
||||
onDisableHotspot();
|
||||
}
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
onError(SS("HotspotManager: " << e.what()));
|
||||
}
|
||||
}
|
||||
void HotspotManagerImpl::onDisconnect()
|
||||
{
|
||||
if (state != State::Initial && state != State::WaitingForNetworkManager)
|
||||
{
|
||||
WaitForNetworkManager();
|
||||
}
|
||||
}
|
||||
|
||||
void HotspotManagerImpl::UpdateNetworkManagerStatus()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!networkManager)
|
||||
{
|
||||
networkManager = NetworkManager::Create(dbusDispatcher);
|
||||
}
|
||||
// turn on wifi if required.
|
||||
auto ethernetDevice = GetDevice(NM_DEVICE_TYPE_ETHERNET);
|
||||
auto wlanDevice = GetDevice(NM_DEVICE_TYPE_WIFI);
|
||||
|
||||
if (ethernetDevice && wlanDevice)
|
||||
{
|
||||
if (state == State::Initial || state == State::WaitingForNetworkManager)
|
||||
{
|
||||
CancelWaitForNetworkManagerTimer();
|
||||
this->onInitialize();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// check to see whether we have BOTH an eth0 device and a wlan0 device.
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
}
|
||||
if (state != State::WaitingForNetworkManager)
|
||||
{
|
||||
this->WaitForNetworkManager();
|
||||
}
|
||||
else
|
||||
{
|
||||
this->StartWaitForNetworkManagerTimer();
|
||||
}
|
||||
}
|
||||
void HotspotManagerImpl::CancelDeviceChangedTimer()
|
||||
{
|
||||
if (devicesChangedTimerHandle)
|
||||
{
|
||||
dbusDispatcher.CancelPost(devicesChangedTimerHandle);
|
||||
devicesChangedTimerHandle = 0;
|
||||
}
|
||||
}
|
||||
void HotspotManagerImpl::ReleaseNetworkManager()
|
||||
{
|
||||
StopScanTimer();
|
||||
CancelAccessPointsChangedTimer();
|
||||
CancelWaitForNetworkManagerTimer();
|
||||
CancelDeviceChangedTimer();
|
||||
|
||||
DisableHotspot();
|
||||
|
||||
ethernetDevice = nullptr;
|
||||
wlanDevice = nullptr;
|
||||
wlanWirelessDevice = nullptr;
|
||||
networkManager = nullptr;
|
||||
|
||||
this->onDeviceRemovedHandle = INVALID_DBUS_EVENT_HANDLE;
|
||||
this->onDeviceAddedHandle = INVALID_DBUS_EVENT_HANDLE;
|
||||
}
|
||||
|
||||
void HotspotManagerImpl::onDevicesChanged()
|
||||
{
|
||||
UpdateNetworkManagerStatus();
|
||||
}
|
||||
Device::ptr HotspotManagerImpl::GetDevice(uint32_t nmDeviceType)
|
||||
{
|
||||
const auto &allDevices = networkManager->GetAllDevices();
|
||||
for (const auto &devicePath : allDevices)
|
||||
{
|
||||
auto device = Device::Create(dbusDispatcher, devicePath);
|
||||
if (device->DeviceType() == nmDeviceType)
|
||||
{
|
||||
return device;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
void HotspotManagerImpl::OnEthernetStateChanged(uint32_t state)
|
||||
{
|
||||
bool newValue = (state == 100);
|
||||
if (newValue != this->ethernetConnected)
|
||||
{
|
||||
this->ethernetConnected = newValue;
|
||||
Lv2Log::debug(SS("HotspotMonitor: ethernetConnected=" << ethernetConnected));
|
||||
FireNetworkChanging();
|
||||
MaybeStartHotspot();
|
||||
}
|
||||
}
|
||||
void HotspotManagerImpl::OnWlanStateChanged(uint32_t state)
|
||||
{
|
||||
this->wlanConnected = (state == 100);
|
||||
Lv2Log::debug(SS("HotspotMonitor: OnWlanStateChanged"));
|
||||
MaybeStartHotspot();
|
||||
}
|
||||
|
||||
void HotspotManagerImpl::onStartMonitoring()
|
||||
{
|
||||
try
|
||||
{
|
||||
ReleaseNetworkManager();
|
||||
|
||||
this->networkManager = NetworkManager::Create(dbusDispatcher);
|
||||
|
||||
if (!networkManager->WirelessEnabled())
|
||||
{
|
||||
networkManager->WirelessEnabled(true);
|
||||
}
|
||||
|
||||
this->onDeviceAddedHandle = this->networkManager->OnDeviceAdded.add(
|
||||
[this](const sdbus::ObjectPath &)
|
||||
{
|
||||
onDevicesChanged();
|
||||
});
|
||||
this->onDeviceRemovedHandle = this->networkManager->OnDeviceRemoved.add(
|
||||
[this](const sdbus::ObjectPath &objectPath)
|
||||
{
|
||||
if (this->ethernetDevice && this->ethernetDevice->getObjectPath() == objectPath)
|
||||
{
|
||||
onDisconnect();
|
||||
}
|
||||
else if (this->wlanDevice && this->wlanDevice->getObjectPath() == objectPath)
|
||||
{
|
||||
onDisconnect();
|
||||
}
|
||||
});
|
||||
|
||||
ethernetDevice = GetDevice(NM_DEVICE_TYPE_ETHERNET);
|
||||
if (!ethernetDevice)
|
||||
{
|
||||
throw std::runtime_error("eth0 device not found.");
|
||||
}
|
||||
this->ethernetDevice->OnStateChanged.add(
|
||||
[this](uint32_t, uint32_t, uint32_t)
|
||||
{
|
||||
OnEthernetStateChanged(ethernetDevice->State());
|
||||
});
|
||||
|
||||
wlanDevice = GetDevice(NM_DEVICE_TYPE_WIFI);
|
||||
if (!wlanDevice)
|
||||
{
|
||||
throw std::runtime_error("wlan0 device not found.");
|
||||
}
|
||||
wlanWirelessDevice = DeviceWireless::Create(dbusDispatcher, wlanDevice->getObjectPath());
|
||||
|
||||
wlanWirelessDevice->OnAccessPointAdded.add(
|
||||
[this](const sdbus::ObjectPath &accessPoint)
|
||||
{
|
||||
onAccessPointChanged();
|
||||
});
|
||||
wlanWirelessDevice->OnAccessPointRemoved.add(
|
||||
[this](const sdbus::ObjectPath &accessPoint)
|
||||
{
|
||||
onAccessPointChanged();
|
||||
});
|
||||
|
||||
this->wlanDevice->OnStateChanged.add(
|
||||
[this](uint32_t, uint32_t, uint32_t)
|
||||
{
|
||||
OnWlanStateChanged(ethernetDevice->State());
|
||||
});
|
||||
Lv2Log::debug("HotspotManager: state=Monitoring");
|
||||
SetState(State::Monitoring);
|
||||
|
||||
Lv2Log::info("HotspotManager: Monitoring network status.");
|
||||
|
||||
OnEthernetStateChanged(ethernetDevice->State());
|
||||
OnWlanStateChanged(wlanDevice->State());
|
||||
|
||||
StartScanTimer();
|
||||
onAccessPointChanged();
|
||||
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
Lv2Log::error(SS("HotspotManager: " << e.what()));
|
||||
WaitForNetworkManager();
|
||||
}
|
||||
}
|
||||
|
||||
void HotspotManagerImpl::StartWaitForNetworkManagerTimer()
|
||||
{
|
||||
CancelWaitForNetworkManagerTimer();
|
||||
networkManagerTimerHandle = dbusDispatcher.PostDelayed(
|
||||
std::chrono::seconds(5),
|
||||
[this]()
|
||||
{
|
||||
networkManagerTimerHandle = 0;
|
||||
this->UpdateNetworkManagerStatus();
|
||||
});
|
||||
}
|
||||
void HotspotManagerImpl::CancelWaitForNetworkManagerTimer()
|
||||
{
|
||||
if (networkManagerTimerHandle)
|
||||
{
|
||||
dbusDispatcher.CancelPost(networkManagerTimerHandle);
|
||||
networkManagerTimerHandle = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void HotspotManagerImpl::WaitForNetworkManager()
|
||||
{
|
||||
Lv2Log::debug("HotspotManager: state=WaitingForNetworkManager");
|
||||
SetState(State::WaitingForNetworkManager);
|
||||
ReleaseNetworkManager();
|
||||
StartWaitForNetworkManagerTimer();
|
||||
}
|
||||
void HotspotManagerImpl::onDisableHotspot()
|
||||
{
|
||||
if (this->state != State::Disabled)
|
||||
{
|
||||
ReleaseNetworkManager();
|
||||
|
||||
Lv2Log::debug("HotspotManager: state=Disabled");
|
||||
SetState(State::Disabled);
|
||||
Lv2Log::info("HotspotManager: Hotspot disabled.");
|
||||
}
|
||||
}
|
||||
void HotspotManagerImpl::onReload()
|
||||
{
|
||||
if (closed)
|
||||
return;
|
||||
WifiConfigSettings oldSettings = this->wifiConfigSettings;
|
||||
;
|
||||
wifiConfigSettings.Load();
|
||||
|
||||
switch (state)
|
||||
{
|
||||
case State::Initial:
|
||||
// ignore.
|
||||
return;
|
||||
case State::Disabled:
|
||||
if (wifiConfigSettings.valid_ && wifiConfigSettings.enable_)
|
||||
{
|
||||
onStartMonitoring();
|
||||
}
|
||||
return;
|
||||
default:
|
||||
this->onDisableHotspot();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
HotspotManagerImpl::~HotspotManagerImpl()
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
void HotspotManagerImpl::ThreadProc()
|
||||
{
|
||||
try
|
||||
{
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
Lv2Log::error(SS("Hotspot Manager thread terminated. " << e.what()));
|
||||
}
|
||||
}
|
||||
void HotspotManagerImpl::Reload()
|
||||
{
|
||||
dbusDispatcher.Post([this]()
|
||||
{ onReload(); });
|
||||
}
|
||||
void HotspotManagerImpl::Close()
|
||||
{
|
||||
if (!closed)
|
||||
{
|
||||
closed = true;
|
||||
dbusDispatcher.Post([this]
|
||||
{ onClose(); });
|
||||
dbusDispatcher.Stop();
|
||||
}
|
||||
}
|
||||
|
||||
HotspotManager::ptr HotspotManager::Create()
|
||||
{
|
||||
return std::make_unique<HotspotManagerImpl>();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
class VectorHash
|
||||
{
|
||||
public:
|
||||
std::size_t operator()(const std::vector<T> &vec) const
|
||||
{
|
||||
std::size_t hash = vec.size();
|
||||
for (auto &i : vec)
|
||||
{
|
||||
hash += static_cast<size_t>(i);
|
||||
hash = ((hash >> 32) ^ hash) * 0x119de1F3;
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
};
|
||||
|
||||
static std::string ssidToString(const std::vector<uint8_t> &ssid)
|
||||
{
|
||||
std::stringstream s;
|
||||
for (auto v: ssid)
|
||||
{
|
||||
if (v == 0) break; // breaks for some arguably legal ssids, but I can live with that.
|
||||
s << (char)v;
|
||||
}
|
||||
return s.str();
|
||||
}
|
||||
|
||||
std::vector<std::vector<uint8_t>> HotspotManagerImpl::GetKnownVisibleAccessPoints()
|
||||
{
|
||||
std::vector<std::vector<uint8_t>> knownSsids = this->GetAutoConnectSsids();
|
||||
|
||||
std::unordered_set<std::vector<uint8_t>, VectorHash<uint8_t>> index{knownSsids.begin(), knownSsids.end()};
|
||||
|
||||
std::vector<ssid_t> result;
|
||||
for (const auto &accessPointPath : wlanWirelessDevice->AccessPoints())
|
||||
{
|
||||
auto accessPoint = AccessPoint::Create(dbusDispatcher, accessPointPath);
|
||||
auto ssid = accessPoint->Ssid();
|
||||
if (index.contains(ssid))
|
||||
{
|
||||
result.push_back(ssid);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<std::vector<uint8_t>> HotspotManagerImpl::GetAutoConnectSsids()
|
||||
{
|
||||
auto availableConnections = wlanDevice->AvailableConnections();
|
||||
std::vector<std::vector<uint8_t>> ssids;
|
||||
|
||||
for (const auto &connectionPath : availableConnections)
|
||||
{
|
||||
auto connection = Connection::Create(dbusDispatcher, connectionPath);
|
||||
try
|
||||
{
|
||||
auto settings = connection->GetSettings();
|
||||
bool autoConnect = true;
|
||||
if (settings["connect"].count("autoconnect") > 0)
|
||||
{
|
||||
autoConnect = settings["connect"]["autoconnect"];
|
||||
}
|
||||
bool isInfrastructure =
|
||||
settings["802-11-wireless"].count("mode") > 0 && settings["802-11-wireless"]["mode"].get<std::string>() == "infrastructure";
|
||||
if (isInfrastructure && autoConnect)
|
||||
{
|
||||
std::vector<uint8_t> ssid =
|
||||
settings["802-11-wireless"]["ssid"];
|
||||
ssids.push_back(std::move(ssid));
|
||||
}
|
||||
}
|
||||
catch (const std::exception &ignored)
|
||||
{
|
||||
// not totally sure of structure of all connection types.
|
||||
}
|
||||
}
|
||||
return ssids;
|
||||
}
|
||||
|
||||
void HotspotManagerImpl::onAccessPointsChanged()
|
||||
{
|
||||
MaybeStartHotspot();
|
||||
}
|
||||
|
||||
void HotspotManagerImpl::CancelAccessPointsChangedTimer()
|
||||
{
|
||||
if (accessPointsChangedTimerHandle)
|
||||
{
|
||||
dbusDispatcher.CancelPost(accessPointsChangedTimerHandle);
|
||||
accessPointsChangedTimerHandle = 0;
|
||||
}
|
||||
}
|
||||
void HotspotManagerImpl::onAccessPointChanged()
|
||||
{
|
||||
// coalesce large bursts of AccessPointChanged calls using a timer.
|
||||
if (!accessPointsChangedTimerHandle)
|
||||
{
|
||||
accessPointsChangedTimerHandle = dbusDispatcher.PostDelayed(
|
||||
std::chrono::milliseconds(512),
|
||||
[this]()
|
||||
{
|
||||
accessPointsChangedTimerHandle = 0;
|
||||
onAccessPointsChanged(); // coalesced handling of one or more changes.
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void HotspotManagerImpl::MaybeStartHotspot()
|
||||
{
|
||||
std::vector<std::vector<uint8_t>> connectableSsids = GetKnownVisibleAccessPoints(); // all the ssids currently visible for which we have credentials.
|
||||
bool wantsHotspot = this->wifiConfigSettings.WantsHotspot(
|
||||
this->ethernetConnected,connectableSsids);
|
||||
|
||||
this->isAutoWlanConnectionVisible = connectableSsids.size() != 0;
|
||||
|
||||
if (this->state == State::Monitoring && wantsHotspot)
|
||||
{
|
||||
StartHotspot();
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((!wantsHotspot) && (this->state == State::HotspotConnected || this->state == State::HotspotConnecting))
|
||||
{
|
||||
StopHotspot();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void HotspotManagerImpl::EnableHotspot()
|
||||
{
|
||||
}
|
||||
|
||||
void HotspotManagerImpl::DisableHotspot()
|
||||
{
|
||||
StopHotspot();
|
||||
}
|
||||
|
||||
Connection::ptr HotspotManagerImpl::FindExistingConnection()
|
||||
{
|
||||
if (wlanDevice)
|
||||
{
|
||||
for (const auto &connectionPath : wlanDevice->AvailableConnections())
|
||||
{
|
||||
auto connection = Connection::Create(dbusDispatcher, connectionPath);
|
||||
auto settings = connection->GetSettings();
|
||||
bool isHotspot =
|
||||
settings["802-11-wireless"].count("mode") > 0 && settings["802-11-wireless"]["mode"].get<std::string>() == "ap";
|
||||
if (isHotspot)
|
||||
{
|
||||
std::string id;
|
||||
if (settings["connection"].count("id") > 0)
|
||||
{
|
||||
id = settings["connection"]["id"].get<std::string>();
|
||||
}
|
||||
if (id.starts_with(PIPEDAL_HOTSPOT_NAME))
|
||||
{
|
||||
return connection;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void HotspotManagerImpl::StartHotspot()
|
||||
{
|
||||
if (this->state == State::Monitoring)
|
||||
{
|
||||
try
|
||||
{
|
||||
ServiceConfiguration serviceConfiguration;
|
||||
serviceConfiguration.Load();
|
||||
|
||||
Lv2Log::debug("HotspotManager: state=HotspotConnecting");
|
||||
SetState(State::HotspotConnecting);
|
||||
|
||||
Lv2Log::info("HotspotManager: Enabling PiPedal hotspot.");
|
||||
|
||||
// do it ONLY if we're in monitoring state.
|
||||
// Create a proxy for NetworkManager
|
||||
// Create connection settings for the hotspot
|
||||
std::map<std::string, std::map<std::string, sdbus::Variant>> settings;
|
||||
|
||||
std::map<std::string, sdbus::Variant> &connection = settings["connection"];
|
||||
|
||||
connection["type"] = "802-11-wireless";
|
||||
connection["autoconnect"] = false;
|
||||
connection["id"] = PIPEDAL_HOTSPOT_NAME;
|
||||
connection["interface-name"] = wlanDevice->Interface();
|
||||
connection["uuid"] = serviceConfiguration.uuid;
|
||||
|
||||
std::map<std::string, sdbus::Variant> &wireless = settings["802-11-wireless"];
|
||||
|
||||
std::string ssid = this->wifiConfigSettings.hotspotName_;
|
||||
std::vector<uint8_t> vSsid{ssid.begin(), ssid.end()};
|
||||
wireless["ssid"] = vSsid;
|
||||
wireless["mode"] = "ap";
|
||||
wireless["band"] = "a";
|
||||
wireless["band"] = "bg";
|
||||
|
||||
uint32_t iChannel;
|
||||
auto channel = this->wifiConfigSettings.channel_;
|
||||
if (channel.length() != 0)
|
||||
{
|
||||
std::stringstream ss{channel};
|
||||
ss >> iChannel;
|
||||
}
|
||||
if (iChannel != 0)
|
||||
{
|
||||
wireless["channel"] = iChannel;
|
||||
}
|
||||
|
||||
std::map<std::string, sdbus::Variant> &wirelessSecurity = settings["802-11-wireless-security"];
|
||||
wirelessSecurity["key-mgmt"] = "wpa-psk";
|
||||
wirelessSecurity["psk"] = wifiConfigSettings.password_;
|
||||
|
||||
settings["ipv4"]["method"] = "shared";
|
||||
settings["ipv6"]["method"] = "shared";
|
||||
// settings["ipv6"]["addr-gen-mode"] = "stable-privacy";
|
||||
|
||||
std::map<std::string, sdbus::Variant> options;
|
||||
options["persist"] = "disk";
|
||||
|
||||
Connection::ptr existingConnection = FindExistingConnection();
|
||||
if (existingConnection)
|
||||
{
|
||||
existingConnection->Update(settings);
|
||||
auto activeConnectionPath = networkManager->ActivateConnection(
|
||||
existingConnection->getObjectPath(),
|
||||
wlanDevice->getObjectPath(),
|
||||
"/");
|
||||
this->activeConnection = ActiveConnection::Create(dbusDispatcher, activeConnectionPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
// Call AddAndActivateConnection2 method to create and activate the hotspot
|
||||
sdbus::ObjectPath nullPath("/");
|
||||
auto result = networkManager->AddAndActivateConnection2(
|
||||
settings, wlanDevice->getObjectPath(), "/", options
|
||||
|
||||
);
|
||||
auto connectionPath = std::get<0>(result);
|
||||
auto activeConnectionPath = std::get<1>(result);
|
||||
// auto resultArgs = std::get<2>(result);
|
||||
|
||||
this->activeConnection = ActiveConnection::Create(dbusDispatcher, activeConnectionPath);
|
||||
}
|
||||
SetState(State::HotspotConnected);
|
||||
Lv2Log::info("HotspotManager: Hotspot activated.");
|
||||
|
||||
FireNetworkChanging();
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
onError(SS("HotspotManager: Activation failed: " << e.what()));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
onError("HotspotManager: Illegal state (StartHotspot)");
|
||||
}
|
||||
}
|
||||
void HotspotManagerImpl::StopHotspot()
|
||||
{
|
||||
// do it regardless of state.
|
||||
try
|
||||
{
|
||||
if (activeConnection)
|
||||
{
|
||||
networkManager->DeactivateConnection(activeConnection->getObjectPath());
|
||||
activeConnection = nullptr;
|
||||
|
||||
FireNetworkChanging();
|
||||
|
||||
}
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
Lv2Log::error("HotspotManager: Failed to deactivate hotspot.");
|
||||
activeConnection = nullptr;
|
||||
}
|
||||
if (this->state == State::HotspotConnected || this->state == State::HotspotConnecting)
|
||||
{
|
||||
Lv2Log::info("HotspotManager: state=HotspotMonitoring");
|
||||
SetState(State::Monitoring);
|
||||
|
||||
Lv2Log::info("HotspotManager: PiPedal hotspot disabled.");
|
||||
}
|
||||
}
|
||||
|
||||
static const std::chrono::seconds scanInterval { 60};
|
||||
|
||||
void HotspotManagerImpl::StartScanTimer()
|
||||
{
|
||||
StopScanTimer();
|
||||
ScanNow();
|
||||
}
|
||||
void HotspotManagerImpl::StopScanTimer()
|
||||
{
|
||||
if (this->scanTimerHandle) {
|
||||
dbusDispatcher.CancelPost(this->scanTimerHandle);
|
||||
this->scanTimerHandle = 0;
|
||||
}
|
||||
}
|
||||
void HotspotManagerImpl::ScanNow()
|
||||
{
|
||||
this->scanTimerHandle = 0;
|
||||
|
||||
if (wlanWirelessDevice) {
|
||||
|
||||
std::map<std::string, sdbus::Variant> options;
|
||||
|
||||
try {
|
||||
Lv2Log::debug("Scanning");
|
||||
wlanWirelessDevice->RequestScan(options);
|
||||
} catch (const std::exception &e)
|
||||
{
|
||||
Lv2Log::error(SS("HotspotMonitor: Wi-Fi RequestScan failed." << e.what()));
|
||||
return;
|
||||
}
|
||||
|
||||
this->scanTimerHandle = this->dbusDispatcher.PostDelayed(
|
||||
std::chrono::duration_cast<std::chrono::steady_clock::duration>(scanInterval),
|
||||
[this]() {
|
||||
ScanNow();
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
HotspotManagerImpl::PostHandle HotspotManagerImpl::Post(PostCallback&&fn)
|
||||
{
|
||||
return dbusDispatcher.Post(std::move(fn));
|
||||
}
|
||||
HotspotManagerImpl::PostHandle HotspotManagerImpl::PostDelayed(const clock::duration&delay,PostCallback&&fn)
|
||||
{
|
||||
return dbusDispatcher.PostDelayed(delay,std::move(fn));
|
||||
|
||||
}
|
||||
bool HotspotManagerImpl::CancelPost(PostHandle handle) {
|
||||
return dbusDispatcher.CancelPost(handle);
|
||||
}
|
||||
|
||||
void HotspotManagerImpl::SetNetworkChangingListener(NetworkChangingListener &&listener)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock { this->networkChangingListenerMutex};
|
||||
this->networkChangingListener = std::move(listener);
|
||||
}
|
||||
void HotspotManagerImpl::FireNetworkChanging() {
|
||||
std::lock_guard<std::recursive_mutex> lock { this->networkChangingListenerMutex};
|
||||
if (this->networkChangingListener)
|
||||
{
|
||||
this->networkChangingListener(this->ethernetConnected,!!activeConnection);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
// Copyright (c) 2024 Robin Davies
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
// this software and associated documentation files (the "Software"), to deal in
|
||||
// the Software without restriction, including without limitation the rights to
|
||||
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
// the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
// subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
#pragma once
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
#include <chrono>
|
||||
|
||||
namespace pipedal {
|
||||
|
||||
class HotspotManager {
|
||||
|
||||
// no move, no delete.
|
||||
HotspotManager(const HotspotManager&) = delete;
|
||||
HotspotManager(HotspotManager&&) = delete;
|
||||
HotspotManager & operator=(const HotspotManager&) = delete;
|
||||
HotspotManager & operator=(const HotspotManager&&) = delete;
|
||||
|
||||
protected:
|
||||
|
||||
HotspotManager() {} // use Create().
|
||||
public:
|
||||
using clock = std::chrono::steady_clock;
|
||||
|
||||
using ptr = std::unique_ptr<HotspotManager>;
|
||||
|
||||
static ptr Create();
|
||||
|
||||
virtual ~HotspotManager() noexcept { }
|
||||
|
||||
virtual void Open() = 0;
|
||||
virtual void Reload() = 0;
|
||||
virtual void Close() = 0;
|
||||
|
||||
using NetworkChangingListener = std::function<void(bool ethernetConnected,bool hotspotEnabled)>;
|
||||
|
||||
virtual void SetNetworkChangingListener(NetworkChangingListener &&listener) = 0;
|
||||
|
||||
using PostHandle = uint64_t;
|
||||
using PostCallback = std::function<void()>;
|
||||
|
||||
virtual PostHandle Post(PostCallback&&fn) = 0;
|
||||
virtual PostHandle PostDelayed(const clock::duration&delay,PostCallback&&fn) = 0;
|
||||
virtual bool CancelPost(PostHandle handle) = 0;
|
||||
|
||||
|
||||
template<class REP,class PERIOD>
|
||||
PostHandle PostDelayed(const std::chrono::duration<REP,PERIOD>&delay,PostCallback&&fn)
|
||||
{
|
||||
return PostDelayed(
|
||||
std::chrono::duration_cast<clock::duration,REP,PERIOD>(delay),
|
||||
std::move(fn));
|
||||
}
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
+84
-4
@@ -35,6 +35,8 @@
|
||||
#include "PiPedalUI.hpp"
|
||||
#include "atom_object.hpp"
|
||||
#include "Lv2PluginChangeMonitor.hpp"
|
||||
#include "HotspotManager.hpp"
|
||||
#include "DBusToLv2Log.hpp"
|
||||
|
||||
#ifndef NO_MLOCK
|
||||
#include <sys/mman.h>
|
||||
@@ -79,6 +81,17 @@ PiPedalModel::PiPedalModel()
|
||||
{
|
||||
this->OnUpdateStatusChanged(updateStatus);
|
||||
});
|
||||
|
||||
|
||||
DbusLogToLv2Log();
|
||||
|
||||
hotspotManager = HotspotManager::Create();
|
||||
hotspotManager->SetNetworkChangingListener(
|
||||
[this](bool ethernetConnected, bool hotspotEnabling) {
|
||||
OnNetworkChanging(ethernetConnected,hotspotEnabling);
|
||||
}
|
||||
);
|
||||
hotspotManager->Open();
|
||||
}
|
||||
|
||||
void PiPedalModel::Close()
|
||||
@@ -106,7 +119,10 @@ void PiPedalModel::Close()
|
||||
|
||||
PiPedalModel::~PiPedalModel()
|
||||
{
|
||||
pluginChangeMonitor = nullptr;
|
||||
CancelNetworkChangingTimer();
|
||||
hotspotManager = nullptr; // turn off the hotspot.
|
||||
|
||||
pluginChangeMonitor = nullptr; // stop monitorin LV2 directories.
|
||||
try
|
||||
{
|
||||
adminClient.UnmonitorGovernor();
|
||||
@@ -952,11 +968,23 @@ void PiPedalModel::SetWifiConfigSettings(const WifiConfigSettings &wifiConfigSet
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(mutex);
|
||||
|
||||
adminClient.SetWifiConfig(wifiConfigSettings);
|
||||
|
||||
|
||||
#if NEW_WIFI_CONFIG
|
||||
if (this->storage.SetWifiConfigSettings(wifiConfigSettings))
|
||||
{
|
||||
if (this->hotspotManager)
|
||||
{
|
||||
this->hotspotManager->Reload();
|
||||
}
|
||||
}
|
||||
#else
|
||||
this->storage.SetWifiConfigSettings(wifiConfigSettings);
|
||||
adminClient.SetWifiConfig(wifiConfigSettings);
|
||||
#endif
|
||||
|
||||
{
|
||||
// yyy: review locking semantics here. This is wrong. Convert to shared pointers?
|
||||
IPiPedalModelSubscriber **t = new IPiPedalModelSubscriber *[this->subscribers.size()];
|
||||
for (size_t i = 0; i < subscribers.size(); ++i)
|
||||
{
|
||||
@@ -964,11 +992,11 @@ void PiPedalModel::SetWifiConfigSettings(const WifiConfigSettings &wifiConfigSet
|
||||
}
|
||||
size_t n = this->subscribers.size();
|
||||
|
||||
WifiConfigSettings tWifiConfigSettings = storage.GetWifiConfigSettings(); // (the passwordless version)
|
||||
WifiConfigSettings settingsWithNoSecrets = storage.GetWifiConfigSettings(); // (the passwordless version)
|
||||
|
||||
for (size_t i = 0; i < n; ++i)
|
||||
{
|
||||
t[i]->OnWifiConfigSettingsChanged(tWifiConfigSettings);
|
||||
t[i]->OnWifiConfigSettingsChanged(settingsWithNoSecrets);
|
||||
}
|
||||
delete[] t;
|
||||
}
|
||||
@@ -2259,3 +2287,55 @@ void PiPedalModel::WaitForAudioDeviceToComeOnline()
|
||||
// pre-cache device info before we let audio services run.
|
||||
GetAlsaDevices();
|
||||
}
|
||||
|
||||
PiPedalModel::PostHandle PiPedalModel::Post(PostCallback&&fn)
|
||||
{
|
||||
// I know. odd place to forward this to, but it's a very serviceable dispatcher implementation.
|
||||
// Why? because it's there, and PiPedalModel has no thread of its own to do dispatching.
|
||||
if (!hotspotManager)
|
||||
{
|
||||
throw std::runtime_error("Too early. It's not ready yet.");
|
||||
}
|
||||
return hotspotManager->Post(std::move(fn));
|
||||
}
|
||||
PiPedalModel::PostHandle PiPedalModel::PostDelayed(const clock::duration&delay,PostCallback&&fn)
|
||||
{
|
||||
if (!hotspotManager)
|
||||
{
|
||||
throw std::runtime_error("Too early. It's not ready yet.");
|
||||
}
|
||||
return hotspotManager->PostDelayed(delay,std::move(fn));
|
||||
|
||||
}
|
||||
bool PiPedalModel::CancelPost(PostHandle handle) {
|
||||
if (!hotspotManager)
|
||||
{
|
||||
throw std::runtime_error("Too early. It's not ready yet.");
|
||||
}
|
||||
return hotspotManager->CancelPost(handle);
|
||||
}
|
||||
|
||||
void PiPedalModel::CancelNetworkChangingTimer()
|
||||
{
|
||||
if (networkChangingDelayHandle)
|
||||
{
|
||||
CancelPost(networkChangingDelayHandle);
|
||||
networkChangingDelayHandle = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void PiPedalModel::OnNetworkChanging(bool ethernetConnected,bool hotspotConnected)
|
||||
{
|
||||
CancelNetworkChangingTimer();
|
||||
this->networkChangingDelayHandle =
|
||||
PostDelayed(std::chrono::seconds(5),
|
||||
[this,ethernetConnected,hotspotConnected]() {
|
||||
this->networkChangingDelayHandle = 0;
|
||||
OnNetworkChanged(ethernetConnected,hotspotConnected);
|
||||
}
|
||||
);
|
||||
}
|
||||
void PiPedalModel::OnNetworkChanged(bool ethernetConnected, bool hotspotConnected)
|
||||
{
|
||||
FireNetworkChanged();
|
||||
}
|
||||
|
||||
@@ -84,9 +84,20 @@ namespace pipedal
|
||||
virtual void Close() = 0;
|
||||
};
|
||||
|
||||
class HotspotManager;
|
||||
|
||||
class PiPedalModel : private IAudioHostCallbacks
|
||||
{
|
||||
public:
|
||||
using clock = std::chrono::steady_clock;
|
||||
|
||||
using PostHandle = uint64_t;
|
||||
using PostCallback = std::function<void()>;
|
||||
using NetworkChangedListener = std::function<void(void)>;
|
||||
|
||||
|
||||
private:
|
||||
std::unique_ptr<HotspotManager> hotspotManager;
|
||||
|
||||
UpdateStatus currentUpdateStatus;
|
||||
Updater updater;
|
||||
@@ -140,6 +151,14 @@ namespace pipedal
|
||||
Storage storage;
|
||||
bool hasPresetChanged = false;
|
||||
|
||||
NetworkChangedListener networkChangedListener;
|
||||
void FireNetworkChanged() {
|
||||
if (networkChangedListener)
|
||||
{
|
||||
networkChangedListener();
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<AudioHost> audioHost;
|
||||
JackConfiguration jackConfiguration;
|
||||
std::shared_ptr<Lv2Pedalboard> lv2Pedalboard;
|
||||
@@ -193,6 +212,11 @@ namespace pipedal
|
||||
virtual void OnNotifyNextMidiProgram(const RealtimeNextMidiProgramRequest&request) override;
|
||||
virtual void OnNotifyLv2RealtimeError(int64_t instanceId,const std::string &error) override;
|
||||
|
||||
PostHandle networkChangingDelayHandle = 0;
|
||||
void CancelNetworkChangingTimer();
|
||||
|
||||
void OnNetworkChanging(bool ethernetConnected,bool hotspotConnected);
|
||||
void OnNetworkChanged(bool ethernetConnected, bool hotspotConnected);
|
||||
|
||||
void UpdateVst3Settings(Pedalboard &pedalboard);
|
||||
|
||||
@@ -203,6 +227,24 @@ namespace pipedal
|
||||
PiPedalModel();
|
||||
virtual ~PiPedalModel();
|
||||
|
||||
|
||||
|
||||
virtual PostHandle Post(PostCallback&&fn);
|
||||
virtual PostHandle PostDelayed(const clock::duration&delay,PostCallback&&fn);
|
||||
virtual bool CancelPost(PostHandle handle);
|
||||
|
||||
template<class REP,class PERIOD>
|
||||
PostHandle PostDelayed(const std::chrono::duration<REP,PERIOD>&delay,PostCallback&&fn)
|
||||
{
|
||||
return PostDelayed(
|
||||
std::chrono::duration_cast<clock::duration,REP,PERIOD>(delay),
|
||||
std::move(fn));
|
||||
}
|
||||
|
||||
void SetNetworkChangedListener(NetworkChangedListener listener) {
|
||||
networkChangedListener = listener;
|
||||
}
|
||||
|
||||
void WaitForAudioDeviceToComeOnline();
|
||||
|
||||
UpdateStatus GetUpdateStatus();
|
||||
|
||||
+51
-6
@@ -76,16 +76,19 @@ bool pipedal::UsingNetworkManager()
|
||||
return std::filesystem::exists(NETWORK_MANAGER_SERVICE_PATH);
|
||||
}
|
||||
|
||||
static bool IsApdInstalled()
|
||||
{
|
||||
|
||||
return std::filesystem::exists(DNSMASQ_APD_PATH);
|
||||
}
|
||||
static bool IsP2pInstalled()
|
||||
{
|
||||
return std::filesystem::exists(DNSMASQ_P2P_PATH) | std::filesystem::exists(NETWORK_MANAGER_DNSMASQ_P2P_PATH);
|
||||
}
|
||||
|
||||
static bool IsApdInstalled()
|
||||
{
|
||||
|
||||
return std::filesystem::exists(DNSMASQ_APD_PATH);
|
||||
}
|
||||
|
||||
|
||||
#if !NEW_WIFI_CONFIG
|
||||
static void restoreApdDhcpdConfFile()
|
||||
{
|
||||
// remove the interface wlan0 section.
|
||||
@@ -123,6 +126,7 @@ static void restoreApdDhcpdConfFile()
|
||||
dhcpcd.Save(dhcpcdConfig);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static void restoreP2pDhcpdConfFile()
|
||||
{
|
||||
@@ -170,6 +174,7 @@ static void restoreP2pDhcpdConfFile()
|
||||
}
|
||||
}
|
||||
|
||||
#if !NEW_WIFI_CONFIG
|
||||
static void restoreApdDnsmasqConfFile()
|
||||
{
|
||||
std::filesystem::path path(DNSMASQ_APD_PATH);
|
||||
@@ -178,6 +183,7 @@ static void restoreApdDnsmasqConfFile()
|
||||
std::filesystem::remove(path);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
static void restoreP2pDnsmasqConfFile()
|
||||
{
|
||||
{
|
||||
@@ -196,6 +202,7 @@ static void restoreP2pDnsmasqConfFile()
|
||||
}
|
||||
}
|
||||
|
||||
#if !NEW_WIFI_CONFIG
|
||||
static void UninstallHostApd()
|
||||
{
|
||||
if (IsApdInstalled())
|
||||
@@ -213,6 +220,7 @@ static void UninstallHostApd()
|
||||
sysExec(SYSTEMCTL_BIN " start wpa_supplicant");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static void InstallP2p(const WifiDirectConfigSettings &settings)
|
||||
{
|
||||
@@ -299,7 +307,8 @@ static void InstallP2p(const WifiDirectConfigSettings &settings)
|
||||
|
||||
static void UninstallP2p();
|
||||
|
||||
void pipedal::SetWifiConfig(const WifiConfigSettings &settings)
|
||||
#if !NEW_WIFI_CONFIG
|
||||
static void SetHostapdWifiConfig(const WifiConfigSettings &settings)
|
||||
{
|
||||
char band;
|
||||
if (!settings.enable_)
|
||||
@@ -476,6 +485,25 @@ void pipedal::SetWifiConfig(const WifiConfigSettings &settings)
|
||||
}
|
||||
::sync();
|
||||
}
|
||||
#endif
|
||||
|
||||
#if NEW_WIFI_CONFIG
|
||||
|
||||
static void SetNetworkManagerWifiConfig(WifiConfigSettings &settings)
|
||||
{
|
||||
settings.Save();
|
||||
}
|
||||
#endif
|
||||
|
||||
void pipedal::SetWifiConfig(WifiConfigSettings &settings)
|
||||
{
|
||||
#if NEW_WIFI_CONFIG
|
||||
SetNetworkManagerWifiConfig(settings);
|
||||
#else
|
||||
SetHostapdWifiConfig(const WifiConfigSettings &settings);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/*********************************************************************************
|
||||
|
||||
@@ -686,6 +714,7 @@ void pipedal::SetWifiDirectConfig(const WifiDirectConfigSettings &settings)
|
||||
::sync();
|
||||
}
|
||||
void pipedal::OnWifiReinstall() {
|
||||
#if ENABLE_WIFI_P2P // Do not enable P2P going forward.
|
||||
WifiDirectConfigSettings settings;
|
||||
settings.Load();
|
||||
if (settings.enable_)
|
||||
@@ -693,14 +722,30 @@ void pipedal::OnWifiReinstall() {
|
||||
SetWifiDirectConfig(settings);
|
||||
::sync();
|
||||
}
|
||||
#endif
|
||||
#if NEW_WIFI_CONFIG
|
||||
{
|
||||
// no action required.
|
||||
// pipedald will pick up the old settings at runtime.
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
void pipedal::OnWifiUninstall(bool preserveState)
|
||||
{
|
||||
// intaller hook
|
||||
#if NEW_WIFI_CONFIG
|
||||
{
|
||||
|
||||
// no action required.
|
||||
// shutting down pipedald is sufficient.
|
||||
}
|
||||
#else
|
||||
if (IsApdInstalled())
|
||||
{
|
||||
UninstallHostApd();
|
||||
}
|
||||
#endif
|
||||
if (IsP2pInstalled())
|
||||
{
|
||||
WifiDirectConfigSettings settings;
|
||||
|
||||
@@ -21,12 +21,13 @@
|
||||
#include "WifiConfigSettings.hpp"
|
||||
#include "WifiDirectConfigSettings.hpp"
|
||||
|
||||
|
||||
namespace pipedal {
|
||||
void SetWifiConfigWLanAddress(const std::string&wLanAddress);
|
||||
const std::string &GetWifiConfigWlanAddress();
|
||||
|
||||
|
||||
void SetWifiConfig(const WifiConfigSettings&settings);
|
||||
void SetWifiConfig(WifiConfigSettings&settings);
|
||||
void SetWifiDirectConfig(const WifiDirectConfigSettings&settings);
|
||||
|
||||
void OnWifiUninstall(bool preserveState = false);
|
||||
|
||||
+18
-56
@@ -38,7 +38,6 @@ using namespace pipedal;
|
||||
const char *BANK_EXTENSION = ".bank";
|
||||
const char *BANKS_FILENAME = "index.banks";
|
||||
|
||||
#define WIFI_CONFIG_SETTINGS_FILENAME "wifiConfigSettings.json";
|
||||
#define USER_SETTINGS_FILENAME "userSettings.json";
|
||||
|
||||
Storage::Storage()
|
||||
@@ -953,43 +952,22 @@ std::string Storage::GetGovernorSettings() const
|
||||
return this->userSettings.governor_;
|
||||
}
|
||||
|
||||
void Storage::SetWifiConfigSettings(const WifiConfigSettings &wifiConfigSettings)
|
||||
bool Storage::SetWifiConfigSettings(const WifiConfigSettings &wifiConfigSettings)
|
||||
{
|
||||
WifiConfigSettings copyToSave = wifiConfigSettings;
|
||||
copyToSave.rebootRequired_ = false;
|
||||
if (!copyToSave.enable_)
|
||||
WifiConfigSettings previousValue;
|
||||
previousValue.Load();
|
||||
if (!copyToSave.hasPassword_)
|
||||
{
|
||||
copyToSave.hasPassword_ = false;
|
||||
copyToSave.hasPassword_ = previousValue.hasPassword_;
|
||||
copyToSave.password_ = previousValue.hasPassword_;
|
||||
}
|
||||
copyToSave.password_ = "";
|
||||
|
||||
std::filesystem::path path = this->dataRoot / WIFI_CONFIG_SETTINGS_FILENAME;
|
||||
{
|
||||
pipedal::ofstream_synced f(path);
|
||||
if (!f.is_open())
|
||||
{
|
||||
throw PiPedalException("Unable to write to " + ((std::string)path));
|
||||
}
|
||||
json_writer writer(f, false);
|
||||
writer.write(©ToSave);
|
||||
}
|
||||
|
||||
WifiConfigSettings copyToStore = wifiConfigSettings;
|
||||
if (copyToStore.enable_)
|
||||
{
|
||||
copyToStore.hasPassword_ = copyToStore.password_.length() != 0 || this->wifiConfigSettings.hasPassword_;
|
||||
}
|
||||
else
|
||||
{
|
||||
copyToStore.hasPassword_ = false;
|
||||
}
|
||||
copyToStore.password_ = "";
|
||||
copyToStore.rebootRequired_ = false;
|
||||
if (copyToStore.enable_ && !copyToStore.hasPassword_)
|
||||
{
|
||||
copyToStore.hasPassword_ = this->wifiConfigSettings.hasPassword_;
|
||||
}
|
||||
this->wifiConfigSettings = copyToStore;
|
||||
bool configChanged = wifiConfigSettings.ConfigurationChanged(previousValue);
|
||||
copyToSave.Save();
|
||||
this->wifiConfigSettings = copyToSave;
|
||||
this->wifiConfigSettings.hasPassword_ = false;
|
||||
this->wifiConfigSettings.password_ = "";
|
||||
return configChanged;
|
||||
}
|
||||
|
||||
void Storage::SetWifiDirectConfigSettings(const WifiDirectConfigSettings &wifiDirectConfigSettings)
|
||||
@@ -1030,27 +1008,8 @@ void Storage::LoadUserSettings()
|
||||
}
|
||||
void Storage::LoadWifiConfigSettings()
|
||||
{
|
||||
std::filesystem::path path = this->dataRoot / WIFI_CONFIG_SETTINGS_FILENAME;
|
||||
|
||||
try
|
||||
{
|
||||
if (std::filesystem::is_regular_file(path))
|
||||
{
|
||||
std::ifstream f(path);
|
||||
if (!f.is_open())
|
||||
{
|
||||
throw PiPedalException("Unable to write to " + ((std::string)path));
|
||||
}
|
||||
json_reader reader(f);
|
||||
WifiConfigSettings wifiConfigSettings;
|
||||
reader.read(&wifiConfigSettings);
|
||||
this->wifiConfigSettings = wifiConfigSettings;
|
||||
}
|
||||
}
|
||||
catch (const std::exception &)
|
||||
{
|
||||
}
|
||||
this->wifiConfigSettings.valid_ = true;
|
||||
this->wifiConfigSettings.Load();
|
||||
this->wifiConfigSettings.valid_ = this->wifiConfigSettings.hasPassword_ && !this->wifiConfigSettings.countryCode_.empty();
|
||||
}
|
||||
void Storage::LoadWifiDirectConfigSettings()
|
||||
{
|
||||
@@ -1065,7 +1024,10 @@ void Storage::LoadWifiDirectConfigSettings()
|
||||
|
||||
WifiConfigSettings Storage::GetWifiConfigSettings()
|
||||
{
|
||||
return this->wifiConfigSettings;
|
||||
WifiConfigSettings result = this->wifiConfigSettings;
|
||||
result.hasPassword_ = false;
|
||||
result.password_ = "";
|
||||
return result;
|
||||
}
|
||||
WifiDirectConfigSettings Storage::GetWifiDirectConfigSettings()
|
||||
{
|
||||
|
||||
+2
-1
@@ -158,7 +158,8 @@ public:
|
||||
void SetJackChannelSelection(const JackChannelSelection&channelSelection);
|
||||
const JackChannelSelection&GetJackChannelSelection(const JackConfiguration &jackConfiguration);
|
||||
|
||||
void SetWifiConfigSettings(const WifiConfigSettings & wifiConfigSettings);
|
||||
// returns true if services needs to be updated.
|
||||
bool SetWifiConfigSettings(const WifiConfigSettings & wifiConfigSettings);
|
||||
WifiConfigSettings GetWifiConfigSettings();
|
||||
|
||||
void SetWifiDirectConfigSettings(const WifiDirectConfigSettings & wifiDirectConfigSettings);
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include <regex>
|
||||
#include "ss.hpp"
|
||||
|
||||
using namespace pipedal;
|
||||
using namespace std;
|
||||
@@ -107,6 +108,7 @@ static inline std::string ValuePart(const std::string &line)
|
||||
throw PiPedalException("Value not found.");
|
||||
return line.substr(pos + 1);
|
||||
}
|
||||
|
||||
std::string SystemConfigFile::Get(const std::string &key) const
|
||||
{
|
||||
int64_t lineIndex = GetLine(key);
|
||||
@@ -346,3 +348,46 @@ void SystemConfigFile::AppendLine(const std::string &line)
|
||||
lines.push_back(line);
|
||||
}
|
||||
|
||||
|
||||
static const std::string REPLACE_SLUG = "# PiPedal replace:";
|
||||
static const std::string ADD_SLUG = "# PiPedal add:";
|
||||
|
||||
void SystemConfigFile::UndoableReplaceLine(int line,const std::string&text)
|
||||
{
|
||||
std::string undoComment = SS(REPLACE_SLUG << lines[line]);
|
||||
lines[line] = undoComment;
|
||||
InsertLine(line+1,text);
|
||||
}
|
||||
int SystemConfigFile::UndoableAddLine(int line,const std::string&text)
|
||||
{
|
||||
std::string undoComment = ADD_SLUG;
|
||||
InsertLine(line,undoComment);
|
||||
InsertLine(line+1,text);
|
||||
return line+2;
|
||||
}
|
||||
|
||||
bool SystemConfigFile::RemoveUndoableActions()
|
||||
{
|
||||
bool changed = false;
|
||||
for (int i = 0; i < GetLineCount(); ++i)
|
||||
{
|
||||
if (lines[i].starts_with("# PiPedal"))
|
||||
{
|
||||
if (lines[i].starts_with(REPLACE_SLUG))
|
||||
{
|
||||
std::string oldText = lines[i].substr(REPLACE_SLUG.length());
|
||||
lines[i] = oldText;
|
||||
this->EraseLine(i+1);
|
||||
changed = true;
|
||||
} else if (lines[i].starts_with(ADD_SLUG))
|
||||
{
|
||||
EraseLine(i);
|
||||
EraseLine(i);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -47,6 +47,7 @@ public:
|
||||
bool HasValue(const std::string&key) const;
|
||||
bool Get(const std::string&key,std::string*pResult) const;
|
||||
std::string Get(const std::string&key) const;
|
||||
const std::string& Get(int position) const { return lines[position]; }
|
||||
void Set(const std::string&key,const std::string &value);
|
||||
void Set(const std::string&key,const std::string &value, const std::string&comment);
|
||||
void SetDefault(const std::string&key, const std::string &value);
|
||||
@@ -66,6 +67,10 @@ public:
|
||||
void InsertLine(int position, const std::string&line);
|
||||
void AppendLine(const std::string&line);
|
||||
void SetLineValue(int index, const std::string&line) { lines[index] = line; }
|
||||
|
||||
void UndoableReplaceLine(int line,const std::string&text);
|
||||
int UndoableAddLine(int line,const std::string&text);
|
||||
bool RemoveUndoableActions();
|
||||
};
|
||||
|
||||
};
|
||||
+29
-21
@@ -1160,23 +1160,7 @@ namespace pipedal
|
||||
m_endpoint.set_close_handler(bind(&WebServerImpl::on_close, this, _1));
|
||||
m_endpoint.set_http_handler(bind(&WebServerImpl::on_http, this, _1));
|
||||
|
||||
std::string hostName = getHostName();
|
||||
if (hostName.length() != 0)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "Listening on " << hostName << ".local:" << this->port;
|
||||
Lv2Log::info(ss.str());
|
||||
}
|
||||
std::string ipv4Address = getIpv4Address("eth0");
|
||||
if (ipv4Address.length() != 0)
|
||||
{
|
||||
Lv2Log::info(SS("Listening on " << ipv4Address << ":" << this->port));
|
||||
}
|
||||
std::string wifiAddress = getIpv4Address("wlan0");
|
||||
if (wifiAddress.length() != 0)
|
||||
{
|
||||
Lv2Log::info(SS("Listening on Wi-Fi address " << wifiAddress << ":" << this->port));
|
||||
}
|
||||
DisplayIpAddresses();
|
||||
|
||||
std::stringstream ss;
|
||||
ss << port;
|
||||
@@ -1252,11 +1236,12 @@ namespace pipedal
|
||||
|
||||
for (auto it = m_connections.begin(); it != m_connections.end(); ++it)
|
||||
{
|
||||
try {
|
||||
m_endpoint.close(*it, websocketpp::close::status::normal, "");
|
||||
} catch (const std::exception&ignored)
|
||||
try
|
||||
{
|
||||
m_endpoint.close(*it, websocketpp::close::status::normal, "");
|
||||
}
|
||||
catch (const std::exception &ignored)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1280,6 +1265,8 @@ namespace pipedal
|
||||
this->pBgThread = new std::thread(ThreadProc, this);
|
||||
}
|
||||
|
||||
virtual void DisplayIpAddresses() override;
|
||||
|
||||
WebServerImpl(const std::string &address, int port, const char *rootPath, int threads, size_t maxUploadSize);
|
||||
};
|
||||
} // namespace pipedal
|
||||
@@ -1314,3 +1301,24 @@ std::shared_ptr<WebServer> pipedal::WebServer::create(
|
||||
{
|
||||
return std::shared_ptr<WebServer>(new WebServerImpl(address.to_string(), port, rootPath, threads, maxUploadSize));
|
||||
}
|
||||
|
||||
void WebServerImpl::DisplayIpAddresses()
|
||||
{
|
||||
std::string hostName = getHostName();
|
||||
if (hostName.length() != 0)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "Listening on mDns address " << hostName << ":" << this->port;
|
||||
Lv2Log::info(ss.str());
|
||||
}
|
||||
std::string ipv4Address = getIpv4Address("eth0");
|
||||
if (ipv4Address.length() != 0)
|
||||
{
|
||||
Lv2Log::info(SS("Listening on eth0 address " << ipv4Address << ":" << this->port));
|
||||
}
|
||||
std::string wifiAddress = getIpv4Address("wlan0");
|
||||
if (wifiAddress.length() != 0)
|
||||
{
|
||||
Lv2Log::info(SS("Listening on Wi-Fi address " << wifiAddress << ":" << this->port));
|
||||
}
|
||||
}
|
||||
@@ -222,6 +222,7 @@ public:
|
||||
virtual ~WebServer() { }
|
||||
|
||||
virtual void SetLogHttpRequests(bool enableLogging) = 0;
|
||||
virtual void DisplayIpAddresses() = 0;
|
||||
|
||||
virtual void AddRequestHandler(std::shared_ptr<RequestHandler> requestHandler) = 0;
|
||||
virtual void AddSocketFactory(std::shared_ptr<ISocketFactory> &socketHandler) = 0;
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
#include "HotspotManager.hpp"
|
||||
#include "WifiConfigSettings.hpp"
|
||||
#include <iostream>
|
||||
#include "Lv2Log.hpp"
|
||||
#include "DBusLog.hpp"
|
||||
|
||||
using namespace pipedal;
|
||||
|
||||
int main(int argc, char**argv)
|
||||
{
|
||||
WifiConfigSettings settings;
|
||||
settings.Load();
|
||||
if (!settings.valid_ || !settings.hasPassword_)
|
||||
{
|
||||
settings.valid_ = true;
|
||||
settings.channel_ = "1";
|
||||
settings.hotspotName_ = "pipedal";
|
||||
settings.password_ = "password";
|
||||
settings.hasPassword_ = true;
|
||||
settings.Save();
|
||||
}
|
||||
|
||||
Lv2Log::log_level(LogLevel::Debug);
|
||||
SetDBusLogLevel(DBusLogLevel::Trace);
|
||||
|
||||
HotspotManager::ptr hotspotManager = HotspotManager::Create();
|
||||
hotspotManager->Open();
|
||||
|
||||
while (true)
|
||||
{
|
||||
std::string line;
|
||||
std::cout << "e=enable,x=disable,q=quit > " << std::endl;;
|
||||
std::getline(std::cin,line);
|
||||
|
||||
if (line == "e")
|
||||
{
|
||||
settings.Load();
|
||||
settings.enable_ = true;
|
||||
settings.Save();
|
||||
hotspotManager->Reload();
|
||||
} else if (line == "x")
|
||||
{
|
||||
settings.Load();
|
||||
settings.enable_ = false;
|
||||
settings.Save();
|
||||
hotspotManager->Reload();
|
||||
} else if (line == "q") {
|
||||
break;
|
||||
} else {
|
||||
std::cout << "Invalid command." << std::endl;
|
||||
}
|
||||
}
|
||||
hotspotManager->Close();
|
||||
hotspotManager = nullptr;
|
||||
|
||||
|
||||
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
+9
-1
@@ -117,7 +117,7 @@ int main(int argc, char *argv[])
|
||||
if (help || parser.Arguments().size() == 0)
|
||||
{
|
||||
std::cout << "pipedald - Pipedal web socket server.\n"
|
||||
"Copyright (c) 2022 Robin Davies.\n"
|
||||
"Copyright (c) 2022-2024 Robin Davies.\n"
|
||||
"\n";
|
||||
}
|
||||
}
|
||||
@@ -241,6 +241,14 @@ int main(int argc, char *argv[])
|
||||
PiPedalModel model;
|
||||
|
||||
|
||||
model.SetNetworkChangedListener(
|
||||
[&server]() mutable {
|
||||
if (server)
|
||||
{
|
||||
server->DisplayIpAddresses();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
model.SetRestartListener(
|
||||
[]()
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
[Adding or changing system-wide NetworkManager connections]
|
||||
Identity=unix-group:pipedal_d
|
||||
Action=org.freedesktop.NetworkManager.settings.modify.system
|
||||
ResultAny=yes
|
||||
ResultInactive=yes
|
||||
ResultActive=yes
|
||||
@@ -0,0 +1,6 @@
|
||||
// grant NetworkManager dbus permissions (no-prompts) to the pipedald systemd service.
|
||||
polpolkit.addRule(function(action, subject) {
|
||||
if (action.id.indexOf("org.freedesktop.NetworkManager.") == 0 && subject.isInGroup("pipedal_d")) {
|
||||
return polkit.Result.YES;
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user