[PATCH 2/8] abrt-server: add support for "POST /creation_notification"
Denys Vlasenko
dvlasenk at redhat.com
Fri Jul 26 09:23:51 UTC 2013
The body of this request contains just the dirname of the new
problem dir.
Receipt of this command makes abrt-server run "post-create"
and then "notify[-dup]" on this dir.
The code for running "post-create" is taked from abtrd,
it is to be removed there later.
Signed-off-by: Denys Vlasenko <dvlasenk at redhat.com>
---
src/daemon/Makefile.am | 3 +-
src/daemon/abrt-server.c | 299 +++++++++++++++++++++++++++++++++++++++++++----
2 files changed, 277 insertions(+), 25 deletions(-)
diff --git a/src/daemon/Makefile.am b/src/daemon/Makefile.am
index 74ff07f..bb8a5f6 100644
--- a/src/daemon/Makefile.am
+++ b/src/daemon/Makefile.am
@@ -40,9 +40,10 @@ abrt_server_SOURCES = \
abrt_server_CPPFLAGS = \
-I$(srcdir)/../include \
-I$(srcdir)/../lib \
+ -DDEFAULT_DUMP_DIR_MODE=$(DEFAULT_DUMP_DIR_MODE) \
+ -DLIBEXEC_DIR=\"$(libexecdir)\" \
$(GLIB_CFLAGS) \
$(LIBREPORT_CFLAGS) \
- -DDEFAULT_DUMP_DIR_MODE=$(DEFAULT_DUMP_DIR_MODE) \
-D_GNU_SOURCE
abrt_server_LDADD = \
../lib/libabrt.la \
diff --git a/src/daemon/abrt-server.c b/src/daemon/abrt-server.c
index 27bb6d6..23552b3 100644
--- a/src/daemon/abrt-server.c
+++ b/src/daemon/abrt-server.c
@@ -168,22 +168,30 @@ static int create_problem_dir(GHashTable *problem_info, unsigned pid)
return 201; /* Created */
}
-/* Remove dump dir */
-static int delete_path(const char *dump_dir_name)
+static bool dir_is_in_dump_location(const char *dump_dir_name)
{
unsigned len = strlen(g_settings_dump_location);
- /* If doesn't start with "g_settings_dump_location/"... */
- if (strncmp(dump_dir_name, g_settings_dump_location, len) != 0
- || dump_dir_name[len] != '/'
- /* or contains "/." anywhere (-> might contain ".." component) */
- || strstr(dump_dir_name + len, "/.")
+ if (strncmp(dump_dir_name, g_settings_dump_location, len) == 0
+ && dump_dir_name[len] == '/'
+ /* must not contain "/." anywhere (IOW: disallow ".." component) */
+ && !strstr(dump_dir_name + len, "/.")
) {
+ return 1;
+ }
+ return 0;
+}
+
+/* Remove dump dir */
+static int delete_path(const char *dump_dir_name)
+{
+ /* If doesn't start with "g_settings_dump_location/"... */
+ if (!dir_is_in_dump_location(dump_dir_name))
+ {
/* Then refuse to operate on it (someone is attacking us??) */
error_msg("Bad problem directory name '%s', not deleting", dump_dir_name);
return 400; /* Bad Request */
}
-
if (!dump_dir_accessible_by_uid(dump_dir_name, client_uid))
{
if (errno == ENOTDIR)
@@ -191,7 +199,6 @@ static int delete_path(const char *dump_dir_name)
error_msg("Path '%s' isn't problem directory", dump_dir_name);
return 404; /* Not Found */
}
-
error_msg("Problem directory '%s' can't be accessed by user with uid %ld", dump_dir_name, (long)client_uid);
return 403; /* Forbidden */
}
@@ -201,6 +208,227 @@ static int delete_path(const char *dump_dir_name)
return 0; /* success */
}
+static pid_t spawn_event_handler_child(const char *dump_dir_name, const char *event_name, int *fdp)
+{
+ char *args[6];
+ args[0] = (char *) LIBEXEC_DIR"/abrt-handle-event";
+ args[1] = (char *) "-e";
+ args[2] = (char *) event_name;
+ args[3] = (char *) "--";
+ args[4] = (char *) dump_dir_name;
+ args[5] = NULL;
+
+ int pipeout[2];
+ int flags = EXECFLG_INPUT_NUL | EXECFLG_OUTPUT | EXECFLG_QUIET | EXECFLG_ERR2OUT;
+ VERB1 flags &= ~EXECFLG_QUIET;
+
+ pid_t child = fork_execv_on_steroids(flags, args, pipeout,
+ /*env_vec:*/ NULL, /*dir:*/ NULL,
+ /*uid(unused):*/ 0);
+ if (fdp)
+ *fdp = pipeout[0];
+ return child;
+}
+
+static int run_post_create(const char *dirname)
+{
+ /* If doesn't start with "g_settings_dump_location/"... */
+ if (!dir_is_in_dump_location(dirname))
+ {
+ /* Then refuse to operate on it (someone is attacking us??) */
+ error_msg("Bad problem directory name '%s'", dirname);
+ return 400; /* Bad Request */
+ }
+ if (!dump_dir_accessible_by_uid(dirname, client_uid))
+ {
+ if (errno == ENOTDIR)
+ {
+ error_msg("Path '%s' isn't problem directory", dirname);
+ return 404; /* Not Found */
+ }
+ error_msg("Problem directory '%s' can't be accessed by user with uid %ld", dirname, (long)client_uid);
+ return 403; /* Forbidden */
+ }
+
+ int child_stdout_fd;
+ int child_pid = spawn_event_handler_child(dirname, "post-create", &child_stdout_fd);
+
+ char *dup_of_dir = NULL;
+ struct strbuf *cmd_output = strbuf_new();
+
+ bool child_is_post_create = 1; /* else it is a notify child */
+
+ read_child_output:
+ //log("Reading from event fd %d", child_stdout_fd);
+
+ /* Read streamed data and split lines */
+ for (;;)
+ {
+ char buf[250]; /* usually we get one line, no need to have big buf */
+ errno = 0;
+ int r = safe_read(child_stdout_fd, buf, sizeof(buf) - 1);
+ if (r <= 0)
+ break;
+ buf[r] = '\0';
+
+ /* split lines in the current buffer */
+ char *raw = buf;
+ char *newline;
+ while ((newline = strchr(raw, '\n')) != NULL)
+ {
+ *newline = '\0';
+ strbuf_append_str(cmd_output, raw);
+ char *msg = cmd_output->buf;
+
+ /* Hmm, DUP_OF_DIR: ends up in syslog. move log() into 'else'? */
+ log("%s", msg);
+
+ if (child_is_post_create
+ && prefixcmp(msg, "DUP_OF_DIR: ") == 0
+ ) {
+ free(dup_of_dir);
+ dup_of_dir = xstrdup(msg + strlen("DUP_OF_DIR: "));
+ }
+
+ strbuf_clear(cmd_output);
+ /* jump to next line */
+ raw = newline + 1;
+ }
+
+ /* beginning of next line. the line continues by next read */
+ strbuf_append_str(cmd_output, raw);
+ }
+
+ /* EOF/error */
+
+ /* Wait for child to actually exit, collect status */
+ int status = 0;
+ if (safe_waitpid(child_pid, &status, 0) <= 0)
+ /* should not happen */
+ perror_msg("waitpid(%d)", child_pid);
+
+ /* If it was a "notify[-dup]" event, then we're done */
+ if (!child_is_post_create)
+ goto ret;
+
+ /* exit 0 means "this is a good, non-dup dir" */
+ /* exit with 1 + "DUP_OF_DIR: dir" string => dup */
+ if (status != 0)
+ {
+ if (WIFSIGNALED(status))
+ {
+ log("'post-create' on '%s' killed by signal %d",
+ dirname, WTERMSIG(status));
+ goto delete_bad_dir;
+ }
+ /* else: it is WIFEXITED(status) */
+ if (!dup_of_dir)
+ {
+ log("'post-create' on '%s' exited with %d",
+ dirname, WEXITSTATUS(status));
+ goto delete_bad_dir;
+ }
+ }
+
+ const char *work_dir = (dup_of_dir ? dup_of_dir : dirname);
+
+ /* Load problem_data (from the *first dir* if this one is a dup) */
+ struct dump_dir *dd = dd_opendir(work_dir, /*flags:*/ 0);
+ if (!dd)
+ /* dd_opendir already emitted error msg */
+ goto delete_bad_dir;
+
+ /* Update count */
+ char *count_str = dd_load_text_ext(dd, FILENAME_COUNT, DD_FAIL_QUIETLY_ENOENT);
+ unsigned long count = strtoul(count_str, NULL, 10);
+
+ /* Don't increase crash count if we are working with newly uploaded
+ * directory (remote crash) which already has its crash count set.
+ */
+ if ((status != 0 && dup_of_dir) || count == 0)
+ {
+ count++;
+ char new_count_str[sizeof(long)*3 + 2];
+ sprintf(new_count_str, "%lu", count);
+ dd_save_text(dd, FILENAME_COUNT, new_count_str);
+
+ /* This condition can be simplified to either
+ * (status * != 0 && * dup_of_dir) or (count == 1). But the
+ * chosen form is much more reliable and safe. We must not call
+ * dd_opendir() to locked dd otherwise we go into a deadlock.
+ */
+ if (strcmp(dd->dd_dirname, dirname) != 0)
+ {
+ /* Update the last occurrence file by the time file of the new problem */
+ struct dump_dir *new_dd = dd_opendir(dirname, DD_OPEN_READONLY);
+ char *last_ocr = NULL;
+ if (new_dd)
+ {
+ /* TIME must exists in a valid dump directory but we don't want to die
+ * due to broken duplicated dump directory */
+ last_ocr = dd_load_text_ext(new_dd, FILENAME_TIME,
+ DD_LOAD_TEXT_RETURN_NULL_ON_FAILURE | DD_FAIL_QUIETLY_ENOENT);
+ dd_close(new_dd);
+ }
+ else
+ { /* dd_opendir() already produced a message with good information about failure */
+ error_msg("Can't read the last occurrence file from the new dump directory.");
+ }
+
+ if (!last_ocr)
+ { /* the new dump directory may lie in the dump location for some time */
+ log("Using current time for the last occurrence file which may be incorrect.");
+ time_t t = time(NULL);
+ last_ocr = xasprintf("%lu", (long)t);
+ }
+
+ dd_save_text(dd, FILENAME_LAST_OCCURRENCE, last_ocr);
+
+ free(last_ocr);
+ }
+ }
+
+ /* Reset mode/uig/gid to correct values for all files created by event run */
+ dd_sanitize_mode_and_owner(dd);
+
+ dd_close(dd);
+
+ if (!dup_of_dir)
+ log("New problem directory %s, processing", work_dir);
+ else
+ {
+ log("Deleting problem directory %s (dup of %s)",
+ strrchr(dirname, '/') + 1,
+ strrchr(dup_of_dir, '/') + 1);
+ delete_dump_dir(dirname);
+ }
+
+ /* Run "notify[-dup]" event */
+ int fd;
+ child_pid = spawn_event_handler_child(
+ work_dir,
+ (dup_of_dir ? "notify-dup" : "notify"),
+ &fd
+ );
+ //log("Started notify, fd %d -> %d", fd, child_stdout_fd);
+ xmove_fd(fd, child_stdout_fd);
+ child_is_post_create = 0;
+ strbuf_clear(cmd_output);
+ free(dup_of_dir);
+ dup_of_dir = NULL;
+ goto read_child_output;
+
+ delete_bad_dir:
+ log("Deleting problem directory '%s'", dirname);
+ delete_dump_dir(dirname);
+
+ ret:
+ strbuf_free(cmd_output);
+ free(dup_of_dir);
+ close(child_stdout_fd);
+ return 0;
+}
+
/* Checks if a string contains only printable characters. */
static gboolean printable_str(const char *str)
{
@@ -402,11 +630,11 @@ static int perform_http_xact(void)
/* First line must be "op<space>[http://host]/path<space>HTTP/n.n".
* <space> is exactly one space char.
*/
- if (strncmp(messagebuf_data, "DELETE ", strlen("DELETE ")) == 0)
+ if (prefixcmp(messagebuf_data, "DELETE ") == 0)
{
messagebuf_data += strlen("DELETE ");
char *space = strchr(messagebuf_data, ' ');
- if (!space || strncmp(space+1, "HTTP/", strlen("HTTP/")) != 0)
+ if (!space || prefixcmp(space+1, "HTTP/") != 0)
return 400; /* Bad Request */
*space = '\0';
//decode_url(messagebuf_data); %20 => ' '
@@ -419,16 +647,29 @@ static int perform_http_xact(void)
* "PUT /" implies creation or replace of resource named "/"!
* Delete PUT in 2014.
*/
- if (strncmp(messagebuf_data, "PUT ", strlen("PUT ")) != 0
- && strncmp(messagebuf_data, "POST ", strlen("POST ")) != 0
+ if (prefixcmp(messagebuf_data, "PUT ") != 0
+ && prefixcmp(messagebuf_data, "POST ") != 0
) {
return 400; /* Bad Request */
}
+ enum {
+ CREATION_NOTIFICATION,
+ CREATION_REQUEST,
+ };
+ int url_type;
+ char *url = skip_non_whitespace(messagebuf_data) + 1; /* skip "POST " */
+ if (prefixcmp(url, "/creation_notification ") == 0)
+ url_type = CREATION_NOTIFICATION;
+ else if (prefixcmp(url, "/ ") == 0)
+ url_type = CREATION_REQUEST;
+ else
+ return 400; /* Bad Request */
+
/* Read body */
if (!body_start)
{
- VERB1 log("EOF detected, exiting");
+ log("Premature EOF detected, exiting");
return 400; /* Bad Request */
}
@@ -439,18 +680,21 @@ static int perform_http_xact(void)
/* Loop until EOF/error/timeout */
while (1)
{
- while (1)
+ if (url_type == CREATION_REQUEST)
{
- unsigned len = strnlen(messagebuf_data, messagebuf_len);
- if (len >= messagebuf_len)
- break;
- /* messagebuf has at least one NUL - process the line */
- process_message(problem_info, messagebuf_data);
- messagebuf_len -= (len + 1);
- memmove(messagebuf_data, messagebuf_data + len + 1, messagebuf_len);
+ while (1)
+ {
+ unsigned len = strnlen(messagebuf_data, messagebuf_len);
+ if (len >= messagebuf_len)
+ break;
+ /* messagebuf has at least one NUL - process the line */
+ process_message(problem_info, messagebuf_data);
+ messagebuf_len -= (len + 1);
+ memmove(messagebuf_data, messagebuf_data + len + 1, messagebuf_len);
+ }
}
- messagebuf_data = xrealloc(messagebuf_data, messagebuf_len + INPUT_BUFFER_SIZE);
+ messagebuf_data = xrealloc(messagebuf_data, messagebuf_len + INPUT_BUFFER_SIZE + 1);
int rd = read(STDIN_FILENO, messagebuf_data + messagebuf_len, INPUT_BUFFER_SIZE);
if (rd < 0)
{
@@ -468,9 +712,16 @@ static int perform_http_xact(void)
error_msg_and_die("Message is too long, aborting");
}
- /* Save problem dir. Don't let alarm to interrupt here */
+ /* Body received, EOF was seen. Don't let alarm to interrupt after this. */
alarm(0);
+ if (url_type == CREATION_NOTIFICATION)
+ {
+ messagebuf_data[messagebuf_len] = '\0';
+ return run_post_create(messagebuf_data);
+ }
+
+ /* Save problem dir */
int ret = 0;
unsigned pid = convert_pid(problem_info);
die_if_data_is_missing(problem_info);
--
1.8.1.4
More information about the Crash-catcher
mailing list