web ui Update feature complete.
This commit is contained in:
+19
-6
@@ -41,7 +41,7 @@ AdminClient::~AdminClient()
|
||||
{
|
||||
}
|
||||
|
||||
bool AdminClient::CanUseShutdownClient()
|
||||
bool AdminClient::CanUseAdminClient()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@@ -104,7 +104,7 @@ bool AdminClient::SetJackServerConfiguration(const JackServerSettings &jackServe
|
||||
|
||||
void AdminClient::SetWifiConfig(const WifiConfigSettings &settings)
|
||||
{
|
||||
if (!CanUseShutdownClient())
|
||||
if (!CanUseAdminClient())
|
||||
{
|
||||
throw PiPedalException("Can't perform this operation when debugging.");
|
||||
}
|
||||
@@ -121,7 +121,7 @@ void AdminClient::SetWifiConfig(const WifiConfigSettings &settings)
|
||||
}
|
||||
void AdminClient::SetWifiDirectConfig(const WifiDirectConfigSettings &settings)
|
||||
{
|
||||
if (!CanUseShutdownClient())
|
||||
if (!CanUseAdminClient())
|
||||
{
|
||||
throw PiPedalException("Can't perform this operation when debugging.");
|
||||
}
|
||||
@@ -139,7 +139,7 @@ void AdminClient::SetWifiDirectConfig(const WifiDirectConfigSettings &settings)
|
||||
|
||||
void AdminClient::SetGovernorSettings(const std::string &settings)
|
||||
{
|
||||
if (!CanUseShutdownClient())
|
||||
if (!CanUseAdminClient())
|
||||
{
|
||||
throw PiPedalException("Can't use AdminClient when running interactively.");
|
||||
}
|
||||
@@ -157,7 +157,7 @@ void AdminClient::SetGovernorSettings(const std::string &settings)
|
||||
|
||||
void AdminClient::MonitorGovernor(const std::string &governor)
|
||||
{
|
||||
if (!CanUseShutdownClient())
|
||||
if (!CanUseAdminClient())
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -174,7 +174,7 @@ void AdminClient::MonitorGovernor(const std::string &governor)
|
||||
}
|
||||
void AdminClient::UnmonitorGovernor()
|
||||
{
|
||||
if (!CanUseShutdownClient())
|
||||
if (!CanUseAdminClient())
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -183,3 +183,16 @@ void AdminClient::UnmonitorGovernor()
|
||||
cmd << '\n';
|
||||
bool ignored = WriteMessage(cmd.str().c_str());
|
||||
}
|
||||
|
||||
|
||||
void AdminClient::InstallUpdate(const std::string&filename)
|
||||
{
|
||||
if (!CanUseAdminClient())
|
||||
{
|
||||
// generally can't do amin operations when running under a debugger.
|
||||
throw std::runtime_error("No admin client.");
|
||||
}
|
||||
std::stringstream cmd;
|
||||
cmd << "InstallUpdate " << filename << '\n';
|
||||
bool ignroed = WriteMessage(cmd.str().c_str());
|
||||
}
|
||||
|
||||
+2
-1
@@ -34,7 +34,7 @@ class AdminClient {
|
||||
public:
|
||||
AdminClient();
|
||||
~AdminClient();
|
||||
bool CanUseShutdownClient();
|
||||
bool CanUseAdminClient();
|
||||
bool RequestShutdown(bool restart);
|
||||
bool SetJackServerConfiguration(const JackServerSettings & jackServerSettings);
|
||||
void SetWifiConfig(const WifiConfigSettings & settings);
|
||||
@@ -42,6 +42,7 @@ public:
|
||||
void SetGovernorSettings(const std::string & governor);
|
||||
void MonitorGovernor(const std::string &governor);
|
||||
void UnmonitorGovernor();
|
||||
void InstallUpdate(const std::string&filename);
|
||||
private:
|
||||
std::mutex mutex;
|
||||
UnixSocket socket;
|
||||
|
||||
@@ -0,0 +1,157 @@
|
||||
// Copyright (c) 2024Robin Davies
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
// this software and associated documentation files (the "Software"), to deal in
|
||||
// the Software without restriction, including without limitation the rights to
|
||||
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
// the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
// subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
#include "AdminInstallUpdate.hpp"
|
||||
#include <fstream>
|
||||
#include <stdexcept>
|
||||
#include "ss.hpp"
|
||||
#include <vector>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include "Lv2Log.hpp"
|
||||
#include <string.h>
|
||||
#include <sys/prctl.h>
|
||||
#include <sys/wait.h>
|
||||
#include <signal.h>
|
||||
|
||||
using namespace pipedal;
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
static fs::path ROOT_INSTALL_DIRECTORY = "/var/pipedal/updates";
|
||||
static fs::path INSTALLER_LOG_FILE_PATH = ROOT_INSTALL_DIRECTORY / "install.log";
|
||||
|
||||
static fs::path UPDATE_DIRECTORY = ROOT_INSTALL_DIRECTORY / "downloads";
|
||||
|
||||
static void removeSignalHandler(int signal)
|
||||
{
|
||||
struct sigaction sa;
|
||||
sa.sa_handler = SIG_DFL;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sa.sa_flags = 0;
|
||||
sigaction(signal, &sa, NULL);
|
||||
}
|
||||
|
||||
static int exec(const std::string &command)
|
||||
{
|
||||
std::string buffer = command;
|
||||
|
||||
|
||||
std::vector<char *> argv;
|
||||
char *p = const_cast<char *>(buffer.c_str());
|
||||
while (*p)
|
||||
{
|
||||
char *start = p;
|
||||
while (*p != ' ' && *p != '\0')
|
||||
{
|
||||
++p;
|
||||
}
|
||||
argv.push_back(start);
|
||||
while (*p == ' ')
|
||||
{
|
||||
*p++ = '\0';
|
||||
}
|
||||
}
|
||||
argv.push_back(nullptr);
|
||||
|
||||
pid_t pid;
|
||||
|
||||
if ((pid = fork()) == 0)
|
||||
{
|
||||
execv(argv[0], (char *const *)argv.data());
|
||||
write(1,"!\n",2);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
if (pid == -1)
|
||||
{
|
||||
write(1,"*",1);
|
||||
perror("execv");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
int returnValue;
|
||||
waitpid(pid, &returnValue, 0);
|
||||
|
||||
|
||||
int exitStatus = WEXITSTATUS(returnValue);
|
||||
return exitStatus;
|
||||
}
|
||||
}
|
||||
|
||||
void updateLog(const std::string &message)
|
||||
{
|
||||
write(1,message.c_str(),message.length());
|
||||
write(1,"\n",1);
|
||||
}
|
||||
void pipedal::AdminInstallUpdate(const std::filesystem::path path)
|
||||
{
|
||||
|
||||
try
|
||||
{
|
||||
fs:create_directories(ROOT_INSTALL_DIRECTORY);
|
||||
Lv2Log::info(SS("Installing " << path));
|
||||
|
||||
if (!path.string().starts_with(UPDATE_DIRECTORY.string()))
|
||||
{
|
||||
throw std::runtime_error("Update file path is incorrect.");
|
||||
}
|
||||
if (!fs::exists(path))
|
||||
{
|
||||
throw std::runtime_error("File does not exist.");
|
||||
}
|
||||
|
||||
std::stringstream ssCmd;
|
||||
ssCmd << "/usr/bin/apt-get -q -y install " << path.string();
|
||||
|
||||
std::string cmd = ssCmd.str();
|
||||
|
||||
// direct standard outputs to a log file.
|
||||
close(0);
|
||||
close(1);
|
||||
close(2);
|
||||
|
||||
int fd = open(INSTALLER_LOG_FILE_PATH.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0644);
|
||||
dup2(fd, 1); // to stdout
|
||||
dup2(fd, 2); // to stderr
|
||||
close(fd);
|
||||
fd = -1;
|
||||
|
||||
// disconnect std input from parent's stdin.
|
||||
int fdNull = open("/dev/null", O_RDONLY, 0644);
|
||||
dup2(fdNull, 0);
|
||||
close(fdNull);
|
||||
|
||||
updateLog("**Stopping pipedald");
|
||||
|
||||
exec("/usr/bin/systemctl stop pipedald");
|
||||
|
||||
updateLog("** Installing update");
|
||||
int retcode = exec(cmd);
|
||||
|
||||
|
||||
// in case we didn't actually update for some reason.
|
||||
updateLog("** Starting pipedald");
|
||||
exec("/usr/bin/systemctl start pipedald");
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
updateLog(e.what());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
// Copyright (c) 2024Robin Davies
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
// this software and associated documentation files (the "Software"), to deal in
|
||||
// the Software without restriction, including without limitation the rights to
|
||||
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
// the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
// subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <filesystem>
|
||||
|
||||
namespace pipedal
|
||||
{
|
||||
void AdminInstallUpdate(const std::filesystem::path path);
|
||||
|
||||
}
|
||||
+11
-1
@@ -220,6 +220,12 @@ bool setJackConfiguration(JackServerSettings serverSettings)
|
||||
return true;
|
||||
}
|
||||
|
||||
void InstallUpdate(const std::filesystem::path path)
|
||||
{
|
||||
std::string cmd = SS("/usr/bin/systemd-run --unit=pidal_update /usr/sbin/pipedal_update " <<path.string());
|
||||
int rc = system(cmd.c_str());
|
||||
}
|
||||
|
||||
class AdminServer
|
||||
{
|
||||
private:
|
||||
@@ -259,7 +265,11 @@ private:
|
||||
}
|
||||
try
|
||||
{
|
||||
if (command == "UnmonitorGovernor")
|
||||
if (command == "InstallUpdate")
|
||||
{
|
||||
std::filesystem::path path = args;
|
||||
InstallUpdate(path);
|
||||
} else if (command == "UnmonitorGovernor")
|
||||
{
|
||||
StopGovernorMonitorThread();
|
||||
result = 0;
|
||||
|
||||
+10
-2
@@ -611,12 +611,20 @@ target_include_directories(capturepresets PRIVATE ${PIPEDAL_INCLUDES})
|
||||
target_link_libraries(capturepresets ${PIPEDAL_LIBS})
|
||||
|
||||
|
||||
add_executable(pipedal_update
|
||||
UpdateMain.cpp
|
||||
Lv2Log.hpp Lv2Log.cpp
|
||||
Lv2SystemdLogger.cpp Lv2SystemdLogger.hpp
|
||||
AdminInstallUpdate.cpp AdminInstallUpdate.hpp
|
||||
)
|
||||
|
||||
target_link_libraries(pipedal_update PRIVATE stdc++fs systemd )
|
||||
|
||||
|
||||
add_executable(pipedaladmind AdminMain.cpp CommandLineParser.hpp
|
||||
UnixSocket.cpp UnixSocket.hpp
|
||||
|
||||
SetWifiConfig.cpp SetWifiConfig.hpp
|
||||
|
||||
JackServerSettings.hpp JackServerSettings.cpp
|
||||
Lv2SystemdLogger.hpp Lv2SystemdLogger.cpp
|
||||
|
||||
@@ -671,7 +679,7 @@ add_custom_target (
|
||||
install (TARGETS pipedalconfig pipedal_latency_test DESTINATION ${CMAKE_INSTALL_PREFIX}/bin
|
||||
EXPORT pipedalTargets)
|
||||
|
||||
install (TARGETS pipedald pipedaladmind DESTINATION ${CMAKE_INSTALL_PREFIX}/sbin
|
||||
install (TARGETS pipedald pipedaladmind pipedal_update DESTINATION ${CMAKE_INSTALL_PREFIX}/sbin
|
||||
EXPORT pipedalSbinTargets)
|
||||
|
||||
|
||||
|
||||
+7
-5
@@ -112,11 +112,11 @@ std::filesystem::path findOnPath(const std::string &command)
|
||||
|
||||
void EnableService()
|
||||
{
|
||||
if (sysExec(SYSTEMCTL_BIN " enable " PIPEDALD_SERVICE ".service") != EXIT_SUCCESS)
|
||||
if (silentSysExec(SYSTEMCTL_BIN " enable " PIPEDALD_SERVICE ".service") != EXIT_SUCCESS)
|
||||
{
|
||||
cout << "Error: Failed to enable the " PIPEDALD_SERVICE " service.\n";
|
||||
}
|
||||
if (sysExec(SYSTEMCTL_BIN " enable " ADMIN_SERVICE ".service") != EXIT_SUCCESS)
|
||||
if (silentSysExec(SYSTEMCTL_BIN " enable " ADMIN_SERVICE ".service") != EXIT_SUCCESS)
|
||||
{
|
||||
cout << "Error: Failed to enable the " ADMIN_SERVICE " service.\n";
|
||||
}
|
||||
@@ -178,8 +178,10 @@ void StopService(bool excludeShutdownService = false)
|
||||
|
||||
void StartService(bool excludeShutdownService = false)
|
||||
{
|
||||
|
||||
silentSysExec("/usr/bin/pulseaudio --kill"); // interferes with Jack audio service startup.
|
||||
if (!UsingNetworkManager())
|
||||
{
|
||||
silentSysExec("/usr/bin/pulseaudio --kill"); // interferes with Jack audio service startup.
|
||||
}
|
||||
if (!excludeShutdownService)
|
||||
{
|
||||
if (sysExec(SYSTEMCTL_BIN " start " ADMIN_SERVICE ".service") != EXIT_SUCCESS)
|
||||
@@ -187,7 +189,7 @@ void StartService(bool excludeShutdownService = false)
|
||||
throw std::runtime_error("Failed to start the " ADMIN_SERVICE " service.");
|
||||
}
|
||||
}
|
||||
if (sysExec(SYSTEMCTL_BIN " start " PIPEDALD_SERVICE ".service") != EXIT_SUCCESS)
|
||||
if (silentSysExec(SYSTEMCTL_BIN " start " PIPEDALD_SERVICE ".service") != EXIT_SUCCESS)
|
||||
{
|
||||
throw std::runtime_error("Failed to start the " PIPEDALD_SERVICE " service.");
|
||||
}
|
||||
|
||||
@@ -42,8 +42,11 @@ void Lv2PluginChangeMonitor::Shutdown()
|
||||
if (monitorThread)
|
||||
{
|
||||
terminateThread = true;
|
||||
uint64_t val = 1;
|
||||
write(shutdown_eventfd,(void*)&val, sizeof(val));
|
||||
monitorThread->join();
|
||||
monitorThread = nullptr;
|
||||
close(shutdown_eventfd);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2212,8 +2212,10 @@ UpdateStatus PiPedalModel::GetUpdateStatus() {
|
||||
|
||||
void PiPedalModel::UpdateNow(const std::string&updateUrl)
|
||||
{
|
||||
sleep(5);
|
||||
throw std::runtime_error("Not implemented yet.");
|
||||
std::lock_guard<std::recursive_mutex> lock(mutex);
|
||||
auto fileName = updater.DownloadUpdate(updateUrl);
|
||||
|
||||
adminClient.InstallUpdate(fileName);
|
||||
|
||||
}
|
||||
void PiPedalModel::ForceUpdateCheck() {
|
||||
|
||||
@@ -1067,7 +1067,7 @@ public:
|
||||
{
|
||||
WifiConfigSettings wifiConfigSettings;
|
||||
pReader->read(&wifiConfigSettings);
|
||||
if (!GetAdminClient().CanUseShutdownClient())
|
||||
if (!GetAdminClient().CanUseAdminClient())
|
||||
{
|
||||
throw PiPedalException("Can't change server settings when running interactively.");
|
||||
}
|
||||
@@ -1088,7 +1088,7 @@ public:
|
||||
{
|
||||
WifiDirectConfigSettings wifiDirectConfigSettings;
|
||||
pReader->read(&wifiDirectConfigSettings);
|
||||
if (!GetAdminClient().CanUseShutdownClient())
|
||||
if (!GetAdminClient().CanUseAdminClient())
|
||||
{
|
||||
throw PiPedalException("Can't change server settings when running interactively.");
|
||||
}
|
||||
@@ -1978,7 +1978,7 @@ std::shared_ptr<ISocketFactory> pipedal::MakePiPedalSocketFactory(PiPedalModel &
|
||||
|
||||
void PiPedalSocketHandler::RequestShutdown(bool restart)
|
||||
{
|
||||
if (GetAdminClient().CanUseShutdownClient())
|
||||
if (GetAdminClient().CanUseAdminClient())
|
||||
{
|
||||
GetAdminClient().RequestShutdown(restart);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
// Copyright (c) 2024Robin Davies
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
// this software and associated documentation files (the "Software"), to deal in
|
||||
// the Software without restriction, including without limitation the rights to
|
||||
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
// the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
// subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
#include "AdminInstallUpdate.hpp"
|
||||
#include "Lv2Log.hpp"
|
||||
#include "Lv2SystemdLogger.hpp"
|
||||
#include <filesystem>
|
||||
|
||||
using namespace pipedal;
|
||||
|
||||
int main(int argc, char**argv)
|
||||
{
|
||||
Lv2Log::set_logger(MakeLv2SystemdLogger());
|
||||
|
||||
if (argc != 2)
|
||||
{
|
||||
Lv2Log::error("Invalid arguments.");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
std::filesystem::path path = argv[1];
|
||||
AdminInstallUpdate(path);
|
||||
}
|
||||
+132
@@ -67,6 +67,9 @@ static UpdateStatus GetCachedUpdateStatus()
|
||||
json_reader reader(f);
|
||||
UpdateStatus status;
|
||||
reader.read(&status);
|
||||
|
||||
// cached curruent version might come from a different version.
|
||||
status.ResetCurrentVersion();
|
||||
return status;
|
||||
}
|
||||
}
|
||||
@@ -552,6 +555,19 @@ UpdateStatus::UpdateStatus()
|
||||
#endif
|
||||
}
|
||||
|
||||
void UpdateStatus::ResetCurrentVersion()
|
||||
{
|
||||
currentVersion_ = PROJECT_VER;
|
||||
currentVersionDisplayName_ = PROJECT_DISPLAY_VERSION;
|
||||
|
||||
#ifdef TEST_UPDATE
|
||||
// uncomment this line to test upgrading.
|
||||
currentVersion_ = "1.2.39";
|
||||
currentVersionDisplayName_ = "PiPedal 1.2.39-Debug";
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
std::chrono::system_clock::time_point UpdateStatus::LastUpdateTime() const
|
||||
{
|
||||
std::chrono::system_clock::duration duration{this->lastUpdateTime_};
|
||||
@@ -583,10 +599,126 @@ const GithubAsset *GithubRelease::GetDownloadForCurrentArchitecture() const
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
UpdateRelease::UpdateRelease()
|
||||
{
|
||||
}
|
||||
|
||||
std::string Updater::GetUpdateFilename(const std::string &url)
|
||||
{
|
||||
std::lock_guard lock(mutex);
|
||||
|
||||
// partialy whitelisting, partly avoiding having to parse a URL.
|
||||
if (this->currentResult.releaseOnlyRelease_.UpdateUrl() == url)
|
||||
{
|
||||
return this->currentResult.releaseOnlyRelease_.AssetName();
|
||||
}
|
||||
if (this->currentResult.releaseOrBetaRelease_.UpdateUrl() == url)
|
||||
{
|
||||
return this->currentResult.releaseOrBetaRelease_.AssetName();
|
||||
}
|
||||
if (this->currentResult.devRelease_.UpdateUrl() == url)
|
||||
{
|
||||
return this->currentResult.devRelease_.AssetName();
|
||||
}
|
||||
throw std::runtime_error("Permission denied. Invalid url.");
|
||||
|
||||
}
|
||||
static std::string unCRLF(const std::string &text)
|
||||
{
|
||||
std::ostringstream ss;
|
||||
for (char c : text)
|
||||
{
|
||||
if (c == '\r')
|
||||
continue;
|
||||
if (c == '\n') {
|
||||
ss << '/';
|
||||
} else
|
||||
{
|
||||
ss << c;
|
||||
}
|
||||
}
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
static void removeOldSiblings(int numberToKeep, const std::filesystem::path &fileToKeep)
|
||||
{
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
auto directory = fileToKeep.parent_path();
|
||||
if (directory.empty()) return; // superstition.
|
||||
struct RemoveEntry {
|
||||
fs::path path;
|
||||
fs::file_time_type time;
|
||||
};
|
||||
std::vector<RemoveEntry> entries;
|
||||
for (const auto&dirEntry : fs::directory_iterator(directory))
|
||||
{
|
||||
if (dirEntry.is_regular_file())
|
||||
{
|
||||
if (dirEntry.path() != fileToKeep)
|
||||
{
|
||||
dirEntry.last_write_time();
|
||||
entries.push_back(RemoveEntry { .path = dirEntry.path(), .time = dirEntry.last_write_time()});
|
||||
}
|
||||
}
|
||||
}
|
||||
std::sort(
|
||||
entries.begin(),entries.end(),
|
||||
[](const RemoveEntry&left, const RemoveEntry&right)
|
||||
{
|
||||
return left.time > right.time; // by time descending
|
||||
}
|
||||
);
|
||||
for (size_t i = numberToKeep; i < entries.size(); ++i)
|
||||
{
|
||||
fs::remove(entries[i].path);
|
||||
}
|
||||
}
|
||||
std::filesystem::path Updater::DownloadUpdate(const std::string &url)
|
||||
{
|
||||
namespace fs = std::filesystem;
|
||||
std::string filename = GetUpdateFilename(url);
|
||||
if (filename.empty())
|
||||
{
|
||||
throw std::runtime_error("Permission denied. Invalid url.");
|
||||
}
|
||||
auto downloadDirectory = WORKING_DIRECTORY / "downloads";
|
||||
std::filesystem::create_directories(downloadDirectory);
|
||||
|
||||
auto downloadPath = downloadDirectory / filename;
|
||||
|
||||
try {
|
||||
fs::remove(downloadPath);
|
||||
std::string args = SS("-s -L " << url << " -o " << downloadPath << " 2>&1");
|
||||
auto curlOutput = sysExecForOutput("curl", args);
|
||||
if (curlOutput.exitCode != EXIT_SUCCESS)
|
||||
{
|
||||
Lv2Log::error(SS("Update download failed." << unCRLF(curlOutput.output)));
|
||||
throw std::runtime_error("PiPedal server does not have access to the internet.");
|
||||
}
|
||||
if (!fs::exists(downloadPath) || fs::file_size(downloadPath) == 0)
|
||||
{
|
||||
throw std::runtime_error("Download failed.");
|
||||
}
|
||||
try {
|
||||
removeOldSiblings(2, downloadPath);
|
||||
} catch (const std::exception&e)
|
||||
{
|
||||
Lv2Log::error(SS("Can't remove download siblings" << e.what()));
|
||||
// and carry on.
|
||||
}
|
||||
return downloadPath;
|
||||
} catch (const std::exception &e)
|
||||
{
|
||||
std::filesystem::remove(downloadPath);
|
||||
throw;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
JSON_MAP_BEGIN(UpdateRelease)
|
||||
JSON_MAP_REFERENCE(UpdateRelease, updateAvailable)
|
||||
JSON_MAP_REFERENCE(UpdateRelease, upgradeVersion)
|
||||
|
||||
@@ -86,6 +86,7 @@ namespace pipedal
|
||||
std::chrono::system_clock::time_point LastUpdateTime() const;
|
||||
void LastUpdateTime(const std::chrono::system_clock::time_point &timePoint);
|
||||
|
||||
void ResetCurrentVersion();
|
||||
bool IsValid() const { return isValid_; }
|
||||
const std::string &ErrorMessage() const { return errorMessage_; }
|
||||
bool IsOnline() const { return isOnline_; }
|
||||
@@ -116,7 +117,10 @@ namespace pipedal
|
||||
UpdatePolicyT GetUpdatePolicy();
|
||||
void SetUpdatePolicy(UpdatePolicyT updatePolicy);
|
||||
void ForceUpdateCheck();
|
||||
std::filesystem::path DownloadUpdate(const std::string &url);
|
||||
private:
|
||||
std::string GetUpdateFilename(const std::string &url);
|
||||
|
||||
UpdatePolicyT updatePolicy = UpdatePolicyT::ReleaseOrBeta;
|
||||
using UpdateReleasePredicate = std::function<bool(const GithubRelease &githubRelease)>;
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ Restart=always
|
||||
RestartSec=60
|
||||
WorkingDirectory=/var/pipedal
|
||||
TimeoutStopSec=15
|
||||
KillMode=process
|
||||
|
||||
|
||||
[Install]
|
||||
|
||||
@@ -18,7 +18,6 @@ Restart=always
|
||||
TimeoutStartSec=60
|
||||
RestartSec=5
|
||||
TimeoutStopSec=15
|
||||
|
||||
WorkingDirectory=/var/pipedal
|
||||
Environment=JACK_PROMISCUOUS_SERVER=audio
|
||||
Environment=JACK_NO_AUDIO_RESERVATION=1
|
||||
|
||||
Reference in New Issue
Block a user