Load server or client args from synergy-config.toml (#7467)

* Load args from .toml file

* Remove .toml files

* Handle _ arg as no key

* Remove dotenv-cpp

* Treat special last arg specially

* Improve restart warning

* Remove rogue include

* Move TOML config loader to new class

* Merge config files into one

* Add override to virtual functions

* Tidy up App decl and add missing overrides

* Add comments to organize function types

* Fixed: config.h doesn't work on Windows

* Move var position

* Improve includes in app classes

* Update ChangeLog

* Restore position of configure_file call

* Remove new entries in .in file
This commit is contained in:
Nick Bolton 2024-09-02 23:48:57 +01:00 committed by GitHub
parent 4b4bfc4bc8
commit b128863c67
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
25 changed files with 501 additions and 174 deletions

1
.gitignore vendored
View file

@ -9,6 +9,7 @@ Brewfile.lock.json
/.cache
# typical developer-created files
synergy-config.toml
.vscode
.DS_Store
*.code-workspace

49
.vscode/launch.json vendored
View file

@ -4,6 +4,7 @@
{
"name": "gui unix",
"type": "lldb",
"cwd": "${workspaceRoot}",
"request": "launch",
"program": "${workspaceFolder}/build/bin/synergy",
"preLaunchTask": "kill-build"
@ -20,6 +21,7 @@
{
"name": "unittests unix",
"type": "lldb",
"cwd": "${workspaceRoot}",
"request": "launch",
"program": "${workspaceFolder}/build/bin/unittests",
"preLaunchTask": "build"
@ -27,7 +29,7 @@
{
"name": "unittests windows",
"type": "cppvsdbg",
"cwd": "${workspaceRoot}/build/bin",
"cwd": "${workspaceRoot}",
"request": "launch",
"program": "${workspaceFolder}/build/bin/unittests",
"internalConsoleOptions": "openOnSessionStart",
@ -36,6 +38,7 @@
{
"name": "integtests unix",
"type": "lldb",
"cwd": "${workspaceRoot}",
"request": "launch",
"program": "${workspaceFolder}/build/bin/integtests",
"preLaunchTask": "build"
@ -43,16 +46,50 @@
{
"name": "integtests windows",
"type": "cppvsdbg",
"cwd": "${workspaceRoot}/build/bin",
"cwd": "${workspaceRoot}",
"request": "launch",
"program": "${workspaceFolder}/build/bin/integtests",
"internalConsoleOptions": "openOnSessionStart",
"preLaunchTask": "build"
},
{
"name": "server unix",
"type": "lldb",
"cwd": "${workspaceRoot}",
"request": "launch",
"program": "${workspaceFolder}/build/bin/synergys",
"preLaunchTask": "build"
},
{
"name": "server windows",
"type": "cppvsdbg",
"cwd": "${workspaceRoot}",
"request": "launch",
"program": "${workspaceFolder}/build/bin/synergys",
"internalConsoleOptions": "openOnSessionStart",
"preLaunchTask": "build"
},
{
"name": "client unix",
"type": "lldb",
"cwd": "${workspaceRoot}",
"request": "launch",
"program": "${workspaceFolder}/build/bin/synergyc",
"preLaunchTask": "build"
},
{
"name": "client windows",
"type": "cppvsdbg",
"cwd": "${workspaceRoot}",
"request": "launch",
"program": "${workspaceFolder}/build/bin/synergyc",
"internalConsoleOptions": "openOnSessionStart",
"preLaunchTask": "build"
},
{
"name": "daemon windows",
"type": "cppvsdbg",
"cwd": "${workspaceRoot}/build/bin",
"cwd": "${workspaceRoot}",
"request": "launch",
"program": "${workspaceFolder}/build/bin/synergyd",
"args": ["-f"],
@ -64,6 +101,12 @@
"type": "cppvsdbg",
"request": "attach",
"processId": "${command:pickProcess}"
},
{
"name": "unix attach",
"type": "lldb",
"request": "attach",
"pid": "${command:pickProcess}"
}
]
}

View file

@ -10,6 +10,7 @@ Enhancements:
- #7461 More robust `choco` install and helpful `install_deps.py` messages
- #7464 Restore `--only-python` arg to only install Python deps
- #7465 Also cut `+` char for `SHORT_VERSION` var used on upload
- #7467 Load server or client args from `synergy-config.toml`
# 1.15.1

View file

@ -10,9 +10,10 @@ macro(configure_libs)
configure_windows_libs()
endif()
config_qt()
configure_qt()
configure_openssl()
configure_coverage()
configure_tomlplusplus()
if(BUILD_TESTS)
configure_gtest()
@ -124,7 +125,9 @@ macro(configure_unix_libs)
set(TIME_WITH_SYS_TIME 1)
set(HAVE_SOCKLEN_T 1)
# For config.h, save the results based on a template (config.h.in).
# Unix only: For config.h, save the results based on a template (config.h.in).
# Note that this won't work on Windows because filenames are not case sensitive,
# and we have header files named "Config.h" (upper case 'C').
configure_file(res/config.h.in ${CMAKE_CURRENT_BINARY_DIR}/src/lib/config.h)
add_definitions(-DSYSAPI_UNIX=1 -DHAVE_CONFIG_H)
@ -361,7 +364,7 @@ macro(configure_windows_libs)
endmacro()
macro(config_qt)
macro(configure_qt)
find_package(
Qt6
@ -523,9 +526,21 @@ macro(configure_wintoast)
file(GLOB WINTOAST_DIR ${CMAKE_SOURCE_DIR}/subprojects/WinToast-*)
if(WINTOAST_DIR)
set(HAVE_WINTOAST true)
add_definitions(-DHAVE_WINTOAST=1)
include_directories(${WINTOAST_DIR}/include)
else()
message(WARNING "WinToast subproject not found")
message(WARNING "Subproject 'WinToast' not found")
endif()
endmacro()
macro(configure_tomlplusplus)
file(GLOB TOMLPLUSPLUS_DIR ${CMAKE_SOURCE_DIR}/subprojects/tomlplusplus-*)
if(TOMLPLUSPLUS_DIR)
set(HAVE_TOMLPLUSPLUS true)
add_definitions(-DHAVE_TOMLPLUSPLUS=1)
include_directories(${TOMLPLUSPLUS_DIR}/include)
else()
message(WARNING "Subproject 'tomlplusplus' not found")
endif()
endmacro()

View file

@ -10,6 +10,9 @@ else
subproject('gtest')
endif
# tomlplusplus: Header-only library
subproject('tomlplusplus')
if host_machine.system() == 'windows'
# WinToast is a niche lib which is not commonly installed,
# so depend only on the subproject.

View file

@ -180,6 +180,3 @@
/* Define if libei ei_device_start_emulating takes a sequence number */
#cmakedefine HAVE_LIBEI_SEQUENCE_NUMBER ${HAVE_LIBEI_SEQUENCE_NUMBER}
/* Define if wintoast is available */
#cmakedefine HAVE_WINTOAST ${HAVE_WINTOAST}

View file

@ -70,7 +70,10 @@ def main():
# either the shell needs to be restarted or the env vars need to be re-sourced.
# Restarting the shell is easier for most people.
if not env.is_linux():
print(f"{colors.WARNING_TEXT} Please restart your shells for new env vars")
print(
f"{colors.WARNING_TEXT} You may need to restart your terminal "
"or IDE to use new env vars"
)
# Useful on Windows, when elevated, Python is opened in a new window and closes
# immediately after the script finishes. This keeps the script window open so that

View file

@ -30,6 +30,8 @@
using namespace synergy::string;
namespace synergy::server {
//
// Config
//
@ -2038,3 +2040,5 @@ XConfigRead::~XConfigRead() _NOEXCEPT {
String XConfigRead::getWhat() const throw() {
return format("XConfigRead", "read error: %{1}", m_error.c_str());
}
} // namespace synergy::server

View file

@ -30,12 +30,15 @@
#include <iosfwd>
namespace synergy::server {
class Config;
class ConfigReadContext;
} // namespace synergy::server
class IEventQueue;
namespace std {
template <> struct iterator_traits<Config> {
template <> struct iterator_traits<synergy::server::Config> {
typedef String value_type;
typedef ptrdiff_t difference_type;
typedef bidirectional_iterator_tag iterator_category;
@ -44,6 +47,8 @@ template <> struct iterator_traits<Config> {
};
}; // namespace std
namespace synergy::server {
//! Server configuration
/*!
This class holds server configuration information. That includes
@ -544,3 +549,5 @@ protected:
private:
String m_error;
};
} // namespace synergy::server

View file

@ -1,6 +1,6 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2012-2016 Symless Ltd.
* Copyright (C) 2012 Symless Ltd.
* Copyright (C) 2002 Chris Schoeneman
*
* This package is free software; you can redistribute it and/or
@ -26,10 +26,7 @@
#include "common/stdexcept.h"
#include "license/License.h"
#include "mt/Thread.h"
#include "net/IDataSocket.h"
#include "net/IListenSocket.h"
#include "net/TCPSocket.h"
#include "net/XSocket.h"
#include "server/ClientListener.h"
#include "server/ClientProxy.h"
#include "server/ClientProxyUnknown.h"
@ -38,12 +35,9 @@
#include "synergy/DropHelper.h"
#include "synergy/FileChunk.h"
#include "synergy/IPlatformScreen.h"
#include "synergy/KeyState.h"
#include "synergy/PacketStreamFilter.h"
#include "synergy/Screen.h"
#include "synergy/StreamChunker.h"
#include "synergy/XScreen.h"
#include "synergy/XSynergy.h"
#include "synergy/option_types.h"
#include "synergy/protocol_types.h"
@ -51,17 +45,16 @@
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <fstream>
#include <sstream>
using namespace synergy::license;
using namespace synergy::server;
//
// Server
//
Server::Server(
Config &config, PrimaryClient *primaryClient, synergy::Screen *screen,
ServerConfig &config, PrimaryClient *primaryClient, synergy::Screen *screen,
IEventQueue *events, synergy::ServerArgs const &args)
: m_mock(false),
m_primaryClient(primaryClient),
@ -265,7 +258,7 @@ Server::~Server() {
removeClient(m_primaryClient);
}
bool Server::setConfig(const Config &config) {
bool Server::setConfig(const ServerConfig &config) {
// refuse configuration if it doesn't include the primary screen
if (!config.isScreen(m_primaryClient->getName())) {
return false;
@ -2086,7 +2079,7 @@ void Server::closeClient(BaseClientProxy *client, const char *msg) {
forceLeaveClient(client);
}
void Server::closeClients(const Config &config) {
void Server::closeClients(const ServerConfig &config) {
// collect the clients that are connected but are being dropped
// from the configuration (or who's canonical name is changing).
typedef std::set<BaseClientProxy *> RemovedClients;

View file

@ -1,6 +1,6 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2012-2016 Symless Ltd.
* Copyright (C) 2012 Symless Ltd.
* Copyright (C) 2002 Chris Schoeneman
*
* This package is free software; you can redistribute it and/or
@ -31,7 +31,6 @@
#include "synergy/ServerArgs.h"
#include "synergy/clipboard_types.h"
#include "synergy/key_types.h"
#include "synergy/languages/LanguageManager.h"
#include "synergy/mouse_types.h"
#include <memory>
@ -51,6 +50,8 @@ class ClientListener;
This class implements the top-level server algorithms for synergy.
*/
class Server : public INode {
using ServerConfig = synergy::server::Config;
public:
//! Lock cursor to screen data
class LockCursorToScreenInfo {
@ -110,8 +111,9 @@ public:
ownership of \p primaryClient.
*/
Server(
Config &config, PrimaryClient *primaryClient, synergy::Screen *screen,
IEventQueue *events, synergy::ServerArgs const &args);
ServerConfig &config, PrimaryClient *primaryClient,
synergy::Screen *screen, IEventQueue *events,
synergy::ServerArgs const &args);
Server(Server const &) = delete;
Server(Server &&) = delete;
~Server();
@ -133,7 +135,7 @@ public:
configuration was accepted (it must include the server's name).
This will disconnect any clients no longer in the configuration.
*/
bool setConfig(const Config &);
bool setConfig(const ServerConfig &);
//! Add a client
/*!
@ -351,7 +353,7 @@ private:
void closeClient(BaseClientProxy *, const char *msg);
// close clients not in \p config
void closeClients(const Config &config);
void closeClients(const ServerConfig &config);
// close all clients whether they've completed the handshake or not,
// except the primary client
@ -422,7 +424,7 @@ private:
SInt32 m_xDelta2, m_yDelta2;
// current configuration
Config *m_config;
ServerConfig *m_config;
// input filter (from m_config);
InputFilter *m_inputFilter;

View file

@ -33,6 +33,7 @@
#include "ipc/IpcMessage.h"
#include "ipc/IpcServerProxy.h"
#include "synergy/ArgsBase.h"
#include "synergy/Config.h"
#include "synergy/XSynergy.h"
#include "synergy/protocol_types.h"
@ -43,8 +44,10 @@
#endif
#include <charconv>
#include <filesystem>
#include <iostream>
#include <sstream>
#include <stdexcept>
#include <stdio.h>
#include <vector>
@ -56,6 +59,10 @@
#include "platform/OSXDragSimulator.h"
#endif
const auto kConfigFilename = "synergy-config.toml";
using namespace synergy;
App *App::s_instance = nullptr;
//
@ -174,8 +181,13 @@ void App::loggingFilterWarning() {
}
void App::initApp(int argc, const char **argv) {
// parse command line
Config config(kConfigFilename, configSection());
if (config.load(argv[0])) {
parseArgs(config.argc(), config.argv());
} else {
parseArgs(argc, argv);
}
ARCH->setProfileDirectory(argsBase().m_profileDirectory);
ARCH->setPluginDirectory(argsBase().m_pluginDirectory);

View file

@ -55,66 +55,43 @@ public:
App &operator=(App const &) = delete;
App &operator=(App &&) = delete;
// Returns args that are common between server and client.
synergy::ArgsBase &argsBase() const { return *m_args; }
// Prints the current compiled version.
virtual void version();
// Prints help specific to client or server.
virtual void help() = 0;
// Parse command line arguments.
virtual void parseArgs(int argc, const char *const *argv) = 0;
int run(int argc, char **argv);
int daemonMainLoop(int, const char **);
virtual void loadConfig() = 0;
virtual bool loadConfig(const String &pathname) = 0;
// A description of the daemon (used only on Windows).
virtual const char *daemonInfo() const = 0;
virtual std::string configSection() const = 0;
// Function pointer for function to exit immediately.
// TODO: this is old C code - use inheritance to normalize
void (*m_bye)(int);
virtual void version();
virtual void setByeFunc(void (*bye)(int)) { m_bye = bye; }
virtual void bye(int error) { m_bye(error); }
virtual IEventQueue *getEvents() const { return m_events; }
ARCH_APP_UTIL &appUtil() { return m_appUtil; }
synergy::ArgsBase &argsBase() const { return *m_args; }
int run(int argc, char **argv);
int daemonMainLoop(int, const char **);
void setupFileLogging();
void loggingFilterWarning();
void initApp(int argc, const char **argv);
void initApp(int argc, char **argv) { initApp(argc, (const char **)argv); }
void setEvents(EventQueue &events) { m_events = &events; }
void setSocketMultiplexer(SocketMultiplexer *sm) { m_socketMultiplexer = sm; }
virtual IArchTaskBarReceiver *taskBarReceiver() const {
return m_taskBarReceiver;
}
SocketMultiplexer *getSocketMultiplexer() const {
return m_socketMultiplexer;
}
static App &instance() {
assert(s_instance != nullptr);
return *s_instance;
}
// If --log was specified in args, then add a file logger.
void setupFileLogging();
// If messages will be hidden (to improve performance), warn user.
void loggingFilterWarning();
// Parses args, sets up file logging, and loads the config.
void initApp(int argc, const char **argv);
// HACK: accept non-const, but make it const anyway
void initApp(int argc, char **argv) { initApp(argc, (const char **)argv); }
ARCH_APP_UTIL &appUtil() { return m_appUtil; }
virtual IArchTaskBarReceiver *taskBarReceiver() const {
return m_taskBarReceiver;
}
virtual void setByeFunc(void (*bye)(int)) { m_bye = bye; }
virtual void bye(int error) { m_bye(error); }
virtual IEventQueue *getEvents() const { return m_events; }
void setSocketMultiplexer(SocketMultiplexer *sm) { m_socketMultiplexer = sm; }
SocketMultiplexer *getSocketMultiplexer() const {
return m_socketMultiplexer;
}
void setEvents(EventQueue &events) { m_events = &events; }
void (*m_bye)(int);
private:
void handleIpcMessage(const Event &, void *);
@ -144,18 +121,24 @@ public:
virtual ~MinimalApp();
// IApp overrides
virtual int standardStartup(int argc, char **argv);
virtual int standardStartup(int argc, char **argv) override;
virtual int runInner(
int argc, char **argv, ILogOutputter *outputter, StartupFunc startup);
virtual void startNode();
virtual int mainLoop();
virtual int foregroundStartup(int argc, char **argv);
virtual synergy::Screen *createScreen();
virtual void loadConfig();
virtual bool loadConfig(const String &pathname);
virtual const char *daemonInfo() const;
virtual const char *daemonName() const;
virtual void parseArgs(int argc, const char *const *argv);
int argc, char **argv, ILogOutputter *outputter,
StartupFunc startup) override;
virtual void startNode() override;
virtual int mainLoop() override;
virtual int foregroundStartup(int argc, char **argv) override;
virtual synergy::Screen *createScreen() override;
virtual void loadConfig() override;
virtual bool loadConfig(const String &pathname) override;
virtual const char *daemonInfo() const override;
virtual const char *daemonName() const override;
virtual void parseArgs(int argc, const char *const *argv) override;
//
// App overrides
//
std::string configSection() const override { return ""; }
private:
Arch m_arch;

View file

@ -298,8 +298,9 @@ bool ArgParser::parseDeprecatedArgs(int argc, const char *const *argv, int &i) {
bool ArgParser::isArg(
int argi, int argc, const char *const *argv, const char *name1,
const char *name2, int minRequiredParameters) {
if ((name1 != nullptr && strcmp(argv[argi], name1) == 0) ||
(name2 != nullptr && strcmp(argv[argi], name2) == 0)) {
const auto match1 = (name1 != nullptr && strcmp(argv[argi], name1) == 0);
const auto match2 = (name2 != nullptr && strcmp(argv[argi], name2) == 0);
if (match1 || match2) {
// match. check args left.
if (argi + minRequiredParameters >= argc) {
LOG(

View file

@ -1,6 +1,6 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2012-2016 Symless Ltd.
* Copyright (C) 2012 Symless Ltd.
* Copyright (C) 2002 Chris Schoeneman
*
* This package is free software; you can redistribute it and/or
@ -21,16 +21,11 @@
#include "arch/Arch.h"
#include "arch/IArchTaskBarReceiver.h"
#include "base/Event.h"
#include "base/EventQueue.h"
#include "base/IEventQueue.h"
#include "base/Log.h"
#include "base/String.h"
#include "base/TMethodEventJob.h"
#include "base/TMethodJob.h"
#include "base/log_outputters.h"
#include "client/Client.h"
#include "common/constants.h"
#include "mt/Thread.h"
#include "net/InverseSockets/InverseSocketFactory.h"
#include "net/NetworkAddress.h"
#include "net/SocketMultiplexer.h"
@ -50,18 +45,27 @@
#if WINAPI_MSWINDOWS
#include "platform/MSWindowsScreen.h"
#endif
#if WINAPI_XWINDOWS
#include "platform/XWindowsScreen.h"
#endif
#if WINAPI_LIBEI
#include "platform/EiScreen.h"
#endif
#if WINAPI_CARBON
#include "platform/OSXDragSimulator.h"
#include "platform/OSXScreen.h"
#endif
#if defined(__APPLE__)
#include "platform/OSXDragSimulator.h"
#if defined(WINAPI_XWINDOWS) or defined(WINAPI_LIBEI)
#include "platform/wayland.h"
#endif
#if defined(MAC_OS_X_VERSION_10_7)
#include "base/TMethodJob.h"
#include "mt/Thread.h"
#endif
#include <memory>

View file

@ -1,6 +1,6 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2012-2016 Symless Ltd.
* Copyright (C) 2012 Symless Ltd.
* Copyright (C) 2002 Chris Schoeneman
*
* This package is free software; you can redistribute it and/or
@ -39,29 +39,35 @@ public:
IEventQueue *events, CreateTaskBarReceiverFunc createTaskBarReceiver);
virtual ~ClientApp();
// Parse client specific command line arguments.
void parseArgs(int argc, const char *const *argv);
//
// IApp overrides
//
// Prints help specific to client.
void help();
// Returns arguments that are common and for client.
synergy::ClientArgs &args() const {
return (synergy::ClientArgs &)argsBase();
}
const char *daemonName() const;
const char *daemonInfo() const;
// TODO: move to server only (not supported on client)
void loadConfig() {}
bool loadConfig(const String &pathname) { return false; }
int foregroundStartup(int argc, char **argv);
int standardStartup(int argc, char **argv);
void parseArgs(int argc, const char *const *argv) override;
void help() override;
const char *daemonName() const override;
const char *daemonInfo() const override;
void loadConfig() override {}
bool loadConfig(const String &pathname) override { return false; }
int foregroundStartup(int argc, char **argv) override;
int standardStartup(int argc, char **argv) override;
int runInner(
int argc, char **argv, ILogOutputter *outputter, StartupFunc startup);
synergy::Screen *createScreen();
int argc, char **argv, ILogOutputter *outputter,
StartupFunc startup) override;
synergy::Screen *createScreen() override;
int mainLoop() override;
void startNode() override;
//
// App overrides
//
std::string configSection() const override { return "client"; }
//
// Regular functions
//
void updateStatus();
void updateStatus(const String &msg);
void resetRestartTimeout();
@ -81,13 +87,18 @@ public:
void closeClient(Client *client);
bool startClient();
void stopClient();
int mainLoop();
void startNode();
Client *getClientPtr() { return m_client; }
synergy::ClientArgs &args() const {
return (synergy::ClientArgs &)argsBase();
}
//
// Static functions
//
static ClientApp &instance() { return (ClientApp &)App::instance(); }
Client *getClientPtr() { return m_client; }
private:
ISocketFactory *getSocketFactory() const;

110
src/lib/synergy/Config.cpp Normal file
View file

@ -0,0 +1,110 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2024 Symless Ltd.
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file LICENSE that should have accompanied this file.
*
* This package 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 "Config.h"
#include "base/Log.h"
#include <cstring>
#include <filesystem>
#include <string>
#include <vector>
#if HAVE_TOMLPLUSPLUS
#include <toml++/toml.hpp>
#endif
namespace synergy {
Config::Config(const std::string &filename, const std::string &section)
: m_filename(filename),
m_section(section) {}
const char *const *Config::argv() const { return m_argv.data(); }
int Config::argc() const { return static_cast<int>(m_argv.size()); }
bool Config::load(const std::string &firstArg) {
#if HAVE_TOMLPLUSPLUS
m_args.push_back(firstArg);
if (m_filename.empty() || !std::filesystem::exists(m_filename)) {
LOG((CLOG_DEBUG "no config file at: %s", m_filename.c_str()));
return false;
}
toml::table configTable;
try {
LOG((CLOG_INFO "loading config file: %s", m_filename.c_str()));
configTable = toml::parse_file(m_filename);
} catch (const toml::parse_error &err) {
LOG((CLOG_ERR "toml parse error: %s", err.what()));
throw ParseError();
}
if (!configTable.contains(m_section)) {
LOG((CLOG_WARN "no %s section found in config file", m_section.c_str()));
return false;
}
const auto &section = configTable[m_section];
const auto args = section["args"];
if (!args.is_table()) {
LOG((CLOG_WARN "no args table found in config file"));
return false;
}
std::string specialLastArg = "";
const auto &table = *(args.as_table());
for (const auto &pair : table) {
const auto &key = pair.first;
if (key.str() == "_last") {
specialLastArg = pair.second.as_string()->get();
continue;
}
m_args.push_back("--" + std::string(key.str()));
if (pair.second.is_string()) {
const auto value = pair.second.as_string()->get();
m_args.push_back(value);
}
}
if (!specialLastArg.empty()) {
m_args.push_back(specialLastArg);
}
if (m_args.empty()) {
LOG((CLOG_WARN "no args loaded from config file"));
return false;
}
for (const auto &arg : m_args) {
m_argv.push_back(arg.c_str());
}
return true;
#else
LOG((CLOG_WARN "toml++ not available, config file not loaded"));
return false;
#endif // HAVE_TOMLPLUSPLUS
}
} // namespace synergy

53
src/lib/synergy/Config.h Normal file
View file

@ -0,0 +1,53 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2024 Symless Ltd.
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file LICENSE that should have accompanied this file.
*
* This package 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/>.
*/
#pragma once
#include <stdexcept>
#include <string>
#include <vector>
namespace synergy {
//! App configuration
/*!
Parses a configuration file describing start args and potentially other
configuration options in future. The configuration file is in TOML format.
Initially this class was created to as a developer convenience; it is a
convenient place to specify args without needing to fiddle with IDE configs.
*/
class Config {
class ParseError : public std::runtime_error {
public:
explicit ParseError() : std::runtime_error("failed to parse config file") {}
};
public:
explicit Config(const std::string &filename, const std::string &section);
bool load(const std::string &firstArg);
const char *const *argv() const;
int argc() const;
private:
std::string m_filename;
std::string m_section;
std::vector<std::string> m_args;
std::vector<const char *> m_argv;
};
} // namespace synergy

View file

@ -1,6 +1,6 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2012-2016 Symless Ltd.
* Copyright (C) 2012 Symless Ltd.
* Copyright (C) 2002 Chris Schoeneman
*
* This package is free software; you can redistribute it and/or
@ -19,22 +19,17 @@
#include "synergy/ServerApp.h"
#include "arch/Arch.h"
#include "base/EventQueue.h"
#include "base/FunctionEventJob.h"
#include "base/IEventQueue.h"
#include "base/Log.h"
#include "base/Path.h"
#include "base/TMethodEventJob.h"
#include "base/TMethodJob.h"
#include "base/log_outputters.h"
#include "common/constants.h"
#include "net/InverseSockets/InverseSocketFactory.h"
#include "net/SocketMultiplexer.h"
#include "net/TCPSocketFactory.h"
#include "net/XSocket.h"
#include "platform/wayland.h"
#include "server/ClientListener.h"
#include "server/ClientProxy.h"
#include "server/Config.h"
#include "server/PrimaryClient.h"
#include "server/Server.h"
#include "synergy/App.h"
@ -51,18 +46,27 @@
#if WINAPI_MSWINDOWS
#include "platform/MSWindowsScreen.h"
#endif
#if WINAPI_XWINDOWS
#include "platform/XWindowsScreen.h"
#endif
#if WINAPI_LIBEI
#include "platform/EiScreen.h"
#endif
#if WINAPI_CARBON
#include "platform/OSXDragSimulator.h"
#include "platform/OSXScreen.h"
#endif
#if defined(__APPLE__)
#include "platform/OSXDragSimulator.h"
#if defined(WINAPI_XWINDOWS) or defined(WINAPI_LIBEI)
#include "platform/wayland.h"
#endif
#if defined(MAC_OS_X_VERSION_10_7)
#include "base/TMethodJob.h"
#include "mt/Thread.h"
#endif
#include <fstream>
@ -70,6 +74,8 @@
#include <sstream>
#include <stdio.h>
using namespace synergy::server;
//
// ServerApp
//
@ -88,6 +94,7 @@ ServerApp::ServerApp(
ServerApp::~ServerApp() {}
void ServerApp::parseArgs(int argc, const char *const *argv) {
ArgParser argParser(this);
bool result = argParser.parseServerArgs(args(), argc, argv);
@ -184,16 +191,18 @@ void ServerApp::reloadConfig(const Event &, void *) {
void ServerApp::loadConfig() {
bool loaded = false;
std::string path;
// load the config file, if specified
if (!args().m_configFile.empty()) {
loaded = loadConfig(args().m_configFile);
path = args().m_configFile;
loaded = loadConfig(path);
}
// load the default configuration if no explicit file given
else {
// get the user's home directory
String path = ARCH->getUserDirectory();
path = ARCH->getUserDirectory();
if (!path.empty()) {
// complete path
path = ARCH->concatPath(path, USR_CONFIG_NAME);
@ -218,7 +227,9 @@ void ServerApp::loadConfig() {
}
if (!loaded) {
LOG((CLOG_CRIT "%s: no configuration available", args().m_pname));
LOG(
(CLOG_CRIT "%s: failed to load config: %s", args().m_pname,
path.c_str()));
m_bye(kExitConfig);
}
}
@ -607,7 +618,8 @@ ClientListener *ServerApp::openClientListener(const NetworkAddress &address) {
return listen;
}
Server *ServerApp::openServer(Config &config, PrimaryClient *primaryClient) {
Server *
ServerApp::openServer(ServerConfig &config, PrimaryClient *primaryClient) {
Server *server =
new Server(config, primaryClient, m_serverScreen, m_events, args());
try {

View file

@ -1,6 +1,6 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2012-2016 Symless Ltd.
* Copyright (C) 2012 Symless Ltd.
* Copyright (C) 2002 Chris Schoeneman
*
* This package is free software; you can redistribute it and/or
@ -25,9 +25,6 @@
#include "net/NetworkAddress.h"
#include "server/Config.h"
#include "synergy/App.h"
#include "synergy/ArgsBase.h"
#include <map>
enum EServerState {
kUninitialized,
@ -53,31 +50,43 @@ class ServerArgs;
}
class ServerApp : public App {
using ServerConfig = synergy::server::Config;
public:
ServerApp(
IEventQueue *events, CreateTaskBarReceiverFunc createTaskBarReceiver);
virtual ~ServerApp();
// Parse server specific command line arguments.
void parseArgs(int argc, const char *const *argv);
//
// IApp overrides
//
// Prints help specific to server.
void help();
void parseArgs(int argc, const char *const *argv) override;
void help() override;
const char *daemonName() const override;
const char *daemonInfo() const override;
void loadConfig() override;
bool loadConfig(const String &pathname) override;
synergy::Screen *createScreen() override;
int mainLoop() override;
int runInner(
int argc, char **argv, ILogOutputter *outputter,
StartupFunc startup) override;
int standardStartup(int argc, char **argv) override;
int foregroundStartup(int argc, char **argv) override;
void startNode() override;
// Returns arguments that are common and for server.
synergy::ServerArgs &args() const {
return (synergy::ServerArgs &)argsBase();
}
//
// App overrides
//
const char *daemonName() const;
const char *daemonInfo() const;
std::string configSection() const override { return "server"; }
// TODO: Document these functions.
static void reloadSignalHandler(Arch::ESignal, void *);
//
// Regular functions
//
void reloadConfig(const Event &, void *);
void loadConfig();
bool loadConfig(const String &pathname);
void forceReconnect(const Event &, void *);
void resetServer(const Event &, void *);
void handleClientConnected(const Event &, void *vlistener);
@ -94,25 +103,31 @@ public:
bool initServer();
void retryHandler(const Event &, void *);
synergy::Screen *openServerScreen();
synergy::Screen *createScreen();
PrimaryClient *openPrimaryClient(const String &name, synergy::Screen *screen);
void handleScreenError(const Event &, void *);
void handleSuspend(const Event &, void *);
void handleResume(const Event &, void *);
ClientListener *openClientListener(const NetworkAddress &address);
Server *openServer(Config &config, PrimaryClient *primaryClient);
Server *openServer(ServerConfig &config, PrimaryClient *primaryClient);
void handleNoClients(const Event &, void *);
bool startServer();
int mainLoop();
int runInner(
int argc, char **argv, ILogOutputter *outputter, StartupFunc startup);
int standardStartup(int argc, char **argv);
int foregroundStartup(int argc, char **argv);
void startNode();
Server *getServerPtr() { return m_server; }
synergy::ServerArgs &args() const {
return (synergy::ServerArgs &)argsBase();
}
//
// Static functions
//
static void reloadSignalHandler(Arch::ESignal, void *);
static ServerApp &instance() { return (ServerApp &)App::instance(); }
Server *getServerPtr() { return m_server; }
private:
void handleScreenSwitched(const Event &, void *data);
ISocketFactory *getSocketFactory() const;
NetworkAddress getAddress(const NetworkAddress &address) const;
Server *m_server;
EServerState m_serverState;
@ -121,11 +136,6 @@ public:
ClientListener *m_listener;
EventQueueTimer *m_timer;
NetworkAddress *m_synergyAddress;
private:
void handleScreenSwitched(const Event &, void *data);
ISocketFactory *getSocketFactory() const;
NetworkAddress getAddress(const NetworkAddress &address) const;
};
// configuration file name

View file

@ -27,6 +27,7 @@ namespace synergy {
class ServerArgs : public ArgsBase {
using License = synergy::license::License;
using Config = synergy::server::Config;
public:
ServerArgs();

View file

@ -0,0 +1,48 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2024 Symless Ltd.
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file LICENSE that should have accompanied this file.
*
* This package 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 "synergy/Config.h"
#include <filesystem>
#include <fstream>
#include <gtest/gtest.h>
using namespace synergy;
const auto kTestFilename = "tmp/test/test.toml";
TEST(ConfigTests, LoadConfigFile) {
std::ofstream testFile(kTestFilename);
testFile << "[test.args]\n"
R"(test-arg = "test opt")";
testFile.close();
try {
Config config(kTestFilename, "test");
ASSERT_TRUE(config.load("test"));
ASSERT_EQ(config.argc(), 3);
ASSERT_STREQ(config.argv()[0], "test");
ASSERT_STREQ(config.argv()[1], "--test-arg");
ASSERT_STREQ(config.argv()[2], "test opt");
} catch (const std::exception &e) {
FAIL() << e.what();
}
std::filesystem::remove(kTestFilename);
}

View file

@ -41,4 +41,5 @@ public:
MOCK_METHOD(int, mainLoop, (), (override));
MOCK_METHOD(int, foregroundStartup, (int, char **), (override));
MOCK_METHOD(synergy::Screen *, createScreen, (), (override));
MOCK_METHOD(std::string, configSection, (), (const, override));
};

View file

@ -6,6 +6,8 @@
/libportal
/gi-docgen
/munit
/dotenv-cpp
/tomlplusplus-*
# Added by dependencies.
/gi-docgen.wrap

View file

@ -0,0 +1,10 @@
[wrap-file]
directory = tomlplusplus-3.4.0
source_url = https://github.com/marzer/tomlplusplus/archive/v3.4.0.tar.gz
source_filename = tomlplusplus-3.4.0.tar.gz
source_hash = 8517f65938a4faae9ccf8ebb36631a38c1cadfb5efa85d9a72e15b9e97d25155
source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/tomlplusplus_3.4.0-1/tomlplusplus-3.4.0.tar.gz
wrapdb_version = 3.4.0-1
[provide]
dependency_names = tomlplusplus