Option to stop coolwsd when configuration files change

- This patch only adds an option to enable this feature. It is off by
  default
- This allows you to, say, have a docker container and --restart=always
  to restart when you update the config
- This patch only listens for "/etc/coolwsd/", so if you specify a config
  file that isn't there then you're out of luck... An improvement for a
  followup patch will be make it listen to wherever your config files
  actually are
- The current docker scripts only listen for modifications, so this
  matches that behavior

Signed-off-by: Skyler Grey <skyler.grey@collabora.com>
Change-Id: I674756b0188893f5d192885bb436256aa827e7b5
pull/7288/head
Skyler Grey 2023-08-23 14:00:10 +00:00 committed by Andras Timar
parent fb5aab4693
commit d94dbf8c32
2 changed files with 119 additions and 0 deletions

View File

@ -269,6 +269,8 @@
<remote_url desc="remote server to which you will send resquest to get remote config in response" type="string" default=""></remote_url>
</remote_config>
<stop_on_config_change desc="Stop coolwsd whenever config files change." type="bool" default="false">false</stop_on_config_change>
<remote_font_config>
<url desc="URL of optional JSON file that lists fonts to be included in Online" type="string" default=""></url>
</remote_font_config>

View File

@ -162,6 +162,12 @@ using Poco::Net::PartHandler;
#endif
#endif
#ifdef __linux__
#if !MOBILEAPP
#include <sys/inotify.h>
#endif
#endif
using namespace COOLProtocol;
using Poco::DirectoryIterator;
@ -1001,6 +1007,109 @@ private:
/// And also cleans up and balances the correct number of children.
static std::unique_ptr<PrisonPoll> PrisonerPoll;
#ifdef __linux__
#if !MOBILEAPP
class InotifySocket : public Socket
{
public:
InotifySocket():
Socket(inotify_init1(IN_NONBLOCK))
, m_stopOnConfigChange(true)
{
if (getFD() == -1)
{
LOG_WRN("Inotify - Failed to start a watcher for the configuration, disabling "
"stop_on_config_change");
m_stopOnConfigChange = false;
return;
}
watch(COOLWSD_CONFIGDIR);
}
/// Check for file changes, stop the server if we find any
void handlePoll(SocketDisposition &disposition, std::chrono::steady_clock::time_point now, int events) override;
int getPollEvents(std::chrono::steady_clock::time_point /* now */,
int64_t & /* timeoutMaxMicroS */) override
{
return POLLIN;
}
bool watch(std::string configFile);
private:
bool m_stopOnConfigChange;
int m_watchedCount = 0;
};
bool InotifySocket::watch(const std::string configFile)
{
LOG_TRC("Inotify - Attempting to watch " << configFile << ", in addition to current "
<< m_watchedCount << " watched files");
if (getFD() == -1)
{
LOG_WRN("Inotify - Trying to watch config file " << configFile
<< " without an inotify file descriptor");
return false;
}
int watchedStatus;
watchedStatus = inotify_add_watch(getFD(), configFile.c_str(), IN_MODIFY);
if (watchedStatus == -1)
LOG_WRN("Inotify - Failed to watch config file " << configFile);
else
m_watchedCount++;
return watchedStatus != -1;
}
void InotifySocket::handlePoll(SocketDisposition & /* disposition */, std::chrono::steady_clock::time_point /* now */, int /* events */)
{
LOG_TRC("InotifyPoll - woken up. Reload on config change: "
<< m_stopOnConfigChange << ", Watching " << m_watchedCount << " files");
if (!m_stopOnConfigChange)
return;
char buf[4096];
const struct inotify_event* event;
ssize_t len;
LOG_TRC("InotifyPoll - Checking for config changes...");
while (true)
{
len = read(getFD(), buf, sizeof(buf));
if (len == -1 && errno != EAGAIN)
{
// Some read error, EAGAIN is when there is no data so let's not warn for it
LOG_WRN("InotifyPoll - Read error " << std::strerror(errno)
<< " when trying to get events");
}
else if (len == -1)
{
LOG_TRC("InotifyPoll - Got to end of data when reading inotify");
}
if (len <= 0)
break;
for (char* ptr = buf; ptr < buf + len; ptr += sizeof(struct inotify_event) + event->len)
{
event = (const struct inotify_event*)ptr;
LOG_WRN("InotifyPoll - Config file " << event->name << " was modified, stopping COOLWSD");
SigUtil::requestShutdown();
}
}
}
#endif // if !MOBILEAPP
#endif // #ifdef __linux__
/// The Web Server instance with the accept socket poll thread.
class COOLWSDServer;
static std::unique_ptr<COOLWSDServer> Server;
@ -2034,6 +2143,7 @@ void COOLWSD::innerInitialize(Application& self)
{ "ssl.sts.max_age", "31536000" },
{ "ssl.key_file_path", COOLWSD_CONFIGDIR "/key.pem" },
{ "ssl.termination", "true" },
{ "stop_on_config_change", "false" },
{ "storage.filesystem[@allow]", "false" },
// "storage.ssl.enable" - deliberately not set; for back-compat
{ "storage.wopi.max_file_size", "0" },
@ -5809,6 +5919,13 @@ int COOLWSD::innerMain()
const auto startStamp = std::chrono::steady_clock::now();
#if !MOBILEAPP
auto stampFetch = startStamp - (fetchUpdateCheck - std::chrono::milliseconds(60000));
#ifdef __linux__
if (getConfigValue<bool>("stop_on_config_change", false)) {
std::shared_ptr<InotifySocket> inotifySocket = std::make_shared<InotifySocket>();
mainWait.insertNewSocket(inotifySocket);
}
#endif
#endif
while (!SigUtil::getShutdownRequestFlag())