/** * Copyright (c) 2021, Timothy Stack * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * Neither the name of Timothy Stack nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef lnav_tailer_looper_hh #define lnav_tailer_looper_hh #include #include #include "base/auto_fd.hh" #include "base/auto_pid.hh" #include "base/isc.hh" #include "base/network.tcp.hh" #include "ghc/filesystem.hpp" #include "mapbox/variant.hpp" namespace tailer { class looper : public isc::service { public: void add_remote(const network::path& path, logfile_open_options_base options); void load_preview(int64_t id, const network::path& path); void complete_path(const network::path& path); bool empty() const { return this->l_netlocs_to_paths.empty(); } std::set active_netlocs() const { std::set retval; for (const auto& pair : this->l_remotes) { retval.insert(pair.first); } return retval; } protected: void loop_body() override; void child_finished(std::shared_ptr child) override; private: class host_tailer : public isc::service { public: static Result, std::string> for_host( const std::string& netloc); host_tailer(const std::string& netloc, auto_pid child, auto_fd to_child, auto_fd from_child, auto_fd err_from_child); void open_remote_path(const std::string& path, logfile_open_options_base loo); void load_preview(int64_t id, const std::string& path); void complete_path(const std::string& path); bool is_synced() const { return this->ht_state.is(); } protected: void* run() override; void loop_body() override; void stopped() override; std::chrono::milliseconds compute_timeout( mstime_t current_time) const override; private: static ghc::filesystem::path tmp_path(); std::string get_display_path(const std::string& remote_path) const; struct connected { auto_pid ht_child; auto_fd ht_to_child; auto_fd ht_from_child; std::map c_desired_paths; std::set c_synced_desired_paths; std::map c_child_paths; std::set c_synced_child_paths; bool c_initial_sync_done{false}; auto_pid close() &&; }; struct disconnected {}; struct synced {}; using state_v = mapbox::util::variant; const std::string ht_netloc; std::string ht_uname; const ghc::filesystem::path ht_local_path; std::set ht_active_files; std::vector ht_error_queue; std::thread ht_error_reader; state_v ht_state{disconnected()}; uint64_t ht_cycle_count{0}; }; static void report_error(std::string path, std::string msg); using attempt_time_point = std::chrono::time_point; struct remote_path_queue { attempt_time_point rpq_next_attempt_time{ std::chrono::steady_clock::now()}; std::map rpq_new_paths; std::map rpq_existing_paths; void send_synced_to_main(const std::string& netloc); }; std::map l_netlocs_to_paths; std::map> l_remotes; }; void cleanup_cache(); } // namespace tailer #endif