diff --git a/CMakeLists.txt b/CMakeLists.txt
index b7cd993..ccbdd43 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -80,6 +80,7 @@ add_executable(${PROJECT_NAME}
imageformats/pngencoder.cpp
zeroconf/mdnspublisher.cpp
zeroconf/hotplugnotifier.cpp
+ zeroconf/networkhotplugnotifier.cpp
${ZEROCONF_FILES}
)
if(CMAKE_SYSTEM_NAME STREQUAL FreeBSD)
diff --git a/server/server.cpp b/server/server.cpp
index 54918c1..cc47945 100644
--- a/server/server.cpp
+++ b/server/server.cpp
@@ -36,6 +36,7 @@ along with this program. If not, see .
#include "purgethread.h"
#include "basic/uuid.h"
#include "zeroconf/hotplugnotifier.h"
+#include "zeroconf/networkhotplugnotifier.h"
#include "web/accessfile.h"
extern const char* GIT_COMMIT_HASH;
@@ -72,6 +73,26 @@ struct Notifier : HotplugNotifier
}
};
+struct NetworkNotifier : NetworkHotplugNotifier
+{
+ Server& server;
+ explicit NetworkNotifier(Server& s)
+ : server(s)
+ {}
+ void onHotplugEvent(Event ev) override
+ {
+ switch (ev) {
+ case addressArrived:
+ case addressLeft:
+ std::clog << "network hotplug event, reloading configuration" << std::endl;
+ server.terminate(SIGHUP);
+ break;
+ case other:
+ break;
+ }
+ }
+};
+
bool
clientIsAirscan(const HttpServer::Request& req)
{
@@ -88,6 +109,7 @@ Server::Server(int argc, char** argv)
, mDiscloseversion(true)
, mLocalonly(true)
, mHotplug(true)
+ , mNetworkhotplug(true)
, mRandompaths(false)
, mCompatiblepath(false)
, mJobtimeout(0)
@@ -95,8 +117,8 @@ Server::Server(int argc, char** argv)
, mStartupTimeSeconds(0)
, mDoRun(true)
{
- std::string port, interface, unixsocket, accesslog, hotplug, announce,
- webinterface, resetoption, discloseversion, localonly, optionsfile,
+ std::string port, interface, unixsocket, accesslog, hotplug, networkhotplug,
+ announce, webinterface, resetoption, discloseversion, localonly, optionsfile,
ignorelist, accessfile, randompaths, compatiblepath, debug, announcesecure,
jobtimeout, purgeinterval;
struct
@@ -109,6 +131,7 @@ Server::Server(int argc, char** argv)
{ "unix-socket", "", "listen on named unix socket", unixsocket },
{ "access-log", "", "HTTP access log, - for stdout", accesslog },
{ "hotplug", "true", "repeat scanner search on hotplug event", hotplug },
+ { "network-hotplug", "true", "restart server on network change", networkhotplug },
{ "mdns-announce", "true", "announce scanners via mDNS", announce },
{ "announce-secure", "false", "announce secure connection", announcesecure },
{ "web-interface", "true", "enable web interface", webinterface },
@@ -175,6 +198,7 @@ Server::Server(int argc, char** argv)
sanecpp::log.rdbuf(std::clog.rdbuf());
mHotplug = (hotplug == "true");
+ mNetworkhotplug = (networkhotplug == "true");
mAnnounce = (announce == "true");
mAnnouncesecure = (announcesecure == "true");
mWebinterface = (webinterface == "true");
@@ -240,6 +264,10 @@ Server::run()
if (mHotplug)
pNotifier = std::make_shared(*this);
+ std::shared_ptr pNetworkNotifier;
+ if (mNetworkhotplug)
+ pNetworkNotifier = std::make_shared(*this);
+
bool ok = false, done = false;
do {
if (unixSocket().empty()) {
diff --git a/server/server.h b/server/server.h
index af002b4..0af9e87 100644
--- a/server/server.h
+++ b/server/server.h
@@ -60,7 +60,7 @@ class Server : public HttpServer
ScannerList mScanners;
std::filebuf mLogfile;
bool mAnnounce, mWebinterface, mResetoption, mDiscloseversion,
- mLocalonly, mHotplug, mRandompaths, mCompatiblepath, mAnnouncesecure;
+ mLocalonly, mHotplug, mNetworkhotplug, mRandompaths, mCompatiblepath, mAnnouncesecure;
std::string mOptionsfile, mAccessfile, mIgnorelist;
int mJobtimeout, mPurgeinterval;
float mStartupTimeSeconds;
diff --git a/zeroconf/networkhotplugnotifier.cpp b/zeroconf/networkhotplugnotifier.cpp
new file mode 100644
index 0000000..746033e
--- /dev/null
+++ b/zeroconf/networkhotplugnotifier.cpp
@@ -0,0 +1,116 @@
+/*
+AirSane Imaging Daemon
+Copyright (C) 2018-2023 Simul Piscator
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*/
+
+#include "networkhotplugnotifier.h"
+
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+struct NetworkHotplugNotifier::Private
+{
+ std::thread mThread;
+ NetworkHotplugNotifier* mpNotifier;
+ int mPipeWriteFd, mPipeReadFd;
+
+ Private(NetworkHotplugNotifier* pNotifier)
+ : mpNotifier(pNotifier), mPipeWriteFd(-1), mPipeReadFd(-1)
+ {
+ int fds[2];
+ if (::pipe(fds) < 0) {
+ std::cerr << "Could not create socket pair " << errno << std::endl;
+ return;
+ }
+ mPipeReadFd = fds[0];
+ mPipeWriteFd = fds[1];
+ mThread = std::thread([this]() { hotplugThread(); });
+ }
+
+ ~Private()
+ {
+ char c = '0';
+ ::write(mPipeWriteFd, &c, 1);
+ mThread.join();
+ ::close(mPipeWriteFd);
+ ::close(mPipeReadFd);
+ }
+
+ void hotplugThread()
+ {
+ int sock = ::socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
+ if (sock < 0) {
+ std::cerr << "Could not create netlink socket: " << errno << std::endl;
+ return;
+ }
+
+ sockaddr_nl addr = {0};
+ addr.nl_family = AF_NETLINK;
+ addr.nl_groups = RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR;
+ if (::bind(sock, reinterpret_cast(&addr), sizeof(addr)) < 0) {
+ std::cerr << "Could not bind netlink socket: " << errno << std::endl;
+ ::close(sock);
+ return;
+ }
+
+ struct pollfd pfds[2] = {0};
+ pfds[0].fd = mPipeReadFd;
+ pfds[0].events = POLLIN;
+ pfds[1].fd = sock;
+ pfds[1].events = POLLIN;
+
+ char buffer[4096];
+ bool done = false;
+ while (!done) {
+ int r = ::poll(pfds, sizeof(pfds)/sizeof(*pfds), -1);
+ if (r > 0 && pfds[0].revents) {
+ done = true;
+ }
+ else if (r > 0 && pfds[1].revents) {
+ int len = ::read(sock, buffer, sizeof(buffer));
+ if (len > 0) {
+ union { const char* c; struct nlmsghdr* n; } data = { buffer };
+ if (data.n->nlmsg_flags & MSG_TRUNC)
+ continue;
+ while (NLMSG_OK(data.n, len) && (data.n->nlmsg_type != NLMSG_DONE)) {
+ if (data.n->nlmsg_type == RTM_NEWADDR)
+ mpNotifier->onHotplugEvent(addressArrived);
+ else if(data.n->nlmsg_type == RTM_DELADDR)
+ mpNotifier->onHotplugEvent(addressLeft);
+ NLMSG_NEXT(data.n, len);
+ }
+ }
+ }
+ }
+ }
+};
+
+NetworkHotplugNotifier::NetworkHotplugNotifier()
+ : p(new Private(this))
+{}
+
+NetworkHotplugNotifier::~NetworkHotplugNotifier()
+{
+ delete p;
+}
diff --git a/zeroconf/networkhotplugnotifier.h b/zeroconf/networkhotplugnotifier.h
new file mode 100644
index 0000000..19a7f67
--- /dev/null
+++ b/zeroconf/networkhotplugnotifier.h
@@ -0,0 +1,45 @@
+/*
+AirSane Imaging Daemon
+Copyright (C) 2018-2023 Simul Piscator
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*/
+
+#ifndef NETWORK_HOTPLUGNOTIFIER_H
+#define NETWORK_HOTPLUGNOTIFIER_H
+
+class NetworkHotplugNotifier
+{
+ NetworkHotplugNotifier(const NetworkHotplugNotifier&) = delete;
+ NetworkHotplugNotifier& operator=(const NetworkHotplugNotifier&) = delete;
+
+public:
+ NetworkHotplugNotifier();
+ virtual ~NetworkHotplugNotifier();
+
+protected:
+ enum Event
+ {
+ other,
+ addressArrived,
+ addressLeft,
+ };
+ virtual void onHotplugEvent(Event) {}
+
+private:
+ struct Private;
+ Private* p;
+};
+
+#endif // NETWORK_HOTPLUGNOTIFIER_H