diff --git a/.vscode/settings.json b/.vscode/settings.json index eb5ed79..efc34fd 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -96,7 +96,8 @@ "nm-device-wifi-p2p.h": "c", "strstream": "cpp", "p2p_i.h": "c", - "p2p.h": "c" + "p2p.h": "c", + "hash_set": "cpp" }, "cSpell.words": [ "Guitarix", diff --git a/CMakeLists.txt b/CMakeLists.txt index 7bef38e..a6b89dc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -50,6 +50,13 @@ install( install( FILES ${PROJECT_SOURCE_DIR}/debian/copyright DESTINATION /usr/share/doc/pipedal ) +#install ( +# DIRECTORY ${PROJECT_SOURCE_DIR}/src/polkit-1/rules/10-pipedal-networkmanager.rule DESTINATION /var/lib/polkit-1/localauthority/ +#) + +install ( + FILES ${PROJECT_SOURCE_DIR}/src/polkit-1/rules/55-pipedal-networkmanager.rule DESTINATION /etc/polkit-1/rules.d/ +) install ( DIRECTORY ${REACT_BUILD_DIRECTORY} DESTINATION /etc/pipedal/react ) diff --git a/NetworkManagerP2P/src/DBusLog.cpp b/NetworkManagerP2P/src/DBusLog.cpp index 9c01c28..fb4a9f8 100644 --- a/NetworkManagerP2P/src/DBusLog.cpp +++ b/NetworkManagerP2P/src/DBusLog.cpp @@ -215,6 +215,11 @@ void LogTrace(const std::string&path,const char*method,const std::string&message } } +void SetDBusLogger(std::unique_ptr && logger) +{ + loggers.clear(); + loggers.push_back(std::move(logger)); +} void SetDBusConsoleLogger() { diff --git a/NetworkManagerP2P/src/DBusLog.hpp b/NetworkManagerP2P/src/DBusLog.hpp index bbbcb12..814b29c 100644 --- a/NetworkManagerP2P/src/DBusLog.hpp +++ b/NetworkManagerP2P/src/DBusLog.hpp @@ -42,6 +42,7 @@ void SetDBusConsoleLogger(); void AddDBusConsoleLogger(); void SetDBusSystemdLogger(); void SetDBusFileLogger(const std::filesystem::path &path); +void SetDBusLogger(std::unique_ptr&&logger); extern void LogError(const std::string&path,const char*method,const std::string&message); extern void LogInfo(const std::string&path,const char*method,const std::string&message); diff --git a/NetworkManagerP2P/src/NMP2pSettings.cpp b/NetworkManagerP2P/src/NMP2pSettings.cpp index e497f45..3b1fb0b 100644 --- a/NetworkManagerP2P/src/NMP2pSettings.cpp +++ b/NetworkManagerP2P/src/NMP2pSettings.cpp @@ -193,7 +193,7 @@ void P2pSettings::Load() } -static void openWithPerms( +static void openWithRestrictedPerms( pipedal::ofstream_synced &f, const std::filesystem::path &path, std::filesystem::perms perms = @@ -229,7 +229,7 @@ void P2pSettings::Save() auto filename = config_filename(); try { pipedal::ofstream_synced f; - openWithPerms(f,filename); + openWithRestrictedPerms(f,filename); if (!f.is_open()) { diff --git a/PiPedalCommon/src/CMakeLists.txt b/PiPedalCommon/src/CMakeLists.txt index 295a63e..154e8ce 100644 --- a/PiPedalCommon/src/CMakeLists.txt +++ b/PiPedalCommon/src/CMakeLists.txt @@ -41,6 +41,34 @@ message(STATUS "NMPIPEDAL CMAKE_CXX_FLAGS: ${CMAKE_CXX_FLAGS}") # Use the sdbus-c++ target in SDBusCpp namespace add_library(PiPedalCommon STATIC + include/dbus/org.freedesktop.NetworkManager.Device.Statistics.hpp + include/dbus/org.freedesktop.NetworkManager.Settings.Connection.hpp + include/dbus/org.freedesktop.NetworkManager.IP6Config.hpp + include/dbus/org.freedesktop.NetworkManager.Connection.Active.hpp + include/dbus/org.freedesktop.NetworkManager.DnsManager.hpp + include/dbus/org.freedesktop.NetworkManager.IP4Config.hpp + include/dbus/org.freedesktop.NetworkManager.Settings.hpp + include/dbus/org.freedesktop.NetworkManager.Device.hpp + include/dbus/org.freedesktop.NetworkManager.DHCP6Config.hpp + include/dbus/org.freedesktop.NetworkManager.AccessPoint.hpp + include/dbus/org.freedesktop.NetworkManager.Device.WifiP2P.hpp + include/dbus/org.freedesktop.NetworkManager.WifiP2PPeer.hpp + include/dbus/org.freedesktop.NetworkManager.hpp + include/dbus/org.freedesktop.NetworkManager.Device.Wireless.hpp + include/dbus/org.freedesktop.NetworkManager.DHCP4Config.hpp + + NetworkManagerInterfaces.cpp + include/NetworkManagerInterfaces.hpp + + DBusEvent.cpp + DBusVariantHelper.cpp + DBusLog.cpp + DBusDispatcher.cpp + include/DBusDispatcher.hpp + include/DBusEvent.hpp + include/DBusLog.hpp + include/DBusVariantHelper.hpp + ofstream_synced.cpp include/ofstream_synced.hpp ConfigSerializer.cpp include/ConfigSerializer.hpp WifiRegs.cpp include/WifiRegs.hpp @@ -66,7 +94,7 @@ add_library(PiPedalCommon STATIC ss.hpp ) target_include_directories(PiPedalCommon PUBLIC "include" ${LIBNL3_INCLUDE_DIRS}) -target_link_libraries(PiPedalCommon PUBLIC ${LIBNL3_LIBRARIES} ) +target_link_libraries(PiPedalCommon PUBLIC ${LIBNL3_LIBRARIES} SDBusCpp::sdbus-c++ ) diff --git a/PiPedalCommon/src/DBusDispatcher.cpp b/PiPedalCommon/src/DBusDispatcher.cpp new file mode 100644 index 0000000..a9ad904 --- /dev/null +++ b/PiPedalCommon/src/DBusDispatcher.cpp @@ -0,0 +1,273 @@ +#include "DBusDispatcher.hpp" +#include "DBusLog.hpp" +#include +#include +#include +#include "ss.hpp" +#include +#include + +DBusDispatcher::DBusDispatcher(bool systemBus) +{ + // SignalStop requires this to be true. + + if (systemBus) + { + busConnection = sdbus::createSystemBusConnection(); + } + else + { + busConnection = sdbus::createSessionBusConnection(); + } +} +DBusDispatcher::~DBusDispatcher() +{ + Stop(); + busConnection = nullptr; +} + +DBusDispatcher::PostHandle DBusDispatcher::Post(DBusDispatcher::PostCallback &&fn) +{ + auto nextHandle = NextHandle(); + { + std::lock_guard lock{postMutex}; + this->postedEvents.push_back(CallbackEntry{std::move(fn), clock::time_point::min(), nextHandle}); + } + WakeThread(); + return nextHandle; +} + +bool DBusDispatcher::CancelPost(DBusDispatcher::PostHandle handle) +{ + { + std::lock_guard lock{postMutex}; + for (auto i = postedEvents.begin(); i != postedEvents.end(); ++i) + { + if (i->handle == handle) + { + postedEvents.erase(i); + return true; + } + } + return false; + } +} + +DBusDispatcher::PostHandle DBusDispatcher::PostDelayed(const clock::duration &delay, PostCallback &&fn) +{ + PostHandle nextHandle = NextHandle(); + { + std::lock_guard lock{postMutex}; + + this->postedEvents.emplace_back( + CallbackEntry{ + std::move(fn), + clock::now() + delay, + nextHandle}); + } + WakeThread(); + return nextHandle; +} + +void DBusDispatcher::Run() +{ + this->eventFd = eventfd(0, 0); + this->serviceThread = std::thread( + [this]() + { this->ThreadProc(); }); +} + +void DBusDispatcher::WakeThread() +{ + uint64_t val = 1; + if (eventFd != -1) + { + std::ignore = write(eventFd, &val, sizeof(val)); + } +} +void DBusDispatcher::ThreadProc() +{ + try + { + + while (true) + { + bool idle; + bool quitting; + while (true) + { + idle = true; + quitting = false; + + while (busConnection->processPendingRequest()) + { + idle = false; + } + { + // process one event. + PostCallback callback; + auto now = clock::now(); + { + std::lock_guard lock{postMutex}; + for (auto iter = postedEvents.begin(); iter != postedEvents.end(); ++iter) + { + if (now >= iter->time) + { + callback = std::move(iter->callback); + postedEvents.erase(iter); + idle = false; + break; + } + } + quitting = this->stopping; // transfer with the mutex held. + } + if (callback) + { + callback(); + } + } + if (idle) + { + break; + } + } + if (idle && signalStopRequested) + { + signalStopRequested = false; + if (!signalStopExecuted) // only ever do this once. + { + signalStopExecuted = true; + std::atomic_thread_fence(std::memory_order::acquire); + signalStopCallback(); + signalStopCallback = std::function(); + this->stopping = true; + idle = false; + } + } + if (idle) + { + if (quitting) + { + if (postedEvents.size() == 0) + { + break; + } + } + sdbus::IConnection::PollData pollData = busConnection->getEventLoopPollData(); + + struct pollfd pollFds[2]; + + pollFds[0].fd = pollData.fd; + pollFds[0].events = pollData.events; + pollFds[0].revents = 0; + pollFds[1].fd = this->eventFd; + pollFds[1].events = POLLIN; + pollFds[1].revents = 0; + int pollTimeout = pollData.getPollTimeout(); + int eventTimeout = GetEventTimeoutMs(); + if (eventTimeout != -1 && eventTimeout < pollTimeout) + { + pollTimeout = eventTimeout;/*+-*/ + } + if (pollTimeout > 250 || pollTimeout == -1) + { + pollTimeout = 250; // poll for quit every 250 ms. + } + if (pollTimeout != 0) + { + poll(pollFds, 2, pollTimeout); + + if (pollFds[1].revents & POLLIN) // received a wakup event? + { + uint64_t counter; + std::ignore = read(pollFds[1].fd, &counter, sizeof(counter)); // reset the wakeup event. + } + } + } + } + } + catch (const std::exception &e) + { + std::string msg = SS("Abnormal termination. " << e.what()); + LogError("DBusDispatcher", "ThreadProc", msg); + } + isFinished = true; + LogTrace("DBusDispatcher", "ThreadProc", "Service thread terminated."); +} + +void DBusDispatcher::Wait() +{ + try + { + if (!threadJoined) + { + threadJoined = true; + serviceThread.join(); + } + } + catch (const std::exception &) + { + } + if (eventFd != -1) + { + close(eventFd); + eventFd = -1; + } +} + +bool DBusDispatcher::IsFinished() +{ + return isFinished; +} +void DBusDispatcher::Stop() +{ + if (!closed) + { + closed = true; + + this->stopping = true; + WakeThread(); + Wait(); + } +} + +void DBusDispatcher::SignalStop(std::function &&callback) +{ + this->signalStopCallback = std::move(callback); + std::atomic_thread_fence(::std::memory_order::release); + this->signalStopRequested = true; +} + +int DBusDispatcher::GetEventTimeoutMs() +{ + std::lock_guard lock{postMutex}; + + clock::time_point nextTime = clock::time_point::max(); + for (const auto &event : this->postedEvents) + { + if (event.time == clock::time_point::min()) + return 0; + if (event.time < nextTime) + { + nextTime = event.time; + } + } + if (nextTime == clock::time_point::max()) + { + return -1; + } + auto clockDuration = nextTime - clock::now(); + auto durationMs = std::chrono::duration_cast(clockDuration); + auto ticks = durationMs.count(); + if (ticks > 250) + return 250; + if (ticks < 0) + return 0; + return (int)ticks; +} + +bool DBusDispatcher::IsStopping() +{ + std::lock_guard lock{postMutex}; + return stopping; +} diff --git a/PiPedalCommon/src/DBusEvent.cpp b/PiPedalCommon/src/DBusEvent.cpp new file mode 100644 index 0000000..a06f285 --- /dev/null +++ b/PiPedalCommon/src/DBusEvent.cpp @@ -0,0 +1,7 @@ +#include "DBusEvent.hpp" + + + +namespace impl { + std::atomic NextHandle; +} \ No newline at end of file diff --git a/PiPedalCommon/src/DBusLog.cpp b/PiPedalCommon/src/DBusLog.cpp new file mode 100644 index 0000000..82c0e15 --- /dev/null +++ b/PiPedalCommon/src/DBusLog.cpp @@ -0,0 +1,255 @@ +#include "DBusLog.hpp" +#include +#include "ss.hpp" +#include +#include +#include +#include "ss.hpp" +#include +#include "ofstream_synced.hpp" + + +namespace impl { + std::mutex logMutex; + DBusLogLevel dbusLogLevel = DBusLogLevel::Info; + +} + +static std::string getDateTime() +{ + auto now = std::chrono::system_clock::now(); + time_t in_time_t = std::chrono::system_clock::to_time_t(now); + auto ms = std::chrono::duration_cast(now.time_since_epoch()) % 1000; + std::stringstream ss; + ss << std::put_time(std::localtime(&in_time_t),"%Y-%m-%d %H:%M:%S"); + ss << '.' << std::setfill('0') << std::setw(3) << ms.count(); + ss << " "; + return ss.str(); +} + +class ConsoleDBusLogger : public IDBusLogger { + +public: + ConsoleDBusLogger(bool showTime = true) + :showTime(showTime) + { + + } + void LogError(const std::string&message) { + std::cout << getDateTime() << "error: " << message << std::endl; + } + void LogWarning(const std::string&message){ + std::cout << getDateTime() << "warning: " << message << std::endl; + } + void LogInfo(const std::string&message) { + std::cout << getDateTime() << "info: " << message << std::endl; + } + void LogDebug(const std::string&message) { + std::cout << getDateTime() << "debug: " << message << std::endl; + } + void LogTrace(const std::string&message) { + std::cout << getDateTime() << " trace: " << message << std::endl; + } +private: + + std::string getDateTime() { + return (showTime) ? getDateTime(): ""; + } + bool showTime; +}; +class FileDBusLogger : public IDBusLogger { + +public: + FileDBusLogger(const std::filesystem::path&path) + { + f.open(path); + if (!f) + { + throw std::runtime_error(SS("Can't open file " << path)); + } + } + void LogError(const std::string&message) { + f << "error: " << message << std::endl; + } + void LogWarning(const std::string&message){ + f << "warning: " << message << std::endl; + } + void LogInfo(const std::string&message) { + f << "info: " << message << std::endl; + } + void LogDebug(const std::string&message) { + f << "debug: " << message << std::endl; + } + void LogTrace(const std::string&message) { + f << "trace: " << message << std::endl; + } +private: + pipedal::ofstream_synced f; +}; + +class SystemdDBusLogger : public IDBusLogger { + +public: + void LogError(const std::string&message) { + sd_journal_print(LOG_ERR,"%s",message.c_str()); + + } + void LogWarning(const std::string&message){ + sd_journal_print(LOG_WARNING,"%s",message.c_str()); + } + void LogInfo(const std::string&message) { + sd_journal_print(LOG_INFO,"%s",message.c_str()); + + } + void LogDebug(const std::string&message) { + sd_journal_print(LOG_DEBUG,"%s",message.c_str()); + } + void LogTrace(const std::string&message) { + // no trace level. just log as debug. + sd_journal_print(LOG_DEBUG,"%s",message.c_str()); + } + +}; + + + + +static std::vector> loggers; + +void SetDBusLogger(std::unique_ptr && logger) +{ + loggers.clear(); + loggers.push_back(std::move(logger)); +} + + +void SetDBusLogLevel(DBusLogLevel level) +{ + impl::dbusLogLevel = level; +} + +void LogError(const std::string&path,const char*method,const std::string&message) +{ + if (impl::dbusLogLevel > DBusLogLevel::Error) return; + std::lock_guard lock{impl::logMutex}; + for (auto &logger: loggers) + { + logger->LogError(SS(path<< "::" << method << ": "<< message)); + } +} +void LogInfo(const std::string&path,const char*method,const std::string&message) +{ + if (impl::dbusLogLevel > DBusLogLevel::Info) return; + std::lock_guard lock{impl::logMutex}; + for (auto &logger: loggers) + { + logger->LogInfo(SS(path<< "::" << method<< ": " << message)); + } +} + +void LogDebug(const std::string&path,const char*method,const std::string&message) +{ + if (impl::dbusLogLevel > DBusLogLevel::Debug) return; + std::lock_guard lock{impl::logMutex}; + + for (auto &logger: loggers) + { + logger->LogDebug(SS(path<< "::" << method << ": "<< message)); + } +} + +void LogDebug(const std::string&path,const char*method,const std::function &text) +{ + if (impl::dbusLogLevel > DBusLogLevel::Debug) return; + std::lock_guard lock{impl::logMutex}; + + for (auto &logger: loggers) + { + logger->LogDebug(SS(path<< "::" << method << ": " << text())); + } +} + +void LogTrace(const std::string&path,const char*method,const std::function &text) +{ + if (impl::dbusLogLevel > DBusLogLevel::Trace) return; + std::lock_guard lock{impl::logMutex}; + for (auto &logger: loggers) + { + logger->LogTrace(SS(path<< "::" << method << ": " << text())); + } +} +void LogWarning(const std::string&path,const char*method,const std::function &text) +{ + if (impl::dbusLogLevel > DBusLogLevel::Warning) return; + std::lock_guard lock{impl::logMutex}; + std::string message = text(); + for (auto &logger: loggers) + { + logger->LogWarning(SS(path<< "::" << method << ": " << message)); + } +} +void LogInfo(const std::string&path,const char*method,const std::function &text) +{ + if (impl::dbusLogLevel > DBusLogLevel::Info) return; + std::lock_guard lock{impl::logMutex}; + std::string message = text();; + for (auto &logger: loggers) + { + logger->LogInfo(SS(path<< "::" << method << ": " << message)); + } +} +void LogError(const std::string&path,const char*method,const std::function &text) +{ + if (impl::dbusLogLevel > DBusLogLevel::Error) return; + std::lock_guard lock{impl::logMutex}; + for (auto &logger: loggers) + { + logger->LogError(SS(path<< "::" << method << ": " << text())); + } +} + + +void LogWarning(const std::string&path,const char*method,const std::string&message) +{ + if (impl::dbusLogLevel > DBusLogLevel::Warning) return; + std::lock_guard lock{impl::logMutex}; + for (auto &logger: loggers) + { + logger->LogWarning(SS(path<< "::" << method << ": " << message)); + } +} + + +void LogTrace(const std::string&path,const char*method,const std::string&message) +{ + if (impl::dbusLogLevel > DBusLogLevel::Trace) return; + + std::lock_guard lock{impl::logMutex}; + for (auto &logger: loggers) + { + logger->LogTrace(SS(path<< "::" << method << ": " << message)); + } +} + + +void SetDBusConsoleLogger() +{ + loggers.clear(); + loggers.push_back(std::make_unique()); +} +void AddDBusConsoleLogger() +{ + loggers.push_back(std::make_unique()); +} +void SetDBusSystemdLogger() +{ + loggers.clear(); + loggers.push_back(std::make_unique()); + +} +void SetDBusFileLogger(const std::filesystem::path &path) +{ + loggers.clear(); + loggers.push_back(std::make_unique(path)); +} + diff --git a/PiPedalCommon/src/DBusVariantHelper.cpp b/PiPedalCommon/src/DBusVariantHelper.cpp new file mode 100644 index 0000000..4cea124 --- /dev/null +++ b/PiPedalCommon/src/DBusVariantHelper.cpp @@ -0,0 +1,175 @@ +#include "DBusVariantHelper.hpp" +#include +#include + + +//std::ostream &operator<<(std::ostream &s, const std::map &properties) +std::ostream&operator<<(std::ostream&s, const sdbus::Variant&v) +{ + // clang-format off +#define HANDLE_VARIANT_TYPE(type) \ + if (v.containsValueOfType()) \ + { \ + s << v.get(); \ + } + + HANDLE_VARIANT_TYPE(std::string) + else if (v.containsValueOfType()) + { + uint8_t value = v.get(); + s << (int)value; + } + else HANDLE_VARIANT_TYPE(uint32_t) + else HANDLE_VARIANT_TYPE(int32_t) + else HANDLE_VARIANT_TYPE(uint64_t) + else HANDLE_VARIANT_TYPE(int64_t) + else HANDLE_VARIANT_TYPE(uint16_t) + else HANDLE_VARIANT_TYPE(int16_t) + else HANDLE_VARIANT_TYPE(bool) + else HANDLE_VARIANT_TYPE(sdbus::Variant) + else if (v.containsValueOfType()) + { + auto objectPath = v.get(); + s << '[' << objectPath.c_str() << ']'; + } + else if (v.containsValueOfType>()) + { + auto values = v.get>(); + { + s << '['; + bool first = true; + for (const sdbus::ObjectPath&value: values) + { + if (!first) s << ", "; + first = false; + s << value.c_str(); + } + s << ']'; + + } + + } + else if (v.containsValueOfType>()) + { + auto paths = v.get >(); + { + s << '['; + bool first = true; + for (const sdbus::ObjectPath&path: paths) + { + if (!first) s << ", "; + first = false; + s << path.c_str(); + } + s << ']'; + + } + } + else if (v.containsValueOfType>()) + { + std::vector bytes = v.get>(); + s << '['; + bool first = true; + for (uint8_t byte: bytes) + { + if (!first) s << ", "; + first = false; + s << (int)byte; + + } + s << ']'; + + } + else if (v.containsValueOfType>>()) + { + auto arrayOfArrays = v.get>>(); + s << '['; + bool first = true; + for (const auto& array: arrayOfArrays) + { + if (!first) s << ", "; + first = false; + bool first2 = true; + for (auto byte: array) + { + if (!first2) s << ", "; + first2 = false; + s << byte; + + } + } + s << ']'; + + } + else if (v.containsValueOfType>()) + { + std::map map = v.get>(); + s << "{"; + bool first = true; + for (const auto &[key, value]: map) + { + if (!first) s << ","; + first = false; + s << '\"' << key << "\": " << value; + } + s << "}"; + } else if (v.containsValueOfType>>()) + { + std::map> values + = v.get>>(); + s << '{'; + bool first = true; + for (const auto &[key,bytes]: values) + { + if (!first) s << ','; + first = false; + s << "{\"" << key << "\": ["; + bool first2 = true; + for (auto b : bytes) + { + if (!first2) s << ','; + first2 = false; + s << b; + } + s << "]"; + } + s << '}'; + + } + + // else HANDLE_VARIANT_TYPE(float) + // else HANDLE_VARIANT_TYPE(double) + // else HANDLE_VARIANT_TYPE(uint64_t) + // else HANDLE_VARIANT_TYPE(int64_t) + // else HANDLE_VARIANT_TYPE(char) + // else HANDLE_VARIANT_TYPE(uint8_t) + // else HANDLE_VARIANT_TYPE(int8_t) + else + { + s << "UNKNOWN VALUE OF TYPE " << v.peekValueType(); + } +#undef HANDLE_VARIANT_TYPE + // clang-format on + return s; +} +std::ostream&operator<<(std::ostream&s,const std::map& properties) +{ + + s << "{"; + bool first = true; + for (const auto &property : properties) + { + if (!first) + { + s << ", "; + } + first = false; + s << '"' << property.first << "\": "; + + const sdbus::Variant &v = property.second; + s << v; + } + + s << "}"; + return s; +} diff --git a/PiPedalCommon/src/NetworkManagerInterfaces.cpp b/PiPedalCommon/src/NetworkManagerInterfaces.cpp new file mode 100644 index 0000000..ff94fda --- /dev/null +++ b/PiPedalCommon/src/NetworkManagerInterfaces.cpp @@ -0,0 +1,46 @@ +#include "NetworkManagerInterfaces.hpp" +#include "ss.hpp" + +using namespace dbus::networkmanager; +using namespace dbus::networkmanager::nm_impl; + +namespace dbus::networkmanager::nm_impl +{ + std::string NetworkManagerStateToString(uint32_t state) + { + // STUB: Not clear which state enum we're getting. + std::stringstream s; + s << state; + return s.str(); + } + + std::string NetworkManagerDeviceStateToString(uint32_t state) + { +#define DEVICE_CASE(name, value) \ + case value: \ + return #name; + + switch (state) + { + DEVICE_CASE(NM_DEVICE_STATE_UNKNOWN, 0) + DEVICE_CASE(NM_DEVICE_STATE_UNMANAGED, 10) + DEVICE_CASE(NM_DEVICE_STATE_UNAVAILABLE, 20) + DEVICE_CASE(NM_DEVICE_STATE_DISCONNECTED, 30) + DEVICE_CASE(NM_DEVICE_STATE_PREPARE, 40) + DEVICE_CASE(NM_DEVICE_STATE_CONFIG, 50) + DEVICE_CASE(NM_DEVICE_STATE_NEED_AUTH, 60) + DEVICE_CASE(NM_DEVICE_STATE_IP_CONFIG, 70) + DEVICE_CASE(NM_DEVICE_STATE_IP_CHECK, 80) + DEVICE_CASE(NM_DEVICE_STATE_SECONDARIES, 90) + DEVICE_CASE(NM_DEVICE_STATE_ACTIVATED, 100) + DEVICE_CASE(NM_DEVICE_STATE_DEACTIVATING, 110) + DEVICE_CASE(NM_DEVICE_STATE_FAILED, 120) + default: + { + std::stringstream s; + s << state; + return s.str(); + } + } + } +} diff --git a/PiPedalCommon/src/WifiConfigSettings.cpp b/PiPedalCommon/src/WifiConfigSettings.cpp index 2ef58c5..60ef354 100644 --- a/PiPedalCommon/src/WifiConfigSettings.cpp +++ b/PiPedalCommon/src/WifiConfigSettings.cpp @@ -21,30 +21,412 @@ #include #include "ss.hpp" #include "ChannelInfo.hpp" +#include +#include "ofstream_synced.hpp" +#include +#include +#include "SysExec.hpp" + +#include +#include +#include using namespace pipedal; using namespace std; +namespace fs = std::filesystem; -JSON_MAP_BEGIN(WifiConfigSettings) - JSON_MAP_REFERENCE(WifiConfigSettings,valid) - JSON_MAP_REFERENCE(WifiConfigSettings,wifiWarningGiven) - JSON_MAP_REFERENCE(WifiConfigSettings,rebootRequired) - JSON_MAP_REFERENCE(WifiConfigSettings,enable) - JSON_MAP_REFERENCE(WifiConfigSettings,hotspotName) - JSON_MAP_REFERENCE(WifiConfigSettings,mdnsName) - JSON_MAP_REFERENCE(WifiConfigSettings,hasPassword) - JSON_MAP_REFERENCE(WifiConfigSettings,password) - JSON_MAP_REFERENCE(WifiConfigSettings,countryCode) - JSON_MAP_REFERENCE(WifiConfigSettings,channel) -JSON_MAP_END() +static const fs::path CONFIG_PATH = "/var/pipedal/config/wifiConfig.json"; -bool WifiConfigSettings::ValidateCountryCode(const std::string&value) +WifiConfigSettings::WifiConfigSettings() { - if (value.size() < 2 || value.size() > 3) return false; - return true; + Load(); } -int32_t pipedal::ChannelToChannelNumber(const std::string&channel) +static std::string getWifiCountryCode() +{ + try + { + SysExecOutput result = sysExecForOutput("iw", "reg get"); + if (result.exitCode != EXIT_SUCCESS) + { + return ""; + } + const std::string &output = result.output; + size_t pos = output.find("country"); + if (pos != std::string::npos) + { + std::string result = output.substr(pos + 8, 2); + if (WifiConfigSettings::ValidateCountryCode(result)) + { + return result; + } + } + } + catch (const std::exception &e) + { + return ""; // unset. + } + return ""; +} + +static void setWifiCountryCode(const std::string &countryCode) +{ + if (!WifiConfigSettings::ValidateCountryCode(countryCode)) + { + throw std::runtime_error("Invalid country code."); + } + SysExecOutput result = sysExecForOutput("iw", SS("reg set " << countryCode)); + if (result.exitCode != EXIT_SUCCESS) + { + throw std::runtime_error(SS("Failed to set country code: " << result.output)); + } +} +bool WifiConfigSettings::ValidateCountryCode(const std::string &text) +{ + if (text.length() != 2) + return false; + return std::isalpha(text[0]) && std::isalpha(text[1]); +} +void WifiConfigSettings::Load() +{ + try + { + if (fs::exists(CONFIG_PATH)) + { + std::ifstream f(CONFIG_PATH); + if (!f) + { + throw std::runtime_error("Can't open file."); + } + json_reader reader(f); + reader.read(this); + } + this->countryCode_ = getWifiCountryCode(); + } + catch (const std::exception &e) + { + throw std::runtime_error(SS("Can't load WifiConfigSettings. " << e.what())); + } +} + +static void openWithPerms( + pipedal::ofstream_synced &f, + const std::filesystem::path &path, + std::filesystem::perms perms = + std::filesystem::perms::owner_read | std::filesystem::perms::owner_write | + std::filesystem::perms::group_read | std::filesystem::perms::group_write) +{ + auto directory = path.parent_path(); + if (!std::filesystem::exists(directory)) + { + std::filesystem::create_directories(directory); + // open and close to make an existing empty file. + // close it. + { + std::ofstream f; + f.open(path); + f.close(); + } + try { + // set the perms. + std::filesystem::permissions( + path, + perms, + std::filesystem::perm_options::replace); + } catch (const std::exception&) { + } + } + + // open for re3al. + f.open(path); +} + +void WifiConfigSettings::Save() +{ + try + { + ofstream_synced f; + openWithPerms(f,CONFIG_PATH); + json_writer writer(f); + writer.write(this); + } + catch (const std::exception &e) + { + throw std::runtime_error(SS("Can't save WifiConfigSettings. " << e.what())); + } +} + +namespace pipedal::priv +{ + + class Device + { + public: + Device(const std::string &path) + : m_proxy(sdbus::createProxy( + sdbus::createSystemBusConnection(), + "org.freedesktop.NetworkManager", + path)) + { + } + + uint32_t GetDeviceType() const + { + sdbus::Variant device_type_variant; + m_proxy->callMethod("Get") + .onInterface("org.freedesktop.DBus.Properties") + .withArguments("org.freedesktop.NetworkManager.Device", "DeviceType") + .storeResultsTo(device_type_variant); + return device_type_variant.get(); + } + + std::string GetPath() const + { + return m_proxy->getObjectPath(); + } + + private: + std::unique_ptr m_proxy; + }; + class NetworkManager + { + public: + NetworkManager() + : m_proxy(sdbus::createProxy( + sdbus::createSystemBusConnection(), + "org.freedesktop.NetworkManager", + "/org/freedesktop/NetworkManager")), + m_settings_proxy(sdbus::createProxy( + sdbus::createSystemBusConnection(), + "org.freedesktop.NetworkManager", + "/org/freedesktop/NetworkManager/Settings")) + { + } + + struct HotspotInfo + { + std::string connection_path; + std::string active_connection_path; + }; + + std::string AddConnection(const std::map> &connection) + { + std::string path; + m_settings_proxy->callMethod("AddConnection") + .onInterface("org.freedesktop.NetworkManager.Settings") + .withArguments(connection) + .storeResultsTo(path); + return path; + } + std::string AddConnection(const std::string &ssid, const std::string &password) + { + // Create connection + std::map> connection; + connection["connection"]["type"] = sdbus::Variant("802-11-wireless"); + connection["connection"]["id"] = sdbus::Variant(ssid); + std::vector ssid_bytes(ssid.begin(), ssid.end()); + connection["802-11-wireless"]["ssid"] = sdbus::Variant(ssid_bytes); + connection["802-11-wireless"]["mode"] = sdbus::Variant(std::string("ap")); + connection["802-11-wireless-security"]["key-mgmt"] = sdbus::Variant(std::string("wpa-psk")); + connection["802-11-wireless-security"]["psk"] = sdbus::Variant(password); + connection["ipv4"]["method"] = sdbus::Variant(std::string("shared")); + connection["ipv6"]["method"] = sdbus::Variant(std::string("ignore")); + + return AddConnection(connection); + } + + std::string ActivateConnection(const std::string &connection_path, const std::string &device_path) + { + std::string active_connection_path; + m_proxy->callMethod("ActivateConnection") + .onInterface("org.freedesktop.NetworkManager") + .withArguments(connection_path, device_path, "/") + .storeResultsTo(active_connection_path); + return active_connection_path; + } + + void DeactivateConnection(const std::string &active_connection_path) + { + m_proxy->callMethod("DeactivateConnection") + .onInterface("org.freedesktop.NetworkManager") + .withArguments(active_connection_path); + } + + std::string GetWirelessDevicePath() + { + auto devices = GetAllDevices(); + for (const auto &device : devices) + { + if (device.GetDeviceType() == 2) + { // 2 is the value for Wi-Fi devices + return device.GetPath(); + } + } + throw std::runtime_error("No Wi-Fi device found"); + } + + std::string CreateHotspot(const std::string &ssid, const std::string &password) + { + // Create connection + std::map> connection; + connection["connection"]["type"] = sdbus::Variant("802-11-wireless"); + connection["connection"]["id"] = sdbus::Variant(ssid); + std::vector ssid_bytes(ssid.begin(), ssid.end()); + connection["802-11-wireless"]["ssid"] = sdbus::Variant(ssid_bytes); + connection["802-11-wireless"]["mode"] = sdbus::Variant(std::string("ap")); + connection["802-11-wireless-security"]["key-mgmt"] = sdbus::Variant(std::string("wpa-psk")); + connection["802-11-wireless-security"]["psk"] = sdbus::Variant(password); + connection["ipv4"]["method"] = sdbus::Variant(std::string("shared")); + connection["ipv6"]["method"] = sdbus::Variant(std::string("ignore")); + + auto connection_path = AddConnection(connection); + return connection_path; + } + + std::string EnableHotspot(const std::string &connection_path) + { + + // Enable hotspot + std::string device_path = GetWirelessDevicePath(); + std::string active_connection_path = ActivateConnection(connection_path, device_path); + + return active_connection_path; + } + + + void RemoveConnection(const std::string &connection) + { + auto connection_proxy = sdbus::createProxy( + m_proxy->getConnection(), + "org.freedesktop.NetworkManager", + connection); + connection_proxy->callMethod("Delete") + .onInterface("org.freedesktop.NetworkManager.Settings.Connection"); + } + + std::optional GetActiveConnection(const std::string &device_path) + { + std::vector active_connections; + m_proxy->callMethod("Get") + .onInterface("org.freedesktop.DBus.Properties") + .withArguments("org.freedesktop.NetworkManager", "ActiveConnections") + .storeResultsTo(active_connections); + + for (const auto &conn : active_connections) + { + auto conn_proxy = sdbus::createProxy( + m_proxy->getConnection(), + "org.freedesktop.NetworkManager", + conn); + + sdbus::ObjectPath conn_device; + conn_proxy->callMethod("Get") + .onInterface("org.freedesktop.DBus.Properties") + .withArguments("org.freedesktop.NetworkManager.Connection.Active", "Devices") + .storeResultsTo(conn_device); + + if (conn_device == device_path) + { + return conn; + } + } + + return std::nullopt; + } + std::string GetHotspotConnectionPath(const std::string &ssid) + { + std::vector connections; + m_settings_proxy->callMethod("ListConnections") + .onInterface("org.freedesktop.NetworkManager.Settings") + .storeResultsTo(connections); + + for (const auto &conn_path : connections) + { + auto conn_proxy = sdbus::createProxy(sdbus::createSystemBusConnection(), + "org.freedesktop.NetworkManager", + conn_path); + + sdbus::Variant settings_variant; + conn_proxy->callMethod("GetSettings") + .onInterface("org.freedesktop.NetworkManager.Settings.Connection") + .storeResultsTo(settings_variant); + + auto settings = settings_variant.get>>(); + + // Check if this is a Wi-Fi connection + if (settings["connection"]["type"].get() == "802-11-wireless") + { + // Check if it's an access point (hotspot) connection + if (settings["802-11-wireless"]["mode"].get() == "ap") + { + // Check if the SSID matches + std::vector conn_ssid = settings["802-11-wireless"]["ssid"].get>(); + std::string conn_ssid_str(conn_ssid.begin(), conn_ssid.end()); + if (conn_ssid_str == ssid) + { + return conn_path; + } + } + } + } + + return ""; // No matching hotspot connection found + } + + private: + std::vector GetAllDevices() + { + sdbus::Variant devices_variant; + m_proxy->callMethod("Get") + .onInterface("org.freedesktop.DBus.Properties") + .withArguments("org.freedesktop.NetworkManager", "AllDevices") + .storeResultsTo(devices_variant); + + std::vector device_paths = devices_variant.get>(); + std::vector devices; + for (const auto &path : device_paths) + { + devices.emplace_back(path); + } + return devices; + } + + std::unique_ptr m_proxy; + std::unique_ptr m_settings_proxy; + }; +} + + +JSON_MAP_BEGIN(WifiConfigSettings) +JSON_MAP_REFERENCE(WifiConfigSettings, valid) +JSON_MAP_REFERENCE(WifiConfigSettings, wifiWarningGiven) +JSON_MAP_REFERENCE(WifiConfigSettings, rebootRequired) +JSON_MAP_REFERENCE(WifiConfigSettings, enable) +JSON_MAP_REFERENCE(WifiConfigSettings, hotspotName) +JSON_MAP_REFERENCE(WifiConfigSettings, mdnsName) +JSON_MAP_REFERENCE(WifiConfigSettings, hasPassword) +JSON_MAP_REFERENCE(WifiConfigSettings, password) +JSON_MAP_REFERENCE(WifiConfigSettings, countryCode) +JSON_MAP_REFERENCE(WifiConfigSettings, channel) +JSON_MAP_REFERENCE(WifiConfigSettings, alwaysOn) +JSON_MAP_REFERENCE(WifiConfigSettings, homeNetworks) +JSON_MAP_END() + +int32_t pipedal::ChannelToChannelNumber(const std::string &channel) +{ + std::string t = channel; + // remove deprecated band specs. + if (t.size() > 1 && t[0] == 'a' || t[0] == 'g') + { + t = t.substr(1); + } + int32_t channelNumber = 0; + std::stringstream ss(t); + ss >> channelNumber; + return channelNumber; +} + +static uint32_t ParseChannel(const std::string &channel) { std::string t = channel; // remove dprecated band specs. @@ -52,24 +434,9 @@ int32_t pipedal::ChannelToChannelNumber(const std::string&channel) { t = t.substr(1); } - int32_t channelNumber = 1; - std::stringstream ss(t); - ss >> channelNumber; - return channelNumber; - -} - -static uint32_t ParseChannel(const std::string & channel) -{ - std::string t = channel; - // remove dprecated band specs. - if (t.size() > 1 && t[0] == 'a' || t[0] == 'g') - { - t = t.substr(1); - } size_t size = t.length(); - unsigned long long lChannel = std::stoull(t,&size); + unsigned long long lChannel = std::stoull(t, &size); if (size != t.length()) { throw invalid_argument("Expecting a number: '" + t + "'."); @@ -92,7 +459,7 @@ uint32_t pipedal::ChannelToWifiFrequency(uint32_t channel) // 2.4GHz. if (channel >= 1 && channel <= 13) { - return 2412 + 5*(channel-1); + return 2412 + 5 * (channel - 1); } if (channel == 14) { @@ -101,26 +468,25 @@ uint32_t pipedal::ChannelToWifiFrequency(uint32_t channel) // 802.11y if (channel >= 131 && channel < 137) { - return 3660 + (channel-131)*5; + return 3660 + (channel - 131) * 5; } if (channel >= 32 && channel <= 68 && (channel & 1) == 0) { - return 5160 + (channel-32)/2*10; + return 5160 + (channel - 32) / 2 * 10; } - if (channel == 96) return 5480; + if (channel == 96) + return 5480; if (channel >= 100 && channel <= 196) { - return 5500 + (channel-100)/5; + return 5500 + (channel - 100) / 5; } throw invalid_argument(SS("Invalid WiFi channel: " << channel)); - } - -bool WifiConfigSettings::ValidateChannel(const std::string&countryCode,const std::string&value) +bool WifiConfigSettings::ValidateChannel(const std::string &countryCode, const std::string &value) { - if (value == "0") // = "Autoselect". + if (value == "0") // = "Autoselect". { return true; } @@ -134,15 +500,17 @@ bool WifiConfigSettings::ValidateChannel(const std::string&countryCode,const std { throw std::invalid_argument(SS("Invalid country code: " << countryCode)); } - auto regDom = getWifiRegClass(countryCode,ParseChannel(value),40); - if (regDom == -1) { - std::vector valid_channels = getValidChannels(countryCode,40); + auto regDom = getWifiRegClass(countryCode, ParseChannel(value), 40); + if (regDom == -1) + { + std::vector valid_channels = getValidChannels(countryCode, 40); std::stringstream ss; ss << "Channel " << value << " is not permitted in the selected country.\n Valid channels: "; bool first = true; - for (auto channel: valid_channels) + for (auto channel : valid_channels) { - if (!first) ss << ", "; + if (!first) + ss << ", "; first = false; ss << channel; } @@ -153,10 +521,136 @@ bool WifiConfigSettings::ValidateChannel(const std::string&countryCode,const std return true; } - void WifiConfigSettings::ParseArguments(const std::vector &argv) - { +static const char* trueValues[] { + "true", + "on", + "yes" + "1", + nullptr +}; +static const char* falseValues[] { + "false", + "off", + "no", + "0", + nullptr +}; + +static bool Matches(const std::string&value, const char**matches) +{ + while(*matches) + { + if (value == *matches) return true; + ++matches; + } + return false; +} + +static bool TryStringToBool(const std::string &value, bool*outputValue) +{ + if (Matches(value,trueValues)) + { + *outputValue = true; + return true; + } + if (Matches(value, falseValues)) { + *outputValue = false; + return true; + } + *outputValue = false; + return false; + +} + +static WifiConfigSettings::ssid_t readSsid(std::istream&ss) +{ + using ssid_t = WifiConfigSettings::ssid_t; + char c; + + ssid_t result; + while (ss.peek() == ' ') + { + ss >> c; + } + if (ss.peek() == '"') + { + ss >> c; + while (!ss.eof() && ss.peek() != '"') + { + ss >> c; + if (c == '\\') + { + if (ss.eof()) + { + break; + } + ss >> c; + switch (c) { + case 'n': + result.push_back((uint8_t)'\n'); + break; + case 'r': + result.push_back((uint8_t)'\r'); + break; + case 't': + result.push_back((uint8_t)'\t'); + break; + case 'b': + result.push_back((uint8_t)'\b'); + break; + default: + result.push_back((uint8_t)c); + break; + } + } else { + result.push_back((uint8_t)c); + } + } + } else { + while (!ss.eof()) + { + if (c == ':') + { + break; + } + ss >> c; + if (c == ':') + { + break; + } + result.push_back((uint8_t)c); + } + } + return result; +} +static std::vector stringToSsidArray(const std::string&value) +{ + using ssid_t = WifiConfigSettings::ssid_t; + std::istringstream ss(value); + + std::vector result; + while (true) + { + ssid_t ssid = readSsid(ss); + if (ssid.size() == 0) + { + break; + } + result.push_back(ssid); + if (ss.peek() == ':') + { + char c; + ss >> c; + } + } + return result; + +} +void WifiConfigSettings::ParseArguments(const std::vector &argv) +{ this->valid_ = false; - if (argv.size() != 4) { + if (argv.size() < 4 || argv.size() > 6) + { throw invalid_argument("Invalid number of arguments."); } this->enable_ = true; @@ -166,23 +660,91 @@ bool WifiConfigSettings::ValidateChannel(const std::string&countryCode,const std this->password_ = argv[2]; this->channel_ = argv[3]; this->hasPassword_ = this->password_.length() != 0; + this->homeNetworks_ = std::vector(); + this->alwaysOn_ = false; + if (!ValidateCountryCode(this->countryCode_)) { throw invalid_argument("Invalid country code."); } - if (this->hotspotName_.length() > 31) throw invalid_argument("Hotspot name is too long."); - if (this->hotspotName_.length() < 1) throw invalid_argument("Hotspot name is too short."); - if (this->password_.size() != 0 && this->password_.size() < 8) throw invalid_argument("Passphrase must be at least 8 characters long."); + if (this->hotspotName_.length() > 31) + throw invalid_argument("Hotspot name is too long."); + if (this->hotspotName_.length() < 1) + throw invalid_argument("Hotspot name is too short."); + if (this->password_.size() != 0 && this->password_.size() < 8) + throw invalid_argument("Passphrase must be at least 8 characters long."); - if (!ValidateChannel(this->countryCode_,this->channel_)) + if (!ValidateChannel(this->countryCode_, this->channel_)) { throw invalid_argument("Channel is not valid."); } + if (argv.size() >= 5) + { + if (!TryStringToBool(argv[4],&this->alwaysOn_)) + { + throw std::runtime_error(SS("Invalid boolean value: " << argv[4])); + } + } + if (argv.size() >= 6) + { + this->homeNetworks_ = stringToSsidArray(argv[5]); + } + // validate that the channel number is supported for the given country code. - this->valid_ = true; +} + +bool WifiConfigSettings::ConfigurationChanged(const WifiConfigSettings&other) const +{ + return !( + this->valid_ == other.valid_ && + this->rebootRequired_ == other.rebootRequired_ && + this->enable_ == other.enable_ && + this->countryCode_ == other.countryCode_ && + this->mdnsName_ == other.mdnsName_ && + this->hasPassword_ == other.hasPassword_ && + this->password_ == other.password_ && + this->channel_ == other.channel_ && + this->homeNetworks_ == other.homeNetworks_ && + this->alwaysOn_ == other.alwaysOn_ + ); + +} +bool WifiConfigSettings::operator==(const WifiConfigSettings&other) const +{ + return !ConfigurationChanged(other) && this->wifiWarningGiven_ == other.wifiWarningGiven_; +} + + +bool WifiConfigSettings::WantsHotspot(bool ethernetConnected, const std::vector &availableNetworks) +{ + if ((!this->valid_) || (!this->enable_)) + return false; + if (ethernetConnected) + { + return false; + } + if (!homeNetworks_.empty()) + { + for (auto &network: availableNetworks) + { + for (auto&homeNetwork: homeNetworks_) + { + if (network == homeNetwork) + { + return false; + } + } + } + } + if (alwaysOn_) + { + return true; + } + return availableNetworks.size() == 0; +} + - } diff --git a/PiPedalCommon/src/include/DBusDispatcher.hpp b/PiPedalCommon/src/include/DBusDispatcher.hpp new file mode 100644 index 0000000..af4288c --- /dev/null +++ b/PiPedalCommon/src/include/DBusDispatcher.hpp @@ -0,0 +1,84 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include + +namespace sdbus { + class IConnection; +}; + + +class DBusDispatcher { + DBusDispatcher(const DBusDispatcher&) = delete; + DBusDispatcher(DBusDispatcher&&) = delete; + DBusDispatcher & operator=(const DBusDispatcher&) = delete; + DBusDispatcher & operator=(const DBusDispatcher&&) = delete; +public: + DBusDispatcher(bool systemBus = true); + ~DBusDispatcher(); + + void Run(); + // Stop everything, shutting down gracefully. + void Stop(); + void Wait(); + bool IsFinished(); + + // Stop, but don't wait. Suitable for use in a signal handler. + // The callback is executed by the dispatcher thread, and can therefore call non-signal-safe functions. + void SignalStop(std::function &&callback); + + bool IsStopping(); + + using PostHandle = uint64_t; + using PostCallback = std::function; + PostHandle Post(PostCallback&&fn); + + using clock = std::chrono::steady_clock; + + template + DBusDispatcher::PostHandle PostDelayed(const std::chrono::duration&delay,PostCallback&&fn) + { + return PostDelayed( + std::chrono::duration_cast(delay), + std::move(fn)); + } + + DBusDispatcher::PostHandle PostDelayed(const clock::duration&delay,PostCallback&&fn); + + bool CancelPost(DBusDispatcher::PostHandle handle); + + sdbus::IConnection&Connection() { return *busConnection;} + +private: + std::atomic isFinished; + std::atomic signalStopRequested; + bool signalStopExecuted = false; + std::function signalStopCallback; + uint64_t NextHandle() { + return nextHandle.fetch_add(1); + } + int GetEventTimeoutMs(); + std::atomic nextHandle; + int eventFd = -1; + bool closed = false; + void ThreadProc(); + void WakeThread(); + std::unique_ptr busConnection; + + struct CallbackEntry { + PostCallback callback; + clock::time_point time; + PostHandle handle; + }; + + std::atomic stopping; + + std::vector postedEvents; + bool threadJoined; + std::thread serviceThread; + std::mutex postMutex; +}; \ No newline at end of file diff --git a/PiPedalCommon/src/include/DBusEvent.hpp b/PiPedalCommon/src/include/DBusEvent.hpp new file mode 100644 index 0000000..35927eb --- /dev/null +++ b/PiPedalCommon/src/include/DBusEvent.hpp @@ -0,0 +1,62 @@ +#pragma once +#include +#include +#include +#include +#include +#include + +namespace impl { + extern std::atomic NextHandle; +} + +using DBusEventHandle=uint64_t; +constexpr DBusEventHandle INVALID_DBUS_EVENT_HANDLE = 0; + + +template +class DBusEvent { +public: + using Callback=std::function; + + + DBusEventHandle add(Callback&&cb) + { + auto handle = impl::NextHandle.fetch_add(1,std::memory_order::acq_rel); + { + std::lock_guard lock{m}; + callbacks[handle] = std::make_shared(std::move(cb)); + } + return handle; + } + void remove(DBusEventHandle h) + { + std::lock_guard lock{m}; + callbacks.erase(h); + } + + void fire(ARG_TYPES...args) + { + std::vector> cbs; + cbs.reserve(callbacks.size()); + { + std::lock_guard lock{m}; + for (const auto &cb: callbacks) + { + cbs.push_back(cb.second); + } + } + for (auto&cb: cbs) + { + (*cb)(args...); + } + { + // destruct shared_ptrs with mutex held. + std::lock_guard lock{m}; + cbs.clear(); + } + } +private: + std::map> callbacks; + std::mutex m; +}; diff --git a/PiPedalCommon/src/include/DBusLog.hpp b/PiPedalCommon/src/include/DBusLog.hpp new file mode 100644 index 0000000..814b29c --- /dev/null +++ b/PiPedalCommon/src/include/DBusLog.hpp @@ -0,0 +1,99 @@ +#pragma once +#include +#include +#include +#include +#include + +enum class DBusLogLevel { + Trace, + Debug, + Info, + Warning, + Error, + None +}; + +namespace impl { + extern std::mutex logMutex; + extern DBusLogLevel dbusLogLevel; +} +extern void SetDBusLogLevel(DBusLogLevel level); +inline DBusLogLevel GetDBusLogLevel() { + return impl::dbusLogLevel; +} +inline bool IsDBusLoggingEnabled(DBusLogLevel level) { + return level >= impl::dbusLogLevel; +} + +class IDBusLogger { +public: + IDBusLogger() {} + virtual ~IDBusLogger() { } + virtual void LogError(const std::string&message) = 0; + virtual void LogWarning(const std::string&message) = 0; + virtual void LogInfo(const std::string&message) = 0; + virtual void LogDebug(const std::string&message) = 0; + virtual void LogTrace(const std::string&message) = 0; + +}; + +void SetDBusConsoleLogger(); +void AddDBusConsoleLogger(); +void SetDBusSystemdLogger(); +void SetDBusFileLogger(const std::filesystem::path &path); +void SetDBusLogger(std::unique_ptr&&logger); + +extern void LogError(const std::string&path,const char*method,const std::string&message); +extern void LogInfo(const std::string&path,const char*method,const std::string&message); +extern void LogDebug(const std::string&path,const char*method,const std::string&message); +extern void LogWarning(const std::string&path,const char*method,const std::string&message); +extern void LogTrace(const std::string&path,const char*method,const std::string&message); + +extern void LogTrace(const std::string&path,const char*method,const std::function &text); +extern void LogDebug(const std::string&path,const char*method,const std::function &text); +extern void LogInfo(const std::string&path,const char*method,const std::function &text); +extern void LogWarning(const std::string&path,const char*method,const std::function &text); +extern void LogError(const std::string&path,const char*method,const std::function &text); + + + +class DBusLog { +protected: + DBusLog(const std::string &path) + :path(path) + { + } +protected: + void LogError(const char*tag,const std::string&message) + { + ::LogError(path,tag,message); + } + void LogWarning(const char *tag, const std::string&message) + { + ::LogWarning(path,tag,message); + } + void LogDebug(const char*tag, const std::string &text) + { + ::LogDebug(path,tag,text); + } + void LogDebug(const char*tag, const std::function &text) + { + if (impl::dbusLogLevel > DBusLogLevel::Debug) return; + ::LogDebug(path,tag,text()); + } + void LogTrace(const char*tag, const std::string &text) + { + ::LogTrace(path,tag,text); + } + void LogTrace(const char*tag, const std::function &text) + { + if (impl::dbusLogLevel > DBusLogLevel::Trace) return; + ::LogTrace(path,tag,text()); + } +private: + std::string path; +}; + + + diff --git a/PiPedalCommon/src/include/DBusVariantHelper.hpp b/PiPedalCommon/src/include/DBusVariantHelper.hpp new file mode 100644 index 0000000..1e71671 --- /dev/null +++ b/PiPedalCommon/src/include/DBusVariantHelper.hpp @@ -0,0 +1,11 @@ +#pragma once +#include +#include +#include + +namespace sdbus { + class Variant; +} + +extern std::ostream&operator<<(std::ostream&s, const sdbus::Variant&v); +extern std::ostream&operator<<(std::ostream&s,const std::map& properties); diff --git a/PiPedalCommon/src/include/NetworkManagerInterfaces.hpp b/PiPedalCommon/src/include/NetworkManagerInterfaces.hpp new file mode 100644 index 0000000..084ebc6 --- /dev/null +++ b/PiPedalCommon/src/include/NetworkManagerInterfaces.hpp @@ -0,0 +1,348 @@ +#pragma once + +#include "DBusEvent.hpp" +#include "DBusLog.hpp" +#include "DBusDispatcher.hpp" + +#include "dbus/org.freedesktop.NetworkManager.Device.WifiP2P.hpp" +#include "dbus/org.freedesktop.NetworkManager.Device.hpp" +#include "dbus/org.freedesktop.NetworkManager.Device.Wireless.hpp" +#include "dbus/org.freedesktop.NetworkManager.Settings.Connection.hpp" +#include "dbus/org.freedesktop.NetworkManager.WifiP2PPeer.hpp" +#include "dbus/org.freedesktop.NetworkManager.AccessPoint.hpp" +#include "dbus/org.freedesktop.NetworkManager.Connection.Active.hpp" + +#include "dbus/org.freedesktop.NetworkManager.hpp" + +#include "libnm/nm-dbus-interface.h" + +namespace dbus::networkmanager { +namespace nm_impl +{ + extern std::string NetworkManagerStateToString(uint32_t state); + std::string NetworkManagerDeviceStateToString(uint32_t state); +}; + +class NetworkManager : public sdbus::ProxyInterfaces +{ +public: + using ptr = std::unique_ptr; + NetworkManager(DBusDispatcher &dispatcher) + : sdbus::ProxyInterfaces( + dispatcher.Connection(), + INTERFACE_NAME, + "/org/freedesktop/NetworkManager") + { + registerProxy(); + } + virtual ~NetworkManager() + { + unregisterProxy(); + } + + static ptr Create(DBusDispatcher &dispatcher) { return std::make_unique(dispatcher); } + DBusEvent<> OnCheckPermissions; + DBusEvent OnStateChanged; + DBusEvent OnDeviceAdded; + DBusEvent OnDeviceRemoved; + +private: + void EventTrace(const char *method, const std::string &message) + { + LogTrace(getObjectPath(), method, message); + } + virtual void onStateChanged(const uint32_t &state) + { + EventTrace("onStateChanged", nm_impl::NetworkManagerStateToString(state)); + OnStateChanged.fire(state); + } + + virtual void onCheckPermissions() { OnCheckPermissions.fire(); } + + virtual void onDeviceAdded(const sdbus::ObjectPath &device_path) + { + EventTrace("onDeviceAdded", device_path.c_str()); + OnDeviceAdded.fire(device_path); + } + virtual void onDeviceRemoved(const sdbus::ObjectPath &device_path) + { + EventTrace("onDeviceRemoved", device_path.c_str()); + + OnDeviceRemoved.fire(device_path); + } +}; + +class Device : public sdbus::ProxyInterfaces +{ +public: + using ptr = std::unique_ptr; + Device(DBusDispatcher &dispatcher, const sdbus::ObjectPath &path) + : sdbus::ProxyInterfaces( + dispatcher.Connection(), + "org.freedesktop.NetworkManager", + path) + { + registerProxy(); + } + virtual ~Device() + { + unregisterProxy(); + } + + static ptr Create(DBusDispatcher &dispatcher, const sdbus::ObjectPath &path) + { + return std::make_unique(dispatcher, path); + } + DBusEvent OnStateChanged; + +private: + const uint32_t NM_DEVICE_TYPE_WIFI_P2P = 30; // from lbnm-dev package. +public: + bool IsP2pDevice() + { + return this->DeviceType() == NM_DEVICE_TYPE_WIFI_P2P; + } + +private: + void EventTrace(const char *method, const std::string &message) + { + LogTrace(getObjectPath(), method, message); + } + + virtual void onStateChanged(const uint32_t &new_state, const uint32_t &old_state, const uint32_t &reason) + { + EventTrace("onStateChanged", nm_impl::NetworkManagerDeviceStateToString(new_state)); + + OnStateChanged.fire(new_state, old_state, reason); + } +}; +class WifiP2P : public sdbus::ProxyInterfaces< + org::freedesktop::NetworkManager::Device::WifiP2P_proxy> +{ +public: + using ptr = std::unique_ptr; + using base = sdbus::ProxyInterfaces< + org::freedesktop::NetworkManager::Device::WifiP2P_proxy>; + + WifiP2P(DBusDispatcher &dispatcher, const std::string &objectPath) + : base(dispatcher.Connection(), "org.freedesktop.NetworkManager", objectPath) + { + registerProxy(); + } + virtual ~WifiP2P() + { + unregisterProxy(); + } + static ptr Create(DBusDispatcher &dispatcher, const std::string &objectPath) + { + return std::make_unique(dispatcher, objectPath); + } + DBusEvent OnPeerAdded; + DBusEvent OnPeerRemoved; + DBusEvent OnStateChanged; + +private: + void EventTrace(const char *method, const std::string &message) + { + LogTrace(getObjectPath(), method, message); + } + virtual void onStateChanged(const uint32_t &new_state, const uint32_t &old_state, const uint32_t &reason) + { + EventTrace("onStateChanged", nm_impl::NetworkManagerStateToString(new_state)); + + OnStateChanged.fire(new_state); + } + + virtual void onPeerAdded(const sdbus::ObjectPath &peer) + { + EventTrace("onPeerAdded", peer); + OnPeerAdded.fire(peer); + } + virtual void onPeerRemoved(const sdbus::ObjectPath &peer) + { + EventTrace("onPeerRemoved", peer); + OnPeerRemoved.fire(peer); + } +}; + +class Connection : public sdbus::ProxyInterfaces +{ +public: + using self = Connection; + using ptr = std::unique_ptr; + + Connection(DBusDispatcher &dispatcher, const sdbus::ObjectPath &objectPath) + : sdbus::ProxyInterfaces( + dispatcher.Connection(), "org.freedesktop.NetworkManager", objectPath) + { + registerProxy(); + } + + static ptr Create(DBusDispatcher &dispatcher, const sdbus::ObjectPath &objectPath) + { + return std::make_unique(dispatcher, objectPath); + } + + virtual ~Connection() + { + unregisterProxy(); + } + DBusEvent<> OnUpdated; + DBusEvent<> OnRemoved; + +private: + virtual void onUpdated() override + { + OnUpdated.fire(); + } + virtual void onRemoved() override + { + OnRemoved.fire(); + } +}; +class WifiP2PPeer : public sdbus::ProxyInterfaces +{ +public: + using self = WifiP2PPeer; + using ptr = std::unique_ptr; + + WifiP2PPeer(DBusDispatcher &dispatcher, const sdbus::ObjectPath &objectPath) + : sdbus::ProxyInterfaces( + dispatcher.Connection(), "org.freedesktop.NetworkManager", objectPath) + { + registerProxy(); + } + virtual ~WifiP2PPeer() + { + unregisterProxy(); + } + static ptr Create(DBusDispatcher &dispatcher, const sdbus::ObjectPath &objectPath) + { + return std::make_unique(dispatcher, objectPath); + } + DBusEvent OnPeerAdded; + DBusEvent OnPeerRemoved; + +private: + virtual void onPeerAdded(const sdbus::ObjectPath &peer) + { + OnPeerAdded.fire(peer); + } + virtual void onPeerRemoved(const sdbus::ObjectPath &peer) + { + OnPeerRemoved.fire(peer); + } +}; + +class DeviceWireless : public sdbus::ProxyInterfaces +{ +public: + using self = DeviceWireless; + using ptr = std::unique_ptr; + using proxy_t = org::freedesktop::NetworkManager::Device::Wireless_proxy; + DeviceWireless(DBusDispatcher &dispatcher, const sdbus::ObjectPath &path) + : sdbus::ProxyInterfaces( + dispatcher.Connection(), + "org.freedesktop.NetworkManager", + path) + { + registerProxy(); + } + virtual ~DeviceWireless() + { + unregisterProxy(); + } + + static ptr Create(DBusDispatcher &dispatcher, const sdbus::ObjectPath &path) + { + return std::make_unique(dispatcher, path); + } + DBusEvent OnAccessPointAdded; + DBusEvent OnAccessPointRemoved; +protected: + void EventTrace(const char *method, const std::string &message) + { + LogTrace(getObjectPath(), method, message); + } + + virtual void onAccessPointAdded(const sdbus::ObjectPath& access_point) override { + EventTrace("onAccessPointAdded", access_point.c_str()); + OnAccessPointAdded.fire(access_point); + } + virtual void onAccessPointRemoved(const sdbus::ObjectPath& access_point) { + EventTrace("onAccessPointRemoved",access_point.c_str()); + + OnAccessPointRemoved.fire(access_point); + + } + +}; + +class AccessPoint : public sdbus::ProxyInterfaces +{ +public: + using self = AccessPoint; + using ptr = std::unique_ptr; + using proxy_t = org::freedesktop::NetworkManager::AccessPoint_proxy; + + AccessPoint(DBusDispatcher &dispatcher, const sdbus::ObjectPath &path) + : sdbus::ProxyInterfaces( + dispatcher.Connection(), + "org.freedesktop.NetworkManager", + path) + { + registerProxy(); + } + virtual ~AccessPoint() + { + unregisterProxy(); + } + + static ptr Create(DBusDispatcher &dispatcher, const sdbus::ObjectPath &path) + { + return std::make_unique(dispatcher, path); + } +protected: + void EventTrace(const char *method, const std::string &message) + { + LogTrace(getObjectPath(), method, message); + } +}; +class ActiveConnection : public sdbus::ProxyInterfaces +{ +public: + using self = ActiveConnection; + using ptr = std::unique_ptr; + using proxy_t = org::freedesktop::NetworkManager::Connection::Active_proxy; + + ActiveConnection(DBusDispatcher &dispatcher, const sdbus::ObjectPath &path) + : sdbus::ProxyInterfaces( + dispatcher.Connection(), + "org.freedesktop.NetworkManager", + path) + { + registerProxy(); + } + virtual ~ActiveConnection() + { + unregisterProxy(); + } + + static ptr Create(DBusDispatcher &dispatcher, const sdbus::ObjectPath &path) + { + return std::make_unique(dispatcher, path); + } + DBusEvent OnStateChanged; +protected: + void EventTrace(const char *method, const std::string &message) + { + LogTrace(getObjectPath(), method, message); + } + virtual void onStateChanged(const uint32_t& state, const uint32_t& reason) { + OnStateChanged.fire(state,reason); + } + +}; + + +} // namespace diff --git a/PiPedalCommon/src/include/WifiConfigSettings.hpp b/PiPedalCommon/src/include/WifiConfigSettings.hpp index 6c1315d..7a059dd 100644 --- a/PiPedalCommon/src/include/WifiConfigSettings.hpp +++ b/PiPedalCommon/src/include/WifiConfigSettings.hpp @@ -21,6 +21,23 @@ #include "json.hpp" + +#ifndef NEW_WIFI_CONFIG +// 0-> Old hostapd-based hotspot. Now dead code. Works with buster only. +// 1-> NetworkManager-based hotspot (supported on bookworm or later). +#define NEW_WIFI_CONFIG 1 +#endif + +// Wifi P2P broken completely on Debian 12 updates on raspberry pi. +// +// Setting it to enable uses current-orphaned dead code as of PiPedal v1.2.49 +// +// 0: disable. +// 1: enable. +#ifndef ENABLE_WIFI_P2P +#define ENABLE_WIFI_P2P 0 +#endif + namespace pipedal { @@ -30,6 +47,12 @@ namespace pipedal { class WifiConfigSettings { public: + using ssid_t = std::vector; + WifiConfigSettings(); + + void Load(); + void Save(); + bool valid_ = false; bool wifiWarningGiven_ = false; bool rebootRequired_ = false; @@ -37,13 +60,20 @@ namespace pipedal { std::string countryCode_ = "US"; // iso 3661 std::string hotspotName_ = "pipedal"; std::string mdnsName_ = "pipedal"; + std::vector homeNetworks_; bool hasPassword_ = false; std::string password_; - std::string channel_ = "g6"; + std::string channel_ = ""; + bool alwaysOn_ = false; void ParseArguments(const std::vector &arguments); static bool ValidateCountryCode(const std::string&value); static bool ValidateChannel(const std::string&countryCode,const std::string&value); + + bool operator==(const WifiConfigSettings&other) const; + bool ConfigurationChanged(const WifiConfigSettings&other) const; + bool WantsHotspot(bool ethernetConnected,const std::vector &availableNetworks); + public: DECLARE_JSON_MAP(WifiConfigSettings); }; diff --git a/PiPedalCommon/src/include/dbus/org.freedesktop.NetworkManager.AccessPoint.hpp b/PiPedalCommon/src/include/dbus/org.freedesktop.NetworkManager.AccessPoint.hpp new file mode 100644 index 0000000..9cd48ad --- /dev/null +++ b/PiPedalCommon/src/include/dbus/org.freedesktop.NetworkManager.AccessPoint.hpp @@ -0,0 +1,87 @@ + +/* + * This file was automatically generated by sdbus-c++-xml2cpp; DO NOT EDIT! + */ + +#ifndef __sdbuscpp__dbus_hpp_org_freedesktop_NetworkManager_AccessPoint_hpp__proxy__H__ +#define __sdbuscpp__dbus_hpp_org_freedesktop_NetworkManager_AccessPoint_hpp__proxy__H__ + +#include +#include +#include + +namespace org { +namespace freedesktop { +namespace NetworkManager { + +class AccessPoint_proxy +{ +public: + static constexpr const char* INTERFACE_NAME = "org.freedesktop.NetworkManager.AccessPoint"; + +protected: + AccessPoint_proxy(sdbus::IProxy& proxy) + : proxy_(proxy) + { + } + + ~AccessPoint_proxy() = default; + +public: + uint32_t Flags() + { + return proxy_.getProperty("Flags").onInterface(INTERFACE_NAME); + } + + uint32_t WpaFlags() + { + return proxy_.getProperty("WpaFlags").onInterface(INTERFACE_NAME); + } + + uint32_t RsnFlags() + { + return proxy_.getProperty("RsnFlags").onInterface(INTERFACE_NAME); + } + + std::vector Ssid() + { + return proxy_.getProperty("Ssid").onInterface(INTERFACE_NAME); + } + + uint32_t Frequency() + { + return proxy_.getProperty("Frequency").onInterface(INTERFACE_NAME); + } + + std::string HwAddress() + { + return proxy_.getProperty("HwAddress").onInterface(INTERFACE_NAME); + } + + uint32_t Mode() + { + return proxy_.getProperty("Mode").onInterface(INTERFACE_NAME); + } + + uint32_t MaxBitrate() + { + return proxy_.getProperty("MaxBitrate").onInterface(INTERFACE_NAME); + } + + uint8_t Strength() + { + return proxy_.getProperty("Strength").onInterface(INTERFACE_NAME); + } + + int32_t LastSeen() + { + return proxy_.getProperty("LastSeen").onInterface(INTERFACE_NAME); + } + +private: + sdbus::IProxy& proxy_; +}; + +}}} // namespaces + +#endif diff --git a/PiPedalCommon/src/include/dbus/org.freedesktop.NetworkManager.Connection.Active.hpp b/PiPedalCommon/src/include/dbus/org.freedesktop.NetworkManager.Connection.Active.hpp new file mode 100644 index 0000000..09b57d2 --- /dev/null +++ b/PiPedalCommon/src/include/dbus/org.freedesktop.NetworkManager.Connection.Active.hpp @@ -0,0 +1,126 @@ + +/* + * This file was automatically generated by sdbus-c++-xml2cpp; DO NOT EDIT! + */ + +#ifndef __sdbuscpp__dbus_hpp_org_freedesktop_NetworkManager_Connection_Active_hpp__proxy__H__ +#define __sdbuscpp__dbus_hpp_org_freedesktop_NetworkManager_Connection_Active_hpp__proxy__H__ + +#include +#include +#include + +namespace org { +namespace freedesktop { +namespace NetworkManager { +namespace Connection { + +class Active_proxy +{ +public: + static constexpr const char* INTERFACE_NAME = "org.freedesktop.NetworkManager.Connection.Active"; + +protected: + Active_proxy(sdbus::IProxy& proxy) + : proxy_(proxy) + { + proxy_.uponSignal("StateChanged").onInterface(INTERFACE_NAME).call([this](const uint32_t& state, const uint32_t& reason){ this->onStateChanged(state, reason); }); + } + + ~Active_proxy() = default; + + virtual void onStateChanged(const uint32_t& state, const uint32_t& reason) = 0; + +public: + sdbus::ObjectPath Connection() + { + return proxy_.getProperty("Connection").onInterface(INTERFACE_NAME); + } + + sdbus::ObjectPath SpecificObject() + { + return proxy_.getProperty("SpecificObject").onInterface(INTERFACE_NAME); + } + + std::string Id() + { + return proxy_.getProperty("Id").onInterface(INTERFACE_NAME); + } + + std::string Uuid() + { + return proxy_.getProperty("Uuid").onInterface(INTERFACE_NAME); + } + + std::string Type() + { + return proxy_.getProperty("Type").onInterface(INTERFACE_NAME); + } + + std::vector Devices() + { + return proxy_.getProperty("Devices").onInterface(INTERFACE_NAME); + } + + uint32_t State() + { + return proxy_.getProperty("State").onInterface(INTERFACE_NAME); + } + + uint32_t StateFlags() + { + return proxy_.getProperty("StateFlags").onInterface(INTERFACE_NAME); + } + + bool Default() + { + return proxy_.getProperty("Default").onInterface(INTERFACE_NAME); + } + + sdbus::ObjectPath Ip4Config() + { + return proxy_.getProperty("Ip4Config").onInterface(INTERFACE_NAME); + } + + sdbus::ObjectPath Dhcp4Config() + { + return proxy_.getProperty("Dhcp4Config").onInterface(INTERFACE_NAME); + } + + bool Default6() + { + return proxy_.getProperty("Default6").onInterface(INTERFACE_NAME); + } + + sdbus::ObjectPath Ip6Config() + { + return proxy_.getProperty("Ip6Config").onInterface(INTERFACE_NAME); + } + + sdbus::ObjectPath Dhcp6Config() + { + return proxy_.getProperty("Dhcp6Config").onInterface(INTERFACE_NAME); + } + + bool Vpn() + { + return proxy_.getProperty("Vpn").onInterface(INTERFACE_NAME); + } + + sdbus::ObjectPath Controller() + { + return proxy_.getProperty("Controller").onInterface(INTERFACE_NAME); + } + + sdbus::ObjectPath Master() + { + return proxy_.getProperty("Master").onInterface(INTERFACE_NAME); + } + +private: + sdbus::IProxy& proxy_; +}; + +}}}} // namespaces + +#endif diff --git a/PiPedalCommon/src/include/dbus/org.freedesktop.NetworkManager.DHCP4Config.hpp b/PiPedalCommon/src/include/dbus/org.freedesktop.NetworkManager.DHCP4Config.hpp new file mode 100644 index 0000000..44baae0 --- /dev/null +++ b/PiPedalCommon/src/include/dbus/org.freedesktop.NetworkManager.DHCP4Config.hpp @@ -0,0 +1,42 @@ + +/* + * This file was automatically generated by sdbus-c++-xml2cpp; DO NOT EDIT! + */ + +#ifndef __sdbuscpp__dbus_hpp_org_freedesktop_NetworkManager_DHCP4Config_hpp__proxy__H__ +#define __sdbuscpp__dbus_hpp_org_freedesktop_NetworkManager_DHCP4Config_hpp__proxy__H__ + +#include +#include +#include + +namespace org { +namespace freedesktop { +namespace NetworkManager { + +class DHCP4Config_proxy +{ +public: + static constexpr const char* INTERFACE_NAME = "org.freedesktop.NetworkManager.DHCP4Config"; + +protected: + DHCP4Config_proxy(sdbus::IProxy& proxy) + : proxy_(proxy) + { + } + + ~DHCP4Config_proxy() = default; + +public: + std::map Options() + { + return proxy_.getProperty("Options").onInterface(INTERFACE_NAME); + } + +private: + sdbus::IProxy& proxy_; +}; + +}}} // namespaces + +#endif diff --git a/PiPedalCommon/src/include/dbus/org.freedesktop.NetworkManager.DHCP6Config.hpp b/PiPedalCommon/src/include/dbus/org.freedesktop.NetworkManager.DHCP6Config.hpp new file mode 100644 index 0000000..c13839c --- /dev/null +++ b/PiPedalCommon/src/include/dbus/org.freedesktop.NetworkManager.DHCP6Config.hpp @@ -0,0 +1,42 @@ + +/* + * This file was automatically generated by sdbus-c++-xml2cpp; DO NOT EDIT! + */ + +#ifndef __sdbuscpp__dbus_hpp_org_freedesktop_NetworkManager_DHCP6Config_hpp__proxy__H__ +#define __sdbuscpp__dbus_hpp_org_freedesktop_NetworkManager_DHCP6Config_hpp__proxy__H__ + +#include +#include +#include + +namespace org { +namespace freedesktop { +namespace NetworkManager { + +class DHCP6Config_proxy +{ +public: + static constexpr const char* INTERFACE_NAME = "org.freedesktop.NetworkManager.DHCP6Config"; + +protected: + DHCP6Config_proxy(sdbus::IProxy& proxy) + : proxy_(proxy) + { + } + + ~DHCP6Config_proxy() = default; + +public: + std::map Options() + { + return proxy_.getProperty("Options").onInterface(INTERFACE_NAME); + } + +private: + sdbus::IProxy& proxy_; +}; + +}}} // namespaces + +#endif diff --git a/PiPedalCommon/src/include/dbus/org.freedesktop.NetworkManager.Device.Statistics.hpp b/PiPedalCommon/src/include/dbus/org.freedesktop.NetworkManager.Device.Statistics.hpp new file mode 100644 index 0000000..6eb0830 --- /dev/null +++ b/PiPedalCommon/src/include/dbus/org.freedesktop.NetworkManager.Device.Statistics.hpp @@ -0,0 +1,58 @@ + +/* + * This file was automatically generated by sdbus-c++-xml2cpp; DO NOT EDIT! + */ + +#ifndef __sdbuscpp__dbus_hpp_org_freedesktop_NetworkManager_Device_Statistics_hpp__proxy__H__ +#define __sdbuscpp__dbus_hpp_org_freedesktop_NetworkManager_Device_Statistics_hpp__proxy__H__ + +#include +#include +#include + +namespace org { +namespace freedesktop { +namespace NetworkManager { +namespace Device { + +class Statistics_proxy +{ +public: + static constexpr const char* INTERFACE_NAME = "org.freedesktop.NetworkManager.Device.Statistics"; + +protected: + Statistics_proxy(sdbus::IProxy& proxy) + : proxy_(proxy) + { + } + + ~Statistics_proxy() = default; + +public: + uint32_t RefreshRateMs() + { + return proxy_.getProperty("RefreshRateMs").onInterface(INTERFACE_NAME); + } + + void RefreshRateMs(const uint32_t& value) + { + proxy_.setProperty("RefreshRateMs").onInterface(INTERFACE_NAME).toValue(value); + } + + uint64_t TxBytes() + { + return proxy_.getProperty("TxBytes").onInterface(INTERFACE_NAME); + } + + uint64_t RxBytes() + { + return proxy_.getProperty("RxBytes").onInterface(INTERFACE_NAME); + } + +private: + sdbus::IProxy& proxy_; +}; + +}}}} // namespaces + +#endif diff --git a/PiPedalCommon/src/include/dbus/org.freedesktop.NetworkManager.Device.WifiP2P.hpp b/PiPedalCommon/src/include/dbus/org.freedesktop.NetworkManager.Device.WifiP2P.hpp new file mode 100644 index 0000000..8ff2ec9 --- /dev/null +++ b/PiPedalCommon/src/include/dbus/org.freedesktop.NetworkManager.Device.WifiP2P.hpp @@ -0,0 +1,64 @@ + +/* + * This file was automatically generated by sdbus-c++-xml2cpp; DO NOT EDIT! + */ + +#ifndef __sdbuscpp__dbus_hpp_org_freedesktop_NetworkManager_Device_WifiP2P_hpp__proxy__H__ +#define __sdbuscpp__dbus_hpp_org_freedesktop_NetworkManager_Device_WifiP2P_hpp__proxy__H__ + +#include +#include +#include + +namespace org { +namespace freedesktop { +namespace NetworkManager { +namespace Device { + +class WifiP2P_proxy +{ +public: + static constexpr const char* INTERFACE_NAME = "org.freedesktop.NetworkManager.Device.WifiP2P"; + +protected: + WifiP2P_proxy(sdbus::IProxy& proxy) + : proxy_(proxy) + { + proxy_.uponSignal("PeerAdded").onInterface(INTERFACE_NAME).call([this](const sdbus::ObjectPath& peer){ this->onPeerAdded(peer); }); + proxy_.uponSignal("PeerRemoved").onInterface(INTERFACE_NAME).call([this](const sdbus::ObjectPath& peer){ this->onPeerRemoved(peer); }); + } + + ~WifiP2P_proxy() = default; + + virtual void onPeerAdded(const sdbus::ObjectPath& peer) = 0; + virtual void onPeerRemoved(const sdbus::ObjectPath& peer) = 0; + +public: + void StartFind(const std::map& options) + { + proxy_.callMethod("StartFind").onInterface(INTERFACE_NAME).withArguments(options); + } + + void StopFind() + { + proxy_.callMethod("StopFind").onInterface(INTERFACE_NAME); + } + +public: + std::string HwAddress() + { + return proxy_.getProperty("HwAddress").onInterface(INTERFACE_NAME); + } + + std::vector Peers() + { + return proxy_.getProperty("Peers").onInterface(INTERFACE_NAME); + } + +private: + sdbus::IProxy& proxy_; +}; + +}}}} // namespaces + +#endif diff --git a/PiPedalCommon/src/include/dbus/org.freedesktop.NetworkManager.Device.Wireless.hpp b/PiPedalCommon/src/include/dbus/org.freedesktop.NetworkManager.Device.Wireless.hpp new file mode 100644 index 0000000..e00de94 --- /dev/null +++ b/PiPedalCommon/src/include/dbus/org.freedesktop.NetworkManager.Device.Wireless.hpp @@ -0,0 +1,103 @@ + +/* + * This file was automatically generated by sdbus-c++-xml2cpp; DO NOT EDIT! + */ + +#ifndef __sdbuscpp__dbus_hpp_org_freedesktop_NetworkManager_Device_Wireless_hpp__proxy__H__ +#define __sdbuscpp__dbus_hpp_org_freedesktop_NetworkManager_Device_Wireless_hpp__proxy__H__ + +#include +#include +#include + +namespace org { +namespace freedesktop { +namespace NetworkManager { +namespace Device { + +class Wireless_proxy +{ +public: + static constexpr const char* INTERFACE_NAME = "org.freedesktop.NetworkManager.Device.Wireless"; + +protected: + Wireless_proxy(sdbus::IProxy& proxy) + : proxy_(proxy) + { + proxy_.uponSignal("AccessPointAdded").onInterface(INTERFACE_NAME).call([this](const sdbus::ObjectPath& access_point){ this->onAccessPointAdded(access_point); }); + proxy_.uponSignal("AccessPointRemoved").onInterface(INTERFACE_NAME).call([this](const sdbus::ObjectPath& access_point){ this->onAccessPointRemoved(access_point); }); + } + + ~Wireless_proxy() = default; + + virtual void onAccessPointAdded(const sdbus::ObjectPath& access_point) = 0; + virtual void onAccessPointRemoved(const sdbus::ObjectPath& access_point) = 0; + +public: + std::vector GetAccessPoints() + { + std::vector result; + proxy_.callMethod("GetAccessPoints").onInterface(INTERFACE_NAME).storeResultsTo(result); + return result; + } + + std::vector GetAllAccessPoints() + { + std::vector result; + proxy_.callMethod("GetAllAccessPoints").onInterface(INTERFACE_NAME).storeResultsTo(result); + return result; + } + + void RequestScan(const std::map& options) + { + proxy_.callMethod("RequestScan").onInterface(INTERFACE_NAME).withArguments(options); + } + +public: + std::string HwAddress() + { + return proxy_.getProperty("HwAddress").onInterface(INTERFACE_NAME); + } + + std::string PermHwAddress() + { + return proxy_.getProperty("PermHwAddress").onInterface(INTERFACE_NAME); + } + + uint32_t Mode() + { + return proxy_.getProperty("Mode").onInterface(INTERFACE_NAME); + } + + uint32_t Bitrate() + { + return proxy_.getProperty("Bitrate").onInterface(INTERFACE_NAME); + } + + std::vector AccessPoints() + { + return proxy_.getProperty("AccessPoints").onInterface(INTERFACE_NAME); + } + + sdbus::ObjectPath ActiveAccessPoint() + { + return proxy_.getProperty("ActiveAccessPoint").onInterface(INTERFACE_NAME); + } + + uint32_t WirelessCapabilities() + { + return proxy_.getProperty("WirelessCapabilities").onInterface(INTERFACE_NAME); + } + + int64_t LastScan() + { + return proxy_.getProperty("LastScan").onInterface(INTERFACE_NAME); + } + +private: + sdbus::IProxy& proxy_; +}; + +}}}} // namespaces + +#endif diff --git a/PiPedalCommon/src/include/dbus/org.freedesktop.NetworkManager.Device.hpp b/PiPedalCommon/src/include/dbus/org.freedesktop.NetworkManager.Device.hpp new file mode 100644 index 0000000..bdb1c90 --- /dev/null +++ b/PiPedalCommon/src/include/dbus/org.freedesktop.NetworkManager.Device.hpp @@ -0,0 +1,233 @@ + +/* + * This file was automatically generated by sdbus-c++-xml2cpp; DO NOT EDIT! + */ + +#ifndef __sdbuscpp__dbus_hpp_org_freedesktop_NetworkManager_Device_hpp__proxy__H__ +#define __sdbuscpp__dbus_hpp_org_freedesktop_NetworkManager_Device_hpp__proxy__H__ + +#include +#include +#include + +namespace org { +namespace freedesktop { +namespace NetworkManager { + +class Device_proxy +{ +public: + static constexpr const char* INTERFACE_NAME = "org.freedesktop.NetworkManager.Device"; + +protected: + Device_proxy(sdbus::IProxy& proxy) + : proxy_(proxy) + { + proxy_.uponSignal("StateChanged").onInterface(INTERFACE_NAME).call([this](const uint32_t& new_state, const uint32_t& old_state, const uint32_t& reason){ this->onStateChanged(new_state, old_state, reason); }); + } + + ~Device_proxy() = default; + + virtual void onStateChanged(const uint32_t& new_state, const uint32_t& old_state, const uint32_t& reason) = 0; + +public: + void Reapply(const std::map>& connection, const uint64_t& version_id, const uint32_t& flags) + { + proxy_.callMethod("Reapply").onInterface(INTERFACE_NAME).withArguments(connection, version_id, flags); + } + + std::tuple>, uint64_t> GetAppliedConnection(const uint32_t& flags) + { + std::tuple>, uint64_t> result; + proxy_.callMethod("GetAppliedConnection").onInterface(INTERFACE_NAME).withArguments(flags).storeResultsTo(result); + return result; + } + + void Disconnect() + { + proxy_.callMethod("Disconnect").onInterface(INTERFACE_NAME); + } + + void Delete() + { + proxy_.callMethod("Delete").onInterface(INTERFACE_NAME); + } + +public: + std::string Udi() + { + return proxy_.getProperty("Udi").onInterface(INTERFACE_NAME); + } + + std::string Path() + { + return proxy_.getProperty("Path").onInterface(INTERFACE_NAME); + } + + std::string Interface() + { + return proxy_.getProperty("Interface").onInterface(INTERFACE_NAME); + } + + std::string IpInterface() + { + return proxy_.getProperty("IpInterface").onInterface(INTERFACE_NAME); + } + + std::string Driver() + { + return proxy_.getProperty("Driver").onInterface(INTERFACE_NAME); + } + + std::string DriverVersion() + { + return proxy_.getProperty("DriverVersion").onInterface(INTERFACE_NAME); + } + + std::string FirmwareVersion() + { + return proxy_.getProperty("FirmwareVersion").onInterface(INTERFACE_NAME); + } + + uint32_t Capabilities() + { + return proxy_.getProperty("Capabilities").onInterface(INTERFACE_NAME); + } + + uint32_t Ip4Address() + { + return proxy_.getProperty("Ip4Address").onInterface(INTERFACE_NAME); + } + + uint32_t State() + { + return proxy_.getProperty("State").onInterface(INTERFACE_NAME); + } + + sdbus::Struct StateReason() + { + return proxy_.getProperty("StateReason").onInterface(INTERFACE_NAME); + } + + sdbus::ObjectPath ActiveConnection() + { + return proxy_.getProperty("ActiveConnection").onInterface(INTERFACE_NAME); + } + + sdbus::ObjectPath Ip4Config() + { + return proxy_.getProperty("Ip4Config").onInterface(INTERFACE_NAME); + } + + sdbus::ObjectPath Dhcp4Config() + { + return proxy_.getProperty("Dhcp4Config").onInterface(INTERFACE_NAME); + } + + sdbus::ObjectPath Ip6Config() + { + return proxy_.getProperty("Ip6Config").onInterface(INTERFACE_NAME); + } + + sdbus::ObjectPath Dhcp6Config() + { + return proxy_.getProperty("Dhcp6Config").onInterface(INTERFACE_NAME); + } + + bool Managed() + { + return proxy_.getProperty("Managed").onInterface(INTERFACE_NAME); + } + + void Managed(const bool& value) + { + proxy_.setProperty("Managed").onInterface(INTERFACE_NAME).toValue(value); + } + + bool Autoconnect() + { + return proxy_.getProperty("Autoconnect").onInterface(INTERFACE_NAME); + } + + void Autoconnect(const bool& value) + { + proxy_.setProperty("Autoconnect").onInterface(INTERFACE_NAME).toValue(value); + } + + bool FirmwareMissing() + { + return proxy_.getProperty("FirmwareMissing").onInterface(INTERFACE_NAME); + } + + bool NmPluginMissing() + { + return proxy_.getProperty("NmPluginMissing").onInterface(INTERFACE_NAME); + } + + uint32_t DeviceType() + { + return proxy_.getProperty("DeviceType").onInterface(INTERFACE_NAME); + } + + std::vector AvailableConnections() + { + return proxy_.getProperty("AvailableConnections").onInterface(INTERFACE_NAME); + } + + std::string PhysicalPortId() + { + return proxy_.getProperty("PhysicalPortId").onInterface(INTERFACE_NAME); + } + + uint32_t Mtu() + { + return proxy_.getProperty("Mtu").onInterface(INTERFACE_NAME); + } + + uint32_t Metered() + { + return proxy_.getProperty("Metered").onInterface(INTERFACE_NAME); + } + + std::vector> LldpNeighbors() + { + return proxy_.getProperty("LldpNeighbors").onInterface(INTERFACE_NAME); + } + + bool Real() + { + return proxy_.getProperty("Real").onInterface(INTERFACE_NAME); + } + + uint32_t Ip4Connectivity() + { + return proxy_.getProperty("Ip4Connectivity").onInterface(INTERFACE_NAME); + } + + uint32_t Ip6Connectivity() + { + return proxy_.getProperty("Ip6Connectivity").onInterface(INTERFACE_NAME); + } + + uint32_t InterfaceFlags() + { + return proxy_.getProperty("InterfaceFlags").onInterface(INTERFACE_NAME); + } + + std::string HwAddress() + { + return proxy_.getProperty("HwAddress").onInterface(INTERFACE_NAME); + } + + std::vector Ports() + { + return proxy_.getProperty("Ports").onInterface(INTERFACE_NAME); + } + +private: + sdbus::IProxy& proxy_; +}; + +}}} // namespaces + +#endif diff --git a/PiPedalCommon/src/include/dbus/org.freedesktop.NetworkManager.DnsManager.hpp b/PiPedalCommon/src/include/dbus/org.freedesktop.NetworkManager.DnsManager.hpp new file mode 100644 index 0000000..9cba4bb --- /dev/null +++ b/PiPedalCommon/src/include/dbus/org.freedesktop.NetworkManager.DnsManager.hpp @@ -0,0 +1,52 @@ + +/* + * This file was automatically generated by sdbus-c++-xml2cpp; DO NOT EDIT! + */ + +#ifndef __sdbuscpp__dbus_hpp_org_freedesktop_NetworkManager_DnsManager_hpp__proxy__H__ +#define __sdbuscpp__dbus_hpp_org_freedesktop_NetworkManager_DnsManager_hpp__proxy__H__ + +#include +#include +#include + +namespace org { +namespace freedesktop { +namespace NetworkManager { + +class DnsManager_proxy +{ +public: + static constexpr const char* INTERFACE_NAME = "org.freedesktop.NetworkManager.DnsManager"; + +protected: + DnsManager_proxy(sdbus::IProxy& proxy) + : proxy_(proxy) + { + } + + ~DnsManager_proxy() = default; + +public: + std::string Mode() + { + return proxy_.getProperty("Mode").onInterface(INTERFACE_NAME); + } + + std::string RcManager() + { + return proxy_.getProperty("RcManager").onInterface(INTERFACE_NAME); + } + + std::vector> Configuration() + { + return proxy_.getProperty("Configuration").onInterface(INTERFACE_NAME); + } + +private: + sdbus::IProxy& proxy_; +}; + +}}} // namespaces + +#endif diff --git a/PiPedalCommon/src/include/dbus/org.freedesktop.NetworkManager.IP4Config.hpp b/PiPedalCommon/src/include/dbus/org.freedesktop.NetworkManager.IP4Config.hpp new file mode 100644 index 0000000..7fdd631 --- /dev/null +++ b/PiPedalCommon/src/include/dbus/org.freedesktop.NetworkManager.IP4Config.hpp @@ -0,0 +1,102 @@ + +/* + * This file was automatically generated by sdbus-c++-xml2cpp; DO NOT EDIT! + */ + +#ifndef __sdbuscpp__dbus_hpp_org_freedesktop_NetworkManager_IP4Config_hpp__proxy__H__ +#define __sdbuscpp__dbus_hpp_org_freedesktop_NetworkManager_IP4Config_hpp__proxy__H__ + +#include +#include +#include + +namespace org { +namespace freedesktop { +namespace NetworkManager { + +class IP4Config_proxy +{ +public: + static constexpr const char* INTERFACE_NAME = "org.freedesktop.NetworkManager.IP4Config"; + +protected: + IP4Config_proxy(sdbus::IProxy& proxy) + : proxy_(proxy) + { + } + + ~IP4Config_proxy() = default; + +public: + std::vector> Addresses() + { + return proxy_.getProperty("Addresses").onInterface(INTERFACE_NAME); + } + + std::vector> AddressData() + { + return proxy_.getProperty("AddressData").onInterface(INTERFACE_NAME); + } + + std::string Gateway() + { + return proxy_.getProperty("Gateway").onInterface(INTERFACE_NAME); + } + + std::vector> Routes() + { + return proxy_.getProperty("Routes").onInterface(INTERFACE_NAME); + } + + std::vector> RouteData() + { + return proxy_.getProperty("RouteData").onInterface(INTERFACE_NAME); + } + + std::vector Nameservers() + { + return proxy_.getProperty("Nameservers").onInterface(INTERFACE_NAME); + } + + std::vector> NameserverData() + { + return proxy_.getProperty("NameserverData").onInterface(INTERFACE_NAME); + } + + std::vector Domains() + { + return proxy_.getProperty("Domains").onInterface(INTERFACE_NAME); + } + + std::vector Searches() + { + return proxy_.getProperty("Searches").onInterface(INTERFACE_NAME); + } + + std::vector DnsOptions() + { + return proxy_.getProperty("DnsOptions").onInterface(INTERFACE_NAME); + } + + int32_t DnsPriority() + { + return proxy_.getProperty("DnsPriority").onInterface(INTERFACE_NAME); + } + + std::vector WinsServers() + { + return proxy_.getProperty("WinsServers").onInterface(INTERFACE_NAME); + } + + std::vector WinsServerData() + { + return proxy_.getProperty("WinsServerData").onInterface(INTERFACE_NAME); + } + +private: + sdbus::IProxy& proxy_; +}; + +}}} // namespaces + +#endif diff --git a/PiPedalCommon/src/include/dbus/org.freedesktop.NetworkManager.IP6Config.hpp b/PiPedalCommon/src/include/dbus/org.freedesktop.NetworkManager.IP6Config.hpp new file mode 100644 index 0000000..94935fb --- /dev/null +++ b/PiPedalCommon/src/include/dbus/org.freedesktop.NetworkManager.IP6Config.hpp @@ -0,0 +1,87 @@ + +/* + * This file was automatically generated by sdbus-c++-xml2cpp; DO NOT EDIT! + */ + +#ifndef __sdbuscpp__dbus_hpp_org_freedesktop_NetworkManager_IP6Config_hpp__proxy__H__ +#define __sdbuscpp__dbus_hpp_org_freedesktop_NetworkManager_IP6Config_hpp__proxy__H__ + +#include +#include +#include + +namespace org { +namespace freedesktop { +namespace NetworkManager { + +class IP6Config_proxy +{ +public: + static constexpr const char* INTERFACE_NAME = "org.freedesktop.NetworkManager.IP6Config"; + +protected: + IP6Config_proxy(sdbus::IProxy& proxy) + : proxy_(proxy) + { + } + + ~IP6Config_proxy() = default; + +public: + std::vector, uint32_t, std::vector>> Addresses() + { + return proxy_.getProperty("Addresses").onInterface(INTERFACE_NAME); + } + + std::vector> AddressData() + { + return proxy_.getProperty("AddressData").onInterface(INTERFACE_NAME); + } + + std::string Gateway() + { + return proxy_.getProperty("Gateway").onInterface(INTERFACE_NAME); + } + + std::vector, uint32_t, std::vector, uint32_t>> Routes() + { + return proxy_.getProperty("Routes").onInterface(INTERFACE_NAME); + } + + std::vector> RouteData() + { + return proxy_.getProperty("RouteData").onInterface(INTERFACE_NAME); + } + + std::vector> Nameservers() + { + return proxy_.getProperty("Nameservers").onInterface(INTERFACE_NAME); + } + + std::vector Domains() + { + return proxy_.getProperty("Domains").onInterface(INTERFACE_NAME); + } + + std::vector Searches() + { + return proxy_.getProperty("Searches").onInterface(INTERFACE_NAME); + } + + std::vector DnsOptions() + { + return proxy_.getProperty("DnsOptions").onInterface(INTERFACE_NAME); + } + + int32_t DnsPriority() + { + return proxy_.getProperty("DnsPriority").onInterface(INTERFACE_NAME); + } + +private: + sdbus::IProxy& proxy_; +}; + +}}} // namespaces + +#endif diff --git a/PiPedalCommon/src/include/dbus/org.freedesktop.NetworkManager.Settings.Connection.hpp b/PiPedalCommon/src/include/dbus/org.freedesktop.NetworkManager.Settings.Connection.hpp new file mode 100644 index 0000000..0beb271 --- /dev/null +++ b/PiPedalCommon/src/include/dbus/org.freedesktop.NetworkManager.Settings.Connection.hpp @@ -0,0 +1,105 @@ + +/* + * This file was automatically generated by sdbus-c++-xml2cpp; DO NOT EDIT! + */ + +#ifndef __sdbuscpp__dbus_hpp_org_freedesktop_NetworkManager_Settings_Connection_hpp__proxy__H__ +#define __sdbuscpp__dbus_hpp_org_freedesktop_NetworkManager_Settings_Connection_hpp__proxy__H__ + +#include +#include +#include + +namespace org { +namespace freedesktop { +namespace NetworkManager { +namespace Settings { + +class Connection_proxy +{ +public: + static constexpr const char* INTERFACE_NAME = "org.freedesktop.NetworkManager.Settings.Connection"; + +protected: + Connection_proxy(sdbus::IProxy& proxy) + : proxy_(proxy) + { + proxy_.uponSignal("Updated").onInterface(INTERFACE_NAME).call([this](){ this->onUpdated(); }); + proxy_.uponSignal("Removed").onInterface(INTERFACE_NAME).call([this](){ this->onRemoved(); }); + } + + ~Connection_proxy() = default; + + virtual void onUpdated() = 0; + virtual void onRemoved() = 0; + +public: + void Update(const std::map>& properties) + { + proxy_.callMethod("Update").onInterface(INTERFACE_NAME).withArguments(properties); + } + + void UpdateUnsaved(const std::map>& properties) + { + proxy_.callMethod("UpdateUnsaved").onInterface(INTERFACE_NAME).withArguments(properties); + } + + void Delete() + { + proxy_.callMethod("Delete").onInterface(INTERFACE_NAME); + } + + std::map> GetSettings() + { + std::map> result; + proxy_.callMethod("GetSettings").onInterface(INTERFACE_NAME).storeResultsTo(result); + return result; + } + + std::map> GetSecrets(const std::string& setting_name) + { + std::map> result; + proxy_.callMethod("GetSecrets").onInterface(INTERFACE_NAME).withArguments(setting_name).storeResultsTo(result); + return result; + } + + void ClearSecrets() + { + proxy_.callMethod("ClearSecrets").onInterface(INTERFACE_NAME); + } + + void Save() + { + proxy_.callMethod("Save").onInterface(INTERFACE_NAME); + } + + std::map Update2(const std::map>& settings, const uint32_t& flags, const std::map& args) + { + std::map result; + proxy_.callMethod("Update2").onInterface(INTERFACE_NAME).withArguments(settings, flags, args).storeResultsTo(result); + return result; + } + +public: + bool Unsaved() + { + return proxy_.getProperty("Unsaved").onInterface(INTERFACE_NAME); + } + + uint32_t Flags() + { + return proxy_.getProperty("Flags").onInterface(INTERFACE_NAME); + } + + std::string Filename() + { + return proxy_.getProperty("Filename").onInterface(INTERFACE_NAME); + } + +private: + sdbus::IProxy& proxy_; +}; + +}}}} // namespaces + +#endif diff --git a/PiPedalCommon/src/include/dbus/org.freedesktop.NetworkManager.Settings.hpp b/PiPedalCommon/src/include/dbus/org.freedesktop.NetworkManager.Settings.hpp new file mode 100644 index 0000000..1285c17 --- /dev/null +++ b/PiPedalCommon/src/include/dbus/org.freedesktop.NetworkManager.Settings.hpp @@ -0,0 +1,112 @@ + +/* + * This file was automatically generated by sdbus-c++-xml2cpp; DO NOT EDIT! + */ + +#ifndef __sdbuscpp__dbus_hpp_org_freedesktop_NetworkManager_Settings_hpp__proxy__H__ +#define __sdbuscpp__dbus_hpp_org_freedesktop_NetworkManager_Settings_hpp__proxy__H__ + +#include +#include +#include + +namespace org { +namespace freedesktop { +namespace NetworkManager { + +class Settings_proxy +{ +public: + static constexpr const char* INTERFACE_NAME = "org.freedesktop.NetworkManager.Settings"; + +protected: + Settings_proxy(sdbus::IProxy& proxy) + : proxy_(proxy) + { + proxy_.uponSignal("NewConnection").onInterface(INTERFACE_NAME).call([this](const sdbus::ObjectPath& connection){ this->onNewConnection(connection); }); + proxy_.uponSignal("ConnectionRemoved").onInterface(INTERFACE_NAME).call([this](const sdbus::ObjectPath& connection){ this->onConnectionRemoved(connection); }); + } + + ~Settings_proxy() = default; + + virtual void onNewConnection(const sdbus::ObjectPath& connection) = 0; + virtual void onConnectionRemoved(const sdbus::ObjectPath& connection) = 0; + +public: + std::vector ListConnections() + { + std::vector result; + proxy_.callMethod("ListConnections").onInterface(INTERFACE_NAME).storeResultsTo(result); + return result; + } + + sdbus::ObjectPath GetConnectionByUuid(const std::string& uuid) + { + sdbus::ObjectPath result; + proxy_.callMethod("GetConnectionByUuid").onInterface(INTERFACE_NAME).withArguments(uuid).storeResultsTo(result); + return result; + } + + sdbus::ObjectPath AddConnection(const std::map>& connection) + { + sdbus::ObjectPath result; + proxy_.callMethod("AddConnection").onInterface(INTERFACE_NAME).withArguments(connection).storeResultsTo(result); + return result; + } + + sdbus::ObjectPath AddConnectionUnsaved(const std::map>& connection) + { + sdbus::ObjectPath result; + proxy_.callMethod("AddConnectionUnsaved").onInterface(INTERFACE_NAME).withArguments(connection).storeResultsTo(result); + return result; + } + + std::tuple> AddConnection2(const std::map>& settings, const uint32_t& flags, const std::map& args) + { + std::tuple> result; + proxy_.callMethod("AddConnection2").onInterface(INTERFACE_NAME).withArguments(settings, flags, args).storeResultsTo(result); + return result; + } + + std::tuple> LoadConnections(const std::vector& filenames) + { + std::tuple> result; + proxy_.callMethod("LoadConnections").onInterface(INTERFACE_NAME).withArguments(filenames).storeResultsTo(result); + return result; + } + + bool ReloadConnections() + { + bool result; + proxy_.callMethod("ReloadConnections").onInterface(INTERFACE_NAME).storeResultsTo(result); + return result; + } + + void SaveHostname(const std::string& hostname) + { + proxy_.callMethod("SaveHostname").onInterface(INTERFACE_NAME).withArguments(hostname); + } + +public: + std::vector Connections() + { + return proxy_.getProperty("Connections").onInterface(INTERFACE_NAME); + } + + std::string Hostname() + { + return proxy_.getProperty("Hostname").onInterface(INTERFACE_NAME); + } + + bool CanModify() + { + return proxy_.getProperty("CanModify").onInterface(INTERFACE_NAME); + } + +private: + sdbus::IProxy& proxy_; +}; + +}}} // namespaces + +#endif diff --git a/PiPedalCommon/src/include/dbus/org.freedesktop.NetworkManager.WifiP2PPeer.hpp b/PiPedalCommon/src/include/dbus/org.freedesktop.NetworkManager.WifiP2PPeer.hpp new file mode 100644 index 0000000..80c607c --- /dev/null +++ b/PiPedalCommon/src/include/dbus/org.freedesktop.NetworkManager.WifiP2PPeer.hpp @@ -0,0 +1,87 @@ + +/* + * This file was automatically generated by sdbus-c++-xml2cpp; DO NOT EDIT! + */ + +#ifndef __sdbuscpp__dbus_hpp_org_freedesktop_NetworkManager_WifiP2PPeer_hpp__proxy__H__ +#define __sdbuscpp__dbus_hpp_org_freedesktop_NetworkManager_WifiP2PPeer_hpp__proxy__H__ + +#include +#include +#include + +namespace org { +namespace freedesktop { +namespace NetworkManager { + +class WifiP2PPeer_proxy +{ +public: + static constexpr const char* INTERFACE_NAME = "org.freedesktop.NetworkManager.WifiP2PPeer"; + +protected: + WifiP2PPeer_proxy(sdbus::IProxy& proxy) + : proxy_(proxy) + { + } + + ~WifiP2PPeer_proxy() = default; + +public: + std::string Name() + { + return proxy_.getProperty("Name").onInterface(INTERFACE_NAME); + } + + uint32_t Flags() + { + return proxy_.getProperty("Flags").onInterface(INTERFACE_NAME); + } + + std::string Manufacturer() + { + return proxy_.getProperty("Manufacturer").onInterface(INTERFACE_NAME); + } + + std::string Model() + { + return proxy_.getProperty("Model").onInterface(INTERFACE_NAME); + } + + std::string ModelNumber() + { + return proxy_.getProperty("ModelNumber").onInterface(INTERFACE_NAME); + } + + std::string Serial() + { + return proxy_.getProperty("Serial").onInterface(INTERFACE_NAME); + } + + std::vector WfdIEs() + { + return proxy_.getProperty("WfdIEs").onInterface(INTERFACE_NAME); + } + + std::string HwAddress() + { + return proxy_.getProperty("HwAddress").onInterface(INTERFACE_NAME); + } + + uint8_t Strength() + { + return proxy_.getProperty("Strength").onInterface(INTERFACE_NAME); + } + + int32_t LastSeen() + { + return proxy_.getProperty("LastSeen").onInterface(INTERFACE_NAME); + } + +private: + sdbus::IProxy& proxy_; +}; + +}}} // namespaces + +#endif diff --git a/PiPedalCommon/src/include/dbus/org.freedesktop.NetworkManager.hpp b/PiPedalCommon/src/include/dbus/org.freedesktop.NetworkManager.hpp new file mode 100644 index 0000000..a4e7746 --- /dev/null +++ b/PiPedalCommon/src/include/dbus/org.freedesktop.NetworkManager.hpp @@ -0,0 +1,320 @@ + +/* + * This file was automatically generated by sdbus-c++-xml2cpp; DO NOT EDIT! + */ + +#ifndef __sdbuscpp__dbus_hpp_org_freedesktop_NetworkManager_hpp__proxy__H__ +#define __sdbuscpp__dbus_hpp_org_freedesktop_NetworkManager_hpp__proxy__H__ + +#include +#include +#include + +namespace org { +namespace freedesktop { + +class NetworkManager_proxy +{ +public: + static constexpr const char* INTERFACE_NAME = "org.freedesktop.NetworkManager"; + +protected: + NetworkManager_proxy(sdbus::IProxy& proxy) + : proxy_(proxy) + { + proxy_.uponSignal("CheckPermissions").onInterface(INTERFACE_NAME).call([this](){ this->onCheckPermissions(); }); + proxy_.uponSignal("StateChanged").onInterface(INTERFACE_NAME).call([this](const uint32_t& state){ this->onStateChanged(state); }); + proxy_.uponSignal("DeviceAdded").onInterface(INTERFACE_NAME).call([this](const sdbus::ObjectPath& device_path){ this->onDeviceAdded(device_path); }); + proxy_.uponSignal("DeviceRemoved").onInterface(INTERFACE_NAME).call([this](const sdbus::ObjectPath& device_path){ this->onDeviceRemoved(device_path); }); + } + + ~NetworkManager_proxy() = default; + + virtual void onCheckPermissions() = 0; + virtual void onStateChanged(const uint32_t& state) = 0; + virtual void onDeviceAdded(const sdbus::ObjectPath& device_path) = 0; + virtual void onDeviceRemoved(const sdbus::ObjectPath& device_path) = 0; + +public: + void Reload(const uint32_t& flags) + { + proxy_.callMethod("Reload").onInterface(INTERFACE_NAME).withArguments(flags); + } + + std::vector GetDevices() + { + std::vector result; + proxy_.callMethod("GetDevices").onInterface(INTERFACE_NAME).storeResultsTo(result); + return result; + } + + std::vector GetAllDevices() + { + std::vector result; + proxy_.callMethod("GetAllDevices").onInterface(INTERFACE_NAME).storeResultsTo(result); + return result; + } + + sdbus::ObjectPath GetDeviceByIpIface(const std::string& iface) + { + sdbus::ObjectPath result; + proxy_.callMethod("GetDeviceByIpIface").onInterface(INTERFACE_NAME).withArguments(iface).storeResultsTo(result); + return result; + } + + sdbus::ObjectPath ActivateConnection(const sdbus::ObjectPath& connection, const sdbus::ObjectPath& device, const sdbus::ObjectPath& specific_object) + { + sdbus::ObjectPath result; + proxy_.callMethod("ActivateConnection").onInterface(INTERFACE_NAME).withArguments(connection, device, specific_object).storeResultsTo(result); + return result; + } + + std::tuple AddAndActivateConnection(const std::map>& connection, const sdbus::ObjectPath& device, const sdbus::ObjectPath& specific_object) + { + std::tuple result; + proxy_.callMethod("AddAndActivateConnection").onInterface(INTERFACE_NAME).withArguments(connection, device, specific_object).storeResultsTo(result); + return result; + } + + std::tuple> AddAndActivateConnection2(const std::map>& connection, const sdbus::ObjectPath& device, const sdbus::ObjectPath& specific_object, const std::map& options) + { + std::tuple> result; + proxy_.callMethod("AddAndActivateConnection2").onInterface(INTERFACE_NAME).withArguments(connection, device, specific_object, options).storeResultsTo(result); + return result; + } + + void DeactivateConnection(const sdbus::ObjectPath& active_connection) + { + proxy_.callMethod("DeactivateConnection").onInterface(INTERFACE_NAME).withArguments(active_connection); + } + + void Sleep(const bool& sleep) + { + proxy_.callMethod("Sleep").onInterface(INTERFACE_NAME).withArguments(sleep); + } + + void Enable(const bool& enable) + { + proxy_.callMethod("Enable").onInterface(INTERFACE_NAME).withArguments(enable); + } + + std::map GetPermissions() + { + std::map result; + proxy_.callMethod("GetPermissions").onInterface(INTERFACE_NAME).storeResultsTo(result); + return result; + } + + void SetLogging(const std::string& level, const std::string& domains) + { + proxy_.callMethod("SetLogging").onInterface(INTERFACE_NAME).withArguments(level, domains); + } + + std::tuple GetLogging() + { + std::tuple result; + proxy_.callMethod("GetLogging").onInterface(INTERFACE_NAME).storeResultsTo(result); + return result; + } + + uint32_t CheckConnectivity() + { + uint32_t result; + proxy_.callMethod("CheckConnectivity").onInterface(INTERFACE_NAME).storeResultsTo(result); + return result; + } + + uint32_t state() + { + uint32_t result; + proxy_.callMethod("state").onInterface(INTERFACE_NAME).storeResultsTo(result); + return result; + } + + sdbus::ObjectPath CheckpointCreate(const std::vector& devices, const uint32_t& rollback_timeout, const uint32_t& flags) + { + sdbus::ObjectPath result; + proxy_.callMethod("CheckpointCreate").onInterface(INTERFACE_NAME).withArguments(devices, rollback_timeout, flags).storeResultsTo(result); + return result; + } + + void CheckpointDestroy(const sdbus::ObjectPath& checkpoint) + { + proxy_.callMethod("CheckpointDestroy").onInterface(INTERFACE_NAME).withArguments(checkpoint); + } + + std::map CheckpointRollback(const sdbus::ObjectPath& checkpoint) + { + std::map result; + proxy_.callMethod("CheckpointRollback").onInterface(INTERFACE_NAME).withArguments(checkpoint).storeResultsTo(result); + return result; + } + + void CheckpointAdjustRollbackTimeout(const sdbus::ObjectPath& checkpoint, const uint32_t& add_timeout) + { + proxy_.callMethod("CheckpointAdjustRollbackTimeout").onInterface(INTERFACE_NAME).withArguments(checkpoint, add_timeout); + } + +public: + std::vector Devices() + { + return proxy_.getProperty("Devices").onInterface(INTERFACE_NAME); + } + + std::vector AllDevices() + { + return proxy_.getProperty("AllDevices").onInterface(INTERFACE_NAME); + } + + std::vector Checkpoints() + { + return proxy_.getProperty("Checkpoints").onInterface(INTERFACE_NAME); + } + + bool NetworkingEnabled() + { + return proxy_.getProperty("NetworkingEnabled").onInterface(INTERFACE_NAME); + } + + bool WirelessEnabled() + { + return proxy_.getProperty("WirelessEnabled").onInterface(INTERFACE_NAME); + } + + void WirelessEnabled(const bool& value) + { + proxy_.setProperty("WirelessEnabled").onInterface(INTERFACE_NAME).toValue(value); + } + + bool WirelessHardwareEnabled() + { + return proxy_.getProperty("WirelessHardwareEnabled").onInterface(INTERFACE_NAME); + } + + bool WwanEnabled() + { + return proxy_.getProperty("WwanEnabled").onInterface(INTERFACE_NAME); + } + + void WwanEnabled(const bool& value) + { + proxy_.setProperty("WwanEnabled").onInterface(INTERFACE_NAME).toValue(value); + } + + bool WwanHardwareEnabled() + { + return proxy_.getProperty("WwanHardwareEnabled").onInterface(INTERFACE_NAME); + } + + bool WimaxEnabled() + { + return proxy_.getProperty("WimaxEnabled").onInterface(INTERFACE_NAME); + } + + void WimaxEnabled(const bool& value) + { + proxy_.setProperty("WimaxEnabled").onInterface(INTERFACE_NAME).toValue(value); + } + + bool WimaxHardwareEnabled() + { + return proxy_.getProperty("WimaxHardwareEnabled").onInterface(INTERFACE_NAME); + } + + uint32_t RadioFlags() + { + return proxy_.getProperty("RadioFlags").onInterface(INTERFACE_NAME); + } + + std::vector ActiveConnections() + { + return proxy_.getProperty("ActiveConnections").onInterface(INTERFACE_NAME); + } + + sdbus::ObjectPath PrimaryConnection() + { + return proxy_.getProperty("PrimaryConnection").onInterface(INTERFACE_NAME); + } + + std::string PrimaryConnectionType() + { + return proxy_.getProperty("PrimaryConnectionType").onInterface(INTERFACE_NAME); + } + + uint32_t Metered() + { + return proxy_.getProperty("Metered").onInterface(INTERFACE_NAME); + } + + sdbus::ObjectPath ActivatingConnection() + { + return proxy_.getProperty("ActivatingConnection").onInterface(INTERFACE_NAME); + } + + bool Startup() + { + return proxy_.getProperty("Startup").onInterface(INTERFACE_NAME); + } + + std::string Version() + { + return proxy_.getProperty("Version").onInterface(INTERFACE_NAME); + } + + std::vector VersionInfo() + { + return proxy_.getProperty("VersionInfo").onInterface(INTERFACE_NAME); + } + + std::vector Capabilities() + { + return proxy_.getProperty("Capabilities").onInterface(INTERFACE_NAME); + } + + uint32_t State() + { + return proxy_.getProperty("State").onInterface(INTERFACE_NAME); + } + + uint32_t Connectivity() + { + return proxy_.getProperty("Connectivity").onInterface(INTERFACE_NAME); + } + + bool ConnectivityCheckAvailable() + { + return proxy_.getProperty("ConnectivityCheckAvailable").onInterface(INTERFACE_NAME); + } + + bool ConnectivityCheckEnabled() + { + return proxy_.getProperty("ConnectivityCheckEnabled").onInterface(INTERFACE_NAME); + } + + void ConnectivityCheckEnabled(const bool& value) + { + proxy_.setProperty("ConnectivityCheckEnabled").onInterface(INTERFACE_NAME).toValue(value); + } + + std::string ConnectivityCheckUri() + { + return proxy_.getProperty("ConnectivityCheckUri").onInterface(INTERFACE_NAME); + } + + std::map GlobalDnsConfiguration() + { + return proxy_.getProperty("GlobalDnsConfiguration").onInterface(INTERFACE_NAME); + } + + void GlobalDnsConfiguration(const std::map& value) + { + proxy_.setProperty("GlobalDnsConfiguration").onInterface(INTERFACE_NAME).toValue(value); + } + +private: + sdbus::IProxy& proxy_; +}; + +}} // namespaces + +#endif diff --git a/PiPedalCommon/src/include/json.hpp b/PiPedalCommon/src/include/json.hpp index 57c37a0..a8007fe 100644 --- a/PiPedalCommon/src/include/json.hpp +++ b/PiPedalCommon/src/include/json.hpp @@ -327,6 +327,14 @@ namespace pipedal bool allowNaN() const { return allowNaN_; } void allowNaN(bool allow) { allowNaN_ = allow; } + void write(uint8_t value) + { + os << value; + } + void write(int8_t value) + { + os << value; + } void write(long long value) { os << value; @@ -882,6 +890,13 @@ namespace pipedal skip_whitespace(); *value = read_boolean(); } + void read(uint8_t*value) + { + skip_whitespace(); + is_ >> *value; + if (is_.fail()) + throw JsonException("Invalid format."); + } void read(int *value) { skip_whitespace(); diff --git a/modules/websocketpp b/modules/websocketpp index b9aeec6..e0ad649 160000 --- a/modules/websocketpp +++ b/modules/websocketpp @@ -1 +1 @@ -Subproject commit b9aeec6eaf3d5610503439b4fae3581d9aff08e8 +Subproject commit e0ad649adaa271f6c5ac94792d8a346466c6190a diff --git a/react/public/var/config.json b/react/public/var/config.json index d16ec84..f808ca9 100644 --- a/react/public/var/config.json +++ b/react/public/var/config.json @@ -1,5 +1,5 @@ { - "socket_server_port": 81, + "socket_server_port": 8080, "socket_server_address": "*", "debug": true, "max_upload_size": 536870912, diff --git a/react/src/PiPedalModel.tsx b/react/src/PiPedalModel.tsx index 7bab2ad..f9c2d9b 100644 --- a/react/src/PiPedalModel.tsx +++ b/react/src/PiPedalModel.tsx @@ -942,7 +942,7 @@ export class PiPedalModel //implements PiPedalModel return this.webSocket.connect(); }) .catch((error) => { - this.setError("Failed to connect to server." + error.toString()); + this.setError("Failed to connect to server. " + this.socketServerUrl); return false; }) .then(() => { diff --git a/react/src/PiPedalSocket.tsx b/react/src/PiPedalSocket.tsx index f611ad9..3cdc6ca 100644 --- a/react/src/PiPedalSocket.tsx +++ b/react/src/PiPedalSocket.tsx @@ -278,7 +278,7 @@ class PiPedalSocket { ws.onerror = null; reject("Connection closed unexpectedly."); }; - ws.onerror = (event: Event) => { + ws.onerror = (evWheent: Event) => { ws.onclose = null; ws.onerror = null; reject("Failed to connect."); @@ -286,7 +286,6 @@ class PiPedalSocket { ws.onopen = (event: Event) => { ws.onerror = self.handleError.bind(self); ws.onclose = self.handleClose.bind(self); - alert("Web socket connected."); //yyy ws.onopen = null; resolve(ws); }; diff --git a/react/src/SettingsDialog.tsx b/react/src/SettingsDialog.tsx index c8481da..9faa8f5 100644 --- a/react/src/SettingsDialog.tsx +++ b/react/src/SettingsDialog.tsx @@ -24,7 +24,7 @@ import ListSelectDialog from './ListSelectDialog'; import IconButton from '@mui/material/IconButton'; import Typography from '@mui/material/Typography'; import { PiPedalModel, PiPedalModelFactory, State } from './PiPedalModel'; -import {ColorTheme} from './DarkMode'; +import { ColorTheme } from './DarkMode'; import ButtonBase from "@mui/material/ButtonBase"; import AppBar from '@mui/material/AppBar'; import Button from '@mui/material/Button'; @@ -394,11 +394,10 @@ const SettingsDialog = withStyles(styles, { withTheme: true })( }); } - handleCheckForUpdates() - { + handleCheckForUpdates() { this.model.showUpdateDialog(); } - + handleMidiMessageSettings() { this.setState({ showSystemMidiBindingsDialog: true @@ -702,13 +701,13 @@ const SettingsDialog = withStyles(styles, { withTheme: true })( this.handleShowWifiDirectConfigDialog()} > + onClick={() => this.handleShowWifiConfigDialog()} >
- Configure Wi-Fi Direct hotspot + Wi-Fi auto hotspot - {this.state.wifiDirectConfigSettings.getSummaryText()} + {this.state.wifiConfigSettings.getSummaryText()}
@@ -756,8 +755,8 @@ const SettingsDialog = withStyles(styles, { withTheme: true })( Color theme - { this.model.getTheme() === ColorTheme.Dark ? "Dark" : - (this.model.getTheme() === ColorTheme.Light ? "Light": "System")} + {this.model.getTheme() === ColorTheme.Dark ? "Dark" : + (this.model.getTheme() === ColorTheme.Light ? "Light" : "System")}
@@ -765,9 +764,9 @@ const SettingsDialog = withStyles(styles, { withTheme: true })( { + onClick={() => { this.model.setShowStatusMonitor(!this.state.showStatusMonitor) - }} > + }} >
{ this.setState({showThemeSelectDialog: false});} } + onClose={() => { this.setState({ showThemeSelectDialog: false }); }} onOk={(selectedTheme: ColorTheme) => { this.model.setTheme(selectedTheme); - this.setState({showThemeSelectDialog: false}); + this.setState({ showThemeSelectDialog: false }); }} defaultTheme={this.model.getTheme()} /> ) } - this.setState({ showWifiConfigDialog: false })} - onOk={(wifiConfigSettings) => this.handleApplyWifiConfig(wifiConfigSettings)} - /> + { + (this.state.showWifiConfigDialog) && + ( + this.setState({ showWifiConfigDialog: false })} + onOk={(wifiConfigSettings) => this.handleApplyWifiConfig(wifiConfigSettings)} + /> + + ) + } this.setState({ showWifiDirectConfigDialog: false })} onOk={(wifiDirectConfigSettings: WifiDirectConfigSettings) => this.handleApplyWifiDirectConfig(wifiDirectConfigSettings)} diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 7e58950..66e305f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -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} ) diff --git a/src/CapturePresetsMain.cpp b/src/CapturePresetsMain.cpp index 7f9dd4f..79d0bbe 100644 --- a/src/CapturePresetsMain.cpp +++ b/src/CapturePresetsMain.cpp @@ -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 diff --git a/src/ConfigMain.cpp b/src/ConfigMain.cpp index f17d360..0a67f51 100644 --- a/src/ConfigMain.cpp +++ b/src/ConfigMain.cpp @@ -22,6 +22,8 @@ #include #include #include "CommandLineParser.hpp" +#include "SystemConfigFile.hpp" + #include #include #include "WriteTemplateFile.hpp" @@ -42,6 +44,7 @@ #include #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) { diff --git a/src/DBusToLv2Log.cpp b/src/DBusToLv2Log.cpp new file mode 100644 index 0000000..62b1c0e --- /dev/null +++ b/src/DBusToLv2Log.cpp @@ -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()); +} diff --git a/src/DBusToLv2Log.hpp b/src/DBusToLv2Log.hpp new file mode 100644 index 0000000..5abfb9d --- /dev/null +++ b/src/DBusToLv2Log.hpp @@ -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(); +} \ No newline at end of file diff --git a/src/HotspotManager.cpp b/src/HotspotManager.cpp new file mode 100644 index 0000000..22dcaa5 --- /dev/null +++ b/src/HotspotManager.cpp @@ -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 +#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 +#include + +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 GetAutoConnectSsids(); + std::vector GetKnownVisibleAccessPoints(); + + void FireNetworkChanging(); + + void MaybeStartHotspot(); + + void StartScanTimer(); + void StopScanTimer(); + void ScanNow(); + + Connection::ptr FindExistingConnection(); + + Device::ptr GetDevice(uint32_t nmDeviceType); + std::atomic closed; + DBusDispatcher dbusDispatcher; + + std::unique_ptr 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(); +} + +template +class VectorHash +{ +public: + std::size_t operator()(const std::vector &vec) const + { + std::size_t hash = vec.size(); + for (auto &i : vec) + { + hash += static_cast(i); + hash = ((hash >> 32) ^ hash) * 0x119de1F3; + } + return hash; + } +}; + +static std::string ssidToString(const std::vector &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> HotspotManagerImpl::GetKnownVisibleAccessPoints() +{ + std::vector> knownSsids = this->GetAutoConnectSsids(); + + std::unordered_set, VectorHash> index{knownSsids.begin(), knownSsids.end()}; + + std::vector 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> HotspotManagerImpl::GetAutoConnectSsids() +{ + auto availableConnections = wlanDevice->AvailableConnections(); + std::vector> 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() == "infrastructure"; + if (isInfrastructure && autoConnect) + { + std::vector 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> 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() == "ap"; + if (isHotspot) + { + std::string id; + if (settings["connection"].count("id") > 0) + { + id = settings["connection"]["id"].get(); + } + 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> settings; + + std::map &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 &wireless = settings["802-11-wireless"]; + + std::string ssid = this->wifiConfigSettings.hotspotName_; + std::vector 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 &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 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 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(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 lock { this->networkChangingListenerMutex}; + this->networkChangingListener = std::move(listener); +} +void HotspotManagerImpl::FireNetworkChanging() { + std::lock_guard lock { this->networkChangingListenerMutex}; + if (this->networkChangingListener) + { + this->networkChangingListener(this->ethernetConnected,!!activeConnection); + } + +} diff --git a/src/HotspotManager.hpp b/src/HotspotManager.hpp new file mode 100644 index 0000000..5107177 --- /dev/null +++ b/src/HotspotManager.hpp @@ -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 +#include +#include + +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; + + static ptr Create(); + + virtual ~HotspotManager() noexcept { } + + virtual void Open() = 0; + virtual void Reload() = 0; + virtual void Close() = 0; + + using NetworkChangingListener = std::function; + + virtual void SetNetworkChangingListener(NetworkChangingListener &&listener) = 0; + + using PostHandle = uint64_t; + using PostCallback = std::function; + + virtual PostHandle Post(PostCallback&&fn) = 0; + virtual PostHandle PostDelayed(const clock::duration&delay,PostCallback&&fn) = 0; + virtual bool CancelPost(PostHandle handle) = 0; + + + template + PostHandle PostDelayed(const std::chrono::duration&delay,PostCallback&&fn) + { + return PostDelayed( + std::chrono::duration_cast(delay), + std::move(fn)); + } + + + + }; + +} \ No newline at end of file diff --git a/src/PiPedalModel.cpp b/src/PiPedalModel.cpp index da2147d..cf4ad3b 100644 --- a/src/PiPedalModel.cpp +++ b/src/PiPedalModel.cpp @@ -35,6 +35,8 @@ #include "PiPedalUI.hpp" #include "atom_object.hpp" #include "Lv2PluginChangeMonitor.hpp" +#include "HotspotManager.hpp" +#include "DBusToLv2Log.hpp" #ifndef NO_MLOCK #include @@ -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 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(); +} diff --git a/src/PiPedalModel.hpp b/src/PiPedalModel.hpp index 8fc4104..1b4f784 100644 --- a/src/PiPedalModel.hpp +++ b/src/PiPedalModel.hpp @@ -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; + using NetworkChangedListener = std::function; + + private: + std::unique_ptr 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; JackConfiguration jackConfiguration; std::shared_ptr 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 + PostHandle PostDelayed(const std::chrono::duration&delay,PostCallback&&fn) + { + return PostDelayed( + std::chrono::duration_cast(delay), + std::move(fn)); + } + + void SetNetworkChangedListener(NetworkChangedListener listener) { + networkChangedListener = listener; + } + void WaitForAudioDeviceToComeOnline(); UpdateStatus GetUpdateStatus(); diff --git a/src/SetWifiConfig.cpp b/src/SetWifiConfig.cpp index c503c37..3e58dbb 100644 --- a/src/SetWifiConfig.cpp +++ b/src/SetWifiConfig.cpp @@ -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; diff --git a/src/SetWifiConfig.hpp b/src/SetWifiConfig.hpp index 820b94b..45229ca 100644 --- a/src/SetWifiConfig.hpp +++ b/src/SetWifiConfig.hpp @@ -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); diff --git a/src/Storage.cpp b/src/Storage.cpp index 9f47749..0c2daa9 100644 --- a/src/Storage.cpp +++ b/src/Storage.cpp @@ -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() { diff --git a/src/Storage.hpp b/src/Storage.hpp index 514686b..913c04d 100644 --- a/src/Storage.hpp +++ b/src/Storage.hpp @@ -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); diff --git a/src/SystemConfigFile.cpp b/src/SystemConfigFile.cpp index c82db9a..8bea50b 100644 --- a/src/SystemConfigFile.cpp +++ b/src/SystemConfigFile.cpp @@ -23,6 +23,7 @@ #include #include #include +#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; +} + + diff --git a/src/SystemConfigFile.hpp b/src/SystemConfigFile.hpp index f4842ae..a8622cb 100644 --- a/src/SystemConfigFile.hpp +++ b/src/SystemConfigFile.hpp @@ -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(); }; }; \ No newline at end of file diff --git a/src/WebServer.cpp b/src/WebServer.cpp index f815102..60e9eef 100644 --- a/src/WebServer.cpp +++ b/src/WebServer.cpp @@ -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 pipedal::WebServer::create( { return std::shared_ptr(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)); + } +} \ No newline at end of file diff --git a/src/WebServer.hpp b/src/WebServer.hpp index 8b86ba8..76b4268 100644 --- a/src/WebServer.hpp +++ b/src/WebServer.hpp @@ -222,6 +222,7 @@ public: virtual ~WebServer() { } virtual void SetLogHttpRequests(bool enableLogging) = 0; + virtual void DisplayIpAddresses() = 0; virtual void AddRequestHandler(std::shared_ptr requestHandler) = 0; virtual void AddSocketFactory(std::shared_ptr &socketHandler) = 0; diff --git a/src/hotspotManagerTestMain.cpp b/src/hotspotManagerTestMain.cpp new file mode 100644 index 0000000..8ac901d --- /dev/null +++ b/src/hotspotManagerTestMain.cpp @@ -0,0 +1,61 @@ +#include "HotspotManager.hpp" +#include "WifiConfigSettings.hpp" +#include +#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; +} + diff --git a/src/main.cpp b/src/main.cpp index 5a19f19..8108120 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -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( []() diff --git a/src/polkit-1/55-org.pipedal.d/55-org.freedesktop.NetworkManager.pkla b/src/polkit-1/55-org.pipedal.d/55-org.freedesktop.NetworkManager.pkla new file mode 100644 index 0000000..f1172b4 --- /dev/null +++ b/src/polkit-1/55-org.pipedal.d/55-org.freedesktop.NetworkManager.pkla @@ -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 diff --git a/src/polkit-1/rules/55-pipedal-networkmanager.rule b/src/polkit-1/rules/55-pipedal-networkmanager.rule new file mode 100644 index 0000000..2c4ea65 --- /dev/null +++ b/src/polkit-1/rules/55-pipedal-networkmanager.rule @@ -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; + } +}); \ No newline at end of file diff --git a/todo.txt b/todo.txt index 3c11328..cef84c8 100644 --- a/todo.txt +++ b/todo.txt @@ -1,3 +1,25 @@ +Installer: + + sudo systemctl restart polkitd.service + + +- Scanning. +- verify that scanning doesn't cause overruns. +- UI changes. +- test behaviour when Osgilliad is offline. +- docs changes. +- unicode commandline arguments. +- unicode filenames. +- update configMain. +- address change handlers in client (and browser?) +- Notify hotspotManager on settings changed. +- Midi binding for hotspotManager. +- Keybaord escape for hotspotManager. +- verify stripping in installer. +- verify update with no keyring. +-verify clean removal of dhcpcd and nm_p2p2d + + - Back button after reloads on Android. - pidedal_nm_p2p2 service not started from web ui. - pipedal_nm_p2pd connection not visible.