mirror of
https://github.com/systemd/systemd-stable.git
synced 2024-10-27 01:55:32 +03:00
journalctl: make matching optionally case sensitive
Case sensitive or case insensitive matching can be requested using --case-sensitive[=yes|no]. Unless specified, matching is case sensitive if the pattern contains any uppercase letters, and case insensitive otherwise. This matches what forward-search does in emacs, and recently also --ignore-case in less. This works surprisingly well, because usually when one is wants to do case-sensitive matching, the pattern is usually camel-cased. In the less frequent case when case-sensitive matching is required with an all-lowercase pattern, --case-sensitive can be used to override the automatic logic.
This commit is contained in:
parent
6becf48ca3
commit
61c5f8a1f0
@ -586,7 +586,19 @@
|
|||||||
field matches the specified regular expression. PERL-compatible regular expressions
|
field matches the specified regular expression. PERL-compatible regular expressions
|
||||||
are used, see
|
are used, see
|
||||||
<citerefentry><refentrytitle>pcre2pattern</refentrytitle><manvolnum>3</manvolnum></citerefentry>
|
<citerefentry><refentrytitle>pcre2pattern</refentrytitle><manvolnum>3</manvolnum></citerefentry>
|
||||||
for a detailed description of the syntax.</para></listitem>
|
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>
|
||||||
|
|
||||||
<varlistentry>
|
<varlistentry>
|
||||||
|
@ -83,6 +83,7 @@
|
|||||||
|
|
||||||
#if HAVE_PCRE2
|
#if HAVE_PCRE2
|
||||||
DEFINE_TRIVIAL_CLEANUP_FUNC(pcre2_match_data*, pcre2_match_data_free);
|
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) {
|
static int pattern_compile(const char *pattern, unsigned flags, pcre2_code **out) {
|
||||||
int errorcode, r;
|
int errorcode, r;
|
||||||
@ -159,7 +160,9 @@ static usec_t arg_vacuum_time = 0;
|
|||||||
static char **arg_output_fields = NULL;
|
static char **arg_output_fields = NULL;
|
||||||
|
|
||||||
#if HAVE_PCRE2
|
#if HAVE_PCRE2
|
||||||
static pcre2_code *arg_pattern = NULL;
|
static const char *arg_pattern = NULL;
|
||||||
|
static pcre2_code *arg_compiled_pattern = NULL;
|
||||||
|
static int arg_case_sensitive = -1; /* -1 means be smart */
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static enum {
|
static enum {
|
||||||
@ -316,68 +319,69 @@ static void help(void) {
|
|||||||
printf("%s [OPTIONS...] [MATCHES...]\n\n"
|
printf("%s [OPTIONS...] [MATCHES...]\n\n"
|
||||||
"Query the journal.\n\n"
|
"Query the journal.\n\n"
|
||||||
"Options:\n"
|
"Options:\n"
|
||||||
" --system Show the system journal\n"
|
" --system Show the system journal\n"
|
||||||
" --user Show the user journal for the current user\n"
|
" --user Show the user journal for the current user\n"
|
||||||
" -M --machine=CONTAINER Operate on local container\n"
|
" -M --machine=CONTAINER Operate on local container\n"
|
||||||
" -S --since=DATE Show entries not older than the specified date\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"
|
" -U --until=DATE Show entries not newer than the specified date\n"
|
||||||
" -c --cursor=CURSOR Show entries starting at the specified cursor\n"
|
" -c --cursor=CURSOR Show entries starting at the specified cursor\n"
|
||||||
" --after-cursor=CURSOR Show entries after the specified cursor\n"
|
" --after-cursor=CURSOR Show entries after the specified cursor\n"
|
||||||
" --show-cursor Print the cursor after all the entries\n"
|
" --show-cursor Print the cursor after all the entries\n"
|
||||||
" -b --boot[=ID] Show current boot or the specified boot\n"
|
" -b --boot[=ID] Show current boot or the specified boot\n"
|
||||||
" --list-boots Show terse information about recorded boots\n"
|
" --list-boots Show terse information about recorded boots\n"
|
||||||
" -k --dmesg Show kernel message log from the current boot\n"
|
" -k --dmesg Show kernel message log from the current boot\n"
|
||||||
" -u --unit=UNIT Show logs from the specified unit\n"
|
" -u --unit=UNIT Show logs from the specified unit\n"
|
||||||
" --user-unit=UNIT Show logs from the specified user unit\n"
|
" --user-unit=UNIT Show logs from the specified user unit\n"
|
||||||
" -t --identifier=STRING Show entries with the specified syslog identifier\n"
|
" -t --identifier=STRING Show entries with the specified syslog identifier\n"
|
||||||
" -p --priority=RANGE Show entries with the specified priority\n"
|
" -p --priority=RANGE Show entries with the specified priority\n"
|
||||||
" -g --grep=PATTERN Show entries with MESSSAGE matching PATTERN\n"
|
" -g --grep=PATTERN Show entries with MESSSAGE matching PATTERN\n"
|
||||||
" -e --pager-end Immediately jump to the end in the pager\n"
|
" --case-sensitive[=BOOL] Force case sensitive or insenstive matching\n"
|
||||||
" -f --follow Follow the journal\n"
|
" -e --pager-end Immediately jump to the end in the pager\n"
|
||||||
" -n --lines[=INTEGER] Number of journal entries to show\n"
|
" -f --follow Follow the journal\n"
|
||||||
" --no-tail Show all lines, even in follow mode\n"
|
" -n --lines[=INTEGER] Number of journal entries to show\n"
|
||||||
" -r --reverse Show the newest entries first\n"
|
" --no-tail Show all lines, even in follow mode\n"
|
||||||
" -o --output=STRING Change journal output mode (short, short-precise,\n"
|
" -r --reverse Show the newest entries first\n"
|
||||||
" short-iso, short-iso-precise, short-full,\n"
|
" -o --output=STRING Change journal output mode (short, short-precise,\n"
|
||||||
" short-monotonic, short-unix, verbose, export,\n"
|
" short-iso, short-iso-precise, short-full,\n"
|
||||||
" json, json-pretty, json-sse, cat)\n"
|
" short-monotonic, short-unix, verbose, export,\n"
|
||||||
" --output-fields=LIST Select fields to print in verbose/export/json modes\n"
|
" json, json-pretty, json-sse, cat)\n"
|
||||||
" --utc Express time in Coordinated Universal Time (UTC)\n"
|
" --output-fields=LIST Select fields to print in verbose/export/json modes\n"
|
||||||
" -x --catalog Add message explanations where available\n"
|
" --utc Express time in Coordinated Universal Time (UTC)\n"
|
||||||
" --no-full Ellipsize fields\n"
|
" -x --catalog Add message explanations where available\n"
|
||||||
" -a --all Show all fields, including long and unprintable\n"
|
" --no-full Ellipsize fields\n"
|
||||||
" -q --quiet Do not show info messages and privilege warning\n"
|
" -a --all Show all fields, including long and unprintable\n"
|
||||||
" --no-pager Do not pipe output into a pager\n"
|
" -q --quiet Do not show info messages and privilege warning\n"
|
||||||
" --no-hostname Suppress output of hostname field\n"
|
" --no-pager Do not pipe output into a pager\n"
|
||||||
" -m --merge Show entries from all available journals\n"
|
" --no-hostname Suppress output of hostname field\n"
|
||||||
" -D --directory=PATH Show journal files from directory\n"
|
" -m --merge Show entries from all available journals\n"
|
||||||
" --file=PATH Show journal file\n"
|
" -D --directory=PATH Show journal files from directory\n"
|
||||||
" --root=ROOT Operate on files below a root directory\n"
|
" --file=PATH Show journal file\n"
|
||||||
|
" --root=ROOT Operate on files below a root directory\n"
|
||||||
#if HAVE_GCRYPT
|
#if HAVE_GCRYPT
|
||||||
" --interval=TIME Time interval for changing the FSS sealing key\n"
|
" --interval=TIME Time interval for changing the FSS sealing key\n"
|
||||||
" --verify-key=KEY Specify FSS verification key\n"
|
" --verify-key=KEY Specify FSS verification key\n"
|
||||||
" --force Override of the FSS key pair with --setup-keys\n"
|
" --force Override of the FSS key pair with --setup-keys\n"
|
||||||
#endif
|
#endif
|
||||||
"\nCommands:\n"
|
"\nCommands:\n"
|
||||||
" -h --help Show this help text\n"
|
" -h --help Show this help text\n"
|
||||||
" --version Show package version\n"
|
" --version Show package version\n"
|
||||||
" -N --fields List all field names currently used\n"
|
" -N --fields List all field names currently used\n"
|
||||||
" -F --field=FIELD List all values that a specified field takes\n"
|
" -F --field=FIELD List all values that a specified field takes\n"
|
||||||
" --disk-usage Show total disk usage of all journal files\n"
|
" --disk-usage Show total disk usage of all journal files\n"
|
||||||
" --vacuum-size=BYTES Reduce disk usage below specified size\n"
|
" --vacuum-size=BYTES Reduce disk usage below specified size\n"
|
||||||
" --vacuum-files=INT Leave only the specified number of journal files\n"
|
" --vacuum-files=INT Leave only the specified number of journal files\n"
|
||||||
" --vacuum-time=TIME Remove journal files older than specified time\n"
|
" --vacuum-time=TIME Remove journal files older than specified time\n"
|
||||||
" --verify Verify journal file consistency\n"
|
" --verify Verify journal file consistency\n"
|
||||||
" --sync Synchronize unwritten journal messages to disk\n"
|
" --sync Synchronize unwritten journal messages to disk\n"
|
||||||
" --flush Flush all journal data from /run into /var\n"
|
" --flush Flush all journal data from /run into /var\n"
|
||||||
" --rotate Request immediate rotation of the journal files\n"
|
" --rotate Request immediate rotation of the journal files\n"
|
||||||
" --header Show journal header information\n"
|
" --header Show journal header information\n"
|
||||||
" --list-catalog Show all message IDs in the catalog\n"
|
" --list-catalog Show all message IDs in the catalog\n"
|
||||||
" --dump-catalog Show entries in the message catalog\n"
|
" --dump-catalog Show entries in the message catalog\n"
|
||||||
" --update-catalog Update the message catalog database\n"
|
" --update-catalog Update the message catalog database\n"
|
||||||
" --new-id128 Generate a new 128-bit ID\n"
|
" --new-id128 Generate a new 128-bit ID\n"
|
||||||
#if HAVE_GCRYPT
|
#if HAVE_GCRYPT
|
||||||
" --setup-keys Generate a new FSS key pair\n"
|
" --setup-keys Generate a new FSS key pair\n"
|
||||||
#endif
|
#endif
|
||||||
, program_invocation_short_name);
|
, program_invocation_short_name);
|
||||||
}
|
}
|
||||||
@ -409,6 +413,7 @@ static int parse_argv(int argc, char *argv[]) {
|
|||||||
ARG_DUMP_CATALOG,
|
ARG_DUMP_CATALOG,
|
||||||
ARG_UPDATE_CATALOG,
|
ARG_UPDATE_CATALOG,
|
||||||
ARG_FORCE,
|
ARG_FORCE,
|
||||||
|
ARG_CASE_SENSITIVE,
|
||||||
ARG_UTC,
|
ARG_UTC,
|
||||||
ARG_SYNC,
|
ARG_SYNC,
|
||||||
ARG_FLUSH,
|
ARG_FLUSH,
|
||||||
@ -449,6 +454,7 @@ static int parse_argv(int argc, char *argv[]) {
|
|||||||
{ "identifier", required_argument, NULL, 't' },
|
{ "identifier", required_argument, NULL, 't' },
|
||||||
{ "priority", required_argument, NULL, 'p' },
|
{ "priority", required_argument, NULL, 'p' },
|
||||||
{ "grep", required_argument, NULL, 'g' },
|
{ "grep", required_argument, NULL, 'g' },
|
||||||
|
{ "case-sensitive", optional_argument, NULL, ARG_CASE_SENSITIVE },
|
||||||
{ "setup-keys", no_argument, NULL, ARG_SETUP_KEYS },
|
{ "setup-keys", no_argument, NULL, ARG_SETUP_KEYS },
|
||||||
{ "interval", required_argument, NULL, ARG_INTERVAL },
|
{ "interval", required_argument, NULL, ARG_INTERVAL },
|
||||||
{ "verify", no_argument, NULL, ARG_VERIFY },
|
{ "verify", no_argument, NULL, ARG_VERIFY },
|
||||||
@ -801,20 +807,23 @@ static int parse_argv(int argc, char *argv[]) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if HAVE_PCRE2
|
#if HAVE_PCRE2
|
||||||
case 'g': {
|
case 'g':
|
||||||
if (arg_pattern) {
|
arg_pattern = optarg;
|
||||||
pcre2_code_free(arg_pattern);
|
break;
|
||||||
arg_pattern = NULL;
|
|
||||||
}
|
case ARG_CASE_SENSITIVE:
|
||||||
r = pattern_compile(optarg, 0, &arg_pattern);
|
if (optarg) {
|
||||||
if (r < 0)
|
r = parse_boolean(optarg);
|
||||||
return r;
|
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;
|
break;
|
||||||
}
|
|
||||||
|
|
||||||
#else
|
#else
|
||||||
case 'g':
|
case 'g':
|
||||||
|
case ARG_CASE_SENSITIVE:
|
||||||
return log_error("Compiled without pattern matching support");
|
return log_error("Compiled without pattern matching support");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -973,6 +982,42 @@ static int parse_argv(int argc, char *argv[]) {
|
|||||||
arg_system_units = strv_free(arg_system_units);
|
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;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2525,7 +2570,7 @@ int main(int argc, char *argv[]) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if HAVE_PCRE2
|
#if HAVE_PCRE2
|
||||||
if (arg_pattern) {
|
if (arg_compiled_pattern) {
|
||||||
_cleanup_(pcre2_match_data_freep) pcre2_match_data *md = NULL;
|
_cleanup_(pcre2_match_data_freep) pcre2_match_data *md = NULL;
|
||||||
const void *message;
|
const void *message;
|
||||||
size_t len;
|
size_t len;
|
||||||
@ -2547,7 +2592,7 @@ int main(int argc, char *argv[]) {
|
|||||||
|
|
||||||
assert_se(message = startswith(message, "MESSAGE="));
|
assert_se(message = startswith(message, "MESSAGE="));
|
||||||
|
|
||||||
r = pcre2_match(arg_pattern,
|
r = pcre2_match(arg_compiled_pattern,
|
||||||
message,
|
message,
|
||||||
len - strlen("MESSAGE="),
|
len - strlen("MESSAGE="),
|
||||||
0, /* start at offset 0 in the subject */
|
0, /* start at offset 0 in the subject */
|
||||||
@ -2631,8 +2676,8 @@ finish:
|
|||||||
free(arg_verify_key);
|
free(arg_verify_key);
|
||||||
|
|
||||||
#if HAVE_PCRE2
|
#if HAVE_PCRE2
|
||||||
if (arg_pattern)
|
if (arg_compiled_pattern)
|
||||||
pcre2_code_free(arg_pattern);
|
pcre2_code_free(arg_compiled_pattern);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
|
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
|
||||||
|
@ -500,7 +500,6 @@ tests += [
|
|||||||
[],
|
[],
|
||||||
'', 'manual'],
|
'', 'manual'],
|
||||||
|
|
||||||
|
|
||||||
[['src/test/test-cgroup-mask.c',
|
[['src/test/test-cgroup-mask.c',
|
||||||
'src/test/test-helper.c'],
|
'src/test/test-helper.c'],
|
||||||
[libcore,
|
[libcore,
|
||||||
|
Loading…
Reference in New Issue
Block a user