Version 2:
* used xmalloc, xchdir
* removed APPROX_BYTES_PER_ARCHIVE_NAME
* the standard descriptors moved to /dev/null
* still using queue as we should decide which use cases we want to
support
* still using struct process because I want to avoid global variables
(well, I know that the structure is overloaded but I'm still happy
with this solution because it is less confusing for me)
git am -s -c *.mbox
-->8-->8-->8-->8-->8-->8-->8-->8-->8-->8-->8-->8-->8-->8-->8-->8--
Related to #657
Signed-off-by: Jakub Filak <jfilak(a)redhat.com>
---
doc/Makefile.am | 1 +
doc/abrt-upload-watch.txt | 51 ++++++
po/POTFILES.in | 1 +
src/daemon/Makefile.am | 25 ++-
src/daemon/abrt-inotify.c | 175 +++++++++++++++++++
src/daemon/abrt-inotify.h | 40 +++++
src/daemon/abrt-upload-watch.c | 376 +++++++++++++++++++++++++++++++++++++++++
src/daemon/abrtd.c | 175 ++-----------------
8 files changed, 678 insertions(+), 166 deletions(-)
create mode 100644 doc/abrt-upload-watch.txt
create mode 100644 src/daemon/abrt-inotify.c
create mode 100644 src/daemon/abrt-inotify.h
create mode 100644 src/daemon/abrt-upload-watch.c
diff --git a/doc/Makefile.am b/doc/Makefile.am
index 872d210..e0c50ff 100644
--- a/doc/Makefile.am
+++ b/doc/Makefile.am
@@ -33,6 +33,7 @@ MAN1_TXT += abrt-install-ccpp-hook.txt
MAN1_TXT += abrt-action-analyze-vmcore.txt
MAN1_TXT += abrt-action-analyze-ccpp-local.txt
MAN1_TXT += abrt-watch-log.txt
+MAN1_TXT += abrt-upload-watch.txt
MAN1_TXT += system-config-abrt.txt
if BUILD_BODHI
MAN1_TXT += abrt-bodhi.txt
diff --git a/doc/abrt-upload-watch.txt b/doc/abrt-upload-watch.txt
new file mode 100644
index 0000000..1486777
--- /dev/null
+++ b/doc/abrt-upload-watch.txt
@@ -0,0 +1,51 @@
+abrt-upload-watch(1)
+==================
+
+NAME
+----
+abrt-upload-watch - Watch upload directory and unpacks incoming archives into
DumpLocation
+
+SYNOPSIS
+--------
+'abrt-upload-watch' [-vs] [-w NUM_WORKERS] [-c CACHE_SIZE_MIB]
[UPLOAD_DIRECTORY]
+
+OPTIONS
+-------
+-v, --verbose::
+ Be more verbose. Can be given multiple times.
+
+-s::
+ Log to syslog
+
+-d::
+ Daemonize
+
+-w NUM_WORKERS::
+ Number of concurrent workers. Default is 10
+
+-c CACHE_SIZE_MIB::
+ Maximal cache size in MiB. Default is 4
+
+UPLOAD_DIRECTORY::
+ Watched directory. Default is a value of WatchCrashdumpArchiveDir option from
abrt.conf
+
+FILES
+-----
+Uses these three configuration options from file '/etc/abrt/abrt.conf':
+
+WatchCrashdumpArchiveDir::
+ Default upload directory
+
+DumpLocation::
+ Place where uploaded archives are unpacked
+
+DeleteUploaded::
+ Specifies if uploaded archives are deleted after unpacking
+
+SEE ALSO
+--------
+abrt.conf(5)
+
+AUTHORS
+-------
+* ABRT team
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 5e14bc8..8d22e35 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -11,6 +11,7 @@ src/daemon/abrt-server.c
src/dbus/abrt-dbus.c
src/daemon/abrtd.c
src/daemon/abrt-handle-event.c
+src/daemon/abrt-upload-watch.c
src/lib/abrt_conf.c
src/lib/hooklib.c
src/lib/problem_api.c
diff --git a/src/daemon/Makefile.am b/src/daemon/Makefile.am
index 33eec63..9369d26 100644
--- a/src/daemon/Makefile.am
+++ b/src/daemon/Makefile.am
@@ -11,14 +11,17 @@ bin_PROGRAMS = \
sbin_PROGRAMS = \
abrtd \
- abrt-server
+ abrt-server \
+ abrt-upload-watch
libexec_PROGRAMS = abrt-handle-event
# This is a daemon, building with full relro and PIE
# for increased security.
abrtd_SOURCES = \
- abrtd.c
+ abrtd.c \
+ abrt-inotify.c \
+ abrt-inotify.h
abrtd_CPPFLAGS = \
-I$(srcdir)/../include \
-I$(srcdir)/../lib \
@@ -49,6 +52,24 @@ abrt_server_LDADD = \
../lib/libabrt.la \
$(LIBREPORT_LIBS)
+abrt_upload_watch_SOURCES = \
+ abrt-upload-watch.c \
+ abrt-inotify.c \
+ abrt-inotify.h
+abrt_upload_watch_CPPFLAGS = \
+ -I$(srcdir)/../include \
+ -I$(srcdir)/../lib \
+ -DDEFAULT_DUMP_DIR_MODE=$(DEFAULT_DUMP_DIR_MODE) \
+ -DLIBEXEC_DIR=\"$(libexecdir)\" \
+ $(GLIB_CFLAGS) \
+ $(GIO_CFLAGS) \
+ $(LIBREPORT_CFLAGS) \
+ -D_GNU_SOURCE
+abrt_upload_watch_LDADD = \
+ ../lib/libabrt.la \
+ $(LIBREPORT_LIBS)
+
+
abrt_handle_event_SOURCES = \
abrt-handle-event.c
abrt_handle_event_CPPFLAGS = \
diff --git a/src/daemon/abrt-inotify.c b/src/daemon/abrt-inotify.c
new file mode 100644
index 0000000..54ab080
--- /dev/null
+++ b/src/daemon/abrt-inotify.c
@@ -0,0 +1,175 @@
+/*
+ Copyright (C) 2013 RedHat inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include "abrt-inotify.h"
+#include "abrt_glib.h"
+#include "libabrt.h"
+
+#include <stdio.h>
+#include <sys/ioctl.h> /* ioctl(FIONREAD) */
+
+struct abrt_inotify_watch
+{
+ abrt_inotify_watch_handler handler;
+ void *user_data;
+ int inotify_fd;
+ int inotify_wd;
+ GIOChannel *channel_inotify;
+ guint channel_inotify_source_id;
+};
+
+/* Inotify handler */
+
+static gboolean handle_inotify_cb(GIOChannel *gio, GIOCondition condition, gpointer
user_data)
+{
+ /* Default size: 128 simultaneous actions (about 1/2 meg) */
+#define INOTIFY_BUF_SIZE ((sizeof(struct inotify_event) + FILENAME_MAX)*128)
+ /* Determine how much to read (it usually is much smaller) */
+ /* NB: this variable _must_ be int-sized, ioctl expects that! */
+ int inotify_bytes = INOTIFY_BUF_SIZE;
+ if (ioctl(g_io_channel_unix_get_fd(gio), FIONREAD, &inotify_bytes) != 0
+ /*|| inotify_bytes < sizeof(struct inotify_event)
+ ^^^^^^^^^^^^^^^^^^^ - WRONG: legitimate 0 was seen when flooded with inotify
events
+ */
+ || inotify_bytes > INOTIFY_BUF_SIZE
+ ) {
+ inotify_bytes = INOTIFY_BUF_SIZE;
+ }
+ VERB3 log("FIONREAD:%d", inotify_bytes);
+
+ if (inotify_bytes == 0)
+ return TRUE; /* "please don't remove this event" */
+
+ /* We may race: more inotify events may happen after ioctl(FIONREAD).
+ * To be more efficient, allocate a bit more space to eat those events too.
+ * This also would help against a bug we once had where
+ * g_io_channel_read_chars() was buffering reads
+ * and we were going out of sync wrt struct inotify_event's layout.
+ */
+ inotify_bytes += 2 * (sizeof(struct inotify_event) + FILENAME_MAX);
+ char *buf = xmalloc(inotify_bytes);
+ errno = 0;
+ gsize len;
+ GError *gerror = NULL;
+ /* Note: we ensured elsewhere that this read is non-blocking, making it ok
+ * for buffer len (inotify_bytes) to be larger than actual available byte count.
+ */
+ GIOStatus err = g_io_channel_read_chars(gio, buf, inotify_bytes, &len,
&gerror);
+ if (err != G_IO_STATUS_NORMAL)
+ {
+ perror_msg("Error reading inotify fd: %s", gerror ? gerror->message
: "unknown");
+ free(buf);
+ if (gerror)
+ g_error_free(gerror);
+ return FALSE; /* "remove this event" (huh??) */
+ }
+
+ struct abrt_inotify_watch *aic = (struct abrt_inotify_watch *)user_data;
+ /* Reconstruct each event */
+ gsize i = 0;
+ for (;;)
+ {
+ if (i >= len)
+ {
+ /* This would catch one of our former bugs. Let's be paranoid */
+ if (i > len)
+ error_msg("warning: ran off struct inotify (this should never
happen): %u > %u", (int)i, (int)len);
+ break;
+ }
+ struct inotify_event *event = (struct inotify_event *) &buf[i];
+ i += sizeof(*event) + event->len;
+
+ aic->handler(aic, event, aic->user_data);
+ }
+ free(buf);
+ return TRUE;
+}
+
+struct abrt_inotify_watch *
+abrt_inotify_watch_init(const char *path, int inotify_flags, abrt_inotify_watch_handler
handler, void *user_data)
+{
+ struct abrt_inotify_watch *aiw = xmalloc(sizeof(*aiw));
+ aiw->handler = handler;
+ aiw->user_data = user_data;
+
+ VERB1 log("Initializing inotify");
+ errno = 0;
+ aiw->inotify_fd = inotify_init();
+ if (aiw->inotify_fd == -1)
+ perror_msg_and_die("inotify_init failed");
+ close_on_exec_on(aiw->inotify_fd);
+
+ aiw->inotify_wd = inotify_add_watch(aiw->inotify_fd, path, inotify_flags);
+ if (aiw->inotify_wd < 0)
+ perror_msg_and_die("inotify_add_watch failed on '%s'", path);
+
+ VERB1 log("Adding inotify watch to glib main loop");
+ /* Without nonblocking mode, users observed abrtd blocking
+ * on inotify read forever. Must set fd to non-blocking:
+ */
+ ndelay_on(aiw->inotify_fd);
+ aiw->channel_inotify = abrt_gio_channel_unix_new(aiw->inotify_fd);
+
+ /*
+ * glib's read buffering must be disabled, or else
+ * FIONREAD-reported "available data" sizes and sizes of reads
+ * can become inconsistent, and worse, buffering can split
+ * struct inotify's (very bad!).
+ */
+ g_io_channel_set_buffered(aiw->channel_inotify, false);
+
+ errno = 0;
+ aiw->channel_inotify_source_id = g_io_add_watch(aiw->channel_inotify,
+ G_IO_IN | G_IO_PRI | G_IO_HUP,
+ handle_inotify_cb,
+ aiw);
+ if (!aiw->channel_inotify_source_id)
+ perror_msg_and_die("g_io_add_watch failed");
+
+ return aiw;
+}
+
+void
+abrt_inotify_watch_reset(struct abrt_inotify_watch *watch, const char *path, int
inotify_flags)
+{
+ inotify_rm_watch(watch->inotify_fd, watch->inotify_wd);
+ watch->inotify_wd = inotify_add_watch(watch->inotify_fd, path, inotify_flags);
+ if (watch->inotify_wd < 0)
+ perror_msg_and_die("inotify_add_watch failed on '%s'", path);
+}
+
+void
+abrt_inotify_watch_destroy(struct abrt_inotify_watch *watch)
+{
+ if (!watch)
+ return;
+
+ inotify_rm_watch(watch->inotify_fd, watch->inotify_wd);
+ g_source_remove(watch->channel_inotify_source_id);
+
+ GError *error = NULL;
+ g_io_channel_shutdown(watch->channel_inotify, FALSE, &error);
+ if (error)
+ {
+ VERB1 log("Can't shutdown inotify gio channel: '%s'",
error->message);
+ g_error_free(error);
+ }
+
+ g_io_channel_unref(watch->channel_inotify);
+ free(watch);
+}
diff --git a/src/daemon/abrt-inotify.h b/src/daemon/abrt-inotify.h
new file mode 100644
index 0000000..7674fc0
--- /dev/null
+++ b/src/daemon/abrt-inotify.h
@@ -0,0 +1,40 @@
+/*
+ Copyright (C) 2013 RedHat inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#ifndef _ABRT_INOTIFY_H_
+#define _ABRT_INOTIFY_H_
+
+#include <sys/inotify.h>
+
+struct abrt_inotify_watch;
+
+typedef void (* abrt_inotify_watch_handler)(
+ struct abrt_inotify_watch *watch,
+ struct inotify_event *event,
+ void *user_data);
+
+struct abrt_inotify_watch *
+abrt_inotify_watch_init(const char *path, int inotify_flags, abrt_inotify_watch_handler
handler, void *user_data);
+
+void
+abrt_inotify_watch_destroy(struct abrt_inotify_watch *watch);
+
+void
+abrt_inotify_watch_reset(struct abrt_inotify_watch *watch, const char *path, int
inotify_flags);
+
+#endif /*_ABRT_INOTIFY_H_*/
diff --git a/src/daemon/abrt-upload-watch.c b/src/daemon/abrt-upload-watch.c
new file mode 100644
index 0000000..88d3033
--- /dev/null
+++ b/src/daemon/abrt-upload-watch.c
@@ -0,0 +1,376 @@
+/*
+ Copyright (C) 2013 ABRT Team
+ Copyright (C) 2013 Red Hat, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+#include "abrt-inotify.h"
+#include "abrt_glib.h"
+#include "libabrt.h"
+
+#include <syslog.h>
+
+#define STRINGIZE_DETAIL(str) #str
+#define STRINGIZE(str) STRINGIZE_DETAIL(str)
+
+#define DEFAULT_COUNT_OF_WORKERS 10
+#define DEFAULT_CACHE_MIB_SIZE 4
+
+static int g_signal_pipe[2];
+
+struct queue
+{
+ unsigned capacity;
+ GQueue q;
+};
+
+static int
+queue_push(struct queue *queue, char *value)
+{
+ if (g_queue_get_length(&queue->q) >= queue->capacity)
+ return 0;
+
+ g_queue_push_head(&queue->q, value);
+
+ return 1;
+}
+
+static char *
+queue_pop(struct queue *queue)
+{
+ if (g_queue_is_empty(&queue->q))
+ return NULL;
+
+ return (char *)g_queue_pop_tail(&queue->q);
+}
+
+struct process
+{
+ GMainLoop *main_loop;
+ const char *upload_directory;
+ unsigned children;
+ unsigned max_children;
+ struct queue queue;
+};
+
+static void
+process_quit(struct process *proc)
+{
+ g_main_loop_quit(proc->main_loop);
+}
+
+static void
+run_abrt_handle_upload(struct process *proc, const char *name)
+{
+ VERB2 log("Processing file '%s' in directory '%s'", name,
proc->upload_directory);
+
+ ++proc->children;
+ VERB3 log("Running workers: %d", proc->children);
+
+ fflush(NULL); /* paranoia */
+ pid_t pid = fork();
+ if (pid < 0)
+ {
+ --proc->children;
+ perror_msg("fork");
+ return;
+ }
+
+ if (pid == 0)
+ {
+ /* child */
+ xchdir(proc->upload_directory);
+ if (g_settings_delete_uploaded)
+ execlp("abrt-handle-upload", "abrt-handle-upload",
"-d",
+ g_settings_dump_location, proc->upload_directory, name,
(char*)NULL);
+ else
+ execlp("abrt-handle-upload", "abrt-handle-upload",
+ g_settings_dump_location, proc->upload_directory, name,
(char*)NULL);
+ error_msg_and_die("Can't execute '%s'",
"abrt-handle-upload");
+ }
+}
+
+static void
+handle_new_path(struct process *proc, char *name)
+{
+ log("Detected creation of file '%s' in upload directory
'%s'", name, proc->upload_directory);
+
+ if (proc->children < proc->max_children)
+ {
+ run_abrt_handle_upload(proc, name);
+ free(name);
+ return;
+ }
+
+ VERB3 log("Pushing '%s' to deferred queue", name);
+ if (!queue_push(&proc->queue, name))
+ {
+ error_msg(_("No free workers and full buffer. Omitting archive
'%s'"), name);
+ free(name);
+ return;
+ }
+}
+
+static void
+decrement_child_count(struct process *proc)
+{
+ --proc->children;
+
+ char *name = queue_pop(&proc->queue);
+ if (!name)
+ {
+ VERB3 log("Deferred queue is empty. Running workers: %d",
proc->children);
+ return;
+ }
+
+ run_abrt_handle_upload(proc, name);
+ free(name);
+}
+
+static void
+handle_signal(int signo)
+{
+ int save_errno = errno;
+ uint8_t sig_caught = signo;
+ if (write(g_signal_pipe[1], &sig_caught, 1))
+ /* we ignore result, if () shuts up stupid compiler */;
+ errno = save_errno;
+}
+
+static gboolean
+handle_signal_pipe_cb(GIOChannel *gio, GIOCondition condition, gpointer user_data)
+{
+ struct process *proc = (struct process *)user_data;
+ uint8_t signals[DEFAULT_COUNT_OF_WORKERS];
+ gsize len = 0;
+
+ for (;;)
+ {
+ GError *error = NULL;
+ GIOStatus stat = g_io_channel_read_chars(gio, (void *)signals, sizeof(signals),
&len, NULL);
+ if (stat == G_IO_STATUS_ERROR)
+ {
+ error_msg_and_die(_("Can't read from gio channel:
'%s'"), error ? error->message : "");
+ }
+ if (stat == G_IO_STATUS_AGAIN)
+ { /* We got all buffered data, but fd is still open. Done for now */
+ return TRUE; /* "glib, please don't remove this event (yet)"
*/
+ }
+ if (stat == G_IO_STATUS_EOF)
+ break;
+
+ /* G_IO_STATUS_NORMAL */
+ for (unsigned signo = 0; signo < len; ++signo)
+ {
+ /* we did receive a signal */
+ VERB3 log("Got signal %d through signal pipe", signals[signo]);
+ if (signals[signo]!= SIGCHLD)
+ {
+ process_quit(proc);
+ return FALSE; /* remove this event */
+ }
+ else
+ {
+ while (safe_waitpid(-1, NULL, WNOHANG) > 0)
+ {
+ decrement_child_count(proc);
+ }
+ }
+ }
+ }
+
+ return TRUE; /* "please don't remove this event" */
+}
+
+static void
+handle_inotify_cb(struct abrt_inotify_watch *watch, struct inotify_event *event, void
*user_data)
+{
+ /* Was the (presumable newly created) file closed in upload dir,
+ * or a file moved to upload dir? */
+ if (event->name && !(event->mask & IN_ISDIR) &&
(event->mask & (IN_CLOSE_WRITE | IN_MOVED_TO)))
+ {
+ const char *ext = strrchr(event->name, '.');
+ if (ext && strcmp(ext + 1, "working") == 0)
+ return;
+
+ handle_new_path((struct process *)user_data, xstrdup(event->name));
+ }
+}
+
+static void
+daemonize()
+{
+ /* forking to background */
+ fflush(NULL); /* paranoia */
+ pid_t pid = fork();
+ if (pid < 0)
+ perror_msg_and_die("fork");
+ if (pid > 0)
+ exit(0);
+
+ /* Child (daemon) continues */
+ if (setsid() < 0)
+ perror_msg_and_die("setsid");
+
+ /* Change the current working directory */
+ xchdir("/");
+
+ /* Reopen the standard file descriptors to "/dev/null" */
+ xmove_fd(xopen("/dev/null", O_RDWR), STDIN_FILENO);
+ xdup2(STDIN_FILENO, STDOUT_FILENO);
+ xdup2(STDIN_FILENO, STDERR_FILENO);
+}
+
+int
+main(int argc, char **argv)
+{
+ /* I18n */
+ setlocale(LC_ALL, "");
+#if ENABLE_NLS
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+#endif
+
+ abrt_init(argv);
+ /* Can't keep these strings/structs static: _() doesn't support that */
+ const char *program_usage_string = _(
+ "& [-vs] [-w NUM] [-c MiB] [UPLOAD_DIRECTORY]\n"
+ "\n"
+ "\nWatches UPLOAD_DIRECTORY and unpacks incoming archives into
DumpLocation"
+ "\nspecified in abrt.conf"
+ "\n"
+ "\nIf UPLOAD_DIRECTORY is not provided, uses a value of"
+ "\nWatchCrashdumpArchiveDir option from abrt.conf"
+ );
+ enum {
+ OPT_v = 1 << 0,
+ OPT_s = 1 << 1,
+ OPT_d = 1 << 2,
+ OPT_w = 1 << 3,
+ OPT_c = 1 << 4,
+ };
+
+ int concurrent_workers = DEFAULT_COUNT_OF_WORKERS;
+ int cache_size_mib = DEFAULT_CACHE_MIB_SIZE;
+
+ /* Keep enum above and order of options below in sync! */
+ struct options program_options[] = {
+ OPT__VERBOSE(&g_verbose),
+ OPT_BOOL('s', NULL, NULL , _("Log to syslog")),
+ OPT_BOOL('d', NULL, NULL , _("Daemize")),
+ OPT_INTEGER('w', NULL, &concurrent_workers, _("Number of
concurrent workers. Default is "STRINGIZE(DEFAULT_COUNT_OF_WORKERS))),
+ OPT_INTEGER('c', NULL, &cache_size_mib, _("Maximal cache size in
MiB. Default is "STRINGIZE(DEFAULT_CACHE_MIB_SIZE))),
+ OPT_END()
+ };
+ unsigned opts = parse_opts(argc, argv, program_options, program_usage_string);
+
+ if (concurrent_workers <= 0)
+ perror_msg_and_die("Invalid number of workers: %d",
concurrent_workers);
+
+ if (cache_size_mib <= 0)
+ perror_msg_and_die("Invalid cache size in MiB: %d", cache_size_mib);
+
+ if (cache_size_mib > UINT_MAX / (1024 * 1024 / FILENAME_MAX))
+ perror_msg_and_die("Too big cache size. Maximum is : %u MiB", UINT_MAX
/ (1024 * 1024 / FILENAME_MAX));
+
+ struct process proc = {0};
+ proc.max_children = concurrent_workers;
+ /* By default it is about 1024 entries */
+ g_queue_init(&proc.queue.q);
+ proc.queue.capacity = cache_size_mib * (1024 * 1024 / FILENAME_MAX);
+ VERB3 log("Max queue size %u", proc.queue.capacity);
+
+ argv += optind;
+ if (argv[0])
+ {
+ proc.upload_directory = argv[0];
+
+ if (argv[1])
+ show_usage_and_die(program_usage_string, program_options);
+ }
+
+ /* Initialization */
+ VERB2 log("Loading settings");
+ if (load_abrt_conf() != 0)
+ return 1;
+
+ if (!proc.upload_directory)
+ proc.upload_directory = g_settings_sWatchCrashdumpArchiveDir;
+
+ if (!proc.upload_directory)
+ error_msg_and_die("Neither UPLOAD_DIRECTORY nor WatchCrashdumpArchiveDir was
specified");
+
+ if (opts & OPT_d)
+ daemonize();
+
+ msg_prefix = g_progname;
+ if ((opts & OPT_d) || (opts & OPT_s) || getenv("ABRT_SYSLOG"))
+ {
+ openlog(msg_prefix, 0, LOG_DAEMON);
+ logmode = LOGMODE_SYSLOG;
+ }
+
+ VERB2 log("Creating glib main loop");
+ proc.main_loop = g_main_loop_new(NULL, FALSE);
+
+ VERB1 log("Setting up a file monitor for '%s'",
proc.upload_directory);
+ /* Never returns NULL; it will die if an error occurs */
+ struct abrt_inotify_watch *aiw = abrt_inotify_watch_init(proc.upload_directory,
+ IN_CLOSE_WRITE | IN_MOVED_TO,
+ handle_inotify_cb, &proc);
+
+ VERB1 log("Setting up a signal handler");
+ /* Set up signal pipe */
+ xpipe(g_signal_pipe);
+ close_on_exec_on(g_signal_pipe[0]);
+ close_on_exec_on(g_signal_pipe[1]);
+ ndelay_on(g_signal_pipe[0]);
+ ndelay_on(g_signal_pipe[1]);
+ signal(SIGTERM, handle_signal);
+ signal(SIGINT, handle_signal);
+ signal(SIGCHLD, handle_signal);
+ GIOChannel *channel_signal = abrt_gio_channel_unix_new(g_signal_pipe[0]);
+ guint channel_signal_source_id = g_io_add_watch(channel_signal,
+ G_IO_IN | G_IO_PRI | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+ handle_signal_pipe_cb,
+ &proc);
+
+ VERB2 log("Starting glib main loop");
+
+ g_main_loop_run(proc.main_loop);
+
+ VERB2 log("Glib main loop finished");
+
+ g_source_remove(channel_signal_source_id);
+
+ GError *error = NULL;
+ g_io_channel_shutdown(channel_signal, FALSE, &error);
+ if (error)
+ {
+ VERB1 log("Can't shutdown gio channel: '%s'", error ?
error->message : "");
+ g_error_free(error);
+ }
+
+ g_io_channel_unref(channel_signal);
+
+ abrt_inotify_watch_destroy(aiw);
+
+ if (proc.main_loop)
+ g_main_loop_unref(proc.main_loop);
+
+ free_abrt_conf_data();
+
+ return 0;
+}
diff --git a/src/daemon/abrtd.c b/src/daemon/abrtd.c
index 124421a..8196d39 100644
--- a/src/daemon/abrtd.c
+++ b/src/daemon/abrtd.c
@@ -21,10 +21,9 @@
#endif
#include <sys/un.h>
#include <syslog.h>
-#include <sys/inotify.h>
-#include <sys/ioctl.h> /* ioctl(FIONREAD) */
#include "abrt_glib.h"
+#include "abrt-inotify.h"
#include "libabrt.h"
@@ -53,7 +52,6 @@
static volatile sig_atomic_t s_sig_caught;
static int s_signal_pipe[2];
static int s_signal_pipe_write = -1;
-static int s_upload_watch = -1;
static unsigned s_timeout;
static bool s_exiting;
@@ -62,7 +60,6 @@ static guint channel_id_socket = 0;
static int child_count = 0;
/* Helpers */
-
static guint add_watch_or_die(GIOChannel *channel, unsigned condition, GIOFunc func)
{
errno = 0;
@@ -193,124 +190,23 @@ static void sanitize_dump_dir_rights(void)
/* Inotify handler */
-static gboolean handle_inotify_cb(GIOChannel *gio, GIOCondition condition, gpointer
ptr_unused)
+static void handle_inotify_cb(struct abrt_inotify_watch *watch, struct inotify_event
*event, gpointer ptr_unused)
{
- /* Default size: 128 simultaneous actions (about 1/2 meg) */
-#define INOTIFY_BUF_SIZE ((sizeof(struct inotify_event) + FILENAME_MAX)*128)
- /* Determine how much to read (it usually is much smaller) */
- /* NB: this variable _must_ be int-sized, ioctl expects that! */
- int inotify_bytes = INOTIFY_BUF_SIZE;
- if (ioctl(g_io_channel_unix_get_fd(gio), FIONREAD, &inotify_bytes) != 0
- /*|| inotify_bytes < sizeof(struct inotify_event)
- ^^^^^^^^^^^^^^^^^^^ - WRONG: legitimate 0 was seen when flooded with inotify
events
- */
- || inotify_bytes > INOTIFY_BUF_SIZE
- ) {
- inotify_bytes = INOTIFY_BUF_SIZE;
- }
- VERB3 log("FIONREAD:%d", inotify_bytes);
-
- if (inotify_bytes == 0)
- return TRUE; /* "please don't remove this event" */
-
- /* We may race: more inotify events may happen after ioctl(FIONREAD).
- * To be more efficient, allocate a bit more space to eat those events too.
- * This also would help against a bug we once had where
- * g_io_channel_read_chars() was buffering reads
- * and we were going out of sync wrt struct inotify_event's layout.
- */
- inotify_bytes += 2 * (sizeof(struct inotify_event) + FILENAME_MAX);
- char *buf = xmalloc(inotify_bytes);
- errno = 0;
- gsize len;
- GError *gerror = NULL;
- /* Note: we ensured elsewhere that this read is non-blocking, making it ok
- * for buffer len (inotify_bytes) to be larger than actual available byte count.
- */
- GIOStatus err = g_io_channel_read_chars(gio, buf, inotify_bytes, &len,
&gerror);
- if (err != G_IO_STATUS_NORMAL)
- {
- perror_msg("Error reading inotify fd: %s", gerror ? gerror->message
: "unknown");
- free(buf);
- if (gerror)
- g_error_free(gerror);
- return FALSE; /* "remove this event" (huh??) */
- }
-
- /* Reconstruct each event */
- gsize i = 0;
- for (;;)
- {
- if (i >= len)
- {
- /* This would catch one of our former bugs. Let's be paranoid */
- if (i > len)
- error_msg("warning: ran off struct inotify (this should never
happen): %u > %u", (int)i, (int)len);
- break;
- }
-
- struct inotify_event *event = (struct inotify_event *) &buf[i];
+/* We no longer need a name of event */
+#if 0
const char *name = NULL;
if (event->len)
name = event->name;
+#endif
//log("i:%d len:%d event->mask:%x IN_ISDIR:%x IN_CLOSE_WRITE:%x
event->len:%d",
// i, len, event->mask, IN_ISDIR, IN_CLOSE_WRITE, event->len);
- i += sizeof(*event) + event->len;
-
- if (event->wd == s_upload_watch)
- {
- /* Was the (presumable newly created) file closed in upload dir,
- * or a file moved to upload dir? */
- if (!(event->mask & IN_ISDIR)
- && event->mask & (IN_CLOSE_WRITE|IN_MOVED_TO)
- && name
- ) {
- const char *ext = strrchr(name, '.');
- if (ext && strcmp(ext + 1, "working") == 0)
- continue;
-
- const char *dir = g_settings_sWatchCrashdumpArchiveDir;
- log("Detected creation of file '%s' in upload directory
'%s'", name, dir);
-
- fflush(NULL); /* paranoia */
- pid_t pid = fork();
- if (pid < 0)
- perror_msg("fork");
- if (pid == 0)
- {
- /* child */
- xchdir(dir);
- if (g_settings_delete_uploaded)
- execlp("abrt-handle-upload",
"abrt-handle-upload", "-d",
- g_settings_dump_location, dir, name, (char*)NULL);
- else
- execlp("abrt-handle-upload",
"abrt-handle-upload",
- g_settings_dump_location, dir, name, (char*)NULL);
- error_msg_and_die("Can't execute '%s'",
"abrt-handle-upload");
- }
-
- if (pid > 0)
- increment_child_count();
- }
- continue;
- }
if (event->mask & IN_DELETE_SELF || event->mask & IN_MOVE_SELF)
{
- /* HACK: we expect that we watch deletion only of
'g_settings_dump_location'
- * but this handler is used for
'g_settings_sWatchCrashdumpArchiveDir' too
- */
log("Recreating deleted dump location '%s'",
g_settings_dump_location);
- int inotify_fd = g_io_channel_unix_get_fd(gio);
- inotify_rm_watch(inotify_fd, event->wd);
sanitize_dump_dir_rights();
- if (inotify_add_watch(inotify_fd, g_settings_dump_location,
IN_DUMP_LOCATION_FLAGS) < 0)
- {
- perror_msg_and_die("inotify_add_watch failed on recreated
'%s'", g_settings_dump_location);
- }
-
- continue;
+ abrt_inotify_watch_reset(watch, g_settings_dump_location,
IN_DUMP_LOCATION_FLAGS);
}
/* We no longer watch for subdirectory creations */
#if 0
@@ -330,10 +226,6 @@ static gboolean handle_inotify_cb(GIOChannel *gio, GIOCondition
condition, gpoin
log("Directory '%s' creation detected", name);
...
#endif
- } /* while */
-
- free(buf);
- return TRUE; /* "please don't remove this event" */
}
@@ -644,11 +536,10 @@ int main(int argc, char** argv)
signal(SIGALRM, handle_signal);
GMainLoop* pMainloop = NULL;
- GIOChannel* channel_inotify = NULL;
- guint channel_id_inotify_event = 0;
GIOChannel* channel_signal = NULL;
guint channel_id_signal_event = 0;
bool pidfile_created = false;
+ struct abrt_inotify_watch *aiw = NULL;
/* Initialization */
VERB1 log("Loading settings");
@@ -700,53 +591,11 @@ int main(int argc, char** argv)
VERB1 log("Creating glib main loop");
pMainloop = g_main_loop_new(NULL, FALSE);
- VERB1 log("Initializing inotify");
- errno = 0;
- int inotify_fd = inotify_init();
- if (inotify_fd == -1)
- perror_msg_and_die("inotify_init failed");
- close_on_exec_on(inotify_fd);
-
/* Watching 'g_settings_dump_location' for delete self
* because hooks expects that the dump location exists if abrtd is running
*/
- if (inotify_add_watch(inotify_fd, g_settings_dump_location, IN_DUMP_LOCATION_FLAGS)
< 0)
- {
- perror_msg("inotify_add_watch failed on '%s'",
g_settings_dump_location);
- goto init_error;
- }
- /* ...and upload dir */
- if (g_settings_sWatchCrashdumpArchiveDir)
- {
- if (strcmp(g_settings_sWatchCrashdumpArchiveDir, g_settings_dump_location) == 0)
- {
- error_msg("%s and %s can't be the same",
"DumpLocation", "WatchCrashdumpArchiveDir");
- goto init_error;
- }
- s_upload_watch = inotify_add_watch(inotify_fd,
g_settings_sWatchCrashdumpArchiveDir, IN_CLOSE_WRITE|IN_MOVED_TO);
- if (s_upload_watch < 0)
- {
- perror_msg("inotify_add_watch failed on '%s'",
g_settings_sWatchCrashdumpArchiveDir);
- goto init_error;
- }
- }
-
- VERB1 log("Adding inotify watch to glib main loop");
- /* Without nonblocking mode, users observed abrtd blocking
- * on inotify read forever. Must set fd to non-blocking:
- */
- ndelay_on(inotify_fd);
- channel_inotify = abrt_gio_channel_unix_new(inotify_fd);
- /*
- * glib's read buffering must be disabled, or else
- * FIONREAD-reported "available data" sizes and sizes of reads
- * can become inconsistent, and worse, buffering can split
- * struct inotify's (very bad!).
- */
- g_io_channel_set_buffered(channel_inotify, false);
- channel_id_inotify_event = add_watch_or_die(channel_inotify,
- G_IO_IN | G_IO_PRI | G_IO_HUP,
- handle_inotify_cb);
+ aiw = abrt_inotify_watch_init(g_settings_dump_location,
+ IN_DUMP_LOCATION_FLAGS, handle_inotify_cb, /*user data*/NULL);
/* Add an event source which waits for INT/TERM signal */
VERB1 log("Adding signal pipe watch to glib main loop");
@@ -792,10 +641,8 @@ int main(int argc, char** argv)
g_source_remove(channel_id_signal_event);
if (channel_signal)
g_io_channel_unref(channel_signal);
- if (channel_id_inotify_event > 0)
- g_source_remove(channel_id_inotify_event);
- if (channel_inotify)
- g_io_channel_unref(channel_inotify);
+
+ abrt_inotify_watch_destroy(aiw);
if (pMainloop)
g_main_loop_unref(pMainloop);
--
1.8.3.1