Files
thehub/libs/apputils/FloweeServiceApplication.h
T
tomFlowee 55328ce0d7 Help binding to future interfaces
The idea of binding to interfaces now will take into account the
interfaces maybe becoming available only after the server started.

If your server starts at machine boot, it is a 50/50 chance that the
network interfaces are already fully configured and have received
addresses. In case of dhcp, more often than not this means that your
server will not be listening at the main interface because it wasn't up
yet.

This new api allows the server to give a function to register a new
interface and we have some linux specific code that will notice changes
in the interfaces and we'll allow the app to bind to it a moment or two
after that.
2025-02-20 20:37:33 +01:00

171 lines
6.5 KiB
C++

/*
* This file is part of the Flowee project
* Copyright (C) 2019-2025 Tom Zander <tom@flowee.org>
*
* 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 FLOWEESERVICEAPPLICATION_H
#define FLOWEESERVICEAPPLICATION_H
#include <QCoreApplication>
#include <QCommandLineParser>
#include <QList>
#include <thread>
#include <Logger.h>
#include <NetworkEndPoint.h>
#include <boost/asio/ip/tcp.hpp>
void HandleSIGTERM(int);
void HandleSIGHUP(int);
class QTcpServer;
/**
* The FloweeServiceApplication is a Qt-CoreApplication inheriting application instance.
* This class adds integration with Flowee components like the network manager and the logging
* subsystem while keeping the Qt style of working, for instance it uses the Qt command line
* parser.
*
* There are two 'modes' you can start the app in, either as intended for a headless server
* or as a CLI tool (typically a client).
*
* @code
* FloweeServiceApplication app(argc, argv);
app.setOrganizationName("MyComapny");
app.setApplicationName("myApp");
QCommandLineParser parser;
parser.setApplicationDescription("Its awesome");
parser.addHelpOption(); // allows users to get an overview of options
QCommandLineOption conf(QStringList() << "conf", "config file", "FILENAME"); // example config
parser.addOption(conf);
// this example is a server, lets add some FloweeServiceAplication own user-options.
app.addServerOptions(parser, FloweeServiceApplication::NoConnect);
parser.process(app.arguments());
// Now allow the FloweeServiceApplication to process the user-options
app.setup("my.log", parser.value(conf));
* @endcode
*/
class FloweeServiceApplication : public QCoreApplication {
Q_OBJECT
public:
/**
* The constructor forwards he argc/argv to QCoreApplication and takes a log section.
* The Flowee Logger uses sections, typically one per library or app. By supplying a
* default section here, all the logging done by this class will be done in that same
* section.
*
* We advice doing a CMAKE define for LOG_DEFAULT_SECTION to your apps default section integer.
*/
FloweeServiceApplication(int &argc, char **argv, short appLogSection = LOG_DEFAULT_SECTION);
~FloweeServiceApplication();
/**
* The command line options the inherting app wants to make available.
* This is list of flags, more than one can be combined.
*/
enum Option {
NoOptions = 0, // Add all the standard options.
NoConnect = 1, // This skips adding of the `--connect` command line option.
NoVerbosity = 2// This skips adding of the --quiet / --verbose command line options.
};
Q_DECLARE_FLAGS(Options, Option)
/// If the app is meant to be a server or service, call this.
void addServerOptions(QCommandLineParser &parser, Options options = NoOptions);
/// If the app is meant to be a CLI tool, call this.
void addClientOptions(QCommandLineParser &parser, Options options = NoOptions);
/**
* After the QCommandLineParser::process has been called, please call setup()
*
* For 'client' operation you likely want to pass in no arguments which makes the logging
* appear only on stdout/stderr.
*
* @param logFile when you are creating a server, passing in a filename makes sure
* the log lines go to the logname.
* @param configFilePath a log-file-config (logs.conf) directory)
*/
void setup(const char *logFilename = nullptr, const QString &configFilePath = QString());
/// Clients that connect to a server can call this to fetch a parsed EndPoint of the server
EndPoint serverAddressFromArguments(uint16_t defaultPort) const;
enum DefaultBindOption {
UserSupplied, ///< If the user doesn't supply a bind option, we don't bind.
LocalhostAsDefault, ///< If no user supplied bind was found, we bind to localhost (ipv4 and ipv6)
AllInterfacesAsDefault, ///< If no user supplied bind was found, we bind to all found interfaces.
YggAsDefault ///< If no user supplied bind was found, we bind to any yggdrasil interfaces
};
/**
* Return all end points based on the command line arguments.
* We accept "localhost" as a string to bind to that.
*
* We accept "0.0.0.0" as a wildcard to all local interfaces. Please note this requires QtNetworkLib.
*/
QList<boost::asio::ip::tcp::endpoint> bindingEndPoints(/*const QCommandLineParser &parser, */uint16_t defaultPort, DefaultBindOption defaultBind = UserSupplied) const;
int bindWith(/*const QCommandLineParser &parser, */uint16_t defaultPort, DefaultBindOption defaultBind,
const std::function<void(const boost::asio::ip::tcp::endpoint&)> &callback);
/// A server that bound SIGHUP will want to call this in the handler to re-create logfiles and such.
void handleSigHub() const;
int bindTo(QTcpServer *server, int defaultPort);
/// \internal callback to update bindings if needed
void networkSetupChanged();
signals:
void reparseConfig() const;
void startRebindTriggered();
private slots:
void rebind();
private:
QCommandLineOption m_debug;
QCommandLineOption m_verbose;
QCommandLineOption m_quiet;
QCommandLineOption m_version;
QCommandLineOption m_bindAddress;
QCommandLineOption m_connect;
QString m_logsconf;
QString m_logFile;
short m_appLogSection = -1;
bool m_isServer = false;
QCommandLineParser *m_parser = nullptr;
struct NetworkRebindData {
bool triggeredRebind = false;
std::function<void(const boost::asio::ip::tcp::endpoint&)> bindOne_function;
QList<boost::asio::ip::tcp::endpoint> boundInterfaces;
std::thread *netWatch = nullptr;
uint16_t defaultPort = 0;
DefaultBindOption defaultBind = UserSupplied;
};
std::unique_ptr<NetworkRebindData> m_rebind;
};
Q_DECLARE_OPERATORS_FOR_FLAGS(FloweeServiceApplication::Options)
#endif