Update mDNS service announcements when name changes.
This commit is contained in:
Vendored
+26
@@ -66,6 +66,32 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "(gdb) makeRelease",
|
||||||
|
"type": "cppdbg",
|
||||||
|
"request": "launch",
|
||||||
|
// Resolved by CMake Tools:
|
||||||
|
"program": "${command:cmake.launchTargetPath}",
|
||||||
|
"args": [ ],
|
||||||
|
"stopAtEntry": false,
|
||||||
|
"cwd": "${workspaceFolder}/../..",
|
||||||
|
"environment": [
|
||||||
|
{
|
||||||
|
"name": "PATH",
|
||||||
|
"value": "$PATH:${command:cmake.launchTargetDirectory}"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"externalConsole": true,
|
||||||
|
"MIMode": "gdb",
|
||||||
|
"setupCommands": [
|
||||||
|
{
|
||||||
|
"description": "Enable pretty-printing for gdb",
|
||||||
|
"text": "-enable-pretty-printing",
|
||||||
|
"ignoreFailures": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
"name": "(gdb) pipedal_nm_p2pd",
|
"name": "(gdb) pipedal_nm_p2pd",
|
||||||
"type": "cppdbg",
|
"type": "cppdbg",
|
||||||
|
|||||||
+2
-3
@@ -4,7 +4,7 @@ project(pipedal
|
|||||||
DESCRIPTION "PiPedal Guitar Effect Pedal For Raspberry Pi"
|
DESCRIPTION "PiPedal Guitar Effect Pedal For Raspberry Pi"
|
||||||
HOMEPAGE_URL "https://rerdavies.github.io/pipedal"
|
HOMEPAGE_URL "https://rerdavies.github.io/pipedal"
|
||||||
)
|
)
|
||||||
set (DISPLAY_VERSION "PiPedal v1.2.49-Release")
|
set (DISPLAY_VERSION "PiPedal v1.2.49-Development")
|
||||||
set (PACKAGE_ARCHITECTURE "arm64")
|
set (PACKAGE_ARCHITECTURE "arm64")
|
||||||
set (CMAKE_INSTALL_PREFIX "/usr/")
|
set (CMAKE_INSTALL_PREFIX "/usr/")
|
||||||
|
|
||||||
@@ -22,7 +22,6 @@ add_subdirectory("src")
|
|||||||
# Replaced with hotspot.
|
# Replaced with hotspot.
|
||||||
#add_subdirectory("NetworkManagerP2P")
|
#add_subdirectory("NetworkManagerP2P")
|
||||||
|
|
||||||
|
|
||||||
# install (TARGETS pipedal_p2pd DESTINATION ${CMAKE_INSTALL_PREFIX}/sbin
|
# install (TARGETS pipedal_p2pd DESTINATION ${CMAKE_INSTALL_PREFIX}/sbin
|
||||||
# )
|
# )
|
||||||
|
|
||||||
@@ -56,7 +55,7 @@ install(
|
|||||||
#)
|
#)
|
||||||
|
|
||||||
install (
|
install (
|
||||||
FILES ${PROJECT_SOURCE_DIR}/src/polkit-1/rules/55-pipedal-networkmanager.rule DESTINATION /etc/polkit-1/rules.d/
|
FILES ${PROJECT_SOURCE_DIR}/src/polkit-1/rules/10-pipedal-networkmanager.rules DESTINATION /etc/polkit-1/rules.d/
|
||||||
)
|
)
|
||||||
install (
|
install (
|
||||||
DIRECTORY ${REACT_BUILD_DIRECTORY} DESTINATION /etc/pipedal/react
|
DIRECTORY ${REACT_BUILD_DIRECTORY} DESTINATION /etc/pipedal/react
|
||||||
|
|||||||
@@ -4,5 +4,6 @@
|
|||||||
"debug": true,
|
"debug": true,
|
||||||
"max_upload_size": 536870912,
|
"max_upload_size": 536870912,
|
||||||
"fakeAndroid": false,
|
"fakeAndroid": false,
|
||||||
"ui_plugins": []
|
"ui_plugins": [],
|
||||||
|
"enable_auto_update": true
|
||||||
}
|
}
|
||||||
+13
-9
@@ -1,10 +1,14 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
# Sign the built .deb package with the private key used in the Auto-update process.
|
||||||
#sign a package with rerdavies@gmail.com's private key.
|
#
|
||||||
cd build
|
# Won't work for you, because you don't have MY private key.
|
||||||
for filename in *.deb; do
|
#
|
||||||
echo gpg --armor --output "$filename".asc -b "$filename"
|
# Auto-update requires that .deb files be distributed from the GitHub rerdavies/pipedal project.
|
||||||
|
# And auto-updates WILL update installs of your forked binary unless you take steps to prevent that.
|
||||||
gpg --armor --output "$filename".asc -b "$filename"
|
#
|
||||||
done
|
# If you are distributing a fork, you should see the file UpdaterSecurity.hpp for details on how to
|
||||||
cd ..
|
# modify pipedal to perform updates from an alternate github site. Or you could modify your fork
|
||||||
|
# to NOT do auto-updating (probably best). The build doesn't currently have a procedure for
|
||||||
|
# knocking out Auto-Update from UpdaterSecurity.hpp. But I would happily accept a pull request
|
||||||
|
# to bring such a feature back into mainline if you do it.
|
||||||
|
build/src/makeRelease
|
||||||
|
|||||||
+30
-37
@@ -45,6 +45,12 @@ void AvahiService::Announce(
|
|||||||
const std::string &mdnsName,
|
const std::string &mdnsName,
|
||||||
bool addTestGroup)
|
bool addTestGroup)
|
||||||
{
|
{
|
||||||
|
// We should update an existing group if the name doesn't change.
|
||||||
|
// but in practice, the name is the only thing that's going to change without restarting the server.
|
||||||
|
if (this->name && strcmp(name.c_str(),this->name) == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
Unannounce();
|
Unannounce();
|
||||||
|
|
||||||
this->portNumber = portNumber;
|
this->portNumber = portNumber;
|
||||||
@@ -162,7 +168,7 @@ void AvahiService::create_group(AvahiClient *c)
|
|||||||
if ((ret = avahi_entry_group_add_service(
|
if ((ret = avahi_entry_group_add_service(
|
||||||
group,
|
group,
|
||||||
AVAHI_IF_UNSPEC,
|
AVAHI_IF_UNSPEC,
|
||||||
AVAHI_PROTO_UNSPEC, // IPv4 for now, until we figure out why dnsqmasq borks IPv6 routing.)
|
AVAHI_PROTO_INET, // IPv4 for now, until we figure out why dnsqmasq borks IPv6 routing.)
|
||||||
(AvahiPublishFlags)0,
|
(AvahiPublishFlags)0,
|
||||||
name,
|
name,
|
||||||
PIPEDAL_SERVICE_TYPE,
|
PIPEDAL_SERVICE_TYPE,
|
||||||
@@ -307,41 +313,32 @@ void AvahiService::Start()
|
|||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
/* Allocate main loop object */
|
/* Allocate main loop object */
|
||||||
if (!(this->threadedPoll = avahi_threaded_poll_new()))
|
if (!this->threadedPoll)
|
||||||
{
|
{
|
||||||
Lv2Log::error("Failed to create Avahi poll object.");
|
if (!(this->threadedPoll = avahi_threaded_poll_new()))
|
||||||
goto fail;
|
{
|
||||||
|
Lv2Log::error("Failed to create Avahi poll object.");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
avahi_threaded_poll_start(threadedPoll);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Allocate a new client */
|
|
||||||
client = avahi_client_new(avahi_threaded_poll_get(threadedPoll), AvahiClientFlags::AVAHI_CLIENT_NO_FAIL, client_callback, (void *)this, &error);
|
|
||||||
/* Check wether creating the client object succeeded */
|
|
||||||
if (!client)
|
if (!client)
|
||||||
{
|
{
|
||||||
Lv2Log::error(SS("Failed to create client: " << avahi_strerror(error)));
|
/* Allocate a new client */
|
||||||
goto fail;
|
client = avahi_client_new(avahi_threaded_poll_get(threadedPoll), AvahiClientFlags::AVAHI_CLIENT_NO_FAIL, client_callback, (void *)this, &error);
|
||||||
|
/* Check wether creating the client object succeeded */
|
||||||
|
if (!client)
|
||||||
|
{
|
||||||
|
Lv2Log::error(SS("Failed to create client: " << avahi_strerror(error)));
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
avahi_threaded_poll_start(threadedPoll);
|
|
||||||
ret = 0;
|
ret = 0;
|
||||||
return;
|
return;
|
||||||
fail:
|
fail:
|
||||||
/* Cleanup things */
|
Stop();
|
||||||
if (client)
|
|
||||||
{
|
|
||||||
avahi_client_free(client);
|
|
||||||
client = nullptr;
|
|
||||||
}
|
|
||||||
if (group)
|
|
||||||
{
|
|
||||||
avahi_entry_group_reset(group);
|
|
||||||
group = nullptr;
|
|
||||||
}
|
|
||||||
if (threadedPoll)
|
|
||||||
{
|
|
||||||
avahi_threaded_poll_free(threadedPoll);
|
|
||||||
threadedPoll = nullptr;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -349,27 +346,23 @@ void AvahiService::Start()
|
|||||||
void AvahiService::Stop()
|
void AvahiService::Stop()
|
||||||
{
|
{
|
||||||
|
|
||||||
if (threadedPoll)
|
/* Cleanup things */
|
||||||
|
if (group)
|
||||||
{
|
{
|
||||||
avahi_threaded_poll_stop(threadedPoll);
|
avahi_entry_group_reset(group);
|
||||||
|
avahi_entry_group_free(group);
|
||||||
|
group = nullptr;
|
||||||
}
|
}
|
||||||
/* Cleanup things */
|
|
||||||
// client owns the group?
|
|
||||||
// if (group)
|
|
||||||
// {
|
|
||||||
// avahi_entry_group_reset(group);
|
|
||||||
// group = nullptr;
|
|
||||||
// }`
|
|
||||||
|
|
||||||
if (client)
|
if (client)
|
||||||
{
|
{
|
||||||
avahi_client_free(client);
|
avahi_client_free(client);
|
||||||
client = nullptr;
|
client = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (threadedPoll)
|
if (threadedPoll)
|
||||||
{
|
{
|
||||||
|
avahi_threaded_poll_stop(threadedPoll);
|
||||||
avahi_threaded_poll_free(threadedPoll);
|
avahi_threaded_poll_free(threadedPoll);
|
||||||
threadedPoll = nullptr;
|
threadedPoll = nullptr;
|
||||||
}
|
}
|
||||||
group = nullptr;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,7 +43,6 @@ namespace pipedal {
|
|||||||
int portNumber, const std::string &name, const std::string &instanceId, const std::string&mdnsName,bool addTestGroup = false);
|
int portNumber, const std::string &name, const std::string &instanceId, const std::string&mdnsName,bool addTestGroup = false);
|
||||||
|
|
||||||
void Unannounce();
|
void Unannounce();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void Start();
|
void Start();
|
||||||
void Stop();
|
void Stop();
|
||||||
@@ -56,8 +55,6 @@ namespace pipedal {
|
|||||||
|
|
||||||
void create_group(AvahiClient *c);
|
void create_group(AvahiClient *c);
|
||||||
|
|
||||||
void threadProc();
|
|
||||||
|
|
||||||
int clientErrno = 0;
|
int clientErrno = 0;
|
||||||
|
|
||||||
int portNumber = -1;
|
int portNumber = -1;
|
||||||
|
|||||||
+32
-8
@@ -301,6 +301,9 @@ add_executable(hotspotManagerTest
|
|||||||
|
|
||||||
target_link_libraries(hotspotManagerTest PRIVATE ${PIPEDAL_LIBS})
|
target_link_libraries(hotspotManagerTest PRIVATE ${PIPEDAL_LIBS})
|
||||||
|
|
||||||
|
set_target_properties(hotspotManagerTest PROPERTIES EXCLUDE_FROM_ALL true)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
add_executable(pipedaltest testMain.cpp
|
add_executable(pipedaltest testMain.cpp
|
||||||
|
|
||||||
@@ -329,6 +332,8 @@ target_link_libraries(pipedaltest PRIVATE ${PIPEDAL_LIBS})
|
|||||||
target_include_directories(pipedaltest PRIVATE ${PIPEDAL_INCLUDES}
|
target_include_directories(pipedaltest PRIVATE ${PIPEDAL_INCLUDES}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
set_target_properties(pipedaltest PROPERTIES EXCLUDE_FROM_ALL true)
|
||||||
|
|
||||||
if (NOT GITHUB_ACTIONS) # Google perftools are not available on Github action servers.
|
if (NOT GITHUB_ACTIONS) # Google perftools are not available on Github action servers.
|
||||||
|
|
||||||
|
|
||||||
@@ -339,6 +344,9 @@ if (NOT GITHUB_ACTIONS) # Google perftools are not available on Github action se
|
|||||||
|
|
||||||
target_include_directories(profilePlugin PRIVATE ${PIPEDAL_INCLUDES}
|
target_include_directories(profilePlugin PRIVATE ${PIPEDAL_INCLUDES}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
set_target_properties(profilePlugin PROPERTIES EXCLUDE_FROM_ALL true)
|
||||||
|
|
||||||
endif()
|
endif()
|
||||||
add_executable(jsonTest
|
add_executable(jsonTest
|
||||||
testMain.cpp
|
testMain.cpp
|
||||||
@@ -356,7 +364,12 @@ target_link_libraries(jsonTest PRIVATE PiPedalCommon)
|
|||||||
target_include_directories(jsonTest PRIVATE ${PIPEDAL_INCLUDES}
|
target_include_directories(jsonTest PRIVATE ${PIPEDAL_INCLUDES}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
set_target_properties(jsonTest PROPERTIES EXCLUDE_FROM_ALL true)
|
||||||
|
|
||||||
|
|
||||||
target_link_libraries(jsonTest PRIVATE pthread)
|
target_link_libraries(jsonTest PRIVATE pthread)
|
||||||
|
|
||||||
|
|
||||||
add_test(NAME jsonTest COMMAND jsonTest)
|
add_test(NAME jsonTest COMMAND jsonTest)
|
||||||
|
|
||||||
|
|
||||||
@@ -561,18 +574,18 @@ if(ENABLE_VST3)
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
add_executable(vst3test Vst3test.cpp
|
# add_executable(vst3test Vst3test.cpp
|
||||||
|
|
||||||
MemDebug.cpp
|
# MemDebug.cpp
|
||||||
MemDebug.hpp
|
# MemDebug.hpp
|
||||||
|
|
||||||
Vst3SdkRepro.cpp
|
# Vst3SdkRepro.cpp
|
||||||
|
|
||||||
${VST3_FILES}
|
# ${VST3_FILES}
|
||||||
|
|
||||||
#vst3/Vst3PresetFile.hpp Vst3PresetFile.cpp
|
# #vst3/Vst3PresetFile.hpp Vst3PresetFile.cpp
|
||||||
asan_options.cpp
|
# asan_options.cpp
|
||||||
)
|
# )
|
||||||
|
|
||||||
target_include_directories(vst3test PRIVATE ${PIPEDAL_INCLUDES}
|
target_include_directories(vst3test PRIVATE ${PIPEDAL_INCLUDES}
|
||||||
)
|
)
|
||||||
@@ -642,6 +655,17 @@ target_include_directories(capturepresets PRIVATE ${PIPEDAL_INCLUDES})
|
|||||||
|
|
||||||
target_link_libraries(capturepresets ${PIPEDAL_LIBS})
|
target_link_libraries(capturepresets ${PIPEDAL_LIBS})
|
||||||
|
|
||||||
|
add_executable(makeRelease
|
||||||
|
makeReleaseMain.cpp
|
||||||
|
CommandLineParser.hpp
|
||||||
|
)
|
||||||
|
target_include_directories(makeRelease PRIVATE ${PIPEDAL_INCLUDES})
|
||||||
|
|
||||||
|
target_link_libraries(makeRelease ${PIPEDAL_LIBS})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
add_executable(pipedal_update
|
add_executable(pipedal_update
|
||||||
UpdateMain.cpp
|
UpdateMain.cpp
|
||||||
|
|||||||
+8
-30
@@ -66,7 +66,6 @@ namespace pipedal::impl
|
|||||||
{
|
{
|
||||||
Initial,
|
Initial,
|
||||||
WaitingForNetworkManager,
|
WaitingForNetworkManager,
|
||||||
Disabled,
|
|
||||||
Monitoring,
|
Monitoring,
|
||||||
HotspotConnecting,
|
HotspotConnecting,
|
||||||
HotspotConnected,
|
HotspotConnected,
|
||||||
@@ -82,7 +81,6 @@ namespace pipedal::impl
|
|||||||
void onDevicesChanged();
|
void onDevicesChanged();
|
||||||
void onDisconnect();
|
void onDisconnect();
|
||||||
void onReload();
|
void onReload();
|
||||||
void onDisableHotspot();
|
|
||||||
|
|
||||||
void DisableHotspot();
|
void DisableHotspot();
|
||||||
void EnableHotspot();
|
void EnableHotspot();
|
||||||
@@ -169,7 +167,7 @@ void HotspotManagerImpl::Open()
|
|||||||
|
|
||||||
void HotspotManagerImpl::onClose()
|
void HotspotManagerImpl::onClose()
|
||||||
{
|
{
|
||||||
onDisableHotspot();
|
StopHotspot();
|
||||||
ReleaseNetworkManager();
|
ReleaseNetworkManager();
|
||||||
|
|
||||||
Lv2Log::debug("HotspotManager: state=Closed");
|
Lv2Log::debug("HotspotManager: state=Closed");
|
||||||
@@ -201,14 +199,7 @@ void HotspotManagerImpl::onInitialize()
|
|||||||
{
|
{
|
||||||
wifiConfigSettings.Load();
|
wifiConfigSettings.Load();
|
||||||
|
|
||||||
if (wifiConfigSettings.valid_ && wifiConfigSettings.IsEnabled())
|
onStartMonitoring();
|
||||||
{
|
|
||||||
onStartMonitoring();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
onDisableHotspot();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch (const std::exception &e)
|
catch (const std::exception &e)
|
||||||
{
|
{
|
||||||
@@ -431,17 +422,6 @@ void HotspotManagerImpl::WaitForNetworkManager()
|
|||||||
ReleaseNetworkManager();
|
ReleaseNetworkManager();
|
||||||
StartWaitForNetworkManagerTimer();
|
StartWaitForNetworkManagerTimer();
|
||||||
}
|
}
|
||||||
void HotspotManagerImpl::onDisableHotspot()
|
|
||||||
{
|
|
||||||
if (this->state != State::Disabled)
|
|
||||||
{
|
|
||||||
ReleaseNetworkManager();
|
|
||||||
|
|
||||||
Lv2Log::debug("HotspotManager: state=Disabled");
|
|
||||||
SetState(State::Disabled);
|
|
||||||
Lv2Log::info("HotspotManager: Hotspot disabled.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void HotspotManagerImpl::onReload()
|
void HotspotManagerImpl::onReload()
|
||||||
{
|
{
|
||||||
if (closed)
|
if (closed)
|
||||||
@@ -453,17 +433,12 @@ void HotspotManagerImpl::onReload()
|
|||||||
switch (state)
|
switch (state)
|
||||||
{
|
{
|
||||||
case State::Initial:
|
case State::Initial:
|
||||||
|
case State::Error:
|
||||||
// ignore.
|
// ignore.
|
||||||
return;
|
return;
|
||||||
case State::Disabled:
|
default:
|
||||||
if (wifiConfigSettings.valid_ && wifiConfigSettings.IsEnabled())
|
MaybeStartHotspot();
|
||||||
{
|
|
||||||
onStartMonitoring();
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
default:
|
|
||||||
this->onDisableHotspot();
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -726,10 +701,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 (this->state == State::Error) return;
|
||||||
|
if (this->closed) return;
|
||||||
|
|
||||||
if (!wlanDevice || !wlanWirelessDevice)
|
if (!wlanDevice || !wlanWirelessDevice)
|
||||||
{
|
{
|
||||||
// devices are transitioning. Do nothing.
|
// devices are transitioning. Do nothing.
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
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);
|
||||||
@@ -787,6 +764,7 @@ Connection::ptr HotspotManagerImpl::FindExistingConnection()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -71,6 +71,7 @@ PiPedalModel::PiPedalModel()
|
|||||||
atomConverter(pluginHost.GetMapFeature())
|
atomConverter(pluginHost.GetMapFeature())
|
||||||
{
|
{
|
||||||
this->updater = Updater::Create();
|
this->updater = Updater::Create();
|
||||||
|
this->updater->Start();
|
||||||
this->currentUpdateStatus = updater->GetCurrentStatus();
|
this->currentUpdateStatus = updater->GetCurrentStatus();
|
||||||
this->pedalboard = Pedalboard::MakeDefault();
|
this->pedalboard = Pedalboard::MakeDefault();
|
||||||
#if JACK_HOST
|
#if JACK_HOST
|
||||||
@@ -980,6 +981,7 @@ void PiPedalModel::SetWifiConfigSettings(const WifiConfigSettings &wifiConfigSet
|
|||||||
{
|
{
|
||||||
this->hotspotManager->Reload();
|
this->hotspotManager->Reload();
|
||||||
}
|
}
|
||||||
|
this->UpdateDnsSd();
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
this->storage.SetWifiConfigSettings(wifiConfigSettings);
|
this->storage.SetWifiConfigSettings(wifiConfigSettings);
|
||||||
@@ -1020,7 +1022,7 @@ static std::string GetP2pdName()
|
|||||||
|
|
||||||
void PiPedalModel::UpdateDnsSd()
|
void PiPedalModel::UpdateDnsSd()
|
||||||
{
|
{
|
||||||
avahiService.Unannounce();
|
// avahiService.Unannounce(); let Announce decide whether it wants to unannounce or update.
|
||||||
|
|
||||||
ServiceConfiguration deviceIdFile;
|
ServiceConfiguration deviceIdFile;
|
||||||
deviceIdFile.Load();
|
deviceIdFile.Load();
|
||||||
|
|||||||
+202
-147
@@ -48,7 +48,8 @@ namespace fs = std::filesystem;
|
|||||||
#undef TEST_UPDATE // do NOT leat this leak into a production build!
|
#undef TEST_UPDATE // do NOT leat this leak into a production build!
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace pipedal {
|
namespace pipedal
|
||||||
|
{
|
||||||
class GithubResponseHeaders
|
class GithubResponseHeaders
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -62,8 +63,8 @@ namespace pipedal {
|
|||||||
uint64_t ratelimit_used_ = 0;
|
uint64_t ratelimit_used_ = 0;
|
||||||
std::string ratelimit_resource_;
|
std::string ratelimit_resource_;
|
||||||
bool limit_exceeded() const { return code_ != 200 && ratelimit_limit_ != 0 && ratelimit_limit_ == ratelimit_used_; }
|
bool limit_exceeded() const { return code_ != 200 && ratelimit_limit_ != 0 && ratelimit_limit_ == ratelimit_used_; }
|
||||||
void Load();
|
void Load(const std::filesystem::path &filename);
|
||||||
void Save();
|
void Save(const std::filesystem::path &filename);
|
||||||
DECLARE_JSON_MAP(GithubResponseHeaders);
|
DECLARE_JSON_MAP(GithubResponseHeaders);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -74,24 +75,37 @@ namespace pipedal {
|
|||||||
class pipedal::UpdaterImpl : public Updater
|
class pipedal::UpdaterImpl : public Updater
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
UpdaterImpl();
|
UpdaterImpl(const std::filesystem::path &workingDirectory);
|
||||||
|
UpdaterImpl() : UpdaterImpl("/var/pipedal/updates") {}
|
||||||
|
|
||||||
virtual ~UpdaterImpl() noexcept;
|
virtual ~UpdaterImpl() noexcept;
|
||||||
|
|
||||||
virtual void SetUpdateListener(UpdateListener &&listener) override;
|
virtual void SetUpdateListener(UpdateListener &&listener) override;
|
||||||
|
virtual void Start() override;
|
||||||
|
virtual void Stop() override;
|
||||||
|
|
||||||
virtual void CheckNow() override;
|
virtual void CheckNow() override;
|
||||||
virtual void Stop();
|
|
||||||
|
|
||||||
virtual UpdatePolicyT GetUpdatePolicy() override;
|
virtual UpdatePolicyT GetUpdatePolicy() override;
|
||||||
virtual void SetUpdatePolicy(UpdatePolicyT updatePolicy) override;
|
virtual void SetUpdatePolicy(UpdatePolicyT updatePolicy) override;
|
||||||
virtual void ForceUpdateCheck() override;
|
virtual void ForceUpdateCheck() override;
|
||||||
virtual void DownloadUpdate(const std::string &url, std::filesystem::path *file, std::filesystem::path *signatureFile) override;
|
virtual void DownloadUpdate(const std::string &url, std::filesystem::path *file, std::filesystem::path *signatureFile) override;
|
||||||
virtual UpdateStatus GetCurrentStatus() const override { return this->currentResult; }
|
virtual UpdateStatus GetCurrentStatus() const override { return this->currentResult; }
|
||||||
|
virtual UpdateStatus GetReleaseGeneratorStatus() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
std::filesystem::path workingDirectory;
|
||||||
|
std::filesystem::path updateStatusCacheFile;
|
||||||
|
std::filesystem::path githubResponseHeaderFilename;
|
||||||
|
|
||||||
GithubResponseHeaders githubResponseHeaders;
|
GithubResponseHeaders githubResponseHeaders;
|
||||||
|
|
||||||
using clock = std::chrono::steady_clock;
|
using clock = std::chrono::steady_clock;
|
||||||
|
|
||||||
|
void SetCachedUpdateStatus(UpdateStatus &updateStatus);
|
||||||
|
|
||||||
|
UpdateStatus GetCachedUpdateStatus();
|
||||||
|
|
||||||
void RetryAfter(clock::duration delay);
|
void RetryAfter(clock::duration delay);
|
||||||
|
|
||||||
template <typename REP, typename PERIOD>
|
template <typename REP, typename PERIOD>
|
||||||
@@ -120,6 +134,7 @@ private:
|
|||||||
int event_reader = -1;
|
int event_reader = -1;
|
||||||
int event_writer = -1;
|
int event_writer = -1;
|
||||||
void ThreadProc();
|
void ThreadProc();
|
||||||
|
UpdateStatus DoUpdate(bool forReleaseGenerator);
|
||||||
void CheckForUpdate(bool useCache);
|
void CheckForUpdate(bool useCache);
|
||||||
UpdateListener listener;
|
UpdateListener listener;
|
||||||
|
|
||||||
@@ -135,26 +150,27 @@ Updater::ptr Updater::Create()
|
|||||||
{
|
{
|
||||||
return std::make_unique<UpdaterImpl>();
|
return std::make_unique<UpdaterImpl>();
|
||||||
}
|
}
|
||||||
|
Updater::ptr Updater::Create(const std::filesystem::path &workingDirectory)
|
||||||
|
{
|
||||||
|
return std::make_unique<UpdaterImpl>(workingDirectory);
|
||||||
|
}
|
||||||
|
|
||||||
static constexpr uint64_t CLOSE_EVENT = 0;
|
static constexpr uint64_t CLOSE_EVENT = 0;
|
||||||
static constexpr uint64_t CHECK_NOW_EVENT = 1;
|
static constexpr uint64_t CHECK_NOW_EVENT = 1;
|
||||||
static constexpr uint64_t UNCACHED_CHECK_NOW_EVENT = 2;
|
static constexpr uint64_t UNCACHED_CHECK_NOW_EVENT = 2;
|
||||||
|
|
||||||
static std::filesystem::path WORKING_DIRECTORY = "/var/pipedal/updates";
|
|
||||||
static std::filesystem::path UPDATE_STATUS_CACHE_FILE = WORKING_DIRECTORY / "updateStatus.json";
|
|
||||||
|
|
||||||
UpdaterImpl::clock::duration UpdaterImpl::updateRate = std::chrono::duration_cast<UpdaterImpl::clock::duration>(std::chrono::days(1));
|
UpdaterImpl::clock::duration UpdaterImpl::updateRate = std::chrono::duration_cast<UpdaterImpl::clock::duration>(std::chrono::days(1));
|
||||||
static std::chrono::system_clock::duration CACHE_DURATION = std::chrono::duration_cast<std::chrono::system_clock::duration>(std::chrono::minutes(30));
|
static std::chrono::system_clock::duration CACHE_DURATION = std::chrono::duration_cast<std::chrono::system_clock::duration>(std::chrono::minutes(30));
|
||||||
|
|
||||||
std::mutex cacheMutex;
|
std::mutex cacheMutex;
|
||||||
static UpdateStatus GetCachedUpdateStatus()
|
UpdateStatus UpdaterImpl::GetCachedUpdateStatus()
|
||||||
{
|
{
|
||||||
std::lock_guard lock{cacheMutex};
|
std::lock_guard lock{cacheMutex};
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (std::filesystem::exists(UPDATE_STATUS_CACHE_FILE))
|
if (std::filesystem::exists(updateStatusCacheFile))
|
||||||
{
|
{
|
||||||
std::ifstream f{UPDATE_STATUS_CACHE_FILE};
|
std::ifstream f{updateStatusCacheFile};
|
||||||
if (f.is_open())
|
if (f.is_open())
|
||||||
{
|
{
|
||||||
json_reader reader(f);
|
json_reader reader(f);
|
||||||
@@ -174,13 +190,13 @@ static UpdateStatus GetCachedUpdateStatus()
|
|||||||
return UpdateStatus();
|
return UpdateStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void SetCachedUpdateStatus(UpdateStatus &updateStatus)
|
void UpdaterImpl::SetCachedUpdateStatus(UpdateStatus &updateStatus)
|
||||||
{
|
{
|
||||||
std::lock_guard lock{cacheMutex};
|
std::lock_guard lock{cacheMutex};
|
||||||
updateStatus.LastUpdateTime(std::chrono::system_clock::now());
|
updateStatus.LastUpdateTime(std::chrono::system_clock::now());
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
pipedal::ofstream_synced f{UPDATE_STATUS_CACHE_FILE};
|
pipedal::ofstream_synced f{updateStatusCacheFile};
|
||||||
json_writer writer{f};
|
json_writer writer{f};
|
||||||
writer.write(updateStatus);
|
writer.write(updateStatus);
|
||||||
}
|
}
|
||||||
@@ -189,13 +205,22 @@ static void SetCachedUpdateStatus(UpdateStatus &updateStatus)
|
|||||||
Lv2Log::error(SS("Unable to write cached UpdateStatus. " << e.what()));
|
Lv2Log::error(SS("Unable to write cached UpdateStatus. " << e.what()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
UpdaterImpl::UpdaterImpl()
|
|
||||||
|
UpdaterImpl::UpdaterImpl(const std::filesystem::path &workingDirectory)
|
||||||
|
: workingDirectory(workingDirectory),
|
||||||
|
updateStatusCacheFile(workingDirectory / "updateStatus.json"),
|
||||||
|
githubResponseHeaderFilename(workingDirectory / "githubHeaders.json")
|
||||||
{
|
{
|
||||||
this->githubResponseHeaders.Load();
|
this->githubResponseHeaders.Load(githubResponseHeaderFilename);
|
||||||
cachedUpdateStatus = GetCachedUpdateStatus();
|
cachedUpdateStatus = GetCachedUpdateStatus();
|
||||||
this->updatePolicy = cachedUpdateStatus.UpdatePolicy();
|
this->updatePolicy = cachedUpdateStatus.UpdatePolicy();
|
||||||
currentResult = cachedUpdateStatus;
|
currentResult = cachedUpdateStatus;
|
||||||
|
}
|
||||||
|
void UpdaterImpl::Start()
|
||||||
|
{
|
||||||
|
#if !ENABLE_AUTO_UPDATE
|
||||||
|
return;
|
||||||
|
#endif
|
||||||
int fds[2];
|
int fds[2];
|
||||||
int rc = pipe(fds);
|
int rc = pipe(fds);
|
||||||
if (rc != 0)
|
if (rc != 0)
|
||||||
@@ -225,6 +250,9 @@ void UpdaterImpl::Stop()
|
|||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
#if !ENABLE_AUTO_UPDATE
|
||||||
|
return;
|
||||||
|
#endif
|
||||||
stopped = true;
|
stopped = true;
|
||||||
|
|
||||||
if (event_writer != -1)
|
if (event_writer != -1)
|
||||||
@@ -563,8 +591,7 @@ JSON_MAP_REFERENCE(GithubResponseHeaders, ratelimit_used)
|
|||||||
JSON_MAP_REFERENCE(GithubResponseHeaders, ratelimit_resource)
|
JSON_MAP_REFERENCE(GithubResponseHeaders, ratelimit_resource)
|
||||||
JSON_MAP_END();
|
JSON_MAP_END();
|
||||||
|
|
||||||
std::filesystem::path GithubResponseHeaders::FILENAME = WORKING_DIRECTORY / "githubHeaders.json";
|
void GithubResponseHeaders::Load(const std::filesystem::path &FILENAME)
|
||||||
void GithubResponseHeaders::Load()
|
|
||||||
{
|
{
|
||||||
std::ifstream f(FILENAME);
|
std::ifstream f(FILENAME);
|
||||||
if (f.is_open())
|
if (f.is_open())
|
||||||
@@ -581,9 +608,9 @@ void GithubResponseHeaders::Load()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void GithubResponseHeaders::Save()
|
void GithubResponseHeaders::Save(const std::filesystem::path &FILENAME)
|
||||||
{
|
{
|
||||||
std::filesystem::create_directories(WORKING_DIRECTORY);
|
std::filesystem::create_directories(FILENAME.parent_path());
|
||||||
ofstream_synced f(FILENAME);
|
ofstream_synced f(FILENAME);
|
||||||
if (!f.is_open())
|
if (!f.is_open())
|
||||||
{
|
{
|
||||||
@@ -622,7 +649,7 @@ GithubResponseHeaders::GithubResponseHeaders(const std::filesystem::path path)
|
|||||||
auto trimPos = line.find_last_not_of("\r\n");
|
auto trimPos = line.find_last_not_of("\r\n");
|
||||||
if (trimPos != std::string::npos)
|
if (trimPos != std::string::npos)
|
||||||
{
|
{
|
||||||
line = line.substr(0,trimPos + 1);
|
line = line.substr(0, trimPos + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!f)
|
if (!f)
|
||||||
@@ -675,6 +702,12 @@ GithubResponseHeaders::GithubResponseHeaders(const std::filesystem::path path)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UpdateStatus UpdaterImpl::GetReleaseGeneratorStatus()
|
||||||
|
{
|
||||||
|
UpdateStatus result = DoUpdate(true);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
void UpdaterImpl::CheckForUpdate(bool useCache)
|
void UpdaterImpl::CheckForUpdate(bool useCache)
|
||||||
{
|
{
|
||||||
UpdateStatus updateResult;
|
UpdateStatus updateResult;
|
||||||
@@ -702,133 +735,11 @@ void UpdaterImpl::CheckForUpdate(bool useCache)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// set default next retry time. github failures may postpone even further.
|
|
||||||
|
|
||||||
Lv2Log::info("Checking for updates.");
|
|
||||||
// const std::string responseOption = "-w \"%{response_code}\"";
|
|
||||||
#ifdef WIN32
|
|
||||||
responseOption = "-w \"%%{response_code}\""; // windows shell requires doubling of the %%.
|
|
||||||
#endif
|
|
||||||
|
|
||||||
TemporaryFile headerFile{WORKING_DIRECTORY};
|
|
||||||
std::string args = SS("-s -L " << GITHUB_RELEASES_URL << " -D " << headerFile.str());
|
|
||||||
|
|
||||||
updateResult.errorMessage_ = "";
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
auto result = sysExecForOutput("curl", args);
|
|
||||||
if (result.exitCode != EXIT_SUCCESS)
|
|
||||||
{
|
|
||||||
RetryAfter(std::chrono::duration_cast<clock::duration>(std::chrono::hours(4)));
|
|
||||||
throw std::runtime_error("Server has no internet access.");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// hard throttling for github.
|
|
||||||
RetryAfter(std::chrono::duration_cast<clock::duration>(std::chrono::hours(8)));
|
|
||||||
|
|
||||||
GithubResponseHeaders githubHeaders{headerFile.Path()};
|
updateResult = DoUpdate(false);
|
||||||
this->githubResponseHeaders = githubHeaders;
|
|
||||||
this->githubResponseHeaders.Save();
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
this->RetryAfter(githubHeaders.ratelimit_reset_ - githubHeaders.date_);
|
|
||||||
throw std::runtime_error(SS("Github API rate limit exceeded. Retrying at " << strTime));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (result.output.length() == 0)
|
|
||||||
{
|
|
||||||
throw std::runtime_error("Server has no internet access.");
|
|
||||||
}
|
|
||||||
std::stringstream ss(result.output);
|
|
||||||
json_reader reader(ss);
|
|
||||||
json_variant vResult(reader);
|
|
||||||
|
|
||||||
if (vResult.is_object())
|
|
||||||
{
|
|
||||||
// an HTML error.
|
|
||||||
updateResult.isOnline_ = false;
|
|
||||||
auto o = vResult.as_object();
|
|
||||||
std::string message = "Unknown error.";
|
|
||||||
if (o->at("message").is_string())
|
|
||||||
{
|
|
||||||
message = o->at("message").as_string();
|
|
||||||
}
|
|
||||||
throw std::runtime_error(SS("Github Service error: " << message));
|
|
||||||
}
|
|
||||||
else if (!vResult.is_array())
|
|
||||||
{
|
|
||||||
throw std::runtime_error("Invalid file format error.");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
json_variant::array_ptr vArray = vResult.as_array();
|
|
||||||
|
|
||||||
std::vector<GithubRelease> releases;
|
|
||||||
for (size_t i = 0; i < vArray->size(); ++i)
|
|
||||||
{
|
|
||||||
auto &el = vArray->at(i);
|
|
||||||
GithubRelease release{el};
|
|
||||||
if (!release.draft && release.GetDownloadForCurrentArchitecture() != nullptr)
|
|
||||||
{
|
|
||||||
if (release.name.find("Experimental") == std::string::npos) // experimental releases do not participate in auto-updates (not even for dev stream)
|
|
||||||
{
|
|
||||||
releases.push_back(std::move(release));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
std::sort(
|
|
||||||
releases.begin(),
|
|
||||||
releases.end(),
|
|
||||||
[](const GithubRelease &left, const GithubRelease &right)
|
|
||||||
{
|
|
||||||
return left.published_at > right.published_at; // latest date first.
|
|
||||||
});
|
|
||||||
updateResult.releaseOnlyRelease_ = getUpdateRelease(
|
|
||||||
releases,
|
|
||||||
updateResult.currentVersion_,
|
|
||||||
[](const GithubRelease &githubRelease)
|
|
||||||
{
|
|
||||||
return !githubRelease.prerelease &&
|
|
||||||
githubRelease.name.find("Release") != std::string::npos;
|
|
||||||
});
|
|
||||||
|
|
||||||
updateResult.releaseOrBetaRelease_ = getUpdateRelease(
|
|
||||||
releases,
|
|
||||||
updateResult.currentVersion_,
|
|
||||||
[](const GithubRelease &githubRelease)
|
|
||||||
{
|
|
||||||
return !githubRelease.prerelease &&
|
|
||||||
(githubRelease.name.find("Release") != std::string::npos ||
|
|
||||||
githubRelease.name.find("Beta") != std::string::npos);
|
|
||||||
});
|
|
||||||
updateResult.devRelease_ = getUpdateRelease(
|
|
||||||
releases,
|
|
||||||
updateResult.currentVersion_,
|
|
||||||
[](const GithubRelease &githubRelease)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
#ifdef TEST_UPDATE
|
|
||||||
updateResult.releaseOrBetaRelease_.upgradeVersionDisplayName_ = "PiPedal v1.2.41-Beta";
|
|
||||||
updateResult.devRelease_.upgradeVersionDisplayName_ = "PiPedal v1.2.39-Experimental";
|
|
||||||
updateResult.devRelease_.upgradeVersion_ = "1.2.39";
|
|
||||||
updateResult.devRelease_.updateAvailable_ = false;
|
|
||||||
#endif
|
|
||||||
updateResult.isValid_ = true;
|
|
||||||
updateResult.isOnline_ = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch (const std::exception &e)
|
catch (const std::exception &e)
|
||||||
{
|
{
|
||||||
@@ -851,6 +762,142 @@ void UpdaterImpl::CheckForUpdate(bool useCache)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UpdateStatus UpdaterImpl::DoUpdate(bool forReleaseGenerator)
|
||||||
|
{
|
||||||
|
UpdateStatus updateResult;
|
||||||
|
if (forReleaseGenerator)
|
||||||
|
{
|
||||||
|
updateResult.currentVersion_ = "0.0.0";
|
||||||
|
}
|
||||||
|
|
||||||
|
// set default next retry time. github failures may postpone even further.
|
||||||
|
|
||||||
|
Lv2Log::info("Checking for updates.");
|
||||||
|
// const std::string responseOption = "-w \"%{response_code}\"";
|
||||||
|
#ifdef WIN32
|
||||||
|
responseOption = "-w \"%%{response_code}\""; // windows shell requires doubling of the %%.
|
||||||
|
#endif
|
||||||
|
|
||||||
|
TemporaryFile headerFile{workingDirectory};
|
||||||
|
std::string args = SS("-s -L " << GITHUB_RELEASES_URL << " -D " << headerFile.str());
|
||||||
|
|
||||||
|
updateResult.errorMessage_ = "";
|
||||||
|
|
||||||
|
auto result = sysExecForOutput("curl", args);
|
||||||
|
if (result.exitCode != EXIT_SUCCESS)
|
||||||
|
{
|
||||||
|
RetryAfter(std::chrono::duration_cast<clock::duration>(std::chrono::hours(4)));
|
||||||
|
throw std::runtime_error("Server has no internet access.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// hard throttling for github.
|
||||||
|
RetryAfter(std::chrono::duration_cast<clock::duration>(std::chrono::hours(8)));
|
||||||
|
|
||||||
|
GithubResponseHeaders githubHeaders{headerFile.Path()};
|
||||||
|
this->githubResponseHeaders = githubHeaders;
|
||||||
|
this->githubResponseHeaders.Save(githubResponseHeaderFilename);
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
this->RetryAfter(githubHeaders.ratelimit_reset_ - githubHeaders.date_);
|
||||||
|
throw std::runtime_error(SS("Github API rate limit exceeded. Retrying at " << strTime));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.output.length() == 0)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Server has no internet access.");
|
||||||
|
}
|
||||||
|
std::stringstream ss(result.output);
|
||||||
|
json_reader reader(ss);
|
||||||
|
json_variant vResult(reader);
|
||||||
|
|
||||||
|
if (vResult.is_object())
|
||||||
|
{
|
||||||
|
// an HTML error.
|
||||||
|
updateResult.isOnline_ = false;
|
||||||
|
auto o = vResult.as_object();
|
||||||
|
std::string message = "Unknown error.";
|
||||||
|
if (o->at("message").is_string())
|
||||||
|
{
|
||||||
|
message = o->at("message").as_string();
|
||||||
|
}
|
||||||
|
throw std::runtime_error(SS("Github Service error: " << message));
|
||||||
|
}
|
||||||
|
else if (!vResult.is_array())
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Invalid file format error.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
json_variant::array_ptr vArray = vResult.as_array();
|
||||||
|
|
||||||
|
std::vector<GithubRelease> releases;
|
||||||
|
for (size_t i = 0; i < vArray->size(); ++i)
|
||||||
|
{
|
||||||
|
auto &el = vArray->at(i);
|
||||||
|
GithubRelease release{el};
|
||||||
|
if (!release.draft && release.GetDownloadForCurrentArchitecture() != nullptr)
|
||||||
|
{
|
||||||
|
if (release.name.find("Experimental") == std::string::npos) // experimental releases do not participate in auto-updates (not even for dev stream)
|
||||||
|
{
|
||||||
|
releases.push_back(std::move(release));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::sort(
|
||||||
|
releases.begin(),
|
||||||
|
releases.end(),
|
||||||
|
[](const GithubRelease &left, const GithubRelease &right)
|
||||||
|
{
|
||||||
|
return left.published_at > right.published_at; // latest date first.
|
||||||
|
});
|
||||||
|
updateResult.releaseOnlyRelease_ = getUpdateRelease(
|
||||||
|
releases,
|
||||||
|
updateResult.currentVersion_,
|
||||||
|
[](const GithubRelease &githubRelease)
|
||||||
|
{
|
||||||
|
return !githubRelease.prerelease &&
|
||||||
|
githubRelease.name.find("Release") != std::string::npos;
|
||||||
|
});
|
||||||
|
|
||||||
|
updateResult.releaseOrBetaRelease_ = getUpdateRelease(
|
||||||
|
releases,
|
||||||
|
updateResult.currentVersion_,
|
||||||
|
[](const GithubRelease &githubRelease)
|
||||||
|
{
|
||||||
|
return !githubRelease.prerelease &&
|
||||||
|
(githubRelease.name.find("Release") != std::string::npos ||
|
||||||
|
githubRelease.name.find("Beta") != std::string::npos);
|
||||||
|
});
|
||||||
|
updateResult.devRelease_ = getUpdateRelease(
|
||||||
|
releases,
|
||||||
|
updateResult.currentVersion_,
|
||||||
|
[](const GithubRelease &githubRelease)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
#ifdef TEST_UPDATE
|
||||||
|
updateResult.releaseOrBetaRelease_.upgradeVersionDisplayName_ = "PiPedal v1.2.41-Beta";
|
||||||
|
updateResult.devRelease_.upgradeVersionDisplayName_ = "PiPedal v1.2.39-Experimental";
|
||||||
|
updateResult.devRelease_.upgradeVersion_ = "1.2.39";
|
||||||
|
updateResult.devRelease_.updateAvailable_ = false;
|
||||||
|
#endif
|
||||||
|
updateResult.isValid_ = true;
|
||||||
|
updateResult.isOnline_ = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return updateResult;
|
||||||
|
}
|
||||||
bool UpdateRelease::operator==(const UpdateRelease &other) const
|
bool UpdateRelease::operator==(const UpdateRelease &other) const
|
||||||
{
|
{
|
||||||
return (updateAvailable_ == other.updateAvailable_) &&
|
return (updateAvailable_ == other.updateAvailable_) &&
|
||||||
@@ -1216,7 +1263,7 @@ void UpdaterImpl::RetryAfter(clock::duration delay)
|
|||||||
}
|
}
|
||||||
void UpdaterImpl::SaveRetryTime(const std::chrono::system_clock::time_point &time)
|
void UpdaterImpl::SaveRetryTime(const std::chrono::system_clock::time_point &time)
|
||||||
{
|
{
|
||||||
fs::path retryTimePath = WORKING_DIRECTORY / "retryTime.json";
|
fs::path retryTimePath = workingDirectory / "retryTime.json";
|
||||||
pipedal::ofstream_synced f{retryTimePath};
|
pipedal::ofstream_synced f{retryTimePath};
|
||||||
if (f.is_open())
|
if (f.is_open())
|
||||||
{
|
{
|
||||||
@@ -1229,7 +1276,7 @@ UpdaterImpl::clock::time_point UpdaterImpl::LoadRetryTime()
|
|||||||
{
|
{
|
||||||
using namespace std::chrono;
|
using namespace std::chrono;
|
||||||
|
|
||||||
fs::path retryTimePath = WORKING_DIRECTORY / "retryTime.json";
|
fs::path retryTimePath = workingDirectory / "retryTime.json";
|
||||||
std::ifstream f{retryTimePath};
|
std::ifstream f{retryTimePath};
|
||||||
if (f.is_open())
|
if (f.is_open())
|
||||||
{
|
{
|
||||||
@@ -1273,7 +1320,7 @@ void UpdaterImpl::DownloadUpdate(const std::string &url, std::filesystem::path *
|
|||||||
{
|
{
|
||||||
throw std::runtime_error(SS("Invalid update url. Downloads from this address are not permitted: " << signatureUrl));
|
throw std::runtime_error(SS("Invalid update url. Downloads from this address are not permitted: " << signatureUrl));
|
||||||
}
|
}
|
||||||
auto downloadDirectory = WORKING_DIRECTORY / "downloads";
|
auto downloadDirectory = workingDirectory / "downloads";
|
||||||
std::filesystem::create_directories(downloadDirectory);
|
std::filesystem::create_directories(downloadDirectory);
|
||||||
|
|
||||||
auto downloadFilePath = downloadDirectory / filename;
|
auto downloadFilePath = downloadDirectory / filename;
|
||||||
@@ -1336,6 +1383,14 @@ void UpdaterImpl::DownloadUpdate(const std::string &url, std::filesystem::path *
|
|||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void UpdateStatus::AddRelease(const std::string&packageName)
|
||||||
|
{
|
||||||
|
this->isValid_ = true;
|
||||||
|
this->errorMessage_ = "";
|
||||||
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
JSON_MAP_BEGIN(UpdateRelease)
|
JSON_MAP_BEGIN(UpdateRelease)
|
||||||
|
|||||||
+6
-2
@@ -47,18 +47,22 @@ namespace pipedal
|
|||||||
using ptr = std::unique_ptr<self>;
|
using ptr = std::unique_ptr<self>;
|
||||||
|
|
||||||
static Updater::ptr Create();
|
static Updater::ptr Create();
|
||||||
|
static Updater::ptr Create(const std::filesystem::path & workingDirectory);
|
||||||
static void ValidateSignature(const std::filesystem::path&file, const std::filesystem::path&signatureFile);
|
static void ValidateSignature(const std::filesystem::path&file, const std::filesystem::path&signatureFile);
|
||||||
|
|
||||||
using UpdateListener = std::function<void(const UpdateStatus &upateResult)>;
|
using UpdateListener = std::function<void(const UpdateStatus &upateResult)>;
|
||||||
virtual void SetUpdateListener(UpdateListener &&listener) = 0;
|
virtual void SetUpdateListener(UpdateListener &&listener) = 0;
|
||||||
virtual void CheckNow() = 0;
|
virtual void Start() = 0;
|
||||||
virtual void Stop() = 0;
|
virtual void Stop() = 0;
|
||||||
|
|
||||||
|
virtual void CheckNow() = 0;
|
||||||
|
|
||||||
virtual UpdatePolicyT GetUpdatePolicy() = 0;
|
virtual UpdatePolicyT GetUpdatePolicy() = 0;
|
||||||
virtual void SetUpdatePolicy(UpdatePolicyT updatePolicy) = 0;
|
virtual void SetUpdatePolicy(UpdatePolicyT updatePolicy) = 0;
|
||||||
virtual void ForceUpdateCheck() = 0;
|
virtual void ForceUpdateCheck() = 0;
|
||||||
virtual void DownloadUpdate(const std::string &url, std::filesystem::path*file, std::filesystem::path*signatureFile) = 0;
|
virtual void DownloadUpdate(const std::string &url, std::filesystem::path*file, std::filesystem::path*signatureFile) = 0;
|
||||||
UpdateStatus virtual GetCurrentStatus() const = 0;
|
virtual UpdateStatus GetCurrentStatus() const = 0;
|
||||||
|
virtual UpdateStatus GetReleaseGeneratorStatus() = 0; // only used when generating releases. Absolutely no caching.
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -24,6 +24,7 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
/*
|
/*
|
||||||
PiPedal uses whitelisting and GPG signatures to verify that updates are valid.
|
PiPedal uses whitelisting and GPG signatures to verify that updates are valid.
|
||||||
You must modify these defines appropriately if you want to host a fork.
|
You must modify these defines appropriately if you want to host a fork.
|
||||||
@@ -32,6 +33,9 @@
|
|||||||
installs published on github/rerdavies/pipedal.
|
installs published on github/rerdavies/pipedal.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#ifndef ENABLE_AUTO_UPDATE
|
||||||
|
#define ENABLE_AUTO_UPDATE 1
|
||||||
|
#endif
|
||||||
#ifndef GITHUB_PROJECT
|
#ifndef GITHUB_PROJECT
|
||||||
#define GITHUB_PROJECT "rerdavies/pipedal"
|
#define GITHUB_PROJECT "rerdavies/pipedal"
|
||||||
#endif
|
#endif
|
||||||
@@ -43,7 +47,7 @@
|
|||||||
#define UPDATE_GPG_ADDRESS "Robin Davies <rerdavies@gmail.com>"
|
#define UPDATE_GPG_ADDRESS "Robin Davies <rerdavies@gmail.com>"
|
||||||
#endif
|
#endif
|
||||||
#ifndef UPDATE_GPG_ADDRESS2
|
#ifndef UPDATE_GPG_ADDRESS2
|
||||||
#define UPDATE_GPG_ADDRESS2 "PiPedal Project <rerdavies@gmail.com>"
|
#define UPDATE_GPG_ADDRESS2 "PiPedal Project <rerdavies@gmail.com>"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -92,6 +92,8 @@ namespace pipedal {
|
|||||||
const UpdateRelease &DevRelease() const { return devRelease_; }
|
const UpdateRelease &DevRelease() const { return devRelease_; }
|
||||||
bool operator==(const UpdateStatus &other) const;
|
bool operator==(const UpdateStatus &other) const;
|
||||||
|
|
||||||
|
void AddRelease(const std::string&packageName); // Release Generator ONLY
|
||||||
|
|
||||||
DECLARE_JSON_MAP(UpdateStatus);
|
DECLARE_JSON_MAP(UpdateStatus);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -43,6 +43,8 @@ TEST_CASE( "updater test", "[updater]" ) {
|
|||||||
++nCalls;
|
++nCalls;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
updater->Start();
|
||||||
|
|
||||||
}
|
}
|
||||||
REQUIRE(nCalls == 1);
|
REQUIRE(nCalls == 1);
|
||||||
|
|
||||||
|
|||||||
@@ -29,6 +29,7 @@
|
|||||||
#include "ZipFile.hpp"
|
#include "ZipFile.hpp"
|
||||||
#include "PiPedalUI.hpp"
|
#include "PiPedalUI.hpp"
|
||||||
#include "ofstream_synced.hpp"
|
#include "ofstream_synced.hpp"
|
||||||
|
#include "UpdaterSecurity.hpp"
|
||||||
|
|
||||||
|
|
||||||
#define PRESET_EXTENSION ".piPreset"
|
#define PRESET_EXTENSION ".piPreset"
|
||||||
@@ -524,8 +525,10 @@ public:
|
|||||||
std::stringstream s;
|
std::stringstream s;
|
||||||
|
|
||||||
s << "{ \"socket_server_port\": " << portNumber
|
s << "{ \"socket_server_port\": " << portNumber
|
||||||
<< ", \"socket_server_address\": \"" << webSocketAddress <<
|
<< ", \"socket_server_address\": \"" << webSocketAddress
|
||||||
"\", \"ui_plugins\": [ ], \"max_upload_size\": " << maxUploadSize << " }";
|
<< "\", \"ui_plugins\": [ ], \"max_upload_size\": " << maxUploadSize
|
||||||
|
<< ", \"enable_auto_update\": " << (ENABLE_AUTO_UPDATE ? " true": "false")
|
||||||
|
<< " }";
|
||||||
|
|
||||||
return s.str();
|
return s.str();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +0,0 @@
|
|||||||
[Adding or changing system-wide NetworkManager connections]
|
|
||||||
Identity=unix-group:pipedal_d
|
|
||||||
Action=org.freedesktop.NetworkManager.settings.modify.system
|
|
||||||
ResultAny=yes
|
|
||||||
ResultInactive=yes
|
|
||||||
ResultActive=yes
|
|
||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
// grant NetworkManager dbus permissions (no-prompts) to the pipedald systemd service.
|
// grant NetworkManager dbus permissions (no-prompts) to the pipedald systemd service.
|
||||||
polpolkit.addRule(function(action, subject) {
|
polkit.addRule(function(action, subject) {
|
||||||
if (action.id.indexOf("org.freedesktop.NetworkManager.") == 0 && subject.isInGroup("pipedal_d")) {
|
if (action.id.indexOf("org.freedesktop.NetworkManager.") == 0 && subject.isInGroup("pipedal_d")) {
|
||||||
return polkit.Result.YES;
|
return polkit.Result.YES;
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,11 @@
|
|||||||
|
- X test reconnect sequence in client.
|
||||||
|
|
||||||
|
-X remove 10-pipedal-networkmanager.rules before testing installer.
|
||||||
|
|
||||||
|
- new signing procedure.
|
||||||
|
|
||||||
- verify that scanning doesn't cause overruns.
|
- verify that scanning doesn't cause overruns.
|
||||||
- address change handlers in client (and browser?)
|
- verify address change behaviour in client
|
||||||
- Keybaord escape for hotspotManager.
|
|
||||||
- verify stripping in installer.
|
- verify stripping in installer.
|
||||||
- verify update with no keyring.
|
- verify update with no keyring.
|
||||||
-verify clean removal of dhcpcd and nm_p2p2d
|
-verify clean removal of dhcpcd and nm_p2p2d
|
||||||
@@ -14,12 +18,11 @@ Localisation: support non-UTF8 code pages.
|
|||||||
- unicode commandline arguments.
|
- unicode commandline arguments.
|
||||||
- review unicode filenames (this is probably ok)
|
- review unicode filenames (this is probably ok)
|
||||||
|
|
||||||
|
- make app use the same theme settings as the website. (make the website use the same theme as the app)
|
||||||
|
|
||||||
- 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
|
||||||
- versioning.
|
- versioning.
|
||||||
- do we want to take a second crack at establishin multi-p2p connections with the driver_param?
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user