Configurable CPU Governor (fixed)

modified:   .vscode/launch.json
	modified:   react/CMakeLists.txt
	modified:   react/public/var/config.json
	new file:   react/src/GovernorSettings.tsx
	modified:   react/src/JackHostStatus.tsx
	new file:   react/src/ListSelectDialog.tsx
	modified:   react/src/PiPedalModel.tsx
	modified:   react/src/SettingsDialog.tsx
	modified:   react/tmp.txt
	modified:   src/BeastServer.cpp
	modified:   src/BeastServer.hpp
	modified:   src/CMakeLists.txt
	modified:   src/ConfigMain.cpp
	new file:   src/CpuGovernor.cpp
	new file:   src/CpuGovernor.hpp
	new file:   src/GovernorSettings.cpp
	new file:   src/GovernorSettings.hpp
	new file:   src/Ipv6Helpers.cpp
	new file:   src/Ipv6Helpers.hpp
	modified:   src/JackHost.cpp
	modified:   src/PiPedalModel.cpp
This commit is contained in:
Robin Davies
2022-03-09 21:35:01 -05:00
parent 5d42ec0753
commit 8ad2de8c72
31 changed files with 1642 additions and 377 deletions
+57 -6
View File
@@ -49,6 +49,40 @@ pipedal::last_modified(const std::filesystem::path &path)
}
}
static std::string getHostName()
{
char buff[512];
if (gethostname(buff,sizeof(buff)) == 0)
{
buff[511] = '\0';
return buff;
}
return "";
}
static std::string getIpv4Address(const std::string interface)
{
int fd = -1;
struct ifreq ifr;
memset(&ifr,0,sizeof(ifr));
fd = socket(AF_INET, SOCK_DGRAM, 0);
/* I want to get an IPv4 IP address */
ifr.ifr_addr.sa_family = AF_INET;
/* I want an IP address attached to "eth0" */
strncpy(ifr.ifr_name, interface.c_str(), IFNAMSIZ-1);
int result = ioctl(fd, SIOCGIFADDR, &ifr);
if (result == -1) return "";
close(fd);
char *name = inet_ntoa(((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr);
if (name == nullptr) return "";
return name;
}
static std::map<std::string, std::string> extensionsToMimeType = {
{".htm", "text/html; charset=UTF-8"},
{".html", "text/html; charset=UTF-8"},
@@ -192,7 +226,8 @@ namespace pipedal
webSocket(webSocket)
{
uri requestUri(webSocket->get_uri()->str().c_str());
fromAddress = requestUri.authority();
fromAddress = SS(webSocket->get_socket().remote_endpoint());
auto pFactory = pServer->GetSocketFactory(requestUri);
if (!pFactory)
@@ -364,11 +399,14 @@ namespace pipedal
con->set_status(websocketpp::http::status_code::ok);
return;
}
for (auto requestHandler : this->request_handlers)
{
if (requestHandler->wants(req.method(), requestUri))
{
std::string fromAddress = SS(con->get_remote_endpoint());
try
{
if (req.method() == HttpVerb::head)
@@ -377,7 +415,7 @@ namespace pipedal
res.set(HttpField::date, HtmlHelper::timeToHttpDate(time(nullptr)));
res.set(HttpField::access_control_allow_origin, origin);
requestHandler->head_response(requestUri, req, res, ec);
requestHandler->head_response(fromAddress,requestUri, req, res, ec);
res.keepAlive(req.keepAlive());
if (ec == std::errc::no_such_file_or_directory)
{
@@ -401,7 +439,7 @@ namespace pipedal
res.set(HttpField::cache_control, "max-age: 31104000"); // cache for a year.
requestHandler->get_response(requestUri, req, res, ec);
requestHandler->get_response(fromAddress,requestUri, req, res, ec);
res.keepAlive(req.keepAlive());
if (ec == std::errc::no_such_file_or_directory)
@@ -425,7 +463,7 @@ namespace pipedal
res.set(HttpField::date, HtmlHelper::timeToHttpDate(time(nullptr)));
res.set(HttpField::access_control_allow_origin, origin);
requestHandler->post_response(requestUri, req, res, ec);
requestHandler->post_response(fromAddress,requestUri, req, res, ec);
if (ec == std::errc::no_such_file_or_directory)
{
@@ -547,15 +585,28 @@ namespace pipedal
m_endpoint.set_close_handler(bind(&BeastServerImpl::on_close, this, _1));
m_endpoint.set_http_handler(bind(&BeastServerImpl::on_http, this, _1));
std::string hostName = getHostName();
if (hostName.length() != 0)
{
std::stringstream ss;
ss << "Listening on " << this->address << ":" << this->port;
ss << "Listening on " << hostName << ".local:" << this->port;
Lv2Log::info(ss.str());
}
std::string ipv4Address = getIpv4Address("eth0");
if (ipv4Address.length() != 0)
{
Lv2Log::info(SS("Listening on " << ipv4Address << ":" << this->port));
}
std::string wifiAddress = getIpv4Address("wlan0");
if (wifiAddress.length() != 0)
{
Lv2Log::info(SS("Listening on Wi-Fi address " << wifiAddress << ":" << this->port));
}
std::stringstream ss;
ss << port;
m_endpoint.listen(this->address, ss.str());
// m_endpoint.listen(this->address, ss.str());
m_endpoint.listen(tcp::v6(),(uint16_t)port);
m_endpoint.start_accept();
// Start IOC service threads.
+34 -1
View File
@@ -3,7 +3,7 @@
#include <string.h>
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/ip/network_v4.hpp>
#include <boost/asio/ip/network_v6.hpp>
#include <cstring>
@@ -164,12 +164,34 @@ public:
HttpResponse &res,
std::error_code &ec) = 0;
virtual void head_response(
const std::string&fromAddress,
const uri&request_uri,
const HttpRequest &req,
HttpResponse &res,
std::error_code &ec) {
head_response(request_uri,req,res,ec);
}
virtual void get_response(
const uri&request_uri,
const HttpRequest &req,
HttpResponse &res,
std::error_code &ec) = 0;
virtual void get_response(
const std::string&fromAddress,
const uri&request_uri,
const HttpRequest &req,
HttpResponse &res,
std::error_code &ec)
{
get_response(request_uri,req,res,ec);
}
virtual void post_response(
const uri&request_uri,
const HttpRequest &req,
@@ -178,6 +200,17 @@ public:
{
get_response(request_uri,req,res,ec);
}
virtual void post_response(
const std::string&fromAddress,
const uri&request_uri,
const HttpRequest &req,
HttpResponse &res,
std::error_code &ec)
{
post_response(
request_uri,req,res,ec);
}
};
class BeastServer {
+5 -1
View File
@@ -102,7 +102,10 @@ add_custom_command(
set (PIPEDAL_SOURCES
PluginPreset.cpp PluginPreset.hpp
Ipv6Helpers.cpp Ipv6Helpers.hpp
PluginPreset.cpp PluginPreset.hpp
CpuGovernor.cpp CpuGovernor.hpp
GovernorSettings.cpp GovernorSettings.hpp
SysExec.cpp SysExec.hpp
BeastServer.cpp BeastServer.hpp HtmlHelper.cpp HtmlHelper.hpp pch.h Uri.cpp Uri.hpp
WifiConfigSettings.hpp WifiConfigSettings.cpp
@@ -233,6 +236,7 @@ add_executable(pipedalshutdownd ShutdownMain.cpp CommandLineParser.hpp
SetWifiConfig.hpp SetWifiConfig.cpp
SystemConfigFile.hpp SystemConfigFile.cpp
SysExec.hpp SysExec.cpp
CpuGovernor.cpp CpuGovernor.hpp
asan_options.cpp
)
target_link_libraries(pipedalshutdownd PRIVATE pthread atomic stdc++fs systemd)
+6 -5
View File
@@ -650,7 +650,7 @@ int main(int argc, char **argv)
<< " (only valid with the -install option). Usually determined " << endl
<< " automatically." << endl
<< endl
<< " --uninstall Remove installed services and service accounts." << endl
<< " --uninstall Remove installed services." << endl
<< endl
<< " --enable Start the pipedal service at boot time." << endl
<< endl
@@ -668,10 +668,11 @@ int main(int argc, char **argv)
<< " web server will listen on 0.0.0.0 (any)." << endl
<< endl
<< " --enable_ap <country_code> <ssid> <wep_password> <channel>" << endl
<< " Enable the Wi-Fi access point. <country_code> is the 2-letter " << endl
<< " ISO-3166 country code for the country in which you are currently" << endl
<< " located. The country code determines which channels may be legally" << endl
<< " used in (and which features need to be enabled) in order to use a " << endl
<< " Enable the Wi-Fi access point. <country_code> is the " << endl
<< " 2-letter ISO-3166 country code for the country in " << endl
<< " which you are currently located. The country code " << endl
<< " determines which channels may be legally used in (and " << endl
<< " which features need to be enabled) in order to use a " << endl
<< " Wi-Fi channel in your legislative regime." << endl
<< endl
<< " --disable_ap Disabled the Wi-Fi access point." << endl
+141
View File
@@ -0,0 +1,141 @@
// 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 "CpuGovernor.hpp"
#include "ss.hpp"
#include <fstream>
#include <filesystem>
#include "PiPedalException.hpp"
#include <unistd.h>
using namespace pipedal;
#ifdef __WIN32__
// not a feature on Win32.
void pipedal::SetCpuGovernor(const std::string &governor) { }
std::vector<std::string> pipedal::GetAvailableGovernors() { return std::vector<std::string>(); }
#else
static const int SYSFS_RETRIES = 3;
std::string pipedal::GetCpuGovernor()
{
std::string result;
try {
std::ifstream f("/sys/devices/system/cpu/cpu0/cpufreq/scaling_governor");
f >> result;
} catch (const std::exception &)
{
}
return result;
}
static bool writeAndVerify(std::filesystem::path&sysfsPath, const std::string &value)
{
// return if the value has already been set.
{
std::ifstream f;
f.open(sysfsPath);
if (f.is_open())
{
std::string line;
std::getline(f,line);
if (!f.fail() && value == line)
{
return true;
}
}
}
for (int i = 0; i < SYSFS_RETRIES; ++i)
{
{
// write the value.
// write the value.
std::ofstream f;
f.open(sysfsPath);
if (f.is_open())
{
f << value;
} else {
std::cout << SS("Can't open " << sysfsPath << " for writing.") << std::endl;
sleep(1);
continue;
}
}
// verify that we wrote it
{
std::ifstream f;
f.open(sysfsPath);
if (f.is_open())
{
std::string line;
std::getline(f,line);
if (value == line)
{
return true;
}
std::cout << "Read value: " << line << "Expected value: " << value << std::endl;
}
std::cout << "Failed to update " << sysfsPath << std::endl;
}
sleep(1);
}
return false;
}
void pipedal::SetCpuGovernor(const std::string &governor) {
// using sysfs
int nCpu = 0;
while (true)
{
std::filesystem::path base = SS("/sys/devices/system/cpu/cpu" << nCpu);
if (!std::filesystem::is_directory(base))
break;
std::filesystem::path sysFsPath = SS("/sys/devices/system/cpu/cpu" << nCpu << "/cpufreq/scaling_governor");
if (!writeAndVerify(sysFsPath,governor))
{
throw PiPedalException(SS("Write to " << sysFsPath << " failed."));
break;
}
++nCpu;
}
}
std::vector<std::string> pipedal::GetAvailableGovernors() {
return std::vector<std::string> {
"performance",
"ondemand",
"powersave"
};
}
#endif
void SetCpuGovernor(const std::string &governor);
std::vector<std::string> GetAvailableGovernors();
+31
View File
@@ -0,0 +1,31 @@
// 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.
#pragma once
#include <string>
#include <vector>
namespace pipedal {
std::string GetCpuGovernor();
void SetCpuGovernor(const std::string &governor);
std::vector<std::string> GetAvailableGovernors();
};
+27
View File
@@ -0,0 +1,27 @@
// 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 "GovernorSettings.hpp"
using namespace pipedal;
JSON_MAP_BEGIN(GovernorSettings)
JSON_MAP_REFERENCE(GovernorSettings, governor)
JSON_MAP_REFERENCE(GovernorSettings, governors)
JSON_MAP_END()
+36
View File
@@ -0,0 +1,36 @@
// 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.
#pragma once
#include "json.hpp"
#include <string>
#include <vector>
namespace pipedal {
class GovernorSettings {
public:
std::string governor_;
std::vector<std::string> governors_;
DECLARE_JSON_MAP(GovernorSettings);
};
}
+360
View File
@@ -0,0 +1,360 @@
// 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 "Ipv6Helpers.hpp"
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "ss.hpp"
#include <sys/types.h>
#include <ifaddrs.h>
#include <memory.h>
#include <exception>
#include <netdb.h>
using namespace pipedal;
std::string pipedal::GetLinkLocalAddress(const std::string fromAddress);
static bool IsIpv4MappedAddress(const struct in6_addr &inetAddr6)
{
return IN6_IS_ADDR_V4MAPPED(&inetAddr6) != 0;
}
static uint32_t GetIpv4MappedAddress(const struct in6_addr &inetAddr6)
{
if (!IsIpv4MappedAddress(inetAddr6))
{
return 0;
}
uint8_t *p = (uint8_t *)&inetAddr6;
return htonl(*(uint32_t *)(p + 12));
}
static bool ipv6NetmaskCompare(const struct in6_addr &left, const struct in6_addr &right, const struct in6_addr &mask)
{
uint8_t *pLeft = (uint8_t *)&left;
uint8_t *pRight = (uint8_t *)&right;
uint8_t *pMask = (uint8_t *)&mask;
for (int i = 0; i < 16; ++i)
{
if ((pLeft[i] & pMask[i]) != (pRight[i] & pMask[i]))
{
return false;
}
}
return true;
}
static bool IsIpv4OnLocalSubnet(uint32_t ipv4Addres)
{
bool result = false;
struct ifaddrs *ifap = nullptr;
if (getifaddrs(&ifap) != 0)
return false;
for (ifaddrs *p = ifap; p != nullptr; p = p->ifa_next)
{
if (p->ifa_addr->sa_family == AF_INET && p->ifa_addr != nullptr && p->ifa_netmask != nullptr)
{ // TODO: Add support for AF_INET6
uint32_t netmask = htonl(((sockaddr_in *)(p->ifa_netmask))->sin_addr.s_addr);
uint32_t ifAddr = htonl(((sockaddr_in *)(p->ifa_addr))->sin_addr.s_addr);
if ((netmask & ifAddr) == (netmask & ipv4Addres))
{
result = true;
break;
}
}
}
freeifaddrs(ifap);
return result;
}
static std::string StripPortNumber(const std::string &fromAddress)
{
std::string address = fromAddress;
if (address.size() == 0)
return fromAddress;
char lastChar = address[address.size() - 1];
size_t pos = address.find_last_of(':');
// if ipv6, make sure we found an actual port address.
size_t posBracket = address.find_last_of(']');
if (posBracket != std::string::npos && pos != std::string::npos)
{
if (posBracket > pos)
{
pos = std::string::npos;
}
}
if (pos != std::string::npos)
{
address = address.substr(0, pos);
}
return address;
}
bool pipedal::IsOnLocalSubnet(const std::string &fromAddress)
{
std::string address = StripPortNumber(fromAddress);
if (address.length() == 0)
return false;
bool result = false;
if (address[0] != '[')
{
// ipv4
struct in_addr inetAddr;
memset(&inetAddr, 0, sizeof(inetAddr));
if (inet_pton(AF_INET, address.c_str(), &inetAddr) == 1)
{
uint32_t remoteAddress = htonl(inetAddr.s_addr);
result = IsIpv4OnLocalSubnet(remoteAddress);
}
}
else
{
// ipv6
if (address[0] != '[' || address[address.length() - 1] != ']')
return false;
address = address.substr(1, address.length() - 2);
// strip scope if neccessary.
auto pos = address.find_last_of('%');
if (pos != std::string::npos)
{
address = address.substr(0, pos);
}
struct in6_addr inetAddr6;
memset(&inetAddr6, 0, sizeof(inetAddr6));
if (inet_pton(AF_INET6, address.c_str(), &inetAddr6) == 1)
{
int32_t remoteAddress = -1;
// cases:
// [::FFFF:ipv4 address]
// [FE80:: ] link local.
// [FEC0:: ] site local.
// others?
if (IsIpv4MappedAddress(inetAddr6))
{
uint32_t remoteAddress = GetIpv4MappedAddress(inetAddr6);
return IsIpv4OnLocalSubnet(remoteAddress);
}
if (IN6_IS_ADDR_LINKLOCAL(&inetAddr6))
{
return true;
}
if (IN6_IS_ADDR_SITELOCAL(&inetAddr6))
{
return true;
}
return false;
#if JUNK
// if the address is global, we have to reject it.
// no point in going further.
struct ifaddrs *ifap = nullptr;
if (getifaddrs(&ifap) != 0)
return false;
const int SITE_LOCAL_SCOPE = 5;
for (ifaddrs *p = ifap; p != nullptr; p = p->ifa_next)
{
if (p->ifa_addr->sa_family == AF_INET6 && p->ifa_addr != nullptr && p->ifa_netmask != nullptr)
{
struct sockaddr_in6 *pAddr = (struct sockaddr_in6 *)(p->ifa_addr);
struct sockaddr_in6 *pNetMask = (struct sockaddr_in6 *)(p->ifa_netmask);
if (pAddr->sin6_scope_id <= SITE_LOCAL_SCOPE)
{
if (ipv6NetmaskCompare(inetAddr6, pAddr->sin6_addr, pNetMask->sin6_addr))
{
result = true;
break;
}
}
}
}
freeifaddrs(ifap);
#endif
}
}
return result;
}
static std::string GetInterfaceForIp4Address(uint32_t ipv4Address)
{
struct ifaddrs *ifap = nullptr;
if (getifaddrs(&ifap) != 0)
return "";
std::string result;
for (ifaddrs *p = ifap; p != nullptr; p = p->ifa_next)
{
if (p->ifa_addr->sa_family == AF_INET && p->ifa_addr != nullptr && p->ifa_netmask != nullptr)
{ // TODO: Add support for AF_INET6
uint32_t netmask = htonl(((sockaddr_in *)(p->ifa_netmask))->sin_addr.s_addr);
uint32_t ifAddr = htonl(((sockaddr_in *)(p->ifa_addr))->sin_addr.s_addr);
if ((netmask & ifAddr) == (netmask & ipv4Address))
{
result = p->ifa_name;
break;
}
}
}
freeifaddrs(ifap);
return result;
}
static std::string GetInterfaceForIp6Address(const in6_addr inetAddr6)
{
struct ifaddrs *ifap = nullptr;
if (getifaddrs(&ifap) != 0)
return "";
std::string result;
for (ifaddrs *p = ifap; p != nullptr; p = p->ifa_next)
{
if (p->ifa_addr->sa_family == AF_INET6 && p->ifa_addr != nullptr && p->ifa_netmask != nullptr)
{ // TODO: Add support for AF_INET6
struct sockaddr_in6 *pAddr = (struct sockaddr_in6 *)(p->ifa_addr);
struct sockaddr_in6 *pNetMask = (struct sockaddr_in6 *)(p->ifa_netmask);
if (ipv6NetmaskCompare(inetAddr6, pAddr->sin6_addr, pNetMask->sin6_addr))
{
result = p->ifa_name;
break;
}
}
}
freeifaddrs(ifap);
return result;
}
static std::string GetLinkLocalAddressForInterface(const std::string &name)
{
struct ifaddrs *ifap = nullptr;
if (getifaddrs(&ifap) != 0)
return "";
std::string result;
for (ifaddrs *p = ifap; p != nullptr; p = p->ifa_next)
{
if (p->ifa_addr->sa_family == AF_INET6 && p->ifa_addr != nullptr && p->ifa_netmask != nullptr)
{ // TODO: Add support for AF_INET6
struct sockaddr_in6 *pAddr = (struct sockaddr_in6 *)(p->ifa_addr);
if (IN6_IS_ADDR_LINKLOCAL(&(pAddr->sin6_addr)))
{
if (name.length() == 0 || name == p->ifa_name)
{
const int BUFSIZE = 128;
char host[BUFSIZE];
if (getnameinfo(p->ifa_addr, sizeof(struct sockaddr_in6), host, BUFSIZE, NULL, 0, NI_NUMERICHOST) == 0)
{
host[BUFSIZE - 1] = '\0';
// trim the interface spec if present
for (char *p = host; *p != 0; ++p)
{
if (*p == '%')
{
*p = '\0';
break;
}
}
result = SS('[' << host << ']');
break;
}
}
}
}
}
freeifaddrs(ifap);
return result;
}
std::string pipedal::GetLinkLocalAddress(const std::string fromAddress)
{
std::string address = StripPortNumber(fromAddress);
std::string result;
if (address[0] != '[')
{
// ipv4
struct in_addr inetAddr;
memset(&inetAddr, 0, sizeof(inetAddr));
if (inet_pton(AF_INET, address.c_str(), &inetAddr) == 1)
{
uint32_t remoteAddress = htonl(inetAddr.s_addr);
std::string interfaceName = GetInterfaceForIp4Address(remoteAddress);
result = GetLinkLocalAddressForInterface(interfaceName);
}
}
else
{
// ipv6
if (address[0] != '[' || address[address.length() - 1] != ']')
throw std::invalid_argument("Bad address.");
address = address.substr(1, address.length() - 2);
// strip scope if neccessary.
auto pos = address.find_last_of('%');
if (pos != std::string::npos)
{
address = address.substr(0, pos);
}
struct in6_addr inetAddr6;
memset(&inetAddr6, 0, sizeof(inetAddr6));
if (inet_pton(AF_INET6, address.c_str(), &inetAddr6) == 1)
{
int32_t remoteAddress = -1;
// cases:
// [::FFFF:ipv4 address]
// [FE80:: ] link local.
// [FEC0:: ] site local.
// others?
if (IN6_IS_ADDR_LINKLOCAL(&inetAddr6))
{
return address;
}
if (IN6_IS_ADDR_SITELOCAL(&inetAddr6))
{
return address;
}
std::string interfaceName;
if (IsIpv4MappedAddress(inetAddr6))
{
uint32_t remoteAddress = GetIpv4MappedAddress(inetAddr6);
interfaceName = GetInterfaceForIp4Address(remoteAddress);
}
else
{
interfaceName = GetInterfaceForIp6Address(inetAddr6);
}
result = GetLinkLocalAddressForInterface(interfaceName);
}
}
if (result == "")
{
result = GetLinkLocalAddressForInterface("");
}
return result;
}
+31
View File
@@ -0,0 +1,31 @@
// 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.
#pragma once
namespace pipedal {
std::string GetLinkLocalAddress(const std::string fromAddress);
bool IsOnLocalSubnet(const std::string&fromAddress);
}
+2 -9
View File
@@ -33,6 +33,7 @@ using namespace pipedal;
#include <thread>
#include <semaphore.h>
#include "VuUpdate.hpp"
#include "CpuGovernor.hpp"
#include "RingBuffer.hpp"
#include "RingBufferReader.hpp"
@@ -99,15 +100,7 @@ static void GetCpuFrequency(uint64_t*freqMin,uint64_t*freqMax)
}
static std::string GetGovernor()
{
std::string result;
try {
std::ifstream f("/sys/devices/system/cpu/cpu0/cpufreq/scaling_governor");
f >> result;
} catch (const std::exception &)
{
}
return result;
return pipedal::GetCpuGovernor();
}
+59 -8
View File
@@ -26,6 +26,7 @@
#include "PiPedalConfiguration.hpp"
#include "ShutdownClient.hpp"
#include "SplitEffect.hpp"
#include "CpuGovernor.hpp"
#ifndef NO_MLOCK
#include <sys/mman.h>
@@ -76,14 +77,31 @@ void PiPedalModel::Close()
PiPedalModel::~PiPedalModel()
{
CurrentPreset currentPreset;
currentPreset.modified_ = this->hasPresetChanged;
currentPreset.preset_ = this->pedalBoard;
storage.SaveCurrentPreset(currentPreset);
if (jackHost)
try {
ShutdownClient::UnmonitorGovernor();
} catch (...) // noexcept here!
{
jackHost->Close();
}
try {
CurrentPreset currentPreset;
currentPreset.modified_ = this->hasPresetChanged;
currentPreset.preset_ = this->pedalBoard;
storage.SaveCurrentPreset(currentPreset);
} catch (...)
{
}
try {
if (jackHost)
{
jackHost->Close();
}
} catch (...)
{
}
}
@@ -134,7 +152,7 @@ void PiPedalModel::Load(const PiPedalConfiguration &configuration)
this->webRoot = configuration.GetWebRoot();
this->jackServerSettings.ReadJackConfiguration();
ShutdownClient::MonitorGovernor(storage.GetGovernorSettings());
// lv2Host.Load(configuration.GetLv2Path().c_str());
@@ -594,6 +612,39 @@ bool PiPedalModel::RenamePreset(int64_t clientId, int64_t instanceId, const std:
}
}
GovernorSettings PiPedalModel::GetGovernorSettings()
{
{
std::lock_guard<std::recursive_mutex> guard(mutex);
GovernorSettings result;
result.governor_ = storage.GetGovernorSettings();
result.governors_ = pipedal::GetAvailableGovernors();
return result;
}
}
void PiPedalModel::SetGovernorSettings(const std::string& governor)
{
std::lock_guard<std::recursive_mutex> guard(mutex);
ShutdownClient::SetGovernorSettings(governor);
this->storage.SetGovernorSettings(governor);
IPiPedalModelSubscriber **t = new IPiPedalModelSubscriber *[this->subscribers.size()];
{
for (size_t i = 0; i < subscribers.size(); ++i)
{
t[i] = this->subscribers[i];
}
size_t n = this->subscribers.size();
for (size_t i = 0; i < n; ++i)
{
t[i]->OnGovernorSettingsChanged(governor);
}
delete[] t;
}
}
void PiPedalModel::SetWifiConfigSettings(const WifiConfigSettings &wifiConfigSettings)
{
std::lock_guard<std::recursive_mutex> guard(mutex);
+5
View File
@@ -20,6 +20,7 @@
#pragma once
#include <mutex>
#include "Lv2Host.hpp"
#include "GovernorSettings.hpp"
#include "PedalBoard.hpp"
#include "Storage.hpp"
#include "Banks.hpp"
@@ -57,6 +58,7 @@ public:
virtual void OnNotifyMidiListener(int64_t clientHandle, bool isNote, uint8_t noteOrControl) = 0;
virtual void OnNotifyAtomOutput(int64_t clientModel,uint64_t instanceId,const std::string&atomType, const std::string&atomJson) = 0;
virtual void OnWifiConfigSettingsChanged(const WifiConfigSettings&wifiConfigSettings) = 0;
virtual void OnGovernorSettingsChanged(const std::string &governor) = 0;
virtual void Close() = 0;
};
@@ -193,6 +195,9 @@ public:
void SetWifiConfigSettings(const WifiConfigSettings&wifiConfigSettings);
WifiConfigSettings GetWifiConfigSettings();
void SetGovernorSettings(const std::string& governor);
GovernorSettings GetGovernorSettings();
+20 -1
View File
@@ -28,6 +28,7 @@
#include "JackConfiguration.hpp"
#include <future>
#include <atomic>
#include "Ipv6Helpers.hpp"
#include "ShutdownClient.hpp"
#include "WifiConfigSettings.hpp"
@@ -804,6 +805,17 @@ public:
this->Reply(replyTo,"setJackserverSettings");
}
else if (message == "setGovernorSettings") {
std::string governor;
pReader->read(&governor);
std::string fromAddress = this->getFromAddress();
if (!IsOnLocalSubnet(fromAddress))
{
throw PiPedalException("Permission denied. Not on local subnet.");
}
this->model.SetGovernorSettings(governor);
this->Reply(replyTo,"setGovernorSettings");
}
else if (message == "setWifiConfigSettings") {
WifiConfigSettings wifiConfigSettings;
pReader->read(&wifiConfigSettings);
@@ -812,7 +824,7 @@ public:
throw PiPedalException("Can't change server settings when running interactively.");
}
std::string fromAddress = this->getFromAddress();
if (!ShutdownClient::IsOnLocalSubnet(fromAddress))
if (!IsOnLocalSubnet(fromAddress))
{
throw PiPedalException("Permission denied. Not on local subnet.");
}
@@ -824,6 +836,10 @@ public:
else if (message == "getWifiConfigSettings") {
this->Reply(replyTo, "getWifiConfigSettings", model.GetWifiConfigSettings());
}
else if (message == "getGovernorSettings") {
this->Reply(replyTo, "getGovernorSettings", model.GetGovernorSettings());
}
else if (message == "getJackServerSettings") {
@@ -1461,6 +1477,9 @@ public:
virtual void OnWifiConfigSettingsChanged(const WifiConfigSettings&wifiConfigSettings) {
Send("onWifiConfigSettingsChanged",wifiConfigSettings);
}
virtual void OnGovernorSettingsChanged(const std::string& governor) {
Send("onGovernorSettingsChanged",governor);
}
virtual void OnPedalBoardChanged(int64_t clientId, const PedalBoard &pedalBoard)
{
+52 -46
View File
@@ -27,6 +27,7 @@
#include <sstream>
#include <sys/types.h>
#include <ifaddrs.h>
#include "Ipv6Helpers.hpp"
using namespace pipedal;
@@ -165,57 +166,12 @@ bool ShutdownClient::SetJackServerConfiguration(const JackServerSettings & jackS
return WriteMessage(s.str().c_str());
}
bool ShutdownClient::IsOnLocalSubnet(const std::string&fromAddress)
{
std::string address = fromAddress;
if (address.size() == 0) return false;
char lastChar = address[address.size()-1];
if (address[0] != '[' || lastChar != ']') // i.e. not an ipv6 address;
{
size_t pos = address.find_last_of(':');
if (pos != std::string::npos)
{
address = address.substr(0,pos);
}
}
if (address[0] == '[') return false;
char buf[512];
struct in_addr inetAddr;
memset(&inetAddr,0,sizeof(inetAddr));
if (inet_pton(AF_INET,address.c_str(),&inetAddr) == 0) {
return false;
}
uint32_t remoteAddress = htonl(inetAddr.s_addr);
struct ifaddrs *ifap = nullptr;
if (getifaddrs(&ifap) != 0) return false;
bool result = false;
for (ifaddrs*p = ifap; p != nullptr; p = p->ifa_next) {
if (p->ifa_addr->sa_family == AF_INET && p->ifa_addr != nullptr && p->ifa_netmask != nullptr) { // TODO: Add support for AF_INET6
uint32_t netmask = htonl(((sockaddr_in*)(p->ifa_netmask))->sin_addr.s_addr);
uint32_t ifAddr = htonl(((sockaddr_in*)(p->ifa_addr))->sin_addr.s_addr);
if ((netmask & ifAddr) == (netmask & remoteAddress))
{
result = true;
break;
}
}
}
freeifaddrs(ifap);
return result;
}
void ShutdownClient::SetWifiConfig(const WifiConfigSettings & settings)
{
if (!CanUseShutdownClient())
{
throw PiPedalException("Can't use ShutdownClient when running interactively.");
throw PiPedalException("Can't perform this operation when debugging.");
}
std::stringstream cmd;
cmd << "WifiConfigSettings ";
@@ -227,3 +183,53 @@ void ShutdownClient::SetWifiConfig(const WifiConfigSettings & settings)
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.");
}
}
+4 -1
View File
@@ -25,14 +25,17 @@
namespace pipedal {
class ShutdownClient {
static bool WriteMessage(const char*message);
public:
static bool CanUseShutdownClient();
static bool RequestShutdown(bool restart);
static bool SetJackServerConfiguration(const JackServerSettings & jackServerSettings);
static bool IsOnLocalSubnet(const std::string&fromAddress);
static void SetWifiConfig(const WifiConfigSettings & settings);
static void SetGovernorSettings(const std::string & governor);
static void MonitorGovernor(const std::string &governor);
static void UnmonitorGovernor();
};
} // namespace
+212 -103
View File
@@ -23,6 +23,7 @@
#include "pch.h"
#include "CommandLineParser.hpp"
#include "CpuGovernor.hpp"
#include <iostream>
#include <cstdint>
#include <boost/asio.hpp>
@@ -40,7 +41,10 @@
#include "SetWifiConfig.hpp"
#include "SysExec.hpp"
#include <filesystem>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <chrono>
using namespace std;
using namespace pipedal;
@@ -50,15 +54,122 @@ using ip::tcp;
const size_t MAX_LENGTH = 128;
static bool startsWith(const std::string&s, const char*text)
class GovernorMonitorThread
{
if (s.length() < strlen(text)) return false;
const char*sp = s.c_str();
public:
~GovernorMonitorThread()
{
Stop();
}
void Start(std::string governor)
{
std::unique_lock<std::mutex> lock(mutex);
if (!pThread)
{
this->governor = governor;
cancelled = false;
wake = false;
pThread = std::make_unique<std::thread>([this]()
{ this->ServiceProc(); });
} else {
this->governor = governor;
wake = true;
lock.unlock();
cv.notify_one();
}
}
void Stop()
{
std::unique_lock<std::mutex> lock(mutex);
if (pThread)
{
wake = true;
cancelled = true;
lock.unlock();
cv.notify_one();
pThread->join();
pThread.reset();
}
}
void SetGovernor(const std::string &governor)
{
{
std::unique_lock<std::mutex> lock(mutex);
wake = true;
this->governor = governor;
}
cv.notify_one();
}
private:
bool wake = false;
bool cancelled = false;
std::string savedGovernor;
void ServiceProc()
{
savedGovernor = pipedal::GetCpuGovernor();
pipedal::SetCpuGovernor(this->governor);
while (true)
{
bool cancelled;
std::string governor;
{
std::unique_lock<std::mutex> lock(mutex);
auto timeToWaitFor = std::chrono::system_clock::now() + 5000ms;
cv.wait_until(
lock,
timeToWaitFor,
[this]()
{ return wake; });
wake = false;
governor = this->governor;
cancelled = this->cancelled;
}
if (cancelled) {
break;
}
std::string activeGovernor = pipedal::GetCpuGovernor();
if (activeGovernor != governor)
{
// somebody set it so they must have wanted it.
// save the value so that we can restore it when done.
savedGovernor = activeGovernor;
// but insist on using ours!!
pipedal::SetCpuGovernor(governor);
}
}
pipedal::SetCpuGovernor(savedGovernor);
}
std::unique_ptr<std::thread> pThread;
std::string governor;
std::mutex mutex;
bool canceled;
std::condition_variable cv;
};
GovernorMonitorThread governorMonitorThread;
void StopGovernorMonitorThread()
{
governorMonitorThread.Stop();
}
void StartGovernorMonitorThread(const std::string&governor)
{
governorMonitorThread.Start(governor);
}
static bool startsWith(const std::string &s, const char *text)
{
if (s.length() < strlen(text))
return false;
const char *sp = s.c_str();
while (*text)
{
if (*text++ != *sp++) return false;
if (*text++ != *sp++)
return false;
}
return true;
}
@@ -67,7 +178,7 @@ static std::vector<std::string> tokenize(std::string value)
std::vector<std::string> result;
std::stringstream s(value);
std::string item;
while (std::getline(s,item,' '))
while (std::getline(s, item, ' '))
{
if (item.length() != 0)
result.push_back(item);
@@ -75,26 +186,17 @@ static std::vector<std::string> tokenize(std::string value)
return result;
}
static void CaptureAccessPoint(const std::string gatewayAddress)
{
}
static void ReleaseAccessPoint(const std::string gatewayAddress)
{
}
void delayedRestartProc()
static void delayedRestartProc()
{
sleep(1); // give a chance for websocket messages to propagate.
Lv2Log::error("Delayed Restart");
std::string pipedalConfigPath = std::filesystem::path(getSelfExePath()).parent_path() / "pipedalconfig";
std::stringstream s;
s << pipedalConfigPath.c_str() << " --restart --excludeShutdownService";
if (::system(s.str().c_str())!= EXIT_SUCCESS)
s << pipedalConfigPath.c_str() << " --restart --excludeShutdownService";
if (::system(s.str().c_str()) != EXIT_SUCCESS)
{
Lv2Log::error("Delayed Restart failed.");
}
}
@@ -110,8 +212,6 @@ bool setJackConfiguration(JackServerSettings serverSettings)
std::thread delayedRestartThread(delayedRestartProc);
delayedRestartThread.detach();
return true;
}
class Reader : public std::enable_shared_from_this<Reader>
@@ -162,7 +262,7 @@ private:
return;
}
socket.async_write_some(
boost::asio::buffer(response.c_str()+ writePosition, response.length()-writePosition),
boost::asio::buffer(response.c_str() + writePosition, response.length() - writePosition),
boost::bind(&Reader::HandleWrite,
shared_from_this(),
boost::asio::placeholders::error,
@@ -185,7 +285,7 @@ private:
if (c == '\n')
{
std::string command = message.str();
cout << "Received command: " << command << endl;
cout << command << endl;
HandleCommand(command);
socket.close();
return;
@@ -202,59 +302,88 @@ private:
socket.close();
}
}
void HandleCommand(const std::string &s)
void HandleCommand(const std::string &text)
{
int result = -1;
try {
if (startsWith(s,"WifiConfigSettings "))
std::string command;
std::string args;
auto pos = text.find_first_of(' ');
if (pos == std::string::npos)
{
command = text;
}
else
{
command = text.substr(0, pos);
args = text.substr(pos + 1);
}
try
{
if (command == "UnmonitorGovernor")
{
std::string json = s.substr(strlen("WifiConfigSettings "));
std::stringstream ss(json);
StopGovernorMonitorThread();
result = 0;
}
else if (command == "MonitorGovernor")
{
std::stringstream ss(args);
std::string governor;
try
{
json_reader reader(ss);
reader.read(&governor);
}
catch (const std::exception &e)
{
throw PiPedalArgumentException("Invalid arguments.");
}
StartGovernorMonitorThread(governor);
result = 0;
}
else if (command == "GovernorSettings")
{
std::stringstream ss(args);
std::string governor;
try
{
json_reader reader(ss);
reader.read(&governor);
}
catch (const std::exception &e)
{
throw PiPedalArgumentException("Invalid arguments.");
}
governorMonitorThread.SetGovernor(governor);
result = 0;
}
else if (command == "WifiConfigSettings ")
{
std::stringstream ss(args);
WifiConfigSettings settings;
try {
try
{
json_reader reader(ss);
reader.read(&settings);
} catch (const std::exception &e)
}
catch (const std::exception &e)
{
throw PiPedalArgumentException("Invalid arguments.");
}
pipedal::SetWifiConfig(settings);
result = 0;
} else if (startsWith(s,"release_ap "))
{
std::vector<std::string> argv = tokenize(s);
if (argv.size() == 2) {
ReleaseAccessPoint(argv[1]);
result = 0;
} else {
throw PiPedalArgumentException("Invalid arguments.");
}
} else if (startsWith(s,"capture_ap "))
{
std::vector<std::string> argv = tokenize(s);
if (argv.size() == 2) {
CaptureAccessPoint(argv[1]);
result = 0;
} else {
throw PiPedalArgumentException("Invalid arguments.");
}
} else if (s == "shutdown")
}
else if (command == "shutdown")
{
result = sysExec("/usr/sbin/shutdown -P now");
}
else if (s == "restart")
else if (command == "restart")
{
result = sysExec("/usr/sbin/shutdown -r now");
} else if (startsWith(s,"setJackConfiguration "))
}
else if (command == "setJackConfiguration")
{
auto remainder = s.substr(strlen("setJackConfiguration "));
std::stringstream input(remainder);
std::stringstream input(args);
JackServerSettings serverSettings;
json_reader reader(input);
@@ -263,12 +392,18 @@ private:
if (input.fail())
{
result = -1;
} else {
result = setJackConfiguration(serverSettings) ? 0: -1;
}
else
{
result = setJackConfiguration(serverSettings) ? 0 : -1;
}
}
} catch (const std::exception &e)
else
{
throw std::invalid_argument(SS("Unknown command:" << command));
}
}
catch (const std::exception &e)
{
std::stringstream t;
t << "-2 " << e.what() << "\n";
@@ -284,7 +419,6 @@ private:
this->writePosition = 0;
WriteSome();
}
};
class Server
@@ -314,16 +448,16 @@ private:
}
public:
//constructor for accepting connection from client
// 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)
io_service(io_service)
{
StartAccept();
}
~Server() {
~Server()
{
}
};
@@ -332,29 +466,26 @@ void runServer(uint16_t port)
asio::ip::tcp::endpoint endpoint(ip::make_address_v4("127.0.0.1"), port);
asio::io_service ios;
try {
Server server(ios,endpoint);
try
{
Server server(ios, endpoint);
ios.run();
cout << "Processing terminated." << endl;
} catch (const std::exception & e)
}
catch (const std::exception &e)
{
cout << "Error: " << e.what() << endl;
}
}
int main(int argc, char **argv)
{
Lv2Log::set_logger(MakeLv2SystemdLogger());
CommandLineParser parser;
uint16_t port = 0;
std::string captureAccessPoint;
std::string releaseAccessPoint;
parser.AddOption("-port", &port);
parser.AddOption("-captureAP",&captureAccessPoint);
parser.AddOption("-releaseAP",&releaseAccessPoint);
try
{
@@ -369,34 +500,12 @@ int main(int argc, char **argv)
cout << "Error: " << e.what() << endl;
return EXIT_FAILURE;
}
if (captureAccessPoint.length() != 0)
if (port == 0)
{
try{
CaptureAccessPoint(captureAccessPoint);
} catch (const std::exception &e)
{
std::cerr << "Error: " << e.what() << std::endl;
return EXIT_FAILURE;
}
std::cout << "Access point captured." << std::endl;
} else if (releaseAccessPoint.length() != 0)
{
try{
ReleaseAccessPoint(releaseAccessPoint);
} catch (const std::exception &e)
{
std::cerr << "Error: " << e.what() << std::endl;
return EXIT_FAILURE;
}
std::cout << "Access point released." << std::endl;
} else {
if (port == 0) {
std::cerr << "Error: port not specified." << std::endl;
return EXIT_FAILURE;
}
runServer(port);
std::cerr << "Error: port not specified." << std::endl;
return EXIT_FAILURE;
}
runServer(port);
governorMonitorThread.Stop();
return EXIT_SUCCESS;
}
+45
View File
@@ -32,6 +32,7 @@ const char *BANK_EXTENSION = ".bank";
const char *BANKS_FILENAME = "index.banks";
#define WIFI_CONFIG_SETTINGS_FILENAME "wifiConfigSettings.json";
#define GOVERNOR_SETTINGS_FILENAME "governorSettings.json";
Storage::Storage()
{
@@ -182,6 +183,7 @@ void Storage::Initialize()
{
}
LoadWifiConfigSettings();
LoadGovernorSettings();
}
@@ -802,6 +804,25 @@ int64_t Storage::UploadBank(BankFile&bankFile,int64_t uploadAfter)
return lastBank;
}
void Storage::SetGovernorSettings(const std::string & governor)
{
std::filesystem::path path = this->dataRoot / GOVERNOR_SETTINGS_FILENAME;
{
std::ofstream f(path);
if (!f.is_open())
{
throw PiPedalException("Unable to write to " + ((std::string)path));
}
json_writer writer(f);
writer.write(governor);
this->governorSettings = governor;
}
}
std::string Storage::GetGovernorSettings() const
{
return this->governorSettings;
}
void Storage::SetWifiConfigSettings(const WifiConfigSettings & wifiConfigSettings)
{
@@ -839,7 +860,30 @@ void Storage::SetWifiConfigSettings(const WifiConfigSettings & wifiConfigSetting
}
this->wifiConfigSettings = copyToStore;
}
void Storage::LoadGovernorSettings()
{
std::filesystem::path path = this->dataRoot / GOVERNOR_SETTINGS_FILENAME;
try {
if (std::filesystem::is_regular_file(path))
{
std::ifstream f(path);
if (!f.is_open())
{
throw PiPedalException("Unable to write to " + ((std::string)path));
}
json_reader reader(f);
std::string governorSettings;
reader.read(&governorSettings);
this->governorSettings = governorSettings;
}
} catch (const std::exception&)
{
this->governorSettings = "performance";
}
this->wifiConfigSettings.valid_ = true;
}
void Storage::LoadWifiConfigSettings()
{
std::filesystem::path path = this->dataRoot / WIFI_CONFIG_SETTINGS_FILENAME;
@@ -1137,6 +1181,7 @@ uint64_t Storage::CopyPluginPreset(const std::string&pluginUri,uint64_t presetId
}
JSON_MAP_BEGIN(CurrentPreset)
JSON_MAP_REFERENCE(CurrentPreset,modified)
JSON_MAP_REFERENCE(CurrentPreset,preset)
+6
View File
@@ -72,6 +72,7 @@ private:
bool isJackChannelSelectionValid = false;
JackChannelSelection jackChannelSelection;
WifiConfigSettings wifiConfigSettings;
std::string governorSettings = "performance";
public:
Storage();
void Initialize();
@@ -85,6 +86,7 @@ public:
void LoadWifiConfigSettings();
void LoadGovernorSettings();
void LoadBank(int64_t instanceId);
const PedalBoard& GetCurrentPreset();
void SaveCurrentPreset(const PedalBoard&pedalBoard);
@@ -116,6 +118,10 @@ public:
void SetWifiConfigSettings(const WifiConfigSettings & wifiConfigSettings);
WifiConfigSettings GetWifiConfigSettings();
void SetGovernorSettings(const std::string & governor);
std::string GetGovernorSettings() const;
void SaveCurrentPreset(const CurrentPreset &currentPreset);
bool RestoreCurrentPreset(CurrentPreset*pResult);
+35 -5
View File
@@ -27,12 +27,13 @@
#include <boost/system/error_code.hpp>
#include <filesystem>
#include "PiPedalConfiguration.hpp"
#include "Shutdown.hpp"
#include "ShutdownClient.hpp"
#include "CommandLineParser.hpp"
#include "Lv2SystemdLogger.hpp"
#include <sys/stat.h>
#include <boost/asio.hpp>
#include "HtmlHelper.hpp"
#include "Ipv6Helpers.hpp"
#include <signal.h>
#include <semaphore.h>
@@ -406,19 +407,26 @@ public:
class InterceptConfig : public RequestHandler
{
private:
std::string response;
uint64_t maxUploadSize;
int portNumber;
public:
InterceptConfig(int portNumber, uint64_t maxUploadSize)
: RequestHandler("/var/config.json"),
maxUploadSize(maxUploadSize)
maxUploadSize(maxUploadSize),
portNumber(portNumber)
{
}
std::string GetConfig(const std::string&fromAddress)
{
std::string linkLocalAddress = GetLinkLocalAddress(fromAddress);
std::stringstream s;
s << "{ \"socket_server_port\": " << portNumber
<< ", \"socket_server_address\": \"*\", \"ui_plugins\": [ ], \"max_upload_size\": " << maxUploadSize << " }";
response = s.str();
<< ", \"socket_server_address\": \"" << linkLocalAddress << "\", \"ui_plugins\": [ ], \"max_upload_size\": " << maxUploadSize << " }";
return s.str();
}
virtual ~InterceptConfig() {}
@@ -428,6 +436,17 @@ public:
HttpResponse &res,
std::error_code &ec)
{
// intercepted. See the other overload.
}
virtual void head_response(
const std::string &fromAddress,
const uri&request_uri,
const HttpRequest &req,
HttpResponse &res,
std::error_code &ec)
{
std::string response = GetConfig(fromAddress);
res.set(HttpField::content_type, "application/json");
res.set(HttpField::cache_control, "no-cache");
res.setContentLength(response.length());
@@ -439,7 +458,18 @@ public:
const HttpRequest &req,
HttpResponse &res,
std::error_code &ec)
{
// intercepted. see the other overload.
}
virtual void get_response(
const std::string&fromAddress,
const uri &request_uri,
const HttpRequest &req,
HttpResponse &res,
std::error_code &ec)
{
std::string response = GetConfig(fromAddress);
res.set(HttpField::content_type, "application/json");
res.set(HttpField::cache_control, "no-cache");
res.setContentLength(response.length());
+1 -2
View File
@@ -53,5 +53,4 @@ template <typename T> class TypeDisplay;
#include "lv2/parameters.lv2/parameters.h"
#include "lv2/units.lv2/units.h"
*/
#define SS(x) ( ((std::stringstream&)(std::stringstream() << x )).str())
#include "ss.hpp"
+26
View File
@@ -0,0 +1,26 @@
// 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.
#pragma once
#include <sstream>
#include <iostream>
// usage: SS("xyz" << 123 << 45.6) returning a std::string rvalue.
#define SS(x) ( ((std::stringstream&)(std::stringstream() << x )).str())