#include "./interface.h" // avoid exporting symbols for internal functions declared within libsyncthinginternal.h as we // are building a static library here // note: cgo uses `__declspec` despite `-buildmode c-archive`; bug or feature? #ifdef PLATFORM_WINDOWS #ifdef __declspec #undef __declspec #endif #define __declspec(foo) #endif #include "libsyncthinginternal.h" #include using namespace std; namespace LibSyncthing { ///! \cond /*! * \brief Holds the user provided logging callback which is assigned via setLoggingCallback(). */ static LoggingCallback loggingCallback; /*! * \brief An indication whether Syncthing is running. Accessible via isSyncthingRunning(). */ static atomic_bool syncthingRunning; /*! * \brief Converts the specified std::string to a "GoString". */ inline _GoString_ gostr(const string &str) { return _GoString_{ str.data(), static_cast(str.size()) }; } /*! * \brief Converts the specified C-string to a std::string. Takes care of freeing \a str. */ inline string stdstr(char *str) { const string copy(str); free(reinterpret_cast(str)); return copy; } /*! * \brief Converts the specified "GoString" to a std::string. */ inline string stdstr(_GoString_ gostr) { return string(gostr.p, static_cast(gostr.n)); } /*! * \brief Internal callback for logging. Calls the user-provided callback. */ void handleLoggingCallback(int logLevelInt, const char *msg, size_t msgSize) { // ignore callback when no callback registered if (!loggingCallback) { return; } // ignore invalid/unknown log level const auto logLevel = static_cast(logLevelInt); if (logLevel < lowestLogLevel || logLevel > highestLogLevel) { return; } loggingCallback(logLevel, msg, msgSize); } class RunningState { public: RunningState(); ~RunningState(); operator bool() const; private: bool m_invalid; static bool s_loggingInitialized; }; bool RunningState::s_loggingInitialized = false; inline RunningState::RunningState() { // prevent running multiple Syncthing instances at the same time (for now) if ((m_invalid = syncthingRunning.load())) { return; } // initialize logging callback if (!s_loggingInitialized) { ::libst_init_logging(); s_loggingInitialized = true; } ::libst_logging_callback_function = handleLoggingCallback; syncthingRunning.store(true); } inline RunningState::~RunningState() { ::libst_logging_callback_function = nullptr; syncthingRunning.store(false); } inline RunningState::operator bool() const { return !m_invalid; } ///! \endcond /*! * \brief Sets the callback function for logging. It will be called when a new log message becomes available. * \remarks The callback is not necessarily invoked in the same thread it was registered. */ void setLoggingCallback(const LoggingCallback &callback) { loggingCallback = callback; } /*! * \brief Sets the callback function for logging. It will be called when a new log message becomes available. * \remarks The callback is not necessarily invoked in the same thread it was registered. */ void setLoggingCallback(LoggingCallback &&callback) { loggingCallback = callback; } /*! * \brief Runs a Syncthing instance using the specified \a options. * \return Returns the exit code (as usual, zero means no error). * \remark * - Does nothing if Syncthing is already running. * - Blocks the current thread as long as the instance is running. * Use e.g. std::thread(runSyncthing, options) to run it in another thread. */ std::int64_t runSyncthing(const RuntimeOptions &options) { const RunningState runningState; if (!runningState) { return -1; } return ::libst_run_syncthing(gostr(options.configDir), gostr(options.dataDir), gostr(options.guiAddress), gostr(options.guiApiKey), options.flags & RuntimeFlags::Verbose, options.flags & RuntimeFlags::AllowNewerConfig, options.flags & RuntimeFlags::NoDefaultConfig, options.flags & RuntimeFlags::EnsureConfigDirExists, options.flags & RuntimeFlags::EnsureDataDirExists); } /*! * \brief Returns whether Syncthing is already running. * \returns Might be called from any thread. */ bool isSyncthingRunning() { return syncthingRunning.load(); } /*! * \brief Stops Syncthing if it is running; otherwise does nothing. * \returns Returns the Syncthing exit code (as usual, zero means no error). * \remarks * - Might be called from any thread. * - Blocks the current thread until the instance has been stopped. * Use e.g. std::thread(stopSyncthing) to run it in another thread. */ std::int64_t stopSyncthing() { return ::libst_stop_syncthing(); } /*! * \brief Returns the ID of the own device. * \remarks The own device ID is initialized within runSyncthing(). */ string ownDeviceId() { return stdstr(::libst_own_device_id()); } /*! * \brief Returns the Syncthing version. */ string syncthingVersion() { return stdstr(::libst_syncthing_version()); } /*! * \brief Returns a long form of the Syncthing version, including the codename. */ string longSyncthingVersion() { return stdstr(::libst_long_syncthing_version()); } } // namespace LibSyncthing