lnav/src/grep_proc.cc

427 lines
13 KiB
C++
Raw Normal View History

2009-09-14 03:07:32 +02:00
/**
2013-05-03 08:02:03 +02:00
* Copyright (c) 2007-2012, Timothy Stack
*
* All rights reserved.
2013-05-28 06:35:00 +02:00
*
2013-05-03 08:02:03 +02:00
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
2013-05-28 06:35:00 +02:00
*
2013-05-03 08:02:03 +02:00
* * 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.
2013-05-28 06:35:00 +02:00
*
2013-05-03 08:02:03 +02:00
* 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;
2022-03-16 23:38:08 +01:00
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2013-05-03 08:02:03 +02:00
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
2009-09-14 03:07:32 +02:00
* @file grep_proc.cc
*/
2022-03-16 23:38:08 +01:00
#include "grep_proc.hh"
2009-09-14 03:07:32 +02:00
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
2022-03-16 23:38:08 +01:00
#include <stdio.h>
#include <string.h>
2009-09-14 03:07:32 +02:00
#include <sys/wait.h>
2022-03-16 23:38:08 +01:00
#include <unistd.h>
2009-09-14 03:07:32 +02:00
2022-07-08 23:17:34 +02:00
#include "base/auto_pid.hh"
2019-05-08 14:30:59 +02:00
#include "base/lnav_log.hh"
2022-03-16 23:38:08 +01:00
#include "base/opt_util.hh"
#include "base/string_util.hh"
2022-03-16 23:38:08 +01:00
#include "config.h"
#include "lnav_util.hh"
#include "vis_line.hh"
2009-09-14 03:07:32 +02:00
template<typename LineType>
grep_proc<LineType>::grep_proc(std::shared_ptr<lnav::pcre2pp::code> code,
grep_proc_source<LineType>& gps,
std::shared_ptr<pollable_supervisor> ps)
: pollable(ps, pollable::category::background), gp_pcre(code),
gp_source(gps)
2009-09-14 03:07:32 +02:00
{
require(this->invariant());
gps.register_proc(this);
2009-09-14 03:07:32 +02:00
}
template<typename LineType>
grep_proc<LineType>::~grep_proc()
2009-09-14 03:07:32 +02:00
{
this->invalidate();
2009-09-14 03:07:32 +02:00
}
template<typename LineType>
2022-03-16 23:38:08 +01:00
void
grep_proc<LineType>::handle_match(
2022-03-31 17:59:19 +02:00
int line, std::string& line_value, int off, int* matches, int count)
2009-09-14 03:07:32 +02:00
{
int lpc;
2013-05-28 06:35:00 +02:00
2009-09-14 03:07:32 +02:00
if (off == 0) {
2013-05-28 06:35:00 +02:00
fprintf(stdout, "%d\n", line);
2009-09-14 03:07:32 +02:00
}
fprintf(stdout, "[%d:%d]\n", matches[0], matches[1]);
for (lpc = 1; lpc < count; lpc++) {
2022-03-16 23:38:08 +01:00
fprintf(stdout, "(%d:%d)", matches[lpc * 2], matches[lpc * 2 + 1]);
2013-05-28 06:35:00 +02:00
fwrite(&(line_value.c_str()[matches[lpc * 2]]),
1,
2022-03-16 23:38:08 +01:00
matches[lpc * 2 + 1] - matches[lpc * 2],
2013-05-28 06:35:00 +02:00
stdout);
fputc('\n', stdout);
2009-09-14 03:07:32 +02:00
}
}
template<typename LineType>
2022-03-16 23:38:08 +01:00
void
grep_proc<LineType>::start()
2009-09-14 03:07:32 +02:00
{
require(this->invariant());
2009-09-14 03:07:32 +02:00
2022-07-11 06:00:45 +02:00
if (this->gp_sink) {
// XXX hack to make sure threads used by line_buffer are not active
// before the fork.
this->gp_sink->grep_quiesce();
}
log_debug("grep_proc(%p): start", this);
2009-09-14 03:07:32 +02:00
if (this->gp_child_started || this->gp_queue.empty()) {
log_debug("grep_proc(%p): nothing to do?", this);
2013-05-28 06:35:00 +02:00
return;
2009-09-14 03:07:32 +02:00
}
auto_pipe in_pipe(STDIN_FILENO);
auto_pipe out_pipe(STDOUT_FILENO);
auto_pipe err_pipe(STDERR_FILENO);
2009-09-14 03:07:32 +02:00
/* Get ahold of some pipes for stdout and stderr. */
if (out_pipe.open() < 0) {
2013-05-28 06:35:00 +02:00
throw error(errno);
2009-09-14 03:07:32 +02:00
}
if (err_pipe.open() < 0) {
2013-05-28 06:35:00 +02:00
throw error(errno);
2009-09-14 03:07:32 +02:00
}
if ((this->gp_child = fork()) < 0) {
2013-05-28 06:35:00 +02:00
throw error(errno);
2009-09-14 03:07:32 +02:00
}
in_pipe.after_fork(this->gp_child);
out_pipe.after_fork(this->gp_child);
err_pipe.after_fork(this->gp_child);
2009-09-14 03:07:32 +02:00
if (this->gp_child != 0) {
2015-09-18 05:55:31 +02:00
log_perror(fcntl(out_pipe.read_end(), F_SETFL, O_NONBLOCK));
log_perror(fcntl(out_pipe.read_end(), F_SETFD, 1));
this->gp_line_buffer.set_fd(out_pipe.read_end());
2013-05-28 06:35:00 +02:00
2015-09-18 05:55:31 +02:00
log_perror(fcntl(err_pipe.read_end(), F_SETFL, O_NONBLOCK));
log_perror(fcntl(err_pipe.read_end(), F_SETFD, 1));
require(this->gp_err_pipe.get() == -1);
this->gp_err_pipe = std::move(err_pipe.read_end());
2013-05-28 06:35:00 +02:00
this->gp_child_started = true;
this->gp_child_queue_size = this->gp_queue.size();
2013-05-28 06:35:00 +02:00
this->gp_queue.clear();
log_debug("grep_proc(%p): started child %d", this, this->gp_child);
2013-05-28 06:35:00 +02:00
return;
2009-09-14 03:07:32 +02:00
}
/* In the child... */
2022-07-08 23:17:34 +02:00
lnav::pid::in_child = true;
2013-05-28 06:35:00 +02:00
2009-09-14 03:07:32 +02:00
/*
* Restore the default signal handlers so we don't hang around
2009-09-14 03:07:32 +02:00
* forever if there is a problem.
*/
signal(SIGINT, SIG_DFL);
signal(SIGTERM, SIG_DFL);
this->child_init();
2013-05-28 06:35:00 +02:00
2012-04-24 23:25:38 +02:00
this->child_loop();
_exit(0);
2012-04-24 23:25:38 +02:00
}
template<typename LineType>
2022-03-16 23:38:08 +01:00
void
grep_proc<LineType>::child_loop()
2012-04-24 23:25:38 +02:00
{
2022-03-16 23:38:08 +01:00
char outbuf[BUFSIZ * 2];
2022-03-31 17:59:19 +02:00
std::string line_value;
2009-09-14 03:07:32 +02:00
/* Make sure buffering is on, not sure of the state in the parent. */
2017-10-29 00:52:06 +02:00
if (setvbuf(stdout, outbuf, _IOFBF, BUFSIZ * 2) < 0) {
2013-05-28 06:35:00 +02:00
perror("setvbuf");
2009-09-14 03:07:32 +02:00
}
2022-03-16 23:38:08 +01:00
lnav_log_file
= make_optional_from_nullable(fopen("/tmp/lnav.grep.err", "a"));
2009-09-14 03:07:32 +02:00
line_value.reserve(BUFSIZ * 2);
while (!this->gp_queue.empty()) {
LineType start_line = this->gp_queue.front().first;
2022-03-16 23:38:08 +01:00
LineType stop_line = this->gp_queue.front().second;
bool done = false;
LineType line;
2013-05-28 06:35:00 +02:00
this->gp_queue.pop_front();
2022-03-16 23:38:08 +01:00
for (line = this->gp_source.grep_initial_line(start_line,
this->gp_highest_line);
line != -1 && (stop_line == -1 || line < stop_line) && !done;
2022-03-16 23:38:08 +01:00
this->gp_source.grep_next_line(line))
{
2013-05-28 06:35:00 +02:00
line_value.clear();
done = !this->gp_source.grep_value_for_line(line, line_value);
if (!done) {
this->gp_pcre->capture_from(line_value)
.for_each([&](lnav::pcre2pp::match_data& md) {
if (md.leading().sf_begin == 0) {
fprintf(stdout, "%d\n", (int) line);
2014-06-18 06:29:42 +02:00
}
2017-10-29 00:52:06 +02:00
fprintf(stdout,
"[%d:%d]\n",
md[0]->sf_begin,
md[0]->sf_end);
for (int lpc = 1; lpc < md.get_count(); lpc++) {
if (!md[lpc]) {
continue;
}
fprintf(stdout,
"(%d:%d)",
md[lpc]->sf_begin,
md[lpc]->sf_end);
fwrite(
md[lpc]->data(), 1, md[lpc]->length(), stdout);
fputc('\n', stdout);
2013-05-28 06:35:00 +02:00
}
fprintf(stdout, "/\n");
});
2013-05-28 06:35:00 +02:00
}
if (((line + 1) % 10000) == 0) {
/* Periodically flush the buffer so the parent sees progress */
this->child_batch();
}
}
if (stop_line == -1) {
// When scanning to the end of the source, we need to return the
// highest line that was seen so that the next request that
// continues from the end works properly.
2017-10-29 00:52:06 +02:00
fprintf(stdout, "h%d\n", line - 1);
}
2022-07-11 06:00:45 +02:00
this->gp_highest_line = line - 1_vl;
2013-05-28 06:35:00 +02:00
this->child_term();
2009-09-14 03:07:32 +02:00
}
}
template<typename LineType>
2022-03-16 23:38:08 +01:00
void
grep_proc<LineType>::cleanup()
2009-09-14 03:07:32 +02:00
{
if (this->gp_child != -1 && this->gp_child != 0) {
2017-04-06 15:36:21 +02:00
int status = 0;
2013-05-28 06:35:00 +02:00
kill(this->gp_child, SIGTERM);
while (waitpid(this->gp_child, &status, 0) < 0 && (errno == EINTR)) {
;
}
require(!WIFSIGNALED(status) || WTERMSIG(status) != SIGABRT);
2022-03-16 23:38:08 +01:00
this->gp_child = -1;
2013-05-28 06:35:00 +02:00
this->gp_child_started = false;
if (this->gp_sink) {
2020-09-03 08:04:21 +02:00
for (size_t lpc = 0; lpc < this->gp_child_queue_size; lpc++) {
this->gp_sink->grep_end(*this);
}
2013-05-28 06:35:00 +02:00
}
2009-09-14 03:07:32 +02:00
}
if (this->gp_err_pipe != -1) {
2013-05-28 06:35:00 +02:00
this->gp_err_pipe.reset();
2009-09-14 03:07:32 +02:00
}
this->gp_pipe_range.clear();
2009-09-14 03:07:32 +02:00
this->gp_line_buffer.reset();
ensure(this->invariant());
2009-09-14 03:07:32 +02:00
if (!this->gp_queue.empty()) {
2013-05-28 06:35:00 +02:00
this->start();
2009-09-14 03:07:32 +02:00
}
}
template<typename LineType>
2022-03-16 23:38:08 +01:00
void
grep_proc<LineType>::dispatch_line(char* line)
2009-09-14 03:07:32 +02:00
{
int start, end, capture_start;
require(line != nullptr);
2009-09-14 03:07:32 +02:00
if (sscanf(line, "h%d", this->gp_highest_line.out()) == 1) {
} else if (sscanf(line, "%d", this->gp_last_line.out()) == 1) {
/* Starting a new line with matches. */
ensure(this->gp_last_line >= 0);
2022-03-16 23:38:08 +01:00
} else if (sscanf(line, "[%d:%d]", &start, &end) == 2) {
require(start >= 0);
require(end >= 0);
2009-09-14 03:07:32 +02:00
2013-05-28 06:35:00 +02:00
/* Pass the match offsets to the sink delegate. */
if (this->gp_sink != nullptr) {
2013-05-28 06:35:00 +02:00
this->gp_sink->grep_match(*this, this->gp_last_line, start, end);
}
2022-03-16 23:38:08 +01:00
} else if (sscanf(line, "(%d:%d)%n", &start, &end, &capture_start) == 2) {
require(start == -1 || start >= 0);
require(end >= 0);
2013-05-28 06:35:00 +02:00
/* Pass the captured strings to the sink delegate. */
if (this->gp_sink != nullptr) {
2022-03-16 23:38:08 +01:00
this->gp_sink->grep_capture(
*this,
this->gp_last_line,
start,
end,
start < 0 ? nullptr : &line[capture_start]);
2013-05-28 06:35:00 +02:00
}
2022-03-16 23:38:08 +01:00
} else if (line[0] == '/') {
if (this->gp_sink != nullptr) {
2013-05-28 06:35:00 +02:00
this->gp_sink->grep_match_end(*this, this->gp_last_line);
}
2022-03-16 23:38:08 +01:00
} else {
log_error("bad line from child -- %s", line);
2009-09-14 03:07:32 +02:00
}
}
template<typename LineType>
2022-03-16 23:38:08 +01:00
void
grep_proc<LineType>::check_poll_set(const std::vector<struct pollfd>& pollfds)
2009-09-14 03:07:32 +02:00
{
require(this->invariant());
2009-09-14 03:07:32 +02:00
if (this->gp_err_pipe != -1 && pollfd_ready(pollfds, this->gp_err_pipe)) {
2013-05-28 06:35:00 +02:00
char buffer[1024 + 1];
ssize_t rc;
2013-05-28 06:35:00 +02:00
rc = read(this->gp_err_pipe, buffer, sizeof(buffer) - 1);
if (rc > 0) {
2022-03-16 23:38:08 +01:00
static const char* PREFIX = ": ";
2013-05-28 06:35:00 +02:00
buffer[rc] = '\0';
if (strncmp(buffer, PREFIX, strlen(PREFIX)) == 0) {
2022-03-16 23:38:08 +01:00
char* lf;
2013-05-28 06:35:00 +02:00
if ((lf = strchr(buffer, '\n')) != nullptr) {
2013-05-28 06:35:00 +02:00
*lf = '\0';
}
if (this->gp_control != nullptr) {
2013-05-28 06:35:00 +02:00
this->gp_control->grep_error(&buffer[strlen(PREFIX)]);
}
}
2022-03-16 23:38:08 +01:00
} else if (rc == 0) {
2013-05-28 06:35:00 +02:00
this->gp_err_pipe.reset();
}
2009-09-14 03:07:32 +02:00
}
2022-03-16 23:38:08 +01:00
if (this->gp_line_buffer.get_fd() != -1
&& pollfd_ready(pollfds, this->gp_line_buffer.get_fd()))
{
2013-05-28 06:35:00 +02:00
try {
static const int MAX_LOOPS = 100;
int loop_count = 0;
bool drained = false;
2013-05-28 06:35:00 +02:00
while (loop_count < MAX_LOOPS) {
2022-03-16 23:38:08 +01:00
auto load_result
= this->gp_line_buffer.load_next_line(this->gp_pipe_range);
if (load_result.isErr()) {
2022-07-08 23:17:34 +02:00
log_error("failed to read from grep_proc child: %s",
load_result.unwrapErr().c_str());
break;
}
auto li = load_result.unwrap();
if (li.li_file_range.empty()) {
drained = true;
break;
}
this->gp_pipe_range = li.li_file_range;
2022-03-16 23:38:08 +01:00
this->gp_line_buffer.read_range(li.li_file_range)
.then([this](auto sbr) {
auto_mem<char> buf;
2022-03-16 23:38:08 +01:00
buf = (char*) malloc(sbr.length() + 1);
sbr.rtrim(is_line_ending);
memcpy(buf, sbr.get_data(), sbr.length());
buf[sbr.length()] = '\0';
this->dispatch_line(buf);
});
2013-05-28 06:35:00 +02:00
loop_count += 1;
}
if (this->gp_sink != nullptr) {
2013-05-28 06:35:00 +02:00
this->gp_sink->grep_end_batch(*this);
}
if (drained && this->gp_line_buffer.is_pipe_closed()) {
2013-05-28 06:35:00 +02:00
this->cleanup();
}
2022-03-16 23:38:08 +01:00
} catch (line_buffer::error& e) {
2013-05-28 06:35:00 +02:00
this->cleanup();
}
2009-09-14 03:07:32 +02:00
}
ensure(this->invariant());
2009-09-14 03:07:32 +02:00
}
template<typename LineType>
2022-03-16 23:38:08 +01:00
grep_proc<LineType>&
grep_proc<LineType>::invalidate()
{
if (this->gp_sink) {
2020-09-12 08:10:11 +02:00
for (size_t lpc = 0; lpc < this->gp_queue.size(); lpc++) {
this->gp_sink->grep_end(*this);
}
}
this->gp_queue.clear();
this->cleanup();
return *this;
}
template<typename LineType>
void
grep_proc<LineType>::update_poll_set(std::vector<struct pollfd>& pollfds)
{
if (this->gp_line_buffer.get_fd() != -1) {
pollfds.push_back(
(struct pollfd){this->gp_line_buffer.get_fd(), POLLIN, 0});
}
2022-07-08 23:17:34 +02:00
if (this->gp_err_pipe.get() != -1) {
pollfds.push_back((struct pollfd){this->gp_err_pipe, POLLIN, 0});
}
}
template class grep_proc<vis_line_t>;