Before this patch, abrtd will be stuck waiting for "post-create to finish,
not accepting, for example, socket connections while it runs.
(On my system, "post-create" takes 1 minute because of broken sendmail).

This is the first part of the fix. It creates and hooks up a GIO
source made from "post-create" stdout to main loop, and consumes its output
concurrently with other tasks.
--
vda


diff -x '*.po' -d -urpN abrt.2/src/daemon/abrtd.c abrt.3/src/daemon/abrtd.c
--- abrt.2/src/daemon/abrtd.c   2012-06-29 12:32:07.457638564 +0200
+++ abrt.3/src/daemon/abrtd.c   2012-06-29 13:10:05.545587500 +0200
@@ -61,7 +61,7 @@ static int socket_client_count = 0;

 /* Helpers */

-static pid_t spawn_event_handler_child(const char *dump_dir_name, const char 
*event_name, FILE **fpp)
+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";
@@ -75,12 +75,12 @@ static pid_t spawn_event_handler_child(c
     int flags = EXECFLG_INPUT_NUL | EXECFLG_OUTPUT | EXECFLG_QUIET | 
EXECFLG_ERR2OUT;
     VERB1 flags &= ~EXECFLG_QUIET;

+    pipeout[0] = -1;
     pid_t child = fork_execv_on_steroids(flags, args, pipeout,
-                                         /*env_vec:*/ NULL, /*dir:*/ NULL, 0);
-
-    *fpp = fdopen(pipeout[0], "r");
-    if (!*fpp)
-        die_out_of_memory();
+                                         /*env_vec:*/ NULL, /*dir:*/ NULL,
+                                         /*uid(unused):*/ 0);
+    if (fdp)
+        *fdp = pipeout[0];
     return child;
 }

@@ -280,55 +280,85 @@ static gboolean handle_signal_cb(GIOChan
 }


-/* Inotify handler */
-
-static problem_data_t *load_problem_data(const char *dump_dir_name)
+/* Event-processing child output handler (such as "post-create" event) */
+struct event_processing_state
 {
-    struct dump_dir *dd = dd_opendir(dump_dir_name, /*flags:*/ 0);
-    if (!dd)
-        return NULL;
-
-    problem_data_t *problem_data = create_problem_data_from_dump_dir(dd);
-    dd_close(dd);
-
-    add_to_problem_data_ext(problem_data, CD_DUMPDIR, dump_dir_name,
-                          CD_FLAG_TXT + CD_FLAG_ISNOTEDITABLE);
-
-    return problem_data;
+    pid_t child_pid;
+    int child_stdout_fd;
+    struct strbuf *cmd_output;
+    char *dirname;
+    char *dup_of_dir;
+};
+static struct event_processing_state *new_event_processing_state(void)
+{
+    struct event_processing_state *p = xzalloc(sizeof(*p));
+    p->child_pid = -1;
+    p->child_stdout_fd = -1;
+    p->cmd_output = strbuf_new();
+    return p;
+}
+static void free_event_processing_state(struct event_processing_state *p)
+{
+    if (!p)
+        return;
+    if (p->child_stdout_fd >= 0)
+        close(p->child_stdout_fd);
+    strbuf_free(p->cmd_output);
+    free(p->dirname);
+    free(p->dup_of_dir);
+    free(p);
 }

-typedef enum {
-    MW_OK,       /* No error */
-    MW_OCCURRED, /* No error, but thus dir is a dup */
-    MW_ERROR,    /* Other error (such as "post-create" event exited with 
nonzero */
-} mw_result_t;
-
-static mw_result_t run_post_create_and_load_data(const char *dump_dir_name, 
problem_data_t **problem_data)
+static gboolean handle_event_output_cb(GIOChannel *gio, GIOCondition 
condition, gpointer ptr)
 {
-    FILE *fp;
-    pid_t child = spawn_event_handler_child(dump_dir_name, "post-create", &fp);
+    struct event_processing_state *state = ptr;

-    char *buf;
-    char *dup_of_dir = NULL;
-    while ((buf = xmalloc_fgetline(fp)) != NULL)
+    /* Read streamed data and split lines */
+    for (;;)
     {
-        /* Hmm, DUP_OF_DIR: ends up in syslog. move log() into 'else'? */
-        log("%s", buf);
+        char buf[250]; /* usually we get one line, no need to have big buf */
+        errno = 0;
+        gsize r = 0;
+        g_io_channel_read_chars(gio, buf, sizeof(buf) - 1, &r, NULL);
+        if (r <= 0)
+            break;
+        buf[r] = '\0';

-        if (strncmp("DUP_OF_DIR: ", buf, strlen("DUP_OF_DIR: ")) == 0)
+        /* split lines in the current buffer */
+        char *raw = buf;
+        char *newline;
+        while ((newline = strchr(raw, '\n')) != NULL)
         {
-            overlapping_strcpy(buf, buf + strlen("DUP_OF_DIR: "));
-            free(dup_of_dir);
-            dup_of_dir = buf;
+            *newline = '\0';
+            strbuf_append_str(state->cmd_output, raw);
+            char *msg = state->cmd_output->buf;
+
+            /* Hmm, DUP_OF_DIR: ends up in syslog. move log() into 'else'? */
+            log("%s", msg);
+            if (strncmp("DUP_OF_DIR: ", msg, strlen("DUP_OF_DIR: ")) == 0)
+            {
+                free(state->dup_of_dir);
+                state->dup_of_dir = xstrdup(msg + strlen("DUP_OF_DIR: "));
+            }
+
+            strbuf_clear(state->cmd_output);
+            /* jump to next line */
+            raw = newline + 1;
         }
-        else
-            free(buf);
+
+        /* beginning of next line. the line continues by next read */
+        strbuf_append_str(state->cmd_output, raw);
     }
-    fclose(fp);

-    /* Prevent having zombie child process */
+    if (errno == EAGAIN)
+        /* We got all buffered data, but fd is still open. Done for now */
+        return TRUE; /* "please don't remove this event (yet)" */
+
+    /* EOF/error */
+
+    /* Wait for child to actually exit, collect status */
     int status;
-    safe_waitpid(child, &status, 0);
+    safe_waitpid(state->child_pid, &status, 0);

     /* exit 0 means "this is a good, non-dup dir" */
     /* exit with 1 + "DUP_OF_DIR: dir" string => dup */
@@ -337,30 +367,24 @@ static mw_result_t run_post_create_and_l
         if (WIFSIGNALED(status))
         {
             log("'post-create' on '%s' killed by signal %d",
-                            dump_dir_name, WTERMSIG(status));
-            return MW_ERROR;
+                            state->dirname, WTERMSIG(status));
         }
         /* else: it is WIFEXITED(status) */
-        if (!dup_of_dir)
+        else if (!state->dup_of_dir)
         {
             log("'post-create' on '%s' exited with %d",
-                            dump_dir_name, WEXITSTATUS(status));
-            return MW_ERROR;
+                            state->dirname, WEXITSTATUS(status));
         }
-        dump_dir_name = dup_of_dir;
+        goto delete_bad_dir;
     }

-    /* Loads problem_data (from the *first dir* if this one is a dup)
-     * Returns:
-     * MW_OCCURRED: "count is != 1" (iow: it is > 1 - dup)
-     * MW_OK: "count is 1" (iow: this is a new problem, not a dup)
-     * else: MW_ERROR
-     */
-    mw_result_t res = MW_ERROR;
-    struct dump_dir *dd = dd_opendir(dump_dir_name, /*flags:*/ 0);
+    char *work_dir = state->dup_of_dir ? state->dup_of_dir : state->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 ret;
+        goto delete_bad_dir;

     /* Reset mode/uig/gid to correct values for all files created by event run 
*/
     dd_sanitize_mode_and_owner(dd);
@@ -370,9 +394,9 @@ static mw_result_t run_post_create_and_l
     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 it's crash count set.
+     * directory (remote crash) which already has its crash count set.
      */
-    if((status != 0 && dup_of_dir) || count == 0)
+    if ((status != 0 && state->dup_of_dir) || count == 0)
     {
         count++;
         char new_count_str[sizeof(long)*3 + 2];
@@ -381,23 +405,53 @@ static mw_result_t run_post_create_and_l
     }
     dd_close(dd);

-    *problem_data = load_problem_data(dump_dir_name);
-    if (*problem_data != NULL)
+    if (!state->dup_of_dir)
+        log("New problem directory %s, processing", work_dir);
+    else
     {
-        res = MW_OK;
-        if (count > 1)
-        {
-            log("Problem directory is a duplicate of %s", dump_dir_name);
-            res = MW_OCCURRED;
-        }
+        log("Deleting problem directory %s (dup of %s)",
+                strrchr(state->dirname, '/') + 1,
+                strrchr(state->dup_of_dir, '/') + 1);
+        delete_dump_dir(state->dirname);
     }
-    /* else: load_problem_data already emitted error msg */
+
+    /* Run "notify[-dup]" event */
+//TODO: also handle it as long-running one
+    int fd;
+    pid_t child = spawn_event_handler_child(
+                    work_dir,
+                    (state->dup_of_dir ? "notify-dup" : "notify"),
+                    &fd
+    );
+    FILE *fp = fdopen(fd, "r");
+    if (!fp)
+        die_out_of_memory();
+    for (;;)
+    {
+        char *buf = xmalloc_fgetline(fp);
+        if (!buf) break;
+        log("%s", buf);
+        free(buf);
+    }
+    fclose(fp);
+    /* Prevent having zombie child process */
+    safe_waitpid(child, NULL, 0);
+
+    goto ret;
+
+ delete_bad_dir:
+    log("Corrupted or bad directory '%s', deleting", state->dirname);
+    delete_dump_dir(state->dirname);

  ret:
-    free(dup_of_dir);
-    return res;
+    free_event_processing_state(state);
+
+    return FALSE; /* "glib, please remove this events source!" */
 }

+
+/* Inotify handler */
+
 static gboolean handle_inotify_cb(GIOChannel *gio, GIOCondition condition, 
gpointer ptr_unused)
 {
     /* Default size: 128 simultaneous actions (about 1/2 meg) */
@@ -495,55 +549,17 @@ static gboolean handle_inotify_cb(GIOCha
             }
         }

-        char *fullname = concat_path_file(g_settings_dump_location, name);
-        problem_data_t *problem_data = NULL;
-        mw_result_t res = run_post_create_and_load_data(fullname, 
&problem_data);
-        switch (res)
-        {
-            case MW_OK:
-                log("New problem directory %s, processing", fullname);
-                /* Fall through */
-
-            case MW_OCCURRED: /* dup */
-            {
-                const char *first = NULL;
-                if (res != MW_OK)
-                {
-                    /* Fetch the name of older directory of which we are a dup 
*/
-                    first = get_problem_item_content_or_NULL(problem_data, 
CD_DUMPDIR);
-
-                    log("Deleting problem directory %s (dup of %s)",
-                            strrchr(fullname, '/') + 1,
-                            strrchr(first, '/') + 1);
-                    delete_dump_dir(fullname);
-                }
+        struct event_processing_state *state = new_event_processing_state();
+        state->dirname = concat_path_file(g_settings_dump_location, name);

-                /* Run "notify[_dup]" event */
-                FILE *fp;
-                pid_t child = spawn_event_handler_child(
-                                (first ? first : fullname),
-                                (first ? "notify-dup" : "notify"),
-                                &fp
-                );
-                char *buf;
-                while ((buf = xmalloc_fgetline(fp)) != NULL)
-                {
-                    log("%s", buf);
-                    free(buf);
-                }
-                fclose(fp);
-                /* Prevent having zombie child process */
-                safe_waitpid(child, NULL, 0);
+        state->child_pid = spawn_event_handler_child(state->dirname, "post-create", 
&state->child_stdout_fd);

-                break;
-            }
-            default: /* always MW_ERROR */
-                log("Corrupted or bad directory %s, deleting", fullname);
-                delete_dump_dir(fullname);
-                break;
-        }
-        free(fullname);
-        free_problem_data(problem_data);
+        GIOChannel *channel_event_output = 
my_io_channel_unix_new(state->child_stdout_fd);
+        g_io_channel_set_close_on_unref(channel_event_output, TRUE);
+        /*uint channel_id_event_output =*/ g_io_add_watch(channel_event_output,
+                                              G_IO_IN,
+                                              handle_event_output_cb,
+                                              state);
     } /* while */

     free(buf);

Reply via email to