Updates bug fixes.

This commit is contained in:
Robin Davies
2024-08-28 21:04:13 -04:00
parent 1e9a0d7b7d
commit 76dfda2b77
7 changed files with 144 additions and 51 deletions
-1
View File
@@ -781,7 +781,6 @@ export class PiPedalModel //implements PiPedalModel
onSocketConnectionLost() { onSocketConnectionLost() {
// remove all the events and subscriptions we have. // remove all the events and subscriptions we have.
// yyy
this.vuSubscriptions = []; this.vuSubscriptions = [];
this.monitorPatchPropertyListeners = []; this.monitorPatchPropertyListeners = [];
+4 -3
View File
@@ -126,7 +126,7 @@ export default class UpdateDialog extends React.Component<UpdateDialogProps, Upd
upToDate(): boolean { upToDate(): boolean {
let updateStatus = this.state.updateStatus; let updateStatus = this.state.updateStatus;
let updateRelease: UpdateRelease = updateStatus.getActiveRelease(); let updateRelease: UpdateRelease = updateStatus.getActiveRelease();
return updateStatus.isValid && !updateRelease.updateAvailable return !updateRelease.updateAvailable
} }
canUpgrade(): boolean { canUpgrade(): boolean {
let updateStatus = this.state.updateStatus; let updateStatus = this.state.updateStatus;
@@ -153,6 +153,7 @@ export default class UpdateDialog extends React.Component<UpdateDialogProps, Upd
let upToDate = this.upToDate(); let upToDate = this.upToDate();
let canUpgrade = this.canUpgrade(); let canUpgrade = this.canUpgrade();
let compact = this.state.compactLandscape; let compact = this.state.compactLandscape;
let showUpgradeVersion = !upToDate && updateStatus.isValid && updateRelease.upgradeVersionDisplayName !== "";
return ( return (
<DialogEx tag="update" open={this.props.open} onClose={() => { this.handleClose(); }} <DialogEx tag="update" open={this.props.open} onClose={() => { this.handleClose(); }}
style={{ userSelect: "none" }} style={{ userSelect: "none" }}
@@ -202,7 +203,7 @@ export default class UpdateDialog extends React.Component<UpdateDialogProps, Upd
<Typography noWrap variant="body2" color="textSecondary" > <Typography noWrap variant="body2" color="textSecondary" >
Current version: Current version:
</Typography> </Typography>
{(!upToDate) && ( {(showUpgradeVersion) && (
<Typography noWrap variant="body2" color="textSecondary" style={{ marginTop: 4 }} > <Typography noWrap variant="body2" color="textSecondary" style={{ marginTop: 4 }} >
Update version: Update version:
</Typography> </Typography>
@@ -213,7 +214,7 @@ export default class UpdateDialog extends React.Component<UpdateDialogProps, Upd
<Typography noWrap variant="body2" color="textSecondary" > <Typography noWrap variant="body2" color="textSecondary" >
{updateStatus.currentVersionDisplayName} {updateStatus.currentVersionDisplayName}
</Typography> </Typography>
{(!upToDate) && ( {(showUpgradeVersion) && (
<Typography noWrap variant="body2" color="textSecondary" style={{ marginTop: 4 }} > <Typography noWrap variant="body2" color="textSecondary" style={{ marginTop: 4 }} >
{updateRelease.upgradeVersionDisplayName} {updateRelease.upgradeVersionDisplayName}
</Typography> </Typography>
+1 -1
View File
@@ -178,7 +178,7 @@ void pipedal::AdminInstallUpdate(const std::filesystem::path path)
} }
catch (const std::exception &e) catch (const std::exception &e)
{ {
updateLog(e.what()); updateLog(SS("Error: " << e.what()));
updateResults.updated_ = false; updateResults.updated_ = false;
updateResults.updateSuccessful_ = false; updateResults.updateSuccessful_ = false;
-2
View File
@@ -792,10 +792,8 @@ void InstallPgpKey()
std::stringstream ss; std::stringstream ss;
ss << (CHOWN_BIN " -R " SERVICE_GROUP_NAME ":" SERVICE_GROUP_NAME " ") << homeDir.c_str(); ss << (CHOWN_BIN " -R " SERVICE_GROUP_NAME ":" SERVICE_GROUP_NAME " ") << homeDir.c_str();
std::string cmd = ss.str(); std::string cmd = ss.str();
cout << cmd << endl;
sysExec(cmd.c_str()); sysExec(cmd.c_str());
} }
cout << "Debug: Finished creating keystring." << endl;
} }
void Install(const fs::path &programPrefix, const std::string endpointAddress) void Install(const fs::path &programPrefix, const std::string endpointAddress)
{ {
+2 -2
View File
@@ -66,6 +66,7 @@ PiPedalModel::PiPedalModel()
: pluginHost(), : pluginHost(),
atomConverter(pluginHost.GetMapFeature()) atomConverter(pluginHost.GetMapFeature())
{ {
this->currentUpdateStatus = updater.GetCurrentStatus();
this->pedalboard = Pedalboard::MakeDefault(); this->pedalboard = Pedalboard::MakeDefault();
#if JACK_HOST #if JACK_HOST
this->jackServerSettings = this->storage.GetJackServerSettings(); // to get onboarding flag. this->jackServerSettings = this->storage.GetJackServerSettings(); // to get onboarding flag.
@@ -1258,7 +1259,6 @@ void PiPedalModel::OnNotifyMidiValueChanged(int64_t instanceId, int portIndex, f
{ {
if (portIndex == -1) if (portIndex == -1)
{ {
// bypass! yyy this->value!!!
this->pedalboard.SetItemEnabled(instanceId, value != 0); this->pedalboard.SetItemEnabled(instanceId, value != 0);
// take a snapshot incase a client unsusbscribes in the notification handler (in which case the mutex won't protect us) // take a snapshot incase a client unsusbscribes in the notification handler (in which case the mutex won't protect us)
IPiPedalModelSubscriber **t = new IPiPedalModelSubscriber *[this->subscribers.size()]; IPiPedalModelSubscriber **t = new IPiPedalModelSubscriber *[this->subscribers.size()];
@@ -2168,7 +2168,7 @@ void PiPedalModel::FireUpdateStatusChanged(const UpdateStatus &updateStatus)
UpdateStatus PiPedalModel::GetUpdateStatus() UpdateStatus PiPedalModel::GetUpdateStatus()
{ {
std::lock_guard<std::recursive_mutex> lock(mutex); std::lock_guard<std::recursive_mutex> lock(mutex);
return currentUpdateStatus; return updater.GetCurrentStatus();
} }
void PiPedalModel::UpdateNow(const std::string &updateUrl) void PiPedalModel::UpdateNow(const std::string &updateUrl)
+134 -42
View File
@@ -63,7 +63,7 @@ static UpdateStatus GetCachedUpdateStatus()
if (std::filesystem::exists(UPDATE_STATUS_CACHE_FILE)) if (std::filesystem::exists(UPDATE_STATUS_CACHE_FILE))
{ {
std::ifstream f{UPDATE_STATUS_CACHE_FILE}; std::ifstream f{UPDATE_STATUS_CACHE_FILE};
if (!f.is_open()) if (f.is_open())
{ {
json_reader reader(f); json_reader reader(f);
UpdateStatus status; UpdateStatus status;
@@ -100,7 +100,7 @@ static void SetCachedUpdateStatus(UpdateStatus &updateStatus)
Updater::Updater() Updater::Updater()
{ {
cachedUpdateStatus = GetCachedUpdateStatus(); cachedUpdateStatus = GetCachedUpdateStatus();
updatePolicy = cachedUpdateStatus.UpdatePolicy(); this->updatePolicy = cachedUpdateStatus.UpdatePolicy();
currentResult = cachedUpdateStatus; currentResult = cachedUpdateStatus;
int fds[2]; int fds[2];
@@ -382,6 +382,36 @@ UpdateRelease Updater::getUpdateRelease(
return UpdateRelease(); return UpdateRelease();
} }
static void CheckUpdateHttpResponse(std::string errorCode)
{
if (errorCode.starts_with("%"))
{
errorCode = errorCode.substr(1);
}
int code = -999;
{
std::istringstream ss{errorCode};
ss >> code;
}
if (code == -999)
{
throw std::runtime_error(SS("Invalid curl response: " << errorCode));
}
if (code == 200)
{
return;
}
if (code == 0)
{
throw std::runtime_error("PiPedal server can't reach the internet.");
}
{
std::string message = SS("HTTP error " << code << "");
throw std::runtime_error(message);
}
}
void Updater::CheckForUpdate(bool useCache) void Updater::CheckForUpdate(bool useCache)
{ {
UpdateStatus updateResult; UpdateStatus updateResult;
@@ -400,7 +430,13 @@ void Updater::CheckForUpdate(bool useCache)
} }
updateResult = this->currentResult; updateResult = this->currentResult;
} }
std::string args = SS("-s " << GITHUB_RELEASES_URL);
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 " << responseOption << " " << GITHUB_RELEASES_URL);
updateResult.errorMessage_ = ""; updateResult.errorMessage_ = "";
@@ -413,6 +449,10 @@ void Updater::CheckForUpdate(bool useCache)
} }
else else
{ {
if (result.output.length() == 0)
{
throw std::runtime_error("Server has no internet access.");
}
std::stringstream ss(result.output); std::stringstream ss(result.output);
json_reader reader(ss); json_reader reader(ss);
json_variant vResult(reader); json_variant vResult(reader);
@@ -535,14 +575,11 @@ void Updater::SetUpdatePolicy(UpdatePolicyT updatePolicy)
if (updatePolicy == this->updatePolicy) if (updatePolicy == this->updatePolicy)
return; return;
this->updatePolicy = updatePolicy; this->updatePolicy = updatePolicy;
if (this->currentResult.UpdatePolicy() != updatePolicy) this->currentResult.UpdatePolicy(updatePolicy);
SetCachedUpdateStatus(this->currentResult);
if (listener)
{ {
this->currentResult.UpdatePolicy(updatePolicy); listener(currentResult);
SetCachedUpdateStatus(this->currentResult);
if (listener)
{
listener(currentResult);
}
} }
} }
void Updater::ForceUpdateCheck() void Updater::ForceUpdateCheck()
@@ -721,30 +758,40 @@ static void removeOldSiblings(int numberToKeep, const std::filesystem::path &fil
} }
} }
static std::string getKeyId(const std::string&gpgText) static std::string getFingerprint(const std::string &gpgText)
{ {
std::string keyPosition = "using RSA key "; std::string keyPosition = "using RSA key ";
size_t nPos = gpgText.find(keyPosition); size_t nPos = gpgText.find(keyPosition);
if (nPos = std::string::npos) if (nPos == std::string::npos)
{ {
return ""; return "";
} }
nPos += keyPosition.size(); nPos += keyPosition.length();
while (nPos < gpgText.length() && gpgText[nPos] == ' ')
{
++nPos;
}
auto start = nPos; auto start = nPos;
while (true) while (true)
{ {
char c = gpgText[nPos]; char c = gpgText[nPos];
if (nPos >= gpgText.length() || c == ' ' || c == '\r' && c == '\n' && c == '\t') if (nPos >= gpgText.length() || c == ' ' || c == '\r' || c == '\n' || c == '\t')
{ {
break; break;
} }
++nPos; ++nPos;
} }
return gpgText.substr(start,nPos-start); return gpgText.substr(start, nPos - start);
} }
static std::string getAddress(const std::string&gpgText) static bool IsSignatureGood(const std::string&gpgText)
{
std::string originPosition = "gpg: Good signature from \"";
size_t nPos = gpgText.find(originPosition);
return nPos != std::string::npos;
}
static std::string getAddress(const std::string &gpgText)
{ {
std::string originPosition = "gpg: Good signature from \""; std::string originPosition = "gpg: Good signature from \"";
@@ -753,6 +800,7 @@ static std::string getAddress(const std::string&gpgText)
{ {
return ""; return "";
} }
nPos += originPosition.length();
auto start = nPos; auto start = nPos;
while (true) while (true)
{ {
@@ -762,38 +810,44 @@ static std::string getAddress(const std::string&gpgText)
} }
++nPos; ++nPos;
} }
return gpgText.substr(start,nPos-start); return gpgText.substr(start, nPos - start);
} }
void Updater::ValidateSignature(const std::filesystem::path&file, const std::filesystem::path&signatureFile) void Updater::ValidateSignature(const std::filesystem::path &file, const std::filesystem::path &signatureFile)
{ {
// sudo gpg --home /var/pipedal/config/gpg --verify pipedal_1.2.41_arm64.deb.asc pipedal_1.2.41_arm64.deb // sudo gpg --home /var/pipedal/config/gpg --verify pipedal_1.2.41_arm64.deb.asc pipedal_1.2.41_arm64.deb
// gpg: assuming signed data in 'pipedal_1.2.41_arm64.deb' // gpg: assuming signed data in 'pipedal_1.2.41_arm64.deb'
// gpg: Signature made Tue 27 Aug 2024 09:25:06 PM EDT // gpg: Signature made Tue 27 Aug 2024 09:25:06 PM EDT
// gpg: using RSA key 381124E2BB4478D225D2313B2AEF3F7BD53EAA59 // gpg: using RSA key 381124E2BB4478D225D2313B2AEF3F7BD53EAA59
// gpg: Good signature from "Robin Davies <rerdavies@gmail.com>" [ultimate] // gpg: Good signature from "Robin Davies <rerdavies@gmail.com>" [ultimate]
std::ostringstream ss; std::ostringstream ss;
ss << "-home " << PGP_UPDATE_KEYRING_PATH ss << "--home " << PGP_UPDATE_KEYRING_PATH
<< " --verify " << " --verify "
<< signatureFile << " " << file; << signatureFile << " " << file;
auto gpgOutput = sysExecForOutput("/usr/bin/gpg",ss.str()); Lv2Log::info(SS("/usr/bin/gpg " << ss.str()));
auto gpgOutput = sysExecForOutput("/usr/bin/gpg", ss.str());
if (gpgOutput.exitCode != EXIT_SUCCESS) if (gpgOutput.exitCode != EXIT_SUCCESS)
{ {
throw std::runtime_error("Update file is not correctly signed."); throw std::runtime_error("Update file is not correctly signed.");
} }
const std::string&gpgText = gpgOutput.output; const std::string &gpgText = gpgOutput.output;
std::string keyId = getKeyId(gpgText); if (!IsSignatureGood(gpgText))
{
throw std::runtime_error("Update signature is not valid.");
}
std::string keyId = getFingerprint(gpgText);
Lv2Log::info(unCRLF(gpgText)); // yyy delete me.
if (keyId != UPDATE_GPG_FINGERPRINT) if (keyId != UPDATE_GPG_FINGERPRINT)
{ {
throw std::runtime_error("Update signature has the wrong fingerprint."); throw std::runtime_error(SS("Update signature has the wrong fingerprint: " << keyId));
} }
std::string origin = getAddress(gpgText); std::string origin = getAddress(gpgText);
if (origin != UPDATE_GPG_ADDRESS) if (origin != UPDATE_GPG_ADDRESS)
{ {
throw std::runtime_error("Update signature has an incorrect address."); throw std::runtime_error(SS("Update signature has an incorrect address." << origin));
} }
} }
@@ -801,9 +855,39 @@ static bool badOutput(const std::filesystem::path &filePath)
{ {
if (!fs::is_regular_file(filePath)) if (!fs::is_regular_file(filePath))
{ {
return false; return true;
}
return fs::file_size(filePath) == 0;
}
static void checkCurlHttpResponse(std::string errorCode)
{
if (errorCode.starts_with("%"))
{
errorCode = errorCode.substr(1);
}
int code = -999;
{
std::istringstream ss{errorCode};
ss >> code;
}
if (code == -999)
{
throw std::runtime_error(SS("Download failed. Invalid curl response: " << errorCode));
}
if (code == 200)
{
return;
}
if (code == 0)
{
throw std::runtime_error("PiPedal server can't reach the internet.");
}
{
std::string message = SS("Download failed (HTTP error " << code << ")");
throw std::runtime_error(message);
} }
return fs::file_size(filePath) != 0;
} }
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)
{ {
@@ -815,7 +899,7 @@ void Updater::DownloadUpdate(const std::string &url, std::filesystem::path *file
} }
// Only permit downloading of updates from the github releases for the pipedal project. // Only permit downloading of updates from the github releases for the pipedal project.
// I don't think this will get past GetUpdateFileName, but defense in depth. We will // I don't think this will get past GetUpdateFileName, but defense in depth. We will
// not download from anywhere we don't trust. // not download from anywhere we don't trust.
if (!WhitelistDownloadUrl(url)) if (!WhitelistDownloadUrl(url))
{ {
@@ -836,22 +920,30 @@ void Updater::DownloadUpdate(const std::string &url, std::filesystem::path *file
fs::remove(downloadFilePath); fs::remove(downloadFilePath);
fs::remove(downloadSignaturePath); fs::remove(downloadSignaturePath);
std::string args = SS("-s -L " << url << " -o " << downloadFilePath << " 2>&1"); const std::string responseOption = "-w \"%{response_code}\"";
auto curlOutput = sysExecForOutput("curl", args); #ifdef WIN32
responseOption = "-w \"%%{response_code}\""; // windows shell requires doubling of the %%.
#endif
std::string args = SS("-s -L " << responseOption << " " << url << " -o " << downloadFilePath.c_str());
Lv2Log::info(SS("/usr/bin/curl " << args)); // yyy delete me.
auto curlOutput = sysExecForOutput("/usr/bin/curl", args);
if (curlOutput.exitCode != EXIT_SUCCESS || badOutput(downloadFilePath)) if (curlOutput.exitCode != EXIT_SUCCESS || badOutput(downloadFilePath))
{ {
Lv2Log::error(SS("Update download failed." << unCRLF(curlOutput.output))); Lv2Log::error(SS("Update download failed. " << unCRLF(curlOutput.output)));
throw std::runtime_error("PiPedal server does not have access to the internet."); throw std::runtime_error("PiPedal server does not have access to the internet.");
} }
checkCurlHttpResponse(curlOutput.output);
args = SS("-s -L " << signatureUrl << " -o " << downloadSignaturePath << " 2>&1"); args = SS("-s -L " << responseOption << " " << signatureUrl << " -o " << downloadSignaturePath.c_str());
Lv2Log::info(SS("/usr/bin/curl " << args));
curlOutput = sysExecForOutput("curl", args); curlOutput = sysExecForOutput("/usr/bin/curl", args);
if (curlOutput.exitCode != EXIT_SUCCESS || badOutput(downloadSignaturePath)) if (curlOutput.exitCode != EXIT_SUCCESS || badOutput(downloadSignaturePath))
{ {
Lv2Log::error(SS("Update download failed." << unCRLF(curlOutput.output))); Lv2Log::error(SS("Update download failed. " << unCRLF(curlOutput.output)));
throw std::runtime_error("PiPedal server does not have access to the internet."); throw std::runtime_error("PiPedal server does not have access to the internet.");
} }
checkCurlHttpResponse(curlOutput.output);
try try
{ {
@@ -867,10 +959,10 @@ void Updater::DownloadUpdate(const std::string &url, std::filesystem::path *file
// Can't only be performed under pipedal_d or root accouts. // Can't only be performed under pipedal_d or root accouts.
// This is as far as you can go while debugging. // This is as far as you can go while debugging.
// The admin process will actually check the signature again running as root; but this is our last // The admin process will actually check the signature again running as root; but this is our last
// chance to give a reasonable error message, so do it here as well, because any subsequent errors // chance to give a reasonable error message, so do it here as well, because any subsequent errors
// can only go to systemd logs. // can only go to systemd logs.
ValidateSignature(downloadFilePath,downloadSignaturePath); ValidateSignature(downloadFilePath, downloadSignaturePath);
*file = downloadFilePath; *file = downloadFilePath;
*signatureFile = downloadSignaturePath; *signatureFile = downloadSignaturePath;
return; return;
+3
View File
@@ -42,6 +42,8 @@ namespace pipedal
ReleaseOnly = 0, ReleaseOnly = 0,
ReleaseOrBeta = 1, ReleaseOrBeta = 1,
Development = 2, Development = 2,
Disabled = 3
}; };
class UpdateRelease class UpdateRelease
{ {
@@ -123,6 +125,7 @@ namespace pipedal
void SetUpdatePolicy(UpdatePolicyT updatePolicy); void SetUpdatePolicy(UpdatePolicyT updatePolicy);
void ForceUpdateCheck(); void ForceUpdateCheck();
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; }
private: private:
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);