1
1
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:
Zbigniew Jędrzejewski-Szmek 2018-01-12 14:31:49 +01:00
parent 6becf48ca3
commit 61c5f8a1f0
3 changed files with 131 additions and 75 deletions

View File

@ -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>

View File

@ -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;

View File

@ -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,