Alsa head corruption in error handling.
This commit is contained in:
@@ -30,6 +30,7 @@
|
|||||||
#include <sdbus-c++/sdbus-c++.h>
|
#include <sdbus-c++/sdbus-c++.h>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <unistd.h> // for gethostname()
|
||||||
|
|
||||||
using namespace pipedal;
|
using namespace pipedal;
|
||||||
using namespace std;
|
using namespace std;
|
||||||
@@ -87,6 +88,18 @@ bool WifiConfigSettings::ValidateCountryCode(const std::string &text)
|
|||||||
return false;
|
return false;
|
||||||
return std::isalpha(text[0]) && std::isalpha(text[1]);
|
return std::isalpha(text[0]) && std::isalpha(text[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static std::string GetHostName()
|
||||||
|
{
|
||||||
|
char buffer[1024];
|
||||||
|
if (gethostname(buffer,1024) != 0)
|
||||||
|
{
|
||||||
|
buffer[0] = '\0';
|
||||||
|
}
|
||||||
|
buffer[1023] = '\0';
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
void WifiConfigSettings::Load()
|
void WifiConfigSettings::Load()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@@ -103,7 +116,7 @@ void WifiConfigSettings::Load()
|
|||||||
}
|
}
|
||||||
this->countryCode_ = getWifiCountryCode();
|
this->countryCode_ = getWifiCountryCode();
|
||||||
this->enable_ = this->autoStartMode_ != (uint16_t)HotspotAutoStartMode::Never;
|
this->enable_ = this->autoStartMode_ != (uint16_t)HotspotAutoStartMode::Never;
|
||||||
this->mdnsName_ = this->hotspotName_;
|
this->mdnsName_ = GetHostName();
|
||||||
}
|
}
|
||||||
catch (const std::exception &e)
|
catch (const std::exception &e)
|
||||||
{
|
{
|
||||||
@@ -689,6 +702,7 @@ void WifiConfigSettings::ParseArguments(
|
|||||||
this->autoStartMode_ = (int16_t)startMode;
|
this->autoStartMode_ = (int16_t)startMode;
|
||||||
this->enable_ = startMode != HotspotAutoStartMode::Never;
|
this->enable_ = startMode != HotspotAutoStartMode::Never;
|
||||||
this->homeNetwork_ = homeNetworkSsid;
|
this->homeNetwork_ = homeNetworkSsid;
|
||||||
|
this->mdnsName_ = GetHostName();
|
||||||
|
|
||||||
this->countryCode_ = argv[0];
|
this->countryCode_ = argv[0];
|
||||||
this->hotspotName_ = argv[1];
|
this->hotspotName_ = argv[1];
|
||||||
|
|||||||
@@ -75,12 +75,12 @@ namespace pipedal {
|
|||||||
bool hasPassword_ = false;
|
bool hasPassword_ = false;
|
||||||
std::string password_;
|
std::string password_;
|
||||||
std::string channel_ = "";
|
std::string channel_ = "";
|
||||||
|
std::string mdnsName_ = "pipedal";
|
||||||
|
|
||||||
bool wifiWarningGiven_ = false; // Do not use. Present only for backward compatibility.
|
bool wifiWarningGiven_ = false; // Do not use. Present only for backward compatibility.
|
||||||
bool valid_ = false; // Do not use. Present only for backward compatibility.
|
bool valid_ = false; // Do not use. Present only for backward compatibility.
|
||||||
private:
|
private:
|
||||||
bool rebootRequired_ = false; // Do not use. Present only for backward compatibility.
|
bool rebootRequired_ = false; // Do not use. Present only for backward compatibility.
|
||||||
std::string mdnsName_ = "pipedal";
|
|
||||||
bool enable_ = false; // Do not use. Present only for backward compatibility.
|
bool enable_ = false; // Do not use. Present only for backward compatibility.
|
||||||
public:
|
public:
|
||||||
bool IsEnabled() const { return autoStartMode_ != 0; }
|
bool IsEnabled() const { return autoStartMode_ != 0; }
|
||||||
|
|||||||
+18
-3
@@ -67,10 +67,9 @@
|
|||||||
useSystem = true;
|
useSystem = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (useSystem)
|
if (useSystem) {
|
||||||
{
|
|
||||||
darkMode = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
|
darkMode = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!darkMode) {
|
if (!darkMode) {
|
||||||
let bgStyle = document.getElementById("bgStyle");
|
let bgStyle = document.getElementById("bgStyle");
|
||||||
@@ -79,6 +78,22 @@
|
|||||||
bgStyle.setAttribute('media', "max-width: 1px");
|
bgStyle.setAttribute('media', "max-width: 1px");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
function removeHashOnLoad() {
|
||||||
|
if (window.location.hash) {
|
||||||
|
// Store the hash value (without the '#' symbol)
|
||||||
|
var hash = window.location.hash.substring(1);
|
||||||
|
|
||||||
|
// Replace the current URL without the hash
|
||||||
|
var newUrl = window.location.href.replace(window.location.hash, '');
|
||||||
|
|
||||||
|
// Use HTML5 history API to change the URL without reloading the page
|
||||||
|
history.replaceState(null, document.title, newUrl);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run the function when the window loads
|
||||||
|
window.addEventListener('load', removeHashOnLoad);
|
||||||
</script>
|
</script>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
|
|||||||
@@ -682,6 +682,8 @@ const AppThemed = withStyles(appStyles)(class extends ResizeResponsiveComponent<
|
|||||||
return "Downloading update...";
|
return "Downloading update...";
|
||||||
case State.InstallingUpdate:
|
case State.InstallingUpdate:
|
||||||
return "Installing update....";
|
return "Installing update....";
|
||||||
|
case State.HotspotChanging:
|
||||||
|
return "Network connection changing..."
|
||||||
default:
|
default:
|
||||||
return "Reconnecting...";
|
return "Reconnecting...";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -392,7 +392,7 @@ const BankDialog = withStyles(styles, { withTheme: true })(
|
|||||||
|
|
||||||
getSelectedBankName() {
|
getSelectedBankName() {
|
||||||
try {
|
try {
|
||||||
return this.model.banks.get().getEntry(this.state.selectedItem)!.name;
|
return this.model.banks.get()?.getEntry(this.state.selectedItem)?.name??"";
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|||||||
+106
-2
@@ -52,6 +52,7 @@ export enum State {
|
|||||||
ReloadingPlugins,
|
ReloadingPlugins,
|
||||||
DownloadingUpdate,
|
DownloadingUpdate,
|
||||||
InstallingUpdate,
|
InstallingUpdate,
|
||||||
|
HotspotChanging,
|
||||||
};
|
};
|
||||||
|
|
||||||
class UpdatedError extends Error {
|
class UpdatedError extends Error {
|
||||||
@@ -65,6 +66,7 @@ export enum ReconnectReason {
|
|||||||
LoadingSettings,
|
LoadingSettings,
|
||||||
ReloadingPlugins,
|
ReloadingPlugins,
|
||||||
Updating,
|
Updating,
|
||||||
|
HotspotChanging
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ControlValueChangedHandler = (key: string, value: number) => void;
|
export type ControlValueChangedHandler = (key: string, value: number) => void;
|
||||||
@@ -466,6 +468,10 @@ export class PiPedalModel //implements PiPedalModel
|
|||||||
case ReconnectReason.Updating:
|
case ReconnectReason.Updating:
|
||||||
this.setState(State.InstallingUpdate);
|
this.setState(State.InstallingUpdate);
|
||||||
break;
|
break;
|
||||||
|
case ReconnectReason.HotspotChanging:
|
||||||
|
this.setState(State.HotspotChanging);
|
||||||
|
this.startHotspotReconnectTimer();
|
||||||
|
break;
|
||||||
|
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@@ -643,9 +649,13 @@ export class PiPedalModel //implements PiPedalModel
|
|||||||
} else if (message === "onUpdateStatusChanged") {
|
} else if (message === "onUpdateStatusChanged") {
|
||||||
let updateStatus = new UpdateStatus().deserialize(body);
|
let updateStatus = new UpdateStatus().deserialize(body);
|
||||||
this.onUpdateStatusChanged(updateStatus);
|
this.onUpdateStatusChanged(updateStatus);
|
||||||
|
} else if (message === "onNetworkChanging")
|
||||||
|
{
|
||||||
|
this.onNetworkChanging(body as boolean);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private updateLaterTimeout?: NodeJS.Timeout = undefined;
|
private updateLaterTimeout?: NodeJS.Timeout = undefined;
|
||||||
|
|
||||||
private clearPromptForUpdateTimer() {
|
private clearPromptForUpdateTimer() {
|
||||||
@@ -789,7 +799,7 @@ export class PiPedalModel //implements PiPedalModel
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
onSocketReconnected() {
|
onSocketReconnected() {
|
||||||
|
this.cancelOnNetworkChanging();
|
||||||
if (this.isAndroidHosted()) {
|
if (this.isAndroidHosted()) {
|
||||||
this.androidHost?.setDisconnected(false);
|
this.androidHost?.setDisconnected(false);
|
||||||
}
|
}
|
||||||
@@ -943,7 +953,7 @@ export class PiPedalModel //implements PiPedalModel
|
|||||||
return this.webSocket.connect();
|
return this.webSocket.connect();
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
this.setError("Failed to connect to server. " + this.socketServerUrl);
|
this.setError("Failed to connect to server. (" + this.socketServerUrl + ")");
|
||||||
return false;
|
return false;
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
@@ -2894,6 +2904,100 @@ export class PiPedalModel //implements PiPedalModel
|
|||||||
window.location.href = url;
|
window.location.href = url;
|
||||||
//window.location.reload();
|
//window.location.reload();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private networkChanging: boolean = false;
|
||||||
|
private networkChanging_expectHotspot = false;
|
||||||
|
private expectNetworkChangeTimeout?: NodeJS.Timeout = undefined;
|
||||||
|
private hotspotReconnectTimer?: NodeJS.Timeout = undefined;
|
||||||
|
|
||||||
|
async detectServer(address: string)
|
||||||
|
{
|
||||||
|
let port = window.location.port;
|
||||||
|
let newUrl = new URL("http://" + address + ":" + port + "/manifest.json");
|
||||||
|
|
||||||
|
try {
|
||||||
|
let response = await fetch(newUrl);
|
||||||
|
if (response.ok)
|
||||||
|
{
|
||||||
|
return "http://" + address +":"+ port;
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
} catch (error: any)
|
||||||
|
{
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async pollForLiveServer() {
|
||||||
|
let wifiConfigSettings = this.wifiConfigSettings.get();
|
||||||
|
if (wifiConfigSettings.mdnsName.length !== 0)
|
||||||
|
{
|
||||||
|
let newUrl = await this.detectServer(wifiConfigSettings.mdnsName);
|
||||||
|
if (newUrl.length !== 0)
|
||||||
|
{
|
||||||
|
return newUrl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (this.networkChanging_expectHotspot)
|
||||||
|
{
|
||||||
|
let newUrl = await this.detectServer("10.40.0.1");
|
||||||
|
if (newUrl.length !== 0)
|
||||||
|
{
|
||||||
|
return newUrl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
cancelHotspotReconnectTimer()
|
||||||
|
{
|
||||||
|
if (this.hotspotReconnectTimer)
|
||||||
|
{
|
||||||
|
clearTimeout(this.hotspotReconnectTimer);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
startHotspotReconnectTimer()
|
||||||
|
{
|
||||||
|
// poll for access to a running pipedal server
|
||||||
|
this.hotspotReconnectTimer = setTimeout(
|
||||||
|
async () => {
|
||||||
|
let newUrl = await this.pollForLiveServer();
|
||||||
|
if (newUrl.length === 0)
|
||||||
|
{
|
||||||
|
this.startHotspotReconnectTimer();
|
||||||
|
} else {
|
||||||
|
this.cancelOnNetworkChanging();
|
||||||
|
window.location.replace(newUrl);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
5*1000);
|
||||||
|
|
||||||
|
}
|
||||||
|
cancelOnNetworkChanging()
|
||||||
|
{
|
||||||
|
this.cancelHotspotReconnectTimer();
|
||||||
|
if (this.expectNetworkChangeTimeout)
|
||||||
|
{
|
||||||
|
clearTimeout(this.expectNetworkChangeTimeout);
|
||||||
|
this.expectNetworkChangeTimeout = undefined;
|
||||||
|
}
|
||||||
|
this.networkChanging = false;
|
||||||
|
this.expectDisconnect(ReconnectReason.Disconnected);
|
||||||
|
}
|
||||||
|
onNetworkChanging(hotspotConnected: boolean)
|
||||||
|
{
|
||||||
|
this.cancelOnNetworkChanging();
|
||||||
|
|
||||||
|
this.networkChanging = true;
|
||||||
|
this.networkChanging_expectHotspot = hotspotConnected;
|
||||||
|
this.expectDisconnect(ReconnectReason.HotspotChanging);
|
||||||
|
|
||||||
|
this.expectNetworkChangeTimeout = setTimeout(
|
||||||
|
() => {
|
||||||
|
this.cancelOnNetworkChanging();
|
||||||
|
},
|
||||||
|
30*1000);
|
||||||
|
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let instance: PiPedalModel | undefined = undefined;
|
let instance: PiPedalModel | undefined = undefined;
|
||||||
|
|||||||
@@ -131,7 +131,24 @@ export const SystemMidiBindingDialog =
|
|||||||
{
|
{
|
||||||
displayName = "Next Preset";
|
displayName = "Next Preset";
|
||||||
instanceId = 2;
|
instanceId = 2;
|
||||||
|
} else if (item.symbol === "startHotspot")
|
||||||
|
{
|
||||||
|
displayName = "Enable Hotspot";
|
||||||
|
instanceId = 3;
|
||||||
|
} else if (item.symbol === "stopHotspot")
|
||||||
|
{
|
||||||
|
displayName = "Disable Hotspot";
|
||||||
|
instanceId = 4;
|
||||||
|
} else if (item.symbol === "shutdown")
|
||||||
|
{
|
||||||
|
displayName = "Shutdown";
|
||||||
|
instanceId = 5;
|
||||||
|
} else if (item.symbol === "reboot")
|
||||||
|
{
|
||||||
|
displayName = "Reboot";
|
||||||
|
instanceId = 6;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (instanceId !== -1)
|
if (instanceId !== -1)
|
||||||
{
|
{
|
||||||
result.push(new BindingEntry(displayName,instanceId,item));
|
result.push(new BindingEntry(displayName,instanceId,item));
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ export default class WifiConfigSettings {
|
|||||||
|
|
||||||
this.wifiWarningGiven = input.wifiWarningGiven;
|
this.wifiWarningGiven = input.wifiWarningGiven;
|
||||||
this.hotspotName = input.hotspotName;
|
this.hotspotName = input.hotspotName;
|
||||||
|
this.mdnsName = input.mdnsName;
|
||||||
this.hasPassword = input.hasPassword;
|
this.hasPassword = input.hasPassword;
|
||||||
this.password = input.password;
|
this.password = input.password;
|
||||||
this.countryCode = input.countryCode;
|
this.countryCode = input.countryCode;
|
||||||
@@ -40,6 +41,7 @@ export default class WifiConfigSettings {
|
|||||||
autoStartMode: number = 0;
|
autoStartMode: number = 0;
|
||||||
hasSavedPassword: boolean = false;
|
hasSavedPassword: boolean = false;
|
||||||
homeNetwork: string = "";
|
homeNetwork: string = "";
|
||||||
|
mdnsName: string = "";
|
||||||
wifiWarningGiven: boolean = false;
|
wifiWarningGiven: boolean = false;
|
||||||
hasPassword: boolean = false;
|
hasPassword: boolean = false;
|
||||||
valid: boolean = false;
|
valid: boolean = false;
|
||||||
|
|||||||
@@ -296,6 +296,10 @@ private:
|
|||||||
|
|
||||||
SystemMidiBinding nextMidiBinding;
|
SystemMidiBinding nextMidiBinding;
|
||||||
SystemMidiBinding prevMidiBinding;
|
SystemMidiBinding prevMidiBinding;
|
||||||
|
SystemMidiBinding startHotspotMidiBinding;
|
||||||
|
SystemMidiBinding stopHotspotMidiBinding;
|
||||||
|
SystemMidiBinding rebootMidiBinding;
|
||||||
|
SystemMidiBinding shutdownMidiBinding;
|
||||||
|
|
||||||
JackChannelSelection channelSelection;
|
JackChannelSelection channelSelection;
|
||||||
std::atomic<bool> active = false;
|
std::atomic<bool> active = false;
|
||||||
@@ -742,6 +746,34 @@ private:
|
|||||||
this->realtimeWriter.OnNextMidiProgram(++(this->midiProgramChangeId), -1);
|
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)
|
else if (midiProgramChangePending)
|
||||||
{
|
{
|
||||||
// defer the message for processing after the program change has completed.
|
// defer the message for processing after the program change has completed.
|
||||||
@@ -1182,6 +1214,12 @@ public:
|
|||||||
hostReader.read(&request);
|
hostReader.read(&request);
|
||||||
pNotifyCallbacks->OnNotifyNextMidiProgram(request);
|
pNotifyCallbacks->OnNotifyNextMidiProgram(request);
|
||||||
}
|
}
|
||||||
|
else if (command == RingBufferCommand::RealtimeMidiEvent)
|
||||||
|
{
|
||||||
|
RealtimeMidiEventRequest request;
|
||||||
|
hostReader.read(&request);
|
||||||
|
pNotifyCallbacks->OnNotifyMidiRealtimeEvent(request.eventType);
|
||||||
|
}
|
||||||
else if (command == RingBufferCommand::Lv2ErrorMessage)
|
else if (command == RingBufferCommand::Lv2ErrorMessage)
|
||||||
{
|
{
|
||||||
size_t size;
|
size_t size;
|
||||||
@@ -1710,6 +1748,7 @@ void AudioHostImpl::SetSystemMidiBindings(const std::vector<MidiBinding> &bindin
|
|||||||
{
|
{
|
||||||
for (auto i = bindings.begin(); i != bindings.end(); ++i)
|
for (auto i = bindings.begin(); i != bindings.end(); ++i)
|
||||||
{
|
{
|
||||||
|
|
||||||
if (i->symbol() == "nextProgram")
|
if (i->symbol() == "nextProgram")
|
||||||
{
|
{
|
||||||
this->nextMidiBinding.SetBinding(*i);
|
this->nextMidiBinding.SetBinding(*i);
|
||||||
@@ -1717,6 +1756,20 @@ void AudioHostImpl::SetSystemMidiBindings(const std::vector<MidiBinding> &bindin
|
|||||||
else if (i->symbol() == "prevProgram")
|
else if (i->symbol() == "prevProgram")
|
||||||
{
|
{
|
||||||
this->prevMidiBinding.SetBinding(*i);
|
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 "PiPedalAlsa.hpp"
|
||||||
#include "Promise.hpp"
|
#include "Promise.hpp"
|
||||||
#include "json_variant.hpp"
|
#include "json_variant.hpp"
|
||||||
|
#include "RealtimeMidiEventType.hpp"
|
||||||
|
|
||||||
namespace pipedal {
|
namespace pipedal {
|
||||||
|
|
||||||
@@ -160,6 +161,7 @@ public:
|
|||||||
virtual void OnNotifyMidiProgramChange(RealtimeMidiProgramRequest&midiProgramRequest) = 0;
|
virtual void OnNotifyMidiProgramChange(RealtimeMidiProgramRequest&midiProgramRequest) = 0;
|
||||||
virtual void OnNotifyNextMidiProgram(const RealtimeNextMidiProgramRequest&request) = 0;
|
virtual void OnNotifyNextMidiProgram(const RealtimeNextMidiProgramRequest&request) = 0;
|
||||||
virtual void OnNotifyLv2RealtimeError(int64_t instanceId,const std::string &error) = 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()
|
endif()
|
||||||
|
|
||||||
set (PIPEDAL_SOURCES
|
set (PIPEDAL_SOURCES
|
||||||
|
RealtimeMidiEventType.hpp
|
||||||
DBusToLv2Log.cpp DBusToLv2Log.hpp
|
DBusToLv2Log.cpp DBusToLv2Log.hpp
|
||||||
HotspotManager.cpp HotspotManager.hpp
|
HotspotManager.cpp HotspotManager.hpp
|
||||||
UpdateResults.cpp UpdateResults.hpp
|
UpdateResults.cpp UpdateResults.hpp
|
||||||
@@ -646,7 +647,7 @@ add_executable(pipedal_update
|
|||||||
UpdateMain.cpp
|
UpdateMain.cpp
|
||||||
UpdateResults.cpp UpdateResults.hpp
|
UpdateResults.cpp UpdateResults.hpp
|
||||||
Lv2SystemdLogger.cpp Lv2SystemdLogger.hpp
|
Lv2SystemdLogger.cpp Lv2SystemdLogger.hpp
|
||||||
UpdateResults.cpp UpdateResults.hpp
|
TemporaryFile.cpp TemporaryFile.hpp
|
||||||
AdminInstallUpdate.cpp AdminInstallUpdate.hpp
|
AdminInstallUpdate.cpp AdminInstallUpdate.hpp
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -909,6 +909,9 @@ void Install(const fs::path &programPrefix, const std::string endpointAddress)
|
|||||||
}
|
}
|
||||||
try
|
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();
|
DeployVarConfig();
|
||||||
|
|
||||||
if (sysExec(GROUPADD_BIN " -f " AUDIO_SERVICE_GROUP_NAME) != EXIT_SUCCESS)
|
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()
|
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<AccessPoint::ptr> allAccessPoints = GetAllAccessPoints();
|
||||||
std::vector<std::vector<uint8_t>> allAccessPointSsids = GetAccessPointSsids(allAccessPoints);
|
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.
|
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"] = "a";
|
||||||
wireless["band"] = "bg";
|
wireless["band"] = "bg";
|
||||||
|
|
||||||
uint32_t iChannel;
|
uint32_t iChannel = 0;
|
||||||
auto channel = this->wifiConfigSettings.channel_;
|
auto channel = this->wifiConfigSettings.channel_;
|
||||||
if (channel.length() != 0)
|
if (channel.length() != 0)
|
||||||
{
|
{
|
||||||
|
|||||||
+21
-37
@@ -30,7 +30,7 @@
|
|||||||
|
|
||||||
using namespace pipedal;
|
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)
|
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
|
{ // TODO: Add support for AF_INET6
|
||||||
struct sockaddr_in6 *pAddr = (struct sockaddr_in6 *)(p->ifa_addr);
|
struct sockaddr_in6 *pAddr = (struct sockaddr_in6 *)(p->ifa_addr);
|
||||||
struct sockaddr_in6 *pNetMask = (struct sockaddr_in6 *)(p->ifa_netmask);
|
struct sockaddr_in6 *pNetMask = (struct sockaddr_in6 *)(p->ifa_netmask);
|
||||||
|
|
||||||
if (ipv6NetmaskCompare(inetAddr6, pAddr->sin6_addr, pNetMask->sin6_addr))
|
if (ipv6NetmaskCompare(inetAddr6, pAddr->sin6_addr, pNetMask->sin6_addr))
|
||||||
{
|
{
|
||||||
result = p->ifa_name;
|
result = p->ifa_name;
|
||||||
@@ -313,7 +314,7 @@ static std::string GetInterfaceForIp6Address(const in6_addr inetAddr6)
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::string GetLinkLocalAddressForInterface(const std::string &name)
|
static std::string GetNonLinkLocalAddressForInterface(const std::string &name)
|
||||||
{
|
{
|
||||||
struct ifaddrs *ifap = nullptr;
|
struct ifaddrs *ifap = nullptr;
|
||||||
if (getifaddrs(&ifap) != 0)
|
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)
|
if (p->ifa_addr->sa_family == AF_INET6 && p->ifa_addr != nullptr && p->ifa_netmask != nullptr)
|
||||||
{ // TODO: Add support for AF_INET6
|
{ // TODO: Add support for AF_INET6
|
||||||
struct sockaddr_in6 *pAddr = (struct sockaddr_in6 *)(p->ifa_addr);
|
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)
|
if (name == p->ifa_name)
|
||||||
{
|
{
|
||||||
@@ -353,7 +354,7 @@ static std::string GetLinkLocalAddressForInterface(const std::string &name)
|
|||||||
freeifaddrs(ifap);
|
freeifaddrs(ifap);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
static std::string GetLinkLocalAddressForIp4Interface(const std::string &name)
|
static std::string GetNonLinkLocalAddressForIp4Interface(const std::string &name)
|
||||||
{
|
{
|
||||||
struct ifaddrs *ifap = nullptr;
|
struct ifaddrs *ifap = nullptr;
|
||||||
if (getifaddrs(&ifap) != 0)
|
if (getifaddrs(&ifap) != 0)
|
||||||
@@ -384,23 +385,13 @@ static std::string GetLinkLocalAddressForIp4Interface(const std::string &name)
|
|||||||
return result;
|
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;
|
std::string result;
|
||||||
if (address[0] != '[')
|
if (address[0] != '[')
|
||||||
{
|
{
|
||||||
// ipv4
|
return address;
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -408,13 +399,14 @@ std::string pipedal::GetLinkLocalAddress(const std::string fromAddress)
|
|||||||
if (address[0] != '[' || address[address.length() - 1] != ']')
|
if (address[0] != '[' || address[address.length() - 1] != ']')
|
||||||
throw std::invalid_argument("Bad address.");
|
throw std::invalid_argument("Bad address.");
|
||||||
address = address.substr(1, address.length() - 2);
|
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;
|
struct in6_addr inetAddr6;
|
||||||
memset(&inetAddr6, 0, sizeof(inetAddr6));
|
memset(&inetAddr6, 0, sizeof(inetAddr6));
|
||||||
if (inet_pton(AF_INET6, address.c_str(), &inetAddr6) == 1)
|
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;
|
int8_t *pAddr = (int8_t *)&inetAddr6;
|
||||||
uint32_t remoteAddress = htonl(*(int32_t *)(pAddr + 12));
|
uint32_t remoteAddress = htonl(*(int32_t *)(pAddr + 12));
|
||||||
std::string interfaceName = GetInterfaceForIp4Address(remoteAddress);
|
std::string interfaceName = GetInterfaceForIp4Address(remoteAddress);
|
||||||
result = GetLinkLocalAddressForIp4Interface(interfaceName);
|
result = GetNonLinkLocalAddressForIp4Interface(interfaceName);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
||||||
std::string interfaceName;
|
|
||||||
if (IsIpv4MappedAddress(inetAddr6))
|
std::string interfaceName = GetInterfaceForIp6Address(inetAddr6);
|
||||||
{
|
result = GetNonLinkLocalAddressForInterface(interfaceName);
|
||||||
uint32_t remoteAddress = GetIpv4MappedAddress(inetAddr6);
|
|
||||||
interfaceName = GetInterfaceForIp4Address(remoteAddress);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
interfaceName = GetInterfaceForIp6Address(inetAddr6);
|
|
||||||
}
|
|
||||||
result = GetLinkLocalAddressForInterface(interfaceName);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (result == "")
|
if (result == "")
|
||||||
{
|
{
|
||||||
result = GetLinkLocalAddressForInterface("");
|
result = GetNonLinkLocalAddressForInterface("");
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string pipedal::GetInterfaceIpv4Address(const std::string& interfaceName)
|
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 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);
|
bool IsOnLocalSubnet(const std::string&fromAddress);
|
||||||
|
|
||||||
|
|||||||
+96
-2
@@ -37,6 +37,7 @@
|
|||||||
#include "Lv2PluginChangeMonitor.hpp"
|
#include "Lv2PluginChangeMonitor.hpp"
|
||||||
#include "HotspotManager.hpp"
|
#include "HotspotManager.hpp"
|
||||||
#include "DBusToLv2Log.hpp"
|
#include "DBusToLv2Log.hpp"
|
||||||
|
#include "SysExec.hpp"
|
||||||
|
|
||||||
#ifndef NO_MLOCK
|
#ifndef NO_MLOCK
|
||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
@@ -1957,7 +1958,7 @@ void PiPedalModel::CancelMonitorPatchProperty(int64_t clientId, int64_t clientHa
|
|||||||
atomOutputListeners.erase(atomOutputListeners.begin() + i);
|
atomOutputListeners.erase(atomOutputListeners.begin() + i);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (midiEventListeners.size() == 0)
|
if (midiEventListeners.size() == 0)
|
||||||
{
|
{
|
||||||
audioHost->SetListenForMidiEvent(false);
|
audioHost->SetListenForMidiEvent(false);
|
||||||
@@ -2343,14 +2344,107 @@ void PiPedalModel::OnNetworkChanging(bool ethernetConnected,bool hotspotConnecte
|
|||||||
{
|
{
|
||||||
CancelNetworkChangingTimer();
|
CancelNetworkChangingTimer();
|
||||||
this->networkChangingDelayHandle =
|
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,ethernetConnected,hotspotConnected]() {
|
||||||
this->networkChangingDelayHandle = 0;
|
this->networkChangingDelayHandle = 0;
|
||||||
OnNetworkChanged(ethernetConnected,hotspotConnected);
|
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)
|
void PiPedalModel::OnNetworkChanged(bool ethernetConnected, bool hotspotConnected)
|
||||||
{
|
{
|
||||||
FireNetworkChanged();
|
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 OnFavoritesChanged(const std::map<std::string, bool> &favorites) = 0;
|
||||||
virtual void OnShowStatusMonitorChanged(bool show) = 0;
|
virtual void OnShowStatusMonitorChanged(bool show) = 0;
|
||||||
virtual void OnSystemMidiBindingsChanged(const std::vector<MidiBinding>&bindings) = 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 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 OnErrorMessage(const std::string&message) = 0;
|
||||||
virtual void OnLv2PluginsChanging() = 0;
|
virtual void OnLv2PluginsChanging() = 0;
|
||||||
|
virtual void OnNetworkChanging(bool hotspotConnected) = 0;
|
||||||
virtual void Close() = 0;
|
virtual void Close() = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -203,6 +205,8 @@ namespace pipedal
|
|||||||
virtual void OnNotifyMidiValueChanged(int64_t instanceId, int portIndex, float value) override;
|
virtual void OnNotifyMidiValueChanged(int64_t instanceId, int portIndex, float value) override;
|
||||||
virtual void OnNotifyMidiListen(bool isNote, uint8_t noteOrControl) 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 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);
|
void OnNotifyPatchProperty(uint64_t instanceId, LV2_URID outputAtomProperty, const std::string &atomJson);
|
||||||
|
|
||||||
@@ -227,7 +231,7 @@ namespace pipedal
|
|||||||
PiPedalModel();
|
PiPedalModel();
|
||||||
virtual ~PiPedalModel();
|
virtual ~PiPedalModel();
|
||||||
|
|
||||||
|
void RequestShutdown(bool restart);
|
||||||
|
|
||||||
virtual PostHandle Post(PostCallback&&fn);
|
virtual PostHandle Post(PostCallback&&fn);
|
||||||
virtual PostHandle PostDelayed(const clock::duration&delay,PostCallback&&fn);
|
virtual PostHandle PostDelayed(const clock::duration&delay,PostCallback&&fn);
|
||||||
|
|||||||
+14
-41
@@ -459,7 +459,6 @@ private:
|
|||||||
{
|
{
|
||||||
return model.GetAdminClient();
|
return model.GetAdminClient();
|
||||||
}
|
}
|
||||||
void RequestShutdown(bool restart);
|
|
||||||
|
|
||||||
std::recursive_mutex writeMutex;
|
std::recursive_mutex writeMutex;
|
||||||
PiPedalModel &model;
|
PiPedalModel &model;
|
||||||
@@ -1246,15 +1245,12 @@ public:
|
|||||||
}
|
}
|
||||||
else if (message == "shutdown")
|
else if (message == "shutdown")
|
||||||
{
|
{
|
||||||
PresetIndex newIndex;
|
model.RequestShutdown(false);
|
||||||
|
|
||||||
RequestShutdown(false);
|
|
||||||
this->Reply(replyTo, "shutdown");
|
this->Reply(replyTo, "shutdown");
|
||||||
}
|
}
|
||||||
else if (message == "restart")
|
else if (message == "restart")
|
||||||
{
|
{
|
||||||
PresetIndex newIndex;
|
model.RequestShutdown(true);
|
||||||
RequestShutdown(true);
|
|
||||||
this->Reply(replyTo, "restart");
|
this->Reply(replyTo, "restart");
|
||||||
}
|
}
|
||||||
else if (message == "deletePresetItem")
|
else if (message == "deletePresetItem")
|
||||||
@@ -1627,6 +1623,18 @@ private:
|
|||||||
Send("onLv2PluginsChanging",true);
|
Send("onLv2PluginsChanging",true);
|
||||||
Flush();
|
Flush();
|
||||||
}
|
}
|
||||||
|
virtual void OnNetworkChanging(bool hotspotConnected) override {
|
||||||
|
try {
|
||||||
|
Send("onNetworkChanging",hotspotConnected);
|
||||||
|
Flush();
|
||||||
|
|
||||||
|
} catch (const std::exception&ignored)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
virtual void OnErrorMessage(const std::string&message)
|
virtual void OnErrorMessage(const std::string&message)
|
||||||
{
|
{
|
||||||
@@ -1982,38 +1990,3 @@ std::shared_ptr<ISocketFactory> pipedal::MakePiPedalSocketFactory(PiPedalModel &
|
|||||||
return std::make_shared<PiPedalSocketFactory>(model);
|
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 "VuUpdate.hpp"
|
||||||
#include "AudioHost.hpp"
|
#include "AudioHost.hpp"
|
||||||
#include "lv2/atom/atom.h"
|
#include "lv2/atom/atom.h"
|
||||||
|
#include "RealtimeMidiEventType.hpp"
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
namespace pipedal
|
namespace pipedal
|
||||||
{
|
{
|
||||||
@@ -65,9 +66,15 @@ namespace pipedal
|
|||||||
SetOutputVolume,
|
SetOutputVolume,
|
||||||
Lv2ErrorMessage,
|
Lv2ErrorMessage,
|
||||||
|
|
||||||
|
RealtimeMidiEvent,
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct RealtimeMidiEventRequest {
|
||||||
|
RealtimeMidiEventType eventType;
|
||||||
|
};
|
||||||
struct RealtimeNextMidiProgramRequest {
|
struct RealtimeNextMidiProgramRequest {
|
||||||
int64_t requestId;
|
int64_t requestId;
|
||||||
int32_t direction;
|
int32_t direction;
|
||||||
@@ -371,6 +378,11 @@ namespace pipedal
|
|||||||
|
|
||||||
write(RingBufferCommand::MidiProgramChange,msg);
|
write(RingBufferCommand::MidiProgramChange,msg);
|
||||||
}
|
}
|
||||||
|
void OnRealtimeMidiEvent(RealtimeMidiEventType eventType)
|
||||||
|
{
|
||||||
|
RealtimeMidiEventRequest msg { eventType};
|
||||||
|
write(RingBufferCommand::RealtimeMidiEvent,msg);
|
||||||
|
}
|
||||||
void OnNextMidiProgram(int64_t requestId,int32_t direction)
|
void OnNextMidiProgram(int64_t requestId,int32_t direction)
|
||||||
{
|
{
|
||||||
RealtimeNextMidiProgramRequest msg { requestId: requestId, direction:direction};
|
RealtimeNextMidiProgramRequest msg { requestId: requestId, direction:direction};
|
||||||
|
|||||||
+30
-10
@@ -960,7 +960,7 @@ bool Storage::SetWifiConfigSettings(const WifiConfigSettings &wifiConfigSettings
|
|||||||
if (!copyToSave.hasPassword_)
|
if (!copyToSave.hasPassword_)
|
||||||
{
|
{
|
||||||
copyToSave.hasPassword_ = previousValue.hasPassword_;
|
copyToSave.hasPassword_ = previousValue.hasPassword_;
|
||||||
copyToSave.password_ = previousValue.hasPassword_;
|
copyToSave.password_ = previousValue.password_;
|
||||||
copyToSave.hasSavedPassword_ = previousValue.hasPassword_;
|
copyToSave.hasSavedPassword_ = previousValue.hasPassword_;
|
||||||
} else {
|
} else {
|
||||||
if (copyToSave.IsEnabled())
|
if (copyToSave.IsEnabled())
|
||||||
@@ -1409,7 +1409,7 @@ void Storage::SetJackServerSettings(const pipedal::JackServerSettings &jackConfi
|
|||||||
|
|
||||||
void Storage::SetSystemMidiBindings(const std::vector<MidiBinding> &bindings)
|
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;
|
pipedal::ofstream_synced f;
|
||||||
f.open(fileName);
|
f.open(fileName);
|
||||||
if (f.is_open())
|
if (f.is_open())
|
||||||
@@ -1422,19 +1422,39 @@ std::vector<MidiBinding> Storage::GetSystemMidiBindings()
|
|||||||
{
|
{
|
||||||
std::vector<MidiBinding> result;
|
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;
|
std::ifstream f;
|
||||||
f.open(fileName);
|
f.open(fileName);
|
||||||
if (f.is_open())
|
if (f.is_open())
|
||||||
{
|
{
|
||||||
json_reader reader(f);
|
try {
|
||||||
reader.read(&result);
|
json_reader reader(f);
|
||||||
}
|
reader.read(&result);
|
||||||
else
|
if (result.size() == 2)
|
||||||
{
|
{
|
||||||
result.push_back(MidiBinding::SystemBinding("prevProgram"));
|
result.push_back(MidiBinding::SystemBinding("stopHotspot"));
|
||||||
result.push_back(MidiBinding::SystemBinding("nextProgram"));
|
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;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
#include "TemporaryFile.hpp"
|
#include "TemporaryFile.hpp"
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
using namespace pipedal;
|
using namespace pipedal;
|
||||||
|
|
||||||
|
|||||||
@@ -28,6 +28,8 @@ namespace pipedal {
|
|||||||
TemporaryFile(const std::filesystem::path&parentDirectory);
|
TemporaryFile(const std::filesystem::path&parentDirectory);
|
||||||
~TemporaryFile();
|
~TemporaryFile();
|
||||||
const std::filesystem::path&Path()const { return path;}
|
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:
|
private:
|
||||||
std::filesystem::path path;
|
std::filesystem::path path;
|
||||||
};
|
};
|
||||||
|
|||||||
+251
-16
@@ -35,6 +35,9 @@
|
|||||||
#include "UpdaterSecurity.hpp"
|
#include "UpdaterSecurity.hpp"
|
||||||
#include "SysExec.hpp"
|
#include "SysExec.hpp"
|
||||||
#include "ofstream_synced.hpp"
|
#include "ofstream_synced.hpp"
|
||||||
|
#include "TemporaryFile.hpp"
|
||||||
|
#include "ofstream_synced.hpp"
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
using namespace pipedal;
|
using namespace pipedal;
|
||||||
namespace fs = std::filesystem;
|
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()
|
void Updater::ThreadProc()
|
||||||
{
|
{
|
||||||
SetThreadName("UpdateMonitor");
|
SetThreadName("UpdateMonitor");
|
||||||
@@ -172,10 +184,28 @@ void Updater::ThreadProc()
|
|||||||
pfd.fd = this->event_reader;
|
pfd.fd = this->event_reader;
|
||||||
pfd.events = POLLIN;
|
pfd.events = POLLIN;
|
||||||
|
|
||||||
|
auto lastRetryTime = clock::time_point();
|
||||||
|
this->updateRetryTime = LoadRetryTime();
|
||||||
try {
|
try {
|
||||||
while (true)
|
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)
|
if (ret == -1)
|
||||||
{
|
{
|
||||||
@@ -184,7 +214,10 @@ void Updater::ThreadProc()
|
|||||||
}
|
}
|
||||||
else if (ret == 0)
|
else if (ret == 0)
|
||||||
{
|
{
|
||||||
CheckForUpdate(true);
|
if (clock::now() >= this->updateRetryTime)
|
||||||
|
{
|
||||||
|
CheckForUpdate(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -195,6 +228,7 @@ void Updater::ThreadProc()
|
|||||||
{
|
{
|
||||||
if (value == CHECK_NOW_EVENT)
|
if (value == CHECK_NOW_EVENT)
|
||||||
{
|
{
|
||||||
|
|
||||||
CheckForUpdate(true);
|
CheckForUpdate(true);
|
||||||
}
|
}
|
||||||
else if (value == UNCACHED_CHECK_NOW_EVENT)
|
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)
|
void Updater::CheckForUpdate(bool useCache)
|
||||||
{
|
{
|
||||||
UpdateStatus updateResult;
|
UpdateStatus updateResult;
|
||||||
|
|
||||||
|
if (useCache)
|
||||||
{
|
{
|
||||||
std::lock_guard lock{mutex};
|
std::lock_guard lock{mutex};
|
||||||
if (useCache && IsCacheValid(cachedUpdateStatus))
|
|
||||||
{
|
{
|
||||||
this->currentResult = cachedUpdateStatus;
|
if (IsCacheValid(cachedUpdateStatus))
|
||||||
this->currentResult.UpdatePolicy(this->updatePolicy);
|
|
||||||
if (listener)
|
|
||||||
{
|
{
|
||||||
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}\"";
|
// const std::string responseOption = "-w \"%{response_code}\"";
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
responseOption = "-w \"%%{response_code}\""; // windows shell requires doubling of the %%.
|
responseOption = "-w \"%%{response_code}\""; // windows shell requires doubling of the %%.
|
||||||
#endif
|
#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_ = "";
|
updateResult.errorMessage_ = "";
|
||||||
|
|
||||||
@@ -451,10 +599,33 @@ void Updater::CheckForUpdate(bool useCache)
|
|||||||
auto result = sysExecForOutput("curl", args);
|
auto result = sysExecForOutput("curl", args);
|
||||||
if (result.exitCode != EXIT_SUCCESS)
|
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.");
|
throw std::runtime_error("Server has no internet access.");
|
||||||
}
|
}
|
||||||
else
|
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)
|
if (result.output.length() == 0)
|
||||||
{
|
{
|
||||||
throw std::runtime_error("Server has no internet access.");
|
throw std::runtime_error("Server has no internet access.");
|
||||||
@@ -468,11 +639,16 @@ void Updater::CheckForUpdate(bool useCache)
|
|||||||
// an HTML error.
|
// an HTML error.
|
||||||
updateResult.isOnline_ = false;
|
updateResult.isOnline_ = false;
|
||||||
auto o = vResult.as_object();
|
auto o = vResult.as_object();
|
||||||
std::string message = o->at("message").as_string();
|
std::string message = "Unknown error.";
|
||||||
auto status_code = o->at("status_code").as_int64();
|
if (o->at("message").is_string())
|
||||||
throw std::runtime_error(SS("Service error. ()" << status_code << ": " << message << ")"));
|
{
|
||||||
}
|
message = o->at("message").as_string();
|
||||||
else
|
}
|
||||||
|
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();
|
json_variant::array_ptr vArray = vResult.as_array();
|
||||||
|
|
||||||
@@ -534,7 +710,7 @@ void Updater::CheckForUpdate(bool useCache)
|
|||||||
}
|
}
|
||||||
catch (const std::exception &e)
|
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.errorMessage_ = e.what();
|
||||||
updateResult.isValid_ = false;
|
updateResult.isValid_ = false;
|
||||||
updateResult.isOnline_ = false;
|
updateResult.isOnline_ = false;
|
||||||
@@ -897,6 +1073,65 @@ static void checkCurlHttpResponse(std::string errorCode)
|
|||||||
throw std::runtime_error(message);
|
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)
|
void Updater::DownloadUpdate(const std::string &url, std::filesystem::path *file, std::filesystem::path *signatureFile)
|
||||||
{
|
{
|
||||||
std::string filename, signatureUrl;
|
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);
|
void DownloadUpdate(const std::string &url, std::filesystem::path*file, std::filesystem::path*signatureFile);
|
||||||
UpdateStatus GetCurrentStatus() const { return this->currentResult; }
|
UpdateStatus GetCurrentStatus() const { return this->currentResult; }
|
||||||
private:
|
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 GetUpdateFilename(const std::string &url);
|
||||||
std::string GetSignatureUrl(const std::string &url);
|
std::string GetSignatureUrl(const std::string &url);
|
||||||
|
|
||||||
@@ -140,7 +153,6 @@ namespace pipedal
|
|||||||
|
|
||||||
UpdateStatus cachedUpdateStatus;
|
UpdateStatus cachedUpdateStatus;
|
||||||
bool stopped = false;
|
bool stopped = false;
|
||||||
using clock = std::chrono::steady_clock;
|
|
||||||
|
|
||||||
int event_reader = -1;
|
int event_reader = -1;
|
||||||
int event_writer = -1;
|
int event_writer = -1;
|
||||||
@@ -154,5 +166,6 @@ namespace pipedal
|
|||||||
bool hasInfo = false;
|
bool hasInfo = false;
|
||||||
UpdateStatus currentResult;
|
UpdateStatus currentResult;
|
||||||
static clock::duration updateRate;
|
static clock::duration updateRate;
|
||||||
|
clock::time_point updateRetryTime;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
+8
-1
@@ -1320,6 +1320,13 @@ void WebServerImpl::DisplayIpAddresses()
|
|||||||
std::string wifiAddress = getIpv4Address("wlan0");
|
std::string wifiAddress = getIpv4Address("wlan0");
|
||||||
if (wifiAddress.length() != 0)
|
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
|
/* When hosting a react app, replace /var/config.json with
|
||||||
data that will connect the react app with our socket server.
|
data that will connect the react app with our socket server.
|
||||||
*/
|
*/
|
||||||
@@ -491,13 +518,8 @@ public:
|
|||||||
}
|
}
|
||||||
std::string GetConfig(const std::string &fromAddress)
|
std::string GetConfig(const std::string &fromAddress)
|
||||||
{
|
{
|
||||||
#define LINK_LOCAL_WEB_SOCKET 1
|
std::string webSocketAddress = GetNonLinkLocalAddress(StripPortNumber(fromAddress));
|
||||||
#if LINK_LOCAL_WEB_SOCKET
|
|
||||||
std::string webSocketAddress = GetLinkLocalAddress(fromAddress);
|
|
||||||
Lv2Log::info(SS("Web Socket Address: " << webSocketAddress << ":" << portNumber));
|
Lv2Log::info(SS("Web Socket Address: " << webSocketAddress << ":" << portNumber));
|
||||||
#else
|
|
||||||
std::string webSocketAddress = "*";
|
|
||||||
#endif
|
|
||||||
|
|
||||||
std::stringstream s;
|
std::stringstream s;
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1,9 @@
|
|||||||
Installer:
|
|
||||||
|
|
||||||
sudo systemctl restart polkitd.service
|
|
||||||
|
|
||||||
- Did I break ALSA device config?be
|
|
||||||
|
|
||||||
|
- Shutdown/reboot midi bindings
|
||||||
|
- Midi binding for hotspotManager.
|
||||||
|
|
||||||
- verify that scanning doesn't cause overruns.
|
- verify that scanning doesn't cause overruns.
|
||||||
- address change handlers in client (and browser?)
|
- address change handlers in client (and browser?)
|
||||||
- Notify hotspotManager on settings changed.
|
|
||||||
- Midi binding for hotspotManager.
|
|
||||||
- Keybaord escape for hotspotManager.
|
- Keybaord escape for hotspotManager.
|
||||||
- verify stripping in installer.
|
- verify stripping in installer.
|
||||||
- verify update with no keyring.
|
- verify update with no keyring.
|
||||||
@@ -17,21 +12,15 @@ Installer:
|
|||||||
X Review docs changes once we go live.
|
X Review docs changes once we go live.
|
||||||
|
|
||||||
- Back button after reloads on Android.
|
- Back button after reloads on Android.
|
||||||
- pidedal_nm_p2p2 service not started from web ui.
|
|
||||||
- pipedal_nm_p2pd connection not visible.
|
|
||||||
- hdcpcd spurious ipv6 error messages.
|
|
||||||
- also stopping on startup, even with big buffers.
|
|
||||||
- pipedald admin needs to fork in response to a wifi p2p update or do it on a thread.
|
|
||||||
|
|
||||||
Localisation: support non-UTF8 code pages.
|
Localisation: support non-UTF8 code pages.
|
||||||
- unicode commandline arguments.
|
- unicode commandline arguments.
|
||||||
- unicode filenames.
|
- review unicode filenames (this is probably ok)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
- make app use the same theme settings as the website.
|
- make app use the same theme settings as the website.
|
||||||
- BUG: gcs when we have an animated output control
|
- BUG: gcs when we have an animated output control
|
||||||
- Shutdown/reboot midi bindings
|
|
||||||
- versioning.
|
- versioning.
|
||||||
- do we want to take a second crack at establishin multi-p2p connections with the driver_param?
|
- do we want to take a second crack at establishin multi-p2p connections with the driver_param?
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user