// DragonX Wallet - ImGui Edition // Copyright 2024-2026 The Hush Developers // Released under the GPLv3 #pragma once #include #include #include #include #include #include #include #include #ifdef _WIN32 #include #else #include #endif namespace dragonx { namespace daemon { /** * @brief Manages the embedded dragonxd process * * Handles starting, stopping, and monitoring the dragonxd daemon process. * Ports functionality from Qt version's connection.cpp. */ class EmbeddedDaemon { public: enum class State { Stopped, Starting, Running, Stopping, Error }; using StateCallback = std::function; EmbeddedDaemon(); ~EmbeddedDaemon(); // Non-copyable EmbeddedDaemon(const EmbeddedDaemon&) = delete; EmbeddedDaemon& operator=(const EmbeddedDaemon&) = delete; /** * @brief Start the embedded dragonxd daemon * @param binary_path Path to dragonxd binary (auto-detect if empty) * @return true if process started successfully */ bool start(const std::string& binary_path = ""); /** * @brief Stop the running daemon * @param wait_ms Maximum milliseconds to wait for graceful shutdown */ void stop(int wait_ms = 5000); /** * @brief Check if daemon process is running */ bool isRunning() const; /** * @brief Get current daemon state */ State getState() const { return state_; } /** * @brief Get daemon process memory usage (RSS) in MB * @return Memory usage in MB, or 0 if unavailable */ double getMemoryUsageMB() const; /** * @brief Get last error message */ const std::string& getLastError() const { return last_error_; } /** * @brief Get dragonxd process output (thread-safe copy) */ std::string getOutput() const { std::lock_guard lk(output_mutex_); return process_output_; } /** * @brief Get new output since a given offset (thread-safe). * Returns the new text and updates offset to the current size. */ std::string getOutputSince(size_t& offset) const { std::lock_guard lk(output_mutex_); if (offset >= process_output_.size()) { offset = process_output_.size(); return {}; } std::string result = process_output_.substr(offset); offset = process_output_.size(); return result; } /** * @brief Get current output size (thread-safe, no copy) */ size_t getOutputSize() const { std::lock_guard lk(output_mutex_); return process_output_.size(); } /** * @brief Get last N lines of daemon output (thread-safe snapshot) */ std::vector getRecentLines(int maxLines = 8) const; /** * @brief Extract the latest block height from daemon output (thread-safe). * Parses the last "height=N" from UpdateTip lines without copying * the entire output buffer. Returns -1 if no UpdateTip found. */ int getLastBlockHeight() const { std::lock_guard lk(output_mutex_); // Search backwards from the end for "height=" size_t pos = process_output_.rfind("height="); if (pos == std::string::npos) return -1; pos += 7; // skip "height=" int h = 0; for (size_t i = pos; i < process_output_.size(); ++i) { char c = process_output_[i]; if (c >= '0' && c <= '9') h = h * 10 + (c - '0'); else break; } return h > 0 ? h : -1; } /** * @brief Whether start() detected an existing daemon on the RPC port. * When true the wallet should connect to it instead of showing an error. */ bool externalDaemonDetected() const { return external_daemon_detected_; } /** * @brief Set callback for state changes */ void setStateCallback(StateCallback cb) { state_callback_ = cb; } /** * @brief Find dragonxd binary in standard locations * @return Path to binary, or empty if not found */ static std::string findDaemonBinary(); /** * @brief Check whether anything is listening on the default RPC port. * Useful for detecting an externally-started daemon. */ static bool isRpcPortInUse(); /** * @brief Get the chain parameters for dragonxd * @return Command line arguments for dragonx chain */ static std::vector getChainParams(); /** * @brief Set debug logging categories (appended as -debug= flags on next start) */ void setDebugCategories(const std::set& cats) { debug_categories_ = cats; } const std::set& getDebugCategories() const { return debug_categories_; } /** * @brief Set maximum peer connections (0 = use daemon default) */ void setMaxConnections(int v) { max_connections_ = v; } /** * @brief Request a blockchain rescan on the next daemon start */ void setRescanOnNextStart(bool v) { rescan_on_next_start_ = v; } bool rescanOnNextStart() const { return rescan_on_next_start_.load(); } /** Get number of consecutive daemon crashes (resets on successful start or manual reset) */ int getCrashCount() const { return crash_count_.load(); } /** Reset crash counter (call on successful connection or manual restart) */ void resetCrashCount() { crash_count_ = 0; } private: void setState(State s, const std::string& message = ""); void monitorProcess(); void drainOutput(); // read any pending stdout into process_output_ void appendOutput(const char* data, size_t len); // append + trim (caller holds output_mutex_) bool startProcess(const std::string& binary_path, const std::vector& args); std::atomic state_{State::Stopped}; std::atomic external_daemon_detected_{false}; std::string last_error_; mutable std::mutex output_mutex_; // protects process_output_ std::string process_output_; StateCallback state_callback_; // Process handle #ifdef _WIN32 HANDLE process_handle_ = nullptr; HANDLE stdout_read_ = nullptr; // pipe handle (Linux-style pipe, unused when tailing debug.log) std::string debug_log_path_; // path to daemon's debug.log for output tailing size_t debug_log_offset_ = 0; // read offset into debug.log std::string launch_cmd_; // full command line used to launch daemon (for diagnostics) std::string launch_binary_; // binary path used (for diagnostics) std::string launch_workdir_; // working directory used (for diagnostics) #else pid_t process_pid_ = 0; int stdout_fd_ = -1; #endif std::thread monitor_thread_; std::atomic should_stop_{false}; std::set debug_categories_; int max_connections_ = 0; // 0 = daemon default std::atomic crash_count_{0}; // consecutive crash counter std::atomic rescan_on_next_start_{false}; // -rescan flag for next start }; } // namespace daemon } // namespace dragonx