analyze/cat-config: add switch to print only "interesting" parts of config files

When looking at configuration, often a user wants to suppress the comments and
just look at the parts that actually configure something, roughly equivalent to
  systemd-analyze cat-config … | rg -v '^(#|;|$)
This switch implements this natively, skipping lines that start with a comment
character or only contain whitespace.

For formats that have section headers, section headers are skipped, if only
followed by stuff that would be skipped. (The last section header is printed
when we're about to print some actual output.)

Note that the caller doesn't know if the format has headers or not. We do format
type detection in pretty-print.c. So the caller only specifies tldr=true|false, and
conf_files_cat() figures out if the format has headers and whether those should
be handled specially.

The comments that show the file name are always printed, even if all of the file
is suppressed.

This is a partial answer to the discussions in
https://github.com/systemd/systemd/pull/28919,
https://github.com/systemd/systemd/pull/29248. If the default config is shown in
config files, the user can conveniently use '--tldr' to show the relevant parts.
This commit is contained in:
Zbigniew Jędrzejewski-Szmek
2023-10-24 15:59:34 +02:00
parent 80788a0bd4
commit 063c838206
13 changed files with 68 additions and 17 deletions
+10
View File
@@ -1470,6 +1470,16 @@ NR NAME SHA256
<xi:include href="version-info.xml" xpointer="v250"/></listitem>
</varlistentry>
<varlistentry>
<term><option>--tldr</option></term>
<listitem><para>With <command>cat-config</command>, only print the "interesting" parts of the
configuration files, skipping comments and empty lines and section headers followed only by
comments and empty lines.</para>
<xi:include href="version-info.xml" xpointer="v255"/></listitem>
</varlistentry>
<xi:include href="standard-options.xml" xpointer="help" />
<xi:include href="standard-options.xml" xpointer="version" />
<xi:include href="standard-options.xml" xpointer="no-pager" />
+1 -1
View File
@@ -35,7 +35,7 @@ int verb_cat_config(int argc, char *argv[], void *userdata) {
} else
t = *arg;
r = conf_files_cat(arg_root, t);
r = conf_files_cat(arg_root, t, arg_cat_flags);
if (r < 0)
return r;
}
+8
View File
@@ -91,6 +91,7 @@ DotMode arg_dot = DEP_ALL;
char **arg_dot_from_patterns = NULL, **arg_dot_to_patterns = NULL;
usec_t arg_fuzz = 0;
PagerFlags arg_pager_flags = 0;
CatFlags arg_cat_flags = 0;
BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
const char *arg_host = NULL;
RuntimeScope arg_runtime_scope = RUNTIME_SCOPE_SYSTEM;
@@ -270,6 +271,7 @@ static int help(int argc, char *argv[], void *userdata) {
" -h --help Show this help\n"
" --version Show package version\n"
" -q --quiet Do not emit hints\n"
" --tldr Skip comments and empty lines\n"
" --root=PATH Operate on an alternate filesystem root\n"
" --image=PATH Operate on disk image as filesystem root\n"
" --image-policy=POLICY Specify disk image dissection policy\n"
@@ -313,6 +315,7 @@ static int parse_argv(int argc, char *argv[]) {
ARG_PROFILE,
ARG_TABLE,
ARG_NO_LEGEND,
ARG_TLDR,
};
static const struct option options[] = {
@@ -346,6 +349,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "profile", required_argument, NULL, ARG_PROFILE },
{ "table", optional_argument, NULL, ARG_TABLE },
{ "no-legend", optional_argument, NULL, ARG_NO_LEGEND },
{ "tldr", no_argument, NULL, ARG_TLDR },
{}
};
@@ -535,6 +539,10 @@ static int parse_argv(int argc, char *argv[]) {
arg_legend = false;
break;
case ARG_TLDR:
arg_cat_flags = CAT_TLDR;
break;
case '?':
return -EINVAL;
+2
View File
@@ -7,6 +7,7 @@
#include "bus-util.h"
#include "json.h"
#include "pager.h"
#include "pretty-print.h"
#include "time-util.h"
#include "unit-file.h"
@@ -20,6 +21,7 @@ extern DotMode arg_dot;
extern char **arg_dot_from_patterns, **arg_dot_to_patterns;
extern usec_t arg_fuzz;
extern PagerFlags arg_pager_flags;
extern CatFlags arg_cat_flags;
extern BusTransport arg_transport;
extern const char *arg_host;
extern RuntimeScope arg_runtime_scope;
+1 -1
View File
@@ -232,7 +232,7 @@ static int run(int argc, char *argv[]) {
if (arg_cat_config) {
pager_open(arg_pager_flags);
return cat_files(NULL, files, 0);
return cat_files(NULL, files, /* flags= */ 0);
}
r = binfmt_mounted_warn();
+2 -2
View File
@@ -1537,7 +1537,7 @@ static int cat_settings(int argc, char *argv[], void *userdata) {
"Invalid settings file path '%s'.",
*name);
q = cat_files(*name, /* dropins = */ NULL, /* flags = */ 0);
q = cat_files(*name, /* dropins = */ NULL, /* flags = */ CAT_FORMAT_HAS_SECTIONS);
if (q < 0)
return r < 0 ? r : q;
continue;
@@ -1558,7 +1558,7 @@ static int cat_settings(int argc, char *argv[], void *userdata) {
return r < 0 ? r : q;
}
q = cat_files(path, /* dropins = */ NULL, /* flags = */ 0);
q = cat_files(path, /* dropins = */ NULL, /* flags = */ CAT_FORMAT_HAS_SECTIONS);
if (q < 0)
return r < 0 ? r : q;
}
+1 -1
View File
@@ -3298,7 +3298,7 @@ static int verb_cat(int argc, char *argv[], void *userdata) {
}
}
r = cat_files(path, dropins, /* flags = */ 0);
r = cat_files(path, dropins, /* flags = */ CAT_FORMAT_HAS_SECTIONS);
if (r < 0)
return ret < 0 ? ret : r;
}
+32 -7
View File
@@ -131,9 +131,9 @@ int terminal_urlify_man(const char *page, const char *section, char **ret) {
return terminal_urlify(url, text, ret);
}
static int cat_file(const char *filename, bool newline) {
static int cat_file(const char *filename, bool newline, CatFlags flags) {
_cleanup_fclose_ FILE *f = NULL;
_cleanup_free_ char *urlified = NULL;
_cleanup_free_ char *urlified = NULL, *section = NULL;
int r;
f = fopen(filename, "re");
@@ -160,6 +160,25 @@ static int cat_file(const char *filename, bool newline) {
if (r == 0)
break;
if (flags & CAT_TLDR) {
const char *t = skip_leading_chars(line, WHITESPACE);
if ((flags & CAT_FORMAT_HAS_SECTIONS) && *t == '[') {
/* The start of a section, let's not print it yet. */
free_and_replace(section, line);
continue;
}
if (IN_SET(*t, '#', ';', '\0'))
continue;
/* Before we print the actual line, print the last section header */
if (section) {
puts(section);
section = mfree(section);
}
}
puts(line);
}
@@ -170,13 +189,13 @@ int cat_files(const char *file, char **dropins, CatFlags flags) {
int r;
if (file) {
r = cat_file(file, false);
r = cat_file(file, /* newline= */ false, flags);
if (r < 0)
return log_warning_errno(r, "Failed to cat %s: %m", file);
}
STRV_FOREACH(path, dropins) {
r = cat_file(*path, file || path != dropins);
r = cat_file(*path, /* newline= */ file || path != dropins, flags);
if (r < 0)
return log_warning_errno(r, "Failed to cat %s: %m", *path);
}
@@ -208,7 +227,10 @@ void print_separator(void) {
static int guess_type(const char **name, char ***prefixes, bool *is_collection, const char **extension) {
/* Try to figure out if name is like tmpfiles.d/ or systemd/system-presets/,
* i.e. a collection of directories without a main config file. */
* i.e. a collection of directories without a main config file.
* Incidentally, all those formats don't use sections. So we return a single
* is_collection boolean, which also means that the format doesn't use sections.
*/
_cleanup_free_ char *n = NULL;
bool usr = false, run = false, coll = false;
@@ -274,7 +296,7 @@ static int guess_type(const char **name, char ***prefixes, bool *is_collection,
return 0;
}
int conf_files_cat(const char *root, const char *name) {
int conf_files_cat(const char *root, const char *name, CatFlags flags) {
_cleanup_strv_free_ char **dirs = NULL, **files = NULL;
_cleanup_free_ char *path = NULL;
char **prefixes = NULL; /* explicit initialization to appease gcc */
@@ -330,5 +352,8 @@ int conf_files_cat(const char *root, const char *name) {
return log_error_errno(r, "Failed to query file list: %m");
/* Show */
return cat_files(path, files, 0);
if (is_collection)
flags |= CAT_FORMAT_HAS_SECTIONS;
return cat_files(path, files, flags);
}
+3 -2
View File
@@ -15,11 +15,12 @@ int terminal_urlify_path(const char *path, const char *text, char **ret);
int terminal_urlify_man(const char *page, const char *section, char **ret);
typedef enum CatFlags {
CAT_DUMMY_FLAG, // This flag only exists a as a placeholder because empty enums are not allowed
CAT_FORMAT_HAS_SECTIONS = 1 << 0, /* Sections are meaningful for this file format */
CAT_TLDR = 1 << 1, /* Only print comments and relevant section headers */
} CatFlags;
int cat_files(const char *file, char **dropins, CatFlags flags);
int conf_files_cat(const char *root, const char *name);
int conf_files_cat(const char *root, const char *name, CatFlags flags);
#define RED_CROSS_MARK_MAX (STRLEN(ANSI_HIGHLIGHT_RED) + STRLEN("✗") + STRLEN(ANSI_NORMAL) + 1)
#define GREEN_CHECK_MARK_MAX (STRLEN(ANSI_HIGHLIGHT_GREEN) + STRLEN("✓") + STRLEN(ANSI_NORMAL) + 1)
+1 -1
View File
@@ -464,7 +464,7 @@ static int run(int argc, char *argv[]) {
if (arg_cat_config) {
pager_open(arg_pager_flags);
return cat_files(NULL, files, 0);
return cat_files(NULL, files, /* flags= */ 0);
}
STRV_FOREACH(f, files)
+1 -1
View File
@@ -93,7 +93,7 @@ int verb_cat(int argc, char *argv[], void *userdata) {
arg_runtime_scope == RUNTIME_SCOPE_SYSTEM ? "" : " --user",
ansi_normal());
r = cat_files(fragment_path, dropin_paths, 0);
r = cat_files(fragment_path, dropin_paths, /* flags= */ CAT_FORMAT_HAS_SECTIONS);
if (r < 0)
return r;
}
+1 -1
View File
@@ -2044,7 +2044,7 @@ static int cat_config(void) {
pager_open(arg_pager_flags);
return cat_files(NULL, files, 0);
return cat_files(NULL, files, /* flags= */ 0);
}
static int help(void) {
+5
View File
@@ -165,6 +165,11 @@ systemd-analyze cat-config /etc/systemd/system.conf >/dev/null
systemd-analyze cat-config systemd/system.conf systemd/journald.conf >/dev/null
systemd-analyze cat-config systemd/system.conf foo/bar systemd/journald.conf >/dev/null
systemd-analyze cat-config foo/bar
systemd-analyze cat-config --tldr systemd/system.conf >/dev/null
systemd-analyze cat-config --tldr /etc/systemd/system.conf >/dev/null
systemd-analyze cat-config --tldr systemd/system.conf systemd/journald.conf >/dev/null
systemd-analyze cat-config --tldr systemd/system.conf foo/bar systemd/journald.conf >/dev/null
systemd-analyze cat-config --tldr foo/bar
# security
systemd-analyze security
systemd-analyze security --json=off