Alsa head corruption in error handling.
This commit is contained in:
@@ -296,6 +296,10 @@ private:
|
||||
|
||||
SystemMidiBinding nextMidiBinding;
|
||||
SystemMidiBinding prevMidiBinding;
|
||||
SystemMidiBinding startHotspotMidiBinding;
|
||||
SystemMidiBinding stopHotspotMidiBinding;
|
||||
SystemMidiBinding rebootMidiBinding;
|
||||
SystemMidiBinding shutdownMidiBinding;
|
||||
|
||||
JackChannelSelection channelSelection;
|
||||
std::atomic<bool> active = false;
|
||||
@@ -742,6 +746,34 @@ private:
|
||||
this->realtimeWriter.OnNextMidiProgram(++(this->midiProgramChangeId), -1);
|
||||
}
|
||||
}
|
||||
else if (this->shutdownMidiBinding.IsMatch(event))
|
||||
{
|
||||
if (shutdownMidiBinding.IsTriggered(event))
|
||||
{
|
||||
this->realtimeWriter.OnRealtimeMidiEvent(RealtimeMidiEventType::Shutdown);
|
||||
}
|
||||
}
|
||||
else if (this->rebootMidiBinding.IsMatch(event))
|
||||
{
|
||||
if (rebootMidiBinding.IsTriggered(event))
|
||||
{
|
||||
this->realtimeWriter.OnRealtimeMidiEvent(RealtimeMidiEventType::Reboot);
|
||||
}
|
||||
}
|
||||
else if (this->startHotspotMidiBinding.IsMatch(event))
|
||||
{
|
||||
if (startHotspotMidiBinding.IsTriggered(event))
|
||||
{
|
||||
this->realtimeWriter.OnRealtimeMidiEvent(RealtimeMidiEventType::StartHotspot);
|
||||
}
|
||||
}
|
||||
else if (this->stopHotspotMidiBinding.IsMatch(event))
|
||||
{
|
||||
if (stopHotspotMidiBinding.IsTriggered(event))
|
||||
{
|
||||
this->realtimeWriter.OnRealtimeMidiEvent(RealtimeMidiEventType::StopHotspot);
|
||||
}
|
||||
}
|
||||
else if (midiProgramChangePending)
|
||||
{
|
||||
// defer the message for processing after the program change has completed.
|
||||
@@ -1182,6 +1214,12 @@ public:
|
||||
hostReader.read(&request);
|
||||
pNotifyCallbacks->OnNotifyNextMidiProgram(request);
|
||||
}
|
||||
else if (command == RingBufferCommand::RealtimeMidiEvent)
|
||||
{
|
||||
RealtimeMidiEventRequest request;
|
||||
hostReader.read(&request);
|
||||
pNotifyCallbacks->OnNotifyMidiRealtimeEvent(request.eventType);
|
||||
}
|
||||
else if (command == RingBufferCommand::Lv2ErrorMessage)
|
||||
{
|
||||
size_t size;
|
||||
@@ -1710,6 +1748,7 @@ void AudioHostImpl::SetSystemMidiBindings(const std::vector<MidiBinding> &bindin
|
||||
{
|
||||
for (auto i = bindings.begin(); i != bindings.end(); ++i)
|
||||
{
|
||||
|
||||
if (i->symbol() == "nextProgram")
|
||||
{
|
||||
this->nextMidiBinding.SetBinding(*i);
|
||||
@@ -1717,6 +1756,20 @@ void AudioHostImpl::SetSystemMidiBindings(const std::vector<MidiBinding> &bindin
|
||||
else if (i->symbol() == "prevProgram")
|
||||
{
|
||||
this->prevMidiBinding.SetBinding(*i);
|
||||
} else if (i->symbol() == "startHotspot")
|
||||
{
|
||||
this->startHotspotMidiBinding.SetBinding(*i);
|
||||
} else if (i->symbol() == "stopHotspot")
|
||||
{
|
||||
this->stopHotspotMidiBinding.SetBinding(*i);
|
||||
} else if (i->symbol() == "reboot")
|
||||
{
|
||||
this->rebootMidiBinding.SetBinding(*i);
|
||||
} else if (i->symbol() == "shutdown")
|
||||
{
|
||||
this->shutdownMidiBinding.SetBinding(*i);
|
||||
} else {
|
||||
Lv2Log::error(SS("Invalid system midi binding: " << i->symbol()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include "PiPedalAlsa.hpp"
|
||||
#include "Promise.hpp"
|
||||
#include "json_variant.hpp"
|
||||
#include "RealtimeMidiEventType.hpp"
|
||||
|
||||
namespace pipedal {
|
||||
|
||||
@@ -160,6 +161,7 @@ public:
|
||||
virtual void OnNotifyMidiProgramChange(RealtimeMidiProgramRequest&midiProgramRequest) = 0;
|
||||
virtual void OnNotifyNextMidiProgram(const RealtimeNextMidiProgramRequest&request) = 0;
|
||||
virtual void OnNotifyLv2RealtimeError(int64_t instanceId,const std::string &error) = 0;
|
||||
virtual void OnNotifyMidiRealtimeEvent(RealtimeMidiEventType eventType) = 0;
|
||||
|
||||
};
|
||||
|
||||
|
||||
+2
-1
@@ -146,6 +146,7 @@ else()
|
||||
endif()
|
||||
|
||||
set (PIPEDAL_SOURCES
|
||||
RealtimeMidiEventType.hpp
|
||||
DBusToLv2Log.cpp DBusToLv2Log.hpp
|
||||
HotspotManager.cpp HotspotManager.hpp
|
||||
UpdateResults.cpp UpdateResults.hpp
|
||||
@@ -646,7 +647,7 @@ add_executable(pipedal_update
|
||||
UpdateMain.cpp
|
||||
UpdateResults.cpp UpdateResults.hpp
|
||||
Lv2SystemdLogger.cpp Lv2SystemdLogger.hpp
|
||||
UpdateResults.cpp UpdateResults.hpp
|
||||
TemporaryFile.cpp TemporaryFile.hpp
|
||||
AdminInstallUpdate.cpp AdminInstallUpdate.hpp
|
||||
)
|
||||
|
||||
|
||||
@@ -909,6 +909,9 @@ void Install(const fs::path &programPrefix, const std::string endpointAddress)
|
||||
}
|
||||
try
|
||||
{
|
||||
// apply policy changes we dropped into the polkit configuration files (allows pipedal_d to use NetworkManager dbus apis).
|
||||
silentSysExec(SYSTEMCTL_BIN " restart polkit");
|
||||
|
||||
DeployVarConfig();
|
||||
|
||||
if (sysExec(GROUPADD_BIN " -f " AUDIO_SERVICE_GROUP_NAME) != EXIT_SUCCESS)
|
||||
|
||||
@@ -725,6 +725,12 @@ static std::vector<std::vector<uint8_t>> GetAccessPointSsids(std::vector<AccessP
|
||||
}
|
||||
void HotspotManagerImpl::MaybeStartHotspot()
|
||||
{
|
||||
if (this->state == State::Error) return;
|
||||
|
||||
if (!wlanDevice || !wlanWirelessDevice)
|
||||
{
|
||||
// devices are transitioning. Do nothing.
|
||||
}
|
||||
std::vector<AccessPoint::ptr> allAccessPoints = GetAllAccessPoints();
|
||||
std::vector<std::vector<uint8_t>> allAccessPointSsids = GetAccessPointSsids(allAccessPoints);
|
||||
std::vector<std::vector<uint8_t>> connectableSsids = GetKnownVisibleAccessPoints(allAccessPointSsids); // all the ssids currently visible for which we have credentials.
|
||||
@@ -820,7 +826,7 @@ void HotspotManagerImpl::StartHotspot()
|
||||
wireless["band"] = "a";
|
||||
wireless["band"] = "bg";
|
||||
|
||||
uint32_t iChannel;
|
||||
uint32_t iChannel = 0;
|
||||
auto channel = this->wifiConfigSettings.channel_;
|
||||
if (channel.length() != 0)
|
||||
{
|
||||
|
||||
+21
-37
@@ -30,7 +30,7 @@
|
||||
|
||||
using namespace pipedal;
|
||||
|
||||
std::string pipedal::GetLinkLocalAddress(const std::string fromAddress);
|
||||
std::string pipedal::GetNonLinkLocalAddress(const std::string fromAddress);
|
||||
|
||||
static bool IsIpv4MappedAddress(const struct in6_addr &inetAddr6)
|
||||
{
|
||||
@@ -302,6 +302,7 @@ static std::string GetInterfaceForIp6Address(const in6_addr inetAddr6)
|
||||
{ // 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;
|
||||
@@ -313,7 +314,7 @@ static std::string GetInterfaceForIp6Address(const in6_addr inetAddr6)
|
||||
return result;
|
||||
}
|
||||
|
||||
static std::string GetLinkLocalAddressForInterface(const std::string &name)
|
||||
static std::string GetNonLinkLocalAddressForInterface(const std::string &name)
|
||||
{
|
||||
struct ifaddrs *ifap = nullptr;
|
||||
if (getifaddrs(&ifap) != 0)
|
||||
@@ -324,7 +325,7 @@ static std::string GetLinkLocalAddressForInterface(const std::string &name)
|
||||
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 (!IN6_IS_ADDR_LINKLOCAL(&(pAddr->sin6_addr)))
|
||||
{
|
||||
if (name == p->ifa_name)
|
||||
{
|
||||
@@ -353,7 +354,7 @@ static std::string GetLinkLocalAddressForInterface(const std::string &name)
|
||||
freeifaddrs(ifap);
|
||||
return result;
|
||||
}
|
||||
static std::string GetLinkLocalAddressForIp4Interface(const std::string &name)
|
||||
static std::string GetNonLinkLocalAddressForIp4Interface(const std::string &name)
|
||||
{
|
||||
struct ifaddrs *ifap = nullptr;
|
||||
if (getifaddrs(&ifap) != 0)
|
||||
@@ -384,23 +385,13 @@ static std::string GetLinkLocalAddressForIp4Interface(const std::string &name)
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string pipedal::GetLinkLocalAddress(const std::string fromAddress)
|
||||
std::string pipedal::GetNonLinkLocalAddress(const std::string fromAddress)
|
||||
{
|
||||
std::string address = StripPortNumber(fromAddress);
|
||||
|
||||
std::string address = 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 = GetLinkLocalAddressForIp4Interface(interfaceName);
|
||||
}
|
||||
return address;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -408,13 +399,14 @@ std::string pipedal::GetLinkLocalAddress(const std::string fromAddress)
|
||||
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);
|
||||
}
|
||||
|
||||
auto nPos = address.find('%') ;
|
||||
if (nPos != std::string::npos)
|
||||
{
|
||||
std::string ifName = address.substr(nPos+1);
|
||||
return GetNonLinkLocalAddressForInterface(ifName);
|
||||
|
||||
}
|
||||
struct in6_addr inetAddr6;
|
||||
memset(&inetAddr6, 0, sizeof(inetAddr6));
|
||||
if (inet_pton(AF_INET6, address.c_str(), &inetAddr6) == 1)
|
||||
@@ -430,33 +422,25 @@ std::string pipedal::GetLinkLocalAddress(const std::string fromAddress)
|
||||
int8_t *pAddr = (int8_t *)&inetAddr6;
|
||||
uint32_t remoteAddress = htonl(*(int32_t *)(pAddr + 12));
|
||||
std::string interfaceName = GetInterfaceForIp4Address(remoteAddress);
|
||||
result = GetLinkLocalAddressForIp4Interface(interfaceName);
|
||||
result = GetNonLinkLocalAddressForIp4Interface(interfaceName);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
std::string interfaceName;
|
||||
if (IsIpv4MappedAddress(inetAddr6))
|
||||
{
|
||||
uint32_t remoteAddress = GetIpv4MappedAddress(inetAddr6);
|
||||
interfaceName = GetInterfaceForIp4Address(remoteAddress);
|
||||
}
|
||||
else
|
||||
{
|
||||
interfaceName = GetInterfaceForIp6Address(inetAddr6);
|
||||
}
|
||||
result = GetLinkLocalAddressForInterface(interfaceName);
|
||||
|
||||
std::string interfaceName = GetInterfaceForIp6Address(inetAddr6);
|
||||
result = GetNonLinkLocalAddressForInterface(interfaceName);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (result == "")
|
||||
{
|
||||
result = GetLinkLocalAddressForInterface("");
|
||||
result = GetNonLinkLocalAddressForInterface("");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string pipedal::GetInterfaceIpv4Address(const std::string& interfaceName)
|
||||
{
|
||||
return GetLinkLocalAddressForIp4Interface(interfaceName);
|
||||
return GetNonLinkLocalAddressForIp4Interface(interfaceName);
|
||||
}
|
||||
|
||||
+1
-1
@@ -27,7 +27,7 @@ namespace pipedal {
|
||||
std::string GetInterfaceIpv4Address(const std::string& interfaceName);
|
||||
|
||||
|
||||
std::string GetLinkLocalAddress(const std::string fromAddress);
|
||||
std::string GetNonLinkLocalAddress(const std::string fromAddress);
|
||||
|
||||
bool IsOnLocalSubnet(const std::string&fromAddress);
|
||||
|
||||
|
||||
+96
-2
@@ -37,6 +37,7 @@
|
||||
#include "Lv2PluginChangeMonitor.hpp"
|
||||
#include "HotspotManager.hpp"
|
||||
#include "DBusToLv2Log.hpp"
|
||||
#include "SysExec.hpp"
|
||||
|
||||
#ifndef NO_MLOCK
|
||||
#include <sys/mman.h>
|
||||
@@ -1957,7 +1958,7 @@ void PiPedalModel::CancelMonitorPatchProperty(int64_t clientId, int64_t clientHa
|
||||
atomOutputListeners.erase(atomOutputListeners.begin() + i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (midiEventListeners.size() == 0)
|
||||
{
|
||||
audioHost->SetListenForMidiEvent(false);
|
||||
@@ -2343,14 +2344,107 @@ void PiPedalModel::OnNetworkChanging(bool ethernetConnected,bool hotspotConnecte
|
||||
{
|
||||
CancelNetworkChangingTimer();
|
||||
this->networkChangingDelayHandle =
|
||||
PostDelayed(std::chrono::seconds(5),
|
||||
PostDelayed(std::chrono::seconds(10), // takes a while for network configuration to be fully applied.
|
||||
[this,ethernetConnected,hotspotConnected]() {
|
||||
this->networkChangingDelayHandle = 0;
|
||||
OnNetworkChanged(ethernetConnected,hotspotConnected);
|
||||
}
|
||||
);
|
||||
|
||||
// take a snapshot incase a client unsusbscribes in the notification handler (in which case the mutex won't protect us)
|
||||
std::vector<IPiPedalModelSubscriber *> t;
|
||||
t.reserve(this->subscribers.size());
|
||||
|
||||
for (size_t i = 0; i < subscribers.size(); ++i)
|
||||
{
|
||||
t.push_back(this->subscribers[i]);
|
||||
}
|
||||
for (size_t i = 0; i < t.size(); ++i)
|
||||
{
|
||||
t[i]->OnNetworkChanging(hotspotConnected);
|
||||
}
|
||||
|
||||
}
|
||||
void PiPedalModel::OnNetworkChanged(bool ethernetConnected, bool hotspotConnected)
|
||||
{
|
||||
FireNetworkChanged();
|
||||
}
|
||||
|
||||
void PiPedalModel::OnNotifyMidiRealtimeEvent(RealtimeMidiEventType eventType)
|
||||
{
|
||||
try {
|
||||
switch (eventType)
|
||||
{
|
||||
case RealtimeMidiEventType::Shutdown:
|
||||
{
|
||||
this->RequestShutdown(false);
|
||||
}
|
||||
break;
|
||||
case RealtimeMidiEventType::Reboot:
|
||||
{
|
||||
this->RequestShutdown(true);
|
||||
}
|
||||
break;
|
||||
case RealtimeMidiEventType::StartHotspot:
|
||||
{
|
||||
WifiConfigSettings settings = storage.GetWifiConfigSettings();
|
||||
if (!settings.hasSavedPassword_)
|
||||
{
|
||||
throw std::runtime_error("Can't start Wi-Fi hotspot because no password has been configured.");
|
||||
}
|
||||
settings.autoStartMode_ = (uint16_t)HotspotAutoStartMode::Always;
|
||||
this->SetWifiConfigSettings(settings);
|
||||
}
|
||||
break;
|
||||
case RealtimeMidiEventType::StopHotspot:
|
||||
{
|
||||
WifiConfigSettings settings = storage.GetWifiConfigSettings();
|
||||
settings.autoStartMode_ = (uint16_t)HotspotAutoStartMode::Never;
|
||||
this->SetWifiConfigSettings(settings);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} catch (const std::exception&e)
|
||||
{
|
||||
Lv2Log::error(SS("Failed to process realtime MIDI event. " << e.what()));
|
||||
}
|
||||
}
|
||||
|
||||
void PiPedalModel::RequestShutdown(bool restart)
|
||||
{
|
||||
if (GetAdminClient().CanUseAdminClient())
|
||||
{
|
||||
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.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,9 +78,11 @@ namespace pipedal
|
||||
virtual void OnFavoritesChanged(const std::map<std::string, bool> &favorites) = 0;
|
||||
virtual void OnShowStatusMonitorChanged(bool show) = 0;
|
||||
virtual void OnSystemMidiBindingsChanged(const std::vector<MidiBinding>&bindings) = 0;
|
||||
|
||||
//virtual void OnPatchPropertyChanged(int64_t clientId, int64_t instanceId,const std::string& propertyUri,const json_variant& value) = 0;
|
||||
virtual void OnErrorMessage(const std::string&message) = 0;
|
||||
virtual void OnLv2PluginsChanging() = 0;
|
||||
virtual void OnNetworkChanging(bool hotspotConnected) = 0;
|
||||
virtual void Close() = 0;
|
||||
};
|
||||
|
||||
@@ -203,6 +205,8 @@ namespace pipedal
|
||||
virtual void OnNotifyMidiValueChanged(int64_t instanceId, int portIndex, float value) override;
|
||||
virtual void OnNotifyMidiListen(bool isNote, uint8_t noteOrControl) override;
|
||||
virtual void OnPatchSetReply(uint64_t instanceId, LV2_URID patchSetProperty, const LV2_Atom*atomValue) override;
|
||||
virtual void OnNotifyMidiRealtimeEvent(RealtimeMidiEventType eventType) override;
|
||||
|
||||
|
||||
void OnNotifyPatchProperty(uint64_t instanceId, LV2_URID outputAtomProperty, const std::string &atomJson);
|
||||
|
||||
@@ -227,7 +231,7 @@ namespace pipedal
|
||||
PiPedalModel();
|
||||
virtual ~PiPedalModel();
|
||||
|
||||
|
||||
void RequestShutdown(bool restart);
|
||||
|
||||
virtual PostHandle Post(PostCallback&&fn);
|
||||
virtual PostHandle PostDelayed(const clock::duration&delay,PostCallback&&fn);
|
||||
|
||||
+14
-41
@@ -459,7 +459,6 @@ private:
|
||||
{
|
||||
return model.GetAdminClient();
|
||||
}
|
||||
void RequestShutdown(bool restart);
|
||||
|
||||
std::recursive_mutex writeMutex;
|
||||
PiPedalModel &model;
|
||||
@@ -1246,15 +1245,12 @@ public:
|
||||
}
|
||||
else if (message == "shutdown")
|
||||
{
|
||||
PresetIndex newIndex;
|
||||
|
||||
RequestShutdown(false);
|
||||
model.RequestShutdown(false);
|
||||
this->Reply(replyTo, "shutdown");
|
||||
}
|
||||
else if (message == "restart")
|
||||
{
|
||||
PresetIndex newIndex;
|
||||
RequestShutdown(true);
|
||||
model.RequestShutdown(true);
|
||||
this->Reply(replyTo, "restart");
|
||||
}
|
||||
else if (message == "deletePresetItem")
|
||||
@@ -1627,6 +1623,18 @@ private:
|
||||
Send("onLv2PluginsChanging",true);
|
||||
Flush();
|
||||
}
|
||||
virtual void OnNetworkChanging(bool hotspotConnected) override {
|
||||
try {
|
||||
Send("onNetworkChanging",hotspotConnected);
|
||||
Flush();
|
||||
|
||||
} catch (const std::exception&ignored)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
virtual void OnErrorMessage(const std::string&message)
|
||||
{
|
||||
@@ -1982,38 +1990,3 @@ std::shared_ptr<ISocketFactory> pipedal::MakePiPedalSocketFactory(PiPedalModel &
|
||||
return std::make_shared<PiPedalSocketFactory>(model);
|
||||
}
|
||||
|
||||
void PiPedalSocketHandler::RequestShutdown(bool restart)
|
||||
{
|
||||
if (GetAdminClient().CanUseAdminClient())
|
||||
{
|
||||
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.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
// Copyright (c) 2024 Robin Davies
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
// this software and associated documentation files (the "Software"), to deal in
|
||||
// the Software without restriction, including without limitation the rights to
|
||||
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
// the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
// subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
#pragma once
|
||||
namespace pipedal {
|
||||
enum class RealtimeMidiEventType {
|
||||
Shutdown,
|
||||
Reboot,
|
||||
StartHotspot,
|
||||
StopHotspot
|
||||
};
|
||||
|
||||
};
|
||||
@@ -24,6 +24,7 @@
|
||||
#include "VuUpdate.hpp"
|
||||
#include "AudioHost.hpp"
|
||||
#include "lv2/atom/atom.h"
|
||||
#include "RealtimeMidiEventType.hpp"
|
||||
#include <chrono>
|
||||
namespace pipedal
|
||||
{
|
||||
@@ -65,9 +66,15 @@ namespace pipedal
|
||||
SetOutputVolume,
|
||||
Lv2ErrorMessage,
|
||||
|
||||
RealtimeMidiEvent,
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
struct RealtimeMidiEventRequest {
|
||||
RealtimeMidiEventType eventType;
|
||||
};
|
||||
struct RealtimeNextMidiProgramRequest {
|
||||
int64_t requestId;
|
||||
int32_t direction;
|
||||
@@ -371,6 +378,11 @@ namespace pipedal
|
||||
|
||||
write(RingBufferCommand::MidiProgramChange,msg);
|
||||
}
|
||||
void OnRealtimeMidiEvent(RealtimeMidiEventType eventType)
|
||||
{
|
||||
RealtimeMidiEventRequest msg { eventType};
|
||||
write(RingBufferCommand::RealtimeMidiEvent,msg);
|
||||
}
|
||||
void OnNextMidiProgram(int64_t requestId,int32_t direction)
|
||||
{
|
||||
RealtimeNextMidiProgramRequest msg { requestId: requestId, direction:direction};
|
||||
|
||||
+30
-10
@@ -960,7 +960,7 @@ bool Storage::SetWifiConfigSettings(const WifiConfigSettings &wifiConfigSettings
|
||||
if (!copyToSave.hasPassword_)
|
||||
{
|
||||
copyToSave.hasPassword_ = previousValue.hasPassword_;
|
||||
copyToSave.password_ = previousValue.hasPassword_;
|
||||
copyToSave.password_ = previousValue.password_;
|
||||
copyToSave.hasSavedPassword_ = previousValue.hasPassword_;
|
||||
} else {
|
||||
if (copyToSave.IsEnabled())
|
||||
@@ -1409,7 +1409,7 @@ void Storage::SetJackServerSettings(const pipedal::JackServerSettings &jackConfi
|
||||
|
||||
void Storage::SetSystemMidiBindings(const std::vector<MidiBinding> &bindings)
|
||||
{
|
||||
std::filesystem::path fileName = this->dataRoot / "SystemMidiBindings.json";
|
||||
std::filesystem::path fileName = this->dataRoot / "config" / "SystemMidiBindings.json";
|
||||
pipedal::ofstream_synced f;
|
||||
f.open(fileName);
|
||||
if (f.is_open())
|
||||
@@ -1422,19 +1422,39 @@ std::vector<MidiBinding> Storage::GetSystemMidiBindings()
|
||||
{
|
||||
std::vector<MidiBinding> result;
|
||||
|
||||
std::filesystem::path fileName = this->dataRoot / "SystemMidiBindings.json";
|
||||
std::filesystem::path fileName = this->dataRoot / "config" / "SystemMidiBindings.json";
|
||||
if (!std::filesystem::exists(fileName)) {
|
||||
// pick up from legacy location?
|
||||
fileName = this->configRoot / "config" / "SystemMidiBindings.json";
|
||||
}
|
||||
std::ifstream f;
|
||||
f.open(fileName);
|
||||
if (f.is_open())
|
||||
{
|
||||
json_reader reader(f);
|
||||
reader.read(&result);
|
||||
}
|
||||
else
|
||||
{
|
||||
result.push_back(MidiBinding::SystemBinding("prevProgram"));
|
||||
result.push_back(MidiBinding::SystemBinding("nextProgram"));
|
||||
try {
|
||||
json_reader reader(f);
|
||||
reader.read(&result);
|
||||
if (result.size() == 2)
|
||||
{
|
||||
result.push_back(MidiBinding::SystemBinding("stopHotspot"));
|
||||
result.push_back(MidiBinding::SystemBinding("startHotspot"));
|
||||
result.push_back(MidiBinding::SystemBinding("shutdown"));
|
||||
result.push_back(MidiBinding::SystemBinding("reboot"));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
catch (const std::exception&e)
|
||||
{
|
||||
Lv2Log::warning(SS("Can't read file " << fileName << ". " << e.what()));
|
||||
}
|
||||
}
|
||||
result.clear();
|
||||
result.push_back(MidiBinding::SystemBinding("prevProgram"));
|
||||
result.push_back(MidiBinding::SystemBinding("nextProgram"));
|
||||
result.push_back(MidiBinding::SystemBinding("stopHotspot"));
|
||||
result.push_back(MidiBinding::SystemBinding("startHotspot"));
|
||||
result.push_back(MidiBinding::SystemBinding("shutdown"));
|
||||
result.push_back(MidiBinding::SystemBinding("reboot"));
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
#include "TemporaryFile.hpp"
|
||||
#include <fstream>
|
||||
|
||||
using namespace pipedal;
|
||||
|
||||
|
||||
@@ -28,6 +28,8 @@ namespace pipedal {
|
||||
TemporaryFile(const std::filesystem::path&parentDirectory);
|
||||
~TemporaryFile();
|
||||
const std::filesystem::path&Path()const { return path;}
|
||||
std::string str() const { return path.c_str(); }
|
||||
const char*c_str() const { return path.c_str(); }
|
||||
private:
|
||||
std::filesystem::path path;
|
||||
};
|
||||
|
||||
+251
-16
@@ -35,6 +35,9 @@
|
||||
#include "UpdaterSecurity.hpp"
|
||||
#include "SysExec.hpp"
|
||||
#include "ofstream_synced.hpp"
|
||||
#include "TemporaryFile.hpp"
|
||||
#include "ofstream_synced.hpp"
|
||||
#include <limits>
|
||||
|
||||
using namespace pipedal;
|
||||
namespace fs = std::filesystem;
|
||||
@@ -165,6 +168,15 @@ void Updater::SetUpdateListener(UpdateListener &&listener)
|
||||
}
|
||||
}
|
||||
|
||||
static void LogNextStartTime(const std::chrono::steady_clock::time_point &clockTime) {
|
||||
using namespace std::chrono;
|
||||
// convert to system_clock;
|
||||
auto delay = clockTime-steady_clock::now();
|
||||
auto systemTime = system_clock::now() + duration_cast<system_clock::duration>(delay);
|
||||
auto t = system_clock::to_time_t(systemTime);
|
||||
Lv2Log::info(SS("Updater: Next update check at " << std::put_time(std::localtime(&t),"%Y-%m-%d %H:%M:%S")));
|
||||
|
||||
}
|
||||
void Updater::ThreadProc()
|
||||
{
|
||||
SetThreadName("UpdateMonitor");
|
||||
@@ -172,10 +184,28 @@ void Updater::ThreadProc()
|
||||
pfd.fd = this->event_reader;
|
||||
pfd.events = POLLIN;
|
||||
|
||||
auto lastRetryTime = clock::time_point();
|
||||
this->updateRetryTime = LoadRetryTime();
|
||||
try {
|
||||
while (true)
|
||||
{
|
||||
int ret = poll(&pfd, 1, std::chrono::duration_cast<std::chrono::milliseconds>(updateRate).count()); // 1000 ms timeout
|
||||
if (updateRetryTime != lastRetryTime) // has retry time changed?
|
||||
{
|
||||
lastRetryTime = updateRetryTime;
|
||||
if (updateRetryTime > clock::now())
|
||||
{
|
||||
LogNextStartTime(updateRetryTime);
|
||||
}
|
||||
}
|
||||
auto clockDelay = this->updateRetryTime-clock::now();
|
||||
auto delayMs = std::chrono::duration_cast<std::chrono::milliseconds>(clockDelay).count();
|
||||
if (delayMs > std::numeric_limits<int>::max()) // could be as little as 32 seconds on a 16-bit int system.
|
||||
{
|
||||
delayMs = std::numeric_limits<int>::max();
|
||||
}
|
||||
|
||||
|
||||
int ret = poll(&pfd, 1, (int)delayMs); // 1000 ms timeout
|
||||
|
||||
if (ret == -1)
|
||||
{
|
||||
@@ -184,7 +214,10 @@ void Updater::ThreadProc()
|
||||
}
|
||||
else if (ret == 0)
|
||||
{
|
||||
CheckForUpdate(true);
|
||||
if (clock::now() >= this->updateRetryTime)
|
||||
{
|
||||
CheckForUpdate(true);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -195,6 +228,7 @@ void Updater::ThreadProc()
|
||||
{
|
||||
if (value == CHECK_NOW_EVENT)
|
||||
{
|
||||
|
||||
CheckForUpdate(true);
|
||||
}
|
||||
else if (value == UNCACHED_CHECK_NOW_EVENT)
|
||||
@@ -418,31 +452,145 @@ static void CheckUpdateHttpResponse(std::string errorCode)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static std::chrono::system_clock::time_point http_date_to_time_point(const std::string& http_date) {
|
||||
std::tm tm = {};
|
||||
std::istringstream ss(http_date);
|
||||
|
||||
ss >> std::get_time(&tm, "%a, %d %b %Y %H:%M:%S GMT");
|
||||
|
||||
if (ss.fail()) {
|
||||
throw std::runtime_error("Failed to parse HTTP date: " + http_date);
|
||||
}
|
||||
|
||||
return std::chrono::system_clock::from_time_t(std::mktime(&tm));
|
||||
}
|
||||
class GitResponseHeaders {
|
||||
public:
|
||||
GitResponseHeaders(const std::filesystem::path path)
|
||||
{
|
||||
// HTTP/2 403
|
||||
// date: Fri, 13 Sep 2024 17:31:22 GMT
|
||||
//...
|
||||
// x-ratelimit-limit: 60
|
||||
// x-ratelimit-remaining: 0
|
||||
// x-ratelimit-reset: 1726250807
|
||||
// x-ratelimit-resource: core
|
||||
// x-ratelimit-used: 60
|
||||
std::ifstream f {path};
|
||||
if (!f.is_open())
|
||||
{
|
||||
throw std::runtime_error(SS("Can't open file " << path << "."));
|
||||
}
|
||||
std::string http;
|
||||
f >> http >> code;
|
||||
std::string line;
|
||||
std::getline(f,line);
|
||||
|
||||
while (true)
|
||||
{
|
||||
std::getline(f,line);
|
||||
auto trimPos = line.find_last_not_of("\r\n");
|
||||
if (trimPos != std::string::npos) {
|
||||
line = line.substr(trimPos+1);
|
||||
}
|
||||
|
||||
if (!f) break;
|
||||
auto pos = line.find(':');
|
||||
if (pos != std::string::npos)
|
||||
{
|
||||
std::string tag = line.substr(0,pos);
|
||||
++pos;
|
||||
while (pos < line.length() && line[pos] == ' ')
|
||||
{
|
||||
++pos;
|
||||
}
|
||||
std::string value = line.substr(pos);
|
||||
uint64_t *pResult = nullptr;
|
||||
if (tag =="date")
|
||||
{
|
||||
this->date = http_date_to_time_point(value);
|
||||
} else if (tag == "x-ratelimit-limit")
|
||||
{
|
||||
pResult = &ratelimit_limit;
|
||||
} else if (tag == "x-ratelimit-remaining")
|
||||
{
|
||||
pResult = &ratelimit_remaining;
|
||||
} else if (tag == "x-ratelimit-reset")
|
||||
{
|
||||
std::istringstream ss {value};
|
||||
uint64_t reset = 0;
|
||||
ss >> reset;
|
||||
auto secs = std::chrono::seconds(reset); // value is seconds in UTC Unix epoch.
|
||||
this->ratelimit_reset = std::chrono::system_clock::time_point(secs);
|
||||
} else if (tag == "x-ratelimit-resource")
|
||||
{
|
||||
ratelimit_resource = value;
|
||||
} else if (tag == "x-ratelimit-used")
|
||||
{
|
||||
pResult = &ratelimit_used;
|
||||
}
|
||||
if (pResult)
|
||||
{
|
||||
std::istringstream ss { value};
|
||||
ss >> *pResult;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
int code = -1;
|
||||
std::chrono::system_clock::time_point date;
|
||||
std::chrono::system_clock::time_point ratelimit_reset;
|
||||
uint64_t ratelimit_limit = 0;
|
||||
uint64_t ratelimit_remaining = 0;
|
||||
uint64_t ratelimit_used = 0;
|
||||
std::string ratelimit_resource;
|
||||
bool limit_exceeded() const { return code != 200 && ratelimit_limit != 0 && ratelimit_limit == ratelimit_used; }
|
||||
|
||||
};
|
||||
void Updater::CheckForUpdate(bool useCache)
|
||||
{
|
||||
UpdateStatus updateResult;
|
||||
|
||||
if (useCache)
|
||||
{
|
||||
std::lock_guard lock{mutex};
|
||||
if (useCache && IsCacheValid(cachedUpdateStatus))
|
||||
{
|
||||
this->currentResult = cachedUpdateStatus;
|
||||
this->currentResult.UpdatePolicy(this->updatePolicy);
|
||||
if (listener)
|
||||
if (IsCacheValid(cachedUpdateStatus))
|
||||
{
|
||||
listener(this->currentResult);
|
||||
this->currentResult = cachedUpdateStatus;
|
||||
this->currentResult.UpdatePolicy(this->updatePolicy);
|
||||
if (listener)
|
||||
{
|
||||
listener(this->currentResult);
|
||||
}
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
updateResult = this->currentResult;
|
||||
}
|
||||
if (clock::now() < this->updateRetryTime)
|
||||
{
|
||||
// forced or not, do NOT hit the githup api before we're supposed to.
|
||||
std::lock_guard lock(mutex);
|
||||
this->currentResult = cachedUpdateStatus;
|
||||
this->currentResult.UpdatePolicy(this->updatePolicy);
|
||||
if (listener)
|
||||
{
|
||||
listener(this->currentResult);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// set default next retry time. github failures may postpone even further.
|
||||
|
||||
Lv2Log::info("Checking for updates.");
|
||||
// const std::string responseOption = "-w \"%{response_code}\"";
|
||||
#ifdef WIN32
|
||||
responseOption = "-w \"%%{response_code}\""; // windows shell requires doubling of the %%.
|
||||
#endif
|
||||
|
||||
std::string args = SS("-s -L " << GITHUB_RELEASES_URL);
|
||||
TemporaryFile headerFile { WORKING_DIRECTORY };
|
||||
std::string args = SS("-s -L " << GITHUB_RELEASES_URL << " -D " << headerFile.str());
|
||||
|
||||
updateResult.errorMessage_ = "";
|
||||
|
||||
@@ -451,10 +599,33 @@ void Updater::CheckForUpdate(bool useCache)
|
||||
auto result = sysExecForOutput("curl", args);
|
||||
if (result.exitCode != EXIT_SUCCESS)
|
||||
{
|
||||
RetryAfter(std::chrono::duration_cast<clock::duration>(std::chrono::hours(4)));
|
||||
throw std::runtime_error("Server has no internet access.");
|
||||
}
|
||||
else
|
||||
{
|
||||
// hard throttling for github.
|
||||
RetryAfter(std::chrono::duration_cast<clock::duration>(std::chrono::hours(8)));
|
||||
|
||||
GitResponseHeaders githubHeaders { headerFile.Path()};
|
||||
|
||||
if (githubHeaders.code != 200)
|
||||
{
|
||||
if (
|
||||
githubHeaders.ratelimit_limit != 0 &&
|
||||
githubHeaders.ratelimit_limit == githubHeaders.ratelimit_used)
|
||||
{
|
||||
std::time_t time = std::chrono::system_clock::to_time_t(githubHeaders.ratelimit_reset);
|
||||
std::string strTime = std::ctime(&time);
|
||||
|
||||
|
||||
Lv2Log::warning(SS("Updater: github rate limit exceeded. Retry at " << strTime));
|
||||
|
||||
this->RetryAfter(githubHeaders.ratelimit_reset-githubHeaders.date);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (result.output.length() == 0)
|
||||
{
|
||||
throw std::runtime_error("Server has no internet access.");
|
||||
@@ -468,11 +639,16 @@ void Updater::CheckForUpdate(bool useCache)
|
||||
// an HTML error.
|
||||
updateResult.isOnline_ = false;
|
||||
auto o = vResult.as_object();
|
||||
std::string message = o->at("message").as_string();
|
||||
auto status_code = o->at("status_code").as_int64();
|
||||
throw std::runtime_error(SS("Service error. ()" << status_code << ": " << message << ")"));
|
||||
}
|
||||
else
|
||||
std::string message = "Unknown error.";
|
||||
if (o->at("message").is_string())
|
||||
{
|
||||
message = o->at("message").as_string();
|
||||
}
|
||||
throw std::runtime_error(SS("Github Service error: " << message ));
|
||||
} else if (!vResult.is_array())
|
||||
{
|
||||
throw std::runtime_error("Invalid file format error.");
|
||||
} else
|
||||
{
|
||||
json_variant::array_ptr vArray = vResult.as_array();
|
||||
|
||||
@@ -534,7 +710,7 @@ void Updater::CheckForUpdate(bool useCache)
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
Lv2Log::error(SS("Failed to fetch update info. " << e.what()));
|
||||
Lv2Log::error(SS("Updater: Failed to fetch update info. " << e.what()));
|
||||
updateResult.errorMessage_ = e.what();
|
||||
updateResult.isValid_ = false;
|
||||
updateResult.isOnline_ = false;
|
||||
@@ -897,6 +1073,65 @@ static void checkCurlHttpResponse(std::string errorCode)
|
||||
throw std::runtime_error(message);
|
||||
}
|
||||
}
|
||||
void Updater::RetryAfter(clock::duration delay)
|
||||
{
|
||||
namespace chron = std::chrono;
|
||||
clock::duration hours24 = chron::duration_cast<clock::duration>(chron::hours(24));
|
||||
if (delay.count() < 0 || delay >= hours24) // non-synced system clock (as Raspberry Pis are prone to without network connections)
|
||||
{
|
||||
delay = chron::duration_cast<clock::duration>(chron::hours(6));
|
||||
}
|
||||
SaveRetryTime(chron::system_clock::now()+std::chrono::duration_cast<chron::system_clock::duration>(delay));
|
||||
|
||||
clock::time_point myRetryTime = clock::now() + delay;
|
||||
|
||||
if (this->updateRetryTime < myRetryTime)
|
||||
{
|
||||
this->updateRetryTime = myRetryTime;
|
||||
}
|
||||
}
|
||||
void Updater::SaveRetryTime(const std::chrono::system_clock::time_point &time)
|
||||
{
|
||||
fs::path retryTimePath = WORKING_DIRECTORY / "retryTime.json";
|
||||
pipedal::ofstream_synced f {retryTimePath};
|
||||
if (f.is_open())
|
||||
{
|
||||
json_writer writer(f);
|
||||
auto rep = time.time_since_epoch().count();
|
||||
writer.write(rep);
|
||||
}
|
||||
|
||||
}
|
||||
Updater::clock::time_point Updater::LoadRetryTime()
|
||||
{
|
||||
using namespace std::chrono;
|
||||
|
||||
fs::path retryTimePath = WORKING_DIRECTORY / "retryTime.json";
|
||||
std::ifstream f {retryTimePath};
|
||||
if (f.is_open())
|
||||
{
|
||||
system_clock::duration::rep tRep = 0;
|
||||
json_reader reader(f);
|
||||
reader.read(&tRep);
|
||||
|
||||
system_clock::duration duration(tRep);
|
||||
|
||||
system_clock::time_point systemTime = system_clock::time_point(duration);
|
||||
|
||||
// now convert to stead_clock time with the understanding that system_clock time may be blown.
|
||||
system_clock::duration systemDuration = systemTime - system_clock::now();
|
||||
clock::duration clockDuration = duration_cast<clock::duration>(systemDuration);
|
||||
if (clockDuration > duration_cast<clock::duration>(hours(48))) // is system clock blown?
|
||||
{
|
||||
clockDuration = duration_cast<clock::duration>(hours(6));
|
||||
}
|
||||
return clock::now() + clockDuration;
|
||||
|
||||
}
|
||||
return clock::now();
|
||||
|
||||
}
|
||||
|
||||
void Updater::DownloadUpdate(const std::string &url, std::filesystem::path *file, std::filesystem::path *signatureFile)
|
||||
{
|
||||
std::string filename, signatureUrl;
|
||||
|
||||
+14
-1
@@ -127,6 +127,19 @@ namespace pipedal
|
||||
void DownloadUpdate(const std::string &url, std::filesystem::path*file, std::filesystem::path*signatureFile);
|
||||
UpdateStatus GetCurrentStatus() const { return this->currentResult; }
|
||||
private:
|
||||
using clock = std::chrono::steady_clock;
|
||||
|
||||
void RetryAfter(clock::duration delay);
|
||||
|
||||
template<typename REP,typename PERIOD>
|
||||
void RetryAfter(std::chrono::duration<REP,PERIOD> duration)
|
||||
{
|
||||
RetryAfter(std::chrono::duration_cast<clock::duration>(duration));
|
||||
}
|
||||
|
||||
void SaveRetryTime(const std::chrono::system_clock::time_point &time);
|
||||
clock::time_point LoadRetryTime();
|
||||
|
||||
std::string GetUpdateFilename(const std::string &url);
|
||||
std::string GetSignatureUrl(const std::string &url);
|
||||
|
||||
@@ -140,7 +153,6 @@ namespace pipedal
|
||||
|
||||
UpdateStatus cachedUpdateStatus;
|
||||
bool stopped = false;
|
||||
using clock = std::chrono::steady_clock;
|
||||
|
||||
int event_reader = -1;
|
||||
int event_writer = -1;
|
||||
@@ -154,5 +166,6 @@ namespace pipedal
|
||||
bool hasInfo = false;
|
||||
UpdateStatus currentResult;
|
||||
static clock::duration updateRate;
|
||||
clock::time_point updateRetryTime;
|
||||
};
|
||||
}
|
||||
+8
-1
@@ -1320,6 +1320,13 @@ void WebServerImpl::DisplayIpAddresses()
|
||||
std::string wifiAddress = getIpv4Address("wlan0");
|
||||
if (wifiAddress.length() != 0)
|
||||
{
|
||||
Lv2Log::info(SS("Listening on Wi-Fi address " << wifiAddress << ":" << this->port));
|
||||
if (wifiAddress == "10.42.0.1")
|
||||
{
|
||||
Lv2Log::info(SS("Listening on Wi-Fi hotspot address " << wifiAddress << ":" << this->port));
|
||||
}
|
||||
else
|
||||
{
|
||||
Lv2Log::info(SS("Listening on Wi-Fi address " << wifiAddress << ":" << this->port));
|
||||
}
|
||||
}
|
||||
}
|
||||
+28
-6
@@ -472,6 +472,33 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/* When hosting a react app, replace /var/config.json with
|
||||
data that will connect the react app with our socket server.
|
||||
*/
|
||||
@@ -491,13 +518,8 @@ public:
|
||||
}
|
||||
std::string GetConfig(const std::string &fromAddress)
|
||||
{
|
||||
#define LINK_LOCAL_WEB_SOCKET 1
|
||||
#if LINK_LOCAL_WEB_SOCKET
|
||||
std::string webSocketAddress = GetLinkLocalAddress(fromAddress);
|
||||
std::string webSocketAddress = GetNonLinkLocalAddress(StripPortNumber(fromAddress));
|
||||
Lv2Log::info(SS("Web Socket Address: " << webSocketAddress << ":" << portNumber));
|
||||
#else
|
||||
std::string webSocketAddress = "*";
|
||||
#endif
|
||||
|
||||
std::stringstream s;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user