summaryrefslogtreecommitdiff
path: root/src/core_dump_handler/dlt_cdh.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/core_dump_handler/dlt_cdh.c')
-rw-r--r--src/core_dump_handler/dlt_cdh.c458
1 files changed, 458 insertions, 0 deletions
diff --git a/src/core_dump_handler/dlt_cdh.c b/src/core_dump_handler/dlt_cdh.c
new file mode 100644
index 0000000..c573982
--- /dev/null
+++ b/src/core_dump_handler/dlt_cdh.c
@@ -0,0 +1,458 @@
+/*
+ * @licence app begin@
+ * SPDX license identifier: MPL-2.0
+ *
+ * Copyright (C) 2011-2015, BMW AG
+ *
+ * This file is part of GENIVI Project DLT - Diagnostic Log and Trace.
+ *
+ * This Source Code Form is subject to the terms of the
+ * Mozilla Public License (MPL), v. 2.0.
+ * If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * For further information see http://www.genivi.org/.
+ * @licence end@
+ */
+
+/*!
+ * \author Magneti Marelli http://www.magnetimarelli.com
+ * \author Lutz Helwing <lutz_helwing@mentor.com>
+ *
+ * \copyright Copyright © 2011-2015 BMW AG. \n
+ * License MPL-2.0: Mozilla Public License version 2.0 http://mozilla.org/MPL/2.0/.
+ *
+ * \file dlt_cdh.c
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <fcntl.h>
+#include <syslog.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <sys/statvfs.h>
+#include "dlt_cdh.h"
+#include <dirent.h>
+
+// Unusual characters in a windows filename are replaced
+#define UNUSUAL_CHARS ":/\\!*"
+#define REPLACEMENT_CHAR '_'
+
+#define COREDUMP_FILESYSTEM "/var"
+#define COREDUMP_FILESYSTEM_MIN_SIZE_MB 40
+#define COREDUMP_HANDLER_PRIORITY -19
+
+void core_locks(const proc_info_t* p_proc, int action);
+
+/* ===================================================================
+ ** Method : init_proc_info(...)
+ **
+ ** Description : initialises all members of process info structure to defined values
+ **
+ ** Parameters : INPUT p_proc
+ ** OUTPUT pointer to initialised crashed process info structure
+ **
+ ** Returns : nothing
+ ** ===================================================================*/
+void init_proc_info(proc_info_t* p_proc)
+{
+ memset(p_proc->name, 0, sizeof(p_proc->name));
+ memset(p_proc->threadname, 0, sizeof(p_proc->threadname));
+
+ p_proc->pid = 0;
+ p_proc->timestamp = 0;
+ p_proc->signal = 0;
+
+ p_proc->can_create_coredump = 1;
+ memset(&p_proc->streamer, 0, sizeof(p_proc->streamer));
+
+ memset(&p_proc->m_Ehdr, 0, sizeof(p_proc->m_Ehdr));
+ p_proc->m_pPhdr = NULL;
+ p_proc->m_Nhdr = NULL;
+
+ p_proc->m_note_page_size = 0;
+
+ memset(&p_proc->m_registers, 0, sizeof(p_proc->m_registers));
+
+ p_proc->m_crashed_pid = 0;
+ p_proc->m_crashid_phase1 = 0;
+ memset(p_proc->m_crashid, 0, sizeof(p_proc->m_crashid));
+}
+
+/* ===================================================================
+ ** Method : read_args(...)
+ **
+ ** Description : reads command line arguments
+ **
+ ** Parameters : INPUT argc
+ ** INPUT argv
+ ** OUTPUT pointer to crashed process info structure
+ **
+ ** Returns : 0 if success, else -1
+ ** ===================================================================*/
+cdh_status_t read_args(int argc, char** argv, proc_info_t* proc)
+{
+ if (argc < 5)
+ {
+ syslog(LOG_ERR, "Usage: cdh timestamp pid signal procname");
+ return CDH_NOK;
+ }
+
+ init_proc_info(proc);
+
+ if (sscanf(argv[1], "%u", &proc->timestamp) != 1)
+ {
+ syslog(LOG_ERR, "Unable to read timestamp argument <%s>. Closing", argv[1]);
+ return CDH_NOK;
+ }
+
+ if (sscanf(argv[2], "%d", &proc->pid) != 1)
+ {
+ syslog(LOG_ERR, "Unable to read pid argument <%s>. Closing", argv[2]);
+ return CDH_NOK;
+ }
+
+ if (sscanf(argv[3], "%d", &proc->signal) != 1)
+ {
+ syslog(LOG_ERR, "Unable to read signal argument <%s>. Closing", argv[3]);
+ return CDH_NOK;
+ }
+
+ // save the thread name given by the kernel
+ strncpy(proc->threadname, argv[4], sizeof(proc->threadname) - 1);
+
+ // initialize the binary name with threadname... in case we cannot read it from /proc
+ strncpy(proc->name, argv[4], sizeof(proc->name) - 1);
+
+ return CDH_OK;
+}
+
+/* ===================================================================
+ ** Method : remove_unusual_chars(...)
+ **
+ ** Description : modify the input string to change UNUSUALS_CHARS to
+ ** REPLACEMENT_CHAR
+ ** Parameters : INPUT/OUTPUT string to be modified
+ **
+ ** Returns : nothing
+ ** ===================================================================*/
+void remove_unusual_chars(char* p_string)
+{
+ unsigned int l_char_index = 0;
+
+ for (l_char_index = 0; l_char_index < sizeof(UNUSUAL_CHARS) - 1; l_char_index++)
+ {
+ char* l_str_pointer = p_string;
+
+ do
+ {
+ l_str_pointer = strchr(l_str_pointer, UNUSUAL_CHARS[l_char_index]);
+
+ if (l_str_pointer != NULL)
+ {
+ *l_str_pointer = REPLACEMENT_CHAR;
+ l_str_pointer++;
+ }
+ }
+ while (l_str_pointer != NULL);
+ }
+}
+
+/* ===================================================================
+ ** Method : check_disk_space(...)
+ **
+ ** Description : check if there is sufficient disk space to write a coredump
+ ** Parameters : INPUT/OUTPUT string to be modified
+ **
+ ** Returns : 0 if success, else -1
+ ** ===================================================================*/
+cdh_status_t check_disk_space()
+{
+ struct statvfs stat;
+ unsigned long free_size = 0;
+
+ if (statvfs(COREDUMP_FILESYSTEM, &stat) < 0)
+ {
+ syslog(LOG_ERR, "ERR cannot stat disk space on %s: %s", COREDUMP_FILESYSTEM, strerror(errno));
+ return CDH_NOK;
+ }
+
+ // free space: size of block * number of free blocks (>>20 => MB)
+ free_size = (stat.f_bsize * stat.f_bavail) >> 20;
+ if (free_size < COREDUMP_FILESYSTEM_MIN_SIZE_MB)
+ {
+ syslog(LOG_WARNING, "ERR insufficient disk space for coredump: %ld MB.", free_size);
+ return CDH_NOK;
+ }
+
+ syslog(LOG_INFO, "INFO disk space for coredump: %ld MB.", free_size);
+ return CDH_OK;
+}
+
+void clean_core_tmp_dir()
+{
+ DIR *d = NULL;
+ struct dirent *dir = NULL;
+
+ if ((d = opendir(CORE_TMP_DIRECTORY)) != NULL)
+ {
+ char lockfilepath[CORE_MAX_FILENAME_LENGTH];
+
+ while ((dir = readdir(d)) != NULL)
+ {
+ struct stat unused_stat;
+
+ // check if lock file exists
+ snprintf(lockfilepath, sizeof(lockfilepath), "%s/%s", CORE_LOCK_DIRECTORY, dir->d_name);
+
+ if (stat(lockfilepath, &unused_stat) != 0)
+ {
+ // No lock file found for this coredump => from previous LC => delete
+ char filepath[CORE_MAX_FILENAME_LENGTH] = { 0 };
+
+ snprintf(filepath, sizeof(filepath), "%s/%s", CORE_TMP_DIRECTORY, dir->d_name);
+
+ syslog(LOG_INFO, "Cleaning %s: delete file %s", CORE_TMP_DIRECTORY, filepath);
+
+ unlink(filepath);
+ }
+ }
+
+ closedir(d);
+ }
+}
+
+/* ===================================================================
+ ** Method : check_core_directory(...)
+ **
+ ** Description : checks the availability of core dumps directory.
+ ** if not available, there is an installation issue.
+ **
+ ** Parameters :
+ **
+ ** Returns : 0 if success, else -1
+ ** ===================================================================*/
+cdh_status_t check_and_create_directory(const char* p_dirname, int create_silently)
+{
+ int l_need_create = 0;
+ int l_need_delete = 0;
+
+ struct stat l_stat;
+
+ if (lstat(p_dirname, &l_stat) < 0)
+ {
+ l_need_create = 1;
+ }
+ else if (!S_ISDIR(l_stat.st_mode))
+ {
+ l_need_delete = 1;
+ l_need_create = 1;
+ }
+
+ if (l_need_delete > 0)
+ {
+ syslog(LOG_WARNING, "WARN core directory '%s' is not a directory => removing it", p_dirname);
+
+ if (unlink(p_dirname) == -1)
+ {
+ syslog(LOG_ERR, "ERR core directory '%s' cannot be unlinked: %s", p_dirname, strerror(errno));
+ return CDH_NOK;
+ }
+ }
+ if (l_need_create > 0)
+ {
+ if (create_silently == 0)
+ syslog(LOG_WARNING, "WARN core directory '%s' does not exist => creation", p_dirname);
+
+ if (mkdir(p_dirname, 0666) == -1)
+ {
+ syslog(LOG_ERR, "ERR core directory '%s' cannot be created: %s", p_dirname, strerror(errno));
+ return CDH_NOK;
+ }
+ }
+
+ return CDH_OK;
+}
+
+/* ===================================================================
+ ** Method : check_core_directory(...)
+ **
+ ** Description : checks the availability of core dumps directory.
+ ** if not available, there is an installation issue.
+ **
+ ** Parameters :
+ **
+ ** Returns : 0 if success, else -1
+ ** ===================================================================*/
+cdh_status_t check_core_directory()
+{
+ if (check_and_create_directory(CORE_DIRECTORY, 0) < 0)
+ return CDH_NOK;
+
+ if (check_and_create_directory(CORE_TMP_DIRECTORY, 0) < 0)
+ return CDH_NOK;
+
+ if (check_and_create_directory(CORE_LOCK_DIRECTORY, 1) < 0)
+ return CDH_NOK;
+
+ clean_core_tmp_dir();
+
+ return CDH_OK;
+}
+
+/* ===================================================================
+ ** Method : move_to_core_directory(...)
+ **
+ ** Description : move the coredump and context files
+ ** from temporary dir to final core directory
+ **
+ ** Parameters :
+ **
+ ** Returns : 0 if success, else -1
+ ** ===================================================================*/
+cdh_status_t move_to_core_directory(proc_info_t* p_proc)
+{
+ char l_src_filename[CORE_MAX_FILENAME_LENGTH] = { 0 };
+ char l_dst_filename[CORE_MAX_FILENAME_LENGTH] = { 0 };
+ char* patterns[] = { CORE_FILE_PATTERN, CONTEXT_FILE_PATTERN };
+ unsigned int pattern_num = 0;
+
+ if (p_proc == NULL)
+ return CDH_NOK;
+
+ for (pattern_num = 0; pattern_num < sizeof(patterns) / sizeof(char*); pattern_num++)
+ {
+ // Don't move coredump if it cannot be created
+ if (p_proc->can_create_coredump == 0 && pattern_num == 0)
+ continue;
+
+ snprintf(l_src_filename, sizeof(l_src_filename), patterns[pattern_num],
+ CORE_TMP_DIRECTORY, p_proc->timestamp, p_proc->name, p_proc->pid);
+
+ snprintf(l_dst_filename, sizeof(l_dst_filename), patterns[pattern_num],
+ CORE_DIRECTORY, p_proc->timestamp, p_proc->name, p_proc->pid);
+
+ syslog(LOG_INFO, "Moving coredump from %s to %s", l_src_filename, l_dst_filename);
+
+ if (rename(l_src_filename, l_dst_filename) < 0)
+ syslog(LOG_ERR, "Moving failed: %s", strerror(errno));
+ }
+
+ return CDH_OK;
+}
+
+/* ===================================================================
+ ** Method : main(...)
+ **
+ ** Description :
+ **
+ ** Parameters : argc, argv
+ **
+ ** Returns :
+ ** ===================================================================*/
+int main(int argc, char* argv[])
+{
+ proc_info_t l_proc_info;
+// char l_exec_name[CORE_MAX_FILENAME_LENGTH] = {0};
+
+ openlog("CoredumpHandler", 0, LOG_DAEMON);
+
+ if (read_args(argc, argv, &l_proc_info) < 0)
+ exit(-1);
+
+ if (get_exec_name(l_proc_info.pid, l_proc_info.name, sizeof(l_proc_info.name)) != 0)
+ syslog(LOG_ERR, "Failed to get executable name");
+
+ syslog(LOG_NOTICE, "Handling coredump procname:%s pid:%d timest:%d signal:%d",
+ l_proc_info.name,
+ l_proc_info.pid,
+ l_proc_info.timestamp,
+ l_proc_info.signal);
+
+ // Increase priority of the coredump handler
+ if (nice(COREDUMP_HANDLER_PRIORITY) != COREDUMP_HANDLER_PRIORITY)
+ syslog(LOG_WARNING, "Failed to change CDH priority");
+
+ if (check_disk_space() < 0)
+ {
+ //return CDH_NOK;
+ l_proc_info.can_create_coredump = 0;
+ }
+
+ if (check_core_directory() < 0)
+ {
+ //return CDH_NOK;
+ l_proc_info.can_create_coredump = 0;
+ }
+
+ remove_unusual_chars(l_proc_info.name);
+
+ core_locks(&l_proc_info, 1);
+
+ write_proc_context(&l_proc_info);
+
+ treat_coredump(&l_proc_info);
+
+ move_to_core_directory(&l_proc_info);
+
+ core_locks(&l_proc_info, 0);
+
+ treat_crash_data(&l_proc_info);
+
+ closelog();
+
+ return CDH_OK;
+}
+
+void core_locks(const proc_info_t* p_proc, int action)
+{
+ char l_lockfilepath[CORE_MAX_FILENAME_LENGTH] = { 0 };
+ char* patterns[] = { CORE_FILE_PATTERN, CONTEXT_FILE_PATTERN };
+ unsigned int pattern_num = 0;
+ int fd_lockfile = -1;
+
+ if (p_proc == NULL)
+ return;
+
+ for (pattern_num = 0; pattern_num < sizeof(patterns) / sizeof(char*); pattern_num++)
+ {
+ snprintf(l_lockfilepath, sizeof(l_lockfilepath), patterns[pattern_num],
+ CORE_LOCK_DIRECTORY, p_proc->timestamp, p_proc->name, p_proc->pid);
+
+ switch (action)
+ {
+ case 0:
+ {
+ unlink(l_lockfilepath);
+ break;
+ }
+
+ case 1:
+ {
+ if ((fd_lockfile = open(l_lockfilepath, O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR | S_IWUSR)) >= 0)
+ {
+ if (write(fd_lockfile, "1", 1) < 0)
+ syslog(LOG_WARNING, "Failed to write lockfile %d: %s", fd_lockfile, strerror(errno));
+
+ close(fd_lockfile);
+ }
+ else
+ {
+ syslog(LOG_WARNING, "Failed to open lockfile %s: %s", l_lockfilepath, strerror(errno));
+ }
+
+ break;
+ }
+
+ default:
+ break;
+ }
+ }
+}