On Wed, 2011-01-12 at 15:02 +0100, Denys Vlasenko wrote:
Basically, "include GLOB_PATTERN" line expands
GLOB_PATTERN
and recurses int every matching file.
Another small change makes it possible to have one rule
to span lines: our current default abrt_event.conf has
sosreport invocation which looks very ugly. Patch rewrites
it in multi-line form.
Patch is tested by adding include post-create.d/*.conf
to /etc/abrt/abrt_event.conf, running
abrt-handle-crashdump -vvv -d /var/spool/abrt/ccpp-1291820534-28933 -e post-create
and observing the results.
The comment in src/daemon/abrt_event.conf is modified to explain
the recognized syntax.
Please review.
Fix for list_possible_events (wasn't saving recursion results).
--
vda
diff -x '*.po' -d -urpN abrt.7/src/daemon/abrt_event.conf
abrt.8/src/daemon/abrt_event.conf
--- abrt.7/src/daemon/abrt_event.conf 2011-01-11 14:40:43.000000000 +0100
+++ abrt.8/src/daemon/abrt_event.conf 2011-01-12 14:55:35.348507882 +0100
@@ -1,17 +1,31 @@
-# This table specifies which programs should be run
+# This configuration file specifies which programs should be run
# when the specified event occurs in crash dump lifetime.
#
# Example:
# EVENT=post-create { pwd; date; }>/tmp/dt; echo $HOSTNAME `uname -r`
#
-# Each line may have conditions to be checked
-# before the program is run.
+# Rule starts with a line with non-space leading character.
+# All subsequent lines which start with space or tab form one rule.
+# Note that separating newline is *retained*. Example:
+# EVENT=post-create date >/tmp/dt # semicolon is not needed here!
+# echo $HOSTNAME `uname -r`
+#
+# Rules may be commented out with #. One # is sufficient to comment out
+# even a multi-line rule (no need to comment out every line).
+#
+# Rule of the form "include GLOB_PATTERN" recurses to each file which matches
+# GLOB_PATTERN. Example: "include post-create.d/*.conf"
+#
+# Any other rules specify which programs to run on the crash dump.
+#
+# Each rule may have conditions to be checked before the program is run.
#
# Conditions have form VAR=VAL, where VAR is either word "EVENT"
# or a name of crash dump element to be checked (for example,
# "executable", "package", hostname" etc).
#
-# If all conditions match, the program is run in the shell.
+# If all conditions match, the remaining part of the rule
+# (the "program" part) is run in the shell.
# All shell language constructs are valid.
# All stdout and stderr output is captured and passed to abrt
# and possibly to abrt's frontends and shown to the user.
@@ -22,7 +36,7 @@
# the error message indicating the reason of the failure,
# and may be used by abrt as such.
#
-# If the program terminates successfully, next line is read
+# If the program terminates successfully, next rule is read
# and processed. This process is repeated until the end of this file.
# abrt-action-analyze-c needs package name, save package data first
@@ -38,7 +52,19 @@ EVENT=post-create analyzer=Kerneloops
# user interaction, uncomment this line:
#EVENT=post-create analyzer=Kerneloops abrt-action-kerneloops
# Example: if you want to save sosreport immediately at the moment of a crash:
-#EVENT=post-create nice sosreport --tmp-dir "$DUMP_DIR" --batch
--only=anaconda --only=bootloader --only=devicemapper --only=filesys --only=hardware
--only=kernel --only=libraries --only=memory --only=networking --only=nfsserver --only=pam
--only=process --only=rpm -k rpm.rpmva=off --only=ssh --only=startup --only=yum
--only=general --only=x11 && { rm sosreport*.md5; mv sosreport*.tar.bz2
sosreport.tar.bz2; mv sosreport*.tar.xz sosreport.tar.xz; true; } 2>/dev/null
+#EVENT=post-create
+ nice sosreport --tmp-dir "$DUMP_DIR" --batch \
+ --only=anaconda --only=bootloader --only=devicemapper \
+ --only=filesys --only=hardware --only=kernel --only=libraries \
+ --only=memory --only=networking --only=nfsserver --only=pam \
+ --only=process --only=rpm -k rpm.rpmva=off --only=ssh \
+ --only=startup --only=yum --only=general --only=x11 \
+ && {
+ rm sosreport*.md5
+ mv sosreport*.tar.bz2 sosreport.tar.bz2
+ mv sosreport*.tar.xz sosreport.tar.xz
+ true
+ } 2>/dev/null
#TODO: implement this (or add this functionality to abrt-action-install-debuginfo):
#EVENT=analyze analyzer=CCpp backtrace= trim-debuginfo-cache /var/cache/abrt-di 4096m
@@ -46,12 +72,14 @@ EVENT=post-create analyzer=Kerneloops
# in the third argument (its format is CACHEDIR[:DEBUGINFODIR...]).
# For example, you can specify a network-mounted shared store
# of all debuginfos this way.
-EVENT=analyze analyzer=CCpp backtrace= abrt-action-install-debuginfo.py
"--core=$DUMP_DIR/coredump" "--tmpdir=/var/run/abrt/$$-$RANDOM"
--cache=/var/cache/abrt-di
+EVENT=analyze analyzer=CCpp backtrace=
+ abrt-action-install-debuginfo.py --core="$DUMP_DIR/coredump"
--tmpdir="/var/run/abrt/$$-$RANDOM" --cache=/var/cache/abrt-di
EVENT=analyze analyzer=CCpp backtrace= abrt-action-generate-backtrace
# Same as "analyze", but executed when user requests "refresh" in
GUI
#EVENT=reanalyze analyzer=CCpp trim-debuginfo-cache /var/cache/abrt-di 4096m
-EVENT=reanalyze analyzer=CCpp abrt-action-install-debuginfo.py
"--core=$DUMP_DIR/coredump" "--tmpdir=/var/run/abrt/$$-$RANDOM"
--cache=/var/cache/abrt-di
+EVENT=reanalyze analyzer=CCpp
+ abrt-action-install-debuginfo.py --core="$DUMP_DIR/coredump"
--tmpdir="/var/run/abrt/$$-$RANDOM" --cache=/var/cache/abrt-di
EVENT=reanalyze analyzer=CCpp abrt-action-generate-backtrace
EVENT=report analyzer=Kerneloops abrt-action-kerneloops
diff -x '*.po' -d -urpN abrt.7/src/lib/run_event.c abrt.8/src/lib/run_event.c
--- abrt.7/src/lib/run_event.c 2011-01-06 17:24:09.000000000 +0100
+++ abrt.8/src/lib/run_event.c 2011-01-12 17:23:48.263967981 +0100
@@ -16,6 +16,7 @@
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
+#include <glob.h>
#include "abrtlib.h"
struct run_event_state *new_run_event_state()
@@ -28,14 +29,15 @@ void free_run_event_state(struct run_eve
free(state);
}
-int run_event_on_dir_name(struct run_event_state *state,
+static int run_event_helper(struct run_event_state *state,
const char *dump_dir_name,
- const char *event
+ const char *event,
+ const char *conf_file_name
) {
- FILE *conffile = fopen(CONF_DIR"/abrt_event.conf", "r");
+ FILE *conffile = fopen(conf_file_name, "r");
if (!conffile)
{
- error_msg("Can't open '%s'",
CONF_DIR"/abrt_event.conf");
+ error_msg("Can't open '%s'", conf_file_name);
return 1;
}
close_on_exec_on(fileno(conffile));
@@ -53,9 +55,21 @@ int run_event_on_dir_name(struct run_eve
/* Read, match, and execute lines from abrt_event.conf */
int retval = -1;
struct dump_dir *dd = NULL;
- char *line;
- while ((line = xmalloc_fgetline(conffile)) != NULL)
+ char *next_line = xmalloc_fgetline(conffile);
+ while (next_line)
{
+ char *line = next_line;
+ while (1)
+ {
+ next_line = xmalloc_fgetline(conffile);
+ if (!next_line || !isblank(next_line[0]))
+ break;
+ char *old_line = line;
+ line = xasprintf("%s\n%s", line, next_line);
+ free(old_line);
+ free(next_line);
+ }
+
/* Line has form: [VAR=VAL]... PROG [ARGS] */
char *p = skip_whitespace(line);
if (*p == '\0' || *p == '#')
@@ -63,6 +77,43 @@ int run_event_on_dir_name(struct run_eve
VERB3 log("%s: line '%s'", __func__, p);
+ if (strncmp(p, "include", strlen("include")) == 0 &&
isblank(p[strlen("include")]))
+ {
+ /* include GLOB_PATTERN */
+ p = skip_whitespace(p + strlen("include"));
+
+ const char *last_slash;
+ char *name_to_glob;
+ if (*p != '/'
+ && (last_slash = strrchr(conf_file_name, '/')) != NULL
+ )
+ /* GLOB_PATTERN is relative, and this include is in path/to/file.conf
+ * Construct path/to/GLOB_PATTERN:
+ */
+ name_to_glob = xasprintf("%.*s%s", (int)(last_slash -
conf_file_name + 1), conf_file_name, p);
+ else
+ /* Either GLOB_PATTERN is absolute, or this include is in file.conf
+ * (no slashes in its name). Use unchanged GLOB_PATTERN:
+ */
+ name_to_glob = xstrdup(p);
+
+ glob_t globbuf;
+ memset(&globbuf, 0, sizeof(globbuf));
+ VERB3 log("%s: globbing '%s'", __func__, name_to_glob);
+ glob(name_to_glob, 0, NULL, &globbuf);
+ free(name_to_glob);
+ char **name = globbuf.gl_pathv;
+ if (name) while (*name)
+ {
+ VERB3 log("%s: recursing into '%s'", __func__, *name);
+ run_event_helper(state, dump_dir_name, event, *name);
+ VERB3 log("%s: returned from '%s'", __func__, *name);
+ name++;
+ }
+ globfree(&globbuf);
+ goto next_line;
+ }
+
while (1) /* word loop */
{
char *end_word = skip_non_whitespace(p);
@@ -93,7 +144,11 @@ int run_event_on_dir_name(struct run_eve
{
dd = dd_opendir(dump_dir_name, /*flags:*/ 0);
if (!dd)
+ {
+ free(line);
+ free(next_line);
goto stop; /* error (note: dd_opendir logged error msg) */
+ }
}
real_val = malloced_val = dd_load_text_ext(dd, p, DD_FAIL_QUIETLY);
}
@@ -176,13 +231,19 @@ int run_event_on_dir_name(struct run_eve
} /* end of line loop */
stop:
- free(line);
dd_close(dd);
fclose(conffile);
return retval;
}
+int run_event_on_dir_name(struct run_event_state *state,
+ const char *dump_dir_name,
+ const char *event
+) {
+ return run_event_helper(state, dump_dir_name, event,
CONF_DIR"/abrt_event.conf");
+}
+
int run_event_on_crash_data(struct run_event_state *state, crash_data_t *data, const char
*event)
{
struct dump_dir *dd = create_dump_dir_from_crash_data(data, NULL);
@@ -202,13 +263,19 @@ int run_event_on_crash_data(struct run_e
return r;
}
-char *list_possible_events(struct dump_dir *dd, const char *dump_dir_name, const char
*pfx)
-{
- FILE *conffile = fopen(CONF_DIR"/abrt_event.conf", "r");
+
+/* TODO: very similar to run_event_helper, try to combine into one fn? */
+static void list_possible_events_helper(struct strbuf *result,
+ struct dump_dir *dd,
+ const char *dump_dir_name,
+ const char *pfx,
+ const char *conf_file_name
+) {
+ FILE *conffile = fopen(conf_file_name, "r");
if (!conffile)
{
- error_msg("Can't open '%s'",
CONF_DIR"/abrt_event.conf");
- return NULL;
+ error_msg("Can't open '%s'", conf_file_name);
+ return;
}
/* We check "dump_dir_name == NULL" later.
@@ -219,10 +286,21 @@ char *list_possible_events(struct dump_d
dump_dir_name = NULL;
unsigned pfx_len = strlen(pfx);
- struct strbuf *result = strbuf_new();
- char *line;
- while ((line = xmalloc_fgetline(conffile)) != NULL)
+ char *next_line = xmalloc_fgetline(conffile);
+ while (next_line)
{
+ char *line = next_line;
+ while (1)
+ {
+ next_line = xmalloc_fgetline(conffile);
+ if (!next_line || !isblank(next_line[0]))
+ break;
+ char *old_line = line;
+ line = xasprintf("%s\n%s", line, next_line);
+ free(old_line);
+ free(next_line);
+ }
+
/* Line has form: [VAR=VAL]... PROG [ARGS] */
char *p = skip_whitespace(line);
if (*p == '\0' || *p == '#')
@@ -230,6 +308,43 @@ char *list_possible_events(struct dump_d
VERB3 log("%s: line '%s'", __func__, p);
+ if (strncmp(p, "include", strlen("include")) == 0 &&
isblank(p[strlen("include")]))
+ {
+ /* include GLOB_PATTERN */
+ p = skip_whitespace(p + strlen("include"));
+
+ const char *last_slash;
+ char *name_to_glob;
+ if (*p != '/'
+ && (last_slash = strrchr(conf_file_name, '/')) != NULL
+ )
+ /* GLOB_PATTERN is relative, and this include is in path/to/file.conf
+ * Construct path/to/GLOB_PATTERN:
+ */
+ name_to_glob = xasprintf("%.*s%s", (int)(last_slash -
conf_file_name + 1), conf_file_name, p);
+ else
+ /* Either GLOB_PATTERN is absolute, or this include is in file.conf
+ * (no slashes in its name). Use unchanged GLOB_PATTERN:
+ */
+ name_to_glob = xstrdup(p);
+
+ glob_t globbuf;
+ memset(&globbuf, 0, sizeof(globbuf));
+ VERB3 log("%s: globbing '%s'", __func__, name_to_glob);
+ glob(name_to_glob, 0, NULL, &globbuf);
+ free(name_to_glob);
+ char **name = globbuf.gl_pathv;
+ if (name) while (*name)
+ {
+ VERB3 log("%s: recursing into '%s'", __func__, *name);
+ list_possible_events_helper(result, dd, dump_dir_name, pfx, *name);
+ VERB3 log("%s: returned from '%s'", __func__, *name);
+ name++;
+ }
+ globfree(&globbuf);
+ goto next_line;
+ }
+
while (1) /* word loop */
{
char *end_word = skip_non_whitespace(p);
@@ -262,7 +377,11 @@ char *list_possible_events(struct dump_d
goto next_word;
dd = dd_opendir(dump_dir_name, /*flags:*/ 0);
if (!dd)
+ {
+ free(line);
+ free(next_line);
goto stop; /* error (note: dd_opendir logged error msg) */
+ }
}
char *real_val = dd_load_text_ext(dd, p, DD_FAIL_QUIETLY);
/* Does VAL match? */
@@ -294,10 +413,14 @@ char *list_possible_events(struct dump_d
} /* end of line loop */
stop:
- free(line);
if (dump_dir_name != NULL)
dd_close(dd);
fclose(conffile);
+}
+char *list_possible_events(struct dump_dir *dd, const char *dump_dir_name, const char
*pfx)
+{
+ struct strbuf *result = strbuf_new();
+ list_possible_events_helper(result, dd, dump_dir_name, pfx,
CONF_DIR"/abrt_event.conf");
return strbuf_free_nobuf(result);
}