|
Packit |
8ea169 |
/*
|
|
Packit |
8ea169 |
* Copyright (C) 2014 ABRT team
|
|
Packit |
8ea169 |
* Copyright (C) 2014 RedHat Inc
|
|
Packit |
8ea169 |
*
|
|
Packit |
8ea169 |
* This program is free software; you can redistribute it and/or modify
|
|
Packit |
8ea169 |
* it under the terms of the GNU General Public License as published by
|
|
Packit |
8ea169 |
* the Free Software Foundation; either version 2 of the License, or
|
|
Packit |
8ea169 |
* (at your option) any later version.
|
|
Packit |
8ea169 |
*
|
|
Packit |
8ea169 |
* This program is distributed in the hope that it will be useful,
|
|
Packit |
8ea169 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit |
8ea169 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
Packit |
8ea169 |
* GNU General Public License for more details.
|
|
Packit |
8ea169 |
*/
|
|
Packit |
8ea169 |
#include "libabrt.h"
|
|
Packit |
8ea169 |
#include "abrt-journal.h"
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
#define ABRT_JOURNAL_WATCH_STATE_FILE VAR_STATE"/abrt-dump-journal-core.state"
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
/*
|
|
Packit |
8ea169 |
* A journal message is a set of key value pairs in the following format:
|
|
Packit |
8ea169 |
* FIELD_NAME=${binary data}
|
|
Packit |
8ea169 |
*
|
|
Packit |
8ea169 |
* A journal message contains many fields useful in syslog but ABRT doesn't
|
|
Packit |
8ea169 |
* need all of them. So the following list defines mapping between journal
|
|
Packit |
8ea169 |
* fields and ABRT problem items.
|
|
Packit |
8ea169 |
*
|
|
Packit |
8ea169 |
* ABRT goes through the list and for each item reads journal field called
|
|
Packit |
8ea169 |
* 'item.name' and saves its contents in $DUMP_DIRECTORY/'item.file'.
|
|
Packit |
8ea169 |
*/
|
|
Packit |
8ea169 |
struct field_mapping {
|
|
Packit |
8ea169 |
const char *name;
|
|
Packit |
8ea169 |
const char *file;
|
|
Packit |
8ea169 |
} fields [] = {
|
|
Packit |
8ea169 |
{ .name = "COREDUMP_EXE", .file = FILENAME_EXECUTABLE, },
|
|
Packit |
8ea169 |
{ .name = "COREDUMP_CMDLINE", .file = FILENAME_CMDLINE, },
|
|
Packit |
8ea169 |
{ .name = "COREDUMP_PROC_STATUS", .file = FILENAME_PROC_PID_STATUS, },
|
|
Packit |
8ea169 |
{ .name = "COREDUMP_PROC_MAPS", .file = FILENAME_MAPS, },
|
|
Packit |
8ea169 |
{ .name = "COREDUMP_PROC_LIMITS", .file = FILENAME_LIMITS, },
|
|
Packit |
8ea169 |
{ .name = "COREDUMP_PROC_CGROUP", .file = FILENAME_CGROUP, },
|
|
Packit |
8ea169 |
{ .name = "COREDUMP_ENVIRON", .file = FILENAME_ENVIRON, },
|
|
Packit |
8ea169 |
{ .name = "COREDUMP_CWD", .file = FILENAME_PWD, },
|
|
Packit |
8ea169 |
{ .name = "COREDUMP_ROOT", .file = FILENAME_ROOTDIR, },
|
|
Packit |
8ea169 |
{ .name = "COREDUMP_OPEN_FDS", .file = FILENAME_OPEN_FDS, },
|
|
Packit |
8ea169 |
{ .name = "COREDUMP_UID", .file = FILENAME_UID, },
|
|
Packit |
8ea169 |
//{ .name = "COREDUMP_GID", .file = FILENAME_GID, },
|
|
Packit |
8ea169 |
{ .name = "COREDUMP_PID", .file = FILENAME_PID, },
|
|
Packit |
8ea169 |
{ .name = "COREDUMP_PROC_MOUNTINFO", .file = FILENAME_MOUNTINFO, },
|
|
Packit |
8ea169 |
};
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
/*
|
|
Packit |
8ea169 |
* Something like 'struct problem_data' but optimized for copying data from
|
|
Packit |
8ea169 |
* journald to ABRT.
|
|
Packit |
8ea169 |
*
|
|
Packit |
8ea169 |
* 'struct problem_data' allocates a new memory for every single item and I
|
|
Packit |
8ea169 |
* found that very inefficient in this case.
|
|
Packit |
8ea169 |
*
|
|
Packit |
8ea169 |
* The following structure holds data that we already retreived from journald
|
|
Packit |
8ea169 |
* so we won't need to retrieve the data again.
|
|
Packit |
8ea169 |
*
|
|
Packit |
8ea169 |
* Why we retrieve data before we store them? Because we do some checking
|
|
Packit |
8ea169 |
* before we start saving data in ABRT. We check whether the signal is one of
|
|
Packit |
8ea169 |
* those we are interested in or whether the executable crashes too often to
|
|
Packit |
8ea169 |
* ignore the current crash ...
|
|
Packit |
8ea169 |
*/
|
|
Packit |
8ea169 |
struct crash_info
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
abrt_journal_t *ci_journal;
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
int ci_signal_no;
|
|
Packit |
8ea169 |
const char *ci_signal_name;
|
|
Packit |
8ea169 |
char *ci_executable_path; ///< /full/path/to/executable
|
|
Packit |
8ea169 |
const char *ci_executable_name; ///< executable
|
|
Packit |
8ea169 |
uid_t ci_uid;
|
|
Packit |
8ea169 |
pid_t ci_pid;
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
struct field_mapping *ci_mapping;
|
|
Packit |
8ea169 |
size_t ci_mapping_items;
|
|
Packit |
8ea169 |
};
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
/*
|
|
Packit |
8ea169 |
* ABRT watch core configuration
|
|
Packit |
8ea169 |
*/
|
|
Packit |
8ea169 |
typedef struct
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
const char *awc_dump_location;
|
|
Packit |
8ea169 |
int awc_throttle;
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
abrt_watch_core_conf_t;
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
/*
|
|
Packit |
8ea169 |
* A helper structured holding executable name and its last occurrence time.
|
|
Packit |
8ea169 |
*
|
|
Packit |
8ea169 |
* It is rather an array than a queue. Ii uses statically allocated array and
|
|
Packit |
8ea169 |
* the head member points the next position for creating a new entry (the next
|
|
Packit |
8ea169 |
* position may be already occupied and in such case the data shall be released).
|
|
Packit |
8ea169 |
*/
|
|
Packit |
8ea169 |
struct occurrence_queue
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
int oq_head; ///< the first empty index
|
|
Packit |
8ea169 |
unsigned oq_size; ///< size of the queue
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
struct last_occurrence
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
unsigned oqlc_stamp;
|
|
Packit |
8ea169 |
char *oqlc_executable;
|
|
Packit |
8ea169 |
} oq_occurrences[8];
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
} s_queue = {
|
|
Packit |
8ea169 |
.oq_head = -1,
|
|
Packit |
8ea169 |
.oq_size = 8,
|
|
Packit |
8ea169 |
};
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
static unsigned
|
|
Packit |
8ea169 |
abrt_journal_get_last_occurrence(const char *executable)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
if (s_queue.oq_head < 0)
|
|
Packit |
8ea169 |
return 0;
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
unsigned index = s_queue.oq_head == 0 ? s_queue.oq_size - 1 : s_queue.oq_head - 1;
|
|
Packit |
8ea169 |
for (unsigned i = 0; i < s_queue.oq_size; ++i)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
if (s_queue.oq_occurrences[index].oqlc_executable == NULL)
|
|
Packit |
8ea169 |
break;
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
if (strcmp(executable, s_queue.oq_occurrences[index].oqlc_executable) == 0)
|
|
Packit |
8ea169 |
return s_queue.oq_occurrences[index].oqlc_stamp;
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
if (index-- == 0)
|
|
Packit |
8ea169 |
index = s_queue.oq_size - 1;
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
return 0;
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
static void
|
|
Packit |
8ea169 |
abrt_journal_update_occurrence(const char *executable, unsigned ts)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
if (s_queue.oq_head < 0)
|
|
Packit |
8ea169 |
s_queue.oq_head = 0;
|
|
Packit |
8ea169 |
else
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
unsigned index = s_queue.oq_head == 0 ? s_queue.oq_size - 1 : s_queue.oq_head - 1;
|
|
Packit |
8ea169 |
for (unsigned i = 0; i < s_queue.oq_size; ++i)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
if (s_queue.oq_occurrences[index].oqlc_executable == NULL)
|
|
Packit |
8ea169 |
break;
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
if (strcmp(executable, s_queue.oq_occurrences[index].oqlc_executable) == 0)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
/* Enhancement: move this entry right behind head */
|
|
Packit |
8ea169 |
s_queue.oq_occurrences[index].oqlc_stamp = ts;
|
|
Packit |
8ea169 |
return;
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
if (index-- == 0)
|
|
Packit |
8ea169 |
index = s_queue.oq_size - 1;
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
s_queue.oq_occurrences[s_queue.oq_head].oqlc_stamp = ts;
|
|
Packit |
8ea169 |
free(s_queue.oq_occurrences[s_queue.oq_head].oqlc_executable);
|
|
Packit |
8ea169 |
s_queue.oq_occurrences[s_queue.oq_head].oqlc_executable = xstrdup(executable);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
if (++s_queue.oq_head >= s_queue.oq_size)
|
|
Packit |
8ea169 |
s_queue.oq_head = 0;
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
return;
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
/*
|
|
Packit |
8ea169 |
* Converts a journal message into an intermediate ABRT problem (struct crash_info).
|
|
Packit |
8ea169 |
*
|
|
Packit |
8ea169 |
* Refuses to create the problem in the following cases:
|
|
Packit |
8ea169 |
* - the crashed executable has 'abrt' prefix
|
|
Packit |
8ea169 |
* - the signals is not fatal (see signal_is_fatal())
|
|
Packit |
8ea169 |
* - the journal message misses one of the following fields
|
|
Packit |
8ea169 |
* - COREDUMP_SIGNAL
|
|
Packit |
8ea169 |
* - COREDUMP_EXE
|
|
Packit |
8ea169 |
* - COREDUMP_UID
|
|
Packit |
8ea169 |
* - COREDUMP_PROC_STATUS
|
|
Packit |
8ea169 |
* - if any data does not have an expected format
|
|
Packit |
8ea169 |
*/
|
|
Packit |
8ea169 |
static int
|
|
Packit |
8ea169 |
abrt_journal_core_retrieve_information(abrt_journal_t *journal, struct crash_info *info)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
if (abrt_journal_get_int_field(journal, "COREDUMP_SIGNAL", &(info->ci_signal_no)) != 0)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
log_info("Failed to get signal number from journal message");
|
|
Packit |
8ea169 |
return -EINVAL;
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
if (!signal_is_fatal(info->ci_signal_no, &(info->ci_signal_name)))
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
log_info("Signal '%d' is not fatal: ignoring crash", info->ci_signal_no);
|
|
Packit |
8ea169 |
return 1;
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
info->ci_executable_path = abrt_journal_get_string_field(journal, "COREDUMP_EXE", NULL);
|
|
Packit |
8ea169 |
if (info->ci_executable_path == NULL)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
log_notice("Could not get crashed 'executable'.");
|
|
Packit |
8ea169 |
return -ENOENT;
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
info->ci_executable_name = strrchr(info->ci_executable_path, '/');
|
|
Packit |
8ea169 |
if (info->ci_executable_name == NULL)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
info->ci_executable_name = info->ci_executable_path;
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
else if(strncmp(++(info->ci_executable_name), "abrt", 4) == 0)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
error_msg("Ignoring crash of ABRT executable '%s'", info->ci_executable_path);
|
|
Packit |
8ea169 |
return 1;
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
if (abrt_journal_get_unsigned_field(journal, "COREDUMP_UID", &(info->ci_uid)))
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
log_info("Failed to get UID from journal message");
|
|
Packit |
8ea169 |
return -EINVAL;
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
/* This is not fatal, the pid is used only in dumpdir name */
|
|
Packit |
8ea169 |
if (abrt_journal_get_int_field(journal, "COREDUMP_PID", &(info->ci_pid)))
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
log_notice("Failed to get PID from journal message.");
|
|
Packit |
8ea169 |
info->ci_pid = getpid();
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
char *proc_status = abrt_journal_get_string_field(journal, "COREDUMP_PROC_STATUS", NULL);
|
|
Packit |
8ea169 |
if (proc_status == NULL)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
log_info("Failed to get /proc/[pid]/status from journal message");
|
|
Packit |
8ea169 |
return -ENOENT;
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
uid_t tmp_fsuid = get_fsuid(proc_status);
|
|
Packit |
8ea169 |
if (tmp_fsuid < 0)
|
|
Packit |
8ea169 |
return -EINVAL;
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
if (tmp_fsuid != info->ci_uid)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
/* use root for suided apps unless it's explicitly set to UNSAFE */
|
|
Packit |
8ea169 |
info->ci_uid = (dump_suid_policy() != DUMP_SUID_UNSAFE) ? 0 : tmp_fsuid;
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
return 0;
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
/*
|
|
Packit |
8ea169 |
* Initializes ABRT problem directory and save the relevant journal message
|
|
Packit |
8ea169 |
* fileds in that directory.
|
|
Packit |
8ea169 |
*/
|
|
Packit |
8ea169 |
static int
|
|
Packit |
8ea169 |
save_systemd_coredump_in_dump_directory(struct dump_dir *dd, struct crash_info *info)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
char coredump_path[PATH_MAX + 1] = { '\0' };
|
|
Packit |
8ea169 |
if (coredump_path != abrt_journal_get_string_field(info->ci_journal, "COREDUMP_FILENAME", coredump_path))
|
|
Packit |
8ea169 |
log_debug("Processing coredumpctl entry without a real file");
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
const size_t len = strlen(coredump_path);
|
|
Packit |
8ea169 |
if ( (len >= 3
|
|
Packit |
8ea169 |
&& coredump_path[len - 3] == '.'
|
|
Packit |
8ea169 |
&& coredump_path[len - 2] == 'x'
|
|
Packit |
8ea169 |
&& coredump_path[len - 1] == 'z')
|
|
Packit |
8ea169 |
|| (len >= 4
|
|
Packit |
8ea169 |
&& coredump_path[len - 4] == '.'
|
|
Packit |
8ea169 |
&& coredump_path[len - 3] == 'l'
|
|
Packit |
8ea169 |
&& coredump_path[len - 2] == 'z'
|
|
Packit |
8ea169 |
&& coredump_path[len - 1] == '4'))
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
if (dd_copy_file_unpack(dd, FILENAME_COREDUMP, coredump_path))
|
|
Packit |
8ea169 |
return -1;
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
else if (len > 0)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
if (dd_copy_file(dd, FILENAME_COREDUMP, coredump_path))
|
|
Packit |
8ea169 |
return -1;
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
else
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
const char *data = NULL;
|
|
Packit |
8ea169 |
size_t data_len = 0;
|
|
Packit |
8ea169 |
int r = abrt_journal_get_field(info->ci_journal, "COREDUMP", (const void **)&data, &data_len);
|
|
Packit |
8ea169 |
if (r < 0)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
log_info("Ignoring coredumpctl entry without core dump file.");
|
|
Packit |
8ea169 |
return -1;
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
dd_save_binary(dd, FILENAME_COREDUMP, data, data_len);
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
dd_save_text(dd, FILENAME_ABRT_VERSION, VERSION);
|
|
Packit |
8ea169 |
dd_save_text(dd, FILENAME_TYPE, "CCpp");
|
|
Packit |
8ea169 |
dd_save_text(dd, FILENAME_ANALYZER, "abrt-journal-core");
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
char *reason;
|
|
Packit |
8ea169 |
if (info->ci_signal_name == NULL)
|
|
Packit |
8ea169 |
reason = xasprintf("%s killed by signal %d", info->ci_executable_name, info->ci_signal_no);
|
|
Packit |
8ea169 |
else
|
|
Packit |
8ea169 |
reason = xasprintf("%s killed by SIG%s", info->ci_executable_name, info->ci_signal_name);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
dd_save_text(dd, FILENAME_REASON, reason);
|
|
Packit |
8ea169 |
free(reason);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
char *cursor = NULL;
|
|
Packit |
8ea169 |
if (abrt_journal_get_cursor(info->ci_journal, &cursor) == 0)
|
|
Packit |
8ea169 |
dd_save_text(dd, "journald_cursor", cursor);
|
|
Packit |
8ea169 |
free(cursor);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
const char *data = NULL;
|
|
Packit |
8ea169 |
size_t data_len = 0;
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
if (!abrt_journal_get_field(info->ci_journal, "COREDUMP_CONTAINER_CMDLINE", (const void **)&data, &data_len))
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
dd_save_binary(dd, FILENAME_CONTAINER_CMDLINE, data, data_len);
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
for (size_t i = 0; i < info->ci_mapping_items; ++i)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
const char *data;
|
|
Packit |
8ea169 |
size_t data_len;
|
|
Packit |
8ea169 |
struct field_mapping *f = info->ci_mapping + i;
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
if (abrt_journal_get_field(info->ci_journal, f->name, (const void **)&data, &data_len))
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
log_info("systemd-coredump journald message misses field: '%s'", f->name);
|
|
Packit |
8ea169 |
continue;
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
dd_save_binary(dd, f->file, data, data_len);
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
return 0;
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
static int
|
|
Packit |
8ea169 |
abrt_journal_core_to_abrt_problem(struct crash_info *info, const char *dump_location)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
struct dump_dir *dd = create_dump_dir_ext(dump_location, "ccpp", info->ci_pid, /*fs owner*/0,
|
|
Packit |
8ea169 |
(save_data_call_back)save_systemd_coredump_in_dump_directory, info);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
if (dd != NULL)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
char *path = xstrdup(dd->dd_dirname);
|
|
Packit |
8ea169 |
dd_close(dd);
|
|
Packit |
8ea169 |
notify_new_path(path);
|
|
Packit |
8ea169 |
log_debug("ABRT daemon has been notified about directory: '%s'", path);
|
|
Packit |
8ea169 |
free(path);
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
return dd == NULL;
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
/*
|
|
Packit |
8ea169 |
* Creates an abrt problem from a journal message
|
|
Packit |
8ea169 |
*/
|
|
Packit |
8ea169 |
static int
|
|
Packit |
8ea169 |
abrt_journal_dump_core(abrt_journal_t *journal, const char *dump_location)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
struct crash_info info = { 0 };
|
|
Packit |
8ea169 |
info.ci_journal = journal;
|
|
Packit |
8ea169 |
info.ci_mapping = fields;
|
|
Packit |
8ea169 |
info.ci_mapping_items = sizeof(fields)/sizeof(*fields);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
/* Compatibility hack, a watch's callback gets the journal already moved
|
|
Packit |
8ea169 |
* to a next message. */
|
|
Packit |
8ea169 |
abrt_journal_next(journal);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
/* This the watch call back mentioned in the comment above. We use the
|
|
Packit |
8ea169 |
* following function also in abrt_journal_watch_cores(). */
|
|
Packit |
8ea169 |
int r = abrt_journal_core_retrieve_information(journal, &info;;
|
|
Packit |
8ea169 |
if (r != 0)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
if (r < 0)
|
|
Packit |
8ea169 |
error_msg(_("Failed to obtain all required information from journald"));
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
goto dump_cleanup;
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
r = abrt_journal_core_to_abrt_problem(&info, dump_location);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
dump_cleanup:
|
|
Packit |
8ea169 |
if (info.ci_executable_path != NULL)
|
|
Packit |
8ea169 |
free(info.ci_executable_path);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
return r;
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
/*
|
|
Packit |
8ea169 |
* A function called when a new journal core is detected.
|
|
Packit |
8ea169 |
*
|
|
Packit |
8ea169 |
* The function retrieves information from journal, checks the last occurrence
|
|
Packit |
8ea169 |
* time of the crashed executable and if there was no recent occurrence creates
|
|
Packit |
8ea169 |
* an ABRT problem from the journal message. Finally updates the last occurrence
|
|
Packit |
8ea169 |
* time.
|
|
Packit |
8ea169 |
*/
|
|
Packit |
8ea169 |
static void
|
|
Packit |
8ea169 |
abrt_journal_watch_cores(abrt_journal_watch_t *watch, void *user_data)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
const abrt_watch_core_conf_t *conf = (const abrt_watch_core_conf_t *)user_data;
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
struct crash_info info = { 0 };
|
|
Packit |
8ea169 |
info.ci_journal = abrt_journal_watch_get_journal(watch);
|
|
Packit |
8ea169 |
info.ci_mapping = fields;
|
|
Packit |
8ea169 |
info.ci_mapping_items = sizeof(fields)/sizeof(*fields);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
int r = abrt_journal_core_retrieve_information(abrt_journal_watch_get_journal(watch), &info;;
|
|
Packit |
8ea169 |
if (r)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
if (r < 0)
|
|
Packit |
8ea169 |
error_msg(_("Failed to obtain all required information from journald"));
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
goto watch_cleanup;
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
// do not dump too often
|
|
Packit |
8ea169 |
// ignore crashes of a single executable appearing in THROTTLE s (keep last 10 executable)
|
|
Packit |
8ea169 |
const unsigned current = time(NULL);
|
|
Packit |
8ea169 |
const unsigned last = abrt_journal_get_last_occurrence(info.ci_executable_path);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
if (current < last)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
error_msg("BUG: current time stamp lower than an old one");
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
if (g_verbose > 2)
|
|
Packit |
8ea169 |
abort();
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
goto watch_cleanup;
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
const unsigned sub = current - last;
|
|
Packit |
8ea169 |
if (sub < conf->awc_throttle)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
/* We don't want to update the counter here. */
|
|
Packit |
8ea169 |
error_msg(_("Not saving repeating crash after %ds (limit is %ds)"), sub, conf->awc_throttle);
|
|
Packit |
8ea169 |
goto watch_cleanup;
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
if (abrt_journal_core_to_abrt_problem(&info, conf->awc_dump_location))
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
error_msg(_("Failed to save detect problem data in abrt database"));
|
|
Packit |
8ea169 |
goto watch_cleanup;
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
abrt_journal_update_occurrence(info.ci_executable_path, current);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
watch_cleanup:
|
|
Packit |
8ea169 |
abrt_journal_save_current_position(info.ci_journal, ABRT_JOURNAL_WATCH_STATE_FILE);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
if (info.ci_executable_path != NULL)
|
|
Packit |
8ea169 |
free(info.ci_executable_path);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
return;
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
static void
|
|
Packit |
8ea169 |
watch_journald(abrt_journal_t *journal, abrt_watch_core_conf_t *conf)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
abrt_journal_watch_t *watch = NULL;
|
|
Packit |
8ea169 |
if (abrt_journal_watch_new(&watch, journal, abrt_journal_watch_cores, (void *)conf) < 0)
|
|
Packit |
8ea169 |
error_msg_and_die(_("Failed to initialize systemd-journal watch"));
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
abrt_journal_watch_run_sync(watch);
|
|
Packit |
8ea169 |
abrt_journal_watch_free(watch);
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
int
|
|
Packit |
8ea169 |
main(int argc, char *argv[])
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
/* I18n */
|
|
Packit |
8ea169 |
setlocale(LC_ALL, "");
|
|
Packit |
8ea169 |
#if ENABLE_NLS
|
|
Packit |
8ea169 |
bindtextdomain(PACKAGE, LOCALEDIR);
|
|
Packit |
8ea169 |
textdomain(PACKAGE);
|
|
Packit |
8ea169 |
#endif
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
abrt_init(argv);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
/* Can't keep these strings/structs static: _() doesn't support that */
|
|
Packit |
8ea169 |
const char *program_usage_string = _(
|
|
Packit |
8ea169 |
"& [-vsf] [-e]/[-c CURSOR] [-t INT]/[-T] [-d DIR]/[-D]\n"
|
|
Packit |
8ea169 |
"\n"
|
|
Packit |
8ea169 |
"Extract coredumps from systemd-journal\n"
|
|
Packit |
8ea169 |
"\n"
|
|
Packit |
8ea169 |
"-c and -e options conflicts because both specifies the first read message.\n"
|
|
Packit |
8ea169 |
"\n"
|
|
Packit |
8ea169 |
"-e is useful only for -f because the following of journal starts by reading \n"
|
|
Packit |
8ea169 |
"the entire journal if the last seen possition is not available.\n"
|
|
Packit |
8ea169 |
"\n"
|
|
Packit |
8ea169 |
"The last seen position is saved in "ABRT_JOURNAL_WATCH_STATE_FILE"\n"
|
|
Packit |
8ea169 |
);
|
|
Packit |
8ea169 |
enum {
|
|
Packit |
8ea169 |
OPT_v = 1 << 0,
|
|
Packit |
8ea169 |
OPT_s = 1 << 1,
|
|
Packit |
8ea169 |
OPT_d = 1 << 2,
|
|
Packit |
8ea169 |
OPT_D = 1 << 3,
|
|
Packit |
8ea169 |
OPT_c = 1 << 4,
|
|
Packit |
8ea169 |
OPT_e = 1 << 5,
|
|
Packit |
8ea169 |
OPT_t = 1 << 6,
|
|
Packit |
8ea169 |
OPT_T = 1 << 7,
|
|
Packit |
8ea169 |
OPT_f = 1 << 8,
|
|
Packit |
8ea169 |
};
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
char *cursor = NULL;
|
|
Packit |
8ea169 |
char *dump_location = NULL;
|
|
Packit |
8ea169 |
int throttle = 0;
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
/* Keep enum above and order of options below in sync! */
|
|
Packit |
8ea169 |
struct options program_options[] = {
|
|
Packit |
8ea169 |
OPT__VERBOSE(&g_verbose),
|
|
Packit |
8ea169 |
OPT_BOOL( 's', NULL, NULL, _("Log to syslog")),
|
|
Packit |
8ea169 |
OPT_STRING('d', NULL, &dump_location, "DIR", _("Create new problem directory in DIR for every coredump")),
|
|
Packit |
8ea169 |
OPT_BOOL( 'D', NULL, NULL, _("Same as -d DumpLocation, DumpLocation is specified in abrt.conf")),
|
|
Packit |
8ea169 |
OPT_STRING('c', NULL, &cursor, "CURSOR", _("Start reading systemd-journal from the CURSOR position")),
|
|
Packit |
8ea169 |
OPT_BOOL( 'e', NULL, NULL, _("Start reading systemd-journal from the end")),
|
|
Packit |
8ea169 |
OPT_INTEGER('t', NULL, &throttle, _("Throttle problem directory creation to 1 per INT second")),
|
|
Packit |
8ea169 |
OPT_BOOL( 'T', NULL, NULL, _("Same as -t INT, INT is specified in plugins/CCpp.conf")),
|
|
Packit |
8ea169 |
OPT_BOOL( 'f', NULL, NULL, _("Follow systemd-journal from the last seen position (if available)")),
|
|
Packit |
8ea169 |
OPT_END()
|
|
Packit |
8ea169 |
};
|
|
Packit |
8ea169 |
unsigned opts = parse_opts(argc, argv, program_options, program_usage_string);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
export_abrt_envvars(0);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
if ((opts & OPT_s) || getenv("ABRT_SYSLOG"))
|
|
Packit |
8ea169 |
logmode = LOGMODE_JOURNAL;
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
if ((opts & OPT_c) && (opts & OPT_e))
|
|
Packit |
8ea169 |
error_msg_and_die(_("You need to specify either -c CURSOR or -e"));
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
/* Initialize ABRT configuration */
|
|
Packit |
8ea169 |
load_abrt_conf();
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
if (opts & OPT_D)
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
if (opts & OPT_d)
|
|
Packit |
8ea169 |
show_usage_and_die(program_usage_string, program_options);
|
|
Packit |
8ea169 |
dump_location = g_settings_dump_location;
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
{ /* Load CCpp.conf */
|
|
Packit |
8ea169 |
map_string_t *settings = new_map_string();
|
|
Packit |
8ea169 |
load_abrt_plugin_conf_file("CCpp.conf", settings);
|
|
Packit |
8ea169 |
const char *value;
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
/* The following commented out lines are not supported by
|
|
Packit |
8ea169 |
* abrt-dump-journal-core but are supported by abrt-hook-ccpp */
|
|
Packit |
8ea169 |
//value = get_map_string_item_or_NULL(settings, "MakeCompatCore");
|
|
Packit |
8ea169 |
//setting_MakeCompatCore = value && string_to_bool(value);
|
|
Packit |
8ea169 |
//value = get_map_string_item_or_NULL(settings, "SaveBinaryImage");
|
|
Packit |
8ea169 |
//setting_SaveBinaryImage = value && string_to_bool(value);
|
|
Packit |
8ea169 |
//value = get_map_string_item_or_NULL(settings, "SaveFullCore");
|
|
Packit |
8ea169 |
//setting_SaveFullCore = value ? string_to_bool(value) : true;
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
value = get_map_string_item_or_NULL(settings, "VerboseLog");
|
|
Packit |
8ea169 |
if (value)
|
|
Packit |
8ea169 |
g_verbose = xatoi_positive(value);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
free_map_string(settings);
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
/* systemd-coredump creates journal messages with SYSLOG_IDENTIFIER equals
|
|
Packit |
8ea169 |
* 'systemd-coredump' and we are interested only in the systemd-coredump
|
|
Packit |
8ea169 |
* messages.
|
|
Packit |
8ea169 |
*
|
|
Packit |
8ea169 |
* Of cores, it is possible to override this when need while debugging.
|
|
Packit |
8ea169 |
*/
|
|
Packit |
8ea169 |
const char *const env_journal_filter = getenv("ABRT_DUMP_JOURNAL_CORE_DEBUG_FILTER");
|
|
Packit |
8ea169 |
GList *coredump_journal_filter = NULL;
|
|
Packit |
8ea169 |
coredump_journal_filter = g_list_append(coredump_journal_filter,
|
|
Packit |
8ea169 |
(env_journal_filter ? (gpointer)env_journal_filter : (gpointer)"SYSLOG_IDENTIFIER=systemd-coredump"));
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
abrt_journal_t *journal = NULL;
|
|
Packit |
8ea169 |
if (abrt_journal_new(&journal))
|
|
Packit |
8ea169 |
error_msg_and_die(_("Cannot open systemd-journal"));
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
if (abrt_journal_set_journal_filter(journal, coredump_journal_filter) < 0)
|
|
Packit |
8ea169 |
error_msg_and_die(_("Cannot filter systemd-journal to systemd-coredump data only"));
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
g_list_free(coredump_journal_filter);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
if ((opts & OPT_e) && abrt_journal_seek_tail(journal) < 0)
|
|
Packit |
8ea169 |
error_msg_and_die(_("Cannot seek to the end of journal"));
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
if (cursor && abrt_journal_set_cursor(journal, cursor))
|
|
Packit |
8ea169 |
error_msg_and_die(_("Failed to set systemd-journal cursor '%s'"), cursor);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
if ((opts & OPT_f))
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
if (!cursor && !(opts & OPT_e))
|
|
Packit |
8ea169 |
{
|
|
Packit |
8ea169 |
abrt_journal_restore_position(journal, ABRT_JOURNAL_WATCH_STATE_FILE);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
/* The stored position has already been seen, so move to the next one. */
|
|
Packit |
8ea169 |
abrt_journal_next(journal);
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
abrt_watch_core_conf_t conf = {
|
|
Packit |
8ea169 |
.awc_dump_location = dump_location,
|
|
Packit |
8ea169 |
.awc_throttle = throttle,
|
|
Packit |
8ea169 |
};
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
watch_journald(journal, &conf;;
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
abrt_journal_save_current_position(journal, ABRT_JOURNAL_WATCH_STATE_FILE);
|
|
Packit |
8ea169 |
}
|
|
Packit |
8ea169 |
else
|
|
Packit |
8ea169 |
abrt_journal_dump_core(journal, dump_location);
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
abrt_journal_free(journal);
|
|
Packit |
8ea169 |
free_abrt_conf_data();
|
|
Packit |
8ea169 |
|
|
Packit |
8ea169 |
return EXIT_SUCCESS;
|
|
Packit |
8ea169 |
}
|