[headless] add options for usage without the UI

This commit is contained in:
Timothy Stack 2014-02-28 20:35:07 -08:00
parent 806b272679
commit 2dd2b02e1b
11 changed files with 570 additions and 276 deletions

3
NEWS
View File

@ -9,6 +9,9 @@ lnav v0.6.3:
hits, and bookmarks.
* The xterm title is update to reflect the file name for the top line
in the view.
* Added a "headless" mode so that you can execute commands and run SQL
queries from the command-line without having to do it from the curses
UI.
* When doing a search or SQL query, any text that is currently being
displayed can be tab-completed.
* The '-H' option was added so you can view the internal help text.

View File

@ -49,4 +49,5 @@
* @param sa The container for any style attributes.
*/
void scrub_ansi_string(std::string &str, string_attrs_t &sa);
#endif

View File

@ -114,7 +114,7 @@ public:
this->reset();
};
/** @return The file descriptor as a pain integer. */
/** @return The file descriptor as a plain integer. */
operator int(void) const { return this->af_fd; };
/**

View File

@ -43,6 +43,44 @@ following options are available when doing so:
on the standard input.
-w file Write the contents of the standard input to this file.
To automatically execute queries or lnav commands after the files
have been loaded, you can use the following options:
-c cmd A command, query, or file to execute. The first character
determines the type of operation: a colon is used for the
built-in commands; a semi-colon for SQL queries; and a
pipe symbol (|) for executing a file containing other
commands. For example, to open the file "foo.log" and go
to the tenth line in the file, you can do:
lnav -c ':goto 10' foo.log
This option can be given multiple times to execute multiple
operations in sequence.
-f file A file that contains commands, queries, or files to execute.
This option is a shortcut for "-c '|file'".
To execute commands/queries without the opening the interactive text UI,
you can pass the '-n' option. This combination of options allows you to
write scripts for processing logs with lnav. For example, to get a list
of IP addresses that dhclient has bound to in CSV format:
#! /usr/bin/lnav -nf
# Usage: dhcp_ip.lnav /var/log/messages
# Only include lines that look like:
# Apr 29 00:31:56 example-centos5 dhclient: bound to 10.1.10.103 -- renewal in 9938 seconds.
:filter-in dhclient: bound to
# The log message parser will extract the IP address as col_0, so we
# select that and alias it to "dhcp_ip".
;select distinct col_0 as dhcp_ip from logline;
# Finally, write the results of the query to stdout.
:write-csv-to -
DISPLAY
-------
@ -302,8 +340,11 @@ COMMANDS
current-time Print the current time in human-readable form and
as a unix-timestamp.
goto <line#|N%> Go to the given line number or N percent into the
file.
goto <line#|N%|time>
Go to the given line number, N percent into the
file, or the given timestamp in the log view. If the
line number is negative, it is considered an offset
from the last line.
highlight <regex> Highlight strings that match the given regular
expression.
@ -356,6 +397,9 @@ COMMANDS
write-to <file> Write any marked lines to the given file.
write-csv-to <file>
Write the results of a SQL query to a CSV-formatted file.
session <cmd> Add the given command to the session file
(~/.lnav/session). Any commands listed in the session file
are executed on startup. Only the highlight, word-wrap, and

View File

@ -407,14 +407,20 @@ public:
{
unsigned long height;
getmaxyx(this->lv_window, height, width_out);
if (this->lv_height < 1) {
height_out = vis_line_t(height) +
this->lv_height -
vis_line_t(this->lv_y);
if (this->lv_window == NULL) {
height_out = vis_line_t(1);
width_out = 0;
}
else {
height_out = this->lv_height;
getmaxyx(this->lv_window, height, width_out);
if (this->lv_height < 1) {
height_out = vis_line_t(height) +
this->lv_height -
vis_line_t(this->lv_y);
}
else {
height_out = this->lv_height;
}
}
};

View File

@ -359,7 +359,9 @@ static int handle_collation_list(void *ptr,
char **colvalues,
char **colnames)
{
lnav_data.ld_rl_view->add_possibility(LNM_SQL, "*", colvalues[1]);
if (lnav_data.ld_rl_view != NULL) {
lnav_data.ld_rl_view->add_possibility(LNM_SQL, "*", colvalues[1]);
}
return 0;
}
@ -369,7 +371,9 @@ static int handle_db_list(void *ptr,
char **colvalues,
char **colnames)
{
lnav_data.ld_rl_view->add_possibility(LNM_SQL, "*", colvalues[1]);
if (lnav_data.ld_rl_view != NULL) {
lnav_data.ld_rl_view->add_possibility(LNM_SQL, "*", colvalues[1]);
}
return 0;
}
@ -379,7 +383,9 @@ static int handle_table_list(void *ptr,
char **colvalues,
char **colnames)
{
lnav_data.ld_rl_view->add_possibility(LNM_SQL, "*", colvalues[0]);
if (lnav_data.ld_rl_view != NULL) {
lnav_data.ld_rl_view->add_possibility(LNM_SQL, "*", colvalues[0]);
}
return 0;
}
@ -389,7 +395,9 @@ static int handle_table_info(void *ptr,
char **colvalues,
char **colnames)
{
lnav_data.ld_rl_view->add_possibility(LNM_SQL, "*", colvalues[1]);
if (lnav_data.ld_rl_view != NULL) {
lnav_data.ld_rl_view->add_possibility(LNM_SQL, "*", colvalues[1]);
}
if (strcmp(colvalues[5], "1") == 0) {
lnav_data.ld_db_key_names.push_back(colvalues[1]);
}
@ -519,29 +527,33 @@ bool setup_logline_table()
}
lnav_data.ld_db_key_names.clear();
lnav_data.ld_rl_view->clear_possibilities(LNM_SQL, "*");
add_view_text_possibilities(LNM_SQL, &log_view);
if (lnav_data.ld_rl_view != NULL) {
lnav_data.ld_rl_view->clear_possibilities(LNM_SQL, "*");
lnav_data.ld_rl_view->add_possibility(LNM_SQL, "*", sql_keywords);
lnav_data.ld_rl_view->add_possibility(LNM_SQL, "*", sql_function_names);
lnav_data.ld_rl_view->add_possibility(LNM_SQL, "*", hidden_table_columns);
lnav_data.ld_rl_view->add_possibility(LNM_SQL, "*", commands);
add_view_text_possibilities(LNM_SQL, &log_view);
for (int lpc = 0; sqlite_registration_funcs[lpc]; lpc++) {
const struct FuncDef *basic_funcs;
const struct FuncDefAgg *agg_funcs;
lnav_data.ld_rl_view->add_possibility(LNM_SQL, "*", sql_keywords);
lnav_data.ld_rl_view->add_possibility(LNM_SQL, "*", sql_function_names);
lnav_data.ld_rl_view->add_possibility(LNM_SQL, "*",
hidden_table_columns);
lnav_data.ld_rl_view->add_possibility(LNM_SQL, "*", commands);
sqlite_registration_funcs[lpc](&basic_funcs, &agg_funcs);
for (int lpc2 = 0; basic_funcs && basic_funcs[lpc2].zName; lpc2++) {
lnav_data.ld_rl_view->add_possibility(LNM_SQL,
"*",
basic_funcs[lpc2].zName);
}
for (int lpc2 = 0; agg_funcs && agg_funcs[lpc2].zName; lpc2++) {
lnav_data.ld_rl_view->add_possibility(LNM_SQL,
"*",
agg_funcs[lpc2].zName);
for (int lpc = 0; sqlite_registration_funcs[lpc]; lpc++) {
const struct FuncDef *basic_funcs;
const struct FuncDefAgg *agg_funcs;
sqlite_registration_funcs[lpc](&basic_funcs, &agg_funcs);
for (int lpc2 = 0; basic_funcs && basic_funcs[lpc2].zName; lpc2++) {
lnav_data.ld_rl_view->add_possibility(LNM_SQL,
"*",
basic_funcs[lpc2].zName);
}
for (int lpc2 = 0; agg_funcs && agg_funcs[lpc2].zName; lpc2++) {
lnav_data.ld_rl_view->add_possibility(LNM_SQL,
"*",
agg_funcs[lpc2].zName);
}
}
}
@ -577,6 +589,10 @@ public:
{
static sig_atomic_t index_counter = 0;
if (lnav_data.ld_flags & LNF_HEADLESS) {
return;
}
/* XXX assert(off <= total); */
if (off > (off_t)total) {
off = total;
@ -600,6 +616,10 @@ public:
{
static sig_atomic_t filter_counter = 0;
if (lnav_data.ld_flags & LNF_HEADLESS) {
return;
}
if ((size_t)cl == (total - 1) ||
ui_periodic_timer::singleton().time_to_update(filter_counter)) {
lnav_data.ld_bottom_source.update_loading(cl, (total - 1));
@ -668,7 +688,6 @@ void rebuild_indexes(bool force)
size_t old_count;
time_t old_time;
old_count = lss.text_line_count();
if (old_count) {
@ -733,6 +752,9 @@ void rebuild_indexes(bool force)
new_data = false;
}
if (front_top < 0) {
front_top += text_view.get_inner_height();
}
if (front_top < text_view.get_inner_height()) {
text_view.set_top(vis_line_t(front_top));
scroll_down = false;
@ -1990,33 +2012,250 @@ string execute_command(string cmdline)
return msg;
}
static void execute_file(string path)
static void open_schema_view(void)
{
ifstream cmd_file(path.c_str());
textview_curses *schema_tc = &lnav_data.ld_views[LNV_SCHEMA];
string schema;
if (cmd_file.is_open()) {
int line_number = 0;
string line;
dump_sqlite_schema(lnav_data.ld_db, schema);
while (getline(cmd_file, line)) {
line_number += 1;
schema += "\n\n-- Virtual Table Definitions --\n\n";
for (log_vtab_manager::iterator vtab_iter =
lnav_data.ld_vtab_manager->begin();
vtab_iter != lnav_data.ld_vtab_manager->end();
++vtab_iter) {
schema += vtab_iter->second->get_table_statement();
}
if (line.empty()) {
continue;
if (schema_tc->get_sub_source() != NULL) {
delete schema_tc->get_sub_source();
}
schema_tc->set_sub_source(new plain_text_source(schema));
ensure_view(schema_tc);
}
string execute_sql(string sql, string &alt_msg)
{
db_label_source & dls = lnav_data.ld_db_rows;
hist_source & hs = lnav_data.ld_db_source;
auto_mem<sqlite3_stmt> stmt(sqlite3_finalize);
string stmt_str = trim(sql);
string retval;
int retcode;
lnav_data.ld_bottom_source.grep_error("");
if (stmt_str == ".schema") {
alt_msg = "";
open_schema_view();
lnav_data.ld_mode = LNM_PAGING;
return "";
}
hs.clear();
hs.get_displayed_buckets().clear();
dls.clear();
dls.dls_stmt_str = stmt_str;
retcode = sqlite3_prepare_v2(lnav_data.ld_db.in(),
stmt_str.c_str(),
-1,
stmt.out(),
NULL);
if (retcode != SQLITE_OK) {
const char *errmsg = sqlite3_errmsg(lnav_data.ld_db);
retval = errmsg;
alt_msg = "";
}
else if (stmt == NULL) {
retval = "";
alt_msg = "";
}
else {
bool done = false;
lnav_data.ld_log_source.text_clear_marks(&BM_QUERY);
while (!done) {
retcode = sqlite3_step(stmt.in());
switch (retcode) {
case SQLITE_OK:
case SQLITE_DONE:
done = true;
break;
case SQLITE_ROW:
sql_callback(stmt.in());
break;
default: {
const char *errmsg;
fprintf(stderr, "code %d\n", retcode);
errmsg = sqlite3_errmsg(lnav_data.ld_db);
retval = errmsg;
done = true;
}
if (line[0] == '#') {
continue;
break;
}
string rc = execute_command(line);
fprintf(stderr,
"%s:%d:execute result -- %s\n",
path.c_str(),
line_number,
rc.c_str());
}
}
if (retcode == SQLITE_DONE) {
lnav_data.ld_views[LNV_LOG].reload_data();
lnav_data.ld_views[LNV_DB].reload_data();
lnav_data.ld_views[LNV_DB].set_left(0);
if (dls.dls_rows.size() > 0) {
vis_bookmarks &bm =
lnav_data.ld_views[LNV_LOG].get_bookmarks();
if (dls.dls_headers.size() == 1 && !bm[&BM_QUERY].empty()) {
retval = "";
alt_msg = HELP_MSG_2(
y, Y,
"to move forward/backward through query results "
"in the log view");
}
else if (dls.dls_rows.size() == 1) {
string row;
hs.text_value_for_line(lnav_data.ld_views[LNV_DB], 1, row, true);
retval = "SQL Result: " + row;
}
else {
char row_count[32];
ensure_view(&lnav_data.ld_views[LNV_DB]);
snprintf(row_count, sizeof(row_count),
ANSI_BOLD("%'d") " row(s) matched",
(int)dls.dls_rows.size());
retval = row_count;
alt_msg = HELP_MSG_2(
y, Y,
"to move forward/backward through query results "
"in the log view");
}
}
else {
retval = "No rows matched";
alt_msg = "";
}
}
if (!(lnav_data.ld_flags & LNF_HEADLESS)) {
lnav_data.ld_bottom_source.update_loading(0, 0);
lnav_data.ld_status[LNS_BOTTOM].do_update();
{
field_overlay_source *fos;
fos = (field_overlay_source *)lnav_data.ld_views[LNV_LOG].
get_overlay_source();
fos->fos_active = fos->fos_active_prev;
redo_search(LNV_DB);
}
}
lnav_data.ld_views[LNV_LOG].reload_data();
return retval;
}
static void execute_file(string path)
{
FILE *file;
if (path == "-") {
file = stdin;
}
else if ((file = fopen(path.c_str(), "r")) == NULL) {
return;
}
int line_number = 0;
char *line = NULL;
size_t line_max_size;
ssize_t line_size;
while ((line_size = getline(&line, &line_max_size, file)) != -1) {
line_number += 1;
if (trim(line).empty()) {
continue;
}
if (line[0] == '#') {
continue;
}
string rc, alt_msg;
if (line[line_size - 1] == '\n') {
line[line_size - 1] = '\0';
}
fprintf(stderr, "line %s--\n", line);
switch (line[0]) {
case ':':
rc = execute_command(&line[1]);
break;
case '/':
case ';':
setup_logline_table();
rc = execute_sql(&line[1], alt_msg);
break;
case '|':
execute_file(&line[1]);
break;
default:
rc = execute_command(line);
break;
}
fprintf(stderr,
"%s:%d:execute result -- %s\n",
path.c_str(),
line_number,
rc.c_str());
}
if (file != stdin) {
fclose(file);
}
}
void execute_init_commands(readline_curses *rc)
{
if (lnav_data.ld_commands.empty()) {
return;
}
for (std::list<string>::iterator iter = lnav_data.ld_commands.begin();
iter != lnav_data.ld_commands.end();
++iter) {
string msg, alt_msg;
switch (iter->at(0)) {
case ':':
msg = execute_command(iter->substr(1));
break;
case '/':
case ';':
setup_logline_table();
msg = execute_sql(iter->substr(1), alt_msg);
break;
case '|':
execute_file(iter->substr(1));
break;
}
if (rc != NULL) {
rc->set_value(msg);
rc->set_alt_value(alt_msg);
}
}
lnav_data.ld_commands.clear();
}
int sql_callback(sqlite3_stmt *stmt)
@ -2076,29 +2315,6 @@ int sql_callback(sqlite3_stmt *stmt)
return retval;
}
static void open_schema_view(void)
{
textview_curses *schema_tc = &lnav_data.ld_views[LNV_SCHEMA];
string schema;
dump_sqlite_schema(lnav_data.ld_db, schema);
schema += "\n\n-- Virtual Table Definitions --\n\n";
for (log_vtab_manager::iterator vtab_iter =
lnav_data.ld_vtab_manager->begin();
vtab_iter != lnav_data.ld_vtab_manager->end();
++vtab_iter) {
schema += vtab_iter->second->get_table_statement();
}
if (schema_tc->get_sub_source() != NULL) {
delete schema_tc->get_sub_source();
}
schema_tc->set_sub_source(new plain_text_source(schema));
ensure_view(schema_tc);
}
void execute_search(lnav_view_t view, const std::string &regex)
{
auto_ptr<grep_highlighter> &gc = lnav_data.ld_search_child[view];
@ -2263,6 +2479,7 @@ static void rl_abort(void *dummy, readline_curses *rc)
static void rl_callback(void *dummy, readline_curses *rc)
{
lnav_data.ld_bottom_source.set_prompt("");
string alt_msg;
switch (lnav_data.ld_mode) {
case LNM_PAGING:
@ -2291,131 +2508,8 @@ static void rl_callback(void *dummy, readline_curses *rc)
break;
case LNM_SQL:
{
db_label_source & dls = lnav_data.ld_db_rows;
hist_source & hs = lnav_data.ld_db_source;
auto_mem<sqlite3_stmt> stmt(sqlite3_finalize);
string stmt_str = trim(rc->get_value());
int retcode;
lnav_data.ld_bottom_source.grep_error("");
if (stmt_str == ".schema") {
rc->set_value("");
rc->set_alt_value("");
open_schema_view();
lnav_data.ld_mode = LNM_PAGING;
return;
}
hs.clear();
hs.get_displayed_buckets().clear();
dls.clear();
dls.dls_stmt_str = rc->get_value();
retcode = sqlite3_prepare_v2(lnav_data.ld_db.in(),
rc->get_value().c_str(),
-1,
stmt.out(),
NULL);
if (retcode != SQLITE_OK) {
const char *errmsg = sqlite3_errmsg(lnav_data.ld_db);
rc->set_value(errmsg);
rc->set_alt_value("");
}
else if (stmt == NULL) {
rc->set_value("");
rc->set_alt_value("");
}
else {
bool done = false;
lnav_data.ld_log_source.text_clear_marks(&BM_QUERY);
while (!done) {
retcode = sqlite3_step(stmt.in());
switch (retcode) {
case SQLITE_OK:
case SQLITE_DONE:
done = true;
break;
case SQLITE_ROW:
sql_callback(stmt.in());
break;
default:
{
const char *errmsg;
fprintf(stderr, "code %d\n", retcode);
errmsg = sqlite3_errmsg(lnav_data.ld_db);
rc->set_value(errmsg);
done = true;
}
break;
}
}
if (retcode == SQLITE_DONE) {
lnav_data.ld_views[LNV_LOG].reload_data();
lnav_data.ld_views[LNV_DB].reload_data();
lnav_data.ld_views[LNV_DB].set_left(0);
if (dls.dls_rows.size() > 0) {
vis_bookmarks &bm =
lnav_data.ld_views[LNV_LOG].get_bookmarks();
if (dls.dls_headers.size() == 1 && !bm[&BM_QUERY].empty()) {
rc->set_value("");
rc->set_alt_value(HELP_MSG_2(
y, Y,
"to move forward/backward through query results "
"in the log view"));
}
else if (dls.dls_rows.size() == 1) {
string row;
hs.text_value_for_line(lnav_data.ld_views[LNV_DB], 1, row, true);
rc->set_value("SQL Result: " + row);
}
else {
char row_count[32];
ensure_view(&lnav_data.ld_views[LNV_DB]);
snprintf(row_count, sizeof(row_count),
ANSI_BOLD("%'d") " row(s) matched",
(int)dls.dls_rows.size());
rc->set_value(row_count);
rc->set_alt_value(HELP_MSG_2(
y, Y,
"to move forward/backward through query results "
"in the log view"));
}
}
else {
rc->set_value("No rows matched");
rc->set_alt_value("");
}
}
lnav_data.ld_bottom_source.update_loading(0, 0);
lnav_data.ld_status[LNS_BOTTOM].do_update();
}
field_overlay_source *fos;
fos =
(field_overlay_source *)lnav_data.ld_views[LNV_LOG].
get_overlay_source();
fos->fos_active = fos->fos_active_prev;
redo_search(LNV_DB);
lnav_data.ld_views[LNV_LOG].reload_data();
}
rc->set_value(execute_sql(rc->get_value(), alt_msg));
rc->set_alt_value(alt_msg);
lnav_data.ld_mode = LNM_PAGING;
break;
}
@ -2439,6 +2533,10 @@ static void usage(void)
" -C Check configuration and then exit.\n"
" -d file Write debug messages to the given file.\n"
" -V Print version information.\n"
"\n"
" -c cmd Execute a command after the files have been loaded.\n"
" -f path Execute the commands in the given file.\n"
" -n Run without the curses UI.\n"
" -s Load the most recent syslog messages file.\n"
" -a Load all of the most recent log file types.\n"
" -r Load older rotated log files as well.\n"
@ -2991,14 +3089,6 @@ void update_hits(void *dummy, textview_curses *tc)
static void looper(void)
{
int fd;
fd =
open(lnav_data.ld_debug_log_name, O_WRONLY | O_CREAT | O_APPEND, 0666);
dup2(fd, STDERR_FILENO);
close(fd);
fprintf(stderr, "startup\n");
try {
readline_context command_context("cmd", &lnav_commands);
@ -3091,36 +3181,6 @@ static void looper(void)
sb.push_back(&lnav_data.ld_bottom_source.marks_wire);
sb.push_back(&lnav_data.ld_term_extra.filename_wire);
{
hist_source &hs = lnav_data.ld_hist_source;
lnav_data.ld_hist_zoom = 2;
hs.set_role_for_type(bucket_type_t(logline::LEVEL_FATAL),
view_colors::VCR_ERROR);
hs.set_role_for_type(bucket_type_t(logline::LEVEL_CRITICAL),
view_colors::VCR_ERROR);
hs.set_role_for_type(bucket_type_t(logline::LEVEL_ERROR),
view_colors::VCR_ERROR);
hs.set_role_for_type(bucket_type_t(logline::LEVEL_WARNING),
view_colors::VCR_WARNING);
hs.set_label_source(new time_label_source());
}
{
hist_source &hs = lnav_data.ld_graph_source;
hs.set_bucket_size(1);
hs.set_group_size(100);
}
{
hist_source &hs = lnav_data.ld_db_source;
hs.set_bucket_size(1);
hs.set_group_size(10);
hs.set_label_source(&lnav_data.ld_db_rows);
}
FD_ZERO(&lnav_data.ld_read_fds);
FD_SET(STDIN_FILENO, &lnav_data.ld_read_fds);
lnav_data.ld_max_fd =
@ -3232,6 +3292,8 @@ static void looper(void)
rebuild_indexes(true);
session_loaded = true;
}
execute_init_commands(lnav_data.ld_rl_view);
}
else {
if (FD_ISSET(STDIN_FILENO, &ready_rfds)) {
@ -3783,6 +3845,36 @@ int main(int argc, char *argv[])
setup_highlights(lnav_data.ld_views[LNV_TEXT].get_highlights());
}
{
hist_source &hs = lnav_data.ld_hist_source;
lnav_data.ld_hist_zoom = 2;
hs.set_role_for_type(bucket_type_t(logline::LEVEL_FATAL),
view_colors::VCR_ERROR);
hs.set_role_for_type(bucket_type_t(logline::LEVEL_CRITICAL),
view_colors::VCR_ERROR);
hs.set_role_for_type(bucket_type_t(logline::LEVEL_ERROR),
view_colors::VCR_ERROR);
hs.set_role_for_type(bucket_type_t(logline::LEVEL_WARNING),
view_colors::VCR_WARNING);
hs.set_label_source(new time_label_source());
}
{
hist_source &hs = lnav_data.ld_graph_source;
hs.set_bucket_size(1);
hs.set_group_size(100);
}
{
hist_source &hs = lnav_data.ld_db_source;
hs.set_bucket_size(1);
hs.set_group_size(10);
hs.set_label_source(&lnav_data.ld_db_rows);
}
for (int lpc = 0; lpc < LNV__MAX; lpc++) {
lnav_data.ld_views[lpc].set_title(view_titles[lpc]);
}
@ -3790,7 +3882,7 @@ int main(int argc, char *argv[])
lnav_data.ld_looping = true;
lnav_data.ld_mode = LNM_PAGING;
lnav_data.ld_debug_log_name = "/dev/null";
while ((c = getopt(argc, argv, "hHarsCd:tw:V")) != -1) {
while ((c = getopt(argc, argv, "hHarsCc:f:d:ntw:V")) != -1) {
switch (c) {
case 'h':
usage();
@ -3802,7 +3894,34 @@ int main(int argc, char *argv[])
break;
case 'C':
exit(0);
exit(EXIT_SUCCESS);
break;
case 'c':
switch (optarg[0]) {
case ':':
case '/':
case ';':
case '|':
break;
default:
fprintf(stderr, "error: command arguments should start with a "
"colon, semi-colon, or pipe-symbol to denote:\n");
fprintf(stderr, "error: a built-in command, SQL query, "
"or a file path that contains commands to execute\n");
usage();
exit(EXIT_FAILURE);
break;
}
lnav_data.ld_commands.push_back(optarg);
break;
case 'f':
if (access(optarg, R_OK) != 0) {
perror("invalid command file");
exit(EXIT_FAILURE);
}
lnav_data.ld_commands.push_back("|" + string(optarg));
break;
case 'd':
@ -3813,6 +3932,10 @@ int main(int argc, char *argv[])
lnav_data.ld_flags |= LNF__ALL;
break;
case 'n':
lnav_data.ld_flags |= LNF_HEADLESS;
break;
case 'r':
lnav_data.ld_flags |= LNF_ROTATED;
break;
@ -3901,7 +4024,7 @@ int main(int argc, char *argv[])
}
}
if (!isatty(STDOUT_FILENO)) {
if (!(lnav_data.ld_flags & LNF_HEADLESS) && !isatty(STDOUT_FILENO)) {
fprintf(stderr, "error: stdout is not a tty.\n");
retval = EXIT_FAILURE;
}
@ -3928,16 +4051,70 @@ int main(int argc, char *argv[])
}
else {
try {
int fd;
rescan_files(true);
init_session();
fd = open(lnav_data.ld_debug_log_name,
O_WRONLY | O_CREAT | O_APPEND, 0666);
dup2(fd, STDERR_FILENO);
close(fd);
fprintf(stderr, "startup\n");
scan_sessions();
if (lnav_data.ld_flags & LNF_HEADLESS) {
textview_curses *tc;
attr_line_t al;
const std::string &line = al.get_string();
guard_termios gt(STDIN_FILENO);
looper();
alerter::singleton().enabled(false);
save_session();
rebuild_indexes(true);
lnav_data.ld_view_stack.push(&lnav_data.ld_views[LNV_LOG]);
lnav_data.ld_views[LNV_LOG].set_top(vis_line_t(0));
execute_init_commands(NULL);
if (!lnav_data.ld_view_stack.empty() &&
!lnav_data.ld_stdout_used) {
bool suppress_empty_lines = false;
list_overlay_source *los;
vis_line_t y;
tc = lnav_data.ld_view_stack.top();
if (tc == &lnav_data.ld_views[LNV_DB]) {
suppress_empty_lines = true;
}
los = tc->get_overlay_source();
for (vis_line_t vl = tc->get_top();
vl < tc->get_inner_height();
++vl, ++y) {
while (los->list_value_for_overlay(*tc, y, al)) {
printf("%s\n", line.c_str());
++y;
}
tc->listview_value_for_row(*tc, vl, al);
if (suppress_empty_lines && line.empty()) {
continue;
}
printf("%s\n", line.c_str());
}
}
}
else {
init_session();
scan_sessions();
guard_termios gt(STDIN_FILENO);
looper();
save_session();
}
}
catch (line_buffer::error & e) {
fprintf(stderr, "error: %s\n", strerror(e.e_err));

View File

@ -75,6 +75,7 @@ enum {
LNB_TIMESTAMP,
LNB_HELP,
LNB_HEADLESS,
LNB_ROTATED,
};
@ -86,6 +87,7 @@ typedef enum {
LNF_TIMESTAMP = (1L << LNB_TIMESTAMP),
LNF_HELP = (1L << LNB_HELP),
LNF_HEADLESS = (1L << LNB_HEADLESS),
LNF__ALL = (LNF_SYSLOG|LNF_HELP)
} lnav_flags_t;
@ -136,10 +138,12 @@ struct _lnav_data {
const char * ld_program_name;
const char * ld_debug_log_name;
std::list<std::string> ld_commands;
std::set<std::pair<std::string, int> > ld_file_names;
std::list<logfile *> ld_files;
std::list<std::string> ld_other_files;
std::list<std::pair<std::string, int> > ld_files_to_front;
bool ld_stdout_used;
sig_atomic_t ld_looping;
sig_atomic_t ld_winched;
sig_atomic_t ld_child_terminated;

View File

@ -171,15 +171,31 @@ static string com_current_time(string cmdline, vector<string> &args)
static string com_goto(string cmdline, vector<string> &args)
{
string retval = "error: expecting line number/percentage";
string retval = "error: expecting line number/percentage or timestamp";
if (args.size() == 0) { }
if (args.size() == 0) {
args.push_back("line-time");
}
else if (args.size() > 1) {
textview_curses *tc = lnav_data.ld_view_stack.top();
int line_number, consumed;
date_time_scanner dts;
struct timeval tv;
struct tm tm;
float value;
if (sscanf(args[1].c_str(), "%f%n", &value, &consumed) == 1) {
if (dts.scan(args[1].c_str(), NULL, &tm, tv) != NULL) {
if (tc == &lnav_data.ld_views[LNV_LOG]) {
vis_line_t vl;
vl = lnav_data.ld_log_source.find_from_time(tv);
tc->set_top(vl);
retval = "";
} else {
retval = "error: time values only work in the log view";
}
}
else if (sscanf(args[1].c_str(), "%f%n", &value, &consumed) == 1) {
if (args[1][consumed] == '%') {
line_number = (int)
((double)tc->get_inner_height() *
@ -187,6 +203,9 @@ static string com_goto(string cmdline, vector<string> &args)
}
else {
line_number = (int)value;
if (line_number < 0) {
line_number = tc->get_inner_height() + line_number;
}
}
tc->set_top(vis_line_t(line_number));
@ -292,7 +311,14 @@ static string com_save_to(string cmdline, vector<string> &args)
}
}
if ((outfile = fopen(wordmem->we_wordv[0], mode)) == NULL) {
if (strcmp(wordmem->we_wordv[0], "-") == 0) {
if (!(lnav_data.ld_flags & LNF_HEADLESS)) {
return "error: writing to stdout is only available in headless mode";
}
outfile = stdout;
lnav_data.ld_stdout_used = true;
}
else if ((outfile = fopen(wordmem->we_wordv[0], mode)) == NULL) {
return "error: unable to open file -- " + string(wordmem->we_wordv[0]);
}
@ -338,7 +364,9 @@ static string com_save_to(string cmdline, vector<string> &args)
}
}
fclose(outfile);
if (outfile != stdout) {
fclose(outfile);
}
outfile = NULL;
return "";
@ -500,8 +528,10 @@ static string com_filter(string cmdline, vector<string> &args)
auto_ptr<pcre_filter> pf(new pcre_filter(lt, args[1], code));
lss.add_filter(pf.release());
lnav_data.ld_rl_view->
add_possibility(LNM_COMMAND, "enabled-filter", args[1]);
if (lnav_data.ld_rl_view != NULL) {
lnav_data.ld_rl_view->add_possibility(
LNM_COMMAND, "enabled-filter", args[1]);
}
rebuild_indexes(true);
retval = "info: filter now active";
@ -531,10 +561,12 @@ static string com_enable_filter(string cmdline, vector<string> &args)
}
else {
lnav_data.ld_log_source.set_filter_enabled(lf, true);
lnav_data.ld_rl_view->
rem_possibility(LNM_COMMAND, "disabled-filter", args[1]);
lnav_data.ld_rl_view->
add_possibility(LNM_COMMAND, "enabled-filter", args[1]);
if (lnav_data.ld_rl_view != NULL) {
lnav_data.ld_rl_view->rem_possibility(
LNM_COMMAND, "disabled-filter", args[1]);
lnav_data.ld_rl_view->add_possibility(
LNM_COMMAND, "enabled-filter", args[1]);
}
rebuild_indexes(true);
retval = "info: filter enabled";
}
@ -563,10 +595,12 @@ static string com_disable_filter(string cmdline, vector<string> &args)
}
else {
lnav_data.ld_log_source.set_filter_enabled(lf, false);
lnav_data.ld_rl_view->
rem_possibility(LNM_COMMAND, "disabled-filter", args[1]);
lnav_data.ld_rl_view->
add_possibility(LNM_COMMAND, "enabled-filter", args[1]);
if (lnav_data.ld_rl_view != NULL) {
lnav_data.ld_rl_view->rem_possibility(
LNM_COMMAND, "disabled-filter", args[1]);
lnav_data.ld_rl_view->add_possibility(
LNM_COMMAND, "enabled-filter", args[1]);
}
rebuild_indexes(true);
retval = "info: filter disabled";
}
@ -630,9 +664,11 @@ static string com_create_logline_table(string cmdline, vector<string> &args)
errmsg = lnav_data.ld_vtab_manager->register_vtab(ldt);
if (errmsg.empty()) {
lnav_data.ld_rl_view->add_possibility(LNM_COMMAND,
"custom-table",
args[1]);
if (lnav_data.ld_rl_view != NULL) {
lnav_data.ld_rl_view->add_possibility(LNM_COMMAND,
"custom-table",
args[1]);
}
retval = "info: created new log table -- " + args[1];
}
else {
@ -655,9 +691,11 @@ static string com_delete_logline_table(string cmdline, vector<string> &args)
string rc = lnav_data.ld_vtab_manager->unregister_vtab(args[1]);
if (rc.empty()) {
lnav_data.ld_rl_view->rem_possibility(LNM_COMMAND,
"custom-table",
args[1]);
if (lnav_data.ld_rl_view != NULL) {
lnav_data.ld_rl_view->rem_possibility(LNM_COMMAND,
"custom-table",
args[1]);
}
retval = "info: deleted logline table";
}
else {
@ -755,8 +793,7 @@ static string com_open(string cmdline, vector<string> &args)
if (access(fn.c_str(), R_OK) != 0 &&
(colon_index = fn.rfind(':')) != string::npos) {
if (sscanf(&fn.c_str()[colon_index + 1], "%d", &top) == 1 &&
top >= 0) {
if (sscanf(&fn.c_str()[colon_index + 1], "%d", &top) == 1) {
fn = fn.substr(0, colon_index);
}
}
@ -798,8 +835,10 @@ static string com_open(string cmdline, vector<string> &args)
retval = "info: opened -- " + fn;
lnav_data.ld_files_to_front.push_back(make_pair(fn, top));
lnav_data.ld_rl_view->set_alt_value(HELP_MSG_1(
X, "to close the file"));
if (lnav_data.ld_rl_view != NULL) {
lnav_data.ld_rl_view->set_alt_value(HELP_MSG_1(
X, "to close the file"));
}
}
}
}

View File

@ -149,7 +149,7 @@ logfile *logfile_sub_source::find(const char *fn,
return retval;
}
vis_line_t logfile_sub_source::find_from_time(time_t start)
vis_line_t logfile_sub_source::find_from_time(const struct timeval &start)
{
vector<content_line_t>::iterator lb;
vis_line_t retval(-1);

View File

@ -303,7 +303,13 @@ public:
return retval;
};
vis_line_t find_from_time(time_t start);
vis_line_t find_from_time(const struct timeval &start);
vis_line_t find_from_time(time_t start) {
struct timeval tv = { start, 0 };
return this->find_from_time(tv);
};
content_line_t at(vis_line_t vl) { return this->lss_index[vl]; };
@ -399,7 +405,13 @@ private:
{
logline *ll_lhs = this->llss_controller.find_line(lhs);
return ll_lhs->get_time() < rhs;
return *ll_lhs < rhs;
};
bool operator()(const content_line_t &lhs, const struct timeval &rhs)
{
logline *ll_lhs = this->llss_controller.find_line(lhs);
return *ll_lhs < rhs;
};
logfile_sub_source & llss_controller;

View File

@ -115,9 +115,16 @@ class alerter {
public:
static alerter &singleton();
void enabled(bool enable) { this->a_enabled = enable; };
void chime(void) {
if (this->a_do_flash)
if (!this->a_enabled) {
return;
}
if (this->a_do_flash) {
::flash();
}
this->a_do_flash = false;
};
@ -129,8 +136,9 @@ public:
};
private:
alerter() : a_do_flash(true), a_last_input(-1) { };
alerter() : a_enabled(false), a_do_flash(true), a_last_input(-1) { };
bool a_enabled;
bool a_do_flash;
int a_last_input;
};