mirror of
https://github.com/tstack/lnav.git
synced 2024-08-17 00:40:30 +03:00
[remote] add some config options and remove the copied tailer binary
Also fix time offset issue
This commit is contained in:
parent
be51a4e3de
commit
c3dc668b69
@ -119,6 +119,8 @@ void file_collection::merge(const file_collection &other)
|
|||||||
this->fc_recursive = other.fc_recursive;
|
this->fc_recursive = other.fc_recursive;
|
||||||
this->fc_rotated = other.fc_rotated;
|
this->fc_rotated = other.fc_rotated;
|
||||||
|
|
||||||
|
this->fc_synced_files.insert(other.fc_synced_files.begin(),
|
||||||
|
other.fc_synced_files.end());
|
||||||
this->fc_name_to_errors.insert(other.fc_name_to_errors.begin(),
|
this->fc_name_to_errors.insert(other.fc_name_to_errors.begin(),
|
||||||
other.fc_name_to_errors.end());
|
other.fc_name_to_errors.end());
|
||||||
this->fc_file_names.insert(other.fc_file_names.begin(),
|
this->fc_file_names.insert(other.fc_file_names.begin(),
|
||||||
@ -390,7 +392,7 @@ void file_collection::expand_filename(lnav::futures::future_queue<file_collectio
|
|||||||
|
|
||||||
isc::to<tailer::looper &, services::remote_tailer_t>()
|
isc::to<tailer::looper &, services::remote_tailer_t>()
|
||||||
.send([=](auto &tlooper) {
|
.send([=](auto &tlooper) {
|
||||||
tlooper.add_remote(rp);
|
tlooper.add_remote(rp, loo);
|
||||||
});
|
});
|
||||||
retval.fc_other_files[path] = file_format_t::FF_REMOTE;
|
retval.fc_other_files[path] = file_format_t::FF_REMOTE;
|
||||||
|
|
||||||
|
@ -62,6 +62,7 @@ struct file_collection {
|
|||||||
fc_renamed_files;
|
fc_renamed_files;
|
||||||
std::set<std::string> fc_closed_files;
|
std::set<std::string> fc_closed_files;
|
||||||
std::map<std::string, file_format_t> fc_other_files;
|
std::map<std::string, file_format_t> fc_other_files;
|
||||||
|
std::set<std::string> fc_synced_files;
|
||||||
std::shared_ptr<safe_scan_progress> fc_progress;
|
std::shared_ptr<safe_scan_progress> fc_progress;
|
||||||
std::vector<struct stat> fc_new_stats;
|
std::vector<struct stat> fc_new_stats;
|
||||||
size_t fc_largest_path_length{0};
|
size_t fc_largest_path_length{0};
|
||||||
|
@ -62,6 +62,58 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"additionalProperties": false
|
"additionalProperties": false
|
||||||
|
},
|
||||||
|
"remote": {
|
||||||
|
"description": "Settings related to remote file support",
|
||||||
|
"title": "/tuning/remote",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"ssh": {
|
||||||
|
"description": "Settings related to the ssh command used to contact remote machines",
|
||||||
|
"title": "/tuning/remote/ssh",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"command": {
|
||||||
|
"title": "/tuning/remote/ssh/command",
|
||||||
|
"description": "The SSH command to execute",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"flags": {
|
||||||
|
"title": "/tuning/remote/ssh/flags",
|
||||||
|
"description": "The flags to pass to the SSH command",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"options": {
|
||||||
|
"description": "The options to pass to the SSH command",
|
||||||
|
"title": "/tuning/remote/ssh/options",
|
||||||
|
"type": "object",
|
||||||
|
"patternProperties": {
|
||||||
|
"(\\w+)": {
|
||||||
|
"title": "/tuning/remote/ssh/options/<option_name>",
|
||||||
|
"description": "Set an option to be passed to the SSH command",
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": false
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"description": "The ssh_config options to pass to SSH with the -o option",
|
||||||
|
"title": "/tuning/remote/ssh/config",
|
||||||
|
"type": "object",
|
||||||
|
"patternProperties": {
|
||||||
|
"(\\w+)": {
|
||||||
|
"title": "/tuning/remote/ssh/config/<config_name>",
|
||||||
|
"description": "Set an SSH configuration value",
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"additionalProperties": false
|
"additionalProperties": false
|
||||||
@ -105,6 +157,7 @@
|
|||||||
"type": "object",
|
"type": "object",
|
||||||
"patternProperties": {
|
"patternProperties": {
|
||||||
"([\\w\\-]+)": {
|
"([\\w\\-]+)": {
|
||||||
|
"description": "Theme definitions",
|
||||||
"title": "/ui/theme-defs/<theme_name>",
|
"title": "/ui/theme-defs/<theme_name>",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
25
src/lnav.cc
25
src/lnav.cc
@ -904,13 +904,32 @@ bool update_active_files(const file_collection& new_files)
|
|||||||
|
|
||||||
bool rescan_files(bool req)
|
bool rescan_files(bool req)
|
||||||
{
|
{
|
||||||
|
auto& mlooper = injector::get<main_looper&, services::main_t>();
|
||||||
bool done = false;
|
bool done = false;
|
||||||
|
auto delay = 0ms;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
auto fc = lnav_data.ld_active_files.rescan_files(req);
|
auto fc = lnav_data.ld_active_files.rescan_files(req);
|
||||||
|
bool all_synced = true;
|
||||||
|
|
||||||
update_active_files(fc);
|
update_active_files(fc);
|
||||||
done = fc.fc_file_names.empty();
|
mlooper.get_port().process_for(delay);
|
||||||
|
if (lnav_data.ld_flags & LNF_HEADLESS) {
|
||||||
|
for (const auto& pair : lnav_data.ld_active_files.fc_other_files) {
|
||||||
|
if (pair.second != file_format_t::FF_REMOTE) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lnav_data.ld_active_files.fc_synced_files
|
||||||
|
.count(pair.first) == 0) {
|
||||||
|
all_synced = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!all_synced) {
|
||||||
|
delay = 30ms;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
done = fc.fc_file_names.empty() && all_synced;
|
||||||
} while (!done);
|
} while (!done);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -2470,8 +2489,8 @@ SELECT tbl_name FROM sqlite_master WHERE sql LIKE 'CREATE VIRTUAL TABLE%'
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
else if (is_glob(argv[lpc]) || strchr(argv[lpc], ':') != nullptr) {
|
else if (is_glob(argv[lpc]) || strchr(argv[lpc], ':') != nullptr) {
|
||||||
lnav_data.ld_active_files.fc_file_names
|
lnav_data.ld_active_files.fc_file_names[argv[lpc]]
|
||||||
.emplace(argv[lpc], logfile_open_options());
|
.with_tail(!(lnav_data.ld_flags & LNF_HEADLESS));
|
||||||
}
|
}
|
||||||
else if (stat(argv[lpc], &st) == -1) {
|
else if (stat(argv[lpc], &st) == -1) {
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
|
@ -3861,7 +3861,7 @@ static Result<string, string> com_config(exec_context &ec, string cmdline, vecto
|
|||||||
|
|
||||||
retval = help_text;
|
retval = help_text;
|
||||||
} else {
|
} else {
|
||||||
retval = "info: " + option + " = " + trim(old_value);
|
retval = fmt::format("{} = {}", option, trim(old_value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -87,6 +87,10 @@ static auto lc = injector::bind<lnav::logfile::config>::to_instance(+[]() {
|
|||||||
return &lnav_config.lc_logfile;
|
return &lnav_config.lc_logfile;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
static auto tc = injector::bind<tailer::config>::to_instance(+[]() {
|
||||||
|
return &lnav_config.lc_tailer;
|
||||||
|
});
|
||||||
|
|
||||||
bool check_experimental(const char *feature_name)
|
bool check_experimental(const char *feature_name)
|
||||||
{
|
{
|
||||||
const char *env_value = getenv("LNAV_EXP");
|
const char *env_value = getenv("LNAV_EXP");
|
||||||
@ -445,7 +449,8 @@ static struct json_path_container global_var_handlers = {
|
|||||||
paths_out.emplace_back(iter.first);
|
paths_out.emplace_back(iter.first);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.FOR_FIELD(_lnav_config, lc_global_vars)};
|
.FOR_FIELD(_lnav_config, lc_global_vars)
|
||||||
|
};
|
||||||
|
|
||||||
static struct json_path_container style_config_handlers =
|
static struct json_path_container style_config_handlers =
|
||||||
json_path_container{
|
json_path_container{
|
||||||
@ -801,6 +806,7 @@ static struct json_path_container theme_def_handlers = {
|
|||||||
|
|
||||||
static struct json_path_container theme_defs_handlers = {
|
static struct json_path_container theme_defs_handlers = {
|
||||||
yajlpp::pattern_property_handler("(?<theme_name>[\\w\\-]+)")
|
yajlpp::pattern_property_handler("(?<theme_name>[\\w\\-]+)")
|
||||||
|
.with_description("Theme definitions")
|
||||||
.with_obj_provider<lnav_theme, _lnav_config>([](const yajlpp_provider_context &ypc, _lnav_config *root) {
|
.with_obj_provider<lnav_theme, _lnav_config>([](const yajlpp_provider_context &ypc, _lnav_config *root) {
|
||||||
lnav_theme < = root->lc_ui_theme_defs[ypc.ypc_extractor.get_substr("theme_name")];
|
lnav_theme < = root->lc_ui_theme_defs[ypc.ypc_extractor.get_substr("theme_name")];
|
||||||
|
|
||||||
@ -891,6 +897,55 @@ static struct json_path_container logfile_handlers = {
|
|||||||
&lnav::logfile::config::lc_max_unrecognized_lines),
|
&lnav::logfile::config::lc_max_unrecognized_lines),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static struct json_path_container ssh_config_handlers = {
|
||||||
|
yajlpp::pattern_property_handler("(?<config_name>\\w+)")
|
||||||
|
.with_synopsis("name")
|
||||||
|
.with_description("Set an SSH configuration value")
|
||||||
|
.with_path_provider<_lnav_config>([](
|
||||||
|
auto *m, std::vector<std::string> &paths_out) {
|
||||||
|
for (const auto& pair : m->lc_tailer.c_ssh_config) {
|
||||||
|
paths_out.emplace_back(pair.first);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.for_field(&_lnav_config::lc_tailer,
|
||||||
|
&tailer::config::c_ssh_config),
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct json_path_container ssh_option_handlers = {
|
||||||
|
yajlpp::pattern_property_handler("(?<option_name>\\w+)")
|
||||||
|
.with_synopsis("name")
|
||||||
|
.with_description("Set an option to be passed to the SSH command")
|
||||||
|
.for_field(&_lnav_config::lc_tailer,
|
||||||
|
&tailer::config::c_ssh_options),
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct json_path_container ssh_handlers = {
|
||||||
|
yajlpp::property_handler("command")
|
||||||
|
.with_synopsis("ssh-command")
|
||||||
|
.with_description("The SSH command to execute")
|
||||||
|
.for_field(&_lnav_config::lc_tailer,
|
||||||
|
&tailer::config::c_ssh_cmd),
|
||||||
|
yajlpp::property_handler("flags")
|
||||||
|
.with_description("The flags to pass to the SSH command")
|
||||||
|
.for_field(&_lnav_config::lc_tailer,
|
||||||
|
&tailer::config::c_ssh_flags),
|
||||||
|
yajlpp::property_handler("options")
|
||||||
|
.with_description("The options to pass to the SSH command")
|
||||||
|
.with_children(ssh_option_handlers),
|
||||||
|
yajlpp::property_handler("config")
|
||||||
|
.with_description(
|
||||||
|
"The ssh_config options to pass to SSH with the -o option")
|
||||||
|
.with_children(ssh_config_handlers),
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct json_path_container remote_handlers = {
|
||||||
|
yajlpp::property_handler("ssh")
|
||||||
|
.with_description(
|
||||||
|
"Settings related to the ssh command used to contact remote "
|
||||||
|
"machines")
|
||||||
|
.with_children(ssh_handlers),
|
||||||
|
};
|
||||||
|
|
||||||
static struct json_path_container tuning_handlers = {
|
static struct json_path_container tuning_handlers = {
|
||||||
yajlpp::property_handler("archive-manager")
|
yajlpp::property_handler("archive-manager")
|
||||||
.with_description("Settings related to opening archive files")
|
.with_description("Settings related to opening archive files")
|
||||||
@ -901,6 +956,9 @@ static struct json_path_container tuning_handlers = {
|
|||||||
yajlpp::property_handler("logfile")
|
yajlpp::property_handler("logfile")
|
||||||
.with_description("Settings related to log files")
|
.with_description("Settings related to log files")
|
||||||
.with_children(logfile_handlers),
|
.with_children(logfile_handlers),
|
||||||
|
yajlpp::property_handler("remote")
|
||||||
|
.with_description("Settings related to remote file support")
|
||||||
|
.with_children(remote_handlers),
|
||||||
};
|
};
|
||||||
|
|
||||||
static set<string> SUPPORTED_CONFIG_SCHEMAS = {
|
static set<string> SUPPORTED_CONFIG_SCHEMAS = {
|
||||||
|
@ -49,6 +49,7 @@
|
|||||||
#include "archive_manager.cfg.hh"
|
#include "archive_manager.cfg.hh"
|
||||||
#include "file_vtab.cfg.hh"
|
#include "file_vtab.cfg.hh"
|
||||||
#include "logfile.cfg.hh"
|
#include "logfile.cfg.hh"
|
||||||
|
#include "tailer/tailer.looper.cfg.hh"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if an experimental feature should be enabled by
|
* Check if an experimental feature should be enabled by
|
||||||
@ -97,6 +98,7 @@ struct _lnav_config {
|
|||||||
archive_manager::config lc_archive_manager;
|
archive_manager::config lc_archive_manager;
|
||||||
file_vtab::config lc_file_vtab;
|
file_vtab::config lc_file_vtab;
|
||||||
lnav::logfile::config lc_logfile;
|
lnav::logfile::config lc_logfile;
|
||||||
|
tailer::config lc_tailer;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern struct _lnav_config lnav_config;
|
extern struct _lnav_config lnav_config;
|
||||||
|
@ -293,6 +293,10 @@ bool logfile::process_prefix(shared_buffer_ref &sbr, const line_info &li)
|
|||||||
logfile::rebuild_result_t logfile::rebuild_index()
|
logfile::rebuild_result_t logfile::rebuild_index()
|
||||||
{
|
{
|
||||||
if (!this->lf_indexing) {
|
if (!this->lf_indexing) {
|
||||||
|
if (this->lf_sort_needed) {
|
||||||
|
this->lf_sort_needed = false;
|
||||||
|
return rebuild_result_t::RR_NEW_ORDER;
|
||||||
|
}
|
||||||
return logfile::rebuild_result_t::RR_NO_NEW_LINES;
|
return logfile::rebuild_result_t::RR_NO_NEW_LINES;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -507,6 +511,10 @@ logfile::rebuild_result_t logfile::rebuild_index()
|
|||||||
retval = RR_NEW_LINES;
|
retval = RR_NEW_LINES;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (this->lf_sort_needed) {
|
||||||
|
retval = RR_NEW_ORDER;
|
||||||
|
this->lf_sort_needed = false;
|
||||||
|
}
|
||||||
|
|
||||||
this->lf_index_time = this->lf_line_buffer.get_file_time();
|
this->lf_index_time = this->lf_line_buffer.get_file_time();
|
||||||
if (!this->lf_index_time) {
|
if (!this->lf_index_time) {
|
||||||
|
@ -94,6 +94,12 @@ struct logfile_open_options {
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logfile_open_options &with_tail(bool val) {
|
||||||
|
this->loo_tail = val;
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
std::string loo_filename;
|
std::string loo_filename;
|
||||||
auto_fd loo_fd;
|
auto_fd loo_fd;
|
||||||
logfile_name_source loo_source{logfile_name_source::USER};
|
logfile_name_source loo_source{logfile_name_source::USER};
|
||||||
@ -102,6 +108,7 @@ struct logfile_open_options {
|
|||||||
bool loo_is_visible{true};
|
bool loo_is_visible{true};
|
||||||
bool loo_non_utf_is_visible{true};
|
bool loo_non_utf_is_visible{true};
|
||||||
ssize_t loo_visible_size_limit{-1};
|
ssize_t loo_visible_size_limit{-1};
|
||||||
|
bool loo_tail{true};
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -263,15 +263,8 @@ void logfile_sub_source::text_value_for_line(textview_curses &tc,
|
|||||||
adjusted_tm);
|
adjusted_tm);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (len > time_range.length()) {
|
|
||||||
ssize_t padding = len - time_range.length();
|
|
||||||
|
|
||||||
value_out.insert(time_range.lr_start,
|
|
||||||
padding,
|
|
||||||
' ');
|
|
||||||
}
|
|
||||||
value_out.replace(time_range.lr_start,
|
value_out.replace(time_range.lr_start,
|
||||||
len,
|
time_range.length(),
|
||||||
buffer,
|
buffer,
|
||||||
len);
|
len);
|
||||||
this->lss_token_shift_start = time_range.lr_start;
|
this->lss_token_shift_start = time_range.lr_start;
|
||||||
|
@ -197,7 +197,6 @@ inline void ftime_a(char *dst, off_t &off_inout, ssize_t len, const struct exttm
|
|||||||
|
|
||||||
inline void ftime_Z(char *dst, off_t &off_inout, ssize_t len, const struct exttm &tm)
|
inline void ftime_Z(char *dst, off_t &off_inout, ssize_t len, const struct exttm &tm)
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void ftime_b(char *dst, off_t &off_inout, ssize_t len, const struct exttm &tm)
|
inline void ftime_b(char *dst, off_t &off_inout, ssize_t len, const struct exttm &tm)
|
||||||
|
@ -65,11 +65,15 @@ bool ptime_fmt(const char *fmt, struct exttm *dst, const char *str, off_t &off,
|
|||||||
case 'a':
|
case 'a':
|
||||||
case 'Z':
|
case 'Z':
|
||||||
if (fmt[lpc + 2]) {
|
if (fmt[lpc + 2]) {
|
||||||
if (!ptime_upto(fmt[lpc + 2], str, off, len)) return false;
|
if (!ptime_upto(fmt[lpc + 2], str, off, len)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
lpc += 1;
|
lpc += 1;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (!ptime_upto_end(str, off, len)) return false;
|
if (!ptime_upto_end(str, off, len)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
lpc += 1;
|
lpc += 1;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -11,6 +11,15 @@
|
|||||||
"archive-manager": {
|
"archive-manager": {
|
||||||
"min-free-space": 33554432,
|
"min-free-space": 33554432,
|
||||||
"cache-ttl": "2d"
|
"cache-ttl": "2d"
|
||||||
|
},
|
||||||
|
"remote": {
|
||||||
|
"ssh": {
|
||||||
|
"command": "ssh",
|
||||||
|
"config": {
|
||||||
|
"BatchMode": "yes",
|
||||||
|
"StrictHostKeyChecking": "no"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -36,6 +36,7 @@ add_library(
|
|||||||
|
|
||||||
tailer.looper.hh
|
tailer.looper.hh
|
||||||
tailer.looper.cc
|
tailer.looper.cc
|
||||||
|
tailer.looper.cfg.hh
|
||||||
tailerbin.h
|
tailerbin.h
|
||||||
tailerbin.cc
|
tailerbin.cc
|
||||||
)
|
)
|
||||||
|
@ -24,6 +24,7 @@ noinst_HEADERS = \
|
|||||||
sha-256.h \
|
sha-256.h \
|
||||||
tailer.h \
|
tailer.h \
|
||||||
tailer.looper.hh \
|
tailer.looper.hh \
|
||||||
|
tailer.looper.cfg.hh \
|
||||||
tailerpp.hh
|
tailerpp.hh
|
||||||
|
|
||||||
libtailercommon_a_SOURCES = \
|
libtailercommon_a_SOURCES = \
|
||||||
|
@ -200,6 +200,9 @@ int main(int argc, char *const *argv)
|
|||||||
ftruncate(fd, ptb.ptb_offset);
|
ftruncate(fd, ptb.ptb_offset);
|
||||||
pwrite(fd, ptb.ptb_bits.data(), ptb.ptb_bits.size(), ptb.ptb_offset);
|
pwrite(fd, ptb.ptb_bits.data(), ptb.ptb_bits.size(), ptb.ptb_offset);
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
[&](const tailer::packet_synced &ps) {
|
||||||
|
|
||||||
},
|
},
|
||||||
[&](const tailer::packet_link &pl) {
|
[&](const tailer::packet_link &pl) {
|
||||||
|
|
||||||
|
@ -49,6 +49,7 @@ typedef enum {
|
|||||||
TPT_ACK_BLOCK,
|
TPT_ACK_BLOCK,
|
||||||
TPT_TAIL_BLOCK,
|
TPT_TAIL_BLOCK,
|
||||||
TPT_LINK_BLOCK,
|
TPT_LINK_BLOCK,
|
||||||
|
TPT_SYNCED,
|
||||||
TPT_LOG,
|
TPT_LOG,
|
||||||
TPT_LOAD_PREVIEW,
|
TPT_LOAD_PREVIEW,
|
||||||
TPT_PREVIEW_ERROR,
|
TPT_PREVIEW_ERROR,
|
||||||
|
@ -35,6 +35,7 @@
|
|||||||
#include "base/lnav_log.hh"
|
#include "base/lnav_log.hh"
|
||||||
#include "base/paths.hh"
|
#include "base/paths.hh"
|
||||||
#include "tailer.looper.hh"
|
#include "tailer.looper.hh"
|
||||||
|
#include "tailer.looper.cfg.hh"
|
||||||
#include "tailer.h"
|
#include "tailer.h"
|
||||||
#include "tailerpp.hh"
|
#include "tailerpp.hh"
|
||||||
#include "lnav.hh"
|
#include "lnav.hh"
|
||||||
@ -85,6 +86,7 @@ static void read_err_pipe(const std::string &netloc, auto_fd &err,
|
|||||||
void tailer::looper::loop_body()
|
void tailer::looper::loop_body()
|
||||||
{
|
{
|
||||||
auto now = std::chrono::steady_clock::now();
|
auto now = std::chrono::steady_clock::now();
|
||||||
|
std::vector<std::string> to_erase;
|
||||||
|
|
||||||
for (auto& qpair : this->l_netlocs_to_paths) {
|
for (auto& qpair : this->l_netlocs_to_paths) {
|
||||||
auto& netloc = qpair.first;
|
auto& netloc = qpair.first;
|
||||||
@ -97,8 +99,17 @@ void tailer::looper::loop_body()
|
|||||||
auto create_res = host_tailer::for_host(netloc);
|
auto create_res = host_tailer::for_host(netloc);
|
||||||
|
|
||||||
if (create_res.isErr()) {
|
if (create_res.isErr()) {
|
||||||
this->report_error(netloc, create_res.unwrapErr());
|
report_error(netloc, create_res.unwrapErr());
|
||||||
rpq.rpq_next_attempt_time = now + HOST_RETRY_DELAY;
|
if (std::any_of(rpq.rpq_new_paths.begin(),
|
||||||
|
rpq.rpq_new_paths.end(),
|
||||||
|
[](const auto& pair) {
|
||||||
|
return !pair.second.loo_tail;
|
||||||
|
})) {
|
||||||
|
rpq.send_synced_to_main(netloc);
|
||||||
|
to_erase.push_back(netloc);
|
||||||
|
} else {
|
||||||
|
rpq.rpq_next_attempt_time = now + HOST_RETRY_DELAY;
|
||||||
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -114,12 +125,13 @@ void tailer::looper::loop_body()
|
|||||||
if (!rpq.rpq_new_paths.empty()) {
|
if (!rpq.rpq_new_paths.empty()) {
|
||||||
log_debug("%s: new paths to monitor -- %s",
|
log_debug("%s: new paths to monitor -- %s",
|
||||||
netloc.c_str(),
|
netloc.c_str(),
|
||||||
rpq.rpq_new_paths.begin()->c_str());
|
rpq.rpq_new_paths.begin()->first.c_str());
|
||||||
this->l_remotes[netloc]->send(
|
this->l_remotes[netloc]->send(
|
||||||
[paths = rpq.rpq_new_paths](auto &ht) {
|
[paths = rpq.rpq_new_paths](auto &ht) {
|
||||||
for (const auto &path : paths) {
|
for (const auto &pair : paths) {
|
||||||
log_debug("adding path to tailer -- %s", path.c_str());
|
log_debug("adding path to tailer -- %s",
|
||||||
ht.open_remote_path(path);
|
pair.first.c_str());
|
||||||
|
ht.open_remote_path(pair.first, pair.second);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -128,12 +140,18 @@ void tailer::looper::loop_body()
|
|||||||
rpq.rpq_new_paths.clear();
|
rpq.rpq_new_paths.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (const auto& netloc : to_erase) {
|
||||||
|
this->l_netlocs_to_paths.erase(netloc);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void tailer::looper::add_remote(const network::path& path)
|
void tailer::looper::add_remote(const network::path &path,
|
||||||
|
logfile_open_options options)
|
||||||
{
|
{
|
||||||
auto netloc_str = fmt::format("{}", path.home());
|
auto netloc_str = fmt::format("{}", path.home());
|
||||||
this->l_netlocs_to_paths[netloc_str].rpq_new_paths.insert(path.p_path);
|
this->l_netlocs_to_paths[netloc_str].rpq_new_paths[path.p_path] =
|
||||||
|
std::move(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
void tailer::looper::load_preview(int64_t id, const network::path& path)
|
void tailer::looper::load_preview(int64_t id, const network::path& path)
|
||||||
@ -192,11 +210,47 @@ void tailer::looper::complete_path(const network::path& path)
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static std::vector<std::string>
|
||||||
|
create_ssh_args_from_config(const std::string& dest)
|
||||||
|
{
|
||||||
|
auto& cfg = injector::get<const tailer::config&>();
|
||||||
|
std::vector<std::string> retval;
|
||||||
|
|
||||||
|
retval.emplace_back(cfg.c_ssh_cmd);
|
||||||
|
if (!cfg.c_ssh_flags.empty()) {
|
||||||
|
if (startswith(cfg.c_ssh_flags, "-")) {
|
||||||
|
retval.emplace_back(cfg.c_ssh_flags);
|
||||||
|
} else {
|
||||||
|
retval.emplace_back(fmt::format("-{}", cfg.c_ssh_flags));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const auto& pair : cfg.c_ssh_options) {
|
||||||
|
if (pair.second.empty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
retval.emplace_back(fmt::format("-{}", pair.first));
|
||||||
|
retval.emplace_back(pair.second);
|
||||||
|
}
|
||||||
|
for (const auto& pair : cfg.c_ssh_config) {
|
||||||
|
if (pair.second.empty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
retval.emplace_back(fmt::format(
|
||||||
|
"-o{}={}", pair.first, pair.second));
|
||||||
|
}
|
||||||
|
retval.emplace_back(dest);
|
||||||
|
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
Result<std::shared_ptr<tailer::looper::host_tailer>, std::string>
|
Result<std::shared_ptr<tailer::looper::host_tailer>, std::string>
|
||||||
tailer::looper::host_tailer::for_host(const std::string& netloc)
|
tailer::looper::host_tailer::for_host(const std::string& netloc)
|
||||||
{
|
{
|
||||||
log_debug("tailer(%s): transferring tailer to remote", netloc.c_str());
|
log_debug("tailer(%s): transferring tailer to remote", netloc.c_str());
|
||||||
|
|
||||||
|
auto& cfg = injector::get<const tailer::config&>();
|
||||||
|
auto tailer_bin_name = fmt::format("tailer.bin.{}", getpid());
|
||||||
|
|
||||||
auto rp = humanize::network::path::from_str(netloc).value();
|
auto rp = humanize::network::path::from_str(netloc).value();
|
||||||
auto ssh_dest = rp.p_locality.l_hostname;
|
auto ssh_dest = rp.p_locality.l_hostname;
|
||||||
if (rp.p_locality.l_username.has_value()) {
|
if (rp.p_locality.l_username.has_value()) {
|
||||||
@ -216,12 +270,21 @@ tailer::looper::host_tailer::for_host(const std::string& netloc)
|
|||||||
err_pipe.after_fork(child.in());
|
err_pipe.after_fork(child.in());
|
||||||
|
|
||||||
if (child.in_child()) {
|
if (child.in_child()) {
|
||||||
execlp("ssh", "ssh",
|
auto arg_strs = create_ssh_args_from_config(ssh_dest);
|
||||||
"-oStrictHostKeyChecking=no",
|
std::vector<char *> args;
|
||||||
"-oBatchMode=yes",
|
|
||||||
ssh_dest.c_str(),
|
arg_strs.emplace_back(fmt::format(
|
||||||
"cat > tailer.bin && chmod ugo+rx ./tailer.bin",
|
"cat > {} && chmod ugo+rx ./{}", tailer_bin_name, tailer_bin_name));
|
||||||
nullptr);
|
|
||||||
|
fmt::print(stderr, "tailer({}): executing -- {}\n",
|
||||||
|
netloc,
|
||||||
|
fmt::join(arg_strs, " "));
|
||||||
|
for (const auto& arg : arg_strs) {
|
||||||
|
args.push_back((char *) arg.data());
|
||||||
|
}
|
||||||
|
args.push_back(nullptr);
|
||||||
|
|
||||||
|
execvp(cfg.c_ssh_cmd.c_str(), args.data());
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -283,13 +346,21 @@ tailer::looper::host_tailer::for_host(const std::string& netloc)
|
|||||||
err_pipe.after_fork(child.in());
|
err_pipe.after_fork(child.in());
|
||||||
|
|
||||||
if (child.in_child()) {
|
if (child.in_child()) {
|
||||||
execlp("ssh", "ssh",
|
auto arg_strs = create_ssh_args_from_config(ssh_dest);
|
||||||
// "-q",
|
std::vector<char *> args;
|
||||||
"-oStrictHostKeyChecking=no",
|
|
||||||
"-oBatchMode=yes",
|
arg_strs.emplace_back(fmt::format(
|
||||||
ssh_dest.c_str(),
|
"./{}", tailer_bin_name, tailer_bin_name));
|
||||||
"./tailer.bin",
|
|
||||||
nullptr);
|
fmt::print(stderr, "tailer({}): executing -- {}\n",
|
||||||
|
netloc,
|
||||||
|
fmt::join(arg_strs, " "));
|
||||||
|
for (const auto& arg : arg_strs) {
|
||||||
|
args.push_back((char *) arg.data());
|
||||||
|
}
|
||||||
|
args.push_back(nullptr);
|
||||||
|
|
||||||
|
execvp(cfg.c_ssh_cmd.c_str(), args.data());
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -346,11 +417,12 @@ tailer::looper::host_tailer::host_tailer(const std::string &netloc,
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void tailer::looper::host_tailer::open_remote_path(const std::string& path)
|
void tailer::looper::host_tailer::open_remote_path(const std::string& path,
|
||||||
|
logfile_open_options loo)
|
||||||
{
|
{
|
||||||
this->ht_state.match(
|
this->ht_state.match(
|
||||||
[&](connected& conn) {
|
[&](connected& conn) {
|
||||||
conn.c_desired_paths.insert(path);
|
conn.c_desired_paths[path] = std::move(loo);
|
||||||
send_packet(conn.ht_to_child.get(),
|
send_packet(conn.ht_to_child.get(),
|
||||||
TPT_OPEN_PATH,
|
TPT_OPEN_PATH,
|
||||||
TPPT_STRING, path.c_str(),
|
TPPT_STRING, path.c_str(),
|
||||||
@ -359,6 +431,9 @@ void tailer::looper::host_tailer::open_remote_path(const std::string& path)
|
|||||||
[&](const disconnected& d) {
|
[&](const disconnected& d) {
|
||||||
log_warning("disconnected from host, cannot tail: %s",
|
log_warning("disconnected from host, cannot tail: %s",
|
||||||
path.c_str());
|
path.c_str());
|
||||||
|
},
|
||||||
|
[&](const synced& s) {
|
||||||
|
log_warning("synced with host, not tailing: %s", path.c_str());
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -387,6 +462,9 @@ void tailer::looper::host_tailer::load_preview(int64_t id, const std::string &pa
|
|||||||
.set_cylon(false)
|
.set_cylon(false)
|
||||||
.set_value(msg);
|
.set_value(msg);
|
||||||
});
|
});
|
||||||
|
},
|
||||||
|
[&](const synced& s) {
|
||||||
|
require(false);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -403,6 +481,9 @@ void tailer::looper::host_tailer::complete_path(const std::string &path)
|
|||||||
[&](const disconnected& d) {
|
[&](const disconnected& d) {
|
||||||
log_warning("disconnected from host, cannot preview: %s",
|
log_warning("disconnected from host, cannot preview: %s",
|
||||||
path.c_str());
|
path.c_str());
|
||||||
|
},
|
||||||
|
[&](const synced& s) {
|
||||||
|
require(false);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -469,6 +550,35 @@ void tailer::looper::host_tailer::loop_body()
|
|||||||
log_debug("Got an offer: %s %lld - %lld", pob.pob_path.c_str(),
|
log_debug("Got an offer: %s %lld - %lld", pob.pob_path.c_str(),
|
||||||
pob.pob_offset, pob.pob_length);
|
pob.pob_offset, pob.pob_length);
|
||||||
|
|
||||||
|
logfile_open_options loo;
|
||||||
|
if (pob.pob_path == pob.pob_root_path) {
|
||||||
|
auto root_iter = conn.c_desired_paths.find(pob.pob_path);
|
||||||
|
|
||||||
|
if (root_iter == conn.c_desired_paths.end()) {
|
||||||
|
log_warning("ignoring unknown root: %s",
|
||||||
|
pob.pob_root_path.c_str());
|
||||||
|
return std::move(this->ht_state);
|
||||||
|
}
|
||||||
|
|
||||||
|
loo = std::move(root_iter->second);
|
||||||
|
} else {
|
||||||
|
auto child_iter = conn.c_child_paths.find(pob.pob_path);
|
||||||
|
if (child_iter == conn.c_child_paths.end()) {
|
||||||
|
auto root_iter = conn.c_desired_paths.find(pob.pob_root_path);
|
||||||
|
|
||||||
|
if (root_iter == conn.c_desired_paths.end()) {
|
||||||
|
log_warning("ignoring child of unknown root: %s",
|
||||||
|
pob.pob_root_path.c_str());
|
||||||
|
return std::move(this->ht_state);
|
||||||
|
}
|
||||||
|
|
||||||
|
conn.c_child_paths[pob.pob_path] = std::move(root_iter->second);
|
||||||
|
child_iter = conn.c_child_paths.find(pob.pob_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
loo = std::move(child_iter->second);
|
||||||
|
}
|
||||||
|
|
||||||
auto remote_path = ghc::filesystem::absolute(
|
auto remote_path = ghc::filesystem::absolute(
|
||||||
ghc::filesystem::path(pob.pob_path)).relative_path();
|
ghc::filesystem::path(pob.pob_path)).relative_path();
|
||||||
auto local_path = this->ht_local_path / remote_path;
|
auto local_path = this->ht_local_path / remote_path;
|
||||||
@ -479,7 +589,7 @@ void tailer::looper::host_tailer::loop_body()
|
|||||||
|
|
||||||
auto custom_name = this->get_display_path(pob.pob_path);
|
auto custom_name = this->get_display_path(pob.pob_path);
|
||||||
isc::to<main_looper &, services::main_t>()
|
isc::to<main_looper &, services::main_t>()
|
||||||
.send([local_path, custom_name](auto &mlooper) {
|
.send([local_path, custom_name, loo](auto &mlooper) {
|
||||||
auto &active_fc = lnav_data.ld_active_files;
|
auto &active_fc = lnav_data.ld_active_files;
|
||||||
auto lpath_str = local_path.string();
|
auto lpath_str = local_path.string();
|
||||||
|
|
||||||
@ -496,7 +606,8 @@ void tailer::looper::host_tailer::loop_body()
|
|||||||
|
|
||||||
fc.fc_file_names[lpath_str]
|
fc.fc_file_names[lpath_str]
|
||||||
.with_filename(custom_name)
|
.with_filename(custom_name)
|
||||||
.with_source(logfile_name_source::REMOTE);
|
.with_source(logfile_name_source::REMOTE)
|
||||||
|
.with_tail(loo.loo_tail);
|
||||||
update_active_files(fc);
|
update_active_files(fc);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -575,6 +686,39 @@ void tailer::looper::host_tailer::loop_body()
|
|||||||
}
|
}
|
||||||
return std::move(this->ht_state);
|
return std::move(this->ht_state);
|
||||||
},
|
},
|
||||||
|
[&](const tailer::packet_synced &ps) {
|
||||||
|
if (ps.ps_root_path == ps.ps_path) {
|
||||||
|
auto iter = conn.c_desired_paths.find(ps.ps_path);
|
||||||
|
|
||||||
|
if (iter != conn.c_desired_paths.end()) {
|
||||||
|
if (!iter->second.loo_tail) {
|
||||||
|
log_info("synced desired path: %s",
|
||||||
|
iter->first.c_str());
|
||||||
|
conn.c_desired_paths.erase(iter);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
auto iter = conn.c_child_paths.find(ps.ps_path);
|
||||||
|
|
||||||
|
if (iter != conn.c_child_paths.end()) {
|
||||||
|
if (!iter->second.loo_tail) {
|
||||||
|
log_info("synced child path: %s",
|
||||||
|
iter->first.c_str());
|
||||||
|
conn.c_child_paths.erase(iter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (conn.c_desired_paths.empty() &&
|
||||||
|
conn.c_child_paths.empty()) {
|
||||||
|
log_info("tailer(%s): all desired paths synced",
|
||||||
|
this->ht_netloc.c_str());
|
||||||
|
return state_v{synced{}};
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::move(this->ht_state);
|
||||||
|
},
|
||||||
[&](const tailer::packet_link &pl) {
|
[&](const tailer::packet_link &pl) {
|
||||||
auto remote_path = ghc::filesystem::absolute(
|
auto remote_path = ghc::filesystem::absolute(
|
||||||
ghc::filesystem::path(pl.pl_path)).relative_path();
|
ghc::filesystem::path(pl.pl_path)).relative_path();
|
||||||
@ -657,7 +801,7 @@ void tailer::looper::host_tailer::loop_body()
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
if (this->ht_state.is<disconnected>()) {
|
if (!this->ht_state.is<connected>()) {
|
||||||
this->s_looping = false;
|
this->s_looping = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -671,7 +815,7 @@ tailer::looper::host_tailer::compute_timeout(mstime_t current_time) const
|
|||||||
|
|
||||||
void tailer::looper::host_tailer::stopped()
|
void tailer::looper::host_tailer::stopped()
|
||||||
{
|
{
|
||||||
if (!this->ht_state.is<disconnected>()) {
|
if (this->ht_state.is<connected>()) {
|
||||||
this->ht_state = disconnected();
|
this->ht_state = disconnected();
|
||||||
}
|
}
|
||||||
if (this->ht_error_reader.joinable()) {
|
if (this->ht_error_reader.joinable()) {
|
||||||
@ -712,11 +856,45 @@ tailer::looper::child_finished(std::shared_ptr<service_base> child)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (child_tailer->is_synced()) {
|
||||||
|
log_info("synced with netloc '%s', removing", iter->first.c_str());
|
||||||
|
auto netloc_iter = this->l_netlocs_to_paths.find(iter->first);
|
||||||
|
|
||||||
|
if (netloc_iter != this->l_netlocs_to_paths.end()) {
|
||||||
|
netloc_iter->second.send_synced_to_main(netloc_iter->first);
|
||||||
|
this->l_netlocs_to_paths.erase(netloc_iter);
|
||||||
|
}
|
||||||
|
}
|
||||||
this->l_remotes.erase(iter);
|
this->l_remotes.erase(iter);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
tailer::looper::remote_path_queue::send_synced_to_main(const std::string& netloc)
|
||||||
|
{
|
||||||
|
std::set<std::string> synced_files;
|
||||||
|
|
||||||
|
for (const auto& pair : this->rpq_new_paths) {
|
||||||
|
if (!pair.second.loo_tail) {
|
||||||
|
synced_files.emplace(fmt::format("{}{}", netloc, pair.first));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const auto& pair : this->rpq_existing_paths) {
|
||||||
|
if (!pair.second.loo_tail) {
|
||||||
|
synced_files.emplace(fmt::format("{}{}", netloc, pair.first));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
isc::to<main_looper&, services::main_t>()
|
||||||
|
.send([file_set = std::move(synced_files)](auto& mlooper) {
|
||||||
|
file_collection fc;
|
||||||
|
|
||||||
|
fc.fc_synced_files = file_set;
|
||||||
|
update_active_files(fc);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
void tailer::looper::report_error(std::string path, std::string msg)
|
void tailer::looper::report_error(std::string path, std::string msg)
|
||||||
{
|
{
|
||||||
isc::to<main_looper&, services::main_t>()
|
isc::to<main_looper&, services::main_t>()
|
||||||
|
47
src/tailer/tailer.looper.cfg.hh
Normal file
47
src/tailer/tailer.looper.cfg.hh
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
/**
|
||||||
|
* 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_cfg_hh
|
||||||
|
#define lnav_tailer_looper_cfg_hh
|
||||||
|
|
||||||
|
namespace tailer {
|
||||||
|
|
||||||
|
struct config {
|
||||||
|
std::string c_ssh_cmd{"ssh"};
|
||||||
|
std::string c_ssh_flags{};
|
||||||
|
std::map<std::string, std::string> c_ssh_options{};
|
||||||
|
std::map<std::string, std::string> c_ssh_config{
|
||||||
|
{"BatchMode", "yes"},
|
||||||
|
{"StrictHostKeyChecking", "yes"},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
@ -31,6 +31,7 @@
|
|||||||
#define lnav_tailer_looper_hh
|
#define lnav_tailer_looper_hh
|
||||||
|
|
||||||
#include <set>
|
#include <set>
|
||||||
|
#include <logfile_fwd.hh>
|
||||||
|
|
||||||
#include "base/isc.hh"
|
#include "base/isc.hh"
|
||||||
#include "base/auto_pid.hh"
|
#include "base/auto_pid.hh"
|
||||||
@ -43,13 +44,17 @@ namespace tailer {
|
|||||||
|
|
||||||
class looper : public isc::service<looper> {
|
class looper : public isc::service<looper> {
|
||||||
public:
|
public:
|
||||||
void add_remote(const network::path& path);
|
void add_remote(const network::path &path, logfile_open_options options);
|
||||||
|
|
||||||
void load_preview(int64_t id, const network::path& path);
|
void load_preview(int64_t id, const network::path& path);
|
||||||
|
|
||||||
void complete_path(const network::path& path);
|
void complete_path(const network::path& path);
|
||||||
|
|
||||||
std::set<std::string> active_netlocs() {
|
bool empty() const {
|
||||||
|
return this->l_netlocs_to_paths.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::set<std::string> active_netlocs() const {
|
||||||
std::set<std::string> retval;
|
std::set<std::string> retval;
|
||||||
|
|
||||||
for (const auto& pair : this->l_remotes) {
|
for (const auto& pair : this->l_remotes) {
|
||||||
@ -75,12 +80,16 @@ private:
|
|||||||
auto_fd to_child, auto_fd from_child,
|
auto_fd to_child, auto_fd from_child,
|
||||||
auto_fd err_from_child);
|
auto_fd err_from_child);
|
||||||
|
|
||||||
void open_remote_path(const std::string& path);
|
void open_remote_path(const std::string& path, logfile_open_options loo);
|
||||||
|
|
||||||
void load_preview(int64_t id, const std::string& path);
|
void load_preview(int64_t id, const std::string& path);
|
||||||
|
|
||||||
void complete_path(const std::string& path);
|
void complete_path(const std::string& path);
|
||||||
|
|
||||||
|
bool is_synced() const {
|
||||||
|
return this->ht_state.is<synced>();
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void *run() override;
|
void *run() override;
|
||||||
|
|
||||||
@ -100,14 +109,16 @@ private:
|
|||||||
auto_pid<process_state::RUNNING> ht_child;
|
auto_pid<process_state::RUNNING> ht_child;
|
||||||
auto_fd ht_to_child;
|
auto_fd ht_to_child;
|
||||||
auto_fd ht_from_child;
|
auto_fd ht_from_child;
|
||||||
std::set<std::string> c_desired_paths;
|
std::map<std::string, logfile_open_options> c_desired_paths;
|
||||||
|
std::map<std::string, logfile_open_options> c_child_paths;
|
||||||
|
|
||||||
auto_pid<process_state::FINISHED> close() &&;
|
auto_pid<process_state::FINISHED> close() &&;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct disconnected {};
|
struct disconnected {};
|
||||||
|
struct synced {};
|
||||||
|
|
||||||
using state_v = mapbox::util::variant<connected, disconnected>;
|
using state_v = mapbox::util::variant<connected, disconnected, synced>;
|
||||||
|
|
||||||
const std::string ht_netloc;
|
const std::string ht_netloc;
|
||||||
const ghc::filesystem::path ht_local_path;
|
const ghc::filesystem::path ht_local_path;
|
||||||
@ -123,8 +134,10 @@ private:
|
|||||||
|
|
||||||
struct remote_path_queue {
|
struct remote_path_queue {
|
||||||
attempt_time_point rpq_next_attempt_time{std::chrono::steady_clock::now()};
|
attempt_time_point rpq_next_attempt_time{std::chrono::steady_clock::now()};
|
||||||
std::set<std::string> rpq_new_paths;
|
std::map<std::string, logfile_open_options> rpq_new_paths;
|
||||||
std::set<std::string> rpq_existing_paths;
|
std::map<std::string, logfile_open_options> rpq_existing_paths;
|
||||||
|
|
||||||
|
void send_synced_to_main(const std::string& netloc);
|
||||||
};
|
};
|
||||||
|
|
||||||
std::map<std::string, remote_path_queue> l_netlocs_to_paths;
|
std::map<std::string, remote_path_queue> l_netlocs_to_paths;
|
||||||
|
@ -385,13 +385,19 @@ void send_preview_data(int64_t id, const char *path, int32_t len, const char *bi
|
|||||||
TPPT_DONE);
|
TPPT_DONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
int poll_paths(struct list *path_list)
|
int poll_paths(struct list *path_list, struct client_path_state *root_cps)
|
||||||
{
|
{
|
||||||
struct client_path_state *curr = (struct client_path_state *) path_list->l_head;
|
struct client_path_state *curr = (struct client_path_state *) path_list->l_head;
|
||||||
|
int is_top = root_cps == NULL;
|
||||||
int retval = 0;
|
int retval = 0;
|
||||||
|
|
||||||
while (curr->cps_node.n_succ != NULL) {
|
while (curr->cps_node.n_succ != NULL) {
|
||||||
|
if (is_top) {
|
||||||
|
root_cps = curr;
|
||||||
|
}
|
||||||
|
|
||||||
if (is_glob(curr->cps_path)) {
|
if (is_glob(curr->cps_path)) {
|
||||||
|
int changes = 0;
|
||||||
glob_t gl;
|
glob_t gl;
|
||||||
|
|
||||||
memset(&gl, 0, sizeof(gl));
|
memset(&gl, 0, sizeof(gl));
|
||||||
@ -407,6 +413,7 @@ int poll_paths(struct list *path_list)
|
|||||||
if ((child = find_client_path_state(
|
if ((child = find_client_path_state(
|
||||||
&prev_children, gl.gl_pathv[lpc])) == NULL) {
|
&prev_children, gl.gl_pathv[lpc])) == NULL) {
|
||||||
child = create_client_path_state(gl.gl_pathv[lpc]);
|
child = create_client_path_state(gl.gl_pathv[lpc]);
|
||||||
|
changes += 1;
|
||||||
} else {
|
} else {
|
||||||
list_remove(&child->cps_node);
|
list_remove(&child->cps_node);
|
||||||
}
|
}
|
||||||
@ -420,9 +427,21 @@ int poll_paths(struct list *path_list)
|
|||||||
&prev_children)) != NULL) {
|
&prev_children)) != NULL) {
|
||||||
send_error(child, "deleted");
|
send_error(child, "deleted");
|
||||||
delete_client_path_state(child);
|
delete_client_path_state(child);
|
||||||
|
changes += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
retval += poll_paths(&curr->cps_children);
|
retval += poll_paths(&curr->cps_children, root_cps);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (changes) {
|
||||||
|
curr->cps_client_state = CS_INIT;
|
||||||
|
} else if (curr->cps_client_state != CS_SYNCED) {
|
||||||
|
send_packet(STDOUT_FILENO,
|
||||||
|
TPT_SYNCED,
|
||||||
|
TPPT_STRING, root_cps->cps_path,
|
||||||
|
TPPT_STRING, curr->cps_path,
|
||||||
|
TPPT_DONE);
|
||||||
|
curr->cps_client_state = CS_SYNCED;
|
||||||
}
|
}
|
||||||
|
|
||||||
curr = (struct client_path_state *) curr->cps_node.n_succ;
|
curr = (struct client_path_state *) curr->cps_node.n_succ;
|
||||||
@ -454,6 +473,7 @@ int poll_paths(struct list *path_list)
|
|||||||
buffer[link_len] = '\0';
|
buffer[link_len] = '\0';
|
||||||
send_packet(STDOUT_FILENO,
|
send_packet(STDOUT_FILENO,
|
||||||
TPT_LINK_BLOCK,
|
TPT_LINK_BLOCK,
|
||||||
|
TPPT_STRING, root_cps->cps_path,
|
||||||
TPPT_STRING, curr->cps_path,
|
TPPT_STRING, curr->cps_path,
|
||||||
TPPT_STRING, buffer,
|
TPPT_STRING, buffer,
|
||||||
TPPT_DONE);
|
TPPT_DONE);
|
||||||
@ -482,13 +502,14 @@ int poll_paths(struct list *path_list)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
retval += poll_paths(&curr->cps_children);
|
retval += poll_paths(&curr->cps_children, root_cps);
|
||||||
|
|
||||||
curr->cps_last_path_state = PS_OK;
|
curr->cps_last_path_state = PS_OK;
|
||||||
} else if (S_ISREG(st.st_mode)) {
|
} else if (S_ISREG(st.st_mode)) {
|
||||||
switch (curr->cps_client_state) {
|
switch (curr->cps_client_state) {
|
||||||
case CS_INIT:
|
case CS_INIT:
|
||||||
case CS_TAILING: {
|
case CS_TAILING:
|
||||||
|
case CS_SYNCED: {
|
||||||
if (curr->cps_client_file_offset < st.st_size) {
|
if (curr->cps_client_file_offset < st.st_size) {
|
||||||
int fd = open(curr->cps_path, O_RDONLY);
|
int fd = open(curr->cps_path, O_RDONLY);
|
||||||
|
|
||||||
@ -515,6 +536,7 @@ int poll_paths(struct list *path_list)
|
|||||||
curr->cps_client_file_read_length = bytes_read;
|
curr->cps_client_file_read_length = bytes_read;
|
||||||
send_packet(STDOUT_FILENO,
|
send_packet(STDOUT_FILENO,
|
||||||
TPT_OFFER_BLOCK,
|
TPT_OFFER_BLOCK,
|
||||||
|
TPPT_STRING, root_cps->cps_path,
|
||||||
TPPT_STRING, curr->cps_path,
|
TPPT_STRING, curr->cps_path,
|
||||||
TPPT_INT64, file_offset,
|
TPPT_INT64, file_offset,
|
||||||
TPPT_INT64, bytes_read,
|
TPPT_INT64, bytes_read,
|
||||||
@ -528,16 +550,25 @@ int poll_paths(struct list *path_list)
|
|||||||
|
|
||||||
send_packet(STDOUT_FILENO,
|
send_packet(STDOUT_FILENO,
|
||||||
TPT_TAIL_BLOCK,
|
TPT_TAIL_BLOCK,
|
||||||
|
TPPT_STRING, root_cps->cps_path,
|
||||||
TPPT_STRING, curr->cps_path,
|
TPPT_STRING, curr->cps_path,
|
||||||
TPPT_INT64, curr->cps_client_file_offset,
|
TPPT_INT64, curr->cps_client_file_offset,
|
||||||
TPPT_BITS, bytes_read, buffer,
|
TPPT_BITS, bytes_read, buffer,
|
||||||
TPPT_DONE);
|
TPPT_DONE);
|
||||||
curr->cps_client_file_offset += bytes_read;
|
curr->cps_client_file_offset += bytes_read;
|
||||||
|
curr->cps_client_state = CS_TAILING;
|
||||||
}
|
}
|
||||||
close(fd);
|
close(fd);
|
||||||
|
|
||||||
retval = 1;
|
retval = 1;
|
||||||
}
|
}
|
||||||
|
} else if (curr->cps_client_state != CS_SYNCED) {
|
||||||
|
send_packet(STDOUT_FILENO,
|
||||||
|
TPT_SYNCED,
|
||||||
|
TPPT_STRING, root_cps->cps_path,
|
||||||
|
TPPT_STRING, curr->cps_path,
|
||||||
|
TPPT_DONE);
|
||||||
|
curr->cps_client_state = CS_SYNCED;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -545,11 +576,6 @@ int poll_paths(struct list *path_list)
|
|||||||
// Still waiting for the client ack
|
// Still waiting for the client ack
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case CS_SYNCED: {
|
|
||||||
fprintf(stderr, "internal-error: got synced for %s\n",
|
|
||||||
curr->cps_path);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
curr->cps_last_path_state = PS_OK;
|
curr->cps_last_path_state = PS_OK;
|
||||||
@ -561,6 +587,7 @@ int poll_paths(struct list *path_list)
|
|||||||
} else {
|
} else {
|
||||||
struct list prev_children;
|
struct list prev_children;
|
||||||
struct dirent *entry;
|
struct dirent *entry;
|
||||||
|
int changes = 0;
|
||||||
|
|
||||||
list_move(&prev_children, &curr->cps_children);
|
list_move(&prev_children, &curr->cps_children);
|
||||||
while ((entry = readdir(dir)) != NULL) {
|
while ((entry = readdir(dir)) != NULL) {
|
||||||
@ -584,6 +611,7 @@ int poll_paths(struct list *path_list)
|
|||||||
// new file
|
// new file
|
||||||
fprintf(stderr, "info: monitoring child path: %s\n", full_path);
|
fprintf(stderr, "info: monitoring child path: %s\n", full_path);
|
||||||
child = create_client_path_state(full_path);
|
child = create_client_path_state(full_path);
|
||||||
|
changes += 1;
|
||||||
} else {
|
} else {
|
||||||
list_remove(&child->cps_node);
|
list_remove(&child->cps_node);
|
||||||
}
|
}
|
||||||
@ -597,9 +625,21 @@ int poll_paths(struct list *path_list)
|
|||||||
&prev_children)) != NULL) {
|
&prev_children)) != NULL) {
|
||||||
send_error(child, "deleted");
|
send_error(child, "deleted");
|
||||||
delete_client_path_state(child);
|
delete_client_path_state(child);
|
||||||
|
changes += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
retval += poll_paths(&curr->cps_children);
|
retval += poll_paths(&curr->cps_children, root_cps);
|
||||||
|
|
||||||
|
if (changes) {
|
||||||
|
curr->cps_client_state = CS_INIT;
|
||||||
|
} else if (curr->cps_client_state != CS_SYNCED) {
|
||||||
|
send_packet(STDOUT_FILENO,
|
||||||
|
TPT_SYNCED,
|
||||||
|
TPPT_STRING, root_cps->cps_path,
|
||||||
|
TPPT_STRING, curr->cps_path,
|
||||||
|
TPPT_DONE);
|
||||||
|
curr->cps_client_state = CS_SYNCED;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
curr->cps_last_path_state = PS_OK;
|
curr->cps_last_path_state = PS_OK;
|
||||||
@ -805,6 +845,9 @@ int main(int argc, char *argv[])
|
|||||||
int done = 0, timeout = 0;
|
int done = 0, timeout = 0;
|
||||||
recv_state_t rstate = RS_PACKET_TYPE;
|
recv_state_t rstate = RS_PACKET_TYPE;
|
||||||
|
|
||||||
|
// No need to leave ourselves around
|
||||||
|
unlink(argv[0]);
|
||||||
|
|
||||||
list_init(&client_path_list);
|
list_init(&client_path_list);
|
||||||
|
|
||||||
while (!done) {
|
while (!done) {
|
||||||
@ -912,7 +955,7 @@ int main(int argc, char *argv[])
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!done) {
|
if (!done) {
|
||||||
if (poll_paths(&client_path_list)) {
|
if (poll_paths(&client_path_list, NULL)) {
|
||||||
timeout = 0;
|
timeout = 0;
|
||||||
} else {
|
} else {
|
||||||
timeout = 1000;
|
timeout = 1000;
|
||||||
|
@ -77,6 +77,7 @@ Result<packet, std::string> read_packet(int fd)
|
|||||||
packet_offer_block pob;
|
packet_offer_block pob;
|
||||||
|
|
||||||
TRY(read_payloads_into(fd,
|
TRY(read_payloads_into(fd,
|
||||||
|
pob.pob_root_path,
|
||||||
pob.pob_path,
|
pob.pob_path,
|
||||||
pob.pob_offset,
|
pob.pob_offset,
|
||||||
pob.pob_length,
|
pob.pob_length,
|
||||||
@ -87,15 +88,25 @@ Result<packet, std::string> read_packet(int fd)
|
|||||||
packet_tail_block ptb;
|
packet_tail_block ptb;
|
||||||
|
|
||||||
TRY(read_payloads_into(fd,
|
TRY(read_payloads_into(fd,
|
||||||
|
ptb.ptb_root_path,
|
||||||
ptb.ptb_path,
|
ptb.ptb_path,
|
||||||
ptb.ptb_offset,
|
ptb.ptb_offset,
|
||||||
ptb.ptb_bits));
|
ptb.ptb_bits));
|
||||||
return Ok(packet{ptb});
|
return Ok(packet{ptb});
|
||||||
}
|
}
|
||||||
|
case TPT_SYNCED: {
|
||||||
|
packet_synced ps;
|
||||||
|
|
||||||
|
TRY(read_payloads_into(fd,
|
||||||
|
ps.ps_root_path,
|
||||||
|
ps.ps_path));
|
||||||
|
return Ok(packet{ps});
|
||||||
|
}
|
||||||
case TPT_LINK_BLOCK: {
|
case TPT_LINK_BLOCK: {
|
||||||
packet_link pl;
|
packet_link pl;
|
||||||
|
|
||||||
TRY(read_payloads_into(fd,
|
TRY(read_payloads_into(fd,
|
||||||
|
pl.pl_root_path,
|
||||||
pl.pl_path,
|
pl.pl_path,
|
||||||
pl.pl_link_value));
|
pl.pl_link_value));
|
||||||
return Ok(packet{pl});
|
return Ok(packet{pl});
|
||||||
|
@ -65,6 +65,7 @@ struct packet_log {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct packet_offer_block {
|
struct packet_offer_block {
|
||||||
|
std::string pob_root_path;
|
||||||
std::string pob_path;
|
std::string pob_path;
|
||||||
int64_t pob_offset;
|
int64_t pob_offset;
|
||||||
int64_t pob_length;
|
int64_t pob_length;
|
||||||
@ -72,12 +73,19 @@ struct packet_offer_block {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct packet_tail_block {
|
struct packet_tail_block {
|
||||||
|
std::string ptb_root_path;
|
||||||
std::string ptb_path;
|
std::string ptb_path;
|
||||||
int64_t ptb_offset;
|
int64_t ptb_offset;
|
||||||
std::vector<uint8_t> ptb_bits;
|
std::vector<uint8_t> ptb_bits;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct packet_synced {
|
||||||
|
std::string ps_root_path;
|
||||||
|
std::string ps_path;
|
||||||
|
};
|
||||||
|
|
||||||
struct packet_link {
|
struct packet_link {
|
||||||
|
std::string pl_root_path;
|
||||||
std::string pl_path;
|
std::string pl_path;
|
||||||
std::string pl_link_value;
|
std::string pl_link_value;
|
||||||
};
|
};
|
||||||
@ -101,7 +109,7 @@ struct packet_possible_path {
|
|||||||
using packet = mapbox::util::variant<
|
using packet = mapbox::util::variant<
|
||||||
packet_eof, packet_error, packet_offer_block, packet_tail_block,
|
packet_eof, packet_error, packet_offer_block, packet_tail_block,
|
||||||
packet_link, packet_preview_error, packet_preview_data,
|
packet_link, packet_preview_error, packet_preview_data,
|
||||||
packet_possible_path>;
|
packet_possible_path, packet_synced>;
|
||||||
|
|
||||||
struct recv_payload_type {};
|
struct recv_payload_type {};
|
||||||
struct recv_payload_length {};
|
struct recv_payload_length {};
|
||||||
|
@ -124,6 +124,9 @@ yajl_gen_status json_path_handler_base::gen(yajlpp_gen_context &ygc, yajl_gen ha
|
|||||||
size_t len;
|
size_t len;
|
||||||
yajl_gen_get_buf(handle, &buf, &len);
|
yajl_gen_get_buf(handle, &buf, &len);
|
||||||
if (status != yajl_gen_status_ok) {
|
if (status != yajl_gen_status_ok) {
|
||||||
|
log_error("yajl_gen failure for: %s -- %d",
|
||||||
|
jph.jph_property.c_str(),
|
||||||
|
status);
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -832,7 +835,8 @@ yajlpp_parse_context &yajlpp_parse_context::set_path(const string &path)
|
|||||||
for (size_t lpc = 0; lpc < path.size(); lpc++) {
|
for (size_t lpc = 0; lpc < path.size(); lpc++) {
|
||||||
switch (path[lpc]) {
|
switch (path[lpc]) {
|
||||||
case '/':
|
case '/':
|
||||||
this->ypc_path_index_stack.push_back(1 + lpc);
|
this->ypc_path_index_stack.push_back(
|
||||||
|
this->ypc_path_index_stack.empty() ? 1 : 0 + lpc);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -846,7 +850,7 @@ const char *yajlpp_parse_context::get_path_fragment(int offset, char *frag_in,
|
|||||||
size_t start, end;
|
size_t start, end;
|
||||||
|
|
||||||
if (offset < 0) {
|
if (offset < 0) {
|
||||||
offset = this->ypc_path_index_stack.size() + offset;
|
offset = ((int) this->ypc_path_index_stack.size()) + offset;
|
||||||
}
|
}
|
||||||
start = this->ypc_path_index_stack[offset] + ((offset == 0) ? 0 : 1);
|
start = this->ypc_path_index_stack[offset] + ((offset == 0) ? 0 : 1);
|
||||||
if ((offset + 1) < (int)this->ypc_path_index_stack.size()) {
|
if ((offset + 1) < (int)this->ypc_path_index_stack.size()) {
|
||||||
|
@ -447,7 +447,7 @@ public:
|
|||||||
typename std::enable_if<!std::is_integral<T>::value>::type* dummy = 0)
|
typename std::enable_if<!std::is_integral<T>::value>::type* dummy = 0)
|
||||||
{
|
{
|
||||||
yajl_gen_array_open(this->yg_handle);
|
yajl_gen_array_open(this->yg_handle);
|
||||||
for (auto elem : container) {
|
for (const auto& elem : container) {
|
||||||
yajl_gen_status rc = (*this)(elem);
|
yajl_gen_status rc = (*this)(elem);
|
||||||
|
|
||||||
if (rc != yajl_gen_status_ok) {
|
if (rc != yajl_gen_status_ok) {
|
||||||
|
@ -530,6 +530,88 @@ struct json_path_handler : public json_path_handler_base {
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<
|
||||||
|
typename... Args,
|
||||||
|
std::enable_if_t<LastIs<std::map<std::string, std::string>, Args...>::value, bool> = true
|
||||||
|
>
|
||||||
|
json_path_handler &for_field(Args... args) {
|
||||||
|
this->add_cb(str_field_cb2);
|
||||||
|
this->jph_str_cb = [args...](yajlpp_parse_context *ypc,
|
||||||
|
const unsigned char *str,
|
||||||
|
size_t len) {
|
||||||
|
auto obj = ypc->ypc_obj_stack.top();
|
||||||
|
auto key = ypc->get_path_fragment(-1);
|
||||||
|
|
||||||
|
json_path_handler::get_field(obj, args...)[key] = std::string((const char *) str, len);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
};
|
||||||
|
this->jph_gen_callback = [args...](yajlpp_gen_context &ygc,
|
||||||
|
const json_path_handler_base &jph,
|
||||||
|
yajl_gen handle) {
|
||||||
|
const auto& field = json_path_handler::get_field(ygc.ygc_obj_stack.top(), args...);
|
||||||
|
|
||||||
|
if (!ygc.ygc_default_stack.empty()) {
|
||||||
|
const auto& field_def = json_path_handler::get_field(ygc.ygc_default_stack.top(), args...);
|
||||||
|
|
||||||
|
if (field == field_def) {
|
||||||
|
return yajl_gen_status_ok;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
yajlpp_generator gen(handle);
|
||||||
|
|
||||||
|
for (const auto& pair : field) {
|
||||||
|
gen(pair.first);
|
||||||
|
gen(pair.second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return yajl_gen_status_ok;
|
||||||
|
};
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<
|
||||||
|
typename... Args,
|
||||||
|
std::enable_if_t<LastIs<std::string, Args...>::value, bool> = true
|
||||||
|
>
|
||||||
|
json_path_handler &for_field(Args... args) {
|
||||||
|
this->add_cb(str_field_cb2);
|
||||||
|
this->jph_str_cb = [args...](yajlpp_parse_context *ypc,
|
||||||
|
const unsigned char *str,
|
||||||
|
size_t len) {
|
||||||
|
auto obj = ypc->ypc_obj_stack.top();
|
||||||
|
|
||||||
|
json_path_handler::get_field(obj, args...) = std::string((const char *) str, len);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
};
|
||||||
|
this->jph_gen_callback = [args...](yajlpp_gen_context &ygc,
|
||||||
|
const json_path_handler_base &jph,
|
||||||
|
yajl_gen handle) {
|
||||||
|
const auto& field = json_path_handler::get_field(ygc.ygc_obj_stack.top(), args...);
|
||||||
|
|
||||||
|
if (!ygc.ygc_default_stack.empty()) {
|
||||||
|
const auto& field_def = json_path_handler::get_field(ygc.ygc_default_stack.top(), args...);
|
||||||
|
|
||||||
|
if (field == field_def) {
|
||||||
|
return yajl_gen_status_ok;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ygc.ygc_depth) {
|
||||||
|
yajl_gen_string(handle, jph.jph_property);
|
||||||
|
}
|
||||||
|
|
||||||
|
yajlpp_generator gen(handle);
|
||||||
|
|
||||||
|
return gen(field);
|
||||||
|
};
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
template<
|
template<
|
||||||
typename... Args,
|
typename... Args,
|
||||||
std::enable_if_t<LastIsNumber<Args...>::value, bool> = true
|
std::enable_if_t<LastIsNumber<Args...>::value, bool> = true
|
||||||
|
@ -24,6 +24,14 @@ AM_CPPFLAGS = \
|
|||||||
# AM_CFLAGS = -fprofile-arcs -ftest-coverage
|
# AM_CFLAGS = -fprofile-arcs -ftest-coverage
|
||||||
# AM_CXXFLAGS = -fprofile-arcs -ftest-coverage
|
# AM_CXXFLAGS = -fprofile-arcs -ftest-coverage
|
||||||
|
|
||||||
|
remote/ssh_host_rsa_key:
|
||||||
|
mkdir -p remote
|
||||||
|
ssh-keygen -f remote/ssh_host_rsa_key -N '' -t rsa
|
||||||
|
|
||||||
|
remote/ssh_host_dsa_key:
|
||||||
|
mkdir -p remote
|
||||||
|
ssh-keygen -f remote/ssh_host_dsa_key -N '' -t dsa
|
||||||
|
|
||||||
noinst_LIBRARIES = \
|
noinst_LIBRARIES = \
|
||||||
libtestdummy.a
|
libtestdummy.a
|
||||||
|
|
||||||
@ -174,6 +182,7 @@ dist_noinst_SCRIPTS = \
|
|||||||
test_logfile.sh \
|
test_logfile.sh \
|
||||||
test_meta.sh \
|
test_meta.sh \
|
||||||
test_mvwattrline.sh \
|
test_mvwattrline.sh \
|
||||||
|
test_remote.sh \
|
||||||
test_scripts.sh \
|
test_scripts.sh \
|
||||||
test_sessions.sh \
|
test_sessions.sh \
|
||||||
test_shlexer.sh \
|
test_shlexer.sh \
|
||||||
@ -408,7 +417,10 @@ DISTCLEANFILES = \
|
|||||||
empty \
|
empty \
|
||||||
scripts-empty
|
scripts-empty
|
||||||
|
|
||||||
|
all-local: remote/ssh_host_dsa_key remote/ssh_host_rsa_key
|
||||||
|
|
||||||
distclean-local:
|
distclean-local:
|
||||||
|
$(RM_V)rm -rf remote
|
||||||
$(RM_V)rm -rf sessions
|
$(RM_V)rm -rf sessions
|
||||||
$(RM_V)rm -rf tmp
|
$(RM_V)rm -rf tmp
|
||||||
$(RM_V)rm -rf rotmp
|
$(RM_V)rm -rf rotmp
|
||||||
|
2
test/logfile_block.1
Normal file
2
test/logfile_block.1
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
Wed May 19 08:00:01 EST 2021 line 1
|
||||||
|
Wed May 19 08:00:03 EST 2021 line 3
|
2
test/logfile_block.2
Normal file
2
test/logfile_block.2
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
Wed May 19 12:00:02 UTC 2021 line 2
|
||||||
|
Wed May 19 12:00:04 UTC 2021 line 4
|
@ -262,7 +262,7 @@ check_error_output "config clock-format 1" <<EOF
|
|||||||
EOF
|
EOF
|
||||||
|
|
||||||
check_output "config clock-format 1" <<EOF
|
check_output "config clock-format 1" <<EOF
|
||||||
info: /ui/clock-format = "%a %b %d %H:%M:%S %Z"
|
/ui/clock-format = "%a %b %d %H:%M:%S %Z"
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
run_test ${lnav_test} -nvq \
|
run_test ${lnav_test} -nvq \
|
||||||
@ -275,9 +275,9 @@ check_error_output "config clock-format 2" <<EOF
|
|||||||
EOF
|
EOF
|
||||||
|
|
||||||
check_output "config clock-format 2" <<EOF
|
check_output "config clock-format 2" <<EOF
|
||||||
info: /ui/clock-format = "%a %b %d %H:%M:%S %Z"
|
/ui/clock-format = "%a %b %d %H:%M:%S %Z"
|
||||||
info: changed config option -- /ui/clock-format
|
info: changed config option -- /ui/clock-format
|
||||||
info: /ui/clock-format = "abc"
|
/ui/clock-format = "abc"
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
run_test ${lnav_test} -nvq \
|
run_test ${lnav_test} -nvq \
|
||||||
@ -292,7 +292,7 @@ EOF
|
|||||||
check_output "config clock-format 3" <<EOF
|
check_output "config clock-format 3" <<EOF
|
||||||
info: changed config option -- /ui/clock-format
|
info: changed config option -- /ui/clock-format
|
||||||
info: reset option -- /ui/clock-format
|
info: reset option -- /ui/clock-format
|
||||||
info: /ui/clock-format = "%a %b %d %H:%M:%S %Z"
|
/ui/clock-format = "%a %b %d %H:%M:%S %Z"
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
|
|
||||||
|
@ -4,6 +4,19 @@ export HOME="./test-config"
|
|||||||
rm -rf ./test-config
|
rm -rf ./test-config
|
||||||
mkdir -p $HOME/.config
|
mkdir -p $HOME/.config
|
||||||
|
|
||||||
|
run_test ${lnav_test} -nN \
|
||||||
|
-c ":config /global/foo bar"
|
||||||
|
|
||||||
|
check_output "config write global var" <<EOF
|
||||||
|
EOF
|
||||||
|
|
||||||
|
run_test ${lnav_test} -nN \
|
||||||
|
-c ":config /global/foo"
|
||||||
|
|
||||||
|
check_output "config read global var" <<EOF
|
||||||
|
/global/foo = "foo"
|
||||||
|
EOF
|
||||||
|
|
||||||
run_test ${lnav_test} -n \
|
run_test ${lnav_test} -n \
|
||||||
-c ":config /ui/theme-defs/default/styles/text/color #f" \
|
-c ":config /ui/theme-defs/default/styles/text/color #f" \
|
||||||
${test_dir}/logfile_access_log.0
|
${test_dir}/logfile_access_log.0
|
||||||
|
26
test/test_remote.sh
Normal file
26
test/test_remote.sh
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
#! /bin/bash
|
||||||
|
|
||||||
|
cat > remote/sshd_config <<EOF
|
||||||
|
Port 2222
|
||||||
|
UsePam no
|
||||||
|
AuthorizedKeysFile .ssh/authorized_keys
|
||||||
|
HostKey ${PWD}/remote/ssh_host_rsa_key
|
||||||
|
HostKey ${PWD}/remote/ssh_host_dsa_key
|
||||||
|
ChallengeResponseAuthentication no
|
||||||
|
PidFile ${PWD}/remote/sshd.pid
|
||||||
|
EOF
|
||||||
|
|
||||||
|
SSHD_PATH=$(which sshd)
|
||||||
|
|
||||||
|
# trap 'kill $(cat remote/sshd.pid)' EXIT
|
||||||
|
|
||||||
|
$SSHD_PATH -E ${PWD}/remote/sshd.log -f remote/sshd_config
|
||||||
|
|
||||||
|
${lnav_test} -nN \
|
||||||
|
-c ":config /tuning/remote/ssh/options/p 2222" \
|
||||||
|
-c ":config /tuning/remote/ssh/flags vv"
|
||||||
|
|
||||||
|
run_test ${lnav_test} -d /tmp/lnav.err -n localhost:testdir
|
||||||
|
|
||||||
|
check_output "config write global var" <<EOF
|
||||||
|
EOF
|
@ -246,6 +246,18 @@ check_output "time_offset in lnav_file table is not working?" <<EOF
|
|||||||
10.112.81.15 - - [15/Feb/2013:06:01:31 +0000] "-" 400 0 "-" "-"
|
10.112.81.15 - - [15/Feb/2013:06:01:31 +0000] "-" 400 0 "-" "-"
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
|
run_test ${lnav_test} -n \
|
||||||
|
-c ";UPDATE lnav_file SET time_offset=14400000 WHERE endswith(filepath, 'logfile_block.1')" \
|
||||||
|
${test_dir}/logfile_block.1 \
|
||||||
|
${test_dir}/logfile_block.2
|
||||||
|
|
||||||
|
check_output "time_offset in lnav_file table is not reordering?" <<EOF
|
||||||
|
Wed May 19 12:00:01 2021 line 1
|
||||||
|
Wed May 19 12:00:02 UTC 2021 line 2
|
||||||
|
Wed May 19 12:00:03 2021 line 3
|
||||||
|
Wed May 19 12:00:04 UTC 2021 line 4
|
||||||
|
EOF
|
||||||
|
|
||||||
run_test ${lnav_test} -n \
|
run_test ${lnav_test} -n \
|
||||||
-c ";SELECT view_name,basename(filepath),visible FROM lnav_view_files" \
|
-c ";SELECT view_name,basename(filepath),visible FROM lnav_view_files" \
|
||||||
-c ":write-csv-to -" \
|
-c ":write-csv-to -" \
|
||||||
|
Loading…
Reference in New Issue
Block a user