Skip to content

Commit

Permalink
Add restart on network address change
Browse files Browse the repository at this point in the history
  • Loading branch information
Simul Piscator committed Nov 8, 2023
1 parent 16bcd3c commit f51fb4f
Show file tree
Hide file tree
Showing 5 changed files with 193 additions and 3 deletions.
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
32 changes: 30 additions & 2 deletions server/server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#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;
Expand Down Expand Up @@ -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)
{
Expand All @@ -88,15 +109,16 @@ Server::Server(int argc, char** argv)
, mDiscloseversion(true)
, mLocalonly(true)
, mHotplug(true)
, mNetworkhotplug(true)
, mRandompaths(false)
, mCompatiblepath(false)
, mJobtimeout(0)
, mPurgeinterval(0)
, 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
Expand All @@ -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 },
Expand Down Expand Up @@ -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");
Expand Down Expand Up @@ -240,6 +264,10 @@ Server::run()
if (mHotplug)
pNotifier = std::make_shared<Notifier>(*this);

std::shared_ptr<NetworkNotifier> pNetworkNotifier;
if (mNetworkhotplug)
pNetworkNotifier = std::make_shared<NetworkNotifier>(*this);

bool ok = false, done = false;
do {
if (unixSocket().empty()) {
Expand Down
2 changes: 1 addition & 1 deletion server/server.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
116 changes: 116 additions & 0 deletions zeroconf/networkhotplugnotifier.cpp
Original file line number Diff line number Diff line change
@@ -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 <http://www.gnu.org/licenses/>.
*/

#include "networkhotplugnotifier.h"

#include <iostream>
#include <vector>
#include <thread>

#include <poll.h>
#include <unistd.h>
#include <netinet/in.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <net/if.h>

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<sockaddr*>(&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;
}
45 changes: 45 additions & 0 deletions zeroconf/networkhotplugnotifier.h
Original file line number Diff line number Diff line change
@@ -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 <http://www.gnu.org/licenses/>.
*/

#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

0 comments on commit f51fb4f

Please sign in to comment.