mirror of
https://github.com/tstack/lnav.git
synced 2024-08-18 01:10:29 +03:00
parent
e3f4330377
commit
5787f47767
38
NEWS.md
38
NEWS.md
@ -1,3 +1,39 @@
|
||||
## lnav v0.12.0
|
||||
|
||||
Features:
|
||||
* Added the `:sh` command and `-e` option to execute a shell
|
||||
command-line and display its output within **lnav**. The
|
||||
captured output will be displayed in the TEXT view. The
|
||||
lines from stdout and stderr are recorded separately so
|
||||
that the lines from stderr can be shown in the theme's
|
||||
"error" highlight. The time that the lines were received
|
||||
are also recorded internally so that the "time-offset"
|
||||
display (enabled by pressing `Shift` + `T`) can be shown
|
||||
and the "jump to slow-down" hotkeys (`s`/`Shift` + `S`)
|
||||
work. Since the line-by-line timestamps are recorded
|
||||
internally, they will not interfere with timestamps that
|
||||
are in the commands output.
|
||||
* Added a `:cd` command to change **lnav**'s current directory.
|
||||
|
||||
Bug Fixes:
|
||||
* When piping data into **lnav**'s stdin, the input used to
|
||||
only be written to a single file without any rotation.
|
||||
Now, the input is written to a directory of rotating files.
|
||||
The same is true for the command-lines executed through the
|
||||
new `:sh` command.
|
||||
* Binary data piped into stdin should now be treated the same
|
||||
as if it was in a file that was passed on the command-line.
|
||||
|
||||
Breaking changes:
|
||||
* Removed the `-w` command-line option. This option was
|
||||
useful when stdin was not automatically preserved. Since
|
||||
the data is now stored (and cleaned up) as well as being
|
||||
spread across multiple files, this option doesn't make
|
||||
sense anymore.
|
||||
* Removed the `-t` command-line flag. Text data fed in
|
||||
on stdin and captured from a `:sh` execution is
|
||||
automatically timestamped.
|
||||
|
||||
## lnav v0.11.2
|
||||
|
||||
Features:
|
||||
@ -12,7 +48,7 @@ Features:
|
||||
field should automatically be determined by the observed
|
||||
values.
|
||||
* Added bunyan log format from Tobias Gruetzmacher.
|
||||
* Added cloudlare log format from @minusf.
|
||||
* Added cloudflare log format from @minusf.
|
||||
* Number fields used in a JSON log format `line-format`
|
||||
array now default to being right-aligned. Also, added
|
||||
`prefix` and `suffix` to `line-format` elements so a
|
||||
|
@ -39,6 +39,26 @@
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"piper": {
|
||||
"description": "Settings related to capturing piped data",
|
||||
"title": "/tuning/piper",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"max-size": {
|
||||
"title": "/tuning/piper/max-size",
|
||||
"description": "The maximum size of a capture file",
|
||||
"type": "integer",
|
||||
"minimum": 128
|
||||
},
|
||||
"rotations": {
|
||||
"title": "/tuning/piper/rotations",
|
||||
"description": "The number of rotated files to keep",
|
||||
"type": "integer",
|
||||
"minimum": 2
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"file-vtab": {
|
||||
"description": "Settings related to the lnav_file virtual-table",
|
||||
"title": "/tuning/file-vtab",
|
||||
|
@ -45,6 +45,12 @@ Options
|
||||
|
||||
Execute the given command file. This option can be given multiple times.
|
||||
|
||||
.. option:: -e <command-line>
|
||||
|
||||
Execute the given shell command-line and display its output. This is
|
||||
equivalent to executing the :code:`:sh` command and passing the
|
||||
:option:`-N` flag. This option can be given multiple times.
|
||||
|
||||
.. option:: -I <path>
|
||||
|
||||
Add a configuration directory.
|
||||
@ -76,14 +82,6 @@ Options
|
||||
|
||||
Recursively load files from the given base directories.
|
||||
|
||||
.. option:: -t
|
||||
|
||||
Prepend timestamps to the lines of data being read in on the standard input.
|
||||
|
||||
.. option:: -w <path>
|
||||
|
||||
Write the contents of the standard input to this file.
|
||||
|
||||
.. option:: -V
|
||||
|
||||
Print the version of lnav.
|
||||
@ -161,8 +159,8 @@ Examples
|
||||
|
||||
lnav /var/log
|
||||
|
||||
To watch the output of make with timestamps prepended:
|
||||
To watch the output of make:
|
||||
|
||||
.. prompt:: bash
|
||||
|
||||
make 2>&1 | lnav -t
|
||||
lnav -e 'make -j4'
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
.. _Configuration:
|
||||
|
||||
Configuration
|
||||
@ -261,6 +260,8 @@ command.
|
||||
|
||||
.. jsonschema:: ../schemas/config-v1.schema.json#/properties/tuning/properties/clipboard
|
||||
|
||||
.. jsonschema:: ../schemas/config-v1.schema.json#/properties/tuning/properties/piper
|
||||
|
||||
.. jsonschema:: ../schemas/config-v1.schema.json#/definitions/clip-commands
|
||||
|
||||
.. jsonschema:: ../schemas/config-v1.schema.json#/properties/tuning/properties/file-vtab
|
||||
|
@ -223,7 +223,9 @@ Display
|
||||
* - :kbd:`Shift` + :kbd:`p`
|
||||
- Switch to/from the pretty-printed view of the displayed log or text files
|
||||
* - :kbd:`Shift` + :kbd:`t`
|
||||
- Display elapsed time between lines
|
||||
- Display the elapsed time from a bookmark to a given line. In the TEXT view,
|
||||
this only works for content that was captured from stdin or a :code:`:sh`
|
||||
command.
|
||||
* - :kbd:`t`
|
||||
- Switch to/from the text file view
|
||||
* - :kbd:`i`
|
||||
|
3
lnav.1
3
lnav.1
@ -94,9 +94,6 @@ Load older rotated log files as well.
|
||||
\fB\-t\fR
|
||||
Prepend timestamps to the lines of data being read in
|
||||
on the standard input.
|
||||
.TP
|
||||
\fB\-w\fR file
|
||||
Write the contents of the standard input to this file.
|
||||
.SS "Optional arguments:"
|
||||
.TP
|
||||
logfile1
|
||||
|
@ -412,7 +412,7 @@ add_library(
|
||||
statusview_curses.cc
|
||||
string-extension-functions.cc
|
||||
sysclip.cc
|
||||
piper_proc.cc
|
||||
piper.looper.cc
|
||||
spectro_impls.cc
|
||||
spectro_source.cc
|
||||
sql_commands.cc
|
||||
@ -503,6 +503,8 @@ add_library(
|
||||
md4cpp.hh
|
||||
optional.hpp
|
||||
pcap_manager.hh
|
||||
piper.looper.hh
|
||||
piper.looper.cfg.hh
|
||||
plain_text_source.hh
|
||||
pretty_printer.hh
|
||||
preview_status_source.hh
|
||||
|
@ -259,7 +259,8 @@ noinst_HEADERS = \
|
||||
md4cpp.hh \
|
||||
optional.hpp \
|
||||
pcap_manager.hh \
|
||||
piper_proc.hh \
|
||||
piper.looper.hh \
|
||||
piper.looper.cfg.hh \
|
||||
plain_text_source.hh \
|
||||
pollable.hh \
|
||||
pretty_printer.hh \
|
||||
@ -428,6 +429,7 @@ libdiag_a_SOURCES = \
|
||||
network-extension-functions.cc \
|
||||
data_parser.cc \
|
||||
pcap_manager.cc \
|
||||
piper.looper.cc \
|
||||
plain_text_source.cc \
|
||||
pollable.cc \
|
||||
pretty_printer.cc \
|
||||
@ -456,7 +458,6 @@ libdiag_a_SOURCES = \
|
||||
text_format.cc \
|
||||
textfile_sub_source.cc \
|
||||
timer.cc \
|
||||
piper_proc.cc \
|
||||
sql_commands.cc \
|
||||
sql_util.cc \
|
||||
state-extension-functions.cc \
|
||||
|
@ -194,6 +194,8 @@ public:
|
||||
*/
|
||||
int get() const { return this->af_fd; }
|
||||
|
||||
bool has_value() const { return this->af_fd != -1; }
|
||||
|
||||
/**
|
||||
* Closes the current file descriptor and replaces its value with the given
|
||||
* one.
|
||||
|
@ -35,6 +35,7 @@
|
||||
#include "base/fs_util.hh"
|
||||
#include "base/injector.hh"
|
||||
#include "base/itertools.hh"
|
||||
#include "base/paths.hh"
|
||||
#include "base/string_util.hh"
|
||||
#include "bound_tags.hh"
|
||||
#include "config.h"
|
||||
@ -842,15 +843,19 @@ execute_init_commands(
|
||||
if (ec_out && fstat(fd_copy, &st) != -1 && st.st_size > 0) {
|
||||
static const auto OUTPUT_NAME = std::string("Initial command output");
|
||||
|
||||
lnav_data.ld_active_files.fc_file_names[OUTPUT_NAME]
|
||||
.with_fd(std::move(fd_copy))
|
||||
.with_include_in_session(false)
|
||||
.with_detect_format(false);
|
||||
lnav_data.ld_files_to_front.emplace_back(OUTPUT_NAME, 0_vl);
|
||||
auto create_piper_res = lnav::piper::create_looper(
|
||||
OUTPUT_NAME, std::move(fd_copy), auto_fd{});
|
||||
if (create_piper_res.isOk()) {
|
||||
lnav_data.ld_active_files.fc_file_names[OUTPUT_NAME]
|
||||
.with_piper(create_piper_res.unwrap())
|
||||
.with_include_in_session(false)
|
||||
.with_detect_format(false);
|
||||
lnav_data.ld_files_to_front.emplace_back(OUTPUT_NAME, 0_vl);
|
||||
|
||||
if (lnav_data.ld_rl_view != nullptr) {
|
||||
lnav_data.ld_rl_view->set_alt_value(
|
||||
HELP_MSG_1(X, "to close the file"));
|
||||
if (lnav_data.ld_rl_view != nullptr) {
|
||||
lnav_data.ld_rl_view->set_alt_value(
|
||||
HELP_MSG_1(X, "to close the file"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -978,24 +983,21 @@ pipe_callback(exec_context& ec, const std::string& cmdline, auto_fd& fd)
|
||||
return std::string();
|
||||
});
|
||||
}
|
||||
auto tmp_fd
|
||||
= lnav::filesystem::open_temp_file(
|
||||
ghc::filesystem::temp_directory_path() / "lnav.out.XXXXXX")
|
||||
.map([](auto pair) {
|
||||
ghc::filesystem::remove(pair.first);
|
||||
auto open_temp_res = lnav::filesystem::open_temp_file(lnav::paths::workdir()
|
||||
/ "exec.XXXXXX");
|
||||
if (open_temp_res.isErr()) {
|
||||
return lnav::futures::make_ready_future(
|
||||
fmt::format(FMT_STRING("error: cannot open temp file -- {}"),
|
||||
open_temp_res.unwrapErr()));
|
||||
}
|
||||
|
||||
auto tmp_pair = open_temp_res.unwrap();
|
||||
|
||||
return std::move(pair.second);
|
||||
})
|
||||
.expect("Cannot create temporary file for callback");
|
||||
auto pp
|
||||
= std::make_shared<piper_proc>(std::move(fd), false, std::move(tmp_fd));
|
||||
static int exec_count = 0;
|
||||
|
||||
lnav_data.ld_pipers.push_back(pp);
|
||||
auto desc
|
||||
= fmt::format(FMT_STRING("[{}] Output of {}"), exec_count++, cmdline);
|
||||
lnav_data.ld_active_files.fc_file_names[desc]
|
||||
.with_fd(pp->get_fd())
|
||||
lnav_data.ld_active_files.fc_file_names[tmp_pair.first]
|
||||
.with_filename(desc)
|
||||
.with_include_in_session(false)
|
||||
.with_detect_format(false);
|
||||
lnav_data.ld_files_to_front.emplace_back(desc, 0_vl);
|
||||
|
@ -72,15 +72,9 @@ struct exec_context {
|
||||
sql_callback_t sql_callback = ::sql_callback,
|
||||
pipe_callback_t pipe_callback = nullptr);
|
||||
|
||||
bool is_read_write() const
|
||||
{
|
||||
return this->ec_perms == perm_t::READ_WRITE;
|
||||
}
|
||||
bool is_read_write() const { return this->ec_perms == perm_t::READ_WRITE; }
|
||||
|
||||
bool is_read_only() const
|
||||
{
|
||||
return this->ec_perms == perm_t::READ_ONLY;
|
||||
}
|
||||
bool is_read_only() const { return this->ec_perms == perm_t::READ_ONLY; }
|
||||
|
||||
exec_context& with_perms(perm_t perms)
|
||||
{
|
||||
@ -91,15 +85,22 @@ struct exec_context {
|
||||
void add_error_context(lnav::console::user_message& um);
|
||||
|
||||
template<typename... Args>
|
||||
Result<std::string, lnav::console::user_message> make_error(
|
||||
fmt::string_view format_str, const Args&... args)
|
||||
lnav::console::user_message make_error_msg(fmt::string_view format_str,
|
||||
const Args&... args)
|
||||
{
|
||||
auto retval = lnav::console::user_message::error(
|
||||
fmt::vformat(format_str, fmt::make_format_args(args...)));
|
||||
|
||||
this->add_error_context(retval);
|
||||
|
||||
return Err(retval);
|
||||
return retval;
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
Result<std::string, lnav::console::user_message> make_error(
|
||||
fmt::string_view format_str, const Args&... args)
|
||||
{
|
||||
return Err(this->make_error_msg(format_str, args...));
|
||||
}
|
||||
|
||||
nonstd::optional<FILE*> get_output()
|
||||
|
@ -162,9 +162,8 @@ file_collection::merge(file_collection& other)
|
||||
other.fc_synced_files.end());
|
||||
this->fc_name_to_errors.insert(other.fc_name_to_errors.begin(),
|
||||
other.fc_name_to_errors.end());
|
||||
this->fc_file_names.insert(
|
||||
std::make_move_iterator(other.fc_file_names.begin()),
|
||||
std::make_move_iterator(other.fc_file_names.end()));
|
||||
this->fc_file_names.insert(other.fc_file_names.begin(),
|
||||
other.fc_file_names.end());
|
||||
if (!other.fc_files.empty()) {
|
||||
for (const auto& lf : other.fc_files) {
|
||||
this->fc_name_to_errors.erase(lf->get_filename());
|
||||
@ -193,7 +192,7 @@ file_collection::merge(file_collection& other)
|
||||
* Functor used to compare files based on their device and inode number.
|
||||
*/
|
||||
struct same_file {
|
||||
explicit same_file(const struct stat& stat) : sf_stat(stat){};
|
||||
explicit same_file(const struct stat& stat) : sf_stat(stat) {}
|
||||
|
||||
/**
|
||||
* Compare the given log file against the 'stat' given in the constructor.
|
||||
@ -203,7 +202,20 @@ struct same_file {
|
||||
*/
|
||||
bool operator()(const std::shared_ptr<logfile>& lf) const
|
||||
{
|
||||
return !lf->is_closed() && this->sf_stat.st_dev == lf->get_stat().st_dev
|
||||
if (lf->is_closed()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto& lf_loo = lf->get_open_options();
|
||||
|
||||
if (lf_loo.loo_temp_dev != 0
|
||||
&& this->sf_stat.st_dev == lf_loo.loo_temp_dev
|
||||
&& this->sf_stat.st_ino == lf_loo.loo_temp_ino)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return this->sf_stat.st_dev == lf->get_stat().st_dev
|
||||
&& this->sf_stat.st_ino == lf->get_stat().st_ino;
|
||||
}
|
||||
|
||||
@ -228,16 +240,12 @@ file_collection::watch_logfile(const std::string& filename,
|
||||
struct stat st;
|
||||
int rc;
|
||||
|
||||
auto filename_key = loo.loo_filename.empty() ? filename : loo.loo_filename;
|
||||
if (this->fc_closed_files.count(filename)) {
|
||||
return lnav::futures::make_ready_future(std::move(retval));
|
||||
}
|
||||
|
||||
if (loo.loo_fd != -1) {
|
||||
rc = fstat(loo.loo_fd, &st);
|
||||
if (rc == 0) {
|
||||
loo.with_stat_for_temp(st);
|
||||
}
|
||||
} else if (loo.loo_temp_file) {
|
||||
if (loo.loo_temp_file) {
|
||||
memset(&st, 0, sizeof(st));
|
||||
st.st_dev = loo.loo_temp_dev;
|
||||
st.st_ino = loo.loo_temp_ino;
|
||||
@ -265,7 +273,7 @@ file_collection::watch_logfile(const std::string& filename,
|
||||
return lnav::futures::make_ready_future(std::move(retval));
|
||||
}
|
||||
}
|
||||
auto err_iter = this->fc_name_to_errors.find(filename);
|
||||
auto err_iter = this->fc_name_to_errors.find(filename_key);
|
||||
if (err_iter != this->fc_name_to_errors.end()) {
|
||||
if (err_iter->second.fei_mtime != st.st_mtime) {
|
||||
this->fc_name_to_errors.erase(err_iter);
|
||||
@ -274,6 +282,9 @@ file_collection::watch_logfile(const std::string& filename,
|
||||
}
|
||||
if (rc == -1) {
|
||||
if (required) {
|
||||
log_error("failed to open required file: %s -- %s",
|
||||
filename.c_str(),
|
||||
strerror(errno));
|
||||
retval.fc_name_to_errors.emplace(filename,
|
||||
file_error_info{
|
||||
time(nullptr),
|
||||
@ -306,7 +317,7 @@ file_collection::watch_logfile(const std::string& filename,
|
||||
|
||||
auto func = [filename,
|
||||
st,
|
||||
loo2 = std::move(loo),
|
||||
loo,
|
||||
prog = this->fc_progress,
|
||||
errs = this->fc_name_to_errors]() mutable {
|
||||
file_collection retval;
|
||||
@ -316,10 +327,10 @@ file_collection::watch_logfile(const std::string& filename,
|
||||
return retval;
|
||||
}
|
||||
|
||||
auto ff = loo2.loo_temp_file ? file_format_t::UNKNOWN
|
||||
: detect_file_format(filename);
|
||||
auto ff = loo.loo_temp_file ? file_format_t::UNKNOWN
|
||||
: detect_file_format(filename);
|
||||
|
||||
loo2.loo_file_format = ff;
|
||||
loo.loo_file_format = ff;
|
||||
switch (ff) {
|
||||
case file_format_t::SQLITE_DB:
|
||||
retval.fc_other_files[filename].ofd_format = ff;
|
||||
@ -330,8 +341,6 @@ file_collection::watch_logfile(const std::string& filename,
|
||||
|
||||
if (res.isOk()) {
|
||||
auto convert_res = res.unwrap();
|
||||
|
||||
loo2.with_fd(std::move(convert_res.cr_destination));
|
||||
retval.fc_child_pollers.emplace_back(child_poller{
|
||||
std::move(convert_res.cr_child),
|
||||
[filename,
|
||||
@ -358,10 +367,15 @@ file_collection::watch_logfile(const std::string& filename,
|
||||
});
|
||||
},
|
||||
});
|
||||
auto open_res = logfile::open(filename, loo2);
|
||||
loo.with_stat_for_temp(st);
|
||||
auto open_res
|
||||
= logfile::open(convert_res.cr_destination, loo);
|
||||
if (open_res.isOk()) {
|
||||
retval.fc_files.push_back(open_res.unwrap());
|
||||
} else {
|
||||
log_error("failed to open: %s -- %s",
|
||||
filename.c_str(),
|
||||
open_res.unwrapErr().c_str());
|
||||
retval.fc_name_to_errors.emplace(
|
||||
filename,
|
||||
file_error_info{
|
||||
@ -384,7 +398,7 @@ file_collection::watch_logfile(const std::string& filename,
|
||||
std::list<archive_manager::extract_progress>::iterator>
|
||||
prog_iter_opt;
|
||||
|
||||
if (loo2.loo_source == logfile_name_source::ARCHIVE) {
|
||||
if (loo.loo_source == logfile_name_source::ARCHIVE) {
|
||||
// Don't try to open nested archives
|
||||
return retval;
|
||||
}
|
||||
@ -451,7 +465,7 @@ file_collection::watch_logfile(const std::string& filename,
|
||||
default:
|
||||
log_info("loading new file: filename=%s", filename.c_str());
|
||||
|
||||
auto open_res = logfile::open(filename, loo2);
|
||||
auto open_res = logfile::open(filename, loo);
|
||||
if (open_res.isOk()) {
|
||||
retval.fc_files.push_back(open_res.unwrap());
|
||||
} else {
|
||||
@ -510,6 +524,7 @@ file_collection::expand_filename(
|
||||
return;
|
||||
}
|
||||
|
||||
auto filename_key = loo.loo_filename.empty() ? path : loo.loo_filename;
|
||||
if (glob(path.c_str(), GLOB_NOCHECK, nullptr, gl.inout()) == 0) {
|
||||
int lpc;
|
||||
|
||||
@ -579,12 +594,18 @@ file_collection::expand_filename(
|
||||
file_collection retval;
|
||||
|
||||
if (gl->gl_pathc == 1) {
|
||||
retval.fc_name_to_errors.emplace(path,
|
||||
log_error("failed to find path: %s -- %s",
|
||||
path.c_str(),
|
||||
errmsg);
|
||||
retval.fc_name_to_errors.emplace(filename_key,
|
||||
file_error_info{
|
||||
time(nullptr),
|
||||
errmsg,
|
||||
});
|
||||
} else {
|
||||
log_error("failed to find path: %s -- %s",
|
||||
path_str.c_str(),
|
||||
errmsg);
|
||||
retval.fc_name_to_errors.emplace(path_str,
|
||||
file_error_info{
|
||||
time(nullptr),
|
||||
@ -617,15 +638,19 @@ file_collection::rescan_files(bool required)
|
||||
[&retval](auto& fc) { retval.merge(fc); });
|
||||
|
||||
for (auto& pair : this->fc_file_names) {
|
||||
if (!pair.second.loo_temp_file) {
|
||||
if (pair.second.loo_piper) {
|
||||
this->expand_filename(
|
||||
fq,
|
||||
pair.second.loo_piper->get_out_pattern().string(),
|
||||
pair.second,
|
||||
required);
|
||||
} else if (!pair.second.loo_temp_file) {
|
||||
this->expand_filename(fq, pair.first, pair.second, required);
|
||||
if (this->fc_rotated) {
|
||||
std::string path = pair.first + ".*";
|
||||
|
||||
this->expand_filename(fq, path, pair.second, false);
|
||||
}
|
||||
} else if (pair.second.loo_fd.get() != -1) {
|
||||
fq.push_back(watch_logfile(pair.first, pair.second, required));
|
||||
}
|
||||
|
||||
if (retval.fc_files.size() >= 100) {
|
||||
@ -647,3 +672,16 @@ file_collection::request_close(const std::shared_ptr<logfile>& lf)
|
||||
lf->close();
|
||||
this->fc_files_generation += 1;
|
||||
}
|
||||
|
||||
size_t
|
||||
file_collection::active_pipers() const
|
||||
{
|
||||
size_t retval = 0;
|
||||
for (const auto& pair : this->fc_file_names) {
|
||||
if (pair.second.loo_piper && !pair.second.loo_piper->is_finished()) {
|
||||
retval += 1;
|
||||
}
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
@ -175,6 +175,8 @@ struct file_collection {
|
||||
void close_files(const std::vector<std::shared_ptr<logfile>>& files);
|
||||
|
||||
void regenerate_unique_file_names();
|
||||
|
||||
size_t active_pipers() const;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -203,18 +203,20 @@ CREATE TABLE lnav_file (
|
||||
= this->lf_collection.fc_file_names.find(lf->get_filename());
|
||||
|
||||
if (iter != this->lf_collection.fc_file_names.end()) {
|
||||
auto loo = std::move(iter->second);
|
||||
auto loo = iter->second;
|
||||
|
||||
this->lf_collection.fc_file_names.erase(iter);
|
||||
|
||||
loo.loo_include_in_session = true;
|
||||
this->lf_collection.fc_file_names[path] = std::move(loo);
|
||||
lf->set_filename(path);
|
||||
this->lf_collection.regenerate_unique_file_names();
|
||||
|
||||
init_session();
|
||||
load_session();
|
||||
this->lf_collection.fc_file_names[path] = loo;
|
||||
}
|
||||
|
||||
lf->set_filename(path);
|
||||
lf->set_include_in_session(true);
|
||||
this->lf_collection.regenerate_unique_file_names();
|
||||
|
||||
init_session();
|
||||
load_session();
|
||||
}
|
||||
|
||||
return SQLITE_OK;
|
||||
|
@ -206,7 +206,6 @@ handle_paging_key(int ch)
|
||||
|
||||
textview_curses* tc = *lnav_data.ld_view_stack.top();
|
||||
exec_context& ec = lnav_data.ld_exec_context;
|
||||
logfile_sub_source* lss = nullptr;
|
||||
text_sub_source* tc_tss = tc->get_sub_source();
|
||||
bookmarks<vis_line_t>::type& bm = tc->get_bookmarks();
|
||||
|
||||
@ -219,7 +218,8 @@ handle_paging_key(int ch)
|
||||
return true;
|
||||
}
|
||||
|
||||
lss = dynamic_cast<logfile_sub_source*>(tc->get_sub_source());
|
||||
auto lss = dynamic_cast<logfile_sub_source*>(tc->get_sub_source());
|
||||
auto text_accel_p = dynamic_cast<text_accel_source*>(tc->get_sub_source());
|
||||
|
||||
/* process the command keystroke */
|
||||
switch (ch) {
|
||||
@ -485,20 +485,28 @@ handle_paging_key(int ch)
|
||||
#endif
|
||||
|
||||
case 's':
|
||||
if (lss) {
|
||||
auto next_top = tc->get_selection() + 2_vl;
|
||||
if (text_accel_p && text_accel_p->is_time_offset_supported()) {
|
||||
auto next_top = tc->get_selection() + 1_vl;
|
||||
|
||||
if (!lss->is_time_offset_enabled()) {
|
||||
if (!tc->is_selectable()) {
|
||||
next_top += 1_vl;
|
||||
}
|
||||
|
||||
if (!text_accel_p->is_time_offset_enabled()) {
|
||||
lnav_data.ld_rl_view->set_alt_value(
|
||||
HELP_MSG_1(T, "to disable elapsed-time mode"));
|
||||
}
|
||||
lss->set_time_offset(true);
|
||||
text_accel_p->set_time_offset(true);
|
||||
while (next_top < tc->get_inner_height()) {
|
||||
if (!lss->find_line(lss->at(next_top))->is_message()) {
|
||||
} else if (lss->get_line_accel_direction(next_top)
|
||||
if (!text_accel_p->text_accel_get_line(next_top)
|
||||
->is_message())
|
||||
{
|
||||
} else if (text_accel_p->get_line_accel_direction(next_top)
|
||||
== log_accel::A_DECEL)
|
||||
{
|
||||
--next_top;
|
||||
if (!tc->is_selectable()) {
|
||||
--next_top;
|
||||
}
|
||||
tc->set_selection(next_top);
|
||||
break;
|
||||
}
|
||||
@ -509,20 +517,27 @@ handle_paging_key(int ch)
|
||||
break;
|
||||
|
||||
case 'S':
|
||||
if (lss) {
|
||||
if (text_accel_p && text_accel_p->is_time_offset_supported()) {
|
||||
auto next_top = tc->get_selection();
|
||||
|
||||
if (!lss->is_time_offset_enabled()) {
|
||||
if (tc->is_selectable() && next_top > 0_vl) {
|
||||
next_top -= 1_vl;
|
||||
}
|
||||
if (!text_accel_p->is_time_offset_enabled()) {
|
||||
lnav_data.ld_rl_view->set_alt_value(
|
||||
HELP_MSG_1(T, "to disable elapsed-time mode"));
|
||||
}
|
||||
lss->set_time_offset(true);
|
||||
text_accel_p->set_time_offset(true);
|
||||
while (0 <= next_top && next_top < tc->get_inner_height()) {
|
||||
if (!lss->find_line(lss->at(next_top))->is_message()) {
|
||||
} else if (lss->get_line_accel_direction(next_top)
|
||||
if (!text_accel_p->text_accel_get_line(next_top)
|
||||
->is_message())
|
||||
{
|
||||
} else if (text_accel_p->get_line_accel_direction(next_top)
|
||||
== log_accel::A_DECEL)
|
||||
{
|
||||
--next_top;
|
||||
if (!tc->is_selectable()) {
|
||||
--next_top;
|
||||
}
|
||||
tc->set_selection(next_top);
|
||||
break;
|
||||
}
|
||||
@ -724,12 +739,16 @@ handle_paging_key(int ch)
|
||||
break;
|
||||
|
||||
case 'T':
|
||||
lnav_data.ld_log_source.toggle_time_offset();
|
||||
if (lss && lss->is_time_offset_enabled()) {
|
||||
lnav_data.ld_rl_view->set_alt_value(HELP_MSG_2(
|
||||
s, S, "to move forward/backward through slow downs"));
|
||||
if (text_accel_p != nullptr
|
||||
&& text_accel_p->is_time_offset_supported())
|
||||
{
|
||||
text_accel_p->toggle_time_offset();
|
||||
if (text_accel_p->is_time_offset_enabled()) {
|
||||
lnav_data.ld_rl_view->set_alt_value(HELP_MSG_2(
|
||||
s, S, "to move forward/backward through slow downs"));
|
||||
}
|
||||
tc->reload_data();
|
||||
}
|
||||
tc->reload_data();
|
||||
break;
|
||||
|
||||
case 'I': {
|
||||
|
@ -44,7 +44,7 @@
|
||||
:alt-msg Press t to switch to the text view
|
||||
|
||||
**See Also**
|
||||
:ref:`echo`, :ref:`eval`, :ref:`export_session_to`, :ref:`rebuild`, :ref:`redirect_to`, :ref:`write_csv_to`, :ref:`write_json_to`, :ref:`write_jsonlines_to`, :ref:`write_raw_to`, :ref:`write_screen_to`, :ref:`write_table_to`, :ref:`write_to`, :ref:`write_view_to`
|
||||
:ref:`cd`, :ref:`echo`, :ref:`eval`, :ref:`export_session_to`, :ref:`rebuild`, :ref:`redirect_to`, :ref:`sh`, :ref:`write_csv_to`, :ref:`write_json_to`, :ref:`write_jsonlines_to`, :ref:`write_raw_to`, :ref:`write_screen_to`, :ref:`write_table_to`, :ref:`write_to`, :ref:`write_view_to`
|
||||
|
||||
----
|
||||
|
||||
@ -72,6 +72,22 @@
|
||||
----
|
||||
|
||||
|
||||
.. _cd:
|
||||
|
||||
:cd *dir*
|
||||
^^^^^^^^^
|
||||
|
||||
Change the current directory
|
||||
|
||||
**Parameters**
|
||||
* **dir\*** --- The new current directory
|
||||
|
||||
**See Also**
|
||||
:ref:`alt_msg`, :ref:`echo`, :ref:`eval`, :ref:`export_session_to`, :ref:`rebuild`, :ref:`redirect_to`, :ref:`sh`, :ref:`write_csv_to`, :ref:`write_json_to`, :ref:`write_jsonlines_to`, :ref:`write_raw_to`, :ref:`write_screen_to`, :ref:`write_table_to`, :ref:`write_to`, :ref:`write_view_to`
|
||||
|
||||
----
|
||||
|
||||
|
||||
.. _clear_comment:
|
||||
|
||||
:clear-comment
|
||||
@ -414,7 +430,7 @@
|
||||
:echo Hello, World!
|
||||
|
||||
**See Also**
|
||||
:ref:`alt_msg`, :ref:`append_to`, :ref:`echoln`, :ref:`eval`, :ref:`export_session_to`, :ref:`export_session_to`, :ref:`pipe_line_to`, :ref:`pipe_to`, :ref:`rebuild`, :ref:`redirect_to`, :ref:`redirect_to`, :ref:`write_csv_to`, :ref:`write_csv_to`, :ref:`write_json_to`, :ref:`write_json_to`, :ref:`write_jsonlines_to`, :ref:`write_jsonlines_to`, :ref:`write_raw_to`, :ref:`write_raw_to`, :ref:`write_screen_to`, :ref:`write_screen_to`, :ref:`write_table_to`, :ref:`write_table_to`, :ref:`write_to`, :ref:`write_to`, :ref:`write_view_to`, :ref:`write_view_to`
|
||||
:ref:`alt_msg`, :ref:`append_to`, :ref:`cd`, :ref:`echoln`, :ref:`eval`, :ref:`export_session_to`, :ref:`export_session_to`, :ref:`pipe_line_to`, :ref:`pipe_to`, :ref:`rebuild`, :ref:`redirect_to`, :ref:`redirect_to`, :ref:`sh`, :ref:`write_csv_to`, :ref:`write_csv_to`, :ref:`write_json_to`, :ref:`write_json_to`, :ref:`write_jsonlines_to`, :ref:`write_jsonlines_to`, :ref:`write_raw_to`, :ref:`write_raw_to`, :ref:`write_screen_to`, :ref:`write_screen_to`, :ref:`write_table_to`, :ref:`write_table_to`, :ref:`write_to`, :ref:`write_to`, :ref:`write_view_to`, :ref:`write_view_to`
|
||||
|
||||
----
|
||||
|
||||
@ -473,7 +489,7 @@
|
||||
:eval ;SELECT * FROM ${table}
|
||||
|
||||
**See Also**
|
||||
:ref:`alt_msg`, :ref:`echo`, :ref:`export_session_to`, :ref:`rebuild`, :ref:`redirect_to`, :ref:`write_csv_to`, :ref:`write_json_to`, :ref:`write_jsonlines_to`, :ref:`write_raw_to`, :ref:`write_screen_to`, :ref:`write_table_to`, :ref:`write_to`, :ref:`write_view_to`
|
||||
:ref:`alt_msg`, :ref:`cd`, :ref:`echo`, :ref:`export_session_to`, :ref:`rebuild`, :ref:`redirect_to`, :ref:`sh`, :ref:`write_csv_to`, :ref:`write_json_to`, :ref:`write_jsonlines_to`, :ref:`write_raw_to`, :ref:`write_screen_to`, :ref:`write_table_to`, :ref:`write_to`, :ref:`write_view_to`
|
||||
|
||||
----
|
||||
|
||||
@ -489,7 +505,7 @@
|
||||
* **path\*** --- The path to the file to write
|
||||
|
||||
**See Also**
|
||||
:ref:`alt_msg`, :ref:`append_to`, :ref:`echo`, :ref:`echo`, :ref:`echoln`, :ref:`eval`, :ref:`pipe_line_to`, :ref:`pipe_to`, :ref:`rebuild`, :ref:`redirect_to`, :ref:`redirect_to`, :ref:`write_csv_to`, :ref:`write_csv_to`, :ref:`write_json_to`, :ref:`write_json_to`, :ref:`write_jsonlines_to`, :ref:`write_jsonlines_to`, :ref:`write_raw_to`, :ref:`write_raw_to`, :ref:`write_screen_to`, :ref:`write_screen_to`, :ref:`write_table_to`, :ref:`write_table_to`, :ref:`write_to`, :ref:`write_to`, :ref:`write_view_to`, :ref:`write_view_to`
|
||||
:ref:`alt_msg`, :ref:`append_to`, :ref:`cd`, :ref:`echo`, :ref:`echo`, :ref:`echoln`, :ref:`eval`, :ref:`pipe_line_to`, :ref:`pipe_to`, :ref:`rebuild`, :ref:`redirect_to`, :ref:`redirect_to`, :ref:`sh`, :ref:`write_csv_to`, :ref:`write_csv_to`, :ref:`write_json_to`, :ref:`write_json_to`, :ref:`write_jsonlines_to`, :ref:`write_jsonlines_to`, :ref:`write_raw_to`, :ref:`write_raw_to`, :ref:`write_screen_to`, :ref:`write_screen_to`, :ref:`write_table_to`, :ref:`write_table_to`, :ref:`write_to`, :ref:`write_to`, :ref:`write_view_to`, :ref:`write_view_to`
|
||||
|
||||
----
|
||||
|
||||
@ -1020,7 +1036,7 @@
|
||||
Forcefully rebuild file indexes
|
||||
|
||||
**See Also**
|
||||
:ref:`alt_msg`, :ref:`echo`, :ref:`eval`, :ref:`export_session_to`, :ref:`redirect_to`, :ref:`write_csv_to`, :ref:`write_json_to`, :ref:`write_jsonlines_to`, :ref:`write_raw_to`, :ref:`write_screen_to`, :ref:`write_table_to`, :ref:`write_to`, :ref:`write_view_to`
|
||||
:ref:`alt_msg`, :ref:`cd`, :ref:`echo`, :ref:`eval`, :ref:`export_session_to`, :ref:`redirect_to`, :ref:`sh`, :ref:`write_csv_to`, :ref:`write_json_to`, :ref:`write_jsonlines_to`, :ref:`write_raw_to`, :ref:`write_screen_to`, :ref:`write_table_to`, :ref:`write_to`, :ref:`write_view_to`
|
||||
|
||||
----
|
||||
|
||||
@ -1043,7 +1059,7 @@
|
||||
:redirect-to /tmp/script-output.txt
|
||||
|
||||
**See Also**
|
||||
:ref:`alt_msg`, :ref:`append_to`, :ref:`echo`, :ref:`echo`, :ref:`echoln`, :ref:`eval`, :ref:`export_session_to`, :ref:`export_session_to`, :ref:`pipe_line_to`, :ref:`pipe_to`, :ref:`rebuild`, :ref:`write_csv_to`, :ref:`write_csv_to`, :ref:`write_json_to`, :ref:`write_json_to`, :ref:`write_jsonlines_to`, :ref:`write_jsonlines_to`, :ref:`write_raw_to`, :ref:`write_raw_to`, :ref:`write_screen_to`, :ref:`write_screen_to`, :ref:`write_table_to`, :ref:`write_table_to`, :ref:`write_to`, :ref:`write_to`, :ref:`write_view_to`, :ref:`write_view_to`
|
||||
:ref:`alt_msg`, :ref:`append_to`, :ref:`cd`, :ref:`echo`, :ref:`echo`, :ref:`echoln`, :ref:`eval`, :ref:`export_session_to`, :ref:`export_session_to`, :ref:`pipe_line_to`, :ref:`pipe_to`, :ref:`rebuild`, :ref:`sh`, :ref:`write_csv_to`, :ref:`write_csv_to`, :ref:`write_json_to`, :ref:`write_json_to`, :ref:`write_jsonlines_to`, :ref:`write_jsonlines_to`, :ref:`write_raw_to`, :ref:`write_raw_to`, :ref:`write_screen_to`, :ref:`write_screen_to`, :ref:`write_table_to`, :ref:`write_table_to`, :ref:`write_to`, :ref:`write_to`, :ref:`write_view_to`, :ref:`write_view_to`
|
||||
|
||||
----
|
||||
|
||||
@ -1175,6 +1191,22 @@
|
||||
----
|
||||
|
||||
|
||||
.. _sh:
|
||||
|
||||
:sh *cmdline*
|
||||
^^^^^^^^^^^^^
|
||||
|
||||
Execute the given command-line and display the captured output
|
||||
|
||||
**Parameters**
|
||||
* **cmdline\*** --- The command-line to execute.
|
||||
|
||||
**See Also**
|
||||
:ref:`alt_msg`, :ref:`cd`, :ref:`echo`, :ref:`eval`, :ref:`export_session_to`, :ref:`rebuild`, :ref:`redirect_to`, :ref:`write_csv_to`, :ref:`write_json_to`, :ref:`write_jsonlines_to`, :ref:`write_raw_to`, :ref:`write_screen_to`, :ref:`write_table_to`, :ref:`write_to`, :ref:`write_view_to`
|
||||
|
||||
----
|
||||
|
||||
|
||||
.. _show_fields:
|
||||
|
||||
:show-fields *field-name*
|
||||
@ -1432,7 +1464,7 @@
|
||||
:write-table-to /tmp/table.txt
|
||||
|
||||
**See Also**
|
||||
:ref:`alt_msg`, :ref:`append_to`, :ref:`create_logline_table`, :ref:`create_search_table`, :ref:`echo`, :ref:`echo`, :ref:`echoln`, :ref:`eval`, :ref:`export_session_to`, :ref:`export_session_to`, :ref:`pipe_line_to`, :ref:`pipe_to`, :ref:`rebuild`, :ref:`redirect_to`, :ref:`redirect_to`, :ref:`write_csv_to`, :ref:`write_csv_to`, :ref:`write_csv_to`, :ref:`write_json_to`, :ref:`write_json_to`, :ref:`write_json_to`, :ref:`write_jsonlines_to`, :ref:`write_jsonlines_to`, :ref:`write_jsonlines_to`, :ref:`write_raw_to`, :ref:`write_raw_to`, :ref:`write_raw_to`, :ref:`write_screen_to`, :ref:`write_screen_to`, :ref:`write_screen_to`, :ref:`write_to`, :ref:`write_to`, :ref:`write_view_to`, :ref:`write_view_to`, :ref:`write_view_to`
|
||||
:ref:`alt_msg`, :ref:`append_to`, :ref:`cd`, :ref:`create_logline_table`, :ref:`create_search_table`, :ref:`echo`, :ref:`echo`, :ref:`echoln`, :ref:`eval`, :ref:`export_session_to`, :ref:`export_session_to`, :ref:`pipe_line_to`, :ref:`pipe_to`, :ref:`rebuild`, :ref:`redirect_to`, :ref:`redirect_to`, :ref:`sh`, :ref:`write_csv_to`, :ref:`write_csv_to`, :ref:`write_csv_to`, :ref:`write_json_to`, :ref:`write_json_to`, :ref:`write_json_to`, :ref:`write_jsonlines_to`, :ref:`write_jsonlines_to`, :ref:`write_jsonlines_to`, :ref:`write_raw_to`, :ref:`write_raw_to`, :ref:`write_raw_to`, :ref:`write_screen_to`, :ref:`write_screen_to`, :ref:`write_screen_to`, :ref:`write_to`, :ref:`write_to`, :ref:`write_view_to`, :ref:`write_view_to`, :ref:`write_view_to`
|
||||
|
||||
----
|
||||
|
||||
@ -1456,7 +1488,7 @@
|
||||
:write-csv-to /tmp/table.csv
|
||||
|
||||
**See Also**
|
||||
:ref:`alt_msg`, :ref:`append_to`, :ref:`create_logline_table`, :ref:`create_search_table`, :ref:`echo`, :ref:`echo`, :ref:`echoln`, :ref:`eval`, :ref:`export_session_to`, :ref:`export_session_to`, :ref:`pipe_line_to`, :ref:`pipe_to`, :ref:`rebuild`, :ref:`redirect_to`, :ref:`redirect_to`, :ref:`write_json_to`, :ref:`write_json_to`, :ref:`write_json_to`, :ref:`write_jsonlines_to`, :ref:`write_jsonlines_to`, :ref:`write_jsonlines_to`, :ref:`write_raw_to`, :ref:`write_raw_to`, :ref:`write_raw_to`, :ref:`write_screen_to`, :ref:`write_screen_to`, :ref:`write_screen_to`, :ref:`write_table_to`, :ref:`write_table_to`, :ref:`write_table_to`, :ref:`write_to`, :ref:`write_to`, :ref:`write_view_to`, :ref:`write_view_to`, :ref:`write_view_to`
|
||||
:ref:`alt_msg`, :ref:`append_to`, :ref:`cd`, :ref:`create_logline_table`, :ref:`create_search_table`, :ref:`echo`, :ref:`echo`, :ref:`echoln`, :ref:`eval`, :ref:`export_session_to`, :ref:`export_session_to`, :ref:`pipe_line_to`, :ref:`pipe_to`, :ref:`rebuild`, :ref:`redirect_to`, :ref:`redirect_to`, :ref:`sh`, :ref:`write_json_to`, :ref:`write_json_to`, :ref:`write_json_to`, :ref:`write_jsonlines_to`, :ref:`write_jsonlines_to`, :ref:`write_jsonlines_to`, :ref:`write_raw_to`, :ref:`write_raw_to`, :ref:`write_raw_to`, :ref:`write_screen_to`, :ref:`write_screen_to`, :ref:`write_screen_to`, :ref:`write_table_to`, :ref:`write_table_to`, :ref:`write_table_to`, :ref:`write_to`, :ref:`write_to`, :ref:`write_view_to`, :ref:`write_view_to`, :ref:`write_view_to`
|
||||
|
||||
----
|
||||
|
||||
@ -1480,7 +1512,7 @@
|
||||
:write-json-to /tmp/table.json
|
||||
|
||||
**See Also**
|
||||
:ref:`alt_msg`, :ref:`append_to`, :ref:`create_logline_table`, :ref:`create_search_table`, :ref:`echo`, :ref:`echo`, :ref:`echoln`, :ref:`eval`, :ref:`export_session_to`, :ref:`export_session_to`, :ref:`pipe_line_to`, :ref:`pipe_to`, :ref:`rebuild`, :ref:`redirect_to`, :ref:`redirect_to`, :ref:`write_csv_to`, :ref:`write_csv_to`, :ref:`write_csv_to`, :ref:`write_jsonlines_to`, :ref:`write_jsonlines_to`, :ref:`write_jsonlines_to`, :ref:`write_raw_to`, :ref:`write_raw_to`, :ref:`write_raw_to`, :ref:`write_screen_to`, :ref:`write_screen_to`, :ref:`write_screen_to`, :ref:`write_table_to`, :ref:`write_table_to`, :ref:`write_table_to`, :ref:`write_to`, :ref:`write_to`, :ref:`write_view_to`, :ref:`write_view_to`, :ref:`write_view_to`
|
||||
:ref:`alt_msg`, :ref:`append_to`, :ref:`cd`, :ref:`create_logline_table`, :ref:`create_search_table`, :ref:`echo`, :ref:`echo`, :ref:`echoln`, :ref:`eval`, :ref:`export_session_to`, :ref:`export_session_to`, :ref:`pipe_line_to`, :ref:`pipe_to`, :ref:`rebuild`, :ref:`redirect_to`, :ref:`redirect_to`, :ref:`sh`, :ref:`write_csv_to`, :ref:`write_csv_to`, :ref:`write_csv_to`, :ref:`write_jsonlines_to`, :ref:`write_jsonlines_to`, :ref:`write_jsonlines_to`, :ref:`write_raw_to`, :ref:`write_raw_to`, :ref:`write_raw_to`, :ref:`write_screen_to`, :ref:`write_screen_to`, :ref:`write_screen_to`, :ref:`write_table_to`, :ref:`write_table_to`, :ref:`write_table_to`, :ref:`write_to`, :ref:`write_to`, :ref:`write_view_to`, :ref:`write_view_to`, :ref:`write_view_to`
|
||||
|
||||
----
|
||||
|
||||
@ -1504,7 +1536,7 @@
|
||||
:write-jsonlines-to /tmp/table.json
|
||||
|
||||
**See Also**
|
||||
:ref:`alt_msg`, :ref:`append_to`, :ref:`create_logline_table`, :ref:`create_search_table`, :ref:`echo`, :ref:`echo`, :ref:`echoln`, :ref:`eval`, :ref:`export_session_to`, :ref:`export_session_to`, :ref:`pipe_line_to`, :ref:`pipe_to`, :ref:`rebuild`, :ref:`redirect_to`, :ref:`redirect_to`, :ref:`write_csv_to`, :ref:`write_csv_to`, :ref:`write_csv_to`, :ref:`write_json_to`, :ref:`write_json_to`, :ref:`write_json_to`, :ref:`write_raw_to`, :ref:`write_raw_to`, :ref:`write_raw_to`, :ref:`write_screen_to`, :ref:`write_screen_to`, :ref:`write_screen_to`, :ref:`write_table_to`, :ref:`write_table_to`, :ref:`write_table_to`, :ref:`write_to`, :ref:`write_to`, :ref:`write_view_to`, :ref:`write_view_to`, :ref:`write_view_to`
|
||||
:ref:`alt_msg`, :ref:`append_to`, :ref:`cd`, :ref:`create_logline_table`, :ref:`create_search_table`, :ref:`echo`, :ref:`echo`, :ref:`echoln`, :ref:`eval`, :ref:`export_session_to`, :ref:`export_session_to`, :ref:`pipe_line_to`, :ref:`pipe_to`, :ref:`rebuild`, :ref:`redirect_to`, :ref:`redirect_to`, :ref:`sh`, :ref:`write_csv_to`, :ref:`write_csv_to`, :ref:`write_csv_to`, :ref:`write_json_to`, :ref:`write_json_to`, :ref:`write_json_to`, :ref:`write_raw_to`, :ref:`write_raw_to`, :ref:`write_raw_to`, :ref:`write_screen_to`, :ref:`write_screen_to`, :ref:`write_screen_to`, :ref:`write_table_to`, :ref:`write_table_to`, :ref:`write_table_to`, :ref:`write_to`, :ref:`write_to`, :ref:`write_view_to`, :ref:`write_view_to`, :ref:`write_view_to`
|
||||
|
||||
----
|
||||
|
||||
@ -1529,7 +1561,7 @@
|
||||
:write-raw-to /tmp/table.txt
|
||||
|
||||
**See Also**
|
||||
:ref:`alt_msg`, :ref:`append_to`, :ref:`create_logline_table`, :ref:`create_search_table`, :ref:`echo`, :ref:`echo`, :ref:`echoln`, :ref:`eval`, :ref:`export_session_to`, :ref:`export_session_to`, :ref:`pipe_line_to`, :ref:`pipe_to`, :ref:`rebuild`, :ref:`redirect_to`, :ref:`redirect_to`, :ref:`write_csv_to`, :ref:`write_csv_to`, :ref:`write_csv_to`, :ref:`write_json_to`, :ref:`write_json_to`, :ref:`write_json_to`, :ref:`write_jsonlines_to`, :ref:`write_jsonlines_to`, :ref:`write_jsonlines_to`, :ref:`write_screen_to`, :ref:`write_screen_to`, :ref:`write_screen_to`, :ref:`write_table_to`, :ref:`write_table_to`, :ref:`write_table_to`, :ref:`write_to`, :ref:`write_to`, :ref:`write_view_to`, :ref:`write_view_to`, :ref:`write_view_to`
|
||||
:ref:`alt_msg`, :ref:`append_to`, :ref:`cd`, :ref:`create_logline_table`, :ref:`create_search_table`, :ref:`echo`, :ref:`echo`, :ref:`echoln`, :ref:`eval`, :ref:`export_session_to`, :ref:`export_session_to`, :ref:`pipe_line_to`, :ref:`pipe_to`, :ref:`rebuild`, :ref:`redirect_to`, :ref:`redirect_to`, :ref:`sh`, :ref:`write_csv_to`, :ref:`write_csv_to`, :ref:`write_csv_to`, :ref:`write_json_to`, :ref:`write_json_to`, :ref:`write_json_to`, :ref:`write_jsonlines_to`, :ref:`write_jsonlines_to`, :ref:`write_jsonlines_to`, :ref:`write_screen_to`, :ref:`write_screen_to`, :ref:`write_screen_to`, :ref:`write_table_to`, :ref:`write_table_to`, :ref:`write_table_to`, :ref:`write_to`, :ref:`write_to`, :ref:`write_view_to`, :ref:`write_view_to`, :ref:`write_view_to`
|
||||
|
||||
----
|
||||
|
||||
@ -1553,7 +1585,7 @@
|
||||
:write-screen-to /tmp/table.txt
|
||||
|
||||
**See Also**
|
||||
:ref:`alt_msg`, :ref:`append_to`, :ref:`create_logline_table`, :ref:`create_search_table`, :ref:`echo`, :ref:`echo`, :ref:`echoln`, :ref:`eval`, :ref:`export_session_to`, :ref:`export_session_to`, :ref:`pipe_line_to`, :ref:`pipe_to`, :ref:`rebuild`, :ref:`redirect_to`, :ref:`redirect_to`, :ref:`write_csv_to`, :ref:`write_csv_to`, :ref:`write_csv_to`, :ref:`write_json_to`, :ref:`write_json_to`, :ref:`write_json_to`, :ref:`write_jsonlines_to`, :ref:`write_jsonlines_to`, :ref:`write_jsonlines_to`, :ref:`write_raw_to`, :ref:`write_raw_to`, :ref:`write_raw_to`, :ref:`write_table_to`, :ref:`write_table_to`, :ref:`write_table_to`, :ref:`write_to`, :ref:`write_to`, :ref:`write_view_to`, :ref:`write_view_to`, :ref:`write_view_to`
|
||||
:ref:`alt_msg`, :ref:`append_to`, :ref:`cd`, :ref:`create_logline_table`, :ref:`create_search_table`, :ref:`echo`, :ref:`echo`, :ref:`echoln`, :ref:`eval`, :ref:`export_session_to`, :ref:`export_session_to`, :ref:`pipe_line_to`, :ref:`pipe_to`, :ref:`rebuild`, :ref:`redirect_to`, :ref:`redirect_to`, :ref:`sh`, :ref:`write_csv_to`, :ref:`write_csv_to`, :ref:`write_csv_to`, :ref:`write_json_to`, :ref:`write_json_to`, :ref:`write_json_to`, :ref:`write_jsonlines_to`, :ref:`write_jsonlines_to`, :ref:`write_jsonlines_to`, :ref:`write_raw_to`, :ref:`write_raw_to`, :ref:`write_raw_to`, :ref:`write_table_to`, :ref:`write_table_to`, :ref:`write_table_to`, :ref:`write_to`, :ref:`write_to`, :ref:`write_view_to`, :ref:`write_view_to`, :ref:`write_view_to`
|
||||
|
||||
----
|
||||
|
||||
@ -1577,7 +1609,7 @@
|
||||
:write-to /tmp/interesting-lines.txt
|
||||
|
||||
**See Also**
|
||||
:ref:`alt_msg`, :ref:`append_to`, :ref:`echo`, :ref:`echo`, :ref:`echoln`, :ref:`eval`, :ref:`export_session_to`, :ref:`export_session_to`, :ref:`pipe_line_to`, :ref:`pipe_to`, :ref:`rebuild`, :ref:`redirect_to`, :ref:`redirect_to`, :ref:`write_csv_to`, :ref:`write_csv_to`, :ref:`write_json_to`, :ref:`write_json_to`, :ref:`write_jsonlines_to`, :ref:`write_jsonlines_to`, :ref:`write_raw_to`, :ref:`write_raw_to`, :ref:`write_screen_to`, :ref:`write_screen_to`, :ref:`write_table_to`, :ref:`write_table_to`, :ref:`write_view_to`, :ref:`write_view_to`
|
||||
:ref:`alt_msg`, :ref:`append_to`, :ref:`cd`, :ref:`echo`, :ref:`echo`, :ref:`echoln`, :ref:`eval`, :ref:`export_session_to`, :ref:`export_session_to`, :ref:`pipe_line_to`, :ref:`pipe_to`, :ref:`rebuild`, :ref:`redirect_to`, :ref:`redirect_to`, :ref:`sh`, :ref:`write_csv_to`, :ref:`write_csv_to`, :ref:`write_json_to`, :ref:`write_json_to`, :ref:`write_jsonlines_to`, :ref:`write_jsonlines_to`, :ref:`write_raw_to`, :ref:`write_raw_to`, :ref:`write_screen_to`, :ref:`write_screen_to`, :ref:`write_table_to`, :ref:`write_table_to`, :ref:`write_view_to`, :ref:`write_view_to`
|
||||
|
||||
----
|
||||
|
||||
@ -1601,7 +1633,7 @@
|
||||
:write-view-to /tmp/table.txt
|
||||
|
||||
**See Also**
|
||||
:ref:`alt_msg`, :ref:`append_to`, :ref:`create_logline_table`, :ref:`create_search_table`, :ref:`echo`, :ref:`echo`, :ref:`echoln`, :ref:`eval`, :ref:`export_session_to`, :ref:`export_session_to`, :ref:`pipe_line_to`, :ref:`pipe_to`, :ref:`rebuild`, :ref:`redirect_to`, :ref:`redirect_to`, :ref:`write_csv_to`, :ref:`write_csv_to`, :ref:`write_csv_to`, :ref:`write_json_to`, :ref:`write_json_to`, :ref:`write_json_to`, :ref:`write_jsonlines_to`, :ref:`write_jsonlines_to`, :ref:`write_jsonlines_to`, :ref:`write_raw_to`, :ref:`write_raw_to`, :ref:`write_raw_to`, :ref:`write_screen_to`, :ref:`write_screen_to`, :ref:`write_screen_to`, :ref:`write_table_to`, :ref:`write_table_to`, :ref:`write_table_to`, :ref:`write_to`, :ref:`write_to`
|
||||
:ref:`alt_msg`, :ref:`append_to`, :ref:`cd`, :ref:`create_logline_table`, :ref:`create_search_table`, :ref:`echo`, :ref:`echo`, :ref:`echoln`, :ref:`eval`, :ref:`export_session_to`, :ref:`export_session_to`, :ref:`pipe_line_to`, :ref:`pipe_to`, :ref:`rebuild`, :ref:`redirect_to`, :ref:`redirect_to`, :ref:`sh`, :ref:`write_csv_to`, :ref:`write_csv_to`, :ref:`write_csv_to`, :ref:`write_json_to`, :ref:`write_json_to`, :ref:`write_json_to`, :ref:`write_jsonlines_to`, :ref:`write_jsonlines_to`, :ref:`write_jsonlines_to`, :ref:`write_raw_to`, :ref:`write_raw_to`, :ref:`write_raw_to`, :ref:`write_screen_to`, :ref:`write_screen_to`, :ref:`write_screen_to`, :ref:`write_table_to`, :ref:`write_table_to`, :ref:`write_table_to`, :ref:`write_to`, :ref:`write_to`
|
||||
|
||||
----
|
||||
|
||||
|
@ -59,6 +59,7 @@
|
||||
#include "fmtlib/fmt/format.h"
|
||||
#include "line_buffer.hh"
|
||||
#include "lnav_util.hh"
|
||||
#include "scn/scn.h"
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
@ -408,7 +409,12 @@ line_buffer::set_fd(auto_fd& fd)
|
||||
char gz_id[2 + 1 + 1 + 4];
|
||||
|
||||
if (pread(fd, gz_id, sizeof(gz_id), 0) == sizeof(gz_id)) {
|
||||
if (gz_id[0] == '\037' && gz_id[1] == '\213') {
|
||||
if (gz_id[0] == 'L' && gz_id[1] == 0 && gz_id[2] == 'N'
|
||||
&& gz_id[3] == 1 && gz_id[4] == 0)
|
||||
{
|
||||
this->lb_line_metadata = true;
|
||||
this->lb_file_offset = 8;
|
||||
} else if (gz_id[0] == '\037' && gz_id[1] == '\213') {
|
||||
int gzfd = dup(fd);
|
||||
|
||||
log_perror(fcntl(gzfd, F_SETFD, FD_CLOEXEC));
|
||||
@ -1022,11 +1028,16 @@ line_buffer::fill_range(file_off_t start, ssize_t max_length)
|
||||
Result<line_info, std::string>
|
||||
line_buffer::load_next_line(file_range prev_line)
|
||||
{
|
||||
const char* line_start = nullptr;
|
||||
bool done = false;
|
||||
line_info retval;
|
||||
|
||||
require(this->lb_fd != -1);
|
||||
|
||||
if (this->lb_line_metadata && prev_line.fr_offset == 0) {
|
||||
prev_line.fr_offset = 8;
|
||||
}
|
||||
|
||||
auto offset = prev_line.next_offset();
|
||||
ssize_t request_size = INITIAL_REQUEST_SIZE;
|
||||
retval.li_file_range.fr_offset = offset;
|
||||
@ -1044,9 +1055,17 @@ line_buffer::load_next_line(file_range prev_line)
|
||||
return Ok(retval);
|
||||
}
|
||||
}
|
||||
if (prev_line.next_offset() == 0) {
|
||||
auto is_utf_res = is_utf8(string_fragment::from_bytes(
|
||||
this->lb_buffer.begin(), this->lb_buffer.size()));
|
||||
this->lb_is_utf8 = is_utf_res.is_valid();
|
||||
if (!this->lb_is_utf8) {
|
||||
log_warning("input is not utf8 -- %s", is_utf_res.usr_message);
|
||||
}
|
||||
}
|
||||
while (!done) {
|
||||
auto old_retval_size = retval.li_file_range.fr_size;
|
||||
const char *line_start, *lf = nullptr;
|
||||
const char* lf = nullptr;
|
||||
|
||||
/* Find the data in the cache and */
|
||||
line_start = this->get_range(offset, retval.li_file_range.fr_size);
|
||||
@ -1177,11 +1196,29 @@ line_buffer::load_next_line(file_range prev_line)
|
||||
= retval.li_utf8_scan_result.usr_has_ansi;
|
||||
retval.li_file_range.fr_metadata.m_valid_utf
|
||||
= retval.li_utf8_scan_result.is_valid();
|
||||
|
||||
if (this->lb_line_metadata) {
|
||||
auto sv = scn::string_view{
|
||||
line_start,
|
||||
(size_t) retval.li_file_range.fr_size,
|
||||
};
|
||||
|
||||
char level;
|
||||
auto scan_res = scn::scan(sv,
|
||||
"{}.{}:{};",
|
||||
retval.li_timestamp.tv_sec,
|
||||
retval.li_timestamp.tv_usec,
|
||||
level);
|
||||
if (scan_res) {
|
||||
retval.li_level = abbrev2level(&level, 1);
|
||||
}
|
||||
}
|
||||
|
||||
return Ok(retval);
|
||||
}
|
||||
|
||||
Result<shared_buffer_ref, std::string>
|
||||
line_buffer::read_range(const file_range fr)
|
||||
line_buffer::read_range(file_range fr)
|
||||
{
|
||||
shared_buffer_ref retval;
|
||||
const char* line_start;
|
||||
@ -1214,6 +1251,15 @@ line_buffer::read_range(const file_range fr)
|
||||
return Err(fmt::format(
|
||||
FMT_STRING("short-read (need: {}; avail: {})"), fr.fr_size, avail));
|
||||
}
|
||||
if (this->lb_line_metadata) {
|
||||
auto new_start
|
||||
= static_cast<const char*>(memchr(line_start, ';', fr.fr_size));
|
||||
if (new_start) {
|
||||
auto offset = new_start - line_start + 1;
|
||||
line_start += offset;
|
||||
fr.fr_size -= offset;
|
||||
}
|
||||
}
|
||||
retval.share(this->lb_share_manager, line_start, fr.fr_size);
|
||||
retval.get_metadata() = fr.fr_metadata;
|
||||
|
||||
|
@ -48,11 +48,16 @@
|
||||
#include "base/is_utf8.hh"
|
||||
#include "base/lnav_log.hh"
|
||||
#include "base/result.h"
|
||||
#include "log_level.hh"
|
||||
#include "safe/safe.h"
|
||||
#include "shared_buffer.hh"
|
||||
|
||||
struct line_info {
|
||||
file_range li_file_range;
|
||||
struct timeval li_timestamp {
|
||||
0, 0
|
||||
};
|
||||
log_level_t li_level{LEVEL_UNKNOWN};
|
||||
bool li_partial{false};
|
||||
utf8_scan_result li_utf8_scan_result{};
|
||||
};
|
||||
@ -175,6 +180,10 @@ public:
|
||||
|
||||
bool is_compressed() const { return this->lb_compressed; }
|
||||
|
||||
bool is_header_utf8() const { return this->lb_is_utf8; }
|
||||
|
||||
bool has_line_metadata() const { return this->lb_line_metadata; }
|
||||
|
||||
file_off_t get_read_offset(file_off_t off) const
|
||||
{
|
||||
if (this->is_compressed()) {
|
||||
@ -331,6 +340,7 @@ private:
|
||||
auto_fd lb_fd; /*< The file to read data from. */
|
||||
safe_gz_indexed lb_gz_file; /*< File reader for gzipped files. */
|
||||
bool lb_bz_file{false}; /*< Flag set for bzip2 compressed files. */
|
||||
bool lb_line_metadata{false};
|
||||
|
||||
auto_buffer lb_buffer{auto_buffer::alloc(DEFAULT_LINE_BUFFER_SIZE)};
|
||||
nonstd::optional<auto_buffer> lb_alt_buffer;
|
||||
@ -355,6 +365,7 @@ private:
|
||||
time_t lb_file_time{0};
|
||||
bool lb_seekable{false}; /*< Flag set for seekable file descriptors. */
|
||||
bool lb_compressed{false};
|
||||
bool lb_is_utf8{true};
|
||||
file_off_t lb_last_line_offset{-1}; /*< */
|
||||
|
||||
std::vector<uint32_t> lb_line_starts;
|
||||
|
265
src/lnav.cc
265
src/lnav.cc
@ -114,7 +114,7 @@
|
||||
#include "log_vtab_impl.hh"
|
||||
#include "logfile.hh"
|
||||
#include "logfile_sub_source.hh"
|
||||
#include "piper_proc.hh"
|
||||
#include "piper.looper.hh"
|
||||
#include "readline_curses.hh"
|
||||
#include "readline_highlighters.hh"
|
||||
#include "regexp_vtab.hh"
|
||||
@ -218,8 +218,6 @@ static const std::vector<std::string> DEFAULT_DB_KEY_NAMES = {
|
||||
"st_gid",
|
||||
};
|
||||
|
||||
const static file_ssize_t MAX_STDIN_CAPTURE_SIZE = 10 * 1024 * 1024;
|
||||
|
||||
static auto bound_pollable_supervisor
|
||||
= injector::bind<pollable_supervisor>::to_singleton();
|
||||
|
||||
@ -543,11 +541,14 @@ usage()
|
||||
|
||||
ex3_term.append(lnav::roles::ok("$"))
|
||||
.append(" ")
|
||||
.append(lnav::roles::file("make"))
|
||||
.append(" 2>&1 | ")
|
||||
.append(lnav::roles::file("lnav"))
|
||||
.append(" ")
|
||||
.append("-t"_symbol)
|
||||
.append("-e"_symbol)
|
||||
.append(" '")
|
||||
.append(lnav::roles::file("make"))
|
||||
.append(" ")
|
||||
.append("-j4"_symbol)
|
||||
.append("' ")
|
||||
.pad_to(40)
|
||||
.with_attr_for_all(VC_ROLE.value(role_t::VCR_QUOTED_CODE));
|
||||
|
||||
@ -623,19 +624,6 @@ make it easier to navigate through files quickly.
|
||||
.append(" ")
|
||||
.append("Load older rotated log files as well.\n")
|
||||
.append(" ")
|
||||
.append("-t"_symbol)
|
||||
.append(" ")
|
||||
.append(R"(Prepend timestamps to the lines of data being read in
|
||||
from the standard input.
|
||||
)")
|
||||
.append(" ")
|
||||
.append("-w"_symbol)
|
||||
.append(" ")
|
||||
.append("file"_variable)
|
||||
.append(" ")
|
||||
.append("Write the contents of the standard input to this file.\n")
|
||||
.append("\n")
|
||||
.append(" ")
|
||||
.append("-c"_symbol)
|
||||
.append(" ")
|
||||
.append("cmd"_variable)
|
||||
@ -648,6 +636,12 @@ make it easier to navigate through files quickly.
|
||||
.append(" ")
|
||||
.append("Execute the commands in the given file.\n")
|
||||
.append(" ")
|
||||
.append("-e"_symbol)
|
||||
.append(" ")
|
||||
.append("cmd"_variable)
|
||||
.append(" ")
|
||||
.append("Execute a shell command-line.\n")
|
||||
.append(" ")
|
||||
.append("-n"_symbol)
|
||||
.append(" ")
|
||||
.append("Run without the curses UI. (headless mode)\n")
|
||||
@ -704,7 +698,7 @@ make it easier to navigate through files quickly.
|
||||
.append("\u2022"_list_glyph)
|
||||
.append(" To watch the output of ")
|
||||
.append(lnav::roles::file("make"))
|
||||
.append(" with timestamps prepended:\n")
|
||||
.append(":\n")
|
||||
.append(" ")
|
||||
.append(ex3_term)
|
||||
.append("\n\n")
|
||||
@ -723,8 +717,8 @@ make it easier to navigate through files quickly.
|
||||
.append(lnav::roles::file(lnav::paths::dotlnav().string()))
|
||||
.append("\n\n ")
|
||||
.append("\u2022"_list_glyph)
|
||||
.append(" Local copies of remote files and files extracted from\n")
|
||||
.append(" archives are stored in:\n")
|
||||
.append(" Local copies of remote files, files extracted from\n")
|
||||
.append(" archives, execution output, and so on are stored in:\n")
|
||||
.append(" \U0001F4C2 ")
|
||||
.append(lnav::roles::file(lnav::paths::workdir().string()))
|
||||
.append("\n\n")
|
||||
@ -982,18 +976,6 @@ match_escape_seq(const char* keyseq)
|
||||
static void
|
||||
gather_pipers()
|
||||
{
|
||||
for (auto iter = lnav_data.ld_pipers.begin();
|
||||
iter != lnav_data.ld_pipers.end();)
|
||||
{
|
||||
pid_t child_pid = (*iter)->get_child_pid();
|
||||
if ((*iter)->has_exited()) {
|
||||
log_info("child piper has exited -- %d", child_pid);
|
||||
iter = lnav_data.ld_pipers.erase(iter);
|
||||
} else {
|
||||
++iter;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto iter = lnav_data.ld_child_pollers.begin();
|
||||
iter != lnav_data.ld_child_pollers.end();)
|
||||
{
|
||||
@ -1010,18 +992,25 @@ gather_pipers()
|
||||
static void
|
||||
wait_for_pipers()
|
||||
{
|
||||
static const auto MAX_SLEEP_TIME = std::chrono::milliseconds(300);
|
||||
auto sleep_time = std::chrono::milliseconds(10);
|
||||
|
||||
for (;;) {
|
||||
gather_pipers();
|
||||
if (lnav_data.ld_pipers.empty() && lnav_data.ld_child_pollers.empty()) {
|
||||
auto piper_count = lnav_data.ld_active_files.active_pipers();
|
||||
if (piper_count == 0 && lnav_data.ld_child_pollers.empty()) {
|
||||
log_debug("all pipers finished");
|
||||
break;
|
||||
}
|
||||
usleep(10000);
|
||||
std::this_thread::sleep_for(sleep_time);
|
||||
rebuild_indexes();
|
||||
|
||||
log_debug("%d pipers and %d children still active",
|
||||
lnav_data.ld_pipers.size(),
|
||||
log_debug("%d pipers and %d children are still active",
|
||||
piper_count,
|
||||
lnav_data.ld_child_pollers.size());
|
||||
if (sleep_time < MAX_SLEEP_TIME) {
|
||||
sleep_time = sleep_time * 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -2106,12 +2095,6 @@ enum class verbosity_t : int {
|
||||
verbose,
|
||||
};
|
||||
|
||||
struct stdin_options_t {
|
||||
ghc::filesystem::path so_out;
|
||||
bool so_timestamp{false};
|
||||
auto_fd so_out_fd;
|
||||
};
|
||||
|
||||
int
|
||||
main(int argc, char* argv[])
|
||||
{
|
||||
@ -2120,12 +2103,9 @@ main(int argc, char* argv[])
|
||||
exec_context& ec = lnav_data.ld_exec_context;
|
||||
int retval = EXIT_SUCCESS;
|
||||
|
||||
std::shared_ptr<piper_proc> stdin_reader;
|
||||
stdin_options_t stdin_opts;
|
||||
bool exec_stdin = false, load_stdin = false, stdin_captured = false;
|
||||
bool exec_stdin = false, load_stdin = false;
|
||||
mode_flags_t mode_flags;
|
||||
const char* LANG = getenv("LANG");
|
||||
ghc::filesystem::path stdin_tmp_path;
|
||||
verbosity_t verbosity = verbosity_t::standard;
|
||||
|
||||
if (LANG == nullptr || strcmp(LANG, "C") == 0) {
|
||||
@ -2314,9 +2294,6 @@ SELECT tbl_name FROM sqlite_master WHERE sql LIKE 'CREATE VIRTUAL TABLE%'
|
||||
auto* install_flag
|
||||
= app.add_flag("-i", mode_flags.mf_install, "install");
|
||||
app.add_flag("-u", mode_flags.mf_update_formats, "update");
|
||||
auto* write_flag = app.add_option("-w", stdin_opts.so_out, "write");
|
||||
auto* ts_flag
|
||||
= app.add_flag("-t", stdin_opts.so_timestamp, "timestamp");
|
||||
auto* no_default_flag
|
||||
= app.add_flag("-N", mode_flags.mf_no_default, "no def");
|
||||
auto* rotated_flag = app.add_flag(
|
||||
@ -2391,15 +2368,24 @@ SELECT tbl_name FROM sqlite_master WHERE sql LIKE 'CREATE VIRTUAL TABLE%'
|
||||
->allow_extra_args(false)
|
||||
->each(file_appender);
|
||||
|
||||
auto shexec_appender = [&mode_flags](std::string cmd) {
|
||||
mode_flags.mf_no_default = true;
|
||||
lnav_data.ld_commands.emplace_back(
|
||||
fmt::format(FMT_STRING(":sh {}"), cmd));
|
||||
};
|
||||
auto* cmdline_opt = app.add_option("-e")
|
||||
->each(shexec_appender)
|
||||
->allow_extra_args(false)
|
||||
->trigger_on_parse(true);
|
||||
|
||||
install_flag->needs(file_opt);
|
||||
install_flag->excludes(write_flag,
|
||||
ts_flag,
|
||||
no_default_flag,
|
||||
install_flag->excludes(no_default_flag,
|
||||
rotated_flag,
|
||||
recurse_flag,
|
||||
headless_flag,
|
||||
cmd_opt,
|
||||
exec_file_opt);
|
||||
exec_file_opt,
|
||||
cmdline_opt);
|
||||
}
|
||||
|
||||
auto is_mmode = argc >= 2 && strcmp(argv[1], "-m") == 0;
|
||||
@ -2668,6 +2654,7 @@ SELECT tbl_name FROM sqlite_master WHERE sql LIKE 'CREATE VIRTUAL TABLE%'
|
||||
log_fos->fos_contexts.emplace("", false, true);
|
||||
lnav_data.ld_views[LNV_LOG]
|
||||
.set_sub_source(&lnav_data.ld_log_source)
|
||||
#if 0
|
||||
.set_delegate(std::make_shared<action_delegate>(
|
||||
lnav_data.ld_log_source,
|
||||
[](auto child_pid) { lnav_data.ld_children.push_back(child_pid); },
|
||||
@ -2677,11 +2664,14 @@ SELECT tbl_name FROM sqlite_master WHERE sql LIKE 'CREATE VIRTUAL TABLE%'
|
||||
pp->get_fd());
|
||||
lnav_data.ld_files_to_front.template emplace_back(desc, 0_vl);
|
||||
}))
|
||||
#endif
|
||||
.add_input_delegate(lnav_data.ld_log_source)
|
||||
.set_tail_space(2_vl)
|
||||
.set_overlay_source(log_fos);
|
||||
auto sel_reload_delegate = [](textview_curses& tc) {
|
||||
if (lnav_config.lc_ui_movement.mode == config_movement_mode::CURSOR) {
|
||||
if (!(lnav_data.ld_flags & LNF_HEADLESS)
|
||||
&& lnav_config.lc_ui_movement.mode == config_movement_mode::CURSOR)
|
||||
{
|
||||
tc.set_selectable(true);
|
||||
}
|
||||
};
|
||||
@ -2803,6 +2793,7 @@ SELECT tbl_name FROM sqlite_master WHERE sql LIKE 'CREATE VIRTUAL TABLE%'
|
||||
lnav_data.ld_mode = ln_mode_t::PAGING;
|
||||
|
||||
if ((isatty(STDIN_FILENO) || is_dev_null(STDIN_FILENO)) && file_args.empty()
|
||||
&& lnav_data.ld_active_files.fc_file_names.empty()
|
||||
&& !mode_flags.mf_no_default)
|
||||
{
|
||||
char start_dir[FILENAME_MAX];
|
||||
@ -2875,8 +2866,8 @@ SELECT tbl_name FROM sqlite_master WHERE sql LIKE 'CREATE VIRTUAL TABLE%'
|
||||
{
|
||||
auto ul = std::make_shared<url_loader>(file_path);
|
||||
|
||||
lnav_data.ld_active_files.fc_file_names[file_path].with_fd(
|
||||
ul->copy_fd());
|
||||
lnav_data.ld_active_files.fc_file_names[ul->get_path()]
|
||||
.with_filename(file_path);
|
||||
isc::to<curl_looper&, services::curl_streamer_t>().send(
|
||||
[ul](auto& clooper) { clooper.add_request(ul); });
|
||||
}
|
||||
@ -2918,25 +2909,15 @@ SELECT tbl_name FROM sqlite_master WHERE sql LIKE 'CREATE VIRTUAL TABLE%'
|
||||
.with_errno_reason());
|
||||
retval = EXIT_FAILURE;
|
||||
} else {
|
||||
auto fifo_tmp_fd
|
||||
= lnav::filesystem::open_temp_file(
|
||||
ghc::filesystem::temp_directory_path()
|
||||
/ "lnav.fifo.XXXXXX")
|
||||
.map([](auto&& pair) {
|
||||
ghc::filesystem::remove(pair.first);
|
||||
|
||||
return std::move(pair.second);
|
||||
})
|
||||
.expect("Cannot create temporary file for FIFO");
|
||||
auto fifo_piper = std::make_shared<piper_proc>(
|
||||
std::move(fifo_fd), false, std::move(fifo_tmp_fd));
|
||||
auto fifo_out_fd = fifo_piper->get_fd();
|
||||
auto desc = fmt::format(FMT_STRING("FIFO [{}]"),
|
||||
lnav_data.ld_fifo_counter++);
|
||||
auto create_piper_res = lnav::piper::create_looper(
|
||||
desc, std::move(fifo_fd), auto_fd{});
|
||||
|
||||
lnav_data.ld_active_files.fc_file_names[desc].with_fd(
|
||||
std::move(fifo_out_fd));
|
||||
lnav_data.ld_pipers.push_back(fifo_piper);
|
||||
if (create_piper_res.isOk()) {
|
||||
lnav_data.ld_active_files.fc_file_names[desc].with_piper(
|
||||
create_piper_res.unwrap());
|
||||
}
|
||||
}
|
||||
} else if ((abspath = realpath(file_path.c_str(), nullptr)) == nullptr)
|
||||
{
|
||||
@ -3039,44 +3020,52 @@ SELECT tbl_name FROM sqlite_master WHERE sql LIKE 'CREATE VIRTUAL TABLE%'
|
||||
retval = EXIT_FAILURE;
|
||||
}
|
||||
|
||||
nonstd::optional<ghc::filesystem::path> stdin_pattern;
|
||||
if (load_stdin && !isatty(STDIN_FILENO) && !is_dev_null(STDIN_FILENO)
|
||||
&& !exec_stdin)
|
||||
{
|
||||
if (stdin_opts.so_out.empty()) {
|
||||
auto pattern
|
||||
= lnav::paths::dotlnav() / "stdin-captures/stdin.XXXXXX";
|
||||
static const std::string STDIN_NAME = "stdin";
|
||||
struct stat stdin_st;
|
||||
|
||||
auto open_result = lnav::filesystem::open_temp_file(pattern);
|
||||
if (open_result.isErr()) {
|
||||
fprintf(stderr,
|
||||
"Unable to open temporary file for stdin: %s",
|
||||
open_result.unwrapErr().c_str());
|
||||
return EXIT_FAILURE;
|
||||
if (fstat(STDIN_FILENO, &stdin_st) == -1) {
|
||||
lnav::console::print(
|
||||
stderr,
|
||||
lnav::console::user_message::error("unable to stat() stdin")
|
||||
.with_errno_reason());
|
||||
retval = EXIT_FAILURE;
|
||||
} else if (S_ISFIFO(stdin_st.st_mode)) {
|
||||
auto stdin_piper_res = lnav::piper::create_looper(
|
||||
STDIN_NAME, auto_fd::dup_of(STDIN_FILENO), auto_fd{});
|
||||
if (stdin_piper_res.isOk()) {
|
||||
auto stdin_piper = stdin_piper_res.unwrap();
|
||||
stdin_pattern = stdin_piper.get_out_pattern();
|
||||
lnav_data.ld_active_files.fc_file_names[stdin_piper.get_name()]
|
||||
.with_piper(stdin_piper)
|
||||
.with_include_in_session(false);
|
||||
}
|
||||
} else if (S_ISREG(stdin_st.st_mode)) {
|
||||
// The shell connected a file directly, just open it up and add it
|
||||
// in here.
|
||||
auto loo = logfile_open_options{}
|
||||
.with_filename(STDIN_NAME)
|
||||
.with_include_in_session(false);
|
||||
|
||||
auto open_res
|
||||
= logfile::open(STDIN_NAME, loo, auto_fd::dup_of(STDIN_FILENO));
|
||||
|
||||
auto temp_pair = open_result.unwrap();
|
||||
stdin_tmp_path = temp_pair.first;
|
||||
stdin_opts.so_out_fd = std::move(temp_pair.second);
|
||||
} else {
|
||||
auto open_res = lnav::filesystem::create_file(
|
||||
stdin_opts.so_out, O_RDWR | O_TRUNC, 0600);
|
||||
if (open_res.isErr()) {
|
||||
fmt::print(stderr, "error: {}\n", open_res.unwrapErr());
|
||||
return EXIT_FAILURE;
|
||||
lnav::console::print(
|
||||
stderr,
|
||||
lnav::console::user_message::error("unable to open stdin")
|
||||
.with_reason(open_res.unwrapErr()));
|
||||
retval = EXIT_FAILURE;
|
||||
} else {
|
||||
file_collection fc;
|
||||
|
||||
fc.fc_files.emplace_back(open_res.unwrap());
|
||||
update_active_files(fc);
|
||||
}
|
||||
|
||||
stdin_opts.so_out_fd = open_res.unwrap();
|
||||
}
|
||||
|
||||
stdin_captured = true;
|
||||
stdin_reader
|
||||
= std::make_shared<piper_proc>(auto_fd(STDIN_FILENO),
|
||||
stdin_opts.so_timestamp,
|
||||
std::move(stdin_opts.so_out_fd));
|
||||
lnav_data.ld_active_files.fc_file_names["stdin"]
|
||||
.with_fd(stdin_reader->get_fd())
|
||||
.with_include_in_session(false);
|
||||
lnav_data.ld_pipers.push_back(stdin_reader);
|
||||
}
|
||||
|
||||
if (!isatty(STDIN_FILENO) && isatty(STDOUT_FILENO)) {
|
||||
@ -3085,7 +3074,7 @@ SELECT tbl_name FROM sqlite_master WHERE sql LIKE 'CREATE VIRTUAL TABLE%'
|
||||
}
|
||||
}
|
||||
|
||||
if (retval == EXIT_SUCCESS
|
||||
if (retval == EXIT_SUCCESS && lnav_data.ld_active_files.fc_files.empty()
|
||||
&& lnav_data.ld_active_files.fc_file_names.empty()
|
||||
&& lnav_data.ld_commands.empty()
|
||||
&& !(lnav_data.ld_show_help_view || mode_flags.mf_no_default))
|
||||
@ -3155,6 +3144,9 @@ SELECT tbl_name FROM sqlite_master WHERE sql LIKE 'CREATE VIRTUAL TABLE%'
|
||||
|
||||
view_colors::init(true);
|
||||
rescan_files(true);
|
||||
wait_for_pipers();
|
||||
rescan_files(true);
|
||||
rebuild_indexes_repeatedly();
|
||||
if (!lnav_data.ld_active_files.fc_name_to_errors.empty()) {
|
||||
for (const auto& pair :
|
||||
lnav_data.ld_active_files.fc_name_to_errors)
|
||||
@ -3198,6 +3190,7 @@ SELECT tbl_name FROM sqlite_master WHERE sql LIKE 'CREATE VIRTUAL TABLE%'
|
||||
tailer::cleanup_cache();
|
||||
line_buffer::cleanup_cache();
|
||||
wait_for_pipers();
|
||||
rescan_files(true);
|
||||
isc::to<curl_looper&, services::curl_streamer_t>()
|
||||
.send_and_wait(
|
||||
[](auto& clooper) { clooper.process_all(); });
|
||||
@ -3332,54 +3325,26 @@ SELECT tbl_name FROM sqlite_master WHERE sql LIKE 'CREATE VIRTUAL TABLE%'
|
||||
|
||||
// When reading from stdin, tell the user where the capture file is
|
||||
// stored so they can look at it later.
|
||||
if (stdin_captured && stdin_opts.so_out.empty()
|
||||
&& !(lnav_data.ld_flags & LNF_HEADLESS))
|
||||
{
|
||||
auto stdin_fd = stdin_reader->get_fd();
|
||||
struct stat stdin_stat;
|
||||
nonstd::optional<file_ssize_t> stdin_size;
|
||||
|
||||
// NB: the file can be deleted by the time we get here
|
||||
fchmod(stdin_fd.get(), S_IRUSR);
|
||||
if (fstat(stdin_fd.get(), &stdin_stat) != -1) {
|
||||
stdin_size = stdin_stat.st_size;
|
||||
}
|
||||
if (!ghc::filesystem::exists(stdin_tmp_path)
|
||||
|| verbosity == verbosity_t::quiet || !stdin_size
|
||||
|| stdin_size.value() == 0
|
||||
|| stdin_size.value() > MAX_STDIN_CAPTURE_SIZE)
|
||||
if (stdin_pattern && !(lnav_data.ld_flags & LNF_HEADLESS)) {
|
||||
file_size_t stdin_size = 0;
|
||||
for (const auto& ent : ghc::filesystem::directory_iterator(
|
||||
stdin_pattern.value().parent_path()))
|
||||
{
|
||||
std::error_code rm_err_code;
|
||||
|
||||
log_info("not saving stdin capture -- %s (size=%d)",
|
||||
stdin_tmp_path.c_str(),
|
||||
stdin_size.value_or(-1));
|
||||
ghc::filesystem::remove(stdin_tmp_path, rm_err_code);
|
||||
} else {
|
||||
auto home = getenv_opt("HOME");
|
||||
auto path_str = stdin_tmp_path.string();
|
||||
|
||||
if (home && startswith(path_str, home.value())) {
|
||||
path_str = path_str.substr(strlen(home.value()));
|
||||
if (path_str[0] != '/') {
|
||||
path_str.insert(0, 1, '/');
|
||||
}
|
||||
path_str.insert(0, 1, '~');
|
||||
}
|
||||
|
||||
lnav::console::print(
|
||||
stderr,
|
||||
lnav::console::user_message::info(
|
||||
attr_line_t()
|
||||
.append(lnav::roles::number(humanize::file_size(
|
||||
stdin_size.value(), humanize::alignment::none)))
|
||||
.append(" of data from stdin was captured and "
|
||||
"will be saved for one day. You can "
|
||||
"reopen it by running:\n")
|
||||
.appendf(FMT_STRING(" {} "),
|
||||
lnav_data.ld_program_name)
|
||||
.append(lnav::roles::file(path_str))));
|
||||
stdin_size += ent.file_size();
|
||||
}
|
||||
|
||||
lnav::console::print(
|
||||
stderr,
|
||||
lnav::console::user_message::info(
|
||||
attr_line_t()
|
||||
.append(lnav::roles::number(humanize::file_size(
|
||||
stdin_size, humanize::alignment::none)))
|
||||
.append(" of data from stdin was captured and "
|
||||
"will be saved for one day. You can "
|
||||
"reopen it by running:\n")
|
||||
.appendf(FMT_STRING(" {} "),
|
||||
lnav_data.ld_program_name)
|
||||
.append(lnav::roles::file(stdin_pattern.value()))));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -62,7 +62,6 @@
|
||||
#include "log_format_loader.hh"
|
||||
#include "log_vtab_impl.hh"
|
||||
#include "logfile.hh"
|
||||
#include "piper_proc.hh"
|
||||
#include "plain_text_source.hh"
|
||||
#include "preview_status_source.hh"
|
||||
#include "readline_curses.hh"
|
||||
@ -243,7 +242,6 @@ struct lnav_data_t {
|
||||
std::unordered_map<std::string, std::string> ld_table_ddl;
|
||||
|
||||
std::list<pid_t> ld_children;
|
||||
std::list<std::shared_ptr<piper_proc>> ld_pipers;
|
||||
|
||||
input_state_tracker ld_input_state;
|
||||
input_dispatcher ld_input_dispatcher;
|
||||
|
@ -2528,8 +2528,8 @@ com_open(exec_context& ec, std::string cmdline, std::vector<std::string>& args)
|
||||
if (!ec.ec_dry_run) {
|
||||
auto ul = std::make_shared<url_loader>(fn);
|
||||
|
||||
lnav_data.ld_active_files.fc_file_names[fn].with_fd(
|
||||
ul->copy_fd());
|
||||
lnav_data.ld_active_files.fc_file_names[ul->get_path()]
|
||||
.with_filename(fn);
|
||||
isc::to<curl_looper&, services::curl_streamer_t>().send(
|
||||
[ul](auto& clooper) { clooper.add_request(ul); });
|
||||
lnav_data.ld_files_to_front.emplace_back(fn, file_loc);
|
||||
@ -2571,25 +2571,20 @@ com_open(exec_context& ec, std::string cmdline, std::vector<std::string>& args)
|
||||
} else if (ec.ec_dry_run) {
|
||||
retval = "";
|
||||
} else {
|
||||
auto fifo_piper = std::make_shared<piper_proc>(
|
||||
std::move(fifo_fd),
|
||||
false,
|
||||
lnav::filesystem::open_temp_file(
|
||||
ghc::filesystem::temp_directory_path()
|
||||
/ "lnav.fifo.XXXXXX")
|
||||
.map([](auto pair) {
|
||||
ghc::filesystem::remove(pair.first);
|
||||
|
||||
return pair;
|
||||
})
|
||||
.expect("Cannot create temporary file for FIFO")
|
||||
.second);
|
||||
auto fifo_out_fd = fifo_piper->get_fd();
|
||||
auto desc = fmt::format(FMT_STRING("FIFO [{}]"),
|
||||
lnav_data.ld_fifo_counter++);
|
||||
lnav_data.ld_active_files.fc_file_names[desc].with_fd(
|
||||
std::move(fifo_out_fd));
|
||||
lnav_data.ld_pipers.push_back(fifo_piper);
|
||||
auto create_piper_res = lnav::piper::create_looper(
|
||||
desc, std::move(fifo_fd), auto_fd{});
|
||||
if (create_piper_res.isErr()) {
|
||||
auto um = lnav::console::user_message::error(
|
||||
attr_line_t("cannot create piper: ")
|
||||
.append(lnav::roles::file(fn)))
|
||||
.with_reason(create_piper_res.unwrapErr())
|
||||
.with_snippets(ec.ec_source);
|
||||
return Err(um);
|
||||
}
|
||||
lnav_data.ld_active_files.fc_file_names[desc].with_piper(
|
||||
create_piper_res.unwrap());
|
||||
}
|
||||
} else if ((abspath = realpath(fn.c_str(), nullptr)) == nullptr) {
|
||||
auto um = lnav::console::user_message::error(
|
||||
@ -4084,6 +4079,127 @@ com_rebuild(exec_context& ec,
|
||||
return Ok(std::string());
|
||||
}
|
||||
|
||||
static Result<std::string, lnav::console::user_message>
|
||||
com_cd(exec_context& ec, std::string cmdline, std::vector<std::string>& args)
|
||||
{
|
||||
if (args.empty()) {
|
||||
args.emplace_back("dirname");
|
||||
return Ok(std::string());
|
||||
}
|
||||
|
||||
if (lnav_data.ld_flags & LNF_SECURE_MODE) {
|
||||
return ec.make_error("{} -- unavailable in secure mode", args[0]);
|
||||
}
|
||||
|
||||
std::vector<std::string> word_exp;
|
||||
std::string pat;
|
||||
|
||||
pat = trim(remaining_args(cmdline, args));
|
||||
|
||||
std::vector<std::string> split_args;
|
||||
shlex lexer(pat);
|
||||
scoped_resolver scopes = {
|
||||
&ec.ec_local_vars.top(),
|
||||
&ec.ec_global_vars,
|
||||
};
|
||||
|
||||
if (!lexer.split(split_args, scopes)) {
|
||||
return ec.make_error("unable to parse arguments");
|
||||
}
|
||||
|
||||
if (split_args.size() != 1) {
|
||||
return ec.make_error("expecting a single argument");
|
||||
}
|
||||
|
||||
struct stat st;
|
||||
|
||||
if (stat(split_args[0].c_str(), &st) != 0) {
|
||||
return Err(ec.make_error_msg("cannot access -- {}", split_args[0])
|
||||
.with_errno_reason());
|
||||
}
|
||||
|
||||
if (!S_ISDIR(st.st_mode)) {
|
||||
return ec.make_error("{} is not a directory", split_args[0]);
|
||||
}
|
||||
|
||||
if (!ec.ec_dry_run) {
|
||||
chdir(split_args[0].c_str());
|
||||
}
|
||||
|
||||
return Ok(std::string());
|
||||
}
|
||||
|
||||
static Result<std::string, lnav::console::user_message>
|
||||
com_sh(exec_context& ec, std::string cmdline, std::vector<std::string>& args)
|
||||
{
|
||||
if (args.empty()) {
|
||||
return Ok(std::string());
|
||||
}
|
||||
|
||||
if (lnav_data.ld_flags & LNF_SECURE_MODE) {
|
||||
return ec.make_error("{} -- unavailable in secure mode", args[0]);
|
||||
}
|
||||
|
||||
if (!ec.ec_dry_run) {
|
||||
auto carg = trim(cmdline.substr(args[0].size()));
|
||||
|
||||
log_info("executing: %s", carg.c_str());
|
||||
|
||||
auto out_pipe_res = auto_pipe::for_child_fd(STDOUT_FILENO);
|
||||
auto err_pipe_res = auto_pipe::for_child_fd(STDERR_FILENO);
|
||||
auto child_res = lnav::pid::from_fork();
|
||||
if (child_res.isErr()) {
|
||||
auto um
|
||||
= lnav::console::user_message::error("unable to fork() child")
|
||||
.with_reason(child_res.unwrapErr());
|
||||
ec.add_error_context(um);
|
||||
return Err(um);
|
||||
}
|
||||
|
||||
auto out_pipe = out_pipe_res.unwrap();
|
||||
auto err_pipe = err_pipe_res.unwrap();
|
||||
|
||||
auto child = child_res.unwrap();
|
||||
out_pipe.after_fork(child.in());
|
||||
err_pipe.after_fork(child.in());
|
||||
if (child.in_child()) {
|
||||
auto dev_null = open("/dev/null", O_RDONLY);
|
||||
|
||||
dup2(dev_null, STDIN_FILENO);
|
||||
const char* exec_args[] = {
|
||||
getenv_opt("SHELL").value_or("bash"),
|
||||
"-c",
|
||||
carg.c_str(),
|
||||
nullptr,
|
||||
};
|
||||
|
||||
execvp(exec_args[0], (char**) exec_args);
|
||||
_exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
auto create_piper_res
|
||||
= lnav::piper::create_looper(carg,
|
||||
std::move(out_pipe.read_end()),
|
||||
std::move(err_pipe.read_end()));
|
||||
|
||||
if (create_piper_res.isErr()) {
|
||||
auto um
|
||||
= lnav::console::user_message::error("unable to create piper")
|
||||
.with_reason(child_res.unwrapErr());
|
||||
ec.add_error_context(um);
|
||||
return Err(um);
|
||||
}
|
||||
|
||||
lnav_data.ld_active_files.fc_file_names[carg].with_piper(
|
||||
create_piper_res.unwrap());
|
||||
lnav_data.ld_child_pollers.emplace_back(
|
||||
child_poller{std::move(child), [](auto& fc, auto& child) {}});
|
||||
lnav_data.ld_files_to_front.emplace_back(carg, file_location_t{});
|
||||
}
|
||||
|
||||
return Ok(std::string());
|
||||
}
|
||||
|
||||
static Result<std::string, lnav::console::user_message>
|
||||
com_shexec(exec_context& ec,
|
||||
std::string cmdline,
|
||||
@ -5691,6 +5807,29 @@ readline_context::command_t STD_COMMANDS[] = {
|
||||
.with_tags({"scripting"})
|
||||
.with_examples({{"To substitute the table name from a variable",
|
||||
";SELECT * FROM ${table}"}})},
|
||||
|
||||
{
|
||||
"sh",
|
||||
com_sh,
|
||||
|
||||
help_text(":sh")
|
||||
.with_summary("Execute the given command-line and display the "
|
||||
"captured output")
|
||||
.with_parameter(
|
||||
help_text("cmdline", "The command-line to execute."))
|
||||
.with_tags({"scripting"}),
|
||||
},
|
||||
|
||||
{
|
||||
"cd",
|
||||
com_cd,
|
||||
|
||||
help_text(":cd")
|
||||
.with_summary("Change the current directory")
|
||||
.with_parameter(help_text("dir", "The new current directory"))
|
||||
.with_tags({"scripting"}),
|
||||
},
|
||||
|
||||
{"config",
|
||||
com_config,
|
||||
|
||||
|
@ -87,6 +87,9 @@ static auto fvc = injector::bind<file_vtab::config>::to_instance(
|
||||
static auto lc = injector::bind<lnav::logfile::config>::to_instance(
|
||||
+[]() { return &lnav_config.lc_logfile; });
|
||||
|
||||
static auto p = injector::bind<lnav::piper::config>::to_instance(
|
||||
+[]() { return &lnav_config.lc_piper; });
|
||||
|
||||
static auto tc = injector::bind<tailer::config>::to_instance(
|
||||
+[]() { return &lnav_config.lc_tailer; });
|
||||
|
||||
@ -1057,6 +1060,19 @@ static const struct json_path_container archive_handlers = {
|
||||
&archive_manager::config::amc_cache_ttl),
|
||||
};
|
||||
|
||||
static const struct json_path_container piper_handlers = {
|
||||
yajlpp::property_handler("max-size")
|
||||
.with_synopsis("<bytes>")
|
||||
.with_description("The maximum size of a capture file")
|
||||
.with_min_value(128)
|
||||
.for_field(&_lnav_config::lc_piper, &lnav::piper::config::c_max_size),
|
||||
yajlpp::property_handler("rotations")
|
||||
.with_synopsis("<count>")
|
||||
.with_min_value(2)
|
||||
.with_description("The number of rotated files to keep")
|
||||
.for_field(&_lnav_config::lc_piper, &lnav::piper::config::c_rotations),
|
||||
};
|
||||
|
||||
static const struct json_path_container file_vtab_handlers = {
|
||||
yajlpp::property_handler("max-content-size")
|
||||
.with_synopsis("<bytes>")
|
||||
@ -1245,6 +1261,9 @@ static const struct json_path_container tuning_handlers = {
|
||||
yajlpp::property_handler("archive-manager")
|
||||
.with_description("Settings related to opening archive files")
|
||||
.with_children(archive_handlers),
|
||||
yajlpp::property_handler("piper")
|
||||
.with_description("Settings related to capturing piped data")
|
||||
.with_children(piper_handlers),
|
||||
yajlpp::property_handler("file-vtab")
|
||||
.with_description("Settings related to the lnav_file virtual-table")
|
||||
.with_children(file_vtab_handlers),
|
||||
|
@ -49,6 +49,7 @@
|
||||
#include "log_level.hh"
|
||||
#include "logfile.cfg.hh"
|
||||
#include "logfile_sub_source.cfg.hh"
|
||||
#include "piper.looper.cfg.hh"
|
||||
#include "styling.hh"
|
||||
#include "sysclip.cfg.hh"
|
||||
#include "tailer/tailer.looper.cfg.hh"
|
||||
@ -109,6 +110,7 @@ struct _lnav_config {
|
||||
key_map lc_active_keymap;
|
||||
|
||||
archive_manager::config lc_archive_manager;
|
||||
lnav::piper::config lc_piper;
|
||||
file_vtab::config lc_file_vtab;
|
||||
lnav::logfile::config lc_logfile;
|
||||
tailer::config lc_tailer;
|
||||
|
@ -27,13 +27,14 @@
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "log_actions.hh"
|
||||
#if 0
|
||||
# include "log_actions.hh"
|
||||
|
||||
#include "base/fs_util.hh"
|
||||
#include "base/injector.hh"
|
||||
#include "bound_tags.hh"
|
||||
#include "config.h"
|
||||
#include "piper_proc.hh"
|
||||
# include "base/fs_util.hh"
|
||||
# include "base/injector.hh"
|
||||
# include "bound_tags.hh"
|
||||
# include "config.h"
|
||||
# include "piper_proc.hh"
|
||||
|
||||
std::string
|
||||
action_delegate::execute_action(const std::string& action_name)
|
||||
@ -235,3 +236,4 @@ action_delegate::text_handle_mouse(textview_curses& tc, mouse_event& me)
|
||||
|
||||
return retval;
|
||||
}
|
||||
#endif
|
||||
|
@ -30,11 +30,12 @@
|
||||
#ifndef log_actions_hh
|
||||
#define log_actions_hh
|
||||
|
||||
#include <functional>
|
||||
#include <utility>
|
||||
#if 0
|
||||
# include <functional>
|
||||
# include <utility>
|
||||
|
||||
#include "log_data_helper.hh"
|
||||
#include "logfile_sub_source.hh"
|
||||
# include "log_data_helper.hh"
|
||||
# include "logfile_sub_source.hh"
|
||||
|
||||
class piper_proc;
|
||||
|
||||
@ -63,5 +64,6 @@ private:
|
||||
int ad_press_value{-1};
|
||||
size_t ad_line_index{0};
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
@ -68,16 +68,16 @@ static const typed_json_path_container<line_buffer::header_data>
|
||||
};
|
||||
|
||||
Result<std::shared_ptr<logfile>, std::string>
|
||||
logfile::open(std::string filename, logfile_open_options& loo)
|
||||
logfile::open(std::string filename, const logfile_open_options& loo, auto_fd fd)
|
||||
{
|
||||
require(!filename.empty());
|
||||
|
||||
auto lf = std::shared_ptr<logfile>(new logfile(std::move(filename), loo));
|
||||
|
||||
memset(&lf->lf_stat, 0, sizeof(lf->lf_stat));
|
||||
if (lf->lf_options.loo_fd == -1) {
|
||||
char resolved_path[PATH_MAX];
|
||||
char resolved_path[PATH_MAX] = "";
|
||||
|
||||
if (!fd.has_value()) {
|
||||
errno = 0;
|
||||
if (realpath(lf->lf_filename.c_str(), resolved_path) == nullptr) {
|
||||
return Err(fmt::format(FMT_STRING("realpath({}) failed with: {}"),
|
||||
@ -96,37 +96,36 @@ logfile::open(std::string filename, logfile_open_options& loo)
|
||||
lf->lf_filename,
|
||||
strerror(errno)));
|
||||
}
|
||||
}
|
||||
|
||||
if ((lf->lf_options.loo_fd = ::open(resolved_path, O_RDONLY)) == -1) {
|
||||
return Err(fmt::format(FMT_STRING("open({}) failed with: {}"),
|
||||
lf->lf_filename,
|
||||
strerror(errno)));
|
||||
}
|
||||
|
||||
lf->lf_options.loo_fd.close_on_exec();
|
||||
|
||||
log_info("Creating logfile: fd=%d; size=%" PRId64 "; mtime=%" PRId64
|
||||
"; filename=%s",
|
||||
(int) lf->lf_options.loo_fd,
|
||||
(long long) lf->lf_stat.st_size,
|
||||
(long long) lf->lf_stat.st_mtime,
|
||||
lf->lf_filename.c_str());
|
||||
|
||||
auto_fd lf_fd;
|
||||
if (fd.has_value()) {
|
||||
lf_fd = std::move(fd);
|
||||
} else if ((lf_fd = ::open(resolved_path, O_RDONLY)) == -1) {
|
||||
return Err(fmt::format(FMT_STRING("open({}) failed with: {}"),
|
||||
lf->lf_filename,
|
||||
strerror(errno)));
|
||||
} else {
|
||||
lf->lf_actual_path = lf->lf_filename;
|
||||
lf->lf_valid_filename = true;
|
||||
} else {
|
||||
log_perror(fstat(lf->lf_options.loo_fd, &lf->lf_stat));
|
||||
lf->lf_named_file = false;
|
||||
lf->lf_valid_filename = false;
|
||||
}
|
||||
|
||||
lf_fd.close_on_exec();
|
||||
|
||||
log_info("Creating logfile: fd=%d; size=%" PRId64 "; mtime=%" PRId64
|
||||
"; filename=%s",
|
||||
(int) lf_fd,
|
||||
(long long) lf->lf_stat.st_size,
|
||||
(long long) lf->lf_stat.st_mtime,
|
||||
lf->lf_filename.c_str());
|
||||
|
||||
if (!lf->lf_options.loo_filename.empty()) {
|
||||
lf->set_filename(lf->lf_options.loo_filename);
|
||||
lf->lf_valid_filename = false;
|
||||
}
|
||||
|
||||
lf->lf_content_id = hasher().update(lf->lf_filename).to_string();
|
||||
lf->lf_line_buffer.set_fd(lf->lf_options.loo_fd);
|
||||
lf->lf_line_buffer.set_fd(lf_fd);
|
||||
lf->lf_index.reserve(INDEX_RESERVE_INCREMENT);
|
||||
|
||||
lf->lf_indexing = lf->lf_options.loo_is_visible;
|
||||
@ -142,8 +141,8 @@ logfile::open(std::string filename, logfile_open_options& loo)
|
||||
return Ok(lf);
|
||||
}
|
||||
|
||||
logfile::logfile(std::string filename, logfile_open_options& loo)
|
||||
: lf_filename(std::move(filename)), lf_options(std::move(loo))
|
||||
logfile::logfile(std::string filename, const logfile_open_options& loo)
|
||||
: lf_filename(std::move(filename)), lf_options(loo)
|
||||
{
|
||||
this->lf_opids.writeAccess()->reserve(64);
|
||||
}
|
||||
@ -416,8 +415,15 @@ logfile::process_prefix(shared_buffer_ref& sbr,
|
||||
short last_millis = 0;
|
||||
uint8_t last_mod = 0, last_opid = 0;
|
||||
|
||||
if (!this->lf_index.empty()) {
|
||||
logline& ll = this->lf_index.back();
|
||||
if (this->lf_format == nullptr && li.li_timestamp.tv_sec != 0) {
|
||||
last_time = li.li_timestamp.tv_sec;
|
||||
last_millis
|
||||
= std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
std::chrono::microseconds(li.li_timestamp.tv_usec))
|
||||
.count();
|
||||
last_level = li.li_level;
|
||||
} else if (!this->lf_index.empty()) {
|
||||
const auto& ll = this->lf_index.back();
|
||||
|
||||
/*
|
||||
* Assume this line is part of the previous one(s) and copy the
|
||||
|
@ -114,7 +114,9 @@ public:
|
||||
* descriptor needs to be seekable.
|
||||
*/
|
||||
static Result<std::shared_ptr<logfile>, std::string> open(
|
||||
std::string filename, logfile_open_options& loo);
|
||||
std::string filename,
|
||||
const logfile_open_options& loo,
|
||||
auto_fd fd = auto_fd{});
|
||||
|
||||
~logfile() override;
|
||||
|
||||
@ -146,6 +148,11 @@ public:
|
||||
|
||||
bool is_compressed() const { return this->lf_line_buffer.is_compressed(); }
|
||||
|
||||
bool has_line_metadata() const
|
||||
{
|
||||
return this->lf_line_buffer.has_line_metadata();
|
||||
}
|
||||
|
||||
bool is_valid_filename() const { return this->lf_valid_filename; }
|
||||
|
||||
file_off_t get_index_size() const { return this->lf_index_size; }
|
||||
@ -195,6 +202,11 @@ public:
|
||||
return this->lf_options;
|
||||
}
|
||||
|
||||
void set_include_in_session(bool enabled)
|
||||
{
|
||||
this->lf_options.with_include_in_session(enabled);
|
||||
}
|
||||
|
||||
void reset_state();
|
||||
|
||||
bool is_time_adjusted() const
|
||||
@ -399,7 +411,7 @@ protected:
|
||||
void set_format_base_time(log_format* lf);
|
||||
|
||||
private:
|
||||
logfile(std::string filename, logfile_open_options& loo);
|
||||
logfile(std::string filename, const logfile_open_options& loo);
|
||||
|
||||
std::string lf_filename;
|
||||
logfile_open_options lf_options;
|
||||
|
@ -37,6 +37,7 @@
|
||||
|
||||
#include "base/auto_fd.hh"
|
||||
#include "file_format.hh"
|
||||
#include "piper.looper.hh"
|
||||
|
||||
using ui_clock = std::chrono::steady_clock;
|
||||
|
||||
@ -65,6 +66,7 @@ struct logfile_open_options_base {
|
||||
ssize_t loo_visible_size_limit{-1};
|
||||
bool loo_tail{true};
|
||||
file_format_t loo_file_format{file_format_t::UNKNOWN};
|
||||
nonstd::optional<lnav::piper::running_handle> loo_piper;
|
||||
};
|
||||
|
||||
struct logfile_open_options : public logfile_open_options_base {
|
||||
@ -82,14 +84,6 @@ struct logfile_open_options : public logfile_open_options_base {
|
||||
return *this;
|
||||
}
|
||||
|
||||
logfile_open_options& with_fd(auto_fd fd)
|
||||
{
|
||||
this->loo_fd = std::move(fd);
|
||||
this->loo_temp_file = true;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
logfile_open_options& with_stat_for_temp(const struct stat& st)
|
||||
{
|
||||
this->loo_temp_dev = st.st_dev;
|
||||
@ -131,7 +125,7 @@ struct logfile_open_options : public logfile_open_options_base {
|
||||
this->loo_non_utf_is_visible = val;
|
||||
|
||||
return *this;
|
||||
};
|
||||
}
|
||||
|
||||
logfile_open_options& with_visible_size_limit(ssize_t val)
|
||||
{
|
||||
@ -154,7 +148,13 @@ struct logfile_open_options : public logfile_open_options_base {
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto_fd loo_fd;
|
||||
logfile_open_options& with_piper(lnav::piper::running_handle handle)
|
||||
{
|
||||
this->loo_piper = handle;
|
||||
this->loo_filename = handle.get_name();
|
||||
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -324,33 +324,9 @@ logfile_sub_source::text_value_for_line(textview_curses& tc,
|
||||
value_out.insert(0, 1, ' ');
|
||||
}
|
||||
|
||||
if (this->lss_flags & F_TIME_OFFSET) {
|
||||
auto curr_tv = this->lss_token_line->get_timeval();
|
||||
struct timeval diff_tv;
|
||||
if (this->tas_display_time_offset) {
|
||||
auto row_vl = vis_line_t(row);
|
||||
|
||||
auto prev_umark
|
||||
= tc.get_bookmarks()[&textview_curses::BM_USER].prev(row_vl);
|
||||
auto next_umark
|
||||
= tc.get_bookmarks()[&textview_curses::BM_USER].next(row_vl);
|
||||
auto prev_emark
|
||||
= tc.get_bookmarks()[&textview_curses::BM_USER_EXPR].prev(row_vl);
|
||||
auto next_emark
|
||||
= tc.get_bookmarks()[&textview_curses::BM_USER_EXPR].next(row_vl);
|
||||
if (!prev_umark && !prev_emark && (next_umark || next_emark)) {
|
||||
auto next_line = this->find_line(this->at(
|
||||
std::max(next_umark.value_or(0), next_emark.value_or(0))));
|
||||
|
||||
diff_tv = curr_tv - next_line->get_timeval();
|
||||
} else {
|
||||
auto prev_row
|
||||
= std::max(prev_umark.value_or(0), prev_emark.value_or(0));
|
||||
auto first_line = this->find_line(this->at(prev_row));
|
||||
auto start_tv = first_line->get_timeval();
|
||||
diff_tv = curr_tv - start_tv;
|
||||
}
|
||||
|
||||
auto relstr = humanize::time::duration::from_tv(diff_tv).to_string();
|
||||
auto relstr = this->get_time_offset_for_line(tc, row_vl);
|
||||
value_out = fmt::format(FMT_STRING("{: >12}|{}"), relstr, value_out);
|
||||
}
|
||||
this->lss_in_value_for_line = false;
|
||||
@ -492,7 +468,7 @@ logfile_sub_source::text_attrs_for_line(textview_curses& lv,
|
||||
this->lss_token_file->get_filename())));
|
||||
}
|
||||
|
||||
if (this->lss_flags & F_TIME_OFFSET) {
|
||||
if (this->tas_display_time_offset) {
|
||||
time_offset_end = 13;
|
||||
lr.lr_start = 0;
|
||||
lr.lr_end = time_offset_end;
|
||||
@ -1117,29 +1093,6 @@ logfile_sub_source::text_update_marks(vis_bookmarks& bm)
|
||||
}
|
||||
}
|
||||
|
||||
log_accel::direction_t
|
||||
logfile_sub_source::get_line_accel_direction(vis_line_t vl)
|
||||
{
|
||||
log_accel la;
|
||||
|
||||
while (vl >= 0) {
|
||||
logline* curr_line = this->find_line(this->at(vl));
|
||||
|
||||
if (!curr_line->is_message()) {
|
||||
--vl;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!la.add_point(curr_line->get_time_in_millis())) {
|
||||
break;
|
||||
}
|
||||
|
||||
--vl;
|
||||
}
|
||||
|
||||
return la.get_direction();
|
||||
}
|
||||
|
||||
void
|
||||
logfile_sub_source::text_filters_changed()
|
||||
{
|
||||
|
@ -240,6 +240,7 @@ private:
|
||||
class logfile_sub_source
|
||||
: public text_sub_source
|
||||
, public text_time_translator
|
||||
, public text_accel_source
|
||||
, public list_input_delegate {
|
||||
public:
|
||||
const static bookmark_type_t BM_ERRORS;
|
||||
@ -252,12 +253,6 @@ public:
|
||||
|
||||
~logfile_sub_source() = default;
|
||||
|
||||
void toggle_time_offset()
|
||||
{
|
||||
this->lss_flags ^= F_TIME_OFFSET;
|
||||
this->clear_line_size_cache();
|
||||
}
|
||||
|
||||
void increase_line_context()
|
||||
{
|
||||
auto old_flags = this->lss_flags;
|
||||
@ -305,20 +300,6 @@ public:
|
||||
return 0;
|
||||
}
|
||||
|
||||
void set_time_offset(bool enabled)
|
||||
{
|
||||
if (enabled)
|
||||
this->lss_flags |= F_TIME_OFFSET;
|
||||
else
|
||||
this->lss_flags &= ~F_TIME_OFFSET;
|
||||
this->clear_line_size_cache();
|
||||
}
|
||||
|
||||
bool is_time_offset_enabled() const
|
||||
{
|
||||
return (bool) (this->lss_flags & F_TIME_OFFSET);
|
||||
}
|
||||
|
||||
bool is_filename_enabled() const
|
||||
{
|
||||
return (bool) (this->lss_flags & F_FILENAME);
|
||||
@ -650,8 +631,6 @@ public:
|
||||
return logline_window(*this, start_vl, end_vl);
|
||||
}
|
||||
|
||||
log_accel::direction_t get_line_accel_direction(vis_line_t vl);
|
||||
|
||||
/**
|
||||
* Container for logfile references that keeps of how many lines in the
|
||||
* logfile have been indexed.
|
||||
@ -834,19 +813,25 @@ public:
|
||||
|
||||
void quiesce();
|
||||
|
||||
protected:
|
||||
void text_accel_display_changed() { this->clear_line_size_cache(); }
|
||||
|
||||
logline* text_accel_get_line(vis_line_t vl)
|
||||
{
|
||||
return this->find_line(this->at(vl));
|
||||
}
|
||||
|
||||
private:
|
||||
static const size_t LINE_SIZE_CACHE_SIZE = 512;
|
||||
|
||||
enum {
|
||||
B_SCRUB,
|
||||
B_TIME_OFFSET,
|
||||
B_FILENAME,
|
||||
B_BASENAME,
|
||||
};
|
||||
|
||||
enum {
|
||||
F_SCRUB = (1UL << B_SCRUB),
|
||||
F_TIME_OFFSET = (1UL << B_TIME_OFFSET),
|
||||
F_FILENAME = (1UL << B_FILENAME),
|
||||
F_BASENAME = (1UL << B_BASENAME),
|
||||
|
||||
|
@ -38,6 +38,7 @@
|
||||
#include <unistd.h>
|
||||
|
||||
#include "base/fs_util.hh"
|
||||
#include "base/paths.hh"
|
||||
#include "config.h"
|
||||
#include "line_buffer.hh"
|
||||
|
||||
@ -48,9 +49,9 @@ convert(const std::string& filename)
|
||||
{
|
||||
log_info("attempting to convert pcap file -- %s", filename.c_str());
|
||||
|
||||
auto outfile = TRY(lnav::filesystem::open_temp_file(
|
||||
ghc::filesystem::temp_directory_path() / "lnav.pcap.XXXXXX"));
|
||||
ghc::filesystem::remove(outfile.first);
|
||||
ghc::filesystem::create_directories(lnav::paths::workdir());
|
||||
auto outfile = TRY(lnav::filesystem::open_temp_file(lnav::paths::workdir()
|
||||
/ "pcap.XXXXXX"));
|
||||
auto err_pipe = TRY(auto_pipe::for_child_fd(STDERR_FILENO));
|
||||
auto child = TRY(lnav::pid::from_fork());
|
||||
|
||||
@ -59,7 +60,8 @@ convert(const std::string& filename)
|
||||
auto dev_null = open("/dev/null", O_RDONLY);
|
||||
|
||||
dup2(dev_null, STDIN_FILENO);
|
||||
dup2(outfile.second.release(), STDOUT_FILENO);
|
||||
dup2(outfile.second.get(), STDOUT_FILENO);
|
||||
outfile.second.reset();
|
||||
setenv("TZ", "UTC", 1);
|
||||
|
||||
const char* args[] = {
|
||||
@ -131,7 +133,7 @@ convert(const std::string& filename)
|
||||
|
||||
return Ok(convert_result{
|
||||
std::move(child),
|
||||
std::move(outfile.second),
|
||||
outfile.first,
|
||||
error_queue,
|
||||
});
|
||||
}
|
||||
|
@ -38,12 +38,13 @@
|
||||
#include "base/auto_fd.hh"
|
||||
#include "base/auto_pid.hh"
|
||||
#include "base/result.h"
|
||||
#include "ghc/filesystem.hpp"
|
||||
|
||||
namespace pcap_manager {
|
||||
|
||||
struct convert_result {
|
||||
auto_pid<process_state::running> cr_child;
|
||||
auto_fd cr_destination;
|
||||
ghc::filesystem::path cr_destination;
|
||||
std::shared_ptr<std::vector<std::string>> cr_error_queue;
|
||||
};
|
||||
|
||||
|
342
src/piper.looper.cc
Normal file
342
src/piper.looper.cc
Normal file
@ -0,0 +1,342 @@
|
||||
/**
|
||||
* Copyright (c) 2023, 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.
|
||||
*/
|
||||
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
|
||||
#include "piper.looper.hh"
|
||||
|
||||
#include <poll.h>
|
||||
|
||||
#include "base/fs_util.hh"
|
||||
#include "base/injector.hh"
|
||||
#include "base/paths.hh"
|
||||
#include "base/time_util.hh"
|
||||
#include "config.h"
|
||||
#include "line_buffer.hh"
|
||||
#include "lnav_util.hh"
|
||||
#include "piper.looper.cfg.hh"
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
static ssize_t
|
||||
write_timestamp(int fd, log_level_t level, off_t woff)
|
||||
{
|
||||
char time_str[64];
|
||||
struct timeval tv;
|
||||
|
||||
gettimeofday(&tv, nullptr);
|
||||
snprintf(time_str,
|
||||
sizeof(time_str),
|
||||
"%ld.%d:%c;",
|
||||
tv.tv_sec,
|
||||
tv.tv_usec,
|
||||
level_names[level][0]);
|
||||
|
||||
return pwrite(fd, time_str, strlen(time_str), woff);
|
||||
}
|
||||
|
||||
namespace lnav {
|
||||
namespace piper {
|
||||
|
||||
looper::looper(std::string name, auto_fd stdout_fd, auto_fd stderr_fd)
|
||||
: l_name(std::move(name)), l_stdout(std::move(stdout_fd)),
|
||||
l_stderr(std::move(stderr_fd))
|
||||
{
|
||||
size_t count = 0;
|
||||
do {
|
||||
this->l_out_dir
|
||||
= lnav::paths::workdir()
|
||||
/ fmt::format(
|
||||
FMT_STRING("piper-{}-{}"),
|
||||
hasher().update(getmstime()).update(l_name).to_string(),
|
||||
count);
|
||||
count += 1;
|
||||
} while (ghc::filesystem::exists(this->l_out_dir));
|
||||
ghc::filesystem::create_directories(this->l_out_dir);
|
||||
this->l_future = std::async(std::launch::async, [this]() { this->loop(); });
|
||||
}
|
||||
|
||||
looper::~looper()
|
||||
{
|
||||
log_info("piper destructed, shutting down: %s", this->l_name.c_str());
|
||||
this->l_looping = false;
|
||||
this->l_future.wait();
|
||||
}
|
||||
|
||||
enum class read_mode_t {
|
||||
binary,
|
||||
line,
|
||||
};
|
||||
|
||||
void
|
||||
looper::loop()
|
||||
{
|
||||
const auto& cfg = injector::get<const config&>();
|
||||
struct pollfd pfd[2];
|
||||
struct {
|
||||
line_buffer lb;
|
||||
file_range last_range;
|
||||
pollfd* pfd{nullptr};
|
||||
log_level_t cf_level{LEVEL_INFO};
|
||||
read_mode_t cf_read_mode{read_mode_t::line};
|
||||
|
||||
void reset_pfd()
|
||||
{
|
||||
this->pfd->fd = this->lb.get_fd();
|
||||
this->pfd->events = POLLIN;
|
||||
this->pfd->revents = 0;
|
||||
}
|
||||
} captured_fds[2];
|
||||
off_t woff = 0, last_woff = 0;
|
||||
auto_fd outfd;
|
||||
size_t rotate_count = 0;
|
||||
|
||||
log_info("starting loop to capture: %s (%d %d)",
|
||||
this->l_name.c_str(),
|
||||
this->l_stdout.get(),
|
||||
this->l_stderr.get());
|
||||
captured_fds[0].lb.set_fd(this->l_stdout);
|
||||
if (this->l_stderr.has_value()) {
|
||||
captured_fds[1].lb.set_fd(this->l_stderr);
|
||||
}
|
||||
captured_fds[1].cf_level = LEVEL_ERROR;
|
||||
do {
|
||||
static const auto TIMEOUT
|
||||
= std::chrono::duration_cast<std::chrono::milliseconds>(1s).count();
|
||||
|
||||
size_t used_pfds = 0;
|
||||
for (auto& cap : captured_fds) {
|
||||
if (cap.lb.get_fd() != -1 && cap.lb.is_pipe()
|
||||
&& !cap.lb.is_pipe_closed())
|
||||
{
|
||||
cap.pfd = &pfd[used_pfds];
|
||||
used_pfds += 1;
|
||||
cap.reset_pfd();
|
||||
} else {
|
||||
cap.pfd = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
if (used_pfds == 0) {
|
||||
log_info("inputs consumed, breaking loop: %s",
|
||||
this->l_name.c_str());
|
||||
this->l_looping = false;
|
||||
break;
|
||||
}
|
||||
|
||||
auto poll_rc = poll(pfd, used_pfds, TIMEOUT);
|
||||
if (poll_rc == 0) {
|
||||
// update the timestamp to keep the file alive from any
|
||||
// cleanup processes
|
||||
if (outfd.has_value()) {
|
||||
log_perror(futimes(outfd.get(), nullptr));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
for (auto& cap : captured_fds) {
|
||||
while (this->l_looping) {
|
||||
if (cap.pfd == nullptr || !(cap.pfd->revents & POLLIN)) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (cap.cf_read_mode == read_mode_t::binary) {
|
||||
char buffer[8192];
|
||||
auto read_rc
|
||||
= read(cap.lb.get_fd(), buffer, sizeof(buffer));
|
||||
|
||||
if (read_rc < 0) {
|
||||
if (errno == EAGAIN) {
|
||||
break;
|
||||
}
|
||||
log_error("failed to read next chunk: %s -- %s",
|
||||
this->l_name.c_str(),
|
||||
strerror(errno));
|
||||
this->l_looping = false;
|
||||
} else if (read_rc == 0) {
|
||||
this->l_looping = false;
|
||||
} else {
|
||||
auto rc = write(outfd.get(), buffer, read_rc);
|
||||
if (rc != read_rc) {
|
||||
log_error(
|
||||
"failed to write to capture file: %s -- %s",
|
||||
this->l_name.c_str(),
|
||||
strerror(errno));
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
auto load_result = cap.lb.load_next_line(cap.last_range);
|
||||
|
||||
if (load_result.isErr()) {
|
||||
log_error("failed to load next line: %s -- %s",
|
||||
this->l_name.c_str(),
|
||||
load_result.unwrapErr().c_str());
|
||||
this->l_looping = false;
|
||||
break;
|
||||
}
|
||||
|
||||
auto li = load_result.unwrap();
|
||||
|
||||
if (cap.last_range.fr_offset == 0 && !cap.lb.is_header_utf8()) {
|
||||
log_info("switching capture to binary mode: %s",
|
||||
this->l_name.c_str());
|
||||
cap.cf_read_mode = read_mode_t::binary;
|
||||
|
||||
auto out_path = this->l_out_dir / "out.0";
|
||||
log_info("creating binary capture file: %s -- %s",
|
||||
this->l_name.c_str(),
|
||||
out_path.c_str());
|
||||
auto create_res = lnav::filesystem::create_file(
|
||||
out_path, O_WRONLY | O_CLOEXEC | O_TRUNC, 0600);
|
||||
if (create_res.isErr()) {
|
||||
log_error("unable to open capture file: %s -- %s",
|
||||
this->l_name.c_str(),
|
||||
create_res.unwrapErr().c_str());
|
||||
break;
|
||||
}
|
||||
|
||||
outfd = create_res.unwrap();
|
||||
auto header_avail = cap.lb.get_available();
|
||||
auto read_res = cap.lb.read_range(header_avail);
|
||||
if (read_res.isOk()) {
|
||||
auto sbr = read_res.unwrap();
|
||||
write(outfd.get(), sbr.get_data(), sbr.length());
|
||||
} else {
|
||||
log_error("failed to get header data: %s -- %s",
|
||||
this->l_name.c_str(),
|
||||
read_res.unwrapErr().c_str());
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (li.li_partial && !cap.lb.is_pipe_closed()) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (li.li_file_range.empty()) {
|
||||
break;
|
||||
}
|
||||
|
||||
auto read_result = cap.lb.read_range(li.li_file_range);
|
||||
|
||||
if (read_result.isErr()) {
|
||||
log_error("failed to read next line: %s -- %s",
|
||||
this->l_name.c_str(),
|
||||
read_result.unwrapErr().c_str());
|
||||
this->l_looping = false;
|
||||
break;
|
||||
}
|
||||
|
||||
auto sbr = read_result.unwrap();
|
||||
|
||||
if (woff > last_woff && woff >= cfg.c_max_size) {
|
||||
log_info(
|
||||
"capture file has reached max size, rotating: %s -- "
|
||||
"%lld",
|
||||
this->l_name.c_str(),
|
||||
woff);
|
||||
outfd.reset();
|
||||
}
|
||||
|
||||
if (!outfd.has_value()) {
|
||||
auto out_path = this->l_out_dir
|
||||
/ fmt::format(FMT_STRING("out.{}"),
|
||||
rotate_count % cfg.c_rotations);
|
||||
log_info("creating capturing file: %s -- %s",
|
||||
this->l_name.c_str(),
|
||||
out_path.c_str());
|
||||
auto create_res = lnav::filesystem::create_file(
|
||||
out_path, O_WRONLY | O_CLOEXEC | O_TRUNC, 0600);
|
||||
if (create_res.isErr()) {
|
||||
log_error("unable to open capture file: %s -- %s",
|
||||
this->l_name.c_str(),
|
||||
create_res.unwrapErr().c_str());
|
||||
break;
|
||||
}
|
||||
|
||||
outfd = create_res.unwrap();
|
||||
rotate_count += 1;
|
||||
|
||||
static const char lnav_header[]
|
||||
= {'L', 0, 'N', 1, 0, 0, 0, 0};
|
||||
auto prc
|
||||
= write(outfd.get(), lnav_header, sizeof(lnav_header));
|
||||
woff = prc;
|
||||
}
|
||||
|
||||
ssize_t wrc;
|
||||
|
||||
last_woff = woff;
|
||||
wrc = write_timestamp(outfd.get(), cap.cf_level, woff);
|
||||
if (wrc == -1) {
|
||||
log_error("unable to write timestamp: %s -- %s",
|
||||
this->l_name.c_str(),
|
||||
strerror(errno));
|
||||
this->l_looping = false;
|
||||
break;
|
||||
}
|
||||
woff += wrc;
|
||||
|
||||
/* Need to do pwrite here since the fd is used by the main
|
||||
* lnav process as well.
|
||||
*/
|
||||
wrc = pwrite(outfd.get(), sbr.get_data(), sbr.length(), woff);
|
||||
if (wrc == -1) {
|
||||
log_error("unable to write captured data: %s -- %s",
|
||||
this->l_name.c_str(),
|
||||
strerror(errno));
|
||||
this->l_looping = false;
|
||||
break;
|
||||
}
|
||||
woff += wrc;
|
||||
|
||||
cap.last_range = li.li_file_range;
|
||||
if (li.li_partial && sbr.get_data()[sbr.length() - 1] != '\n'
|
||||
&& (cap.last_range.next_offset() != cap.lb.get_file_size()))
|
||||
{
|
||||
woff = last_woff;
|
||||
}
|
||||
}
|
||||
}
|
||||
} while (this->l_looping);
|
||||
|
||||
log_info("exiting loop to capture: %s", this->l_name.c_str());
|
||||
}
|
||||
|
||||
Result<handle<state::running>, std::string>
|
||||
create_looper(std::string name, auto_fd stdout_fd, auto_fd stderr_fd)
|
||||
{
|
||||
return Ok(handle<state::running>(std::make_shared<looper>(
|
||||
name, std::move(stdout_fd), std::move(stderr_fd))));
|
||||
}
|
||||
|
||||
} // namespace piper
|
||||
} // namespace lnav
|
@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Copyright (c) 2007-2012, Timothy Stack
|
||||
* Copyright (c) 2023, Timothy Stack
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
@ -25,62 +25,22 @@
|
||||
* 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.
|
||||
*
|
||||
* @file piper_proc.hh
|
||||
*/
|
||||
|
||||
#ifndef piper_proc_hh
|
||||
#define piper_proc_hh
|
||||
#ifndef piper_looper_cfg_hh
|
||||
#define piper_looper_cfg_hh
|
||||
|
||||
#include <string>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
namespace lnav {
|
||||
namespace piper {
|
||||
|
||||
#include "base/auto_fd.hh"
|
||||
|
||||
/**
|
||||
* Creates a subprocess that reads data from a pipe and writes it to a file so
|
||||
* lnav can treat it like any other file and do preads.
|
||||
*
|
||||
* TODO: Add support for gzipped files.
|
||||
*/
|
||||
class piper_proc {
|
||||
public:
|
||||
class error : public std::exception {
|
||||
public:
|
||||
error(int err) : e_err(err) {}
|
||||
|
||||
int e_err;
|
||||
};
|
||||
|
||||
/**
|
||||
* Forks a subprocess that will read data from the given file descriptor
|
||||
* and write it to a temporary file.
|
||||
*
|
||||
* @param pipefd The file descriptor to read the file contents from.
|
||||
* @param timestamp True if an ISO 8601 timestamp should be prepended onto
|
||||
* the lines read from pipefd.
|
||||
* @param filefd The descriptor for the backing file.
|
||||
*/
|
||||
piper_proc(auto_fd pipefd, bool timestamp, auto_fd filefd);
|
||||
|
||||
bool has_exited();
|
||||
|
||||
/**
|
||||
* Terminates the child process.
|
||||
*/
|
||||
virtual ~piper_proc();
|
||||
|
||||
/** @return The file descriptor for the temporary file. */
|
||||
auto_fd get_fd() { return this->pp_fd.dup(); }
|
||||
|
||||
pid_t get_child_pid() const { return this->pp_child; }
|
||||
|
||||
private:
|
||||
/** A file descriptor that refers to the temporary file. */
|
||||
auto_fd pp_fd;
|
||||
|
||||
/** The child process' pid. */
|
||||
pid_t pp_child;
|
||||
struct config {
|
||||
uint64_t c_max_size{10ULL * 1024ULL * 1024ULL};
|
||||
uint32_t c_rotations{4};
|
||||
};
|
||||
|
||||
} // namespace piper
|
||||
} // namespace lnav
|
||||
|
||||
#endif
|
131
src/piper.looper.hh
Normal file
131
src/piper.looper.hh
Normal file
@ -0,0 +1,131 @@
|
||||
/**
|
||||
* Copyright (c) 2023, 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 piper_looper_hh
|
||||
#define piper_looper_hh
|
||||
|
||||
#include <future>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "base/auto_fd.hh"
|
||||
#include "base/result.h"
|
||||
#include "ghc/filesystem.hpp"
|
||||
|
||||
namespace lnav {
|
||||
namespace piper {
|
||||
|
||||
enum class state {
|
||||
running,
|
||||
finished,
|
||||
};
|
||||
|
||||
class looper {
|
||||
public:
|
||||
looper(std::string name, auto_fd stdout_fd, auto_fd stderr_fd);
|
||||
|
||||
~looper();
|
||||
|
||||
std::string get_name() const { return this->l_name; }
|
||||
|
||||
ghc::filesystem::path get_out_dir() const { return this->l_out_dir; }
|
||||
|
||||
ghc::filesystem::path get_out_pattern() const
|
||||
{
|
||||
return this->l_out_dir / "out.*";
|
||||
}
|
||||
|
||||
bool is_finished() const
|
||||
{
|
||||
return this->l_future.wait_for(std::chrono::seconds(0))
|
||||
== std::future_status::ready;
|
||||
}
|
||||
|
||||
private:
|
||||
void loop();
|
||||
|
||||
std::atomic<bool> l_looping{true};
|
||||
const std::string l_name;
|
||||
ghc::filesystem::path l_out_dir;
|
||||
auto_fd l_stdout;
|
||||
auto_fd l_stderr;
|
||||
std::future<void> l_future;
|
||||
};
|
||||
|
||||
template<state LooperState>
|
||||
class handle {
|
||||
public:
|
||||
explicit handle(std::shared_ptr<looper> looper)
|
||||
: h_looper(std::move(looper))
|
||||
{
|
||||
}
|
||||
|
||||
std::string get_name() const { return this->h_looper->get_name(); }
|
||||
|
||||
ghc::filesystem::path get_out_dir() const
|
||||
{
|
||||
return this->h_looper->get_out_dir();
|
||||
}
|
||||
|
||||
ghc::filesystem::path get_out_pattern() const
|
||||
{
|
||||
return this->h_looper->get_out_pattern();
|
||||
}
|
||||
|
||||
bool is_finished() const { return this->h_looper->is_finished(); }
|
||||
|
||||
handle<state::finished> close() &&
|
||||
{
|
||||
static_assert(LooperState == state::running,
|
||||
"this method is only available in the running state");
|
||||
|
||||
this->h_looper->close();
|
||||
|
||||
return handle<state::finished>{nullptr};
|
||||
}
|
||||
|
||||
bool operator==(const handle& other) const
|
||||
{
|
||||
return this->h_looper.get() == other.h_looper.get();
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<looper> h_looper;
|
||||
};
|
||||
|
||||
using running_handle = handle<state::running>;
|
||||
|
||||
Result<handle<state::running>, std::string> create_looper(std::string name,
|
||||
auto_fd stdout_fd,
|
||||
auto_fd stderr_fd);
|
||||
|
||||
} // namespace piper
|
||||
} // namespace lnav
|
||||
|
||||
#endif
|
@ -1,237 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2007-2012, 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.
|
||||
*
|
||||
* @file piper_proc.cc
|
||||
*/
|
||||
|
||||
#include "piper_proc.hh"
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <poll.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "base/fs_util.hh"
|
||||
#include "base/lnav_log.hh"
|
||||
#include "config.h"
|
||||
#include "line_buffer.hh"
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
static const char* STDIN_EOF_MSG = "---- END-OF-STDIN ----";
|
||||
|
||||
static ssize_t
|
||||
write_timestamp(int fd, off_t woff)
|
||||
{
|
||||
char time_str[64];
|
||||
struct timeval tv;
|
||||
char ms_str[10];
|
||||
|
||||
gettimeofday(&tv, nullptr);
|
||||
strftime(time_str, sizeof(time_str), "%FT%T", localtime(&tv.tv_sec));
|
||||
snprintf(ms_str, sizeof(ms_str), ".%03d", (int) (tv.tv_usec / 1000));
|
||||
strcat(time_str, ms_str);
|
||||
strcat(time_str, " ");
|
||||
return pwrite(fd, time_str, strlen(time_str), woff);
|
||||
}
|
||||
|
||||
piper_proc::piper_proc(auto_fd pipefd, bool timestamp, auto_fd filefd)
|
||||
: pp_fd(std::move(filefd)), pp_child(-1)
|
||||
{
|
||||
require(pipefd.get() >= 0);
|
||||
require(this->pp_fd.get() >= 0);
|
||||
|
||||
log_perror(fcntl(this->pp_fd.get(), F_SETFD, FD_CLOEXEC));
|
||||
|
||||
this->pp_child = fork();
|
||||
switch (this->pp_child) {
|
||||
case -1:
|
||||
throw error(errno);
|
||||
|
||||
case 0: {
|
||||
line_buffer lb;
|
||||
off_t woff = 0, last_woff = 0;
|
||||
file_range last_range;
|
||||
|
||||
auto open_res = lnav::filesystem::open_file("/dev/null", O_RDWR);
|
||||
if (open_res.isErr()) {
|
||||
fprintf(stderr,
|
||||
"unable to open /dev/null: %s\n",
|
||||
open_res.unwrapErr().c_str());
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
auto nullfd = open_res.unwrap();
|
||||
if (pipefd != STDIN_FILENO) {
|
||||
dup2(nullfd, STDIN_FILENO);
|
||||
}
|
||||
dup2(nullfd, STDOUT_FILENO);
|
||||
for (int fd_to_close = 0; fd_to_close < 1024; fd_to_close++) {
|
||||
int flags;
|
||||
|
||||
if (fd_to_close == this->pp_fd.get()) {
|
||||
continue;
|
||||
}
|
||||
if ((flags = fcntl(fd_to_close, F_GETFD)) == -1) {
|
||||
continue;
|
||||
}
|
||||
if (flags & FD_CLOEXEC) {
|
||||
close(fd_to_close);
|
||||
}
|
||||
}
|
||||
log_perror(fcntl(pipefd.get(), F_SETFL, O_NONBLOCK));
|
||||
lb.set_fd(pipefd);
|
||||
do {
|
||||
static const auto TIMEOUT
|
||||
= std::chrono::duration_cast<std::chrono::milliseconds>(1s)
|
||||
.count();
|
||||
struct pollfd pfd = {lb.get_fd(), POLLIN, 0};
|
||||
|
||||
auto poll_rc = poll(&pfd, 1, TIMEOUT);
|
||||
if (poll_rc == 0) {
|
||||
// update the timestamp to keep the file alive from any
|
||||
// cleanup processes
|
||||
log_perror(futimes(this->pp_fd.get(), nullptr));
|
||||
continue;
|
||||
}
|
||||
while (true) {
|
||||
auto load_result = lb.load_next_line(last_range);
|
||||
|
||||
if (load_result.isErr()) {
|
||||
break;
|
||||
}
|
||||
|
||||
auto li = load_result.unwrap();
|
||||
|
||||
if (li.li_partial && !lb.is_pipe_closed()) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (li.li_file_range.empty()) {
|
||||
break;
|
||||
}
|
||||
|
||||
auto read_result = lb.read_range(li.li_file_range);
|
||||
|
||||
if (read_result.isErr()) {
|
||||
break;
|
||||
}
|
||||
|
||||
auto sbr = read_result.unwrap();
|
||||
|
||||
ssize_t wrc;
|
||||
|
||||
last_woff = woff;
|
||||
if (timestamp) {
|
||||
wrc = write_timestamp(this->pp_fd, woff);
|
||||
if (wrc == -1) {
|
||||
perror("Unable to write to output file for stdin");
|
||||
break;
|
||||
}
|
||||
woff += wrc;
|
||||
}
|
||||
|
||||
/* Need to do pwrite here since the fd is used by the main
|
||||
* lnav process as well.
|
||||
*/
|
||||
wrc = pwrite(
|
||||
this->pp_fd, sbr.get_data(), sbr.length(), woff);
|
||||
if (wrc == -1) {
|
||||
perror("Unable to write to output file for stdin");
|
||||
break;
|
||||
}
|
||||
woff += wrc;
|
||||
|
||||
last_range = li.li_file_range;
|
||||
if (li.li_partial
|
||||
&& sbr.get_data()[sbr.length() - 1] != '\n'
|
||||
&& (last_range.next_offset() != lb.get_file_size()))
|
||||
{
|
||||
woff = last_woff;
|
||||
}
|
||||
}
|
||||
} while (lb.is_pipe() && !lb.is_pipe_closed());
|
||||
|
||||
if (timestamp) {
|
||||
ssize_t wrc;
|
||||
|
||||
wrc = write_timestamp(this->pp_fd, woff);
|
||||
if (wrc == -1) {
|
||||
perror("Unable to write to output file for stdin");
|
||||
break;
|
||||
}
|
||||
woff += wrc;
|
||||
wrc = pwrite(
|
||||
this->pp_fd, STDIN_EOF_MSG, strlen(STDIN_EOF_MSG), woff);
|
||||
if (wrc == -1) {
|
||||
perror("Unable to write to output file for stdin");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
_exit(0);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
piper_proc::has_exited()
|
||||
{
|
||||
if (this->pp_child > 0) {
|
||||
int rc, status;
|
||||
|
||||
rc = waitpid(this->pp_child, &status, WNOHANG);
|
||||
if (rc == -1 || rc == 0) {
|
||||
return false;
|
||||
}
|
||||
this->pp_child = -1;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
piper_proc::~piper_proc()
|
||||
{
|
||||
if (this->pp_child > 0) {
|
||||
int status;
|
||||
|
||||
kill(this->pp_child, SIGTERM);
|
||||
while (waitpid(this->pp_child, &status, 0) < 0 && (errno == EINTR)) {
|
||||
;
|
||||
}
|
||||
|
||||
this->pp_child = -1;
|
||||
}
|
||||
}
|
@ -27,8 +27,10 @@
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "base/fs_util.hh"
|
||||
#include "base/humanize.network.hh"
|
||||
#include "base/injector.hh"
|
||||
#include "base/paths.hh"
|
||||
#include "command_executor.hh"
|
||||
#include "config.h"
|
||||
#include "field_overlay_source.hh"
|
||||
@ -736,23 +738,26 @@ rl_callback_int(readline_curses* rc, bool is_alt)
|
||||
}
|
||||
|
||||
case ln_mode_t::EXEC: {
|
||||
auto_mem<FILE> tmpout(fclose);
|
||||
auto open_temp_res = lnav::filesystem::open_temp_file(
|
||||
lnav::paths::workdir() / "exec.XXXXXX");
|
||||
|
||||
tmpout = std::tmpfile();
|
||||
|
||||
if (!tmpout) {
|
||||
if (open_temp_res.isErr()) {
|
||||
rc->set_value(fmt::format(
|
||||
FMT_STRING("Unable to open temporary output file: {}"),
|
||||
strerror(errno)));
|
||||
open_temp_res.unwrapErr()));
|
||||
} else {
|
||||
auto fd_copy = auto_fd::dup_of(fileno(tmpout));
|
||||
char desc[256], timestamp[32];
|
||||
time_t current_time = time(nullptr);
|
||||
const auto path_and_args = rc->get_value();
|
||||
auto tmp_pair = open_temp_res.unwrap();
|
||||
auto fd_copy = tmp_pair.second.dup();
|
||||
|
||||
{
|
||||
exec_context::output_guard og(
|
||||
ec, "tmp", std::make_pair(tmpout.release(), fclose));
|
||||
ec,
|
||||
"tmp",
|
||||
std::make_pair(fdopen(tmp_pair.second.release(), "w"),
|
||||
fclose));
|
||||
|
||||
auto exec_res
|
||||
= execute_file(ec, path_and_args.get_string());
|
||||
@ -782,8 +787,8 @@ rl_callback_int(readline_curses* rc, bool is_alt)
|
||||
"Output of %s (%s)",
|
||||
path_and_args.get_string().c_str(),
|
||||
timestamp);
|
||||
lnav_data.ld_active_files.fc_file_names[desc]
|
||||
.with_fd(std::move(fd_copy))
|
||||
lnav_data.ld_active_files.fc_file_names[tmp_pair.first]
|
||||
.with_filename(desc)
|
||||
.with_include_in_session(false)
|
||||
.with_detect_format(false);
|
||||
lnav_data.ld_files_to_front.emplace_back(desc, 0_vl);
|
||||
|
@ -421,6 +421,30 @@ readline_context::attempted_completion(const char* text, int start, int end)
|
||||
|
||||
if (proto.empty()) {
|
||||
arg_possibilities = nullptr;
|
||||
} else if (proto[0] == "dirname") {
|
||||
shlex fn_lexer(rl_line_buffer, rl_point);
|
||||
std::vector<std::string> fn_list;
|
||||
|
||||
fn_lexer.split(fn_list, scope);
|
||||
|
||||
const auto& last_fn = fn_list.size() <= 1 ? ""
|
||||
: fn_list.back();
|
||||
|
||||
static std::set<std::string> dir_name_set;
|
||||
|
||||
dir_name_set.clear();
|
||||
auto_mem<char> completed_fn;
|
||||
int fn_state = 0;
|
||||
|
||||
while ((completed_fn = rl_filename_completion_function(
|
||||
last_fn.c_str(), fn_state))
|
||||
!= nullptr)
|
||||
{
|
||||
dir_name_set.insert(completed_fn.in());
|
||||
fn_state += 1;
|
||||
}
|
||||
arg_possibilities = &dir_name_set;
|
||||
arg_needs_shlex = true;
|
||||
} else if (proto[0] == "filename") {
|
||||
shlex fn_lexer(rl_line_buffer, rl_point);
|
||||
std::vector<std::string> fn_list;
|
||||
@ -862,7 +886,7 @@ readline_curses::start()
|
||||
}
|
||||
}
|
||||
if (FD_ISSET(this->rc_command_pipe[RCF_SLAVE], &ready_rfds)) {
|
||||
char msg[1024 + 1];
|
||||
char msg[8 + MAXPATHLEN + 1024];
|
||||
|
||||
if ((rc = recvstring(this->rc_command_pipe[RCF_SLAVE],
|
||||
msg,
|
||||
@ -875,7 +899,13 @@ readline_curses::start()
|
||||
char type[1024];
|
||||
|
||||
msg[rc] = '\0';
|
||||
if (sscanf(msg, "i:%d:%n", &rl_point, &prompt_start) == 1) {
|
||||
if (startswith(msg, "cd:")) {
|
||||
const char* cwd = &msg[3];
|
||||
|
||||
log_perror(chdir(cwd));
|
||||
} else if (sscanf(msg, "i:%d:%n", &rl_point, &prompt_start)
|
||||
== 1)
|
||||
{
|
||||
const char* initial = &msg[prompt_start];
|
||||
|
||||
rl_extend_line_buffer(strlen(initial) + 1);
|
||||
@ -1247,12 +1277,21 @@ readline_curses::focus(int context,
|
||||
const std::string& prompt,
|
||||
const std::string& initial)
|
||||
{
|
||||
char buffer[1024];
|
||||
char cwd[MAXPATHLEN + 1024];
|
||||
char buffer[8 + sizeof(cwd)];
|
||||
|
||||
curs_set(1);
|
||||
|
||||
this->rc_active_context = context;
|
||||
|
||||
getcwd(cwd, sizeof(cwd));
|
||||
snprintf(buffer, sizeof(buffer), "cd:%s", cwd);
|
||||
if (sendstring(
|
||||
this->rc_command_pipe[RCF_MASTER], buffer, strlen(buffer) + 1)
|
||||
== -1)
|
||||
{
|
||||
perror("focus: write failed");
|
||||
}
|
||||
snprintf(buffer, sizeof(buffer), "f:%d:%s", context, prompt.c_str());
|
||||
if (sendstring(
|
||||
this->rc_command_pipe[RCF_MASTER], buffer, strlen(buffer) + 1)
|
||||
|
@ -26,6 +26,10 @@
|
||||
"transfer-command": "cat > {0:} && chmod ugo+rx ./{0:}"
|
||||
}
|
||||
},
|
||||
"piper": {
|
||||
"max-size": 10485760,
|
||||
"rotations": 4
|
||||
},
|
||||
"clipboard": {
|
||||
"impls": {
|
||||
"MacOS": {
|
||||
|
@ -75,10 +75,18 @@ textfile_sub_source::text_value_for_line(textview_curses& tc,
|
||||
if (line < 0 || line >= lfo->lfo_filter_state.tfs_index.size()) {
|
||||
value_out.clear();
|
||||
} else {
|
||||
auto read_result = lf->read_line(
|
||||
lf->begin() + lfo->lfo_filter_state.tfs_index[line]);
|
||||
auto ll = lf->begin() + lfo->lfo_filter_state.tfs_index[line];
|
||||
auto read_result = lf->read_line(ll);
|
||||
if (read_result.isOk()) {
|
||||
value_out = to_string(read_result.unwrap());
|
||||
if (lf->has_line_metadata()
|
||||
&& this->tas_display_time_offset)
|
||||
{
|
||||
auto relstr = this->get_time_offset_for_line(
|
||||
tc, vis_line_t(line));
|
||||
value_out = fmt::format(
|
||||
FMT_STRING("{: >12}|{}"), relstr, value_out);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@ -100,16 +108,53 @@ textfile_sub_source::text_attrs_for_line(textview_curses& tc,
|
||||
return;
|
||||
}
|
||||
|
||||
auto rend_iter = this->tss_rendered_files.find(lf->get_filename());
|
||||
if (rend_iter != this->tss_rendered_files.end()) {
|
||||
rend_iter->second.rf_text_source->text_attrs_for_line(
|
||||
tc, row, value_out);
|
||||
}
|
||||
|
||||
struct line_range lr;
|
||||
|
||||
lr.lr_start = 0;
|
||||
lr.lr_end = -1;
|
||||
auto rend_iter = this->tss_rendered_files.find(lf->get_filename());
|
||||
if (rend_iter != this->tss_rendered_files.end()) {
|
||||
rend_iter->second.rf_text_source->text_attrs_for_line(
|
||||
tc, row, value_out);
|
||||
} else {
|
||||
auto* lfo
|
||||
= dynamic_cast<line_filter_observer*>(lf->get_logline_observer());
|
||||
if (row >= 0 && row < lfo->lfo_filter_state.tfs_index.size()) {
|
||||
auto ll = lf->begin() + lfo->lfo_filter_state.tfs_index[row];
|
||||
|
||||
value_out.emplace_back(lr, SA_LEVEL.value(ll->get_msg_level()));
|
||||
if (lf->has_line_metadata() && this->tas_display_time_offset) {
|
||||
auto time_offset_end = 13;
|
||||
lr.lr_start = 0;
|
||||
lr.lr_end = time_offset_end;
|
||||
|
||||
shift_string_attrs(value_out, 0, time_offset_end);
|
||||
|
||||
value_out.emplace_back(lr,
|
||||
VC_ROLE.value(role_t::VCR_OFFSET_TIME));
|
||||
value_out.emplace_back(line_range(12, 13),
|
||||
VC_GRAPHIC.value(ACS_VLINE));
|
||||
|
||||
role_t bar_role = role_t::VCR_NONE;
|
||||
|
||||
switch (this->get_line_accel_direction(vis_line_t(row))) {
|
||||
case log_accel::A_STEADY:
|
||||
break;
|
||||
case log_accel::A_DECEL:
|
||||
bar_role = role_t::VCR_DIFF_DELETE;
|
||||
break;
|
||||
case log_accel::A_ACCEL:
|
||||
bar_role = role_t::VCR_DIFF_ADD;
|
||||
break;
|
||||
}
|
||||
if (bar_role != role_t::VCR_NONE) {
|
||||
value_out.emplace_back(line_range(12, 13),
|
||||
VC_ROLE.value(bar_role));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
value_out.emplace_back(lr, logline::L_FILE.value(this->current_file()));
|
||||
}
|
||||
|
||||
@ -157,6 +202,7 @@ textfile_sub_source::to_front(const std::shared_ptr<logfile>& lf)
|
||||
}
|
||||
}
|
||||
this->tss_files.push_front(lf);
|
||||
this->set_time_offset(false);
|
||||
this->tss_view->reload_data();
|
||||
}
|
||||
|
||||
@ -166,6 +212,7 @@ textfile_sub_source::rotate_left()
|
||||
if (this->tss_files.size() > 1) {
|
||||
this->tss_files.push_back(this->tss_files.front());
|
||||
this->tss_files.pop_front();
|
||||
this->set_time_offset(false);
|
||||
this->tss_view->reload_data();
|
||||
this->tss_view->redo_search();
|
||||
}
|
||||
@ -177,6 +224,7 @@ textfile_sub_source::rotate_right()
|
||||
if (this->tss_files.size() > 1) {
|
||||
this->tss_files.push_front(this->tss_files.back());
|
||||
this->tss_files.pop_back();
|
||||
this->set_time_offset(false);
|
||||
this->tss_view->reload_data();
|
||||
this->tss_view->redo_search();
|
||||
}
|
||||
@ -197,6 +245,7 @@ textfile_sub_source::remove(const std::shared_ptr<logfile>& lf)
|
||||
detach_observer(lf);
|
||||
}
|
||||
}
|
||||
this->set_time_offset(false);
|
||||
}
|
||||
|
||||
void
|
||||
@ -873,3 +922,11 @@ textfile_sub_source::to_front(const std::string& filename)
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
logline*
|
||||
textfile_sub_source::text_accel_get_line(vis_line_t vl)
|
||||
{
|
||||
auto lf = this->current_file();
|
||||
auto* lfo = dynamic_cast<line_filter_observer*>(lf->get_logline_observer());
|
||||
return (lf->begin() + lfo->lfo_filter_state.tfs_index[vl]).base();
|
||||
}
|
||||
|
@ -41,14 +41,13 @@
|
||||
class textfile_sub_source
|
||||
: public text_sub_source
|
||||
, public vis_location_history
|
||||
, public text_accel_source
|
||||
, public text_anchors {
|
||||
public:
|
||||
using file_iterator = std::deque<std::shared_ptr<logfile>>::iterator;
|
||||
|
||||
textfile_sub_source() { this->tss_supports_filtering = true; }
|
||||
|
||||
~textfile_sub_source() override = default;
|
||||
|
||||
bool empty() const { return this->tss_files.empty(); }
|
||||
|
||||
size_t size() const { return this->tss_files.size(); }
|
||||
@ -109,6 +108,8 @@ public:
|
||||
|
||||
class scan_callback {
|
||||
public:
|
||||
virtual ~scan_callback() = default;
|
||||
|
||||
virtual void closed_files(
|
||||
const std::vector<std::shared_ptr<logfile>>& files)
|
||||
= 0;
|
||||
@ -144,6 +145,18 @@ public:
|
||||
|
||||
void quiesce() override;
|
||||
|
||||
bool is_time_offset_supported() const override
|
||||
{
|
||||
const auto lf = this->current_file();
|
||||
if (lf != nullptr && lf->has_line_metadata()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
logline* text_accel_get_line(vis_line_t vl) override;
|
||||
|
||||
private:
|
||||
void detach_observer(std::shared_ptr<logfile> lf)
|
||||
{
|
||||
|
@ -33,6 +33,7 @@
|
||||
#include "textview_curses.hh"
|
||||
|
||||
#include "base/ansi_scrubber.hh"
|
||||
#include "base/humanize.time.hh"
|
||||
#include "base/injector.hh"
|
||||
#include "base/time_util.hh"
|
||||
#include "config.h"
|
||||
@ -124,6 +125,58 @@ text_filter::end_of_message(logfile_filter_state& lfs)
|
||||
lfs.tfs_lines_for_message[this->lf_index] = 0;
|
||||
}
|
||||
|
||||
log_accel::direction_t
|
||||
text_accel_source::get_line_accel_direction(vis_line_t vl)
|
||||
{
|
||||
log_accel la;
|
||||
|
||||
while (vl >= 0) {
|
||||
const auto* curr_line = this->text_accel_get_line(vl);
|
||||
|
||||
if (!curr_line->is_message()) {
|
||||
--vl;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!la.add_point(curr_line->get_time_in_millis())) {
|
||||
break;
|
||||
}
|
||||
|
||||
--vl;
|
||||
}
|
||||
|
||||
return la.get_direction();
|
||||
}
|
||||
|
||||
std::string
|
||||
text_accel_source::get_time_offset_for_line(textview_curses& tc, vis_line_t vl)
|
||||
{
|
||||
auto ll = this->text_accel_get_line(vl);
|
||||
auto curr_tv = ll->get_timeval();
|
||||
struct timeval diff_tv;
|
||||
|
||||
auto prev_umark = tc.get_bookmarks()[&textview_curses::BM_USER].prev(vl);
|
||||
auto next_umark = tc.get_bookmarks()[&textview_curses::BM_USER].next(vl);
|
||||
auto prev_emark
|
||||
= tc.get_bookmarks()[&textview_curses::BM_USER_EXPR].prev(vl);
|
||||
auto next_emark
|
||||
= tc.get_bookmarks()[&textview_curses::BM_USER_EXPR].next(vl);
|
||||
if (!prev_umark && !prev_emark && (next_umark || next_emark)) {
|
||||
auto next_line = this->text_accel_get_line(
|
||||
std::max(next_umark.value_or(0), next_emark.value_or(0)));
|
||||
|
||||
diff_tv = curr_tv - next_line->get_timeval();
|
||||
} else {
|
||||
auto prev_row
|
||||
= std::max(prev_umark.value_or(0), prev_emark.value_or(0));
|
||||
auto first_line = this->text_accel_get_line(prev_row);
|
||||
auto start_tv = first_line->get_timeval();
|
||||
diff_tv = curr_tv - start_tv;
|
||||
}
|
||||
|
||||
return humanize::time::duration::from_tv(diff_tv).to_string();
|
||||
}
|
||||
|
||||
const bookmark_type_t textview_curses::BM_USER("user");
|
||||
const bookmark_type_t textview_curses::BM_USER_EXPR("user-expr");
|
||||
const bookmark_type_t textview_curses::BM_SEARCH("search");
|
||||
|
@ -43,6 +43,7 @@
|
||||
#include "highlighter.hh"
|
||||
#include "listview_curses.hh"
|
||||
#include "lnav_config_fwd.hh"
|
||||
#include "log_accel.hh"
|
||||
#include "logfile_fwd.hh"
|
||||
#include "ring_span.hh"
|
||||
#include "text_format.hh"
|
||||
@ -237,6 +238,43 @@ protected:
|
||||
};
|
||||
};
|
||||
|
||||
class text_accel_source {
|
||||
public:
|
||||
virtual ~text_accel_source() = default;
|
||||
|
||||
virtual log_accel::direction_t get_line_accel_direction(vis_line_t vl);
|
||||
|
||||
void toggle_time_offset()
|
||||
{
|
||||
this->tas_display_time_offset = !this->tas_display_time_offset;
|
||||
this->text_accel_display_changed();
|
||||
}
|
||||
|
||||
void set_time_offset(bool enabled)
|
||||
{
|
||||
if (this->tas_display_time_offset != enabled) {
|
||||
this->tas_display_time_offset = enabled;
|
||||
this->text_accel_display_changed();
|
||||
}
|
||||
}
|
||||
|
||||
bool is_time_offset_enabled() const
|
||||
{
|
||||
return this->tas_display_time_offset;
|
||||
}
|
||||
|
||||
virtual bool is_time_offset_supported() const { return true; }
|
||||
|
||||
virtual logline* text_accel_get_line(vis_line_t vl) = 0;
|
||||
|
||||
std::string get_time_offset_for_line(textview_curses& tc, vis_line_t vl);
|
||||
|
||||
protected:
|
||||
virtual void text_accel_display_changed() {}
|
||||
|
||||
bool tas_display_time_offset{false};
|
||||
};
|
||||
|
||||
class text_anchors {
|
||||
public:
|
||||
virtual ~text_anchors() = default;
|
||||
|
@ -37,20 +37,21 @@
|
||||
# include <paths.h>
|
||||
|
||||
# include "base/fs_util.hh"
|
||||
# include "base/paths.hh"
|
||||
# include "curl_looper.hh"
|
||||
|
||||
class url_loader : public curl_request {
|
||||
public:
|
||||
url_loader(const std::string& url) : curl_request(url)
|
||||
{
|
||||
auto tmp_res = lnav::filesystem::open_temp_file(
|
||||
ghc::filesystem::temp_directory_path() / "lnav.url.XXXXXX");
|
||||
auto tmp_res = lnav::filesystem::open_temp_file(lnav::paths::workdir()
|
||||
/ "url.XXXXXX");
|
||||
if (tmp_res.isErr()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto tmp_pair = tmp_res.unwrap();
|
||||
ghc::filesystem::remove(tmp_pair.first);
|
||||
this->ul_path = tmp_pair.first;
|
||||
this->ul_fd = std::move(tmp_pair.second);
|
||||
|
||||
curl_easy_setopt(this->cr_handle, CURLOPT_URL, this->cr_name.c_str());
|
||||
@ -60,9 +61,7 @@ public:
|
||||
curl_easy_setopt(this->cr_handle, CURLOPT_BUFFERSIZE, 128L * 1024L);
|
||||
}
|
||||
|
||||
int get_fd() const { return this->ul_fd.get(); }
|
||||
|
||||
auto_fd copy_fd() const { return this->ul_fd.dup(); }
|
||||
ghc::filesystem::path get_path() const { return this->ul_path; }
|
||||
|
||||
long complete(CURLcode result)
|
||||
{
|
||||
@ -93,7 +92,8 @@ public:
|
||||
|
||||
time(¤t_time);
|
||||
if (file_time == -1
|
||||
|| (current_time - file_time) < FOLLOW_IF_MODIFIED_SINCE) {
|
||||
|| (current_time - file_time) < FOLLOW_IF_MODIFIED_SINCE)
|
||||
{
|
||||
char range[64];
|
||||
struct stat st;
|
||||
off_t start;
|
||||
@ -142,6 +142,7 @@ private:
|
||||
return retval;
|
||||
}
|
||||
|
||||
ghc::filesystem::path ul_path;
|
||||
auto_fd ul_fd;
|
||||
off_t ul_resume_offset{0};
|
||||
};
|
||||
|
@ -6,10 +6,14 @@ EXPECTED_FILES = \
|
||||
$(srcdir)/%reldir%/test_cli.sh_5524542b1a6954ff9741155101497270a2f0c557.out \
|
||||
$(srcdir)/%reldir%/test_cli.sh_97e19b9ff3775d84074455a2e8993a0611b1c269.err \
|
||||
$(srcdir)/%reldir%/test_cli.sh_97e19b9ff3775d84074455a2e8993a0611b1c269.out \
|
||||
$(srcdir)/%reldir%/test_cli.sh_a1a09f890f4604309d0a81bbbec8e50fb7d5e887.err \
|
||||
$(srcdir)/%reldir%/test_cli.sh_a1a09f890f4604309d0a81bbbec8e50fb7d5e887.out \
|
||||
$(srcdir)/%reldir%/test_cli.sh_c69c835a3c43210225cf62564b3e9584c899af20.err \
|
||||
$(srcdir)/%reldir%/test_cli.sh_c69c835a3c43210225cf62564b3e9584c899af20.out \
|
||||
$(srcdir)/%reldir%/test_cli.sh_f2e41555f1a5f40f54ce241207af602ed1503a2b.err \
|
||||
$(srcdir)/%reldir%/test_cli.sh_f2e41555f1a5f40f54ce241207af602ed1503a2b.out \
|
||||
$(srcdir)/%reldir%/test_cli.sh_ff7da172f4350a2adb74b8764575823d798ed8b6.err \
|
||||
$(srcdir)/%reldir%/test_cli.sh_ff7da172f4350a2adb74b8764575823d798ed8b6.out \
|
||||
$(srcdir)/%reldir%/test_cmds.sh_015ffe79a08f4c9f0cd1cb84c6afa4398f879fc7.err \
|
||||
$(srcdir)/%reldir%/test_cmds.sh_015ffe79a08f4c9f0cd1cb84c6afa4398f879fc7.out \
|
||||
$(srcdir)/%reldir%/test_cmds.sh_017b495b95218b7c083951e2dba331cfec6e90be.err \
|
||||
$(srcdir)/%reldir%/test_cmds.sh_017b495b95218b7c083951e2dba331cfec6e90be.out \
|
||||
$(srcdir)/%reldir%/test_cmds.sh_0b1e4b1523dfca71927b1fe721c74490c51361d1.err \
|
||||
@ -96,6 +100,8 @@ EXPECTED_FILES = \
|
||||
$(srcdir)/%reldir%/test_cmds.sh_62d68c0a11757c996f24c8f003e6b4059c3e30b2.out \
|
||||
$(srcdir)/%reldir%/test_cmds.sh_661ec61acdd8f6fa6ec1e3c2cf5f896eef431351.err \
|
||||
$(srcdir)/%reldir%/test_cmds.sh_661ec61acdd8f6fa6ec1e3c2cf5f896eef431351.out \
|
||||
$(srcdir)/%reldir%/test_cmds.sh_68c774418bac897bd4d4fe9dbbf08454886b2e15.err \
|
||||
$(srcdir)/%reldir%/test_cmds.sh_68c774418bac897bd4d4fe9dbbf08454886b2e15.out \
|
||||
$(srcdir)/%reldir%/test_cmds.sh_6a6031113aca32fabc5a3da64b7be46f5ce5a312.err \
|
||||
$(srcdir)/%reldir%/test_cmds.sh_6a6031113aca32fabc5a3da64b7be46f5ce5a312.out \
|
||||
$(srcdir)/%reldir%/test_cmds.sh_6e016c0ed61fc652be1a79b864875ffede64f281.err \
|
||||
@ -130,10 +136,14 @@ EXPECTED_FILES = \
|
||||
$(srcdir)/%reldir%/test_cmds.sh_8d5b43c693e78804a8fb06989392fa8cccb46b7b.out \
|
||||
$(srcdir)/%reldir%/test_cmds.sh_9445861db011dfa2d21a44788047de345ee291e8.err \
|
||||
$(srcdir)/%reldir%/test_cmds.sh_9445861db011dfa2d21a44788047de345ee291e8.out \
|
||||
$(srcdir)/%reldir%/test_cmds.sh_9527f941dc84a2ac3a030f222e41c6ccd1961cbe.err \
|
||||
$(srcdir)/%reldir%/test_cmds.sh_9527f941dc84a2ac3a030f222e41c6ccd1961cbe.out \
|
||||
$(srcdir)/%reldir%/test_cmds.sh_95beaabe41d72cf4c6810e79c623da759ac1c71b.err \
|
||||
$(srcdir)/%reldir%/test_cmds.sh_95beaabe41d72cf4c6810e79c623da759ac1c71b.out \
|
||||
$(srcdir)/%reldir%/test_cmds.sh_968dac54dc80d91a5da2322890c6c26dfa0d8462.err \
|
||||
$(srcdir)/%reldir%/test_cmds.sh_968dac54dc80d91a5da2322890c6c26dfa0d8462.out \
|
||||
$(srcdir)/%reldir%/test_cmds.sh_9dfc433f14b811afb3ec4daef0f33e4c9b14a6d7.err \
|
||||
$(srcdir)/%reldir%/test_cmds.sh_9dfc433f14b811afb3ec4daef0f33e4c9b14a6d7.out \
|
||||
$(srcdir)/%reldir%/test_cmds.sh_a00943ef715598c7554b85de8502454e41bb9e28.err \
|
||||
$(srcdir)/%reldir%/test_cmds.sh_a00943ef715598c7554b85de8502454e41bb9e28.out \
|
||||
$(srcdir)/%reldir%/test_cmds.sh_a1123427c31c022433d66d05ee5d5e1c8ab415e4.err \
|
||||
@ -150,6 +160,8 @@ EXPECTED_FILES = \
|
||||
$(srcdir)/%reldir%/test_cmds.sh_ac45fb0f8f9578c3ded0855f694698ec38ce31ad.out \
|
||||
$(srcdir)/%reldir%/test_cmds.sh_af0fcbd30b3fd0d13477aa3325ef0302052a4d9f.err \
|
||||
$(srcdir)/%reldir%/test_cmds.sh_af0fcbd30b3fd0d13477aa3325ef0302052a4d9f.out \
|
||||
$(srcdir)/%reldir%/test_cmds.sh_b3d0588ad144a841200692b46125bddf66f5d8bb.err \
|
||||
$(srcdir)/%reldir%/test_cmds.sh_b3d0588ad144a841200692b46125bddf66f5d8bb.out \
|
||||
$(srcdir)/%reldir%/test_cmds.sh_b5a530d16c982cf769151291f0bfd612ea71183f.err \
|
||||
$(srcdir)/%reldir%/test_cmds.sh_b5a530d16c982cf769151291f0bfd612ea71183f.out \
|
||||
$(srcdir)/%reldir%/test_cmds.sh_b6a3bb78e9d60e5e1f5ce5b18e40d2f1662707ab.err \
|
||||
@ -184,6 +196,8 @@ EXPECTED_FILES = \
|
||||
$(srcdir)/%reldir%/test_cmds.sh_ca66660c973f76a3c2a147c7f5035bcb4e8a8bbc.out \
|
||||
$(srcdir)/%reldir%/test_cmds.sh_ccd326da92d1cacda63501cd1a3077381a18e8f2.err \
|
||||
$(srcdir)/%reldir%/test_cmds.sh_ccd326da92d1cacda63501cd1a3077381a18e8f2.out \
|
||||
$(srcdir)/%reldir%/test_cmds.sh_d1afefacbdd387f02562c8633968b0162a588502.err \
|
||||
$(srcdir)/%reldir%/test_cmds.sh_d1afefacbdd387f02562c8633968b0162a588502.out \
|
||||
$(srcdir)/%reldir%/test_cmds.sh_d3b69abdfb39e4bfa5828c2f9593e2b2b7ed4d5d.err \
|
||||
$(srcdir)/%reldir%/test_cmds.sh_d3b69abdfb39e4bfa5828c2f9593e2b2b7ed4d5d.out \
|
||||
$(srcdir)/%reldir%/test_cmds.sh_d76d77ad95b9f120825417a6a8220c13df9541fc.err \
|
||||
|
@ -1,3 +0,0 @@
|
||||
2013-06-06T19:13:20.123 Hello, World!
|
||||
2013-06-06T19:13:20.123 Goodbye, World!
|
||||
2013-06-06T19:13:20.123 ---- END-OF-STDIN ----
|
@ -0,0 +1,4 @@
|
||||
Feb 25 16:20:12 192.168.4.2 haproxy[7]: 95.216.197.33:56224 [25/Feb/2019:16:20:10.111] prod_http_in/sktst2: SSL handshake failure
|
||||
Feb 25 16:20:12 192.168.4.2 haproxy[7]: 87.183.41.77:50189 [25/Feb/2019:16:20:12.331] prod_http_in~ bk_ktest_sonst/nginx_sonst 0/0/1/0/1 200 2496 - - ---- 9/9/0/0/0 0/0 {Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:65.0) Gecko/20100101 Firefox/65.0} {} "GET /media/core/core.css?1550939640 HTTP/1.1"
|
||||
Feb 25 16:20:12 192.168.4.2 haproxy[7]: 87.183.41.77:50187 [25/Feb/2019:16:20:12.325] prod_http_in~ bk_ktest_sonst/nginx_sonst 0/0/1/0/1 200 1859 - - ---- 9/9/0/0/0 0/0 {Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:65.0) Gecko/20100101 Firefox/65.0} {} "GET /media/pi_popup/1.1.0/magnific-popup.css?1550939704 HTTP/1.1"
|
||||
Feb 25 16:20:12 192.168.4.2 haproxy[7]: 87.183.41.77:50188 [25/Feb/2019:16:20:12.321] prod_http_in~ bk_ktest_sonst/nginx_sonst 0/0/1/0/1 200 5959 - - ---- 9/9/0/0/0 0/0 {Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:65.0) Gecko/20100101 Firefox/65.0} {} "GET /media/pi_fontawesome/css/font-awesome.css?1550939694 HTTP/1.1"
|
@ -0,0 +1,2 @@
|
||||
Hello, World!
|
||||
Goodbye, World!
|
@ -0,0 +1,7 @@
|
||||
[1m[31m✘ error[0m: cannot access -- /bad-dir
|
||||
[1m[31mreason[0m: No such file or directory
|
||||
[36m --> [0m[1mcommand-option[0m:1
|
||||
[36m | [0m[37m[40m:[0m[1m[36m[40mcd[0m[37m[40m /bad-dir [0m
|
||||
[36m =[0m [36mhelp[0m: [4m:[0m[1m[4mcd[0m[4m [0m[4mdir[0m
|
||||
══════════════════════════════════════════════════════════════════════
|
||||
Change the current directory
|
@ -0,0 +1,3 @@
|
||||
192.168.202.254 - - [20/Jul/2009:22:59:26 +0000] "GET /vmw/cgi/tramp HTTP/1.0" 200 134 "-" "gPXE/0.9.7"
|
||||
[31m192.168.202.254[0m[31m - [0m[31m-[0m[31m [[0m[31m20/Jul/2009:22:59:29 +0000[0m[31m] "[0m[31mGET[0m[31m [0m[31m/vmw/vSphere/default/vmkboot.gz[0m[31m [0m[31mHTTP/1.0[0m[31m" 404 46210 "[0m[31m-[0m[31m" "[0m[31mgPXE/0.9.7[0m[31m"[0m
|
||||
192.168.202.254 - - [20/Jul/2009:22:59:29 +0000] "GET /vmw/vSphere/default/vmkernel.gz HTTP/1.0" 200 78929 "-" "gPXE/0.9.7"
|
@ -0,0 +1 @@
|
||||
[31mHello, World![0m
|
@ -0,0 +1 @@
|
||||
[31m/bin/bash: bad-command: command not found[0m
|
@ -0,0 +1 @@
|
||||
Hello, World!
|
@ -702,7 +702,7 @@ For support questions, email:
|
||||
[4mParameter[0m
|
||||
[4mmsg[0m The message to display
|
||||
[4mSee Also[0m
|
||||
[1m:echo[0m, [1m:eval[0m, [1m:export-session-to[0m, [1m:rebuild[0m, [1m:redirect-to[0m,
|
||||
[1m:cd[0m, [1m:echo[0m, [1m:eval[0m, [1m:export-session-to[0m, [1m:rebuild[0m, [1m:redirect-to[0m, [1m:sh[0m,
|
||||
[1m:write-csv-to[0m, [1m:write-json-to[0m, [1m:write-jsonlines-to[0m, [1m:write-raw-to[0m,
|
||||
[1m:write-screen-to[0m, [1m:write-table-to[0m, [1m:write-to[0m, [1m:write-view-to[0m
|
||||
[4mExample[0m
|
||||
@ -726,6 +726,16 @@ For support questions, email:
|
||||
|
||||
|
||||
|
||||
[4m:[0m[1m[4mcd[0m[4m [0m[4mdir[0m
|
||||
══════════════════════════════════════════════════════════════════════
|
||||
Change the current directory
|
||||
[4mParameter[0m
|
||||
[4mdir[0m The new current directory
|
||||
[4mSee Also[0m
|
||||
[1m:alt-msg[0m, [1m:echo[0m, [1m:eval[0m, [1m:export-session-to[0m, [1m:rebuild[0m, [1m:redirect-to[0m,
|
||||
[1m:sh[0m, [1m:write-csv-to[0m, [1m:write-json-to[0m, [1m:write-jsonlines-to[0m, [1m:write-raw-to[0m,
|
||||
[1m:write-screen-to[0m, [1m:write-table-to[0m, [1m:write-to[0m, [1m:write-view-to[0m
|
||||
|
||||
[4m:[0m[1m[4mclear-comment[0m
|
||||
══════════════════════════════════════════════════════════════════════
|
||||
Clear the comment attached to the top log line
|
||||
@ -934,12 +944,13 @@ For support questions, email:
|
||||
[4m-n[0m Do not print a line-feed at the end of the output
|
||||
[4mmsg[0m The message to display
|
||||
[4mSee Also[0m
|
||||
[1m:alt-msg[0m, [1m:append-to[0m, [1m:eval[0m, [1m:export-session-to[0m, [1m:export-session-to[0m,
|
||||
[1m:pipe-line-to[0m, [1m:pipe-to[0m, [1m:rebuild[0m, [1m:redirect-to[0m, [1m:redirect-to[0m,
|
||||
[1m:write-csv-to[0m, [1m:write-csv-to[0m, [1m:write-json-to[0m, [1m:write-json-to[0m,
|
||||
[1m:write-jsonlines-to[0m, [1m:write-jsonlines-to[0m, [1m:write-raw-to[0m, [1m:write-raw-to[0m,
|
||||
[1m:write-screen-to[0m, [1m:write-screen-to[0m, [1m:write-table-to[0m, [1m:write-table-to[0m,
|
||||
[1m:write-to[0m, [1m:write-to[0m, [1m:write-view-to[0m, [1m:write-view-to[0m, [1mecholn()[0m
|
||||
[1m:alt-msg[0m, [1m:append-to[0m, [1m:cd[0m, [1m:eval[0m, [1m:export-session-to[0m,
|
||||
[1m:export-session-to[0m, [1m:pipe-line-to[0m, [1m:pipe-to[0m, [1m:rebuild[0m, [1m:redirect-to[0m,
|
||||
[1m:redirect-to[0m, [1m:sh[0m, [1m:write-csv-to[0m, [1m:write-csv-to[0m, [1m:write-json-to[0m,
|
||||
[1m:write-json-to[0m, [1m:write-jsonlines-to[0m, [1m:write-jsonlines-to[0m,
|
||||
[1m:write-raw-to[0m, [1m:write-raw-to[0m, [1m:write-screen-to[0m, [1m:write-screen-to[0m,
|
||||
[1m:write-table-to[0m, [1m:write-table-to[0m, [1m:write-to[0m, [1m:write-to[0m, [1m:write-view-to[0m,
|
||||
[1m:write-view-to[0m, [1mecholn()[0m
|
||||
[4mExample[0m
|
||||
#1 To output 'Hello, World!':
|
||||
[37m[40m:[0m[1m[36m[40mecho[0m[37m[40m Hello, World! [0m
|
||||
@ -974,7 +985,7 @@ For support questions, email:
|
||||
[4mParameter[0m
|
||||
[4mcommand[0m The command or query to perform substitution on.
|
||||
[4mSee Also[0m
|
||||
[1m:alt-msg[0m, [1m:echo[0m, [1m:export-session-to[0m, [1m:rebuild[0m, [1m:redirect-to[0m,
|
||||
[1m:alt-msg[0m, [1m:cd[0m, [1m:echo[0m, [1m:export-session-to[0m, [1m:rebuild[0m, [1m:redirect-to[0m, [1m:sh[0m,
|
||||
[1m:write-csv-to[0m, [1m:write-json-to[0m, [1m:write-jsonlines-to[0m, [1m:write-raw-to[0m,
|
||||
[1m:write-screen-to[0m, [1m:write-table-to[0m, [1m:write-to[0m, [1m:write-view-to[0m
|
||||
[4mExample[0m
|
||||
@ -990,9 +1001,9 @@ For support questions, email:
|
||||
[4mParameter[0m
|
||||
[4mpath[0m The path to the file to write
|
||||
[4mSee Also[0m
|
||||
[1m:alt-msg[0m, [1m:append-to[0m, [1m:echo[0m, [1m:echo[0m, [1m:eval[0m, [1m:pipe-line-to[0m, [1m:pipe-to[0m,
|
||||
[1m:rebuild[0m, [1m:redirect-to[0m, [1m:redirect-to[0m, [1m:write-csv-to[0m, [1m:write-csv-to[0m,
|
||||
[1m:write-json-to[0m, [1m:write-json-to[0m, [1m:write-jsonlines-to[0m,
|
||||
[1m:alt-msg[0m, [1m:append-to[0m, [1m:cd[0m, [1m:echo[0m, [1m:echo[0m, [1m:eval[0m, [1m:pipe-line-to[0m,
|
||||
[1m:pipe-to[0m, [1m:rebuild[0m, [1m:redirect-to[0m, [1m:redirect-to[0m, [1m:sh[0m, [1m:write-csv-to[0m,
|
||||
[1m:write-csv-to[0m, [1m:write-json-to[0m, [1m:write-json-to[0m, [1m:write-jsonlines-to[0m,
|
||||
[1m:write-jsonlines-to[0m, [1m:write-raw-to[0m, [1m:write-raw-to[0m, [1m:write-screen-to[0m,
|
||||
[1m:write-screen-to[0m, [1m:write-table-to[0m, [1m:write-table-to[0m, [1m:write-to[0m,
|
||||
[1m:write-to[0m, [1m:write-view-to[0m, [1m:write-view-to[0m, [1mecholn()[0m
|
||||
@ -1336,7 +1347,7 @@ For support questions, email:
|
||||
══════════════════════════════════════════════════════════════════════
|
||||
Forcefully rebuild file indexes
|
||||
[4mSee Also[0m
|
||||
[1m:alt-msg[0m, [1m:echo[0m, [1m:eval[0m, [1m:export-session-to[0m, [1m:redirect-to[0m,
|
||||
[1m:alt-msg[0m, [1m:cd[0m, [1m:echo[0m, [1m:eval[0m, [1m:export-session-to[0m, [1m:redirect-to[0m, [1m:sh[0m,
|
||||
[1m:write-csv-to[0m, [1m:write-json-to[0m, [1m:write-jsonlines-to[0m, [1m:write-raw-to[0m,
|
||||
[1m:write-screen-to[0m, [1m:write-table-to[0m, [1m:write-to[0m, [1m:write-view-to[0m
|
||||
|
||||
@ -1348,12 +1359,12 @@ For support questions, email:
|
||||
[4mpath[0m The path to the file to write. If not specified, the
|
||||
current redirect will be cleared
|
||||
[4mSee Also[0m
|
||||
[1m:alt-msg[0m, [1m:append-to[0m, [1m:echo[0m, [1m:echo[0m, [1m:eval[0m, [1m:export-session-to[0m,
|
||||
[1m:export-session-to[0m, [1m:pipe-line-to[0m, [1m:pipe-to[0m, [1m:rebuild[0m, [1m:write-csv-to[0m,
|
||||
[1m:write-csv-to[0m, [1m:write-json-to[0m, [1m:write-json-to[0m, [1m:write-jsonlines-to[0m,
|
||||
[1m:write-jsonlines-to[0m, [1m:write-raw-to[0m, [1m:write-raw-to[0m, [1m:write-screen-to[0m,
|
||||
[1m:write-screen-to[0m, [1m:write-table-to[0m, [1m:write-table-to[0m, [1m:write-to[0m,
|
||||
[1m:write-to[0m, [1m:write-view-to[0m, [1m:write-view-to[0m, [1mecholn()[0m
|
||||
[1m:alt-msg[0m, [1m:append-to[0m, [1m:cd[0m, [1m:echo[0m, [1m:echo[0m, [1m:eval[0m, [1m:export-session-to[0m,
|
||||
[1m:export-session-to[0m, [1m:pipe-line-to[0m, [1m:pipe-to[0m, [1m:rebuild[0m, [1m:sh[0m,
|
||||
[1m:write-csv-to[0m, [1m:write-csv-to[0m, [1m:write-json-to[0m, [1m:write-json-to[0m,
|
||||
[1m:write-jsonlines-to[0m, [1m:write-jsonlines-to[0m, [1m:write-raw-to[0m, [1m:write-raw-to[0m,
|
||||
[1m:write-screen-to[0m, [1m:write-screen-to[0m, [1m:write-table-to[0m, [1m:write-table-to[0m,
|
||||
[1m:write-to[0m, [1m:write-to[0m, [1m:write-view-to[0m, [1m:write-view-to[0m, [1mecholn()[0m
|
||||
[4mExample[0m
|
||||
#1 To write the output of lnav commands to the file /tmp/script-output.txt:
|
||||
[37m[40m:[0m[1m[36m[40mredirect-to[0m[37m[40m /tmp/script-output.txt [0m
|
||||
@ -1430,6 +1441,17 @@ For support questions, email:
|
||||
|
||||
|
||||
|
||||
[4m:[0m[1m[4msh[0m[4m [0m[4mcmdline[0m
|
||||
══════════════════════════════════════════════════════════════════════
|
||||
Execute the given command-line and display the captured output
|
||||
[4mParameter[0m
|
||||
[4mcmdline[0m The command-line to execute.
|
||||
[4mSee Also[0m
|
||||
[1m:alt-msg[0m, [1m:cd[0m, [1m:echo[0m, [1m:eval[0m, [1m:export-session-to[0m, [1m:rebuild[0m,
|
||||
[1m:redirect-to[0m, [1m:write-csv-to[0m, [1m:write-json-to[0m, [1m:write-jsonlines-to[0m,
|
||||
[1m:write-raw-to[0m, [1m:write-screen-to[0m, [1m:write-table-to[0m, [1m:write-to[0m,
|
||||
[1m:write-view-to[0m
|
||||
|
||||
[4m:[0m[1m[4mshow-fields[0m[4m [0m[4mfield-name[0m[4m1[0m[4m [[0m[4m...[0m[4m [0m[4mfield-name[0m[4mN[0m[4m][0m
|
||||
══════════════════════════════════════════════════════════════════════
|
||||
Show log message fields that were previously hidden
|
||||
@ -1577,9 +1599,9 @@ For support questions, email:
|
||||
[4m--anonymize[0m Anonymize the table contents
|
||||
[4mpath[0m The path to the file to write
|
||||
[4mSee Also[0m
|
||||
[1m:alt-msg[0m, [1m:append-to[0m, [1m:create-logline-table[0m, [1m:create-search-table[0m,
|
||||
[1m:alt-msg[0m, [1m:append-to[0m, [1m:cd[0m, [1m:create-logline-table[0m, [1m:create-search-table[0m,
|
||||
[1m:echo[0m, [1m:echo[0m, [1m:eval[0m, [1m:export-session-to[0m, [1m:export-session-to[0m,
|
||||
[1m:pipe-line-to[0m, [1m:pipe-to[0m, [1m:rebuild[0m, [1m:redirect-to[0m, [1m:redirect-to[0m,
|
||||
[1m:pipe-line-to[0m, [1m:pipe-to[0m, [1m:rebuild[0m, [1m:redirect-to[0m, [1m:redirect-to[0m, [1m:sh[0m,
|
||||
[1m:write-csv-to[0m, [1m:write-csv-to[0m, [1m:write-csv-to[0m, [1m:write-json-to[0m,
|
||||
[1m:write-json-to[0m, [1m:write-json-to[0m, [1m:write-jsonlines-to[0m,
|
||||
[1m:write-jsonlines-to[0m, [1m:write-jsonlines-to[0m, [1m:write-raw-to[0m, [1m:write-raw-to[0m,
|
||||
@ -1599,9 +1621,9 @@ For support questions, email:
|
||||
[4m--anonymize[0m Anonymize the row contents
|
||||
[4mpath[0m The path to the file to write
|
||||
[4mSee Also[0m
|
||||
[1m:alt-msg[0m, [1m:append-to[0m, [1m:create-logline-table[0m, [1m:create-search-table[0m,
|
||||
[1m:alt-msg[0m, [1m:append-to[0m, [1m:cd[0m, [1m:create-logline-table[0m, [1m:create-search-table[0m,
|
||||
[1m:echo[0m, [1m:echo[0m, [1m:eval[0m, [1m:export-session-to[0m, [1m:export-session-to[0m,
|
||||
[1m:pipe-line-to[0m, [1m:pipe-to[0m, [1m:rebuild[0m, [1m:redirect-to[0m, [1m:redirect-to[0m,
|
||||
[1m:pipe-line-to[0m, [1m:pipe-to[0m, [1m:rebuild[0m, [1m:redirect-to[0m, [1m:redirect-to[0m, [1m:sh[0m,
|
||||
[1m:write-json-to[0m, [1m:write-json-to[0m, [1m:write-json-to[0m, [1m:write-jsonlines-to[0m,
|
||||
[1m:write-jsonlines-to[0m, [1m:write-jsonlines-to[0m, [1m:write-raw-to[0m, [1m:write-raw-to[0m,
|
||||
[1m:write-raw-to[0m, [1m:write-screen-to[0m, [1m:write-screen-to[0m, [1m:write-screen-to[0m,
|
||||
@ -1620,9 +1642,9 @@ For support questions, email:
|
||||
[4m--anonymize[0m Anonymize the JSON values
|
||||
[4mpath[0m The path to the file to write
|
||||
[4mSee Also[0m
|
||||
[1m:alt-msg[0m, [1m:append-to[0m, [1m:create-logline-table[0m, [1m:create-search-table[0m,
|
||||
[1m:alt-msg[0m, [1m:append-to[0m, [1m:cd[0m, [1m:create-logline-table[0m, [1m:create-search-table[0m,
|
||||
[1m:echo[0m, [1m:echo[0m, [1m:eval[0m, [1m:export-session-to[0m, [1m:export-session-to[0m,
|
||||
[1m:pipe-line-to[0m, [1m:pipe-to[0m, [1m:rebuild[0m, [1m:redirect-to[0m, [1m:redirect-to[0m,
|
||||
[1m:pipe-line-to[0m, [1m:pipe-to[0m, [1m:rebuild[0m, [1m:redirect-to[0m, [1m:redirect-to[0m, [1m:sh[0m,
|
||||
[1m:write-csv-to[0m, [1m:write-csv-to[0m, [1m:write-csv-to[0m, [1m:write-jsonlines-to[0m,
|
||||
[1m:write-jsonlines-to[0m, [1m:write-jsonlines-to[0m, [1m:write-raw-to[0m, [1m:write-raw-to[0m,
|
||||
[1m:write-raw-to[0m, [1m:write-screen-to[0m, [1m:write-screen-to[0m, [1m:write-screen-to[0m,
|
||||
@ -1641,9 +1663,9 @@ For support questions, email:
|
||||
[4m--anonymize[0m Anonymize the JSON values
|
||||
[4mpath[0m The path to the file to write
|
||||
[4mSee Also[0m
|
||||
[1m:alt-msg[0m, [1m:append-to[0m, [1m:create-logline-table[0m, [1m:create-search-table[0m,
|
||||
[1m:alt-msg[0m, [1m:append-to[0m, [1m:cd[0m, [1m:create-logline-table[0m, [1m:create-search-table[0m,
|
||||
[1m:echo[0m, [1m:echo[0m, [1m:eval[0m, [1m:export-session-to[0m, [1m:export-session-to[0m,
|
||||
[1m:pipe-line-to[0m, [1m:pipe-to[0m, [1m:rebuild[0m, [1m:redirect-to[0m, [1m:redirect-to[0m,
|
||||
[1m:pipe-line-to[0m, [1m:pipe-to[0m, [1m:rebuild[0m, [1m:redirect-to[0m, [1m:redirect-to[0m, [1m:sh[0m,
|
||||
[1m:write-csv-to[0m, [1m:write-csv-to[0m, [1m:write-csv-to[0m, [1m:write-json-to[0m,
|
||||
[1m:write-json-to[0m, [1m:write-json-to[0m, [1m:write-raw-to[0m, [1m:write-raw-to[0m,
|
||||
[1m:write-raw-to[0m, [1m:write-screen-to[0m, [1m:write-screen-to[0m, [1m:write-screen-to[0m,
|
||||
@ -1666,9 +1688,9 @@ For support questions, email:
|
||||
[4m--anonymize[0m Anonymize the lines
|
||||
[4mpath[0m The path to the file to write
|
||||
[4mSee Also[0m
|
||||
[1m:alt-msg[0m, [1m:append-to[0m, [1m:create-logline-table[0m, [1m:create-search-table[0m,
|
||||
[1m:alt-msg[0m, [1m:append-to[0m, [1m:cd[0m, [1m:create-logline-table[0m, [1m:create-search-table[0m,
|
||||
[1m:echo[0m, [1m:echo[0m, [1m:eval[0m, [1m:export-session-to[0m, [1m:export-session-to[0m,
|
||||
[1m:pipe-line-to[0m, [1m:pipe-to[0m, [1m:rebuild[0m, [1m:redirect-to[0m, [1m:redirect-to[0m,
|
||||
[1m:pipe-line-to[0m, [1m:pipe-to[0m, [1m:rebuild[0m, [1m:redirect-to[0m, [1m:redirect-to[0m, [1m:sh[0m,
|
||||
[1m:write-csv-to[0m, [1m:write-csv-to[0m, [1m:write-csv-to[0m, [1m:write-json-to[0m,
|
||||
[1m:write-json-to[0m, [1m:write-json-to[0m, [1m:write-jsonlines-to[0m,
|
||||
[1m:write-jsonlines-to[0m, [1m:write-jsonlines-to[0m, [1m:write-screen-to[0m,
|
||||
@ -1689,9 +1711,9 @@ For support questions, email:
|
||||
[4m--anonymize[0m Anonymize the lines
|
||||
[4mpath[0m The path to the file to write
|
||||
[4mSee Also[0m
|
||||
[1m:alt-msg[0m, [1m:append-to[0m, [1m:create-logline-table[0m, [1m:create-search-table[0m,
|
||||
[1m:alt-msg[0m, [1m:append-to[0m, [1m:cd[0m, [1m:create-logline-table[0m, [1m:create-search-table[0m,
|
||||
[1m:echo[0m, [1m:echo[0m, [1m:eval[0m, [1m:export-session-to[0m, [1m:export-session-to[0m,
|
||||
[1m:pipe-line-to[0m, [1m:pipe-to[0m, [1m:rebuild[0m, [1m:redirect-to[0m, [1m:redirect-to[0m,
|
||||
[1m:pipe-line-to[0m, [1m:pipe-to[0m, [1m:rebuild[0m, [1m:redirect-to[0m, [1m:redirect-to[0m, [1m:sh[0m,
|
||||
[1m:write-csv-to[0m, [1m:write-csv-to[0m, [1m:write-csv-to[0m, [1m:write-json-to[0m,
|
||||
[1m:write-json-to[0m, [1m:write-json-to[0m, [1m:write-jsonlines-to[0m,
|
||||
[1m:write-jsonlines-to[0m, [1m:write-jsonlines-to[0m, [1m:write-raw-to[0m, [1m:write-raw-to[0m,
|
||||
@ -1711,9 +1733,9 @@ For support questions, email:
|
||||
[4m--anonymize[0m Anonymize the table contents
|
||||
[4mpath[0m The path to the file to write
|
||||
[4mSee Also[0m
|
||||
[1m:alt-msg[0m, [1m:append-to[0m, [1m:create-logline-table[0m, [1m:create-search-table[0m,
|
||||
[1m:alt-msg[0m, [1m:append-to[0m, [1m:cd[0m, [1m:create-logline-table[0m, [1m:create-search-table[0m,
|
||||
[1m:echo[0m, [1m:echo[0m, [1m:eval[0m, [1m:export-session-to[0m, [1m:export-session-to[0m,
|
||||
[1m:pipe-line-to[0m, [1m:pipe-to[0m, [1m:rebuild[0m, [1m:redirect-to[0m, [1m:redirect-to[0m,
|
||||
[1m:pipe-line-to[0m, [1m:pipe-to[0m, [1m:rebuild[0m, [1m:redirect-to[0m, [1m:redirect-to[0m, [1m:sh[0m,
|
||||
[1m:write-csv-to[0m, [1m:write-csv-to[0m, [1m:write-csv-to[0m, [1m:write-json-to[0m,
|
||||
[1m:write-json-to[0m, [1m:write-json-to[0m, [1m:write-jsonlines-to[0m,
|
||||
[1m:write-jsonlines-to[0m, [1m:write-jsonlines-to[0m, [1m:write-raw-to[0m, [1m:write-raw-to[0m,
|
||||
@ -1733,9 +1755,9 @@ For support questions, email:
|
||||
[4m--anonymize[0m Anonymize the lines
|
||||
[4mpath[0m The path to the file to write
|
||||
[4mSee Also[0m
|
||||
[1m:alt-msg[0m, [1m:append-to[0m, [1m:echo[0m, [1m:echo[0m, [1m:eval[0m, [1m:export-session-to[0m,
|
||||
[1m:alt-msg[0m, [1m:append-to[0m, [1m:cd[0m, [1m:echo[0m, [1m:echo[0m, [1m:eval[0m, [1m:export-session-to[0m,
|
||||
[1m:export-session-to[0m, [1m:pipe-line-to[0m, [1m:pipe-to[0m, [1m:rebuild[0m, [1m:redirect-to[0m,
|
||||
[1m:redirect-to[0m, [1m:write-csv-to[0m, [1m:write-csv-to[0m, [1m:write-json-to[0m,
|
||||
[1m:redirect-to[0m, [1m:sh[0m, [1m:write-csv-to[0m, [1m:write-csv-to[0m, [1m:write-json-to[0m,
|
||||
[1m:write-json-to[0m, [1m:write-jsonlines-to[0m, [1m:write-jsonlines-to[0m,
|
||||
[1m:write-raw-to[0m, [1m:write-raw-to[0m, [1m:write-screen-to[0m, [1m:write-screen-to[0m,
|
||||
[1m:write-table-to[0m, [1m:write-table-to[0m, [1m:write-view-to[0m, [1m:write-view-to[0m,
|
||||
@ -1754,9 +1776,9 @@ For support questions, email:
|
||||
[4m--anonymize[0m Anonymize the lines
|
||||
[4mpath[0m The path to the file to write
|
||||
[4mSee Also[0m
|
||||
[1m:alt-msg[0m, [1m:append-to[0m, [1m:create-logline-table[0m, [1m:create-search-table[0m,
|
||||
[1m:alt-msg[0m, [1m:append-to[0m, [1m:cd[0m, [1m:create-logline-table[0m, [1m:create-search-table[0m,
|
||||
[1m:echo[0m, [1m:echo[0m, [1m:eval[0m, [1m:export-session-to[0m, [1m:export-session-to[0m,
|
||||
[1m:pipe-line-to[0m, [1m:pipe-to[0m, [1m:rebuild[0m, [1m:redirect-to[0m, [1m:redirect-to[0m,
|
||||
[1m:pipe-line-to[0m, [1m:pipe-to[0m, [1m:rebuild[0m, [1m:redirect-to[0m, [1m:redirect-to[0m, [1m:sh[0m,
|
||||
[1m:write-csv-to[0m, [1m:write-csv-to[0m, [1m:write-csv-to[0m, [1m:write-json-to[0m,
|
||||
[1m:write-json-to[0m, [1m:write-json-to[0m, [1m:write-jsonlines-to[0m,
|
||||
[1m:write-jsonlines-to[0m, [1m:write-jsonlines-to[0m, [1m:write-raw-to[0m, [1m:write-raw-to[0m,
|
||||
|
@ -0,0 +1,6 @@
|
||||
[1m[31m✘ error[0m: {test_dir}/logfile_access_log.0 is not a directory
|
||||
[36m --> [0m[1mcommand-option[0m:1
|
||||
[36m | [0m[37m[40m:[0m[1m[36m[40mcd[0m[37m[40m {test_dir}/logfile_access_log.0[0m
|
||||
[36m =[0m [36mhelp[0m: [4m:[0m[1m[4mcd[0m[4m [0m[4mdir[0m
|
||||
══════════════════════════════════════════════════════════════════════
|
||||
Change the current directory
|
@ -5,7 +5,7 @@ export YES_COLOR=1
|
||||
|
||||
run_cap_test ${lnav_test} -n -c 'foo'
|
||||
|
||||
run_cap_test ${lnav_test} -d /tmp/lnav.err -t -n <<EOF
|
||||
run_cap_test ${lnav_test} -d /tmp/lnav.err -n <<EOF
|
||||
Hello, World!
|
||||
Goodbye, World!
|
||||
EOF
|
||||
@ -29,3 +29,12 @@ printf "a\ba _\ba a\b_" | run_cap_test env TEST_COMMENT="overstrike bold" \
|
||||
grep abcd textfile_long_lines.0 | run_cap_test \
|
||||
${lnav_test} -n -d /tmp/lnav.err \
|
||||
-c ';SELECT filepath, lines FROM lnav_file'
|
||||
|
||||
export HOME="./piper-config"
|
||||
rm -rf ./piper-config
|
||||
mkdir -p $HOME/.config
|
||||
|
||||
${lnav_test} -Nn -c ':config /tuning/piper/max-size 128'
|
||||
|
||||
cat ${test_dir}/logfile_haproxy.0 | run_cap_test \
|
||||
env TEST_COMMENT="stdin rotation" ${lnav_test} -n
|
||||
|
@ -2,6 +2,25 @@
|
||||
|
||||
export YES_COLOR=1
|
||||
|
||||
run_cap_test ${lnav_test} -nN \
|
||||
-c ":cd /bad-dir"
|
||||
|
||||
run_cap_test ${lnav_test} -nN \
|
||||
-c ":cd ${test_dir}/logfile_access_log.0"
|
||||
|
||||
run_cap_test ${lnav_test} -nN \
|
||||
-c ":cd ${test_dir}" \
|
||||
-c ":open logfile_access_log.0"
|
||||
|
||||
run_cap_test env SHELL=/bin/bash ${lnav_test} -nN \
|
||||
-e "bad-command"
|
||||
|
||||
run_cap_test ${lnav_test} -nN \
|
||||
-e "echo Hello, World!"
|
||||
|
||||
run_cap_test ${lnav_test} -nN \
|
||||
-e "echo Hello, World! > /dev/stderr"
|
||||
|
||||
run_cap_test ${lnav_test} -n \
|
||||
-c ":switch-to-view help" \
|
||||
${test_dir}/logfile_access_log.0
|
||||
|
@ -37,15 +37,6 @@ run_cap_test ${lnav_test} -n \
|
||||
-c ';SELECT * FROM logline' \
|
||||
${test_dir}/logfile_block.1
|
||||
|
||||
run_test ${lnav_test} -d /tmp/lnav.err -n -w logfile_stdin.0.log \
|
||||
-c ':shexec sleep 1 && touch -t 200711030923 logfile_stdin.0.log' <<EOF
|
||||
2013-06-06T19:13:20.123 Hi
|
||||
EOF
|
||||
|
||||
check_output "piping to stdin is not working?" <<EOF
|
||||
2013-06-06T19:13:20.123 Hi
|
||||
EOF
|
||||
|
||||
if test x"${TSHARK_CMD}" != x""; then
|
||||
run_test env TZ=UTC ${lnav_test} -n ${test_dir}/dhcp.pcapng
|
||||
|
||||
@ -54,12 +45,23 @@ if test x"${TSHARK_CMD}" != x""; then
|
||||
2004-12-05T19:16:24.317 192.168.0.1 → 192.168.0.10 DHCP 342 DHCP Offer - Transaction ID 0x3d1d
|
||||
2004-12-05T19:16:24.387 0.0.0.0 → 255.255.255.255 DHCP 314 DHCP Request - Transaction ID 0x3d1e
|
||||
2004-12-05T19:16:24.387 192.168.0.1 → 192.168.0.10 DHCP 342 DHCP ACK - Transaction ID 0x3d1e
|
||||
EOF
|
||||
|
||||
# make sure piped binary data is left alone
|
||||
run_test cat ${test_dir}/dhcp.pcapng | env TZ=UTC ${lnav_test} -n
|
||||
|
||||
check_output "pcap file is not recognized" <<EOF
|
||||
2004-12-05T19:16:24.317 0.0.0.0 → 255.255.255.255 DHCP 314 DHCP Discover - Transaction ID 0x3d1d
|
||||
2004-12-05T19:16:24.317 192.168.0.1 → 192.168.0.10 DHCP 342 DHCP Offer - Transaction ID 0x3d1d
|
||||
2004-12-05T19:16:24.387 0.0.0.0 → 255.255.255.255 DHCP 314 DHCP Request - Transaction ID 0x3d1e
|
||||
2004-12-05T19:16:24.387 192.168.0.1 → 192.168.0.10 DHCP 342 DHCP ACK - Transaction ID 0x3d1e
|
||||
EOF
|
||||
|
||||
run_test ${lnav_test} -n ${test_dir}/dhcp-trunc.pcapng
|
||||
|
||||
check_error_output "truncated pcap file is not recognized" <<EOF
|
||||
error: unable to open file: {test_dir}/dhcp-trunc.pcapng -- tshark: The file "{test_dir}/dhcp-trunc.pcapng" appears to have been cut short in the middle of a packet.
|
||||
✘ error: unable to open file: {test_dir}/dhcp-trunc.pcapng
|
||||
reason: tshark: The file "{test_dir}/dhcp-trunc.pcapng" appears to have been cut short in the middle of a packet.
|
||||
EOF
|
||||
fi
|
||||
|
||||
@ -588,15 +590,6 @@ info 0x0
|
||||
error 0x0
|
||||
EOF
|
||||
|
||||
run_test ${lnav_test} -d /tmp/lnav.err -nt -w logfile_stdin.log <<EOF
|
||||
Hi
|
||||
EOF
|
||||
|
||||
check_output "piping to stdin is not working?" <<EOF
|
||||
2013-06-06T19:13:20.123 Hi
|
||||
2013-06-06T19:13:20.123 ---- END-OF-STDIN ----
|
||||
EOF
|
||||
|
||||
run_test ${lnav_test} -C ${test_dir}/logfile_bad_access_log.0
|
||||
|
||||
sed -ibak -e "s|/.*/logfile_bad_access_log.0|logfile_bad_access_log.0|g" `test_err_filename`
|
||||
|
Loading…
Reference in New Issue
Block a user