diff options
Diffstat (limited to '')
-rw-r--r-- | src/tailer/tailerpp.hh | 315 |
1 files changed, 315 insertions, 0 deletions
diff --git a/src/tailer/tailerpp.hh b/src/tailer/tailerpp.hh new file mode 100644 index 0000000..0447379 --- /dev/null +++ b/src/tailer/tailerpp.hh @@ -0,0 +1,315 @@ +/** + * 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 <string> +#include <vector> + +#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<uint8_t> 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<uint8_t> ppd_bits; +}; + +struct packet_possible_path { + std::string ppp_path; +}; + +using packet = mapbox::util::variant<packet_eof, + packet_announce, + packet_error, + packet_offer_block, + packet_tail_block, + packet_link, + packet_preview_error, + packet_preview_data, + packet_possible_path, + packet_synced>; + +struct recv_payload_type {}; +struct recv_payload_length {}; +struct recv_payload_content {}; + +int readall(int sock, void* buf, size_t len); + +namespace details { + +template<class...> +using void_t = void; + +template<class, class = void> +struct has_data : std::false_type {}; + +template<class T> +struct has_data<T, decltype(void(std::declval<T&>().data()))> + : std::true_type {}; + +template<typename T, std::enable_if_t<has_data<T>::value, bool> = true> +uint8_t* +get_data(T& t) +{ + return (uint8_t*) t.data(); +} + +template<typename T, std::enable_if_t<!has_data<T>::value, bool> = true> +uint8_t* +get_data(T& t) +{ + return (uint8_t*) &t; +} + +} // namespace details + +template<int PAYLOAD_TYPE, typename EXPECT = recv_payload_type> +struct protocol_recv { + static constexpr bool HAS_LENGTH = (PAYLOAD_TYPE == TPPT_STRING) + || (PAYLOAD_TYPE == TPPT_BITS); + + using after_type = typename std::conditional<HAS_LENGTH, + recv_payload_length, + recv_payload_content>::type; + + static Result<protocol_recv<PAYLOAD_TYPE, after_type>, std::string> create( + int fd) + { + return protocol_recv<PAYLOAD_TYPE>(fd).read_type(); + } + + Result<protocol_recv<PAYLOAD_TYPE, after_type>, std::string> read_type() && + { + static_assert(std::is_same<EXPECT, recv_payload_type>::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<PAYLOAD_TYPE, after_type>(this->pr_fd)); + } + + template<typename T> + Result<protocol_recv<PAYLOAD_TYPE, recv_payload_content>, std::string> + read_length(T& data) && + { + static_assert(std::is_same<EXPECT, recv_payload_length>::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<PAYLOAD_TYPE, recv_payload_content>( + this->pr_fd, this->pr_length)); + } + + template<typename T> + Result<void, std::string> read_content(T& data) && + { + static_assert(std::is_same<EXPECT, recv_payload_content>::value, + "read_content() cannot be called in this state"); + static_assert(!HAS_LENGTH || details::has_data<T>::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<int P, typename E> + 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<void, std::string> +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<typename... Ts> +Result<void, std::string> read_payloads_into(int fd, + std::string& str, + Ts&... args); + +template<typename... Ts> +Result<void, std::string> +read_payloads_into(int fd, std::vector<uint8_t>& bits, Ts&... args) +{ + TRY(TRY(TRY(protocol_recv<TPPT_BITS>::create(fd)).read_length(bits)) + .read_content(bits)); + + return read_payloads_into(fd, args...); +} + +template<typename... Ts> +Result<void, std::string> +read_payloads_into(int fd, hash_frag& thf, Ts&... args) +{ + TRY(TRY(protocol_recv<TPPT_HASH>::create(fd)).read_content(thf.thf_hash)); + + return read_payloads_into(fd, args...); +} + +template<typename... Ts> +Result<void, std::string> +read_payloads_into(int fd, int64_t& i, Ts&... args) +{ + TRY(TRY(protocol_recv<TPPT_INT64>::create(fd)).read_content(i)); + + return read_payloads_into(fd, args...); +} + +template<typename... Ts> +Result<void, std::string> +read_payloads_into(int fd, std::string& str, Ts&... args) +{ + TRY(TRY(TRY(protocol_recv<TPPT_STRING>::create(fd)).read_length(str)) + .read_content(str)); + + return read_payloads_into(fd, args...); +} + +Result<packet, std::string> read_packet(int fd); + +} // namespace tailer + +#endif |