diff options
-rw-r--r-- | abrt.spec | 1 | ||||
-rw-r--r-- | inc/CrashTypes.h | 3 | ||||
-rw-r--r-- | src/Daemon/Daemon.cpp | 90 | ||||
-rw-r--r-- | src/Daemon/Makefile.am | 4 | ||||
-rw-r--r-- | src/Daemon/Settings.cpp | 6 | ||||
-rw-r--r-- | src/Daemon/Settings.h | 1 | ||||
-rw-r--r-- | src/Daemon/abrt-handle-upload | 71 | ||||
-rw-r--r-- | src/Daemon/abrt.conf | 5 |
8 files changed, 155 insertions, 26 deletions
@@ -315,6 +315,7 @@ fi %doc README COPYING %{_sbindir}/%{name}d %{_bindir}/%{name}-debuginfo-install +%{_bindir}/%{name}-handle-upload %{_bindir}/%{name}-backtrace %config(noreplace) %{_sysconfdir}/%{name}/%{name}.conf %config(noreplace) %{_sysconfdir}/%{name}/gpg_keys diff --git a/inc/CrashTypes.h b/inc/CrashTypes.h index 8b9fda93..25dbcd46 100644 --- a/inc/CrashTypes.h +++ b/inc/CrashTypes.h @@ -49,6 +49,9 @@ #define FILENAME_REPRODUCE "reproduce" #define FILENAME_RATING "rating" #define FILENAME_HOSTNAME "hostname" +// Optional. Set to "1" by abrt-handle-upload for every unpacked crashdump +#define FILENAME_REMOTE "remote" +// TODO: TicketUploader also has open-coded "TICKET", "CUSTOMER" files // Apart from CD_UID, which is also stored as a file in dump directory, // these items only exist in db. (CD_UID is also a file because diff --git a/src/Daemon/Daemon.cpp b/src/Daemon/Daemon.cpp index 331de4cf..e9db034d 100644 --- a/src/Daemon/Daemon.cpp +++ b/src/Daemon/Daemon.cpp @@ -21,6 +21,7 @@ #include <resolv.h> /* res_init */ #include <string> #include <sys/inotify.h> +#include <sys/ioctl.h> /* ioctl(FIONREAD) */ #include <xmlrpc-c/base.h> #include <xmlrpc-c/client.h> #include <glib.h> @@ -111,6 +112,7 @@ typedef struct cron_callback_data_t 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; @@ -430,41 +432,76 @@ static gboolean handle_signal_cb(GIOChannel *gio, GIOCondition condition, gpoint /* Inotify handler */ static gboolean handle_inotify_cb(GIOChannel *gio, GIOCondition condition, gpointer ptr_unused) { - /* 128 simultaneous actions */ -//TODO: use ioctl(FIONREAD) to determine how much to read -#define INOTIFY_BUFF_SIZE ((sizeof(struct inotify_event) + FILENAME_MAX)*128) - char *buf = (char*)xmalloc(INOTIFY_BUFF_SIZE); - gsize len; - gsize i = 0; + /* 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) + || inotify_bytes > INOTIFY_BUF_SIZE + ) { + inotify_bytes = INOTIFY_BUF_SIZE; + } + VERB3 log("FIONREAD:%d", inotify_bytes); + + char *buf = (char*)xmalloc(inotify_bytes); errno = 0; - GIOError err = g_io_channel_read(gio, buf, INOTIFY_BUFF_SIZE, &len); + gsize len; + GIOError err = g_io_channel_read(gio, buf, inotify_bytes, &len); if (err != G_IO_ERROR_NONE) { perror_msg("Error reading inotify fd"); free(buf); return FALSE; } - /* reconstruct each event and send message to the dbus */ + + /* Reconstruct each event and send message to the dbus */ + gsize i = 0; while (i < len) { + struct inotify_event *event = (struct inotify_event *) &buf[i]; const char *name = NULL; - struct inotify_event *event; - - event = (struct inotify_event *) &buf[i]; if (event->len) - name = &buf[i] + sizeof (*event); - i += sizeof (*event) + event->len; + name = event->name; + //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.c_str(); + log("Detected creation of file '%s' in upload directory '%s'", name, dir); + if (fork() == 0) + { + xchdir(dir); + execlp("abrt-handle-upload", "abrt-handle-upload", DEBUG_DUMPS_DIR, dir, name, (char*)NULL); + error_msg_and_die("Can't execute '%s'", "abrt-handle-upload"); + } + } + continue; + } - /* ignore lock files and such */ - if (!(event->mask & IN_ISDIR)) + if (!(event->mask & IN_ISDIR) || !name) { + /* ignore lock files and such */ // Happens all the time during normal run //VERB3 log("File '%s' creation detected, ignoring", name); continue; } if (strcmp(strchrnul(name, '.'), ".new") == 0) { - VERB3 log("Directory '%s' creation detected, ignoring", name); + //VERB3 log("Directory '%s' creation detected, ignoring", name); continue; } log("Directory '%s' creation detected", name); @@ -797,6 +834,9 @@ int main(int argc, char** argv) { init_daemon_logging(&watcher); + VERB1 log("Loading settings"); + LoadSettings(); + VERB1 log("Initializing XML-RPC library"); xmlrpc_env env; xmlrpc_env_init(&env); @@ -815,26 +855,28 @@ int main(int argc, char** argv) if (inotify_fd == -1) perror_msg_and_die("inotify_init failed"); close_on_exec_on(inotify_fd); - if (inotify_add_watch(inotify_fd, DEBUG_DUMPS_DIR, IN_CREATE | IN_MOVED_TO) == -1) + if (inotify_add_watch(inotify_fd, DEBUG_DUMPS_DIR, IN_CREATE | IN_MOVED_TO) < 0) perror_msg_and_die("inotify_add_watch failed on '%s'", DEBUG_DUMPS_DIR); + if (!g_settings_sWatchCrashdumpArchiveDir.empty()) + { + s_upload_watch = inotify_add_watch(inotify_fd, g_settings_sWatchCrashdumpArchiveDir.c_str(), IN_CLOSE_WRITE|IN_MOVED_TO); + if (s_upload_watch < 0) + perror_msg_and_die("inotify_add_watch failed on '%s'", g_settings_sWatchCrashdumpArchiveDir.c_str()); + } + VERB1 log("Adding inotify watch to glib main loop"); + pGiochannel_inotify = g_io_channel_unix_new(inotify_fd); + g_io_add_watch(pGiochannel_inotify, G_IO_IN, handle_inotify_cb, NULL); VERB1 log("Loading plugins from "PLUGINS_LIB_DIR); g_pPluginManager = new CPluginManager(); g_pPluginManager->LoadPlugins(); - VERB1 log("Loading settings"); - LoadSettings(); - if (SetUpMW() != 0) /* logging is inside */ throw 1; if (SetUpCron() != 0) throw 1; - VERB1 log("Adding inotify watch to glib main loop"); - pGiochannel_inotify = g_io_channel_unix_new(inotify_fd); - g_io_add_watch(pGiochannel_inotify, G_IO_IN, handle_inotify_cb, NULL); /* Add an event source which waits for INT/TERM signal */ - VERB1 log("Adding signal pipe watch to glib main loop"); pGiochannel_signal = g_io_channel_unix_new(s_signal_pipe[0]); g_io_add_watch(pGiochannel_signal, G_IO_IN, handle_signal_cb, NULL); diff --git a/src/Daemon/Makefile.am b/src/Daemon/Makefile.am index 56292150..34243b1d 100644 --- a/src/Daemon/Makefile.am +++ b/src/Daemon/Makefile.am @@ -1,4 +1,4 @@ -bin_SCRIPTS = abrt-debuginfo-install +bin_SCRIPTS = abrt-debuginfo-install abrt-handle-upload sbin_PROGRAMS = abrtd @@ -52,7 +52,7 @@ dist_comredhatabrtservice_DATA = com.redhat.abrt.service man_MANS = abrtd.8 abrt.conf.5 -EXTRA_DIST = $(man_MANS) abrt-debuginfo-install +EXTRA_DIST = $(man_MANS) abrt-debuginfo-install abrt-handle-upload DEFS = -DLOCALEDIR=\"$(localedir)\" @DEFS@ diff --git a/src/Daemon/Settings.cpp b/src/Daemon/Settings.cpp index 24cce59b..a57cf0c7 100644 --- a/src/Daemon/Settings.cpp +++ b/src/Daemon/Settings.cpp @@ -56,6 +56,7 @@ set_string_t g_settings_setOpenGPGPublicKeys; set_string_t g_settings_setBlackListedPkgs; set_string_t g_settings_setBlackListedPaths; std::string g_settings_sDatabase; +std::string g_settings_sWatchCrashdumpArchiveDir; unsigned int g_settings_nMaxCrashReportsSize = 1000; bool g_settings_bProcessUnpackaged = false; @@ -203,6 +204,11 @@ static void ParseCommon() { g_settings_sDatabase = it->second; } + it = s_mapSectionCommon.find("WatchCrashdumpArchiveDir"); + if (it != end) + { + g_settings_sWatchCrashdumpArchiveDir = it->second; + } it = s_mapSectionCommon.find("MaxCrashReportsSize"); if (it != end) { diff --git a/src/Daemon/Settings.h b/src/Daemon/Settings.h index 0395ed72..6dd964c9 100644 --- a/src/Daemon/Settings.h +++ b/src/Daemon/Settings.h @@ -32,6 +32,7 @@ extern unsigned int g_settings_nMaxCrashReportsSize; extern bool g_settings_bOpenGPGCheck; extern bool g_settings_bProcessUnpackaged; extern std::string g_settings_sDatabase; +extern std::string g_settings_sWatchCrashdumpArchiveDir; extern map_cron_t g_settings_mapCron; extern vector_pair_string_string_t g_settings_vectorActionsAndReporters; extern map_analyzer_actions_and_reporters_t g_settings_mapAnalyzerActionsAndReporters; diff --git a/src/Daemon/abrt-handle-upload b/src/Daemon/abrt-handle-upload new file mode 100644 index 00000000..82f52692 --- /dev/null +++ b/src/Daemon/abrt-handle-upload @@ -0,0 +1,71 @@ +#!/bin/sh +# Called by abrtd when a new file is noticed in upload directory. +# The task of this script is to unpack the file and move +# crashdump(s) found in it to abrt's crashdump directory. +# +# Usage: abrt-handle-upload ABRT_DIR UPLOAD_DIR FILENAME + +#echo "Started: $0 $*" + +print_clean_and_die() +{ + printf "%s\n" "$*" + #echo delete_on_exit="$delete_on_exit" + test "$delete_on_exit" && rm -rf -- $delete_on_exit + exit $die_exitcode +} + +die_exitcode=1 +delete_on_exit="" + +abrt_dir="$1" +upload_dir="$2" +archive="$3" + +test -d "$abrt_dir" || print_clean_and_die "Not a directory: '$abrt_dir'" +test -d "$upload_dir" || print_clean_and_die "Not a directory: '$upload_dir'" +test x"${archive%.working}" != x"$archive" && print_clean_and_die "Skipping: '$archive'" +test x"${archive#/}" != x"$archive" && print_clean_and_die "Skipping: '$archive' (starts with slash)" +test x"${archive#.}" != x"$archive" && print_clean_and_die "Skipping: '$archive' (starts with dot)" +test x"${archive#*..}" != x"$archive" && print_clean_and_die "Skipping: '$archive' (contains ..)" +test x"${archive#* }" != x"$archive" && print_clean_and_die "Skipping: '$archive' (contains space)" +# Note: next line has a tab! +test x"${archive#* }" != x"$archive" && print_clean_and_die "Skipping: '$archive' (contains tab)" + +cd -- "$upload_dir" || print_clean_and_die "Can't chdir to '$upload_dir'" + +unpacker="" +test x"${archive%.tar.gz}" != x"$archive" && unpacker="gunzip" +test x"${archive%.tar.bz2}" != x"$archive" && unpacker="bunzip2" +test x"${archive%.tar.xz}" != x"$archive" && unpacker="unxz" + +test "$unpacker" || print_clean_and_die "Unknown file type: '$archive'" + +tempdir="remote.`date +%Y-%m-%d-%H:%M:%S.%N`.$$" + +mv -- "$archive" "$archive.working" || print_clean_and_die "Can't lock '$archive'" + +delete_on_exit="$archive.working" +$unpacker -t -- "$archive.working" || print_clean_and_die "Verification error on '$archive'" + +echo "Unpacking '$archive'" +mkdir "$tempdir" || print_clean_and_die "Can't create '$tempdir' directory" +delete_on_exit="$archive.working $tempdir" +$unpacker <"$archive.working" | tar xf - -C "$tempdir" || print_clean_and_die "Can't unpack '$archive'" + +# The archive can contain either plain dump files +# or one or more complete crashdump directories. +# Checking second possibility first. +if test -f "$tempdir/analyzer" && test -f "$tempdir/time" && test -f "$tempdir/uid"; then + printf "1" >"$tempdir/remote" + mv -- "$tempdir" "$abrt_dir" +else + for d in "$tempdir"/*; do + test -d "$d" || continue + printf "1" >"$tempdir/$d/remote" + mv -- "$d" "$abrt_dir" + done +fi + +die_exitcode=0 +print_clean_and_die "'$archive' processed successfully" diff --git a/src/Daemon/abrt.conf b/src/Daemon/abrt.conf index 1bed49ca..6a70b820 100644 --- a/src/Daemon/abrt.conf +++ b/src/Daemon/abrt.conf @@ -12,6 +12,11 @@ ProcessUnpackaged = no BlackListedPaths = /usr/share/doc/*, */example* # Which database plugin to use Database = SQLite3 +# Enable this if you want abrtd to auto-unpack crashdump tarballs which appear +# in this directory (for example, uploaded via ftp, scp etc). +# Note: you must ensure that whatever directory you specify here exists +# and is writable for abrtd. abrtd will not create it automatically. +#WatchCrashdumpArchiveDir = /var/spool/abrt-upload # Max size for crash storage [MiB] MaxCrashReportsSize = 1000 # Vector of actions and reporters which are activated immediately |