1
1
mirror of https://github.com/tstack/lnav.git synced 2024-09-11 13:05:51 +03:00

[arc] report errors when opening archives

This commit is contained in:
Timothy Stack 2020-10-31 22:19:41 -07:00
parent 563fa94d39
commit e4ea9ca584
14 changed files with 664 additions and 95 deletions

View File

@ -390,6 +390,10 @@ add_library(diag STATIC
base/result.h
styling.hh
ring_span.hh
safe/accessmode.h
safe/defaulttypes.h
safe/mutableref.h
safe/safe.h
sequence_sink.hh
shlex.hh
simdutf8check.h

View File

@ -322,6 +322,10 @@ noinst_HEADERS = \
regexp_vtab.hh \
relative_time.hh \
ring_span.hh \
safe/accessmode.h \
safe/defaulttypes.h \
safe/mutableref.h \
safe/safe.h \
sequence_matcher.hh \
sequence_sink.hh \
session_data.hh \

View File

@ -134,10 +134,12 @@ filename_to_tmp_path(const std::string &filename)
}
#if HAVE_ARCHIVE_H
static int
copy_data(const ghc::filesystem::path &path,
static walk_result_t
copy_data(const std::string& filename,
struct archive *ar,
struct archive_entry *entry,
struct archive *aw,
const ghc::filesystem::path &entry_path,
struct extract_progress *ep)
{
int r;
@ -148,32 +150,38 @@ copy_data(const ghc::filesystem::path &path,
for (;;) {
r = archive_read_data_block(ar, &buff, &size, &offset);
if (r == ARCHIVE_EOF) {
return (ARCHIVE_OK);
return Ok();
}
if (r < ARCHIVE_OK) {
return (r);
if (r != ARCHIVE_OK) {
return Err(fmt::format("failed to read file: {} >> {} -- {}",
filename,
archive_entry_pathname_utf8(entry),
archive_error_string(ar)));
}
r = archive_write_data_block(aw, buff, size, offset);
if (r < ARCHIVE_OK) {
log_error("%s", archive_error_string(aw));
return (r);
if (r != ARCHIVE_OK) {
return Err(fmt::format("failed to write file: {} -- {}",
entry_path.string(),
archive_error_string(aw)));
}
total += size;
ep->ep_out_size.fetch_add(size);
if ((total - last_space_check) > (1024 * 1024)) {
auto tmp_space = ghc::filesystem::space(path);
auto tmp_space = ghc::filesystem::space(entry_path);
if (tmp_space.available < MIN_FREE_SPACE) {
log_error("available space too low: %lld", tmp_space.available);
return ARCHIVE_FATAL;
return Err(fmt::format(
"{} -- available space too low: %lld",
entry_path.string(),
tmp_space.available));
}
}
}
}
static void extract(const std::string &filename, const extract_cb &cb)
static walk_result_t extract(const std::string &filename, const extract_cb &cb)
{
static int FLAGS = ARCHIVE_EXTRACT_TIME
| ARCHIVE_EXTRACT_PERM
@ -191,7 +199,7 @@ static void extract(const std::string &filename, const extract_cb &cb)
ghc::filesystem::last_write_time(
done_path, std::chrono::system_clock::now());
log_debug("already extracted! %s", done_path.c_str());
return;
return Ok();
}
auto_mem<archive> arc(archive_free);
@ -204,7 +212,9 @@ static void extract(const std::string &filename, const extract_cb &cb)
archive_write_disk_set_options(ext, FLAGS);
archive_write_disk_set_standard_lookup(ext);
if (archive_read_open_filename(arc, filename.c_str(), 10240) != ARCHIVE_OK) {
return;
return Err(fmt::format("unable to open archive: {} -- {}",
filename,
archive_error_string(arc)));
}
log_info("extracting %s to %s",
@ -217,11 +227,10 @@ static void extract(const std::string &filename, const extract_cb &cb)
log_info("all done");
break;
}
if (r < ARCHIVE_OK) {
log_error("%s", archive_error_string(arc));
}
if (r < ARCHIVE_WARN) {
return;
if (r != ARCHIVE_OK) {
return Err(fmt::format("unable to read entry header: {} -- {}",
filename,
archive_error_string(arc)));
}
auto_mem<archive_entry> wentry(archive_entry_free);
@ -237,23 +246,18 @@ static void extract(const std::string &filename, const extract_cb &cb)
wentry, S_IRUSR | (S_ISDIR(entry_mode) ? S_IXUSR|S_IWUSR : 0));
r = archive_write_header(ext, wentry);
if (r < ARCHIVE_OK) {
log_error("%s", archive_error_string(ext));
return Err(fmt::format("unable to write entry: {} -- {}",
entry_path.string(),
archive_error_string(ext)));
}
else if (archive_entry_size(entry) > 0) {
r = copy_data(tmp_path, arc, ext, prog);
if (r < ARCHIVE_OK) {
log_error("%s", archive_error_string(ext));
}
if (r < ARCHIVE_WARN) {
return;
}
TRY(copy_data(filename, arc, entry, ext, entry_path, prog));
}
r = archive_write_finish_entry(ext);
if (r < ARCHIVE_OK) {
log_error("%s", archive_error_string(ext));
}
if (r < ARCHIVE_WARN) {
return;
if (r != ARCHIVE_OK) {
return Err(fmt::format("unable to finish entry: {} -- {}",
entry_path.string(),
archive_error_string(ext)));
}
}
archive_read_close(arc);
@ -261,21 +265,21 @@ static void extract(const std::string &filename, const extract_cb &cb)
auto_fd(open(done_path.c_str(), O_CREAT | O_WRONLY, 0600));
// TODO return errors
return Ok();
}
#endif
void walk_archive_files(const std::string &filename,
const extract_cb &cb,
const std::function<void(
const fs::path&,
const fs::directory_entry &)>& callback)
walk_result_t walk_archive_files(
const std::string &filename,
const extract_cb &cb,
const std::function<void(
const fs::path&,
const fs::directory_entry &)>& callback)
{
#if HAVE_ARCHIVE_H
auto tmp_path = filename_to_tmp_path(filename);
extract(filename, cb);
TRY(extract(filename, cb));
for (const auto& entry : fs::recursive_directory_iterator(tmp_path)) {
if (!entry.is_regular_file()) {
@ -284,6 +288,10 @@ void walk_archive_files(const std::string &filename,
callback(tmp_path, entry);
}
return Ok();
#else
return Err("not compiled with libarchive");
#endif
}

View File

@ -37,6 +37,7 @@
#include <functional>
#include <utility>
#include "base/result.h"
#include "ghc/filesystem.hpp"
namespace archive_manager {
@ -59,11 +60,14 @@ bool is_archive(const std::string &filename);
ghc::filesystem::path filename_to_tmp_path(const std::string &filename);
void walk_archive_files(const std::string &filename,
const extract_cb &cb,
const std::function<void(
const ghc::filesystem::path &,
const ghc::filesystem::directory_entry &)> &);
using walk_result_t = Result<void, std::string>;
walk_result_t walk_archive_files(
const std::string &filename,
const extract_cb &cb,
const std::function<void(
const ghc::filesystem::path &,
const ghc::filesystem::directory_entry &)> &);
}
#endif

View File

@ -629,16 +629,22 @@ struct Storage<void, E> {
}
}
template<typename U>
template<typename U,
typename = std::enable_if_t<!std::is_same<U, void>::value>>
const U& get() const {
return *reinterpret_cast<const U *>(&storage_);
}
template<typename U>
U& get() {
template<typename U,
typename = std::enable_if_t<!std::is_same<U, void>::value>>
typename std::add_lvalue_reference<U>::type get() {
return *reinterpret_cast<U *>(&storage_);
}
template<typename U,
typename = std::enable_if_t<std::is_same<U, void>::value>>
void get() {}
type storage_;
bool initialized_;
};

View File

@ -258,8 +258,8 @@ files_overlay_source::list_value_for_overlay(const listview_curses &lv, int y,
{
if (y == 0) {
auto &fc = lnav_data.ld_active_files;
auto &sp = fc.fc_progress;
std::lock_guard<std::mutex> guard(sp->sp_mutex);
auto &fc_prog = fc.fc_progress;
safe::WriteAccess<safe_scan_progress> sp(*fc_prog);
if (!sp->sp_extractions.empty()) {
static char PROG[] = "-\\|/";

View File

@ -578,15 +578,9 @@ void rebuild_indexes()
if ((!lf->exists() || lf->is_closed())) {
log_info("closed log file: %s", lf->get_filename().c_str());
if (!lf->is_valid_filename()) {
lnav_data.ld_active_files.fc_file_names.erase(lf->get_filename());
}
lnav_data.ld_text_source.remove(lf);
lnav_data.ld_log_source.remove_file(lf);
file_iter = lnav_data.ld_active_files.fc_files.erase(file_iter);
lnav_data.ld_active_files.fc_files_generation += 1;
lnav_data.ld_active_files.regenerate_unique_file_names();
lnav_data.ld_active_files.close_file(lf);
}
else {
++file_iter;
@ -964,9 +958,16 @@ struct same_file {
const struct stat &sf_stat;
};
static std::mutex REALPATH_CACHE_MUTEX;
static std::unordered_map<std::string, std::string> REALPATH_CACHE;
void file_collection::close_file(const std::shared_ptr<logfile> &lf)
{
if (!lf->is_valid_filename()) {
if (lf->is_valid_filename()) {
std::lock_guard<std::mutex> lg(REALPATH_CACHE_MUTEX);
REALPATH_CACHE.erase(lf->get_filename());
} else {
this->fc_file_names.erase(lf->get_filename());
}
auto file_iter = find(this->fc_files.begin(),
@ -1092,10 +1093,16 @@ file_collection::watch_logfile(const string& filename, logfile_open_options &loo
if (this->fc_other_files.find(filename) != this->fc_other_files.end()) {
return make_ready_future(retval);
}
return std::async(std::launch::async, [filename, &loo, prog=this->fc_progress]() {
file_format_t ff = detect_file_format(filename);
return std::async(std::launch::async, [filename, &loo, prog=this->fc_progress, errs=this->fc_name_to_errors]() {
file_collection retval;
if (errs.find(filename) != errs.end()) {
// The file is broken, no reason to try and reopen
return retval;
}
file_format_t ff = detect_file_format(filename);
switch (ff) {
case file_format_t::FF_SQLITE_DB:
attach_sqlite_db(lnav_data.ld_db.in(), filename);
@ -1103,15 +1110,23 @@ file_collection::watch_logfile(const string& filename, logfile_open_options &loo
break;
case file_format_t::FF_ARCHIVE: {
retval.fc_other_files[filename] = "Archive";
archive_manager::walk_archive_files(
filename,
[prog](const auto& path, const auto total) {
std::lock_guard<std::mutex> guard(prog->sp_mutex);
prog->sp_extractions.clear();
prog->sp_extractions.emplace_back(path, total);
nonstd::optional<std::list<archive_manager::extract_progress>::iterator>
prog_iter_opt;
return &prog->sp_extractions.back();
auto res = archive_manager::walk_archive_files(
filename,
[prog, &prog_iter_opt](
const auto& path, const auto total) {
safe::WriteAccess<safe_scan_progress> sp(*prog);
prog_iter_opt | [&sp](auto prog_iter) {
sp->sp_extractions.erase(prog_iter);
};
auto prog_iter = sp->sp_extractions.emplace(
sp->sp_extractions.begin(), path, total);
prog_iter_opt = prog_iter;
return &(*prog_iter);
},
[&filename, &retval](const auto& tmp_path,
const auto& entry) {
@ -1134,7 +1149,6 @@ file_collection::watch_logfile(const string& filename, logfile_open_options &loo
log_info("adding file from archive: %s/%s",
filename.c_str(),
entry.path().c_str());
// TODO add some heuristics for hiding files
retval.fc_file_names[entry.path().string()] =
logfile_open_options()
.with_filename(custom_name.string())
@ -1142,9 +1156,18 @@ file_collection::watch_logfile(const string& filename, logfile_open_options &loo
.with_non_utf_visibility(false)
.with_visible_size_limit(128 * 1024);
});
if (res.isErr()) {
log_error("archive extraction failed: %s",
res.unwrapErr().c_str());
retval.clear();
retval.fc_name_to_errors[filename] = res.unwrapErr();
} else {
retval.fc_other_files[filename] = "Archive";
}
{
std::lock_guard<std::mutex> guard(prog->sp_mutex);
prog->sp_extractions.clear();
prog_iter_opt | [&prog](auto prog_iter) {
prog->writeAccess()->sp_extractions.erase(prog_iter);
};
}
break;
}
@ -1189,18 +1212,25 @@ file_collection::watch_logfile(const string& filename, logfile_open_options &loo
* @param path The glob pattern to expand.
* @param required Passed to watch_logfile.
*/
file_collection file_collection::expand_filename(const string& path, logfile_open_options &loo, bool required)
void file_collection::expand_filename(future_queue<file_collection> &fq,
const string& path,
logfile_open_options &loo,
bool required)
{
static_root_mem<glob_t, globfree> gl;
file_collection retval;
{
std::lock_guard<std::mutex> lg(REALPATH_CACHE_MUTEX);
if (REALPATH_CACHE.find(path) != REALPATH_CACHE.end()) {
return;
}
}
if (is_url(path.c_str())) {
return retval;
return;
}
else if (glob(path.c_str(), GLOB_NOCHECK, nullptr, gl.inout()) == 0) {
future_queue<file_collection> fq([&retval](auto& fc) {
retval.merge(fc);
});
int lpc;
if (gl->gl_pathc == 1 /*&& gl.gl_matchc == 0*/) {
@ -1216,22 +1246,34 @@ file_collection file_collection::expand_filename(const string& path, logfile_ope
strcmp(path.c_str(), gl->gl_pathv[0]) != 0) {
required = false;
}
for (lpc = 0; lpc < (int)gl->gl_pathc; lpc++) {
auto_mem<char> abspath;
if ((abspath = realpath(gl->gl_pathv[lpc], nullptr)) == nullptr) {
if (required) {
fprintf(stderr, "Cannot find file: %s -- %s",
gl->gl_pathv[lpc], strerror(errno));
std::lock_guard<std::mutex> lg(REALPATH_CACHE_MUTEX);
for (lpc = 0; lpc < (int)gl->gl_pathc; lpc++) {
auto path_str = std::string(gl->gl_pathv[lpc]);
auto iter = REALPATH_CACHE.find(path_str);
if (iter == REALPATH_CACHE.end()) {
auto_mem<char> abspath;
if ((abspath = realpath(gl->gl_pathv[lpc], nullptr)) ==
nullptr) {
if (required) {
fprintf(stderr, "Cannot find file: %s -- %s",
gl->gl_pathv[lpc], strerror(errno));
}
continue;
} else {
auto p = REALPATH_CACHE.emplace(path_str, abspath);
iter = p.first;
}
}
else if (required || access(abspath.in(), R_OK) == 0) {
fq.push_back(watch_logfile(abspath.in(), loo, required));
if (required || access(iter->second.c_str(), R_OK) == 0) {
fq.push_back(watch_logfile(iter->second, loo, required));
}
}
}
return retval;
}
file_collection file_collection::rescan_files(bool required)
@ -1243,11 +1285,11 @@ file_collection file_collection::rescan_files(bool required)
for (auto& pair : this->fc_file_names) {
if (pair.second.loo_fd == -1) {
retval.merge(this->expand_filename(pair.first, pair.second, required));
this->expand_filename(fq, pair.first, pair.second, required);
if (lnav_data.ld_flags & LNF_ROTATED) {
string path = pair.first + ".*";
retval.merge(this->expand_filename(path, pair.second, false));
this->expand_filename(fq, path, pair.second, false);
}
} else {
fq.push_back(watch_logfile(pair.first, pair.second, required));
@ -2939,7 +2981,7 @@ int main(int argc, char *argv[])
if (!lnav_data.ld_active_files.fc_name_to_errors.empty()) {
for (const auto& pair : lnav_data.ld_active_files.fc_name_to_errors) {
fprintf(stderr,
"error: unable to read file: %s -- %s\n",
"error: unable to open file: %s -- %s\n",
pair.first.c_str(),
pair.second.c_str());
}

View File

@ -43,6 +43,8 @@
#include <stack>
#include <memory>
#include "base/future_util.hh"
#include "safe/safe.h"
#include "logfile.hh"
#include "hist_source.hh"
#include "statusview_curses.hh"
@ -212,10 +214,11 @@ struct key_repeat_history {
};
struct scan_progress {
std::mutex sp_mutex;
std::list<archive_manager::extract_progress> sp_extractions;
};
using safe_scan_progress = safe::Safe<scan_progress>;
struct file_collection {
std::map<std::string, std::string> fc_name_to_errors;
std::map<std::string, logfile_open_options> fc_file_names;
@ -225,10 +228,11 @@ struct file_collection {
fc_renamed_files;
std::set<std::string> fc_closed_files;
std::map<std::string, std::string> fc_other_files;
std::shared_ptr<scan_progress> fc_progress;
std::shared_ptr<safe_scan_progress> fc_progress;
size_t fc_largest_path_length{0};
file_collection() : fc_progress(std::make_shared<scan_progress>()) {}
file_collection()
: fc_progress(std::make_shared<safe::Safe<scan_progress>>()) {}
void clear() {
this->fc_name_to_errors.clear();
@ -238,7 +242,7 @@ struct file_collection {
this->fc_other_files.clear();
}
file_collection rescan_files(bool required = false);
file_collection expand_filename(const std::string& path, logfile_open_options &loo, bool required);
void expand_filename(future_queue<file_collection> &fq, const std::string& path, logfile_open_options &loo, bool required);
std::future<file_collection>
watch_logfile(const std::string& filename, logfile_open_options &loo, bool required);
void merge(const file_collection &other);

49
src/safe/accessmode.h Normal file
View File

@ -0,0 +1,49 @@
/**
* @file accessmode.h
* @author L.-C. C.
* @brief
* @version 0.1
* @date 2019-01-20
*
* @copyright Copyright (c) 2019
*
*/
#pragma once
#include <mutex>
#if __cplusplus >= 201402L
#include <shared_mutex>
#endif // __cplusplus >= 201402L
namespace safe
{
enum class AccessMode
{
ReadOnly,
ReadWrite
};
template<typename LockType>
struct AccessTraits
{
static constexpr bool IsReadOnly = false;
};
template<typename MutexType>
struct AccessTraits<std::lock_guard<MutexType>>
{
static constexpr bool IsReadOnly = false;
};
template<typename MutexType>
struct AccessTraits<std::unique_lock<MutexType>>
{
static constexpr bool IsReadOnly = false;
};
#if __cplusplus >= 201402L
template<typename MutexType>
struct AccessTraits<std::shared_lock<MutexType>>
{
static constexpr bool IsReadOnly = true;
};
#endif // __cplusplus >= 201402L
} // namespace safe

23
src/safe/defaulttypes.h Normal file
View File

@ -0,0 +1,23 @@
/**
* @file defaulttypes.h
* @author L.-C. C.
* @brief
* @version 0.1
* @date 2020-01-29
*
* @copyright Copyright (c) 2020
*
*/
#pragma once
#include <mutex>
namespace safe
{
using DefaultMutex = std::mutex;
template<typename MutexType>
using DefaultReadOnlyLock = std::lock_guard<MutexType>;
template<typename MutexType>
using DefaultReadWriteLock = std::lock_guard<MutexType>;
} // namespace safe

40
src/safe/mutableref.h Normal file
View File

@ -0,0 +1,40 @@
/**
* @file mutableref.h
* @author L.-C. C.
* @brief
* @version 0.1
* @date 2020-01-03
*
* @copyright Copyright (c) 2020
*
*/
#pragma once
#include <utility>
namespace safe
{
namespace impl
{
/**
* @brief A helper class that defines a member variable of type
* Type. The variable is defined "mutable Type" if Type is not a
* reference, the variable is "Type&" if Type is a reference.
*
* @tparam Type The type of the variable to define.
*/
template<typename Type>
struct MutableIfNotReference
{
/// Mutable Type object.
mutable Type get;
};
template<typename Type>
struct MutableIfNotReference<Type&>
{
/// Reference to a Type object.
Type& get;
};
} // namespace impl
} // namespace safe

359
src/safe/safe.h Normal file
View File

@ -0,0 +1,359 @@
/**
* @file safe.h
* @author L.-C. C.
* @brief
* @version 0.1
* @date 2018-09-21
*
* @copyright Copyright (c) 2018
*
*/
#pragma once
#include "accessmode.h"
#include "defaulttypes.h"
#include "mutableref.h"
#include <type_traits>
#include <utility>
#if __cplusplus >= 201703L
#define EXPLICIT_IF_CPP17 explicit
#define EXPLICITLY_CONSTRUCT_RETURN_TYPE_IF_CPP17 ReturnType
#else
#define EXPLICIT_IF_CPP17
#define EXPLICITLY_CONSTRUCT_RETURN_TYPE_IF_CPP17
#endif
namespace safe
{
/**
* @brief Use this tag to default construct the mutex when constructing a
* Safe object.
*/
struct DefaultConstructMutex {};
static constexpr DefaultConstructMutex default_construct_mutex;
/**
* @brief Wraps a value together with a mutex.
*
* @tparam ValueType The type of the value to protect.
* @tparam MutexType The type of the mutex.
*/
template<typename ValueType, typename MutexType = DefaultMutex>
class Safe
{
private:
/// Type ValueType with reference removed, if present
using RemoveRefValueType = typename std::remove_reference<ValueType>::type;
/// Type MutexType with reference removed, if present
using RemoveRefMutexType = typename std::remove_reference<MutexType>::type;
/**
* @brief Manages a mutex and gives pointer-like access to a value
* object.
*
* @tparam LockType The type of the lock object that manages the
* mutex, example: std::lock_guard.
* @tparam Mode Determines the access mode of the Access
* object. Can be either AccessMode::ReadOnly or
* AccessMode::ReadWrite.
*/
template<template<typename> class LockType, AccessMode Mode>
class Access
{
// Make sure AccessMode is ReadOnly if a read-only lock is used
static_assert(!(AccessTraits<LockType<RemoveRefMutexType>>::IsReadOnly && Mode==AccessMode::ReadWrite), "Cannot have ReadWrite access mode with ReadOnly lock. Check the value of AccessTraits<LockType>::IsReadOnly if it exists.");
/// ValueType with const qualifier if AccessMode is ReadOnly.
using ConstIfReadOnlyValueType = typename std::conditional<Mode==AccessMode::ReadOnly, const RemoveRefValueType, RemoveRefValueType>::type;
public:
/// Pointer-to-const ValueType
using ConstPointerType = const ConstIfReadOnlyValueType*;
/// Pointer-to-const ValueType if Mode is ReadOnly, pointer to ValueType otherwise.
using PointerType = ConstIfReadOnlyValueType*;
/// Reference-to-const ValueType
using ConstReferenceType = const ConstIfReadOnlyValueType&;
/// Reference-to-const ValueType if Mode is ReadOnly, reference to ValueType otherwise.
using ReferenceType = ConstIfReadOnlyValueType&;
/**
* @brief Construct an Access object from a possibly const
* reference to the value object and any additionnal argument
* needed to construct the Lock object.
*
* @tparam LockArgs Deduced from lockArgs.
* @param value Reference to the value.
* @param lockArgs Arguments needed to construct the lock object.
*/
template<typename... OtherLockArgs>
EXPLICIT_IF_CPP17
Access(ReferenceType value, MutexType& mutex, OtherLockArgs&&... otherLockArgs):
lock(mutex, std::forward<OtherLockArgs>(otherLockArgs)...),
m_value(value)
{}
/**
* @brief Construct a read-only Access object from a const
* safe::Safe object and any additionnal argument needed to
* construct the Lock object.
*
* If needed, you can provide additionnal arguments to construct
* the lock object (such as std::adopt_lock). The mutex from the
* safe::Locakble object is already passed to the lock object's
* constructor though, you must not provide it.
*
* @tparam OtherLockArgs Deduced from otherLockArgs.
* @param safe The const Safe object to give protected access to.
* @param otherLockArgs Other arguments needed to construct the lock
* object.
*/
template<typename... OtherLockArgs>
EXPLICIT_IF_CPP17
Access(const Safe& safe, OtherLockArgs&&... otherLockArgs):
Access(safe.m_value, safe.m_mutex.get, std::forward<OtherLockArgs>(otherLockArgs)...)
{}
/**
* @brief Construct a read-write Access object from a
* safe::Safe object and any additionnal argument needed to
* construct the Lock object.
*
* If needed, you can provide additionnal arguments to construct
* the lock object (such as std::adopt_lock). The mutex from the
* safe object is already passed to the lock object's constructor
* though, you must not provide it.
*
* @tparam OtherLockArgs Deduced from otherLockArgs.
* @param safe The Safe object to give protected access to.
* @param otherLockArgs Other arguments needed to construct the lock
* object.
*/
template<typename... OtherLockArgs>
EXPLICIT_IF_CPP17
Access(Safe& safe, OtherLockArgs&&... otherLockArgs):
Access(safe.m_value, safe.m_mutex.get, std::forward<OtherLockArgs>(otherLockArgs)...)
{}
/**
* @brief Construct an Access object from another one.
* OtherLockType must implement release() like std::unique_lock
* does.
*
* @tparam OtherLockType Deduced from otherAccess.
* @tparam OtherMode Deduced from otherAccess.
* @tparam OtherLockArgs Deduced from otherLockArgs.
* @param otherAccess The Access object to construct from.
* @param otherLockArgs Other arguments needed to construct the lock
* object.
*/
template<template<typename> class OtherLockType, AccessMode OtherMode, typename... OtherLockArgs>
EXPLICIT_IF_CPP17
Access(Access<OtherLockType, OtherMode>& otherAccess, OtherLockArgs&&... otherLockArgs):
Access(*otherAccess, *otherAccess.lock.release(), std::adopt_lock, std::forward<OtherLockArgs>(otherLockArgs)...)
{
static_assert(OtherMode == AccessMode::ReadWrite || OtherMode == Mode, "Cannot construct a ReadWrite Access object from a ReadOnly one!");
}
/**
* @brief Const accessor to the value.
* @return ConstPointerType Const pointer to the protected value.
*/
ConstPointerType operator->() const noexcept
{
return &m_value;
}
/**
* @brief Accessor to the value.
* @return ValuePointerType Pointer to the protected value.
*/
PointerType operator->() noexcept
{
return &m_value;
}
/**
* @brief Const accessor to the value.
* @return ConstValueReferenceType Const reference to the protected
* value.
*/
ConstReferenceType operator*() const noexcept
{
return m_value;
}
/**
* @brief Accessor to the value.
* @return ValueReferenceType Reference to the protected.
*/
ReferenceType operator*() noexcept
{
return m_value;
}
/// The lock that manages the mutex.
mutable LockType<RemoveRefMutexType> lock;
private:
/// The protected value.
ReferenceType m_value;
};
/// Reference-to-const ValueType.
using ConstValueReferenceType = const RemoveRefValueType&;
/// Reference to ValueType.
using ValueReferenceType = RemoveRefValueType&;
/// Reference to MutexType.
using MutexReferenceType = RemoveRefMutexType&;
public:
/// Aliases to ReadAccess and WriteAccess classes for this Safe class.
template<template<typename> class LockType=DefaultReadOnlyLock>
using ReadAccess = Access<LockType, AccessMode::ReadOnly>;
template<template<typename> class LockType=DefaultReadWriteLock>
using WriteAccess = Access<LockType, AccessMode::ReadWrite>;
/**
* @brief Construct a Safe object
*/
Safe() = default;
/**
* @brief Construct a Safe object with default construction of
* the mutex and perfect forwarding of the other arguments to
* construct the value object.
*
* @tparam ValueArgs Deduced from valueArgs.
* @param valueArgs Perfect forwarding arguments to construct the value object.
* @param tag Indicates that the mutex should be default constructed.
*/
template<typename... ValueArgs>
explicit Safe(DefaultConstructMutex, ValueArgs&&... valueArgs):
m_mutex(),
m_value(std::forward<ValueArgs>(valueArgs)...)
{}
/**
* @brief Construct a Safe object, forwarding the first
* argument to construct the mutex and the other arguments to
* construct the value object.
*
* @tparam MutexArg Deduced from mutexArg.
* @tparam ValueArgs Deduced from valueArgs.
* @param valueArgs Perfect forwarding arguments to construct the
* value object.
* @param mutexArg Perfect forwarding argument to construct the
* mutex object.
*/
template<typename MutexArg, typename... ValueArgs>
explicit Safe(MutexArg&& mutexArg, ValueArgs&&... valueArgs):
m_mutex{std::forward<MutexArg>(mutexArg)},
m_value(std::forward<ValueArgs>(valueArgs)...)
{}
/// Delete all copy/move construction/assignment, as these operations
/// require locking the mutex under the covers.
/// Use copy(), assign() and other defined constructors to get the behavior
/// you need with an explicit syntax.
Safe(const Safe&) = delete;
Safe(Safe&&) = delete;
Safe& operator =(const Safe&) = delete;
Safe& operator =(Safe&&) = delete;
template<template<typename> class LockType=DefaultReadOnlyLock, typename... LockArgs>
ReadAccess<LockType> readAccess(LockArgs&&... lockArgs) const
{
// using ReturnType = ReadAccess<LockType>;
return EXPLICITLY_CONSTRUCT_RETURN_TYPE_IF_CPP17{*this, std::forward<LockArgs>(lockArgs)...};
}
template<template<typename> class LockType=DefaultReadWriteLock, typename... LockArgs>
WriteAccess<LockType> writeAccess(LockArgs&&... lockArgs)
{
// using ReturnType = WriteAccess<LockType>;
return EXPLICITLY_CONSTRUCT_RETURN_TYPE_IF_CPP17{*this, std::forward<LockArgs>(lockArgs)...};
}
template<template<typename> class LockType=DefaultReadOnlyLock, typename... LockArgs>
RemoveRefValueType copy(LockArgs&&... lockArgs) const
{
return *readAccess<LockType>(std::forward<LockArgs>(lockArgs)...);
}
template<template<typename> class LockType=DefaultReadWriteLock, typename... LockArgs>
void assign(ConstValueReferenceType value, LockArgs&&... lockArgs)
{
*writeAccess<LockType>(std::forward<LockArgs>(lockArgs)...) = value;
}
template<template<typename> class LockType=DefaultReadWriteLock, typename... LockArgs>
void assign(RemoveRefValueType&& value, LockArgs&&... lockArgs)
{
*writeAccess<LockType>(std::forward<LockArgs>(lockArgs)...) = std::move(value);
}
/**
* @brief Unsafe const accessor to the value. If you use this
* function, you exit the realm of safe!
*
* @return ConstValueReferenceType Const reference to the value
* object.
*/
ConstValueReferenceType unsafe() const noexcept
{
return m_value;
}
/**
* @brief Unsafe accessor to the value. If you use this function,
* you exit the realm of safe!
*
* @return ValueReferenceType Reference to the value object.
*/
ValueReferenceType unsafe() noexcept
{
return m_value;
}
/**
* @brief Accessor to the mutex.
*
* @return MutexReferenceType Reference to the mutex.
*/
MutexReferenceType mutex() const noexcept
{
return m_mutex.get;
}
private:
/// The helper object that holds the mutable mutex, or a reference to a mutex.
impl::MutableIfNotReference<MutexType> m_mutex;
/// The value to protect.
ValueType m_value;
};
/**
* @brief Type alias for read-only Access.
*
* @tparam SafeType The type of Safe object to give read-only access to.
* @tparam LockType=DefaultReadOnlyLock The type of lock.
*/
template<
typename SafeType,
template<typename> class LockType=DefaultReadOnlyLock>
using ReadAccess = typename SafeType::template ReadAccess<LockType>;
/**
* @brief Type alias for read-write Access.
*
* @tparam SafeType The type of Safe object to give read-write access to.
* @tparam LockType=DefaultReadWriteLock The type of lock.
*/
template<
typename SafeType,
template<typename> class LockType=DefaultReadWriteLock>
using WriteAccess = typename SafeType::template WriteAccess<LockType>;
} // namespace safe
#undef EXPLICIT_IF_CPP17
#undef EXPLICITLY_CONSTRUCT_RETURN_TYPE_IF_CPP17

View File

@ -374,6 +374,7 @@ DISTCLEANFILES = \
distclean-local:
$(RM_V)rm -rf sessions
$(RM_V)rm -rf tmp
$(RM_V)rm -rf rotmp
$(RM_V)rm -rf meta-sessions
$(RM_V)rm -rf test-config
$(RM_V)rm -rf .lnav

View File

@ -15,7 +15,9 @@ EOF
fi
if test x"${LIBARCHIVE_LIBS}" != x""; then
(cd ${srcdir} && tar cfz ${builddir}/test-logs.tgz logfile_access_log.* logfile_empty.0)
(cd ${srcdir} && tar cfz ${builddir}/test-logs.tgz logfile_access_log.* logfile_empty.0 -C ${builddir} ../src/lnav)
dd if=test-logs.tgz of=test-logs-trunc.tgz bs=4096 count=20
mkdir -p tmp
run_test env TMPDIR=tmp ${lnav_test} -n test-logs.tgz
@ -46,6 +48,29 @@ EOF
logfile_access_log.0 1
logfile_access_log.1 1
logfile_empty.0 0
EOF
run_test env TMPDIR=tmp ${lnav_test} -n \
test-logs-trunc.tgz
sed -e "s|${builddir}||g" `test_err_filename` | head -1 \
> test_logfile.trunc.out
mv test_logfile.trunc.out `test_err_filename`
check_error_output "truncated tgz not reported correctly" <<EOF
error: unable to open file: /test-logs-trunc.tgz -- failed to read file: /test-logs-trunc.tgz >> ../src/lnav -- truncated gzip input
EOF
mkdir -p rotmp
chmod ugo-w rotmp
run_test env TMPDIR=rotmp ${lnav_test} -n test-logs.tgz
sed -e "s|lnav-[0-9]*-archives|lnav-NNN-archives|g" \
-e "s|${builddir}||g" \
`test_err_filename` | head -1 \
> test_logfile.rotmp.out
mv test_logfile.rotmp.out `test_err_filename`
check_error_output "archive not unpacked" <<EOF
error: unable to open file: /test-logs.tgz -- unable to write entry: rotmp/lnav-NNN-archives/test-logs.tgz/logfile_access_log.0 -- Failed to create dir 'rotmp/lnav-NNN-archives'
EOF
fi