/** * 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_tailerpp_hh #define lnav_tailerpp_hh #include #include #include "base/result.h" #include "fmt/format.h" #include "mapbox/variant.hpp" #include "sha-256.h" #include "tailer.h" namespace tailer { struct packet_eof {}; struct packet_error { std::string pe_path; std::string pe_msg; }; struct packet_announce { std::string pa_uname; }; struct hash_frag { uint8_t thf_hash[SHA256_BLOCK_SIZE]; bool operator==(const hash_frag& other) const { return memcmp(this->thf_hash, other.thf_hash, sizeof(this->thf_hash)) == 0; } }; struct packet_log { std::string pl_msg; }; struct packet_offer_block { std::string pob_root_path; std::string pob_path; int64_t pob_mtime; int64_t pob_offset; int64_t pob_length; hash_frag pob_hash; }; struct packet_tail_block { std::string ptb_root_path; std::string ptb_path; int64_t ptb_mtime; int64_t ptb_offset; std::vector ptb_bits; }; struct packet_synced { std::string ps_root_path; std::string ps_path; }; struct packet_link { std::string pl_root_path; std::string pl_path; std::string pl_link_value; }; struct packet_preview_error { int64_t ppe_id; std::string ppe_path; std::string ppe_msg; }; struct packet_preview_data { int64_t ppd_id; std::string ppd_path; std::vector ppd_bits; }; struct packet_possible_path { std::string ppp_path; }; using packet = mapbox::util::variant; struct recv_payload_type {}; struct recv_payload_length {}; struct recv_payload_content {}; int readall(int sock, void* buf, size_t len); namespace details { template using void_t = void; template struct has_data : std::false_type {}; template struct has_data().data()))> : std::true_type {}; template::value, bool> = true> uint8_t* get_data(T& t) { return (uint8_t*) t.data(); } template::value, bool> = true> uint8_t* get_data(T& t) { return (uint8_t*) &t; } } // namespace details template struct protocol_recv { static constexpr bool HAS_LENGTH = (PAYLOAD_TYPE == TPPT_STRING) || (PAYLOAD_TYPE == TPPT_BITS); using after_type = typename std::conditional::type; static Result, std::string> create( int fd) { return protocol_recv(fd).read_type(); } Result, std::string> read_type() && { static_assert(std::is_same::value, "read_type() cannot be called in this state"); tailer_packet_payload_type_t payload_type; if (readall(this->pr_fd, &payload_type, sizeof(payload_type)) == -1) { return Err( fmt::format(FMT_STRING("unable to read payload type: {}"), strerror(errno))); } if (payload_type != PAYLOAD_TYPE) { return Err(fmt::format( FMT_STRING("payload-type mismatch, got: {}; expected: {}"), (int) payload_type, PAYLOAD_TYPE)); } return Ok(protocol_recv(this->pr_fd)); } template Result, std::string> read_length(T& data) && { static_assert(std::is_same::value, "read_length() cannot be called in this state"); if (readall(this->pr_fd, &this->pr_length, sizeof(this->pr_length)) == -1) { return Err( fmt::format(FMT_STRING("unable to read content length: {}"), strerror(errno))); } try { data.resize(this->pr_length); } catch (...) { return Err(fmt::format(FMT_STRING("unable to resize data to {}"), this->pr_length)); } return Ok(protocol_recv( this->pr_fd, this->pr_length)); } template Result read_content(T& data) && { static_assert(std::is_same::value, "read_content() cannot be called in this state"); static_assert(!HAS_LENGTH || details::has_data::value, "boo"); if (!HAS_LENGTH) { this->pr_length = sizeof(T); } if (readall(this->pr_fd, details::get_data(data), this->pr_length) == -1) { return Err(fmt::format(FMT_STRING("unable to read content -- {}"), strerror(errno))); } return Ok(); } private: template friend struct protocol_recv; explicit protocol_recv(int fd, int32_t length = 0) : pr_fd(fd), pr_length(length) { } int pr_fd; int32_t pr_length; }; inline Result read_payloads_into(int fd) { tailer_packet_payload_type_t payload_type; readall(fd, &payload_type, sizeof(payload_type)); if (payload_type != TPPT_DONE) { return Err(std::string("not done")); } return Ok(); } template Result read_payloads_into(int fd, std::string& str, Ts&... args); template Result read_payloads_into(int fd, std::vector& bits, Ts&... args) { TRY(TRY(TRY(protocol_recv::create(fd)).read_length(bits)) .read_content(bits)); return read_payloads_into(fd, args...); } template Result read_payloads_into(int fd, hash_frag& thf, Ts&... args) { TRY(TRY(protocol_recv::create(fd)).read_content(thf.thf_hash)); return read_payloads_into(fd, args...); } template Result read_payloads_into(int fd, int64_t& i, Ts&... args) { TRY(TRY(protocol_recv::create(fd)).read_content(i)); return read_payloads_into(fd, args...); } template Result read_payloads_into(int fd, std::string& str, Ts&... args) { TRY(TRY(TRY(protocol_recv::create(fd)).read_length(str)) .read_content(str)); return read_payloads_into(fd, args...); } Result read_packet(int fd); } // namespace tailer #endif