From 7f058b6ed99e552c8ae82fea63cf6f3556b6c93a Mon Sep 17 00:00:00 2001 From: Robin Davies Date: Fri, 22 Apr 2022 02:20:44 -0400 Subject: [PATCH] Wi-Fi Direct cmdline --- .vscode/launch.json | 96 ++- CMakeLists.txt | 12 +- mem_guard,x | 0 src/AdminClient.cpp | 188 ++++++ src/{ShutdownClient.hpp => AdminClient.hpp} | 27 +- src/{ShutdownMain.cpp => AdminMain.cpp} | 300 ++++----- src/AvahiService.cpp | 340 ++++++++++ src/AvahiService.hpp | 74 ++ src/AvahiServiceTest.cpp | 47 ++ src/CMakeLists.txt | 42 +- src/ConfigMain.cpp | 81 ++- src/ConfigUtil.cpp | 139 ++++ src/{templateP2pd.service => ConfigUtil.hpp} | 20 +- src/DeviceIdFile.cpp | 76 +++ src/DeviceIdFile.hpp | 41 ++ src/JackHost.cpp | 6 +- src/P2pConfigFiles.hpp | 2 +- src/PiPedalConfiguration.hpp | 1 + src/PiPedalModel.cpp | 14 +- src/PiPedalModel.hpp | 10 + src/PiPedalSocket.cpp | 74 +- src/PrettyPrinter.hpp | 2 +- src/SetWifiConfig.cpp | 166 +++-- src/SetWifiConfig.hpp | 2 - src/ShutdownClient.cpp | 235 ------- src/UnixSocket.cpp | 253 +++++++ src/UnixSocket.hpp | 96 +++ src/UnixSocketTest.cpp | 71 ++ src/WifiConfigSettings.cpp | 62 +- src/WifiConfigSettings.hpp | 4 + src/WifiDirectConfigSettings.cpp | 50 +- src/WriteTemplateFile.cpp | 39 +- src/WriteTemplateFile.hpp | 19 +- src/dbus/bluez.h | 28 - src/dbus/bluez.xml | 276 -------- src/dbus/bluez_adaptor.h | 428 ------------ src/dbus/bluez_proxy.h | 631 ------------------ src/dbus/bluez_test.cpp | 112 ---- src/main.cpp | 92 +-- src/templates/30-pipedal-apd.conf | 24 + src/templates/30-pipedal-p2p.conf | 11 + .../jack.service.template} | 0 .../pipedal_p2pd.conf.template} | 10 +- src/templates/pipedal_p2pd.service.template | 13 + .../pipedaladmind.service.template} | 0 .../pipedald.service.template} | 0 src/testMain.cpp | 1 - 47 files changed, 2106 insertions(+), 2109 deletions(-) delete mode 100644 mem_guard,x create mode 100644 src/AdminClient.cpp rename src/{ShutdownClient.hpp => AdminClient.hpp} (65%) rename src/{ShutdownMain.cpp => AdminMain.cpp} (69%) create mode 100644 src/AvahiService.cpp create mode 100644 src/AvahiService.hpp create mode 100644 src/AvahiServiceTest.cpp create mode 100644 src/ConfigUtil.cpp rename src/{templateP2pd.service => ConfigUtil.hpp} (84%) create mode 100644 src/DeviceIdFile.cpp create mode 100644 src/DeviceIdFile.hpp delete mode 100644 src/ShutdownClient.cpp create mode 100644 src/UnixSocket.cpp create mode 100644 src/UnixSocket.hpp create mode 100644 src/UnixSocketTest.cpp delete mode 100644 src/dbus/bluez.h delete mode 100644 src/dbus/bluez.xml delete mode 100644 src/dbus/bluez_adaptor.h delete mode 100644 src/dbus/bluez_proxy.h delete mode 100644 src/dbus/bluez_test.cpp create mode 100644 src/templates/30-pipedal-apd.conf create mode 100644 src/templates/30-pipedal-p2p.conf rename src/{templateJack.service => templates/jack.service.template} (100%) rename src/{template_p2pd.conf => templates/pipedal_p2pd.conf.template} (79%) create mode 100644 src/templates/pipedal_p2pd.service.template rename src/{templateAdmin.service => templates/pipedaladmind.service.template} (100%) rename src/{template.service => templates/pipedald.service.template} (100%) diff --git a/.vscode/launch.json b/.vscode/launch.json index a06c04d..bbb3ffa 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -5,13 +5,45 @@ "version": "0.2.0", "configurations": [ + { + "name": "(gdb) generic", + "type": "cppdbg", + "request": "launch", + // Resolved by CMake Tools: + "program": "${command:cmake.launchTargetPath}", + "args": [ ], + "stopAtEntry": false, + "cwd": "${workspaceFolder}", + "environment": [ + { + // add the directory where our target was built to the PATHs + // it gets resolved by CMake Tools: + "name": "PATH", + "value": "$PATH:${command:cmake.launchTargetDirectory}" + }, + { + "name": "ASAN_OPTIONS", + "value": "detect_leaks=0" + + } + ], + "externalConsole": false, + "MIMode": "gdb", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + } + ] + }, { "name": "(gdb) pipedaltest Launch", "type": "cppdbg", "request": "launch", // Resolved by CMake Tools: "program": "${command:cmake.launchTargetPath}", - "args": [ "[Dev]" ], + "args": [ "[avahi_service]" ], "stopAtEntry": false, "cwd": "${workspaceFolder}", "environment": [ @@ -61,6 +93,11 @@ "name": "PATH", "value": "$PATH:${command:cmake.launchTargetDirectory}" }, + { + "name": "ASAN_OPTIONS", + "value": "detect_leaks=0" + + }, { "name": "OTHER_VALUE", "value": "Something something" @@ -98,6 +135,12 @@ "name": "PATH", "value": "$PATH:${command:cmake.launchTargetDirectory}" }, + { + "name": "ASAN_OPTIONS", + "value": "detect_leaks=0" + + }, + { "name": "OTHER_VALUE", "value": "Something something" @@ -120,7 +163,8 @@ // Resolved by CMake Tools: "program": "${command:cmake.launchTargetPath}", "args": [ - + "--enable-p2p" , "--nopkexec" + //,"CA", "PiPedalTest","12345678","1" ], "stopAtEntry": false, "cwd": "${workspaceFolder}", @@ -132,6 +176,52 @@ "value": "$PATH:${command:cmake.launchTargetDirectory}" }, { + "name": "ASAN_OPTIONS", + "value": "detect_leaks=0" + + }, + { + "name": "OTHER_VALUE", + "value": "Something something" + } + ], + "externalConsole": false, + "MIMode": "gdb", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + } + ] + }, + + { + "name": "(gdb) pipedal_p2pd", + "type": "cppdbg", + "request": "launch", + // Resolved by CMake Tools: + "program": "${command:cmake.launchTargetPath}", + "args": [ + "--config-file", "/etc/pipedal/config/pipedal_p2pd.conf" + , "--trace-messages", "--log-level", "debug", + + ], + "stopAtEntry": false, + "cwd": "${workspaceFolder}", + "environment": [ + { + // add the directory where our target was built to the PATHs + // it gets resolved by CMake Tools: + "name": "PATH", + "value": "$PATH:${command:cmake.launchTargetDirectory}" + }, + { + "name": "ASAN_OPTIONS", + "value": "detect_leaks=0" + + }, + { "name": "OTHER_VALUE", "value": "Something something" } @@ -148,5 +238,7 @@ } + + ] } \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 4b81cf2..c518c6d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -51,14 +51,10 @@ install ( install (FILES ${PROJECT_SOURCE_DIR}/build/src/notices.txt DESTINATION /etc/pipedal/react/var/) -install (FILES ${PROJECT_SOURCE_DIR}/src/template.service - ${PROJECT_SOURCE_DIR}/src/templateAdmin.service - ${PROJECT_SOURCE_DIR}/src/templateJack.service - ${PROJECT_SOURCE_DIR}/src/defaultFavorites.json - ${PROJECT_SOURCE_DIR}/src/templateP2pd.service - ${PROEJCT_SOURCE_DIR}/src/template_p2pd.conf - - DESTINATION /etc/pipedal/config ) +install ( + DIRECTORY ${PROJECT_SOURCE_DIR}/src/templates + DESTINATION /etc/pipedal/config/ +) install (DIRECTORY ${LV2_SOURCE_DIRECTORY} DESTINATION /usr/lib/lv2 diff --git a/mem_guard,x b/mem_guard,x deleted file mode 100644 index e69de29..0000000 diff --git a/src/AdminClient.cpp b/src/AdminClient.cpp new file mode 100644 index 0000000..9d7dbd6 --- /dev/null +++ b/src/AdminClient.cpp @@ -0,0 +1,188 @@ +// 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 "pch.h" +#include "AdminClient.hpp" +#include "PiPedalException.hpp" +#include +#include +#include +#include "Lv2Log.hpp" +#include +#include +#include +#include "Ipv6Helpers.hpp" + +using namespace pipedal; + +const char ADMIN_SOCKET_NAME[] = "/run/pipedal/pipedal_admin"; + +AdminClient::AdminClient() +{ +} + +AdminClient::~AdminClient() +{ +} + +bool AdminClient::CanUseShutdownClient() +{ + return true; +} + +bool AdminClient::RequestShutdown(bool restart) +{ + const char *message = restart ? "restart\n" : "shutdown\n"; + return WriteMessage(message); +} + +bool AdminClient::WriteMessage(const char *message) +{ + std::lock_guard lock{mutex}; + + if (!socket.IsOpen()) + { + try + { + socket.Connect(ADMIN_SOCKET_NAME, "pipedal_d"); + } + catch (const std::exception &e) + { + Lv2Log::error(SS("Failed to connect to PiPedal Admin service.")); + return false; + } + } + + socket.Send(message, strlen(message)); + + char responseBuffer[1024]; + + socket.Receive(responseBuffer, sizeof(responseBuffer)); + + int response = atoi(responseBuffer); + if (response == -2) // throw exception with message + { + const char *p = responseBuffer; + while (*p != ' ' && *p != '\n' && *p != '\0') + { + ++p; + } + if (*p != 0) + ++p; + throw PiPedalStateException(p); + } + return response == 0; +} + +bool AdminClient::SetJackServerConfiguration(const JackServerSettings &jackServerSettings) +{ + std::stringstream s; + s << "setJackConfiguration "; + + json_writer writer(s, true); + writer.write(jackServerSettings); + s << std::endl; + return WriteMessage(s.str().c_str()); +} + +void AdminClient::SetWifiConfig(const WifiConfigSettings &settings) +{ + if (!CanUseShutdownClient()) + { + throw PiPedalException("Can't perform this operation when debugging."); + } + std::stringstream cmd; + cmd << "WifiConfigSettings "; + json_writer writer(cmd, true); + writer.write(settings); + cmd << '\n'; + bool result = WriteMessage(cmd.str().c_str()); + if (!result) + { // unexpected. Should throw exception on failure. + throw PiPedalException("Operation failed."); + } +} +void AdminClient::SetWifiDirectConfig(const WifiDirectConfigSettings &settings) +{ + if (!CanUseShutdownClient()) + { + throw PiPedalException("Can't perform this operation when debugging."); + } + std::stringstream cmd; + cmd << "WifiDirectConfigSettings "; + json_writer writer(cmd, true); + writer.write(settings); + cmd << '\n'; + bool result = WriteMessage(cmd.str().c_str()); + if (!result) + { // unexpected. Should throw exception on failure. + throw PiPedalException("Operation failed."); + } +} + +void AdminClient::SetGovernorSettings(const std::string &settings) +{ + if (!CanUseShutdownClient()) + { + throw PiPedalException("Can't use AdminClient when running interactively."); + } + std::stringstream cmd; + cmd << "GovernorSettings "; + json_writer writer(cmd, true); + writer.write(settings); + cmd << '\n'; + bool result = WriteMessage(cmd.str().c_str()); + if (!result) + { // unexpected. Should throw exception on failure. + throw PiPedalException("Operation failed."); + } +} + +void AdminClient::MonitorGovernor(const std::string &governor) +{ + if (!CanUseShutdownClient()) + { + return; + } + std::stringstream cmd; + cmd << "MonitorGovernor "; + json_writer writer(cmd, true); + writer.write(governor); + cmd << '\n'; + bool result = WriteMessage(cmd.str().c_str()); + if (!result) + { // unexpected. Should throw exception on failure. + throw PiPedalException("Operation failed."); + } +} +void AdminClient::UnmonitorGovernor() +{ + if (!CanUseShutdownClient()) + { + return; + } + std::stringstream cmd; + cmd << "UnmonitorGovernor"; + cmd << '\n'; + bool result = WriteMessage(cmd.str().c_str()); + if (!result) + { // unexpected. Should throw exception on failure. + throw PiPedalException("Operation failed."); + } +} diff --git a/src/ShutdownClient.hpp b/src/AdminClient.hpp similarity index 65% rename from src/ShutdownClient.hpp rename to src/AdminClient.hpp index d28dd2e..0b59193 100644 --- a/src/ShutdownClient.hpp +++ b/src/AdminClient.hpp @@ -22,20 +22,29 @@ #include #include "JackServerSettings.hpp" #include "WifiConfigSettings.hpp" +#include "WifiDirectConfigSettings.hpp" +#include "UnixSocket.hpp" +#include namespace pipedal { -class ShutdownClient { - static bool WriteMessage(const char*message); +class AdminClient { + bool WriteMessage(const char*message); public: - static bool CanUseShutdownClient(); - static bool RequestShutdown(bool restart); - static bool SetJackServerConfiguration(const JackServerSettings & jackServerSettings); - static void SetWifiConfig(const WifiConfigSettings & settings); - static void SetGovernorSettings(const std::string & governor); - static void MonitorGovernor(const std::string &governor); - static void UnmonitorGovernor(); + AdminClient(); + ~AdminClient(); + bool CanUseShutdownClient(); + bool RequestShutdown(bool restart); + bool SetJackServerConfiguration(const JackServerSettings & jackServerSettings); + void SetWifiConfig(const WifiConfigSettings & settings); + void SetWifiDirectConfig(const WifiDirectConfigSettings & settings); + void SetGovernorSettings(const std::string & governor); + void MonitorGovernor(const std::string &governor); + void UnmonitorGovernor(); +private: + std::mutex mutex; + UnixSocket socket; }; } // namespace \ No newline at end of file diff --git a/src/ShutdownMain.cpp b/src/AdminMain.cpp similarity index 69% rename from src/ShutdownMain.cpp rename to src/AdminMain.cpp index e1dbf67..c9203b4 100644 --- a/src/ShutdownMain.cpp +++ b/src/AdminMain.cpp @@ -22,14 +22,14 @@ // non-interactive services are not allowed to execute) #include "pch.h" +#include +#include +#include "ss.hpp" #include "CommandLineParser.hpp" #include "CpuGovernor.hpp" #include #include -#include -#include #include -#include #include #include #include @@ -45,14 +45,13 @@ #include #include #include +#include "UnixSocket.hpp" +#include using namespace std; using namespace pipedal; -using namespace boost; -using namespace boost::asio; -using ip::tcp; -const size_t MAX_LENGTH = 128; +const char ADMIN_SOCKET_NAME[] = "/run/pipedal/pipedal_admin"; class GovernorMonitorThread { @@ -69,12 +68,13 @@ public: this->governor = governor; cancelled = false; wake = false; - pThread = std::make_unique([this]() - { this->ServiceProc(); }); - } else { + pThread = std::make_unique([this]() { this->ServiceProc(); }); + } + else + { this->governor = governor; wake = true; - + lock.unlock(); cv.notify_one(); } @@ -121,13 +121,13 @@ private: cv.wait_until( lock, timeToWaitFor, - [this]() - { return wake; }); + [this]() { return wake; }); wake = false; governor = this->governor; cancelled = this->cancelled; } - if (cancelled) { + if (cancelled) + { break; } std::string activeGovernor = pipedal::GetCpuGovernor(); @@ -156,7 +156,7 @@ void StopGovernorMonitorThread() { governorMonitorThread.Stop(); } -void StartGovernorMonitorThread(const std::string&governor) +void StartGovernorMonitorThread(const std::string &governor) { governorMonitorThread.Start(governor); } @@ -214,96 +214,26 @@ bool setJackConfiguration(JackServerSettings serverSettings) return true; } -class Reader : public std::enable_shared_from_this +class AdminServer { private: - tcp::socket socket; - int readAvailable = MAX_LENGTH; - char data[MAX_LENGTH]; - char *writePointer; - ssize_t writeLength; std::stringstream message; size_t writePosition = 0; std::string response; -public: - Reader(asio::io_service &ios) - : socket(ios) - { - } - static std::shared_ptr Create(asio::io_service &ios) - { - return std::make_shared(ios); - } - - tcp::socket &Socket() { return socket; } - - void Start() - { - ReadSome(); - } + char buffer[4096]; private: - void ReadSome() + bool HandleCommand() { - socket.async_read_some( - boost::asio::buffer(data, readAvailable), - boost::bind(&Reader::HandleRead, - shared_from_this(), - boost::asio::placeholders::error, - boost::asio::placeholders::bytes_transferred)); - } - void WriteSome() - { - if (writePosition == response.size()) - { - socket.close(); - return; - } - socket.async_write_some( - boost::asio::buffer(response.c_str() + writePosition, response.length() - writePosition), - boost::bind(&Reader::HandleWrite, - shared_from_this(), - boost::asio::placeholders::error, - boost::asio::placeholders::bytes_transferred)); - } + std::string sender; + size_t length = socket.ReceiveFrom(buffer, sizeof(buffer), &sender); + if (length > 0 && buffer[length - 1] == '\n') + --length; + buffer[length] = 0; -private: - void HandleWrite(const boost::system::error_code &err, size_t bytes_transferred) - { - writePosition += bytes_transferred; - WriteSome(); - } - void HandleRead(const boost::system::error_code &err, size_t bytes_transferred) - { - if (!err) - { - for (size_t i = 0; i < bytes_transferred; ++i) - { - char c = data[i]; - if (c == '\n') - { - std::string command = message.str(); - cout << command << endl; - HandleCommand(command); - socket.close(); - return; - } - else - { - message.put(data[i]); - } - } - ReadSome(); - } - else - { - socket.close(); - } - } - void HandleCommand(const std::string &text) - { + std::string text{buffer}; int result = -1; std::string command; std::string args; @@ -317,6 +247,10 @@ private: command = text.substr(0, pos); args = text.substr(pos + 1); } + if (command == "__quit") + { + return false; + } try { if (command == "UnmonitorGovernor") @@ -372,6 +306,22 @@ private: pipedal::SetWifiConfig(settings); result = 0; } + else if (command == "WifiDirectConfigSettings") + { + std::stringstream ss(args); + WifiDirectConfigSettings settings; + try + { + json_reader reader(ss); + reader.read(&settings); + } + catch (const std::exception &e) + { + throw PiPedalArgumentException("Invalid arguments."); + } + pipedal::SetWifiDirectConfig(settings); + result = 0; + } else if (command == "shutdown") { result = sysExec("/usr/sbin/shutdown -P now"); @@ -405,77 +355,104 @@ private: } catch (const std::exception &e) { - std::stringstream t; - t << "-2 " << e.what() << "\n"; - this->response = t.str(); - this->writePosition = 0; - WriteSome(); - return; + + std::string reply = SS("-2 " << e.what() << "\n"); + + try + { + socket.SendTo(reply.c_str(), reply.length(), sender); + } + catch (const std::exception /*ignored*/) + { + } + return true; } - std::stringstream t; - t << result << "\n"; - this->response = t.str(); - this->writePosition = 0; - WriteSome(); - } -}; - -class Server -{ -private: - tcp::acceptor acceptor_; - boost::asio::io_service &io_service; - - void HandleAccept(std::shared_ptr connection, const boost::system::error_code &err) - { - if (!err) + std::string reply; + reply = SS(result << "\n"); + try { - connection->Start(); + socket.SendTo(reply.c_str(), reply.length(), sender); } - StartAccept(); + catch (const std::exception /*ignored*/) + { + } + + return true; } - void StartAccept() - { - // socket - std::shared_ptr reader = Reader::Create(io_service); +private: + UnixSocket socket; + UnixSocket cancelSocket; - // asynchronous accept operation and wait for a new connection. - acceptor_.async_accept(reader->Socket(), - boost::bind(&Server::HandleAccept, this, reader, - boost::asio::placeholders::error)); + std::unique_ptr serviceThread; + void ServiceProc() + { + try + { + while (true) + { + if (!HandleCommand()) + { + break; + } + } + } + catch (const std::exception &) + { + } } public: - // constructor for accepting connection from client - Server(boost::asio::io_service &io_service, const tcp::endpoint &endpoint) - : acceptor_(io_service, endpoint), - io_service(io_service) + void Start() + { + socket.Bind(ADMIN_SOCKET_NAME, "pipedal_d"); + cancelSocket.Connect(ADMIN_SOCKET_NAME, "pipedal_d"); - { - StartAccept(); + serviceThread = std::make_unique([this] { + this->ServiceProc(); + }); } - ~Server() + + void Stop() { + + if (serviceThread) + { + std::string msg{"__quit\n"}; + try + { + cancelSocket.Send(msg.c_str(), msg.length()); + } + catch (const std::exception &ignored) + { + }; + + serviceThread->join(); + serviceThread = nullptr; + } } }; -void runServer(uint16_t port) -{ - asio::ip::tcp::endpoint endpoint(ip::make_address_v4("127.0.0.1"), port); - asio::io_service ios; +static bool signaled = false; - try - { - Server server(ios, endpoint); - ios.run(); - cout << "Processing terminated." << endl; - } - catch (const std::exception &e) - { - cout << "Error: " << e.what() << endl; - } +static void sigHandler(int signal) +{ + signaled = true; +} + +struct sigaction saInt, oldActionInt; +struct sigaction saTerm, oldActionTerm; + +static void setSignalHandlers() +{ + memset(&saInt, 0, sizeof(saInt)); + saInt.sa_handler = sigHandler; + sigaction(SIGINT, &saInt, &oldActionInt); + + memset(&saTerm, 0, sizeof(saTerm)); + saTerm.sa_handler = sigHandler; + sigaction(SIGTERM, &saTerm, &oldActionTerm); } int main(int argc, char **argv) @@ -485,7 +462,7 @@ int main(int argc, char **argv) uint16_t port = 0; - parser.AddOption("-port", &port); + parser.AddOption("-port", &port); // ignored legacy option. try { @@ -500,12 +477,29 @@ int main(int argc, char **argv) cout << "Error: " << e.what() << endl; return EXIT_FAILURE; } - if (port == 0) + + mkdir("/run/pipedal", S_IRWXU | S_IRWXG); + + struct group *group_; + group_ = getgrnam("pipedal_d"); + if (group_ == nullptr) { - std::cerr << "Error: port not specified." << std::endl; - return EXIT_FAILURE; + throw UnixSocketException("Group not found."); } - runServer(port); + + chown("/run/pipedal", -1, group_->gr_gid); + + setSignalHandlers(); + + AdminServer server; + server.Start(); + + while (!signaled) + { + usleep(300000); + } + server.Stop(); + governorMonitorThread.Stop(); return EXIT_SUCCESS; } diff --git a/src/AvahiService.cpp b/src/AvahiService.cpp new file mode 100644 index 0000000..4fbd70b --- /dev/null +++ b/src/AvahiService.cpp @@ -0,0 +1,340 @@ +/* + * MIT License + * + * Copyright (c) 2022 Robin E. R. 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 "AvahiService.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "Lv2Log.hpp" +#include "ss.hpp" + +using namespace pipedal; + +void AvahiService::Announce( + int portNumber, + const std::string &name, + const std::string &instanceId, + const std::string &mdnsName) +{ + Unannounce(); + + this->portNumber = portNumber; + this->instanceId = instanceId; + this->mdnsName = mdnsName; + + this->name = avahi_strdup(name.c_str()); + + Start(); +} + +void AvahiService::Unannounce() +{ + Stop(); + if (name) + { + avahi_free(name); + this->name = nullptr; + } +} + +void AvahiService::entry_group_callback(AvahiEntryGroup *g, AvahiEntryGroupState state, void *userData) +{ + ((AvahiService *)userData)->EntryGroupCallback(g, state); +} +void AvahiService::EntryGroupCallback(AvahiEntryGroup *g, AvahiEntryGroupState state) +{ + group = g; + /* Called whenever the entry group state changes */ + switch (state) + { + case AVAHI_ENTRY_GROUP_ESTABLISHED: + /* The entry group has been established successfully */ + Lv2Log::debug(SS("Service " << name << " successfully established.")); + break; + case AVAHI_ENTRY_GROUP_COLLISION: + { + char *n; + /* A service name collision with a remote service + * happened. Let's pick a new name */ + n = avahi_alternative_service_name(name); + avahi_free(name); + name = n; + Lv2Log::debug(SS("Service name collision, renaming service to '" << name << "'\n")); + /* And recreate the services */ + create_group(avahi_entry_group_get_client(g)); + break; + } + case AVAHI_ENTRY_GROUP_FAILURE: + Lv2Log::error(SS("Entry group failure: " << avahi_strerror(avahi_client_errno(avahi_entry_group_get_client(g))) << "\n")); + /* Some kind of failure happened while we were registering our services */ + avahi_threaded_poll_quit(threadedPoll); + break; + case AVAHI_ENTRY_GROUP_UNCOMMITED: + case AVAHI_ENTRY_GROUP_REGISTERING:; + } +} + + +static int toRawDns(char*rawResult,size_t size, const std::string &name) +{ + // from standard name format to xyzfoocom<00> format + size_t len = name.length(); + + rawResult[0] = 0; + if (len+1 >= size-1) return 0; + + rawResult[len+1] = 0; + int count = 0; + for (int i = len-1; i >= 0; --i) + { + if (name[i] == '.') + { + rawResult[i+1] = (char)count; + count = 0; + } else { + rawResult[i+1] = name[i]; + ++count; + } + } + rawResult[0] = count; + return len+1; + +} +void AvahiService::create_group(AvahiClient *c) +{ + char *n; + int ret; + assert(c); + /* If this is the first time we're called, let's create a new + * entry group if necessary */ + if (!group) + { + if (!(group = avahi_entry_group_new(c, entry_group_callback, (void *)this))) + { + Lv2Log::error(SS("avahi_entry_group_new() failed: " << avahi_strerror(avahi_client_errno(c)))); + goto fail; + } + } + /* If the group is empty (either because it was just created, or + * because it was reset previously, add our entries. */ + if (avahi_entry_group_is_empty(group)) + { + Lv2Log::debug(SS("Adding service '" << name)); + + std::string instanceTxtRecord = SS("pipedal_id=" << this->instanceId); + +#define PIPEDAL_SERVICE_TYPE "_pipedal._tcp" + + if ((ret = avahi_entry_group_add_service( + group, + AVAHI_IF_UNSPEC, + AVAHI_PROTO_UNSPEC, + (AvahiPublishFlags)0, + name, + PIPEDAL_SERVICE_TYPE, + NULL, + NULL, + portNumber, + + // txt records. + instanceTxtRecord.c_str(), + NULL)) < 0) + { + if (ret == AVAHI_ERR_COLLISION) + goto collision; + Lv2Log::error(SS("Failed to add _pipedal._tcp service: " << avahi_strerror(ret))); + goto fail; + } + + /* Tell the server to register the service */ + if ((ret = avahi_entry_group_commit(group)) < 0) + { + Lv2Log::error(SS("Failed to commit entry group: " << avahi_strerror(ret))); + goto fail; + } + } + return; +collision: + /* A service name collision with a local service happened. Let's + * pick a new name */ + n = avahi_alternative_service_name(name); + avahi_free(name); + name = n; + Lv2Log::warning(SS("Service name collision, renaming service to '" << name << "'")); + avahi_entry_group_reset(group); + create_group(c); + return; +fail: + avahi_threaded_poll_quit(threadedPoll); +} + +void AvahiService::client_callback(AvahiClient *c, AvahiClientState state, AVAHI_GCC_UNUSED void *userdata) +{ + ((AvahiService *)userdata)->ClientCallback(c, state); +} + +void AvahiService::ClientCallback(AvahiClient *c, AvahiClientState state) +{ + assert(c); + /* Called whenever the client or server state changes */ + switch (state) + { + case AVAHI_CLIENT_S_RUNNING: + /* The server has startup successfully and registered its host + * name on the network, so it's time to create our services */ + create_group(c); + break; + case AVAHI_CLIENT_FAILURE: + this->clientErrno = avahi_client_errno(c); + + if (this->clientErrno == AVAHI_ERR_DISCONNECTED) + { + // tear the client down and restart it + Lv2Log::info("Avahi connection lost. Reconnecting."); + + if (group) + { + avahi_entry_group_reset(group); + group = nullptr; + } + + if (client) + { + avahi_client_free(client); + client = nullptr; + } + int error; + client = avahi_client_new(avahi_threaded_poll_get(threadedPoll), AvahiClientFlags::AVAHI_CLIENT_NO_FAIL, client_callback, (void *)this, &error); + /* Check wether creating the client object succeeded */ + if (!client) + { + Lv2Log::error(SS("Failed to create client: " << avahi_strerror(error))); + avahi_threaded_poll_quit(threadedPoll); + } + } + else + { + Lv2Log::error(SS("Client failure: " << avahi_strerror(avahi_client_errno(c)))); + avahi_threaded_poll_quit(threadedPoll); + } + break; + case AVAHI_CLIENT_S_COLLISION: + /* Let's drop our registered services. When the server is back + * in AVAHI_SERVER_RUNNING state we will register them + * again with the new host name. */ + case AVAHI_CLIENT_S_REGISTERING: + /* The server records are now being established. This + * might be caused by a host name change. We need to wait + * for our own records to register until the host name is + * properly esatblished. */ + if (group) + avahi_entry_group_reset(group); + break; + case AVAHI_CLIENT_CONNECTING:; + } +} + +void AvahiService::Start() +{ + + int error; + int ret = 1; + struct timeval tv; + this->clientErrno = 0; + while (true) + { + /* Allocate main loop object */ + if (!(this->threadedPoll = avahi_threaded_poll_new())) + { + Lv2Log::error("Failed to create Avahi poll object."); + goto fail; + } + + /* Allocate a new client */ + client = avahi_client_new(avahi_threaded_poll_get(threadedPoll), AvahiClientFlags::AVAHI_CLIENT_NO_FAIL, client_callback, (void *)this, &error); + /* Check wether creating the client object succeeded */ + if (!client) + { + Lv2Log::error(SS("Failed to create client: " << avahi_strerror(error))); + goto fail; + } + + avahi_threaded_poll_start(threadedPoll); + ret = 0; + return; + fail: + /* Cleanup things */ + if (client) + { + avahi_client_free(client); + client = nullptr; + } + if (group) + { + avahi_entry_group_reset(group); + group = nullptr; + } + if (threadedPoll) + { + avahi_threaded_poll_free(threadedPoll); + threadedPoll = nullptr; + } + } + return; +} + +void AvahiService::Stop() +{ + + if (threadedPoll) + { + avahi_threaded_poll_stop(threadedPoll); + } + /* Cleanup things */ + // client owns the group? + // if (group) + // { + // avahi_entry_group_reset(group); + // group = nullptr; + // }` + + if (client) + { + avahi_client_free(client); + client = nullptr; + } + if (threadedPoll) + { + avahi_threaded_poll_free(threadedPoll); + threadedPoll = nullptr; + } + group = nullptr; +} diff --git a/src/AvahiService.hpp b/src/AvahiService.hpp new file mode 100644 index 0000000..0d9128f --- /dev/null +++ b/src/AvahiService.hpp @@ -0,0 +1,74 @@ +/* + * MIT License + * + * Copyright (c) 2022 Robin E. R. 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 + +// forward declarations. +class AvahiEntryGroup; +class AvahiThreadedPoll; + +#include + + +namespace pipedal { + + class AvahiService { + public: + ~AvahiService() { Unannounce(); } + void Announce(int portNumber, const std::string &name, const std::string &instanceId, const std::string&mdnsName); + + void Unannounce(); + + private: + void Start(); + void Stop(); + static void entry_group_callback(AvahiEntryGroup*g, AvahiEntryGroupState state, void *userData); + void EntryGroupCallback(AvahiEntryGroup*g, AvahiEntryGroupState state); + + static void client_callback(AvahiClient *c, AvahiClientState state, AVAHI_GCC_UNUSED void * userdata); + void ClientCallback(AvahiClient *c, AvahiClientState state); + + + void create_group(AvahiClient *c); + + void threadProc(); + + int clientErrno = 0; + + int portNumber = -1; + std::string instanceId; + std::string mdnsName; + + AvahiClient *client = NULL; + AvahiEntryGroup *group = NULL; + AvahiThreadedPoll *threadedPoll = NULL; + char*name = NULL; + + }; + +} + diff --git a/src/AvahiServiceTest.cpp b/src/AvahiServiceTest.cpp new file mode 100644 index 0000000..890279b --- /dev/null +++ b/src/AvahiServiceTest.cpp @@ -0,0 +1,47 @@ +/* + * MIT License + * + * Copyright (c) 2022 Robin E. R. 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 "catch.hpp" +#include "AvahiService.hpp" +#include "ss.hpp" +#include +#include + +using namespace pipedal; + +TEST_CASE("Avahi Service Test", "[avahi_service][dev]") +{ + + { + AvahiService service; + + service.Announce(81, "Test Announcement", "0a6045b0-1753-4104-b3e4-b9713b9cc358","pipedal"); + + sleep(10); + + service.Unannounce(); + service.Announce(81, "Test Announcement 2", "0a6045b0-1753-4104-b3e4-b9713b9cc358","pipedal"); + sleep(10000); + } +} \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index ccc2df0..60d48ee 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -14,6 +14,7 @@ find_package(sdbus-c++ REQUIRED) + # Can't get the pkg_check to work. # pkg_check_modules(LIBNL3 "nl-genl-3") # if(!LIBNL3_FOUND) @@ -88,21 +89,13 @@ message (STATUS "Cxx flags: ${CMAKE_CXX_FLAGS}" ) add_definitions(-DBOOST_ERROR_CODE_HEADER_ONLY -DBOOST_BIND_GLOBAL_PLACEHOLDERS -DONBOARDING) -# build Bluez dbus proxies -# requires apt install libsdbus-c++-bin -add_custom_command( - OUTPUT ${PROJECT_SOURCE_DIR}/src/dbus/bluez_proxy.h ${PROJECT_SOURCE_DIR}/src/dbus/bluez_adaptor.h - DEPENDS ${PROJECT_SOURCE_DIR}/src/dbus/bluez.xml - COMMAND sdbus-c++-xml2cpp ${PROJECT_SOURCE_DIR}/src/dbus/bluez.xml - --proxy=${PROJECT_SOURCE_DIR}/src/dbus/bluez_proxy.h - --adaptor=${PROJECT_SOURCE_DIR}/src/dbus/bluez_adaptor.h - - COMMENT "Generating D-Bus bindings for bluez" -) set (PIPEDAL_SOURCES P2pConfigFiles.hpp + AvahiService.cpp AvahiService.hpp + DeviceIdFile.cpp DeviceIdFile.hpp + StdErrorCapture.hpp StdErrorCapture.cpp Ipv6Helpers.cpp Ipv6Helpers.hpp PluginPreset.cpp PluginPreset.hpp @@ -112,6 +105,8 @@ set (PIPEDAL_SOURCES BeastServer.cpp BeastServer.hpp HtmlHelper.cpp HtmlHelper.hpp pch.h Uri.cpp Uri.hpp WifiConfigSettings.hpp WifiConfigSettings.cpp WifiDirectConfigSettings.hpp WifiDirectConfigSettings.cpp + ConfigUtil.hpp ConfigUtil.cpp + RequestHandler.hpp json.cpp json.hpp Scratch.cpp Lv2Host.hpp Lv2Host.cpp PluginType.hpp PluginType.cpp Lv2Log.hpp Lv2Log.cpp @@ -142,7 +137,7 @@ set (PIPEDAL_SOURCES CommandLineParser.hpp Lv2SystemdLogger.hpp Lv2SystemdLogger.cpp JackServerSettings.hpp JackServerSettings.cpp - ShutdownClient.hpp ShutdownClient.cpp + AdminClient.hpp AdminClient.cpp MidiBinding.hpp MidiBinding.cpp PiPedalMath.hpp Locale.hpp Locale.cpp @@ -153,6 +148,8 @@ set (PIPEDAL_SOURCES RegDb.cpp RegDb.hpp PiPedalAlsa.hpp PiPedalAlsa.cpp InheritPriorityMutex.hpp InheritPriorityMutex.cpp + UnixSocket.cpp UnixSocket.hpp + ) configure_file(config.hpp.in config.hpp) @@ -173,17 +170,17 @@ target_include_directories(pipedald PRIVATE ${JACK_INCLUDE_DIRS} ${LILV_0_INCLUDE_DIRS} ${LIBNL3_INCLUDE_DIRS} ${LVDEV_INCLUDE_DIRS} ) -target_link_libraries(pipedald PRIVATE pthread atomic stdc++fs asound +target_link_libraries(pipedald PRIVATE pthread atomic stdc++fs asound avahi-common avahi-client ${LILV_0_LIBRARIES} ${JACK_LIBRARIES} ${LIBNL3_LIBRARIES} systemd SDBusCpp::sdbus-c++ ) ################################# add_executable(pipedaltest ${PIPEDAL_SOURCES} testMain.cpp - dbus/bluez_proxy.h dbus/bluez_adaptor.h - dbus/bluez_test.cpp jsonTest.cpp + AvahiServiceTest.cpp WifiChannelsTest.cpp PiPedalAlsaTest.cpp + UnixSocketTest.cpp Lv2HostLeakTest.cpp @@ -207,7 +204,7 @@ target_include_directories(pipedaltest PRIVATE ${JACK_INCLUDE_DIRS} ${LILV_0_INCLUDE_DIRS} ${LIBNL3_INCLUDE_DIRS} ) -target_link_libraries(pipedaltest PRIVATE pthread atomic stdc++fs asound +target_link_libraries(pipedaltest PRIVATE pthread atomic stdc++fs asound avahi-common avahi-client ${LILV_0_LIBRARIES} ${JACK_LIBRARIES} ${LIBNL3_LIBRARIES} systemd SDBusCpp::sdbus-c++ ) @@ -223,6 +220,7 @@ add_test(NAME DevTest COMMAND pipedaltest "[Dev]") add_executable(pipedalconfig PrettyPrinter.hpp + DeviceIdFile.cpp DeviceIdFile.hpp json.cpp json.hpp HtmlHelper.cpp HtmlHelper.hpp CommandLineParser.hpp @@ -231,9 +229,11 @@ add_executable(pipedalconfig ConfigMain.cpp WifiConfigSettings.cpp WifiConfigSettings.hpp WifiDirectConfigSettings.cpp WifiDirectConfigSettings.hpp + ConfigUtil.hpp ConfigUtil.cpp JackServerSettings.hpp JackServerSettings.cpp SetWifiConfig.hpp SetWifiConfig.cpp SystemConfigFile.hpp SystemConfigFile.cpp + WriteTemplateFile.hpp WriteTemplateFile.cpp SysExec.hpp SysExec.cpp asan_options.cpp Lv2Log.cpp Lv2Log.hpp @@ -241,15 +241,21 @@ add_executable(pipedalconfig ) -target_link_libraries(pipedalconfig PRIVATE pthread atomic stdc++fs +target_link_libraries(pipedalconfig PRIVATE pthread atomic uuid stdc++fs ) -add_executable(pipedaladmind ShutdownMain.cpp CommandLineParser.hpp +add_executable(pipedaladmind AdminMain.cpp CommandLineParser.hpp + UnixSocket.cpp UnixSocket.hpp + DeviceIdFile.cpp DeviceIdFile.hpp + JackServerSettings.hpp JackServerSettings.cpp json.hpp json.cpp HtmlHelper.hpp HtmlHelper.cpp Lv2Log.hpp Lv2Log.cpp Lv2SystemdLogger.hpp Lv2SystemdLogger.cpp WifiConfigSettings.cpp WifiConfigSettings.hpp + WriteTemplateFile.cpp WriteTemplateFile.hpp WifiDirectConfigSettings.cpp WifiDirectConfigSettings.hpp + ConfigUtil.hpp ConfigUtil.cpp + SetWifiConfig.hpp SetWifiConfig.cpp SystemConfigFile.hpp SystemConfigFile.cpp SysExec.hpp SysExec.cpp diff --git a/src/ConfigMain.cpp b/src/ConfigMain.cpp index bd088d2..b112930 100644 --- a/src/ConfigMain.cpp +++ b/src/ConfigMain.cpp @@ -33,9 +33,14 @@ #include "JackServerSettings.hpp" #include "P2pConfigFiles.hpp" #include "PrettyPrinter.hpp" +#include "DeviceIdFile.hpp" +#include +#include #define SS(x) (((std::stringstream &)(std::stringstream() << x)).str()) +#define TEMPLATE_PATH(filename) ("/etc/pipedal/config/templates/" filename ".service") + using namespace std; using namespace pipedal; @@ -380,7 +385,7 @@ void InstallJackService() // deploy the systemd service file std::map map; // nothing to customize. - WriteTemplateFile(map, std::filesystem::path("/etc/pipedal/config/templateJack.service"), GetServiceFileName(JACK_SERVICE)); + WriteTemplateFile(map, GetServiceFileName(JACK_SERVICE)); MaybeStartJackService(); } @@ -424,13 +429,65 @@ void Uninstall() sysExec(SYSTEMCTL_BIN " daemon-reload"); } +std::random_device randdev; + + +std::string MakeUuid() +{ + uuid_t uuid; + uuid_generate(uuid); + char strUid[36 + 1]; + uuid_unparse(uuid, strUid); + return strUid; +} + +static std::string RandomChars(int nchars) +{ + std::stringstream s; + + std::uniform_int_distribution distr(0,26+26+10-1); + + for (int i = 0; i < nchars; ++i) + { + int v = distr(randdev); + char c; + if (v < 10) + { + c = (char)('0' + v); + } else { + v -= 10; + if (v < 26) { + c = (char)('a'+v); + } else { + v -= 26; + c = (char)('A'+v); + } + } + s << c; + } + return s.str(); +} + +static void PrepareDeviceidFile() +{ + if (!std::filesystem::exists(DeviceIdFile::DEVICEID_FILE_NAME)) + { + DeviceIdFile deviceIdFile; + + deviceIdFile.deviceName = "PiPedal-" + RandomChars(2); + deviceIdFile.uuid = MakeUuid(); + deviceIdFile.Save(); + } +} + void Install(const std::filesystem::path &programPrefix, const std::string endpointAddress) { if (sysExec(GROUPADD_BIN " -f " AUDIO_SERVICE_GROUP_NAME) != EXIT_SUCCESS) { throw PiPedalException("Failed to create audio service group."); } - + + PrepareDeviceidFile(); InstallJackService(); auto endpos = endpointAddress.find_last_of(':'); if (endpos == string::npos) @@ -556,13 +613,13 @@ void Install(const std::filesystem::path &programPrefix, const std::string endpo } s << (programPrefix / "bin" / NATIVE_SERVICE).string() - << " /etc/pipedal/config /etc/pipedal/react -port " << endpointAddress << " -systemd -shutdownPort " << shutdownPort; + << " /etc/pipedal/config /etc/pipedal/react -port " << endpointAddress << " -systemd"; map["COMMAND"] = s.str(); } - WriteTemplateFile(map, std::filesystem::path("/etc/pipedal/config/template.service"), GetServiceFileName(NATIVE_SERVICE)); + WriteTemplateFile(map, GetServiceFileName(NATIVE_SERVICE)); - map["DESCRIPTION"] = "PiPedal Shutdown Service"; + map["DESCRIPTION"] = "PiPedal Admin Service"; { std::stringstream s; @@ -572,7 +629,7 @@ void Install(const std::filesystem::path &programPrefix, const std::string endpo map["COMMAND"] = s.str(); } - WriteTemplateFile(map, std::filesystem::path("/etc/pipedal/config/templateAdmin.service"), GetServiceFileName(ADMIN_SERVICE)); + WriteTemplateFile(map, GetServiceFileName(ADMIN_SERVICE)); // /usr/bin/pipedal_p2pd --config-file /etc/pipedal/config/template_p2pd.conf @@ -581,7 +638,7 @@ void Install(const std::filesystem::path &programPrefix, const std::string endpo << " --config-file " << PIPEDAL_P2PD_CONF_PATH); map["COMMAND"] = pipedal_p2pd_cmd; - WriteTemplateFile(map, std::filesystem::path("/etc/pipedal/config/templateP2pd.service"), GetServiceFileName(PIPEDAL_P2PD_SERVICE)); + WriteTemplateFile(map, GetServiceFileName(PIPEDAL_P2PD_SERVICE)); sysExec(SYSTEMCTL_BIN " daemon-reload"); @@ -638,8 +695,10 @@ static void PrintHelp() << HangingIndent() << " --restart\tRestart the pipedal services." << "\n" << "\n" - << HangingIndent() << " --enable-p2p [[] ]\t" + << HangingIndent() << " --enable-p2p [ [[] ] ]\t" << "Enable the P2P (Wi-Fi Direct) hotspot." << "\n\n" + << "With no additional arguments, the P2P channel is enabled with most-recent settings." + << "\n\n" << " is the 2-letter ISO-3166 country code for " "the country you are in. see below for further notes." << "\n\n" @@ -658,7 +717,7 @@ static void PrintHelp() << "device to PiPedal. (It's also available on the Settings page of PiPedal, if you have access to PiPedal UI on another device.)" << "\n\n" << "For best performance, the channel number should be 1, 6, or 11 (the Wifi Direct \"social\" channels). " - << "If you don't supply a channel number, PiPedal will select the least-busy of channels 1, 6 and 11." + << "Channel number defaults to 1." << "\n\n" << HangingIndent() << " --disable-p2p\tDisabled Wi-Fi Direct access." @@ -878,8 +937,10 @@ int main(int argc, char **argv) WifiDirectConfigSettings settings; settings.ParseArguments(argv); settings.valid_ = true; - settings.enable_ = false; + settings.enable_ = true; SetWifiDirectConfig(settings); + RestartService(true); // also have to retart web service so that it gets the correct device name. + } else if (disable_p2p) { diff --git a/src/ConfigUtil.cpp b/src/ConfigUtil.cpp new file mode 100644 index 0000000..d8d235b --- /dev/null +++ b/src/ConfigUtil.cpp @@ -0,0 +1,139 @@ +/* + * MIT License + * + * Copyright (c) 2022 Robin E. R. 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 "ConfigUtil.hpp" +#include +#include + +using namespace pipedal; +using namespace std; + +static std::string getKey(const std::string &line, size_t length) +{ + std::stringstream s; + size_t start = 0; + size_t end = length; + char c; + while (start < end && (line[start] == ' ' || line[start] == '\t')) + ++start; + + while (end > start && (line[end - 1] == ' ' || line[end - 1] == '\t')) + --end; + + return line.substr(start, end - start); +} +static std::string trim(const std::string &value) +{ + size_t start = 0; + size_t end = value.length(); + + while (start < end && ( value[start] == ' ' || value[start] == '\t')) + ++start; + + while (end > start && (value[end - 1] == ' ' || value[end - 1] == '\t')) + --end; + + return value.substr(start, end - start); +} +static std::string unquote(const std::string &value) +{ + if (value.length() > 2) + { + if ( + (value[0] == '\'' && value[value.length() - 1] == '\'') || (value[0] == '\"' && value[value.length() - 1] == '\"')) + { + char quotChar = value[0]; + + std::stringstream ss; + for (size_t i = 1; i < value.length() - 1; ++i) + { + char c = value[i]; + if (c == '\\') + { + ++i; + c = value[i]; + switch (c) + { + case 'r': + c = '\r'; + break; + case 'n': + c = '\n'; + break; + case 't': + c = '\t'; + break; + default: + break; + } + ss << c; + } + else + { + ss << c; + } + } + return ss.str(); + } + + } + return value; +} +bool ConfigUtil::GetConfigLine(const std::string & filePath,const std::string & key, std::string *pValue) +{ + + ifstream f; + f.open(filePath); + if (f.is_open()) + { + std::string line; + + while (true) + { + if (f.eof() || f.fail()) + { + break; + } + getline(f, line); + auto pos = line.find_first_of('#'); + if (pos != std::string::npos) + { + line = line.substr(0, pos); + } + pos = line.find_first_of('='); + if (pos != std::string::npos) + { + std::string thisKey = getKey(line, pos); + if (thisKey == key) + { + std::string value = line.substr(pos + 1); + + *pValue = unquote(trim(value)); + return true; + } + } + } + } + return false; +} \ No newline at end of file diff --git a/src/templateP2pd.service b/src/ConfigUtil.hpp similarity index 84% rename from src/templateP2pd.service rename to src/ConfigUtil.hpp index 7c8190c..388b292 100644 --- a/src/templateP2pd.service +++ b/src/ConfigUtil.hpp @@ -22,17 +22,13 @@ * SOFTWARE. */ -[Unit] -Description=PiPedal P2P Session Manager -After=network.target +#pragma once -[Service] -ExecStart=${COMMAND} -Type=notify -Restart=always -RestartSec=60 -WorkingDirectory=/var/pipedal +#include - -[Install] -WantedBy=multi-user.target +namespace pipedal { + class ConfigUtil { + public: + static bool GetConfigLine(const std::string & filePath,const std::string & key, std::string *pValue); + }; +} \ No newline at end of file diff --git a/src/DeviceIdFile.cpp b/src/DeviceIdFile.cpp new file mode 100644 index 0000000..b2789eb --- /dev/null +++ b/src/DeviceIdFile.cpp @@ -0,0 +1,76 @@ +/* + * MIT License + * + * Copyright (c) 2022 Robin E. R. 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 "DeviceIdFile.hpp" +#include +#include +#include +#include +#include + +using namespace pipedal; + +using namespace std; + + +const char DeviceIdFile::DEVICEID_FILE_NAME[] = "/etc/pipedal/config/device_uuid"; + +void DeviceIdFile::Load() +{ + ifstream f; + + f.open(DEVICEID_FILE_NAME); + if (!f.is_open()) + { + throw invalid_argument("Can't open file " + std::string(DEVICEID_FILE_NAME)); + } + + std::getline(f, uuid); + std::getline(f, deviceName); +} +void DeviceIdFile::Save() +{ + { + std::string path = DEVICEID_FILE_NAME; + ofstream f; + f.open(path, ios_base::trunc); + if (!f.is_open()) + { + throw invalid_argument("Can't write to file " + path); + } + + struct group *group_; + group_ = getgrnam("pipedal_d"); + if (group_ == nullptr) + { + throw logic_error("Group not found: pipedal_d"); + } + chown(path.c_str(),-1,group_->gr_gid); + chmod(path.c_str(), S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH); + + + f << uuid << endl; + f << deviceName << endl; + } +} \ No newline at end of file diff --git a/src/DeviceIdFile.hpp b/src/DeviceIdFile.hpp new file mode 100644 index 0000000..073b961 --- /dev/null +++ b/src/DeviceIdFile.hpp @@ -0,0 +1,41 @@ +/* + * MIT License + * + * Copyright (c) 2022 Robin E. R. 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 + +namespace pipedal { + class DeviceIdFile { + public: + static const char DEVICEID_FILE_NAME[]; + + void Load(); + void Save(); + + + std::string uuid; + std::string deviceName; + }; +} \ No newline at end of file diff --git a/src/JackHost.cpp b/src/JackHost.cpp index 466c906..98ab932 100644 --- a/src/JackHost.cpp +++ b/src/JackHost.cpp @@ -55,7 +55,7 @@ using namespace pipedal; #define JACK_SESSION_CALLBACKS 0 -#include "ShutdownClient.hpp" +#include "AdminClient.hpp" const double VU_UPDATE_RATE_S = 1.0 / 30; const double OVERRUN_GRACE_PERIOD_S = 15; @@ -1363,7 +1363,9 @@ private: this_->restarting = true; // this_->Close(); (JackServerConfiguration now does a service restart.) try { - ShutdownClient::SetJackServerConfiguration(jackServerSettings); + AdminClient client; + + client.SetJackServerConfiguration(jackServerSettings); //this_->Open(this_->channelSelection); this_->restarting = false; onComplete(true, ""); diff --git a/src/P2pConfigFiles.hpp b/src/P2pConfigFiles.hpp index a565a3d..6a29a4a 100644 --- a/src/P2pConfigFiles.hpp +++ b/src/P2pConfigFiles.hpp @@ -27,5 +27,5 @@ #define DEVICE_GUID_FILE "/etc/pipedal/config/device_uuid" -#define PIPEDAL_P2PD_CONF_PATH "/etc/pipedal/config/template_p2pd.conf" +#define PIPEDAL_P2PD_CONF_PATH "/etc/pipedal/config/pipedal_p2pd.conf" diff --git a/src/PiPedalConfiguration.hpp b/src/PiPedalConfiguration.hpp index 1b66730..862a647 100644 --- a/src/PiPedalConfiguration.hpp +++ b/src/PiPedalConfiguration.hpp @@ -106,6 +106,7 @@ public: } return socketServerAddress_.substr(0,pos); } + uint16_t GetSocketServerPort() const { try { size_t pos = this->socketServerAddress_.find_last_of(':'); diff --git a/src/PiPedalModel.cpp b/src/PiPedalModel.cpp index cca6764..580b52b 100644 --- a/src/PiPedalModel.cpp +++ b/src/PiPedalModel.cpp @@ -24,7 +24,7 @@ #include "Lv2Log.hpp" #include #include "PiPedalConfiguration.hpp" -#include "ShutdownClient.hpp" +#include "AdminClient.hpp" #include "SplitEffect.hpp" #include "CpuGovernor.hpp" @@ -78,7 +78,7 @@ void PiPedalModel::Close() PiPedalModel::~PiPedalModel() { try { - ShutdownClient::UnmonitorGovernor(); + adminClient.UnmonitorGovernor(); } catch (...) // noexcept here! { @@ -153,7 +153,7 @@ void PiPedalModel::Load(const PiPedalConfiguration &configuration) this->webRoot = configuration.GetWebRoot(); this->jackServerSettings.ReadJackConfiguration(); - ShutdownClient::MonitorGovernor(storage.GetGovernorSettings()); + adminClient.MonitorGovernor(storage.GetGovernorSettings()); // lv2Host.Load(configuration.GetLv2Path().c_str()); @@ -633,7 +633,7 @@ GovernorSettings PiPedalModel::GetGovernorSettings() void PiPedalModel::SetGovernorSettings(const std::string& governor) { std::lock_guard guard(mutex); - ShutdownClient::SetGovernorSettings(governor); + adminClient.SetGovernorSettings(governor); this->storage.SetGovernorSettings(governor); @@ -657,7 +657,7 @@ void PiPedalModel::SetWifiConfigSettings(const WifiConfigSettings &wifiConfigSet { std::lock_guard guard(mutex); - ShutdownClient::SetWifiConfig(wifiConfigSettings); + adminClient.SetWifiConfig(wifiConfigSettings); this->storage.SetWifiConfigSettings(wifiConfigSettings); @@ -1022,7 +1022,7 @@ void PiPedalModel::SetJackServerSettings(const JackServerSettings &jackServerSet { std::lock_guard guard(mutex); - if (!ShutdownClient::CanUseShutdownClient()) + if (!adminClient.CanUseShutdownClient()) { throw PiPedalException("Can't change server settings when running a debug server."); } @@ -1043,7 +1043,7 @@ void PiPedalModel::SetJackServerSettings(const JackServerSettings &jackServerSet } delete[] t; - if (ShutdownClient::CanUseShutdownClient()) + if (adminClient.CanUseShutdownClient()) { // save the current (edited) preset now in case the service shutdown isn't clean. diff --git a/src/PiPedalModel.hpp b/src/PiPedalModel.hpp index da71c2d..ffedd15 100644 --- a/src/PiPedalModel.hpp +++ b/src/PiPedalModel.hpp @@ -33,6 +33,10 @@ #include "PiPedalConfiguration.hpp" #include "JackServerSettings.hpp" #include "WifiConfigSettings.hpp" +#include "AdminClient.hpp" + + + namespace pipedal { @@ -68,6 +72,8 @@ private: PiPedalAlsaDevices alsaDevices; std::recursive_mutex mutex; + AdminClient adminClient; + class MidiListener { public: @@ -142,6 +148,10 @@ public: virtual ~PiPedalModel(); void Close(); + + AdminClient&GetAdminClient() { return adminClient; } + + void LoadLv2PluginInfo(const PiPedalConfiguration&configuration); void Load(const PiPedalConfiguration&configuration); diff --git a/src/PiPedalSocket.cpp b/src/PiPedalSocket.cpp index 40a8553..e870c6e 100644 --- a/src/PiPedalSocket.cpp +++ b/src/PiPedalSocket.cpp @@ -30,7 +30,7 @@ #include #include "Ipv6Helpers.hpp" -#include "ShutdownClient.hpp" +#include "AdminClient.hpp" #include "WifiConfigSettings.hpp" #include "WifiChannels.hpp" #include "SysExec.hpp" @@ -334,41 +334,16 @@ JSON_MAP_END() -static void requestShutdown(bool restart) -{ - if (ShutdownClient::CanUseShutdownClient()) - { - ShutdownClient::RequestShutdown(restart); - } - else { - // ONLY works when interactively logged in. - std::stringstream s; - s << "/usr/sbin/shutdown "; - if (restart) - { - s << "-r"; - } else - { - s << "-P"; - } - s << " now"; - - if (sysExec(s.str().c_str()) != EXIT_SUCCESS) - { - Lv2Log::error("shutdown failed."); - if (restart) { - throw new PiPedalStateException("Restart request failed."); - } else { - throw new PiPedalStateException("Shutdown request failed."); - } - } - } -} class PiPedalSocketHandler : public SocketHandler, public IPiPedalModelSubscriber { private: + AdminClient& GetAdminClient() const { + return model.GetAdminClient(); + } + void RequestShutdown(bool restart); + std::recursive_mutex writeMutex; PiPedalModel &model; static std::atomic nextClientId; @@ -819,7 +794,7 @@ public: else if (message == "setWifiConfigSettings") { WifiConfigSettings wifiConfigSettings; pReader->read(&wifiConfigSettings); - if (!ShutdownClient::CanUseShutdownClient()) + if (!GetAdminClient().CanUseShutdownClient()) { throw PiPedalException("Can't change server settings when running interactively."); } @@ -963,13 +938,13 @@ public: { PresetIndex newIndex; - requestShutdown(false); + RequestShutdown(false); this->Reply(replyTo,"shutdown"); } else if (message == "restart") { PresetIndex newIndex; - requestShutdown(true); + RequestShutdown(true); this->Reply(replyTo,"restart"); } else if (message == "deletePresetItem") @@ -1548,3 +1523,34 @@ std::shared_ptr pipedal::MakePiPedalSocketFactory(PiPedalModel & return std::make_shared(model); } + +void PiPedalSocketHandler::RequestShutdown(bool restart) +{ + if (GetAdminClient().CanUseShutdownClient()) + { + GetAdminClient().RequestShutdown(restart); + } + else { + // ONLY works when interactively logged in. + std::stringstream s; + s << "/usr/sbin/shutdown "; + if (restart) + { + s << "-r"; + } else + { + s << "-P"; + } + s << " now"; + + if (sysExec(s.str().c_str()) != EXIT_SUCCESS) + { + Lv2Log::error("shutdown failed."); + if (restart) { + throw new PiPedalStateException("Restart request failed."); + } else { + throw new PiPedalStateException("Shutdown request failed."); + } + } + } +} diff --git a/src/PrettyPrinter.hpp b/src/PrettyPrinter.hpp index 66d2169..6293823 100644 --- a/src/PrettyPrinter.hpp +++ b/src/PrettyPrinter.hpp @@ -50,7 +50,7 @@ namespace pipedal PrettyPrinter() : s(std::cout) { - lineBuffer.resize(lineWidth * 2); + lineBuffer.reserve(120); } std::ostream&Stream() const { return s;} diff --git a/src/SetWifiConfig.cpp b/src/SetWifiConfig.cpp index 3e4cf79..80a5d47 100644 --- a/src/SetWifiConfig.cpp +++ b/src/SetWifiConfig.cpp @@ -21,6 +21,7 @@ #include "ss.hpp" #include "P2pConfigFiles.hpp" +#include "DeviceIdFile.hpp" #include "SetWifiConfig.hpp" #include "PiPedalException.hpp" #include "SystemConfigFile.hpp" @@ -43,7 +44,10 @@ using namespace std; static char DNSMASQ_APD_PATH[] = "/etc/dnsmasq.d/30-pipedal-apd.conf"; -static char DNSMASQ_P2P_PATH[] = "/etc/dnsmasqdnsmaqsq.d/30-pipedal-p2p.conf"; + +static char DNSMASQ_P2P_SOURCE_PATH[] = "/etc/pipedal/config/templates/30-pipedal-p2p.conf"; + +static char DNSMASQ_P2P_PATH[] = "/etc/dnsmasq.d/30-pipedal-p2p.conf"; static bool IsApdInstalled() { @@ -54,53 +58,6 @@ static bool IsP2pInstalled() { } -uint32_t pipedal::ChannelToWifiFrequency(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); - if (size != t.length()) - { - throw invalid_argument("Expecting a number: '" + t + "'."); - } - return ChannelToWifiFrequency((uint32_t)lChannel); -} - -uint32_t pipedal::ChannelToWifiFrequency(uint32_t channel) -{ - // 2.4GHz. - if (channel >= 1 && channel <= 13) - { - return 2412 + 5*(channel-1); - } - if (channel == 14) - { - return 2484; - } - // 802.11y - if (channel >= 131 && channel < 137) - { - return 3660 + (channel-131)*5; - } - if (channel >= 32 && channel <= 68 && (channel & 1) == 0) - { - return 5160 + (channel-32)/2*10; - } - if (channel == 96) return 5480; - - if (channel >= 100 && channel <= 196) - { - return 5500 + (channel-100)/5; - } - throw invalid_argument(SS("Invalid channel: " << channel)); - -} static void restoreApdDhcpdConfFile() { @@ -140,6 +97,44 @@ static void restoreApdDhcpdConfFile() } } +static void restoreP2pDhcpdConfFile() +{ + // remove the interface wlan0 section. + std::filesystem::path dhcpcdConfig("/etc/dhcpcd.conf"); + if (std::filesystem::exists(dhcpcdConfig)) + { + SystemConfigFile dhcpcd(dhcpcdConfig); + + int line = dhcpcd.GetLineThatStartsWith("hostname"); + std::string hostNameLine; + hostNameLine = "hostname"; // the default value. + + if (line != -1) + { + dhcpcd.SetLineValue(line, hostNameLine); + } + + line = dhcpcd.GetLineNumber("interface p2p-wlan0-0"); + if (line != -1) + { + dhcpcd.EraseLine(line); + while (line < dhcpcd.GetLineCount()) + { + const std::string &lineValue = dhcpcd.GetLineValue(line); + if (lineValue.length() > 0 && (lineValue[0] == ' ' || lineValue[0] == '\t')) + { + dhcpcd.EraseLine(line); + } + else + { + break; + } + } + } + dhcpcd.Save(dhcpcdConfig); + } +} + static void restoreApdDnsmasqConfFile() { @@ -149,6 +144,14 @@ static void restoreApdDnsmasqConfFile() std::filesystem::remove(path); } } +static void restoreP2pDnsmasqConfFile() +{ + std::filesystem::path path(DNSMASQ_P2P_PATH); + if (std::filesystem::exists(path)) + { + std::filesystem::remove(path); + } +} static void UninstallHostApd() { @@ -324,7 +327,7 @@ void pipedal::SetWifiConfig(const WifiConfigSettings &settings) sysExec("rfkill unblock wlan"); sysExec(SYSTEMCTL_BIN " daemon-reload"); - sysExec(SYSTEMCTL_BIN " mask wpa_supplicant"); +// sysExec(SYSTEMCTL_BIN " mask wpa_supplicant"); sysExec(SYSTEMCTL_BIN " stop wpa_supplicant"); sysExec(SYSTEMCTL_BIN " unmask hostapd"); @@ -339,7 +342,7 @@ void pipedal::SetWifiConfig(const WifiConfigSettings &settings) sysExec(SYSTEMCTL_BIN " enable hostapd"); sysExec(SYSTEMCTL_BIN " stop wpa_supplicant"); - sysExec(SYSTEMCTL_BIN " mask wpa_supplicant"); +// sysExec(SYSTEMCTL_BIN " mask wpa_supplicant"); sysExec(SYSTEMCTL_BIN " restart dnsmasq"); sysExec(SYSTEMCTL_BIN " enable dnsmasq"); @@ -355,9 +358,9 @@ p2p configuration: dnsmaqsq.d/30-pipedal-p2p.conf: interface=p2p-wlan0-0 - dhcp-range=p2p-wlan0,172.24.0.3,172.24.0.127,1h + dhcp-range=p2p-wlan0,173.24.0.3,172.23.0.127,1h domain=local - address=/pipedal.local/172.24.0.1 # do this through DNS-SD? + address=/pipedal.local/172.23.0.2 except-interface=eth0 except-interface=wlan0 @@ -368,7 +371,7 @@ dhcpcd.conf: ====== interface p2p-wlan0-0 static ip_address=172.24.0.1/16 - domain_name_server=172.24.0.1 + domain_name_servers=172.24.0.1 Watch resolv.conf to make sure we don't lose DNS servers. ************************************************************************************/ @@ -378,14 +381,14 @@ void UninstallP2p() { if (IsP2pInstalled()) { - sysExec(SYSTEMCTL_BIN " stop pipidal_p2pd"); - sysExec(SYSTEMCTL_BIN " disable pipidal_p2pd"); + sysExec(SYSTEMCTL_BIN " stop pipedal_p2pd"); + sysExec(SYSTEMCTL_BIN " disable pipedal_p2pd"); sysExec(SYSTEMCTL_BIN " stop dnsmasq"); sysExec(SYSTEMCTL_BIN " disable dnsmasq"); - restoreApdDhcpdConfFile(); - restoreApdDnsmasqConfFile(); + restoreP2pDhcpdConfFile(); + restoreP2pDnsmasqConfFile(); sysExec(SYSTEMCTL_BIN " enable wpa_supplicant"); sysExec(SYSTEMCTL_BIN " start wpa_supplicant"); @@ -395,9 +398,11 @@ void UninstallP2p() void pipedal::SetWifiDirectConfig(const WifiDirectConfigSettings &settings) { + char band; if (!settings.enable_) { + cout << "Disabling P2P" << endl; UninstallP2p(); } else @@ -419,11 +424,21 @@ void pipedal::SetWifiDirectConfig(const WifiDirectConfigSettings &settings) map["INSTANCE_ID_FILE"] = DEVICE_GUID_FILE; - WriteTemplateFile(map, std::filesystem::path("/etc/pipedal/config/template_p2pd.conf"), PIPEDAL_P2PD_CONF_PATH); + WriteTemplateFile(map, PIPEDAL_P2PD_CONF_PATH); } + // ******************* device_uuid + + DeviceIdFile deviceIdFile; + deviceIdFile.Load(); + deviceIdFile.deviceName = settings.hotspotName_; + deviceIdFile.Save(); // ******************** dsnmasq ****** + + std::filesystem::copy_file(DNSMASQ_P2P_SOURCE_PATH, DNSMASQ_P2P_PATH); + +#if JUNK // copy into place instaead. std::filesystem::path dnsMasqPath(DNSMASQ_P2P_PATH); SystemConfigFile dnsMasq; @@ -447,15 +462,24 @@ void pipedal::SetWifiDirectConfig(const WifiDirectConfigSettings &settings) dnsMasq.Set("interface", "p2p-wlan0-0", "Name of the Wi-Fi interface"); - dnsMasq.Set("listen-address", APD_IP_ADDRESS); + dnsMasq.Set("listen-address", P2P_IP_ADDRESS); dnsMasq.SetDefault("dhcp-range", P2P_IP_PREFIX ".3," P2P_IP_PREFIX ".127,1h", "dhcp configuration"); dnsMasq.SetDefault("domain", "local"); std::string strAddress = SS("/" << settings.mdnsName_ << ".local/" P2P_IP_PREFIX << ".2"); dnsMasq.Set("address", strAddress); + + dnsMasq.EraseLine("except-interface=eth0"); + dnsMasq.EraseLine("except-interface=wlan0"); + dnsMasq.EraseLine("except-interface=lo"); + dnsMasq.AppendLine("except-interface=eth0"); dnsMasq.AppendLine("except-interface=wlan0"); + dnsMasq.AppendLine("except-interface=lo"); + dnsMasq.Save(dnsMasqPath); + +#endif // ****** dhcpd.conf ***/ std::filesystem::path dhcpcdConfig("/etc/dhcpcd.conf"); @@ -518,34 +542,36 @@ void pipedal::SetWifiDirectConfig(const WifiDirectConfigSettings &settings) dhcpcd.InsertLine(line++, "interface p2p-wlan0-0"); dhcpcd.InsertLine(line++, " static ip_address=" P2P_INTERFACE_ADDR); - dhcpcd.InsertLine(line++, " domain_name_servier=" P2P_IP_ADDRESS); + dhcpcd.InsertLine(line++, " domain_name_servers=" P2P_IP_ADDRESS); dhcpcd.Save(dhcpcdConfig); } - // ***** save the config files *** - - dnsMasq.Save(dnsMasqPath); // **************** start services ************ + + cout << "Starting services." << endl; sysExec("rfkill unblock wlan"); + sysExec(SYSTEMCTL_BIN " daemon-reload"); - sysExec(SYSTEMCTL_BIN " restart wpa_supplicant"); + + sysExec(SYSTEMCTL_BIN " restart dnsmasq"); + sysExec(SYSTEMCTL_BIN " enable dnsmasq"); + + sysExec(SYSTEMCTL_BIN " restart dhcpcd"); + sysExec(SYSTEMCTL_BIN " unmask pipedal_p2pd"); + cout << "."; cout.flush(); if (sysExec(SYSTEMCTL_BIN " restart pipedal_p2pd") != 0) { throw PiPedalException("Unable to start the pipedal_p2pd."); } - if (sysExec("systemctl is-active --quiet pipedal_p2pd") != 0) - { - throw PiPedalException("Unable to start the access point."); - } + sysExec(SYSTEMCTL_BIN " enable pipedal_p2pd"); - sysExec(SYSTEMCTL_BIN " restart dnsmasq"); - sysExec(SYSTEMCTL_BIN " enable dnsmasq"); } + cout << "Ready." << endl; } void pipedal::OnWifiUninstall() diff --git a/src/SetWifiConfig.hpp b/src/SetWifiConfig.hpp index 4dd2ec5..3c3f09c 100644 --- a/src/SetWifiConfig.hpp +++ b/src/SetWifiConfig.hpp @@ -23,8 +23,6 @@ namespace pipedal { - uint32_t ChannelToWifiFrequency(const std::string &channel); - uint32_t ChannelToWifiFrequency(uint32_t channel); void SetWifiConfig(const WifiConfigSettings&settings); void SetWifiDirectConfig(const WifiDirectConfigSettings&settings); diff --git a/src/ShutdownClient.cpp b/src/ShutdownClient.cpp deleted file mode 100644 index 5de4b36..0000000 --- a/src/ShutdownClient.cpp +++ /dev/null @@ -1,235 +0,0 @@ -// 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 "pch.h" -#include "ShutdownClient.hpp" -#include "PiPedalException.hpp" -#include -#include -#include -#include "Lv2Log.hpp" -#include -#include -#include -#include "Ipv6Helpers.hpp" - - -using namespace pipedal; - - - -extern uint16_t g_ShutdownPort; // set in main.cpp - - -bool ShutdownClient::CanUseShutdownClient() -{ - return g_ShutdownPort != 0; -} - - - - -bool ShutdownClient::RequestShutdown(bool restart) -{ - const char*message = restart? "restart\n": "shutdown\n"; - return WriteMessage(message); -} - -bool ShutdownClient::WriteMessage(const char*message) { - - uint16_t shutdownServerPort = g_ShutdownPort; - if (g_ShutdownPort == 0) - { - throw PiPedalException("No shutdown server available."); - } - - struct sockaddr_in serv_addr; - - memset(&serv_addr,0,sizeof(serv_addr)); - serv_addr.sin_family = AF_INET; - serv_addr.sin_port = htons(shutdownServerPort); - if (inet_pton(AF_INET,"127.0.0.1",&serv_addr.sin_addr) <= 0) - { - Lv2Log::error("Failed to parse socket address."); - return false; - } - int sock = socket(AF_INET,SOCK_STREAM,0); - if (sock < 0) - { - Lv2Log::error("Can't create socket."); - return false; - } - - if (connect(sock,(struct sockaddr*)&serv_addr,sizeof(serv_addr)) < 0) - { - std::stringstream s; - s << "Connect to 127.0.0.1:" << shutdownServerPort << " failed."; - Lv2Log::error(s.str()); - close(sock); - return false; - } - int length = strlen(message); - const char *p = message; - - bool success = true; - while (length != 0) { - auto nWritten = write(sock,p,length); - if (nWritten < 0) - { - success = false; - Lv2Log::error("Shutdown server write failed."); - close(sock); - return false; - } - p += nWritten; - length -= nWritten; - } - shutdown(sock, SHUT_WR); - char responseBuffer[1024]; - ssize_t available = sizeof(responseBuffer); - char *pWrite = responseBuffer; - char*pEndOfLine = responseBuffer; - - bool eolFound = false; - while (!eolFound) - { - ssize_t nRead = read(sock,pWrite,available); - if (nRead == -1) - { - *pWrite = 0; - break; - } - if (nRead <= 0) { - Lv2Log::error("Failed to read shutdown server response."); - close(sock); - return false; - - } - pWrite += nRead; - available -= nRead; - if (available == 0) - { - Lv2Log::error("Bad response from shutdown server."); - close(sock); - return false; - } - - while (pEndOfLine != pWrite) - { - if (*pEndOfLine++ == '\n') - { - eolFound = true; - pEndOfLine[-1] = '\0'; - break; - } - } - } - int response = atoi(responseBuffer); - close(sock); - if (response == -2) // throw exception with message - { - const char *p = responseBuffer; - while (*p != ' ' && *p != '\n' && *p != '\0') - { - ++p; - } - if (*p != 0) ++p; - throw PiPedalStateException(p); - } - return response == 0; -} - -bool ShutdownClient::SetJackServerConfiguration(const JackServerSettings & jackServerSettings) -{ - std::stringstream s; - s << "setJackConfiguration "; - - json_writer writer(s,true); - writer.write(jackServerSettings); - s << std::endl; - return WriteMessage(s.str().c_str()); -} - - -void ShutdownClient::SetWifiConfig(const WifiConfigSettings & settings) -{ - if (!CanUseShutdownClient()) - { - throw PiPedalException("Can't perform this operation when debugging."); - } - std::stringstream cmd; - cmd << "WifiConfigSettings "; - json_writer writer(cmd,true); - writer.write(settings); - cmd << '\n'; - bool result = WriteMessage(cmd.str().c_str()); - if (!result) { // unexpected. Should throw exception on failure. - throw PiPedalException("Operation failed."); - } -} - -void ShutdownClient::SetGovernorSettings(const std::string & settings) -{ - if (!CanUseShutdownClient()) - { - throw PiPedalException("Can't use ShutdownClient when running interactively."); - } - std::stringstream cmd; - cmd << "GovernorSettings "; - json_writer writer(cmd,true); - writer.write(settings); - cmd << '\n'; - bool result = WriteMessage(cmd.str().c_str()); - if (!result) { // unexpected. Should throw exception on failure. - throw PiPedalException("Operation failed."); - } -} - -void ShutdownClient::MonitorGovernor(const std::string &governor) -{ - if (!CanUseShutdownClient()) - { - return; - } - std::stringstream cmd; - cmd << "MonitorGovernor "; - json_writer writer(cmd,true); - writer.write(governor); - cmd << '\n'; - bool result = WriteMessage(cmd.str().c_str()); - if (!result) { // unexpected. Should throw exception on failure. - throw PiPedalException("Operation failed."); - } - -} -void ShutdownClient::UnmonitorGovernor() -{ - if (!CanUseShutdownClient()) - { - return; - } - std::stringstream cmd; - cmd << "UnmonitorGovernor"; - cmd << '\n'; - bool result = WriteMessage(cmd.str().c_str()); - if (!result) { // unexpected. Should throw exception on failure. - throw PiPedalException("Operation failed."); - } - -} diff --git a/src/UnixSocket.cpp b/src/UnixSocket.cpp new file mode 100644 index 0000000..96c36d3 --- /dev/null +++ b/src/UnixSocket.cpp @@ -0,0 +1,253 @@ +/* + * MIT License + * + * Copyright (c) 2022 Robin E. R. 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 "UnixSocket.hpp" +#include +#include +#include +#include +#include "gsl/util" + +#include +#include +#include +#include +#include +#include +#include "ss.hpp" + +using namespace std; +namespace pipedal +{ + namespace detail + { + class UnixSocketData + { + public: + UnixSocketData() + { + memset(&localAddress, 0, sizeof(localAddress)); + memset(&remoteAddress, 0, sizeof(remoteAddress)); + localAddress.sun_family = AF_UNIX; + remoteAddress.sun_family = AF_UNIX; + } + int socket = -1; + struct sockaddr_un localAddress; + struct sockaddr_un remoteAddress; + }; + } +} +using namespace pipedal; +using namespace pipedal::detail; +using namespace gsl; + + +bool UnixSocket::IsOpen() const { return data->socket != -1; } + +UnixSocketException::UnixSocketException(int errno_) + : what_(strerror(errno_)), errno_(errno_) +{ +} + +UnixSocketException::UnixSocketException(const std::string what_, int errno_) + : what_(what_ + " " + strerror(errno_) + ")"), errno_(errno_) +{ +} +UnixSocketException::UnixSocketException(const std::string what_) + : what_(what_), errno_(0) +{ +} + +void UnixSocket::Bind(const std::string &socketName, const std::string &permissionGroup) +{ + if (socketName.length() >= sizeof(data->localAddress.sun_path) - 1) + { + throw UnixSocketException("Socket name too long.", E2BIG); + } + int s = socket(PF_UNIX, SOCK_DGRAM, 0); + auto socket_cleanup = finally([&]() { + if (s != -1) + { + close(s); + s = -1; + } + }); + strcpy(data->localAddress.sun_path, socketName.c_str()); + + for (int retry = 0; /**/; ++retry) + { + int err = bind(s, (struct sockaddr *)&data->localAddress, sizeof(data->localAddress)); + + if (err < 0) + { + if (retry != 2) + { + unlink(data->localAddress.sun_path); + continue; + } + throw UnixSocketException("Can't bind socket " + socketName, errno); + } + else + { + break; + } + } + + if (permissionGroup != "") + { + gid_t gid; + struct group *group_; + group_ = getgrnam(permissionGroup.c_str()); + if (group_ == nullptr) + { + throw UnixSocketException("Group not found."); + } + gid = group_->gr_gid; + + chown(data->localAddress.sun_path, -1, gid); + chmod(data->localAddress.sun_path, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); + } + + data->socket = s; + s = -1; +} + +UnixSocket::UnixSocket() +{ + data = new UnixSocketData(); +} + +void UnixSocket::Close() +{ + if (data->socket != -1) + { + close(data->socket); + unlink(data->localAddress.sun_path); + } +} +UnixSocket::~UnixSocket() +{ + Close(); + delete data; +} + +static std::atomic instanceId; + +void UnixSocket::Connect(const std::string &remoteSocketName, const std::string &permissionGroup) +{ + + const std::string localSocketName = + SS("/tmp/ppadmin-" << getpid() << "-" << (instanceId++)); + + if (localSocketName.length() >= sizeof(data->localAddress.sun_path) - 1) + { + throw UnixSocketException("Local socket path is too long. ", E2BIG); + } + strcpy(data->localAddress.sun_path, localSocketName.c_str()); + + int s = socket(PF_UNIX, SOCK_DGRAM, 0); + auto socket_cleanup = finally([&]() { + if (s != -1) + { + close(s); + s = -1; + unlink(data->localAddress.sun_path); + } + }); + + int e = bind(s, (sockaddr *)&data->localAddress, sizeof(data->localAddress)); + + if (permissionGroup.length() != 0) + { + struct group *g = getgrnam(permissionGroup.c_str()); + if (g == nullptr) + { + throw UnixSocketException("Group not found"); + } + gid_t gid = g->gr_gid; + + chown(data->localAddress.sun_path, -1, gid); + chmod(data->localAddress.sun_path, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); + } + + strcpy(data->remoteAddress.sun_path, remoteSocketName.c_str()); + e = connect(s, (sockaddr *)&data->remoteAddress, sizeof(data->remoteAddress)); + if (e < 0) + { + throw UnixSocketException("Can't connect to socket " + remoteSocketName, errno); + } + data->socket = s; + s = -1; +} + +size_t UnixSocket::Send(const void *buffer, size_t length) +{ + ssize_t size = send(data->socket, buffer, length, 0); + if (size < 0) + { + throw UnixSocketException(errno); + } + return (size_t)size; +} + +size_t UnixSocket::Receive(void *buffer, size_t length) +{ + ssize_t received = recv(data->socket, buffer, length, 0); + if (received < 0) + { + throw UnixSocketException(errno); + } + return (size_t)received; +} + +size_t UnixSocket::ReceiveFrom(void *buffer, size_t length, std::string *pFrom) +{ + sockaddr_un sockAddr; + socklen_t sockAddrLen = sizeof(sockAddr); + + ssize_t received = recvfrom(data->socket, buffer, length, 0, (sockaddr *)&sockAddr, &sockAddrLen); + if (received < 0) + { + throw UnixSocketException(errno); + } + *pFrom = std::string(sockAddr.sun_path); + return (size_t)received; +} + +size_t UnixSocket::SendTo(const void *buffer, size_t length, const std::string &toAddress) +{ + sockaddr_un sockAddr; + if (toAddress.length() >= sizeof(sockAddr.sun_path) - 1) + { + throw UnixSocketException("Address too long.", E2BIG); + } + strcpy(sockAddr.sun_path, toAddress.c_str()); + sockAddr.sun_family = AF_UNIX; + + ssize_t size = sendto(data->socket, buffer, length, 0, (sockaddr *)&sockAddr, sizeof(sockaddr_un)); + if (size < 0) + { + throw UnixSocketException(errno); + } + return (size_t)size; +} diff --git a/src/UnixSocket.hpp b/src/UnixSocket.hpp new file mode 100644 index 0000000..30bac95 --- /dev/null +++ b/src/UnixSocket.hpp @@ -0,0 +1,96 @@ +/* + * MIT License + * + * Copyright (c) 2022 Robin E. R. 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 + +struct sockaddr_un; + +namespace pipedal { + + class UnixSocketException : public std::exception { + public: + UnixSocketException(int errno_); + UnixSocketException(const std::string what_,int errno_); + UnixSocketException(const std::string what_); + + const char*what() const noexcept { return what_.c_str(); } + + int get_errno() const { return errno_; } + private: + int errno_; + std::string what_; + + }; + + namespace detail { + class UnixSocketData; + } + class UnixSocket { + public: + using sockaddr_t = sockaddr_un; + + UnixSocket(); + ~UnixSocket(); + + bool IsOpen() const; + + /** + * @brief Prepare a server socket to recive data. + * + * permissionGroup controls permissions on the socekt. + * Proceses that wish to read or write to the socket must belong to permissionGroup. + * @param socketName Server socket name. + * @param permissionGroup The group which is allowed to connect to this socket. + */ + void Bind(const std::string&socketName,const std::string&permissionGroup); + + /** + * @brief Prepare a socket to write to a server socket. + * + * The optional groupPermission is applied to the client socket to allow + * server process that are not highly privileged to write replies. The + * server process must belong the group named by permissionGroup. + * + * @param remoteSocketName The name of the server socket. + * @param permissionGroup` The permission to appy to the client socket. + */ + void Connect(const std::string&remoteSocketName, const std::string &groupPermission = ""); + + void Close(); + + + size_t Send(const void*buffer, size_t length); + size_t Receive(void *buffer, size_t length); + + size_t ReceiveFrom(void*buffer, size_t length,std::string*fromAddress); + size_t SendTo(const void*buffer, size_t length,const std::string&toAddress); + + + private: + detail::UnixSocketData *data; + }; + +}; \ No newline at end of file diff --git a/src/UnixSocketTest.cpp b/src/UnixSocketTest.cpp new file mode 100644 index 0000000..c1540c5 --- /dev/null +++ b/src/UnixSocketTest.cpp @@ -0,0 +1,71 @@ +/* + * MIT License + * + * Copyright (c) 2022 Robin E. R. 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 "catch.hpp" +#include "UnixSocket.hpp" +#include "ss.hpp" +#include +#include + +using namespace pipedal; + + +TEST_CASE( "Unix Socket Test", "[unix_socket_test][dev]" ) { + UnixSocket serverSocket; + UnixSocket clientSocket; + + std::string serverSocketName = SS("/tmp/ServerSocketTest-Server-" << getpid() << "-0"); + + char buffer[1024]; + serverSocket.Bind(serverSocketName,"pipedal_d"); + clientSocket.Connect(serverSocketName,"pipedal_d"); + + std::string testData = "abc123"; + clientSocket.Send(testData.c_str(),testData.length()); + + size_t length = serverSocket.Receive(buffer,sizeof(buffer)); + assert(length == testData.length()); + + buffer[length] = 0; + assert(testData == buffer); + + clientSocket.Send(testData.c_str(),testData.length()); + + std::string fromAddress; + length = serverSocket.ReceiveFrom(buffer,sizeof(buffer),&fromAddress); + + serverSocket.SendTo(buffer,length,fromAddress); + + size_t length2 = clientSocket.Receive(buffer,length); + assert(length == length2); + buffer[length2] = 0; + assert(testData == std::string(buffer)); + + + + + + + +} \ No newline at end of file diff --git a/src/WifiConfigSettings.cpp b/src/WifiConfigSettings.cpp index ea90587..cdb886b 100644 --- a/src/WifiConfigSettings.cpp +++ b/src/WifiConfigSettings.cpp @@ -44,11 +44,71 @@ bool WifiConfigSettings::ValidateCountryCode(const std::string&value) } -bool WifiConfigSettings::ValidateChannel(const std::string&arguments) +uint32_t pipedal::ChannelToWifiFrequency(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); + if (size != t.length()) + { + throw invalid_argument("Expecting a number: '" + t + "'."); + } + return ChannelToWifiFrequency((uint32_t)lChannel); +} + +uint32_t pipedal::ChannelToWifiFrequency(uint32_t channel) +{ + if (channel > 1000) // must be a frequency. + { + return channel; + } + // 2.4GHz. + if (channel >= 1 && channel <= 13) + { + return 2412 + 5*(channel-1); + } + if (channel == 14) + { + return 2484; + } + // 802.11y + if (channel >= 131 && channel < 137) + { + return 3660 + (channel-131)*5; + } + if (channel >= 32 && channel <= 68 && (channel & 1) == 0) + { + return 5160 + (channel-32)/2*10; + } + if (channel == 96) return 5480; + + if (channel >= 100 && channel <= 196) + { + return 5500 + (channel-100)/5; + } + throw invalid_argument(SS("Invalid channel: " << channel)); + +} + + +bool WifiConfigSettings::ValidateChannel(const std::string&value) { // 1) frequency in khz. // 2) unadorned channel number 1, 2,3 &c. // 3) With band annotated: g1, a51. + try { + ChannelToWifiFrequency(value); + return true; + } catch (const std::string&/**/) + { + return false; + } // come back to this. return true; } diff --git a/src/WifiConfigSettings.hpp b/src/WifiConfigSettings.hpp index 2559ba1..aefde44 100644 --- a/src/WifiConfigSettings.hpp +++ b/src/WifiConfigSettings.hpp @@ -23,6 +23,10 @@ namespace pipedal { + + uint32_t ChannelToWifiFrequency(const std::string &channel); + uint32_t ChannelToWifiFrequency(uint32_t channel); + class WifiConfigSettings { public: bool valid_ = false; diff --git a/src/WifiDirectConfigSettings.cpp b/src/WifiDirectConfigSettings.cpp index e9defe9..bb9cb55 100644 --- a/src/WifiDirectConfigSettings.cpp +++ b/src/WifiDirectConfigSettings.cpp @@ -22,6 +22,9 @@ #include "WifiConfigSettings.hpp" #include #include +#include "ConfigUtil.hpp" +#include "DeviceIdFile.hpp" + using namespace pipedal; using namespace std; @@ -71,17 +74,48 @@ static void ValidatePin(const std::string&pin) void WifiDirectConfigSettings::ParseArguments(const std::vector &arguments) { this->valid_ = false; - if (arguments.size() < 2 || arguments.size() > 4) - { - throw invalid_argument("Incorrect number of arguments supplied"); - } this->enable_ = true; - this->countryCode_ = arguments[0]; - this->hotspotName_ = arguments[1]; - this->pin_ = arguments.size() >= 3 ? arguments[2] : ""; - this->channel_ = arguments.size() >= 4? arguments[3]: ""; + if (arguments.size() == 0) + { + if (!ConfigUtil::GetConfigLine("/etc/pipedal/config/pipedal_p2pd.conf","country_code",&this->countryCode_)) + { + throw invalid_argument("Default value for country code not found."); + } + if (!ConfigUtil::GetConfigLine("/etc/pipedal/config/pipedal_p2pd.conf","p2p_pin",&this->pin_)) + { + throw invalid_argument("Default value for pin not found."); + } + if (!ConfigUtil::GetConfigLine("/etc/pipedal/config/pipedal_p2pd.conf","wifiGroupFrequency",&this->channel_)) + { + throw invalid_argument("Default value for pin not found."); + } + try { + DeviceIdFile deviceIdFile; + deviceIdFile.Load(); + if (deviceIdFile.deviceName == "") + { + throw std::invalid_argument(""); + } + this->hotspotName_ = deviceIdFile.deviceName; + } catch (const std::exception &e) + { + throw std::invalid_argument("Default value for device name not found."); + } + } else { + if (arguments.size() < 2 || arguments.size() > 4) + { + throw invalid_argument("Incorrect number of arguments supplied"); + } + this->countryCode_ = arguments[0]; + this->hotspotName_ = arguments[1]; + this->pin_ = arguments.size() >= 3 ? arguments[2] : ""; + this->channel_ = arguments.size() >= 4? arguments[3]: ""; + } + if (this->channel_ == "") { + this->channel_ = "1"; + } size_t maxLength = 32-string("DIRECT-XX-").length(); if (hotspotName_.size() >= maxLength) { throw invalid_argument("Device name is too long."); } if (hotspotName_.size() < 1) { throw invalid_argument("Device name is too short."); } diff --git a/src/WriteTemplateFile.cpp b/src/WriteTemplateFile.cpp index bc4bf8e..942ca67 100644 --- a/src/WriteTemplateFile.cpp +++ b/src/WriteTemplateFile.cpp @@ -17,48 +17,69 @@ // 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 "pch.h" + #include "WriteTemplateFile.hpp" #include #include "PiPedalException.hpp" using namespace pipedal; +void pipedal::WriteTemplateFile( + const std::map &map, + const std::filesystem::path &outputFile) +{ + std::string inputFile = "/etc/pipedal/config/templates/" + outputFile.filename().string() + ".template"; + WriteTemplateFile(map, inputFile,outputFile); +} void pipedal::WriteTemplateFile( - const std::map &map, - std::filesystem::path inputFile, - std::filesystem::path outputFile) + const std::map &map, + const std::filesystem::path &inputFile, + const std::filesystem::path &outputFile) { std::ofstream out(outputFile); std::ifstream in(inputFile); + if (!in.is_open()) + { + throw PiPedalArgumentException("File not found: " + inputFile.string()); + } + while (in.peek() != -1) { char c = in.get(); if (c == '$') { - if (in.peek() == '{') { + if (in.peek() == '{') + { in.get(); std::stringstream sName; - try { + try + { while ((c = in.get()) != '}') { sName.put(c); } - } catch (const std::exception&) + } + catch (const std::exception &) { throw PiPedalException("Unexpected end of file. Expecting '}'."); } std::string s = map.at(sName.str()); out << s; - } else if (in.peek() == '$') + } + else if (in.peek() == '$') { out.put(in.get()); - } else { + } + else + { out.put(c); } - } else { + } + else + { out.put(c); } } diff --git a/src/WriteTemplateFile.hpp b/src/WriteTemplateFile.hpp index 502c3ef..a488e0b 100644 --- a/src/WriteTemplateFile.hpp +++ b/src/WriteTemplateFile.hpp @@ -25,6 +25,21 @@ namespace pipedal { void WriteTemplateFile( const std::map &map, - std::filesystem::path inputFile, - std::filesystem::path outputFile); + const std::filesystem::path& inputFile, + const std::filesystem::path& outputFile); + + /** + * @brief Write template file. + * + * The input file is + * + * /etc/pipedal/config/template/.template + * + * @param map Dictionary of values to substitute. + * @param outputFile The full path of the output file. + */ + void WriteTemplateFile( + const std::map &map, + const std::filesystem::path &outputFile); + } \ No newline at end of file diff --git a/src/dbus/bluez.h b/src/dbus/bluez.h deleted file mode 100644 index abe417f..0000000 --- a/src/dbus/bluez.h +++ /dev/null @@ -1,28 +0,0 @@ -#include "bluez_proxy.h" - - -namespace org { namespace bluez { - - -using BleDevice_Interfaces = sdbus::ProxyInterfaces< - org::bluez::LEAdvertisingManager1_proxy, - org::bluez::NetworkServer1_proxy, - org::bluez::GattManager1_proxy - >; - -class BleDeviceProxy : public BleDevice_Interfaces -{ -public: - BleDeviceProxy(std::string devicePath) - : BleDevice_Interfaces("org.bluez",std::move(devicePath)) - { - registerProxy(); - } - ~BleDeviceProxy() - { - unregisterProxy(); - } -}; - - -}}; // namesapce \ No newline at end of file diff --git a/src/dbus/bluez.xml b/src/dbus/bluez.xml deleted file mode 100644 index 9703cbf..0000000 --- a/src/dbus/bluez.xml +++ /dev/null @@ -1,276 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/dbus/bluez_adaptor.h b/src/dbus/bluez_adaptor.h deleted file mode 100644 index f4c6638..0000000 --- a/src/dbus/bluez_adaptor.h +++ /dev/null @@ -1,428 +0,0 @@ - -/* - * This file was automatically generated by sdbus-c++-xml2cpp; DO NOT EDIT! - */ - -#ifndef __sdbuscpp___home_pi_src_pipedal_src_dbus_bluez_adaptor_h__adaptor__H__ -#define __sdbuscpp___home_pi_src_pipedal_src_dbus_bluez_adaptor_h__adaptor__H__ - -#include -#include -#include - -namespace org { -namespace bluez { - -class GattService1_adaptor -{ -public: - static constexpr const char* INTERFACE_NAME = "org.bluez.GattService1"; - -protected: - GattService1_adaptor(sdbus::IObject& object) - : object_(object) - { - object_.registerProperty("UUID").onInterface(INTERFACE_NAME).withGetter([this](){ return this->UUID(); }); - object_.registerProperty("Primary").onInterface(INTERFACE_NAME).withGetter([this](){ return this->Primary(); }); - object_.registerProperty("Characteristics").onInterface(INTERFACE_NAME).withGetter([this](){ return this->Characteristics(); }); - } - - ~GattService1_adaptor() = default; - -private: - virtual std::string UUID() = 0; - virtual bool Primary() = 0; - virtual std::vector Characteristics() = 0; - -private: - sdbus::IObject& object_; -}; - -}} // namespaces - -namespace org { -namespace bluez { - -class GattCharacteristic1_adaptor -{ -public: - static constexpr const char* INTERFACE_NAME = "org.bluez.GattCharacteristic1"; - -protected: - GattCharacteristic1_adaptor(sdbus::IObject& object) - : object_(object) - { - object_.registerMethod("WriteValue").onInterface(INTERFACE_NAME).withInputParamNames("value").implementedAs([this](const std::vector& value){ return this->WriteValue(value); }); - object_.registerMethod("StartNotify").onInterface(INTERFACE_NAME).implementedAs([this](){ return this->StartNotify(); }); - object_.registerMethod("StopNotify").onInterface(INTERFACE_NAME).implementedAs([this](){ return this->StopNotify(); }); - object_.registerProperty("UUID").onInterface(INTERFACE_NAME).withGetter([this](){ return this->UUID(); }); - object_.registerProperty("Service").onInterface(INTERFACE_NAME).withGetter([this](){ return this->Service(); }); - object_.registerProperty("Value").onInterface(INTERFACE_NAME).withGetter([this](){ return this->Value(); }); - object_.registerProperty("Notifying").onInterface(INTERFACE_NAME).withGetter([this](){ return this->Notifying(); }); - object_.registerProperty("Flags").onInterface(INTERFACE_NAME).withGetter([this](){ return this->Flags(); }); - object_.registerProperty("Descriptors").onInterface(INTERFACE_NAME).withGetter([this](){ return this->Descriptors(); }); - } - - ~GattCharacteristic1_adaptor() = default; - -private: - virtual void WriteValue(const std::vector& value) = 0; - virtual void StartNotify() = 0; - virtual void StopNotify() = 0; - -private: - virtual std::string UUID() = 0; - virtual sdbus::ObjectPath Service() = 0; - virtual std::vector Value() = 0; - virtual bool Notifying() = 0; - virtual std::vector Flags() = 0; - virtual std::vector Descriptors() = 0; - -private: - sdbus::IObject& object_; -}; - -}} // namespaces - -namespace org { -namespace bluez { - -class GattDescriptor1_adaptor -{ -public: - static constexpr const char* INTERFACE_NAME = "org.bluez.GattDescriptor1"; - -protected: - GattDescriptor1_adaptor(sdbus::IObject& object) - : object_(object) - { - object_.registerMethod("ReadValue").onInterface(INTERFACE_NAME).withOutputParamNames("value").implementedAs([this](){ return this->ReadValue(); }); - object_.registerProperty("UUID").onInterface(INTERFACE_NAME).withGetter([this](){ return this->UUID(); }); - object_.registerProperty("Characteristic").onInterface(INTERFACE_NAME).withGetter([this](){ return this->Characteristic(); }); - object_.registerProperty("Value").onInterface(INTERFACE_NAME).withGetter([this](){ return this->Value(); }); - object_.registerProperty("Flags").onInterface(INTERFACE_NAME).withGetter([this](){ return this->Flags(); }); - } - - ~GattDescriptor1_adaptor() = default; - -private: - virtual std::vector ReadValue() = 0; - -private: - virtual std::string UUID() = 0; - virtual sdbus::ObjectPath Characteristic() = 0; - virtual std::vector Value() = 0; - virtual std::vector Flags() = 0; - -private: - sdbus::IObject& object_; -}; - -}} // namespaces - -namespace org { -namespace bluez { - -class LEAdvertisement1_adaptor -{ -public: - static constexpr const char* INTERFACE_NAME = "org.bluez.LEAdvertisement1"; - -protected: - LEAdvertisement1_adaptor(sdbus::IObject& object) - : object_(object) - { - object_.registerMethod("Release").onInterface(INTERFACE_NAME).implementedAs([this](){ return this->Release(); }).withNoReply(); - object_.registerProperty("Type").onInterface(INTERFACE_NAME).withGetter([this](){ return this->Type(); }); - object_.registerProperty("ServiceUUIDs").onInterface(INTERFACE_NAME).withGetter([this](){ return this->ServiceUUIDs(); }); - object_.registerProperty("ManufacturerData").onInterface(INTERFACE_NAME).withGetter([this](){ return this->ManufacturerData(); }); - object_.registerProperty("SolicitUUIDs").onInterface(INTERFACE_NAME).withGetter([this](){ return this->SolicitUUIDs(); }); - object_.registerProperty("ServiceData").onInterface(INTERFACE_NAME).withGetter([this](){ return this->ServiceData(); }); - object_.registerProperty("IncludeTxPower").onInterface(INTERFACE_NAME).withGetter([this](){ return this->IncludeTxPower(); }); - } - - ~LEAdvertisement1_adaptor() = default; - -private: - virtual void Release() = 0; - -private: - virtual std::string Type() = 0; - virtual std::vector ServiceUUIDs() = 0; - virtual std::map ManufacturerData() = 0; - virtual std::vector SolicitUUIDs() = 0; - virtual std::map ServiceData() = 0; - virtual bool IncludeTxPower() = 0; - -private: - sdbus::IObject& object_; -}; - -}} // namespaces - -namespace org { -namespace freedesktop { -namespace DBus { - -class Introspectable_adaptor -{ -public: - static constexpr const char* INTERFACE_NAME = "org.freedesktop.DBus.Introspectable"; - -protected: - Introspectable_adaptor(sdbus::IObject& object) - : object_(object) - { - object_.registerMethod("Introspect").onInterface(INTERFACE_NAME).withOutputParamNames("xml").implementedAs([this](){ return this->Introspect(); }); - } - - ~Introspectable_adaptor() = default; - -private: - virtual std::string Introspect() = 0; - -private: - sdbus::IObject& object_; -}; - -}}} // namespaces - -namespace org { -namespace bluez { - -class Adapter1_adaptor -{ -public: - static constexpr const char* INTERFACE_NAME = "org.bluez.Adapter1"; - -protected: - Adapter1_adaptor(sdbus::IObject& object) - : object_(object) - { - object_.registerMethod("StartDiscovery").onInterface(INTERFACE_NAME).implementedAs([this](){ return this->StartDiscovery(); }); - object_.registerMethod("SetDiscoveryFilter").onInterface(INTERFACE_NAME).withInputParamNames("properties").implementedAs([this](const std::map& properties){ return this->SetDiscoveryFilter(properties); }); - object_.registerMethod("StopDiscovery").onInterface(INTERFACE_NAME).implementedAs([this](){ return this->StopDiscovery(); }); - object_.registerMethod("RemoveDevice").onInterface(INTERFACE_NAME).withInputParamNames("device").implementedAs([this](const sdbus::ObjectPath& device){ return this->RemoveDevice(device); }); - object_.registerMethod("GetDiscoveryFilters").onInterface(INTERFACE_NAME).withOutputParamNames("filters").implementedAs([this](){ return this->GetDiscoveryFilters(); }); - object_.registerProperty("Address").onInterface(INTERFACE_NAME).withGetter([this](){ return this->Address(); }); - object_.registerProperty("AddressType").onInterface(INTERFACE_NAME).withGetter([this](){ return this->AddressType(); }); - object_.registerProperty("Name").onInterface(INTERFACE_NAME).withGetter([this](){ return this->Name(); }); - object_.registerProperty("Alias").onInterface(INTERFACE_NAME).withGetter([this](){ return this->Alias(); }).withSetter([this](const std::string& value){ this->Alias(value); }); - object_.registerProperty("Class").onInterface(INTERFACE_NAME).withGetter([this](){ return this->Class(); }); - object_.registerProperty("Powered").onInterface(INTERFACE_NAME).withGetter([this](){ return this->Powered(); }).withSetter([this](const bool& value){ this->Powered(value); }); - object_.registerProperty("Discoverable").onInterface(INTERFACE_NAME).withGetter([this](){ return this->Discoverable(); }).withSetter([this](const bool& value){ this->Discoverable(value); }); - object_.registerProperty("DiscoverableTimeout").onInterface(INTERFACE_NAME).withGetter([this](){ return this->DiscoverableTimeout(); }).withSetter([this](const uint32_t& value){ this->DiscoverableTimeout(value); }); - object_.registerProperty("Pairable").onInterface(INTERFACE_NAME).withGetter([this](){ return this->Pairable(); }).withSetter([this](const bool& value){ this->Pairable(value); }); - object_.registerProperty("PairableTimeout").onInterface(INTERFACE_NAME).withGetter([this](){ return this->PairableTimeout(); }).withSetter([this](const uint32_t& value){ this->PairableTimeout(value); }); - object_.registerProperty("Discovering").onInterface(INTERFACE_NAME).withGetter([this](){ return this->Discovering(); }); - object_.registerProperty("UUIDs").onInterface(INTERFACE_NAME).withGetter([this](){ return this->UUIDs(); }); - object_.registerProperty("Modalias").onInterface(INTERFACE_NAME).withGetter([this](){ return this->Modalias(); }); - object_.registerProperty("Roles").onInterface(INTERFACE_NAME).withGetter([this](){ return this->Roles(); }); - } - - ~Adapter1_adaptor() = default; - -private: - virtual void StartDiscovery() = 0; - virtual void SetDiscoveryFilter(const std::map& properties) = 0; - virtual void StopDiscovery() = 0; - virtual void RemoveDevice(const sdbus::ObjectPath& device) = 0; - virtual std::vector GetDiscoveryFilters() = 0; - -private: - virtual std::string Address() = 0; - virtual std::string AddressType() = 0; - virtual std::string Name() = 0; - virtual std::string Alias() = 0; - virtual void Alias(const std::string& value) = 0; - virtual uint32_t Class() = 0; - virtual bool Powered() = 0; - virtual void Powered(const bool& value) = 0; - virtual bool Discoverable() = 0; - virtual void Discoverable(const bool& value) = 0; - virtual uint32_t DiscoverableTimeout() = 0; - virtual void DiscoverableTimeout(const uint32_t& value) = 0; - virtual bool Pairable() = 0; - virtual void Pairable(const bool& value) = 0; - virtual uint32_t PairableTimeout() = 0; - virtual void PairableTimeout(const uint32_t& value) = 0; - virtual bool Discovering() = 0; - virtual std::vector UUIDs() = 0; - virtual std::string Modalias() = 0; - virtual std::vector Roles() = 0; - -private: - sdbus::IObject& object_; -}; - -}} // namespaces - -namespace org { -namespace freedesktop { -namespace DBus { - -class Properties_adaptor -{ -public: - static constexpr const char* INTERFACE_NAME = "org.freedesktop.DBus.Properties"; - -protected: - Properties_adaptor(sdbus::IObject& object) - : object_(object) - { - object_.registerMethod("Get").onInterface(INTERFACE_NAME).withInputParamNames("interface", "name").withOutputParamNames("value").implementedAs([this](const std::string& interface, const std::string& name){ return this->Get(interface, name); }); - object_.registerMethod("Set").onInterface(INTERFACE_NAME).withInputParamNames("interface", "name", "value").implementedAs([this](const std::string& interface, const std::string& name, const sdbus::Variant& value){ return this->Set(interface, name, value); }); - object_.registerMethod("GetAll").onInterface(INTERFACE_NAME).withInputParamNames("interface").withOutputParamNames("properties").implementedAs([this](const std::string& interface){ return this->GetAll(interface); }); - object_.registerSignal("PropertiesChanged").onInterface(INTERFACE_NAME).withParameters, std::vector>("interface", "changed_properties", "invalidated_properties"); - } - - ~Properties_adaptor() = default; - -public: - void emitPropertiesChanged(const std::string& interface, const std::map& changed_properties, const std::vector& invalidated_properties) - { - object_.emitSignal("PropertiesChanged").onInterface(INTERFACE_NAME).withArguments(interface, changed_properties, invalidated_properties); - } - -private: - virtual sdbus::Variant Get(const std::string& interface, const std::string& name) = 0; - virtual void Set(const std::string& interface, const std::string& name, const sdbus::Variant& value) = 0; - virtual std::map GetAll(const std::string& interface) = 0; - -private: - sdbus::IObject& object_; -}; - -}}} // namespaces - -namespace org { -namespace bluez { - -class GattManager1_adaptor -{ -public: - static constexpr const char* INTERFACE_NAME = "org.bluez.GattManager1"; - -protected: - GattManager1_adaptor(sdbus::IObject& object) - : object_(object) - { - object_.registerMethod("RegisterApplication").onInterface(INTERFACE_NAME).withInputParamNames("application", "options").implementedAs([this](const sdbus::ObjectPath& application, const std::map& options){ return this->RegisterApplication(application, options); }); - object_.registerMethod("UnregisterApplication").onInterface(INTERFACE_NAME).withInputParamNames("application").implementedAs([this](const sdbus::ObjectPath& application){ return this->UnregisterApplication(application); }); - } - - ~GattManager1_adaptor() = default; - -private: - virtual void RegisterApplication(const sdbus::ObjectPath& application, const std::map& options) = 0; - virtual void UnregisterApplication(const sdbus::ObjectPath& application) = 0; - -private: - sdbus::IObject& object_; -}; - -}} // namespaces - -namespace org { -namespace bluez { - -class LEAdvertisingManager1_adaptor -{ -public: - static constexpr const char* INTERFACE_NAME = "org.bluez.LEAdvertisingManager1"; - -protected: - LEAdvertisingManager1_adaptor(sdbus::IObject& object) - : object_(object) - { - object_.registerMethod("RegisterAdvertisement").onInterface(INTERFACE_NAME).withInputParamNames("advertisement", "options").implementedAs([this](const sdbus::ObjectPath& advertisement, const std::map& options){ return this->RegisterAdvertisement(advertisement, options); }); - object_.registerMethod("UnregisterAdvertisement").onInterface(INTERFACE_NAME).withInputParamNames("service").implementedAs([this](const sdbus::ObjectPath& service){ return this->UnregisterAdvertisement(service); }); - object_.registerProperty("ActiveInstances").onInterface(INTERFACE_NAME).withGetter([this](){ return this->ActiveInstances(); }); - object_.registerProperty("SupportedInstances").onInterface(INTERFACE_NAME).withGetter([this](){ return this->SupportedInstances(); }); - object_.registerProperty("SupportedIncludes").onInterface(INTERFACE_NAME).withGetter([this](){ return this->SupportedIncludes(); }); - object_.registerProperty("SupportedSecondaryChannels").onInterface(INTERFACE_NAME).withGetter([this](){ return this->SupportedSecondaryChannels(); }); - } - - ~LEAdvertisingManager1_adaptor() = default; - -private: - virtual void RegisterAdvertisement(const sdbus::ObjectPath& advertisement, const std::map& options) = 0; - virtual void UnregisterAdvertisement(const sdbus::ObjectPath& service) = 0; - -private: - virtual uint8_t ActiveInstances() = 0; - virtual uint8_t SupportedInstances() = 0; - virtual std::vector SupportedIncludes() = 0; - virtual std::vector SupportedSecondaryChannels() = 0; - -private: - sdbus::IObject& object_; -}; - -}} // namespaces - -namespace org { -namespace bluez { - -class Media1_adaptor -{ -public: - static constexpr const char* INTERFACE_NAME = "org.bluez.Media1"; - -protected: - Media1_adaptor(sdbus::IObject& object) - : object_(object) - { - object_.registerMethod("RegisterEndpoint").onInterface(INTERFACE_NAME).withInputParamNames("endpoint", "properties").implementedAs([this](const sdbus::ObjectPath& endpoint, const std::map& properties){ return this->RegisterEndpoint(endpoint, properties); }); - object_.registerMethod("UnregisterEndpoint").onInterface(INTERFACE_NAME).withInputParamNames("endpoint").implementedAs([this](const sdbus::ObjectPath& endpoint){ return this->UnregisterEndpoint(endpoint); }); - object_.registerMethod("RegisterPlayer").onInterface(INTERFACE_NAME).withInputParamNames("player", "properties").implementedAs([this](const sdbus::ObjectPath& player, const std::map& properties){ return this->RegisterPlayer(player, properties); }); - object_.registerMethod("UnregisterPlayer").onInterface(INTERFACE_NAME).withInputParamNames("player").implementedAs([this](const sdbus::ObjectPath& player){ return this->UnregisterPlayer(player); }); - object_.registerMethod("RegisterApplication").onInterface(INTERFACE_NAME).withInputParamNames("application", "options").implementedAs([this](const sdbus::ObjectPath& application, const std::map& options){ return this->RegisterApplication(application, options); }); - object_.registerMethod("UnregisterApplication").onInterface(INTERFACE_NAME).withInputParamNames("application").implementedAs([this](const sdbus::ObjectPath& application){ return this->UnregisterApplication(application); }); - } - - ~Media1_adaptor() = default; - -private: - virtual void RegisterEndpoint(const sdbus::ObjectPath& endpoint, const std::map& properties) = 0; - virtual void UnregisterEndpoint(const sdbus::ObjectPath& endpoint) = 0; - virtual void RegisterPlayer(const sdbus::ObjectPath& player, const std::map& properties) = 0; - virtual void UnregisterPlayer(const sdbus::ObjectPath& player) = 0; - virtual void RegisterApplication(const sdbus::ObjectPath& application, const std::map& options) = 0; - virtual void UnregisterApplication(const sdbus::ObjectPath& application) = 0; - -private: - sdbus::IObject& object_; -}; - -}} // namespaces - -namespace org { -namespace bluez { - -class NetworkServer1_adaptor -{ -public: - static constexpr const char* INTERFACE_NAME = "org.bluez.NetworkServer1"; - -protected: - NetworkServer1_adaptor(sdbus::IObject& object) - : object_(object) - { - object_.registerMethod("Register").onInterface(INTERFACE_NAME).withInputParamNames("uuid", "bridge").implementedAs([this](const std::string& uuid, const std::string& bridge){ return this->Register(uuid, bridge); }); - object_.registerMethod("Unregister").onInterface(INTERFACE_NAME).withInputParamNames("uuid").implementedAs([this](const std::string& uuid){ return this->Unregister(uuid); }); - } - - ~NetworkServer1_adaptor() = default; - -private: - virtual void Register(const std::string& uuid, const std::string& bridge) = 0; - virtual void Unregister(const std::string& uuid) = 0; - -private: - sdbus::IObject& object_; -}; - -}} // namespaces - -#endif diff --git a/src/dbus/bluez_proxy.h b/src/dbus/bluez_proxy.h deleted file mode 100644 index e9012d6..0000000 --- a/src/dbus/bluez_proxy.h +++ /dev/null @@ -1,631 +0,0 @@ - -/* - * This file was automatically generated by sdbus-c++-xml2cpp; DO NOT EDIT! - */ - -#ifndef __sdbuscpp___home_pi_src_pipedal_src_dbus_bluez_proxy_h__proxy__H__ -#define __sdbuscpp___home_pi_src_pipedal_src_dbus_bluez_proxy_h__proxy__H__ - -#include -#include -#include - -namespace org { -namespace bluez { - -class GattService1_proxy -{ -public: - static constexpr const char* INTERFACE_NAME = "org.bluez.GattService1"; - -protected: - GattService1_proxy(sdbus::IProxy& proxy) - : proxy_(proxy) - { - } - - ~GattService1_proxy() = default; - -public: - std::string UUID() - { - return proxy_.getProperty("UUID").onInterface(INTERFACE_NAME); - } - - bool Primary() - { - return proxy_.getProperty("Primary").onInterface(INTERFACE_NAME); - } - - std::vector Characteristics() - { - return proxy_.getProperty("Characteristics").onInterface(INTERFACE_NAME); - } - -private: - sdbus::IProxy& proxy_; -}; - -}} // namespaces - -namespace org { -namespace bluez { - -class GattCharacteristic1_proxy -{ -public: - static constexpr const char* INTERFACE_NAME = "org.bluez.GattCharacteristic1"; - -protected: - GattCharacteristic1_proxy(sdbus::IProxy& proxy) - : proxy_(proxy) - { - } - - ~GattCharacteristic1_proxy() = default; - -public: - void WriteValue(const std::vector& value) - { - proxy_.callMethod("WriteValue").onInterface(INTERFACE_NAME).withArguments(value); - } - - void StartNotify() - { - proxy_.callMethod("StartNotify").onInterface(INTERFACE_NAME); - } - - void StopNotify() - { - proxy_.callMethod("StopNotify").onInterface(INTERFACE_NAME); - } - -public: - std::string UUID() - { - return proxy_.getProperty("UUID").onInterface(INTERFACE_NAME); - } - - sdbus::ObjectPath Service() - { - return proxy_.getProperty("Service").onInterface(INTERFACE_NAME); - } - - std::vector Value() - { - return proxy_.getProperty("Value").onInterface(INTERFACE_NAME); - } - - bool Notifying() - { - return proxy_.getProperty("Notifying").onInterface(INTERFACE_NAME); - } - - std::vector Flags() - { - return proxy_.getProperty("Flags").onInterface(INTERFACE_NAME); - } - - std::vector Descriptors() - { - return proxy_.getProperty("Descriptors").onInterface(INTERFACE_NAME); - } - -private: - sdbus::IProxy& proxy_; -}; - -}} // namespaces - -namespace org { -namespace bluez { - -class GattDescriptor1_proxy -{ -public: - static constexpr const char* INTERFACE_NAME = "org.bluez.GattDescriptor1"; - -protected: - GattDescriptor1_proxy(sdbus::IProxy& proxy) - : proxy_(proxy) - { - } - - ~GattDescriptor1_proxy() = default; - -public: - std::vector ReadValue() - { - std::vector result; - proxy_.callMethod("ReadValue").onInterface(INTERFACE_NAME).storeResultsTo(result); - return result; - } - -public: - std::string UUID() - { - return proxy_.getProperty("UUID").onInterface(INTERFACE_NAME); - } - - sdbus::ObjectPath Characteristic() - { - return proxy_.getProperty("Characteristic").onInterface(INTERFACE_NAME); - } - - std::vector Value() - { - return proxy_.getProperty("Value").onInterface(INTERFACE_NAME); - } - - std::vector Flags() - { - return proxy_.getProperty("Flags").onInterface(INTERFACE_NAME); - } - -private: - sdbus::IProxy& proxy_; -}; - -}} // namespaces - -namespace org { -namespace bluez { - -class LEAdvertisement1_proxy -{ -public: - static constexpr const char* INTERFACE_NAME = "org.bluez.LEAdvertisement1"; - -protected: - LEAdvertisement1_proxy(sdbus::IProxy& proxy) - : proxy_(proxy) - { - } - - ~LEAdvertisement1_proxy() = default; - -public: - void Release() - { - proxy_.callMethod("Release").onInterface(INTERFACE_NAME).dontExpectReply(); - } - -public: - std::string Type() - { - return proxy_.getProperty("Type").onInterface(INTERFACE_NAME); - } - - std::vector ServiceUUIDs() - { - return proxy_.getProperty("ServiceUUIDs").onInterface(INTERFACE_NAME); - } - - std::map ManufacturerData() - { - return proxy_.getProperty("ManufacturerData").onInterface(INTERFACE_NAME); - } - - std::vector SolicitUUIDs() - { - return proxy_.getProperty("SolicitUUIDs").onInterface(INTERFACE_NAME); - } - - std::map ServiceData() - { - return proxy_.getProperty("ServiceData").onInterface(INTERFACE_NAME); - } - - bool IncludeTxPower() - { - return proxy_.getProperty("IncludeTxPower").onInterface(INTERFACE_NAME); - } - -private: - sdbus::IProxy& proxy_; -}; - -}} // namespaces - -namespace org { -namespace freedesktop { -namespace DBus { - -class Introspectable_proxy -{ -public: - static constexpr const char* INTERFACE_NAME = "org.freedesktop.DBus.Introspectable"; - -protected: - Introspectable_proxy(sdbus::IProxy& proxy) - : proxy_(proxy) - { - } - - ~Introspectable_proxy() = default; - -public: - std::string Introspect() - { - std::string result; - proxy_.callMethod("Introspect").onInterface(INTERFACE_NAME).storeResultsTo(result); - return result; - } - -private: - sdbus::IProxy& proxy_; -}; - -}}} // namespaces - -namespace org { -namespace bluez { - -class Adapter1_proxy -{ -public: - static constexpr const char* INTERFACE_NAME = "org.bluez.Adapter1"; - -protected: - Adapter1_proxy(sdbus::IProxy& proxy) - : proxy_(proxy) - { - } - - ~Adapter1_proxy() = default; - -public: - void StartDiscovery() - { - proxy_.callMethod("StartDiscovery").onInterface(INTERFACE_NAME); - } - - void SetDiscoveryFilter(const std::map& properties) - { - proxy_.callMethod("SetDiscoveryFilter").onInterface(INTERFACE_NAME).withArguments(properties); - } - - void StopDiscovery() - { - proxy_.callMethod("StopDiscovery").onInterface(INTERFACE_NAME); - } - - void RemoveDevice(const sdbus::ObjectPath& device) - { - proxy_.callMethod("RemoveDevice").onInterface(INTERFACE_NAME).withArguments(device); - } - - std::vector GetDiscoveryFilters() - { - std::vector result; - proxy_.callMethod("GetDiscoveryFilters").onInterface(INTERFACE_NAME).storeResultsTo(result); - return result; - } - -public: - std::string Address() - { - return proxy_.getProperty("Address").onInterface(INTERFACE_NAME); - } - - std::string AddressType() - { - return proxy_.getProperty("AddressType").onInterface(INTERFACE_NAME); - } - - std::string Name() - { - return proxy_.getProperty("Name").onInterface(INTERFACE_NAME); - } - - std::string Alias() - { - return proxy_.getProperty("Alias").onInterface(INTERFACE_NAME); - } - - void Alias(const std::string& value) - { - proxy_.setProperty("Alias").onInterface(INTERFACE_NAME).toValue(value); - } - - uint32_t Class() - { - return proxy_.getProperty("Class").onInterface(INTERFACE_NAME); - } - - bool Powered() - { - return proxy_.getProperty("Powered").onInterface(INTERFACE_NAME); - } - - void Powered(const bool& value) - { - proxy_.setProperty("Powered").onInterface(INTERFACE_NAME).toValue(value); - } - - bool Discoverable() - { - return proxy_.getProperty("Discoverable").onInterface(INTERFACE_NAME); - } - - void Discoverable(const bool& value) - { - proxy_.setProperty("Discoverable").onInterface(INTERFACE_NAME).toValue(value); - } - - uint32_t DiscoverableTimeout() - { - return proxy_.getProperty("DiscoverableTimeout").onInterface(INTERFACE_NAME); - } - - void DiscoverableTimeout(const uint32_t& value) - { - proxy_.setProperty("DiscoverableTimeout").onInterface(INTERFACE_NAME).toValue(value); - } - - bool Pairable() - { - return proxy_.getProperty("Pairable").onInterface(INTERFACE_NAME); - } - - void Pairable(const bool& value) - { - proxy_.setProperty("Pairable").onInterface(INTERFACE_NAME).toValue(value); - } - - uint32_t PairableTimeout() - { - return proxy_.getProperty("PairableTimeout").onInterface(INTERFACE_NAME); - } - - void PairableTimeout(const uint32_t& value) - { - proxy_.setProperty("PairableTimeout").onInterface(INTERFACE_NAME).toValue(value); - } - - bool Discovering() - { - return proxy_.getProperty("Discovering").onInterface(INTERFACE_NAME); - } - - std::vector UUIDs() - { - return proxy_.getProperty("UUIDs").onInterface(INTERFACE_NAME); - } - - std::string Modalias() - { - return proxy_.getProperty("Modalias").onInterface(INTERFACE_NAME); - } - - std::vector Roles() - { - return proxy_.getProperty("Roles").onInterface(INTERFACE_NAME); - } - -private: - sdbus::IProxy& proxy_; -}; - -}} // namespaces - -namespace org { -namespace freedesktop { -namespace DBus { - -class Properties_proxy -{ -public: - static constexpr const char* INTERFACE_NAME = "org.freedesktop.DBus.Properties"; - -protected: - Properties_proxy(sdbus::IProxy& proxy) - : proxy_(proxy) - { - proxy_.uponSignal("PropertiesChanged").onInterface(INTERFACE_NAME).call([this](const std::string& interface, const std::map& changed_properties, const std::vector& invalidated_properties){ this->onPropertiesChanged(interface, changed_properties, invalidated_properties); }); - } - - ~Properties_proxy() = default; - - virtual void onPropertiesChanged(const std::string& interface, const std::map& changed_properties, const std::vector& invalidated_properties) = 0; - -public: - sdbus::Variant Get(const std::string& interface, const std::string& name) - { - sdbus::Variant result; - proxy_.callMethod("Get").onInterface(INTERFACE_NAME).withArguments(interface, name).storeResultsTo(result); - return result; - } - - void Set(const std::string& interface, const std::string& name, const sdbus::Variant& value) - { - proxy_.callMethod("Set").onInterface(INTERFACE_NAME).withArguments(interface, name, value); - } - - std::map GetAll(const std::string& interface) - { - std::map result; - proxy_.callMethod("GetAll").onInterface(INTERFACE_NAME).withArguments(interface).storeResultsTo(result); - return result; - } - -private: - sdbus::IProxy& proxy_; -}; - -}}} // namespaces - -namespace org { -namespace bluez { - -class GattManager1_proxy -{ -public: - static constexpr const char* INTERFACE_NAME = "org.bluez.GattManager1"; - -protected: - GattManager1_proxy(sdbus::IProxy& proxy) - : proxy_(proxy) - { - } - - ~GattManager1_proxy() = default; - -public: - void RegisterApplication(const sdbus::ObjectPath& application, const std::map& options) - { - proxy_.callMethod("RegisterApplication").onInterface(INTERFACE_NAME).withArguments(application, options); - } - - void UnregisterApplication(const sdbus::ObjectPath& application) - { - proxy_.callMethod("UnregisterApplication").onInterface(INTERFACE_NAME).withArguments(application); - } - -private: - sdbus::IProxy& proxy_; -}; - -}} // namespaces - -namespace org { -namespace bluez { - -class LEAdvertisingManager1_proxy -{ -public: - static constexpr const char* INTERFACE_NAME = "org.bluez.LEAdvertisingManager1"; - -protected: - LEAdvertisingManager1_proxy(sdbus::IProxy& proxy) - : proxy_(proxy) - { - } - - ~LEAdvertisingManager1_proxy() = default; - -public: - void RegisterAdvertisement(const sdbus::ObjectPath& advertisement, const std::map& options) - { - proxy_.callMethod("RegisterAdvertisement").onInterface(INTERFACE_NAME).withArguments(advertisement, options); - } - - void UnregisterAdvertisement(const sdbus::ObjectPath& service) - { - proxy_.callMethod("UnregisterAdvertisement").onInterface(INTERFACE_NAME).withArguments(service); - } - -public: - uint8_t ActiveInstances() - { - return proxy_.getProperty("ActiveInstances").onInterface(INTERFACE_NAME); - } - - uint8_t SupportedInstances() - { - return proxy_.getProperty("SupportedInstances").onInterface(INTERFACE_NAME); - } - - std::vector SupportedIncludes() - { - return proxy_.getProperty("SupportedIncludes").onInterface(INTERFACE_NAME); - } - - std::vector SupportedSecondaryChannels() - { - return proxy_.getProperty("SupportedSecondaryChannels").onInterface(INTERFACE_NAME); - } - -private: - sdbus::IProxy& proxy_; -}; - -}} // namespaces - -namespace org { -namespace bluez { - -class Media1_proxy -{ -public: - static constexpr const char* INTERFACE_NAME = "org.bluez.Media1"; - -protected: - Media1_proxy(sdbus::IProxy& proxy) - : proxy_(proxy) - { - } - - ~Media1_proxy() = default; - -public: - void RegisterEndpoint(const sdbus::ObjectPath& endpoint, const std::map& properties) - { - proxy_.callMethod("RegisterEndpoint").onInterface(INTERFACE_NAME).withArguments(endpoint, properties); - } - - void UnregisterEndpoint(const sdbus::ObjectPath& endpoint) - { - proxy_.callMethod("UnregisterEndpoint").onInterface(INTERFACE_NAME).withArguments(endpoint); - } - - void RegisterPlayer(const sdbus::ObjectPath& player, const std::map& properties) - { - proxy_.callMethod("RegisterPlayer").onInterface(INTERFACE_NAME).withArguments(player, properties); - } - - void UnregisterPlayer(const sdbus::ObjectPath& player) - { - proxy_.callMethod("UnregisterPlayer").onInterface(INTERFACE_NAME).withArguments(player); - } - - void RegisterApplication(const sdbus::ObjectPath& application, const std::map& options) - { - proxy_.callMethod("RegisterApplication").onInterface(INTERFACE_NAME).withArguments(application, options); - } - - void UnregisterApplication(const sdbus::ObjectPath& application) - { - proxy_.callMethod("UnregisterApplication").onInterface(INTERFACE_NAME).withArguments(application); - } - -private: - sdbus::IProxy& proxy_; -}; - -}} // namespaces - -namespace org { -namespace bluez { - -class NetworkServer1_proxy -{ -public: - static constexpr const char* INTERFACE_NAME = "org.bluez.NetworkServer1"; - -protected: - NetworkServer1_proxy(sdbus::IProxy& proxy) - : proxy_(proxy) - { - } - - ~NetworkServer1_proxy() = default; - -public: - void Register(const std::string& uuid, const std::string& bridge) - { - proxy_.callMethod("Register").onInterface(INTERFACE_NAME).withArguments(uuid, bridge); - } - - void Unregister(const std::string& uuid) - { - proxy_.callMethod("Unregister").onInterface(INTERFACE_NAME).withArguments(uuid); - } - -private: - sdbus::IProxy& proxy_; -}; - -}} // namespaces - -#endif diff --git a/src/dbus/bluez_test.cpp b/src/dbus/bluez_test.cpp deleted file mode 100644 index a4e5ca0..0000000 --- a/src/dbus/bluez_test.cpp +++ /dev/null @@ -1,112 +0,0 @@ -#include "bluez_adaptor.h" -#include "bluez.h" -#include - -#include "catch.hpp" -#include "gsl/gsl" - -#define TEST_UUID "6b49786f-271d-415d-8e2c-bd5ef8aa6f47" -using TestAdvertisementInterfaces = sdbus::AdaptorInterfaces; - - -static const char* SERVICE_NAME="com.twoplay.pipedal.onboarding"; -static const char* SERVICE_PATH="/com/twoplay/pipedal/onboarding"; - - -class TestAdvertisement: public TestAdvertisementInterfaces -{ -private: - std::vector serviceUuids; -public: - TestAdvertisement(sdbus::IConnection& connection, std::string objectPath) - : TestAdvertisementInterfaces(connection,std::move(objectPath)) - { - serviceUuids.push_back(TEST_UUID); - } -private: - - virtual void Release() { } - -private: - virtual std::string Type() { return "peripheral"; } - virtual std::vector ServiceUUIDs() { return serviceUuids; } - virtual std::map ManufacturerData() { - return std::map(); - }; - virtual std::vector SolicitUUIDs() { - return std::vector(); - } - virtual std::map ServiceData() - { - return std::map(); - } - virtual bool IncludeTxPower() { - return false; - } - - -}; - - -using namespace gsl; -using namespace std; - -class TestServer { -private: - std::string devicePath; - - std::unique_ptr advertisement; - -private: - - void ListenProc() - { - - } -public: - TestServer(const std::string &devicePath) - : devicePath(devicePath) - { - - - - } - - void Run() - { - - auto connector = sdbus::createSystemBusConnection(SERVICE_NAME); - std::unique_ptr advertisement = std::make_unique(*connector,SERVICE_PATH); - - std::unique_ptr device = std::make_unique(devicePath); - - - std::map advertisementOptions; - device->RegisterAdvertisement(advertisement->getObjectPath(),advertisementOptions); - - auto _ = finally([&device,&advertisement] { - device->UnregisterAdvertisement(advertisement->getObjectPath()); - }); - - std::thread thread( - [this] () { - this->ListenProc(); - } - ); - - thread.join(); - - - } -}; - -TEST_CASE( "Bluetooth service", "[bluetooth_service]" ) { - org::bluez::BleDeviceProxy device("/org/bluez/hci0"); - - auto includes = device.SupportedIncludes(); - - TestServer server {"/org/bluez/hci0"}; - - server.Run(); - -} diff --git a/src/main.cpp b/src/main.cpp index 3a6a664..0f5c983 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -21,13 +21,15 @@ #include "BeastServer.hpp" #include #include "Lv2Log.hpp" +#include "DeviceIdFile.hpp" +#include "AvahiService.hpp" #include "PiPedalSocket.hpp" #include "Lv2Host.hpp" #include #include #include "PiPedalConfiguration.hpp" -#include "ShutdownClient.hpp" +#include "AdminClient.hpp" #include "CommandLineParser.hpp" #include "Lv2SystemdLogger.hpp" #include @@ -87,7 +89,7 @@ public: { } - virtual bool wants(const std::string& method, const uri &request_uri) const + virtual bool wants(const std::string &method, const uri &request_uri) const { if (request_uri.segment_count() != 2 || request_uri.segment(0) != "var") { @@ -135,7 +137,7 @@ public: return result; } - void GetPluginPresets(const uri &request_uri, std::string*pName,std::string *pContent) + void GetPluginPresets(const uri &request_uri, std::string *pName, std::string *pContent) { std::string pluginUri = request_uri.query("id"); auto plugin = model->GetLv2Host().GetPluginInfo(pluginUri); @@ -193,7 +195,7 @@ public: { std::string name; std::string content; - GetPluginPresets(request_uri,&name,&content); + GetPluginPresets(request_uri, &name, &content); res.set(HttpField::content_type, "application/octet-stream"); res.set(HttpField::cache_control, "no-cache"); res.setContentLength(content.length()); @@ -253,13 +255,14 @@ public: { std::string name; std::string content; - GetPluginPresets(request_uri,&name,&content); + GetPluginPresets(request_uri, &name, &content); res.set(HttpField::content_type, "application/octet-stream"); res.set(HttpField::cache_control, "no-cache"); res.setContentLength(content.length()); res.set(HttpField::content_disposition, GetContentDispositionHeader(name, PLUGIN_PRESETS_EXTENSION)); res.setBody(content); - } else if (segment == "downloadPreset") + } + else if (segment == "downloadPreset") { std::string name; std::string content; @@ -312,7 +315,7 @@ public: if (segment == "uploadPluginPresets") { - const std::string& presetBody = req.body(); + const std::string &presetBody = req.body(); std::stringstream s(presetBody); json_reader reader(s); PluginPresets presets; @@ -327,9 +330,10 @@ public: res.setContentLength(result.length()); res.setBody(result); - } else if (segment == "uploadPreset") + } + else if (segment == "uploadPreset") { - const std::string& presetBody = req.body(); + const std::string &presetBody = req.body(); std::stringstream s(presetBody); json_reader reader(s); @@ -356,7 +360,7 @@ public: } else if (segment == "uploadBank") { - const std::string& presetBody = req.body(); + const std::string &presetBody = req.body(); std::istringstream s(presetBody); json_reader reader(s); @@ -417,34 +421,34 @@ public: portNumber(portNumber) { } - std::string GetConfig(const std::string&fromAddress) + std::string GetConfig(const std::string &fromAddress) { std::string linkLocalAddress = GetLinkLocalAddress(fromAddress); std::stringstream s; s << "{ \"socket_server_port\": " << portNumber - << ", \"socket_server_address\": \"" << linkLocalAddress << "\", \"ui_plugins\": [ ], \"max_upload_size\": " << maxUploadSize << " }"; + << ", \"socket_server_address\": \"" << linkLocalAddress << "\", \"ui_plugins\": [ ], \"max_upload_size\": " << maxUploadSize << " }"; return s.str(); } virtual ~InterceptConfig() {} - virtual void head_response( - const uri&request_uri, + virtual void head_response( + const uri &request_uri, const HttpRequest &req, HttpResponse &res, - std::error_code &ec) + std::error_code &ec) { // intercepted. See the other overload. } - virtual void head_response( - const std::string &fromAddress, - const uri&request_uri, + virtual void head_response( + const std::string &fromAddress, + const uri &request_uri, const HttpRequest &req, HttpResponse &res, - std::error_code &ec) + std::error_code &ec) { std::string response = GetConfig(fromAddress); res.set(HttpField::content_type, "application/json"); @@ -458,12 +462,12 @@ public: const HttpRequest &req, HttpResponse &res, std::error_code &ec) - { - // intercepted. see the other overload. - } + { + // intercepted. see the other overload. + } virtual void get_response( - const std::string&fromAddress, + const std::string &fromAddress, const uri &request_uri, const HttpRequest &req, HttpResponse &res, @@ -477,7 +481,6 @@ public: } }; - static bool isJackServiceRunning() { // look for the jack shmem . @@ -485,7 +488,6 @@ static bool isJackServiceRunning() return std::filesystem::exists(path); } -uint16_t g_ShutdownPort = 0; int main(int argc, char *argv[]) { @@ -502,7 +504,6 @@ int main(int argc, char *argv[]) bool help = false; bool error = false; bool systemd = false; - uint16_t shutdownPort = 0; std::string portOption; CommandLineParser parser; @@ -510,7 +511,6 @@ int main(int argc, char *argv[]) parser.AddOption("--help", &help); parser.AddOption("-systemd", &systemd); parser.AddOption("-port", &portOption); - parser.AddOption("-shutdownPort", &shutdownPort); try { @@ -543,7 +543,6 @@ int main(int argc, char *argv[]) << "Options:\n" << " -systemd: Log to systemd journals, wait for jack service.\n" << " -port: Port to listen on e.g. 0.0.0.0:80\n" - << " -shutdownPort: Port for the shutdown service.\n" << "Example:\n" << " pipedal /etc/pipedal/config /etc/pipedal/react -port 0.0.0.0:80 \n" "\n" @@ -558,7 +557,6 @@ int main(int argc, char *argv[]) return error ? EXIT_FAILURE : EXIT_SUCCESS; } - g_ShutdownPort = shutdownPort; if (systemd) { Lv2Log::set_logger(MakeLv2SystemdLogger()); @@ -576,7 +574,7 @@ int main(int argc, char *argv[]) PiPedalConfiguration configuration; try { - configuration.Load(doc_root,web_root); + configuration.Load(doc_root, web_root); } catch (const std::exception &e) { @@ -608,7 +606,6 @@ int main(int argc, char *argv[]) Lv2Log::info("Document root: %s Threads: %d", doc_root.c_str(), (int)threads); server->SetLogHttpRequests(configuration.LogHttpRequests()); - } catch (const std::exception &e) { @@ -648,7 +645,7 @@ int main(int argc, char *argv[]) } } } - + // pre-cache device info before we let audio services run. model.GetAlsaDevices(); } @@ -659,8 +656,8 @@ int main(int argc, char *argv[]) { // Tell systemd we're done. sd_notifyf(0, "READY=1\n" - "MAINPID=%lu", - (unsigned long) getpid()); + "MAINPID=%lu", + (unsigned long)getpid()); } if (!isJackServiceRunning()) @@ -684,7 +681,9 @@ int main(int argc, char *argv[]) { Lv2Log::info("Found Jack service."); sleep(3); // jack needs a little time to get up to speed. - } else { + } + else + { Lv2Log::info("Jack service not started."); } @@ -700,13 +699,24 @@ int main(int argc, char *argv[]) std::shared_ptr downloadIntercept = std::make_shared(&model); server->AddRequestHandler(downloadIntercept); - server->RunInBackground(SIGUSR1); - - sem_wait(&signalSemaphore); - - if (systemd) { - sd_notify(0, "STOPPING=1"); + // Publish DNS Service. + DeviceIdFile deviceIdFile; + deviceIdFile.Load(); + AvahiService service; + service.Announce( + configuration.GetSocketServerPort(),deviceIdFile.deviceName,deviceIdFile.uuid,"pipedal"); + + + + server->RunInBackground(SIGUSR1); + + sem_wait(&signalSemaphore); + + if (systemd) + { + sd_notify(0, "STOPPING=1"); + } } Lv2Log::info("Shutting down gracefully."); diff --git a/src/templates/30-pipedal-apd.conf b/src/templates/30-pipedal-apd.conf new file mode 100644 index 0000000..fd8c808 --- /dev/null +++ b/src/templates/30-pipedal-apd.conf @@ -0,0 +1,24 @@ +/* + * MIT License + * + * Copyright (c) 2022 Robin E. R. 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. + */ + diff --git a/src/templates/30-pipedal-p2p.conf b/src/templates/30-pipedal-p2p.conf new file mode 100644 index 0000000..37457e6 --- /dev/null +++ b/src/templates/30-pipedal-p2p.conf @@ -0,0 +1,11 @@ +server=8.8.8.8 +server=8.8.4.4 + + +interface=p2p-wlan0-0 + dhcp-range=p2p-wlan0,173.23.0.3,172.23.0.127,1h + domain=local + address=/pipedal.local/172.23.0.2 + +except-interface=eth0 +except-interface=wlan0 diff --git a/src/templateJack.service b/src/templates/jack.service.template similarity index 100% rename from src/templateJack.service rename to src/templates/jack.service.template diff --git a/src/template_p2pd.conf b/src/templates/pipedal_p2pd.conf.template similarity index 79% rename from src/template_p2pd.conf rename to src/templates/pipedal_p2pd.conf.template index 6fc4c12..13e5be5 100644 --- a/src/template_p2pd.conf +++ b/src/templates/pipedal_p2pd.conf.template @@ -1,8 +1,3 @@ -${COUNTRY_CODE} -${PIN} -${DEVICE_NAME} -${WIFI_GROUP_FREQUENCY} -${INSTANCE_ID_FILE} /var/pipedal/instance_id # WiFi regdomain 2-letter country code. # see: http://www.davros.org/misc/iso3166.txt country_code=${COUNTRY_CODE} @@ -37,9 +32,8 @@ p2p_go_he=0 # Ipv4 address for the P2P group interface p2p_ip_address=172.24.0.2/16 -# File containing the a globally-unique id identifying the service on this machine. -# Syntax: 0a6045b0-1753-4104-b3e4-b9713b9cc356 -# +# File containing the globally-unique id that identifies the service on this machine +# in this format: 0a6045b0-1753-4104-b3e4-b9713b9cc356 service_guid_file=${INSTANCE_ID_FILE} diff --git a/src/templates/pipedal_p2pd.service.template b/src/templates/pipedal_p2pd.service.template new file mode 100644 index 0000000..749ec81 --- /dev/null +++ b/src/templates/pipedal_p2pd.service.template @@ -0,0 +1,13 @@ +[Unit] +Description=PiPedal P2P Session Manager +After=network.target + +[Service] +ExecStart=${COMMAND} --systemd --log-level debug +Type=notify +WorkingDirectory=/var/pipedal +TimeOutStopSec=5 + + +[Install] +WantedBy=multi-user.target diff --git a/src/templateAdmin.service b/src/templates/pipedaladmind.service.template similarity index 100% rename from src/templateAdmin.service rename to src/templates/pipedaladmind.service.template diff --git a/src/template.service b/src/templates/pipedald.service.template similarity index 100% rename from src/template.service rename to src/templates/pipedald.service.template diff --git a/src/testMain.cpp b/src/testMain.cpp index b428637..92935ee 100644 --- a/src/testMain.cpp +++ b/src/testMain.cpp @@ -21,4 +21,3 @@ #define CATCH_CONFIG_MAIN #include -uint16_t g_ShutdownPort = 0;