mirror of
https://github.com/systemd/systemd.git
synced 2024-11-27 12:13:33 +08:00
Merge pull request #7881 from keszybz/pcre
Add new --grep option to journalctl
This commit is contained in:
commit
7755083256
@ -578,6 +578,29 @@
|
||||
priorities.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>-g</option></term>
|
||||
<term><option>--grep=</option></term>
|
||||
|
||||
<listitem><para>Filter output to entries where the <varname>MESSAGE=</varname>
|
||||
field matches the specified regular expression. PERL-compatible regular expressions
|
||||
are used, see
|
||||
<citerefentry><refentrytitle>pcre2pattern</refentrytitle><manvolnum>3</manvolnum></citerefentry>
|
||||
for a detailed description of the syntax.</para>
|
||||
|
||||
<para>If the pattern is all lowercase, matching is case insensitive.
|
||||
Otherwise, matching is case sensitive. This can be overridden with the
|
||||
<option>--case-sensitive</option> option, see below.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--case-sensitive<optional>=BOOLEAN</optional></option></term>
|
||||
|
||||
<listitem><para>Make pattern matching case sensitive or case insenstive.</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>-c</option></term>
|
||||
<term><option>--cursor=</option></term>
|
||||
|
15
meson.build
15
meson.build
@ -1104,6 +1104,17 @@ else
|
||||
endif
|
||||
conf.set10('HAVE_XKBCOMMON', have)
|
||||
|
||||
want_pcre2 = get_option('pcre2')
|
||||
if want_pcre2 != 'false'
|
||||
libpcre2 = dependency('libpcre2-8',
|
||||
required : want_pcre2 == 'true')
|
||||
have = libpcre2.found()
|
||||
else
|
||||
have = false
|
||||
libpcre2 = []
|
||||
endif
|
||||
conf.set10('HAVE_PCRE2', have)
|
||||
|
||||
want_glib = get_option('glib')
|
||||
if want_glib != 'false' and not fuzzer_build
|
||||
libglib = dependency('glib-2.0',
|
||||
@ -1465,7 +1476,8 @@ exe = executable('journalctl',
|
||||
dependencies : [threads,
|
||||
libqrencode,
|
||||
libxz,
|
||||
liblz4],
|
||||
liblz4,
|
||||
libpcre2],
|
||||
install_rpath : rootlibexecdir,
|
||||
install : true,
|
||||
install_dir : rootbindir)
|
||||
@ -2817,6 +2829,7 @@ foreach tuple : [
|
||||
['gnu-efi', have_gnu_efi],
|
||||
['kmod'],
|
||||
['xkbcommon'],
|
||||
['pcre2'],
|
||||
['blkid'],
|
||||
['dbus'],
|
||||
['glib'],
|
||||
|
@ -260,6 +260,8 @@ option('lz4', type : 'combo', choices : ['auto', 'true', 'false'],
|
||||
description : 'lz4 compression support')
|
||||
option('xkbcommon', type : 'combo', choices : ['auto', 'true', 'false'],
|
||||
description : 'xkbcommon keymap support')
|
||||
option('pcre2', type : 'combo', choices : ['auto', 'true', 'false'],
|
||||
description : 'regexp matching support using pcre2')
|
||||
option('glib', type : 'combo', choices : ['auto', 'true', 'false'],
|
||||
description : 'libglib support (for tests only)')
|
||||
option('dbus', type : 'combo', choices : ['auto', 'true', 'false'],
|
||||
|
@ -140,6 +140,12 @@
|
||||
#define _IDN_FEATURE_ "-IDN"
|
||||
#endif
|
||||
|
||||
#if HAVE_PCRE2
|
||||
#define _PCRE2_FEATURE_ "+PCRE2"
|
||||
#else
|
||||
#define _PCRE2_FEATURE_ "-PCRE2"
|
||||
#endif
|
||||
|
||||
#define _CGROUP_HIEARCHY_ "default-hierarchy=" DEFAULT_HIERARCHY_NAME
|
||||
|
||||
#define SYSTEMD_FEATURES \
|
||||
@ -163,4 +169,5 @@
|
||||
_KMOD_FEATURE_ " " \
|
||||
_IDN2_FEATURE_ " " \
|
||||
_IDN_FEATURE_ " " \
|
||||
_PCRE2_FEATURE_ " " \
|
||||
_CGROUP_HIEARCHY_
|
||||
|
@ -30,6 +30,7 @@
|
||||
#include "gunicode.h"
|
||||
#include "macro.h"
|
||||
#include "string-util.h"
|
||||
#include "terminal-util.h"
|
||||
#include "utf8.h"
|
||||
#include "util.h"
|
||||
|
||||
@ -648,7 +649,17 @@ char *strreplace(const char *text, const char *old_string, const char *new_strin
|
||||
return ret;
|
||||
}
|
||||
|
||||
char *strip_tab_ansi(char **ibuf, size_t *_isz) {
|
||||
static void advance_offsets(ssize_t diff, size_t offsets[2], size_t shift[2], size_t size) {
|
||||
if (!offsets)
|
||||
return;
|
||||
|
||||
if ((size_t) diff < offsets[0])
|
||||
shift[0] += size;
|
||||
if ((size_t) diff < offsets[1])
|
||||
shift[1] += size;
|
||||
}
|
||||
|
||||
char *strip_tab_ansi(char **ibuf, size_t *_isz, size_t highlight[2]) {
|
||||
const char *i, *begin = NULL;
|
||||
enum {
|
||||
STATE_OTHER,
|
||||
@ -656,7 +667,7 @@ char *strip_tab_ansi(char **ibuf, size_t *_isz) {
|
||||
STATE_BRACKET
|
||||
} state = STATE_OTHER;
|
||||
char *obuf = NULL;
|
||||
size_t osz = 0, isz;
|
||||
size_t osz = 0, isz, shift[2] = {};
|
||||
FILE *f;
|
||||
|
||||
assert(ibuf);
|
||||
@ -684,15 +695,18 @@ char *strip_tab_ansi(char **ibuf, size_t *_isz) {
|
||||
break;
|
||||
else if (*i == '\x1B')
|
||||
state = STATE_ESCAPE;
|
||||
else if (*i == '\t')
|
||||
else if (*i == '\t') {
|
||||
fputs(" ", f);
|
||||
else
|
||||
advance_offsets(i - *ibuf, highlight, shift, 7);
|
||||
} else
|
||||
fputc(*i, f);
|
||||
|
||||
break;
|
||||
|
||||
case STATE_ESCAPE:
|
||||
if (i >= *ibuf + isz) { /* EOT */
|
||||
fputc('\x1B', f);
|
||||
advance_offsets(i - *ibuf, highlight, shift, 1);
|
||||
break;
|
||||
} else if (*i == '[') {
|
||||
state = STATE_BRACKET;
|
||||
@ -700,6 +714,7 @@ char *strip_tab_ansi(char **ibuf, size_t *_isz) {
|
||||
} else {
|
||||
fputc('\x1B', f);
|
||||
fputc(*i, f);
|
||||
advance_offsets(i - *ibuf, highlight, shift, 1);
|
||||
state = STATE_OTHER;
|
||||
}
|
||||
|
||||
@ -711,6 +726,7 @@ char *strip_tab_ansi(char **ibuf, size_t *_isz) {
|
||||
(!(*i >= '0' && *i <= '9') && !IN_SET(*i, ';', 'm'))) {
|
||||
fputc('\x1B', f);
|
||||
fputc('[', f);
|
||||
advance_offsets(i - *ibuf, highlight, shift, 2);
|
||||
state = STATE_OTHER;
|
||||
i = begin-1;
|
||||
} else if (*i == 'm')
|
||||
@ -732,6 +748,11 @@ char *strip_tab_ansi(char **ibuf, size_t *_isz) {
|
||||
if (_isz)
|
||||
*_isz = osz;
|
||||
|
||||
if (highlight) {
|
||||
highlight[0] += shift[0];
|
||||
highlight[1] += shift[1];
|
||||
}
|
||||
|
||||
return obuf;
|
||||
}
|
||||
|
||||
|
@ -177,7 +177,7 @@ char* strshorten(char *s, size_t l);
|
||||
|
||||
char *strreplace(const char *text, const char *old_string, const char *new_string);
|
||||
|
||||
char *strip_tab_ansi(char **p, size_t *l);
|
||||
char *strip_tab_ansi(char **ibuf, size_t *_isz, size_t highlight[2]);
|
||||
|
||||
char *strextend_with_separator(char **x, const char *separator, ...) _sentinel_;
|
||||
|
||||
|
@ -226,7 +226,8 @@ static ssize_t request_reader_entries(
|
||||
return MHD_CONTENT_READER_END_WITH_ERROR;
|
||||
}
|
||||
|
||||
r = output_journal(m->tmp, m->journal, m->mode, 0, OUTPUT_FULL_WIDTH, NULL, NULL);
|
||||
r = output_journal(m->tmp, m->journal, m->mode, 0, OUTPUT_FULL_WIDTH,
|
||||
NULL, NULL, NULL);
|
||||
if (r < 0) {
|
||||
log_error_errno(r, "Failed to serialize item: %m");
|
||||
return MHD_CONTENT_READER_END_WITH_ERROR;
|
||||
|
@ -34,6 +34,11 @@
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#if HAVE_PCRE2
|
||||
# define PCRE2_CODE_UNIT_WIDTH 8
|
||||
# include <pcre2.h>
|
||||
#endif
|
||||
|
||||
#include "sd-bus.h"
|
||||
#include "sd-journal.h"
|
||||
|
||||
@ -76,6 +81,34 @@
|
||||
|
||||
#define DEFAULT_FSS_INTERVAL_USEC (15*USEC_PER_MINUTE)
|
||||
|
||||
#if HAVE_PCRE2
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(pcre2_match_data*, pcre2_match_data_free);
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(pcre2_code*, pcre2_code_free);
|
||||
|
||||
static int pattern_compile(const char *pattern, unsigned flags, pcre2_code **out) {
|
||||
int errorcode, r;
|
||||
PCRE2_SIZE erroroffset;
|
||||
pcre2_code *p;
|
||||
|
||||
p = pcre2_compile((PCRE2_SPTR8) pattern,
|
||||
PCRE2_ZERO_TERMINATED, flags, &errorcode, &erroroffset, NULL);
|
||||
if (!p) {
|
||||
unsigned char buf[LINE_MAX];
|
||||
|
||||
r = pcre2_get_error_message(errorcode, buf, sizeof buf);
|
||||
|
||||
log_error("Bad pattern \"%s\": %s",
|
||||
pattern,
|
||||
r < 0 ? "unknown error" : (char*) buf);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
*out = p;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
enum {
|
||||
/* Special values for arg_lines */
|
||||
ARG_LINES_DEFAULT = -2,
|
||||
@ -126,6 +159,12 @@ static uint64_t arg_vacuum_n_files = 0;
|
||||
static usec_t arg_vacuum_time = 0;
|
||||
static char **arg_output_fields = NULL;
|
||||
|
||||
#if HAVE_PCRE2
|
||||
static const char *arg_pattern = NULL;
|
||||
static pcre2_code *arg_compiled_pattern = NULL;
|
||||
static int arg_case_sensitive = -1; /* -1 means be smart */
|
||||
#endif
|
||||
|
||||
static enum {
|
||||
ACTION_SHOW,
|
||||
ACTION_NEW_ID128,
|
||||
@ -280,67 +319,69 @@ static void help(void) {
|
||||
printf("%s [OPTIONS...] [MATCHES...]\n\n"
|
||||
"Query the journal.\n\n"
|
||||
"Options:\n"
|
||||
" --system Show the system journal\n"
|
||||
" --user Show the user journal for the current user\n"
|
||||
" -M --machine=CONTAINER Operate on local container\n"
|
||||
" -S --since=DATE Show entries not older than the specified date\n"
|
||||
" -U --until=DATE Show entries not newer than the specified date\n"
|
||||
" -c --cursor=CURSOR Show entries starting at the specified cursor\n"
|
||||
" --after-cursor=CURSOR Show entries after the specified cursor\n"
|
||||
" --show-cursor Print the cursor after all the entries\n"
|
||||
" -b --boot[=ID] Show current boot or the specified boot\n"
|
||||
" --list-boots Show terse information about recorded boots\n"
|
||||
" -k --dmesg Show kernel message log from the current boot\n"
|
||||
" -u --unit=UNIT Show logs from the specified unit\n"
|
||||
" --user-unit=UNIT Show logs from the specified user unit\n"
|
||||
" -t --identifier=STRING Show entries with the specified syslog identifier\n"
|
||||
" -p --priority=RANGE Show entries with the specified priority\n"
|
||||
" -e --pager-end Immediately jump to the end in the pager\n"
|
||||
" -f --follow Follow the journal\n"
|
||||
" -n --lines[=INTEGER] Number of journal entries to show\n"
|
||||
" --no-tail Show all lines, even in follow mode\n"
|
||||
" -r --reverse Show the newest entries first\n"
|
||||
" -o --output=STRING Change journal output mode (short, short-precise,\n"
|
||||
" short-iso, short-iso-precise, short-full,\n"
|
||||
" short-monotonic, short-unix, verbose, export,\n"
|
||||
" json, json-pretty, json-sse, cat)\n"
|
||||
" --output-fields=LIST Select fields to print in verbose/export/json modes\n"
|
||||
" --utc Express time in Coordinated Universal Time (UTC)\n"
|
||||
" -x --catalog Add message explanations where available\n"
|
||||
" --no-full Ellipsize fields\n"
|
||||
" -a --all Show all fields, including long and unprintable\n"
|
||||
" -q --quiet Do not show info messages and privilege warning\n"
|
||||
" --no-pager Do not pipe output into a pager\n"
|
||||
" --no-hostname Suppress output of hostname field\n"
|
||||
" -m --merge Show entries from all available journals\n"
|
||||
" -D --directory=PATH Show journal files from directory\n"
|
||||
" --file=PATH Show journal file\n"
|
||||
" --root=ROOT Operate on files below a root directory\n"
|
||||
" --system Show the system journal\n"
|
||||
" --user Show the user journal for the current user\n"
|
||||
" -M --machine=CONTAINER Operate on local container\n"
|
||||
" -S --since=DATE Show entries not older than the specified date\n"
|
||||
" -U --until=DATE Show entries not newer than the specified date\n"
|
||||
" -c --cursor=CURSOR Show entries starting at the specified cursor\n"
|
||||
" --after-cursor=CURSOR Show entries after the specified cursor\n"
|
||||
" --show-cursor Print the cursor after all the entries\n"
|
||||
" -b --boot[=ID] Show current boot or the specified boot\n"
|
||||
" --list-boots Show terse information about recorded boots\n"
|
||||
" -k --dmesg Show kernel message log from the current boot\n"
|
||||
" -u --unit=UNIT Show logs from the specified unit\n"
|
||||
" --user-unit=UNIT Show logs from the specified user unit\n"
|
||||
" -t --identifier=STRING Show entries with the specified syslog identifier\n"
|
||||
" -p --priority=RANGE Show entries with the specified priority\n"
|
||||
" -g --grep=PATTERN Show entries with MESSSAGE matching PATTERN\n"
|
||||
" --case-sensitive[=BOOL] Force case sensitive or insenstive matching\n"
|
||||
" -e --pager-end Immediately jump to the end in the pager\n"
|
||||
" -f --follow Follow the journal\n"
|
||||
" -n --lines[=INTEGER] Number of journal entries to show\n"
|
||||
" --no-tail Show all lines, even in follow mode\n"
|
||||
" -r --reverse Show the newest entries first\n"
|
||||
" -o --output=STRING Change journal output mode (short, short-precise,\n"
|
||||
" short-iso, short-iso-precise, short-full,\n"
|
||||
" short-monotonic, short-unix, verbose, export,\n"
|
||||
" json, json-pretty, json-sse, cat)\n"
|
||||
" --output-fields=LIST Select fields to print in verbose/export/json modes\n"
|
||||
" --utc Express time in Coordinated Universal Time (UTC)\n"
|
||||
" -x --catalog Add message explanations where available\n"
|
||||
" --no-full Ellipsize fields\n"
|
||||
" -a --all Show all fields, including long and unprintable\n"
|
||||
" -q --quiet Do not show info messages and privilege warning\n"
|
||||
" --no-pager Do not pipe output into a pager\n"
|
||||
" --no-hostname Suppress output of hostname field\n"
|
||||
" -m --merge Show entries from all available journals\n"
|
||||
" -D --directory=PATH Show journal files from directory\n"
|
||||
" --file=PATH Show journal file\n"
|
||||
" --root=ROOT Operate on files below a root directory\n"
|
||||
#if HAVE_GCRYPT
|
||||
" --interval=TIME Time interval for changing the FSS sealing key\n"
|
||||
" --verify-key=KEY Specify FSS verification key\n"
|
||||
" --force Override of the FSS key pair with --setup-keys\n"
|
||||
" --interval=TIME Time interval for changing the FSS sealing key\n"
|
||||
" --verify-key=KEY Specify FSS verification key\n"
|
||||
" --force Override of the FSS key pair with --setup-keys\n"
|
||||
#endif
|
||||
"\nCommands:\n"
|
||||
" -h --help Show this help text\n"
|
||||
" --version Show package version\n"
|
||||
" -N --fields List all field names currently used\n"
|
||||
" -F --field=FIELD List all values that a specified field takes\n"
|
||||
" --disk-usage Show total disk usage of all journal files\n"
|
||||
" --vacuum-size=BYTES Reduce disk usage below specified size\n"
|
||||
" --vacuum-files=INT Leave only the specified number of journal files\n"
|
||||
" --vacuum-time=TIME Remove journal files older than specified time\n"
|
||||
" --verify Verify journal file consistency\n"
|
||||
" --sync Synchronize unwritten journal messages to disk\n"
|
||||
" --flush Flush all journal data from /run into /var\n"
|
||||
" --rotate Request immediate rotation of the journal files\n"
|
||||
" --header Show journal header information\n"
|
||||
" --list-catalog Show all message IDs in the catalog\n"
|
||||
" --dump-catalog Show entries in the message catalog\n"
|
||||
" --update-catalog Update the message catalog database\n"
|
||||
" --new-id128 Generate a new 128-bit ID\n"
|
||||
" -h --help Show this help text\n"
|
||||
" --version Show package version\n"
|
||||
" -N --fields List all field names currently used\n"
|
||||
" -F --field=FIELD List all values that a specified field takes\n"
|
||||
" --disk-usage Show total disk usage of all journal files\n"
|
||||
" --vacuum-size=BYTES Reduce disk usage below specified size\n"
|
||||
" --vacuum-files=INT Leave only the specified number of journal files\n"
|
||||
" --vacuum-time=TIME Remove journal files older than specified time\n"
|
||||
" --verify Verify journal file consistency\n"
|
||||
" --sync Synchronize unwritten journal messages to disk\n"
|
||||
" --flush Flush all journal data from /run into /var\n"
|
||||
" --rotate Request immediate rotation of the journal files\n"
|
||||
" --header Show journal header information\n"
|
||||
" --list-catalog Show all message IDs in the catalog\n"
|
||||
" --dump-catalog Show entries in the message catalog\n"
|
||||
" --update-catalog Update the message catalog database\n"
|
||||
" --new-id128 Generate a new 128-bit ID\n"
|
||||
#if HAVE_GCRYPT
|
||||
" --setup-keys Generate a new FSS key pair\n"
|
||||
" --setup-keys Generate a new FSS key pair\n"
|
||||
#endif
|
||||
, program_invocation_short_name);
|
||||
}
|
||||
@ -372,6 +413,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
ARG_DUMP_CATALOG,
|
||||
ARG_UPDATE_CATALOG,
|
||||
ARG_FORCE,
|
||||
ARG_CASE_SENSITIVE,
|
||||
ARG_UTC,
|
||||
ARG_SYNC,
|
||||
ARG_FLUSH,
|
||||
@ -411,6 +453,8 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
{ "header", no_argument, NULL, ARG_HEADER },
|
||||
{ "identifier", required_argument, NULL, 't' },
|
||||
{ "priority", required_argument, NULL, 'p' },
|
||||
{ "grep", required_argument, NULL, 'g' },
|
||||
{ "case-sensitive", optional_argument, NULL, ARG_CASE_SENSITIVE },
|
||||
{ "setup-keys", no_argument, NULL, ARG_SETUP_KEYS },
|
||||
{ "interval", required_argument, NULL, ARG_INTERVAL },
|
||||
{ "verify", no_argument, NULL, ARG_VERIFY },
|
||||
@ -762,6 +806,27 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
break;
|
||||
}
|
||||
|
||||
#if HAVE_PCRE2
|
||||
case 'g':
|
||||
arg_pattern = optarg;
|
||||
break;
|
||||
|
||||
case ARG_CASE_SENSITIVE:
|
||||
if (optarg) {
|
||||
r = parse_boolean(optarg);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Bad --case-sensitive= argument \"%s\": %m", optarg);
|
||||
arg_case_sensitive = r;
|
||||
} else
|
||||
arg_case_sensitive = true;
|
||||
|
||||
break;
|
||||
#else
|
||||
case 'g':
|
||||
case ARG_CASE_SENSITIVE:
|
||||
return log_error("Compiled without pattern matching support");
|
||||
#endif
|
||||
|
||||
case 'S':
|
||||
r = parse_timestamp(optarg, &arg_since);
|
||||
if (r < 0) {
|
||||
@ -917,6 +982,42 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
arg_system_units = strv_free(arg_system_units);
|
||||
}
|
||||
|
||||
|
||||
#if HAVE_PCRE2
|
||||
if (arg_pattern) {
|
||||
unsigned flags;
|
||||
|
||||
if (arg_case_sensitive >= 0)
|
||||
flags = !arg_case_sensitive * PCRE2_CASELESS;
|
||||
else {
|
||||
_cleanup_(pcre2_match_data_freep) pcre2_match_data *md = NULL;
|
||||
bool has_case;
|
||||
_cleanup_(pcre2_code_freep) pcre2_code *cs = NULL;
|
||||
|
||||
md = pcre2_match_data_create(1, NULL);
|
||||
if (!md)
|
||||
return log_oom();
|
||||
|
||||
r = pattern_compile("[[:upper:]]", 0, &cs);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = pcre2_match(cs, (PCRE2_SPTR8) arg_pattern, PCRE2_ZERO_TERMINATED, 0, 0, md, NULL);
|
||||
has_case = r >= 0;
|
||||
|
||||
flags = !has_case * PCRE2_CASELESS;
|
||||
}
|
||||
|
||||
log_debug("Doing case %s matching based on %s",
|
||||
flags & PCRE2_CASELESS ? "insensitive" : "sensitive",
|
||||
arg_case_sensitive >= 0 ? "request" : "pattern casing");
|
||||
|
||||
r = pattern_compile(arg_pattern, flags, &arg_compiled_pattern);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
#endif
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -2415,6 +2516,7 @@ int main(int argc, char *argv[]) {
|
||||
for (;;) {
|
||||
while (arg_lines < 0 || n_shown < arg_lines || (arg_follow && !first_line)) {
|
||||
int flags;
|
||||
size_t highlight[2] = {};
|
||||
|
||||
if (need_seek) {
|
||||
if (!arg_reverse)
|
||||
@ -2468,6 +2570,58 @@ int main(int argc, char *argv[]) {
|
||||
}
|
||||
}
|
||||
|
||||
#if HAVE_PCRE2
|
||||
if (arg_compiled_pattern) {
|
||||
_cleanup_(pcre2_match_data_freep) pcre2_match_data *md = NULL;
|
||||
const void *message;
|
||||
size_t len;
|
||||
PCRE2_SIZE *ovec;
|
||||
|
||||
md = pcre2_match_data_create(1, NULL);
|
||||
if (!md)
|
||||
return log_oom();
|
||||
|
||||
r = sd_journal_get_data(j, "MESSAGE", &message, &len);
|
||||
if (r < 0) {
|
||||
if (r == -ENOENT) {
|
||||
need_seek = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
log_error_errno(r, "Failed to get MESSAGE field: %m");
|
||||
goto finish;
|
||||
}
|
||||
|
||||
assert_se(message = startswith(message, "MESSAGE="));
|
||||
|
||||
r = pcre2_match(arg_compiled_pattern,
|
||||
message,
|
||||
len - strlen("MESSAGE="),
|
||||
0, /* start at offset 0 in the subject */
|
||||
0, /* default options */
|
||||
md,
|
||||
NULL);
|
||||
if (r == PCRE2_ERROR_NOMATCH) {
|
||||
need_seek = true;
|
||||
continue;
|
||||
}
|
||||
if (r < 0) {
|
||||
unsigned char buf[LINE_MAX];
|
||||
int r2;
|
||||
|
||||
r2 = pcre2_get_error_message(r, buf, sizeof buf);
|
||||
log_error("Pattern matching failed: %s",
|
||||
r2 < 0 ? "unknown error" : (char*) buf);
|
||||
r = -EINVAL;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
ovec = pcre2_get_ovector_pointer(md);
|
||||
highlight[0] = ovec[0];
|
||||
highlight[1] = ovec[1];
|
||||
}
|
||||
#endif
|
||||
|
||||
flags =
|
||||
arg_all * OUTPUT_SHOW_ALL |
|
||||
arg_full * OUTPUT_FULL_WIDTH |
|
||||
@ -2476,7 +2630,8 @@ int main(int argc, char *argv[]) {
|
||||
arg_utc * OUTPUT_UTC |
|
||||
arg_no_hostname * OUTPUT_NO_HOSTNAME;
|
||||
|
||||
r = output_journal(stdout, j, arg_output, 0, flags, arg_output_fields, &ellipsized);
|
||||
r = output_journal(stdout, j, arg_output, 0, flags,
|
||||
arg_output_fields, highlight, &ellipsized);
|
||||
need_seek = true;
|
||||
if (r == -EADDRNOTAVAIL)
|
||||
break;
|
||||
@ -2527,5 +2682,10 @@ finish:
|
||||
free(arg_root);
|
||||
free(arg_verify_key);
|
||||
|
||||
#if HAVE_PCRE2
|
||||
if (arg_compiled_pattern)
|
||||
pcre2_code_free(arg_compiled_pattern);
|
||||
#endif
|
||||
|
||||
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
|
||||
}
|
||||
|
@ -166,8 +166,17 @@ static bool shall_print(const char *p, size_t l, OutputFlags flags) {
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool print_multiline(FILE *f, unsigned prefix, unsigned n_columns, OutputFlags flags, int priority, const char* message, size_t message_len) {
|
||||
const char *color_on = "", *color_off = "";
|
||||
static bool print_multiline(
|
||||
FILE *f,
|
||||
unsigned prefix,
|
||||
unsigned n_columns,
|
||||
OutputFlags flags,
|
||||
int priority,
|
||||
const char* message,
|
||||
size_t message_len,
|
||||
size_t highlight[2]) {
|
||||
|
||||
const char *color_on = "", *color_off = "", *highlight_on = "";
|
||||
const char *pos, *end;
|
||||
bool ellipsized = false;
|
||||
int line = 0;
|
||||
@ -176,9 +185,11 @@ static bool print_multiline(FILE *f, unsigned prefix, unsigned n_columns, Output
|
||||
if (priority <= LOG_ERR) {
|
||||
color_on = ANSI_HIGHLIGHT_RED;
|
||||
color_off = ANSI_NORMAL;
|
||||
highlight_on = ANSI_HIGHLIGHT;
|
||||
} else if (priority <= LOG_NOTICE) {
|
||||
color_on = ANSI_HIGHLIGHT;
|
||||
color_off = ANSI_NORMAL;
|
||||
highlight_on = ANSI_HIGHLIGHT_RED;
|
||||
}
|
||||
}
|
||||
|
||||
@ -209,9 +220,28 @@ static bool print_multiline(FILE *f, unsigned prefix, unsigned n_columns, Output
|
||||
|
||||
if (flags & (OUTPUT_FULL_WIDTH | OUTPUT_SHOW_ALL) ||
|
||||
(prefix + len + 1 < n_columns && !tail_line)) {
|
||||
fprintf(f, "%*s%s%.*s%s\n",
|
||||
continuation * prefix, "",
|
||||
color_on, len, pos, color_off);
|
||||
if (highlight &&
|
||||
(size_t) (pos - message) <= highlight[0] &&
|
||||
highlight[0] < (size_t) len) {
|
||||
|
||||
fprintf(f, "%*s%s%.*s",
|
||||
continuation * prefix, "",
|
||||
color_on, (int) highlight[0], pos);
|
||||
fprintf(f, "%s%.*s",
|
||||
highlight_on,
|
||||
(int) (MIN((size_t) len, highlight[1]) - highlight[0]),
|
||||
pos + highlight[0]);
|
||||
if ((size_t) len > highlight[1])
|
||||
fprintf(f, "%s%.*s",
|
||||
color_on,
|
||||
(int) (len - highlight[1]),
|
||||
pos + highlight[1]);
|
||||
fprintf(f, "%s\n", color_off);
|
||||
|
||||
} else
|
||||
fprintf(f, "%*s%s%.*s%s\n",
|
||||
continuation * prefix, "",
|
||||
color_on, len, pos, color_off);
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -369,7 +399,8 @@ static int output_short(
|
||||
OutputMode mode,
|
||||
unsigned n_columns,
|
||||
OutputFlags flags,
|
||||
Set *output_fields) {
|
||||
Set *output_fields,
|
||||
size_t highlight[2]) {
|
||||
|
||||
int r;
|
||||
const void *data;
|
||||
@ -390,6 +421,7 @@ static int output_short(
|
||||
PARSE_FIELD_VEC_ENTRY("_SOURCE_REALTIME_TIMESTAMP=", &realtime, &realtime_len),
|
||||
PARSE_FIELD_VEC_ENTRY("_SOURCE_MONOTONIC_TIMESTAMP=", &monotonic, &monotonic_len),
|
||||
};
|
||||
size_t highlight_shifted[] = {highlight ? highlight[0] : 0, highlight ? highlight[1] : 0};
|
||||
|
||||
assert(f);
|
||||
assert(j);
|
||||
@ -421,7 +453,7 @@ static int output_short(
|
||||
}
|
||||
|
||||
if (!(flags & OUTPUT_SHOW_ALL))
|
||||
strip_tab_ansi(&message, &message_len);
|
||||
strip_tab_ansi(&message, &message_len, highlight_shifted);
|
||||
|
||||
if (priority_len == 1 && *priority >= '0' && *priority <= '7')
|
||||
p = *priority - '0';
|
||||
@ -468,7 +500,9 @@ static int output_short(
|
||||
} else {
|
||||
fputs(": ", f);
|
||||
ellipsized |=
|
||||
print_multiline(f, n + 2, n_columns, flags, p, message, message_len);
|
||||
print_multiline(f, n + 2, n_columns, flags, p,
|
||||
message, message_len,
|
||||
highlight_shifted);
|
||||
}
|
||||
|
||||
if (flags & OUTPUT_CATALOG)
|
||||
@ -483,7 +517,8 @@ static int output_verbose(
|
||||
OutputMode mode,
|
||||
unsigned n_columns,
|
||||
OutputFlags flags,
|
||||
Set *output_fields) {
|
||||
Set *output_fields,
|
||||
size_t highlight[2]) {
|
||||
|
||||
const void *data;
|
||||
size_t length;
|
||||
@ -561,7 +596,7 @@ static int output_verbose(
|
||||
(((length < PRINT_CHAR_THRESHOLD) || flags & OUTPUT_FULL_WIDTH)
|
||||
&& utf8_is_printable(data, length))) {
|
||||
fprintf(f, " %s%.*s=", on, fieldlen, (const char*)data);
|
||||
print_multiline(f, 4 + fieldlen + 1, 0, OUTPUT_FULL_WIDTH, 0, c + 1, length - fieldlen - 1);
|
||||
print_multiline(f, 4 + fieldlen + 1, 0, OUTPUT_FULL_WIDTH, 0, c + 1, length - fieldlen - 1, NULL);
|
||||
fputs(off, f);
|
||||
} else {
|
||||
char bytes[FORMAT_BYTES_MAX];
|
||||
@ -590,7 +625,8 @@ static int output_export(
|
||||
OutputMode mode,
|
||||
unsigned n_columns,
|
||||
OutputFlags flags,
|
||||
Set *output_fields) {
|
||||
Set *output_fields,
|
||||
size_t highlight[2]) {
|
||||
|
||||
sd_id128_t boot_id;
|
||||
char sid[33];
|
||||
@ -732,7 +768,8 @@ static int output_json(
|
||||
OutputMode mode,
|
||||
unsigned n_columns,
|
||||
OutputFlags flags,
|
||||
Set *output_fields) {
|
||||
Set *output_fields,
|
||||
size_t highlight[2]) {
|
||||
|
||||
uint64_t realtime, monotonic;
|
||||
_cleanup_free_ char *cursor = NULL;
|
||||
@ -961,15 +998,22 @@ static int output_cat(
|
||||
OutputMode mode,
|
||||
unsigned n_columns,
|
||||
OutputFlags flags,
|
||||
Set *output_fields) {
|
||||
Set *output_fields,
|
||||
size_t highlight[2]) {
|
||||
|
||||
const void *data;
|
||||
size_t l;
|
||||
int r;
|
||||
const char *highlight_on = "", *highlight_off = "";
|
||||
|
||||
assert(j);
|
||||
assert(f);
|
||||
|
||||
if (flags & OUTPUT_COLOR) {
|
||||
highlight_on = ANSI_HIGHLIGHT_RED;
|
||||
highlight_off = ANSI_NORMAL;
|
||||
}
|
||||
|
||||
sd_journal_set_data_threshold(j, 0);
|
||||
|
||||
r = sd_journal_get_data(j, "MESSAGE", &data, &l);
|
||||
@ -987,7 +1031,17 @@ static int output_cat(
|
||||
|
||||
assert(l >= 8);
|
||||
|
||||
fwrite((const char*) data + 8, 1, l - 8, f);
|
||||
if (highlight && (flags & OUTPUT_COLOR)) {
|
||||
assert(highlight[0] <= highlight[1]);
|
||||
assert(highlight[1] <= l - 8);
|
||||
|
||||
fwrite((const char*) data + 8, 1, highlight[0], f);
|
||||
fwrite(highlight_on, 1, strlen(highlight_on), f);
|
||||
fwrite((const char*) data + 8 + highlight[0], 1, highlight[1] - highlight[0], f);
|
||||
fwrite(highlight_off, 1, strlen(highlight_off), f);
|
||||
fwrite((const char*) data + 8 + highlight[1], 1, l - 8 - highlight[1], f);
|
||||
} else
|
||||
fwrite((const char*) data + 8, 1, l - 8, f);
|
||||
fputc('\n', f);
|
||||
|
||||
return 0;
|
||||
@ -999,7 +1053,8 @@ static int (*output_funcs[_OUTPUT_MODE_MAX])(
|
||||
OutputMode mode,
|
||||
unsigned n_columns,
|
||||
OutputFlags flags,
|
||||
Set *output_fields) = {
|
||||
Set *output_fields,
|
||||
size_t highlight[2]) = {
|
||||
|
||||
[OUTPUT_SHORT] = output_short,
|
||||
[OUTPUT_SHORT_ISO] = output_short,
|
||||
@ -1023,6 +1078,7 @@ int output_journal(
|
||||
unsigned n_columns,
|
||||
OutputFlags flags,
|
||||
char **output_fields,
|
||||
size_t highlight[2],
|
||||
bool *ellipsized) {
|
||||
|
||||
int ret;
|
||||
@ -1043,7 +1099,7 @@ int output_journal(
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = output_funcs[mode](f, j, mode, n_columns, flags, fields);
|
||||
ret = output_funcs[mode](f, j, mode, n_columns, flags, fields, highlight);
|
||||
|
||||
if (ellipsized && ret > 0)
|
||||
*ellipsized = true;
|
||||
@ -1125,7 +1181,7 @@ static int show_journal(FILE *f,
|
||||
line++;
|
||||
maybe_print_begin_newline(f, &flags);
|
||||
|
||||
r = output_journal(f, j, mode, n_columns, flags, NULL, ellipsized);
|
||||
r = output_journal(f, j, mode, n_columns, flags, NULL, NULL, ellipsized);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
@ -39,6 +39,7 @@ int output_journal(
|
||||
unsigned n_columns,
|
||||
OutputFlags flags,
|
||||
char **output_fields,
|
||||
size_t highlight[2],
|
||||
bool *ellipsized);
|
||||
|
||||
int add_match_this_boot(sd_journal *j, const char *machine);
|
||||
|
@ -515,7 +515,6 @@ tests += [
|
||||
[],
|
||||
'', 'manual'],
|
||||
|
||||
|
||||
[['src/test/test-cgroup-mask.c',
|
||||
'src/test/test-helper.c'],
|
||||
[libcore,
|
||||
|
@ -28,24 +28,24 @@ int main(int argc, char *argv[]) {
|
||||
char *p;
|
||||
|
||||
assert_se(p = strdup("\tFoobar\tbar\twaldo\t"));
|
||||
assert_se(strip_tab_ansi(&p, NULL));
|
||||
assert_se(strip_tab_ansi(&p, NULL, NULL));
|
||||
fprintf(stdout, "<%s>\n", p);
|
||||
assert_se(streq(p, " Foobar bar waldo "));
|
||||
free(p);
|
||||
|
||||
assert_se(p = strdup(ANSI_HIGHLIGHT "Hello" ANSI_NORMAL ANSI_HIGHLIGHT_RED " world!" ANSI_NORMAL));
|
||||
assert_se(strip_tab_ansi(&p, NULL));
|
||||
assert_se(strip_tab_ansi(&p, NULL, NULL));
|
||||
fprintf(stdout, "<%s>\n", p);
|
||||
assert_se(streq(p, "Hello world!"));
|
||||
free(p);
|
||||
|
||||
assert_se(p = strdup("\x1B[\x1B[\t\x1B[" ANSI_HIGHLIGHT "\x1B[" "Hello" ANSI_NORMAL ANSI_HIGHLIGHT_RED " world!" ANSI_NORMAL));
|
||||
assert_se(strip_tab_ansi(&p, NULL));
|
||||
assert_se(strip_tab_ansi(&p, NULL, NULL));
|
||||
assert_se(streq(p, "\x1B[\x1B[ \x1B[\x1B[Hello world!"));
|
||||
free(p);
|
||||
|
||||
assert_se(p = strdup("\x1B[waldo"));
|
||||
assert_se(strip_tab_ansi(&p, NULL));
|
||||
assert_se(strip_tab_ansi(&p, NULL, NULL));
|
||||
assert_se(streq(p, "\x1B[waldo"));
|
||||
free(p);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user