summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/dlt.conf.5.md20
-rw-r--r--include/dlt/dlt_common.h55
-rw-r--r--include/dlt/dlt_multiple_files.h154
-rw-r--r--include/dlt/dlt_offline_trace.h103
-rw-r--r--src/console/dlt-convert.c11
-rw-r--r--src/daemon/CMakeLists.txt1
-rw-r--r--src/daemon/dlt-daemon.c52
-rw-r--r--src/daemon/dlt-daemon.h11
-rw-r--r--src/daemon/dlt.conf9
-rw-r--r--src/gateway/dlt_gateway.c3
-rw-r--r--src/lib/CMakeLists.txt1
-rw-r--r--src/shared/dlt_common.c156
-rw-r--r--src/shared/dlt_multiple_files.c499
-rw-r--r--src/shared/dlt_offline_trace.c397
-rw-r--r--tests/CMakeLists.txt6
-rw-r--r--tests/gtest_dlt_daemon_multiple_files_logging.cpp277
16 files changed, 1247 insertions, 508 deletions
diff --git a/doc/dlt.conf.5.md b/doc/dlt.conf.5.md
index aeb2dfc..8090438 100644
--- a/doc/dlt.conf.5.md
+++ b/doc/dlt.conf.5.md
@@ -79,6 +79,26 @@ If LoggingMode is set to 2 logs are written to the file path given here.
Default: /tmp/dlt.log
+## EnableLoggingFileLimit
+
+Only relevant for logging in file (LoggingMode = 2).
+If EnableLoggingFileLimit is set to 0, the daemon logs to one logging file without any size limit.
+If EnableLoggingFileLimit is set to 1, the daemon considers the size limits configured by LoggingFileSize and LoggingFileMaxSize. If the limits are configured accordingly, multiple log files are used.
+
+ Default: 0
+
+## LoggingFileSize
+
+Only considered for logging in file (LoggingMode = 2) and EnableLoggingFileLimit = 1. Maximum size in bytes of one logging file.
+
+ Default: 250000
+
+## LoggingFileMaxSize
+
+Only considered for logging in file (LoggingMode = 2) and EnableLoggingFileLimit = 1. Maximum size in bytes of all logging files.
+
+ Default: 1000000
+
## TimeOutOnSend
Socket timeout in seconds for sending to clients.
diff --git a/include/dlt/dlt_common.h b/include/dlt/dlt_common.h
index ead124f..41dcf68 100644
--- a/include/dlt/dlt_common.h
+++ b/include/dlt/dlt_common.h
@@ -191,13 +191,13 @@
# define LOG_DAEMON (3 << 3)
# endif
-enum {
+typedef enum {
DLT_LOG_TO_CONSOLE = 0,
DLT_LOG_TO_SYSLOG = 1,
DLT_LOG_TO_FILE = 2,
DLT_LOG_TO_STDERR = 3,
DLT_LOG_DROPPED = 4
-};
+} DltLoggingMode;
/**
* The standard TCP Port used for DLT daemon, can be overwritten via -p \<port\> when starting dlt-daemon
@@ -1670,6 +1670,57 @@ void dlt_hex_ascii_to_binary(const char *ptr, uint8_t *binary, int *size);
*/
int dlt_execute_command(char *filename, char *command, ...);
+/**
+ * Return the extension of given file name.
+ * @param filename Only file names without prepended path allowed.
+ * @return pointer to extension
+ */
+char *get_filename_ext(const char *filename);
+
+/**
+ * Extract the base name of given file name (without the extension).
+ * @param abs_file_name Absolute path to file name.
+ * @param base_name Base name it is extracted to.
+ * @param base_name_length Base name length.
+ * @return indicating success
+ */
+bool dlt_extract_base_name_without_ext(const char* const abs_file_name, char* base_name, long base_name_len);
+
+/**
+ * Initialize (external) logging facility
+ * @param mode DltLoggingMode, 0 = log to stdout, 1 = log to syslog, 2 = log to file, 3 = log to stderr
+ * @param enable_multiple_logfiles, true if multiple logfiles (incl. size limits) should be use
+ * @param logging_file_size, maximum size in bytes of one logging file
+ * @param logging_files_max_size, maximum size in bytes of all logging files
+ */
+DltReturnValue dlt_log_init_multiple_logfiles_support(DltLoggingMode mode, bool enable_multiple_logfiles, int logging_file_size, int logging_files_max_size);
+
+/**
+ * Initialize (external) logging facility for single logfile.
+ */
+DltReturnValue dlt_log_init_single_logfile();
+
+/**
+ * Initialize (external) logging facility for multiple files logging.
+ */
+DltReturnValue dlt_log_init_multiple_logfiles(int logging_file_size, int logging_files_max_size);
+
+/**
+ * Logs into log files represented by the multiple files buffer.
+ * @param format First element in a specific format that will be logged.
+ * @param ... Further elements in a specific format that will be logged.
+ */
+void dlt_log_multiple_files_write(const char* format, ...);
+
+void dlt_log_free_single_logfile();
+
+void dlt_log_free_multiple_logfiles();
+
+/**
+ * Checks whether (internal) logging in multiple files is active.
+ */
+bool dlt_is_log_in_multiple_files_active();
+
# ifdef __cplusplus
}
# endif
diff --git a/include/dlt/dlt_multiple_files.h b/include/dlt/dlt_multiple_files.h
new file mode 100644
index 0000000..d5e13c7
--- /dev/null
+++ b/include/dlt/dlt_multiple_files.h
@@ -0,0 +1,154 @@
+/*
+ * 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/.
+ */
+
+/*!
+ * \author
+ * Oleg Tropmann <oleg.tropmann@daimler.com>
+ * Daniel Weber <daniel.w.weber@daimler.com>
+ *
+ * \copyright Copyright © 2022 Mercedes-Benz AG. \n
+ * License MPL-2.0: Mozilla Public License version 2.0 http://mozilla.org/MPL/2.0/.
+ *
+ * \file dlt_multiple_files.h
+ */
+
+
+#ifndef DLT_MULTIPLE_FILES_H
+#define DLT_MULTIPLE_FILES_H
+
+#include <limits.h>
+
+#include "dlt_common.h"
+#include "dlt_types.h"
+
+#define MULTIPLE_FILES_FILENAME_INDEX_DELIM "."
+#define MULTIPLE_FILES_FILENAME_TIMESTAMP_DELIM "_"
+
+/**
+ * Represents a ring buffer of multiple files of identical file size.
+ * File names differ in timestamp or index (depending on chosen mode).
+ * This buffer is used, e.g. for dlt offline traces and the internal dlt logging (dlt.log)
+ */
+typedef struct
+{
+ char directory[NAME_MAX + 1];/**< (String) Store DLT messages to local directory */
+ char filename[NAME_MAX + 1]; /**< (String) Filename of currently used log file */
+ int fileSize; /**< (int) Maximum size in bytes of one file, e.g. for offline trace 1000000 as default */
+ int maxSize; /**< (int) Maximum size of all files, e.g. for offline trace 4000000 as default */
+ bool filenameTimestampBased; /**< (bool) is filename timestamp based? false = index based (Default: true) */
+ char filenameBase[NAME_MAX + 1];/**< (String) Prefix of file name */
+ char filenameExt[NAME_MAX + 1];/**< (String) Extension of file name */
+ int ohandle; /**< (int) file handle to current output file */
+} MultipleFilesRingBuffer;
+
+/**
+ * Initialise the multiple files buffer.
+ * This function call opens the currently used log file.
+ * A check of the complete size of the files is done during startup.
+ * Old files are deleted, if there is not enough space left to create new file.
+ * This function must be called before using further multiple files functions.
+ * @param files_buffer pointer to MultipleFilesRingBuffer struct.
+ * @param directory directory where to store multiple files.
+ * @param file_size maximum size of one files.
+ * @param max_size maximum size of complete multiple files in bytes.
+ * @param filename_timestamp_based filename to be created on timestamp-based or index-based.
+ * @param append Indicates whether the current log files is used or a new file should be be created
+ * @param filename_base Base name.
+ * @param filename_ext File extension.
+ * @return negative value if there was an error.
+ */
+extern DltReturnValue multiple_files_buffer_init(MultipleFilesRingBuffer *files_buffer,
+ const char *directory,
+ int file_size,
+ int max_size,
+ bool filename_timestamp_based,
+ bool append,
+ const char *filename_base,
+ const char *filename_ext);
+
+/**
+ * Uninitialise the multiple files buffer.
+ * This function call closes currently used log file.
+ * This function must be called after usage of multiple files.
+ * @param files_buffer pointer to MultipleFilesRingBuffer struct.
+ * @return negative value if there was an error.
+*/
+extern DltReturnValue multiple_files_buffer_free(const MultipleFilesRingBuffer *files_buffer);
+
+/**
+ * Write data into multiple files.
+ * If the current used log file exceeds the max file size, new log file is created.
+ * A check of the complete size of the multiple files is done before new file is created.
+ * Old files are deleted, if there is not enough space left to create new file.
+ * @param files_buffer pointer to MultipleFilesRingBuffer struct.
+ * @param data pointer to first data block to be written, null if not used.
+ * @param size size in bytes of first data block to be written, 0 if not used.
+ * @return negative value if there was an error.
+ */
+extern DltReturnValue multiple_files_buffer_write(MultipleFilesRingBuffer *files_buffer,
+ const unsigned char *data,
+ int size);
+
+/**
+ * First the limits are verified. Then the oldest file is deleted and a new file is created on demand.
+ * @param files_buffer pointer to MultipleFilesRingBuffer struct.
+ * @param size size in bytes of data that will be written.
+ */
+void multiple_files_buffer_rotate_file(MultipleFilesRingBuffer *files_buffer,
+ int size);
+
+/**
+ * Writes the given data to current file specified by corresponding file handle.
+ * @param files_buffer pointer to MultipleFilesRingBuffer struct.
+ * @param data pointer to data block to be written, null if not used.
+ * @param size size in bytes of given data block to be written, 0 if not used.
+ */
+DltReturnValue multiple_files_buffer_write_chunk(const MultipleFilesRingBuffer *files_buffer,
+ const unsigned char *data,
+ int size);
+
+/**
+ * Get size of currently used multiple files buffer.
+ * @return size in bytes.
+ */
+extern ssize_t multiple_files_buffer_get_total_size(const MultipleFilesRingBuffer *files_buffer);
+
+/**
+ * Provides info about the multiple files storage directory.
+ * @param path path of the storage directory
+ * @param file_name filename to search for
+ * @param newest pointer to store newest filename
+ * @param oldest pointer to store oldest filename
+ * @return num of files in the directory.
+ */
+unsigned int multiple_files_buffer_storage_dir_info(const char *path, const char *file_name,
+ char *newest, char *oldest);
+
+/**
+ * Creates filename with index.
+ * @param files_buffer pointer to MultipleFilesRingBuffer struct.
+ * @param length the maximum length of the log_file_name.
+ * @param idx index to be used for file name creation.
+ */
+void multiple_files_buffer_file_name(MultipleFilesRingBuffer *files_buffer, size_t length, unsigned int idx);
+
+/**
+ * Generates index for log file name.
+ * @param file filename supplied to create index.
+ * @return the index to be used for log file name.
+ */
+unsigned int multiple_files_buffer_get_idx_of_log_file(char *file);
+
+#endif // DLT_MULTIPLE_FILES_H
diff --git a/include/dlt/dlt_offline_trace.h b/include/dlt/dlt_offline_trace.h
index b5e096c..8e571eb 100644
--- a/include/dlt/dlt_offline_trace.h
+++ b/include/dlt/dlt_offline_trace.h
@@ -57,105 +57,32 @@
#include <limits.h>
+#include "dlt_multiple_files.h"
#include "dlt_types.h"
#define DLT_OFFLINETRACE_FILENAME_BASE "dlt_offlinetrace"
-#define DLT_OFFLINETRACE_FILENAME_INDEX_DELI "."
-#define DLT_OFFLINETRACE_FILENAME_TIMESTAMP_DELI "_"
#define DLT_OFFLINETRACE_FILENAME_EXT ".dlt"
-typedef struct
-{
- char directory[NAME_MAX + 1];/**< (String) Store DLT messages to local directory */
- char filename[NAME_MAX + 1]; /**< (String) Filename of currently used log file */
- int fileSize; /**< (int) Maximum size in bytes of one trace file (Default: 1000000) */
- int maxSize; /**< (int) Maximum size of all trace files (Default: 4000000) */
- int filenameTimestampBased; /**< (int) timestamp based or index based (Default: 1 Timestamp based) */
- int ohandle;
-} DltOfflineTrace;
-
-/**
- * Initialise the offline trace
- * This function call opens the currently used log file.
- * A check of the complete size of the offline trace is done during startup.
- * Old files are deleted, if there is not enough space left to create new file.
- * This function must be called before using further offline trace functions.
- * @param trace pointer to offline trace structure
- * @param directory directory where to store offline trace files
- * @param fileSize maximum size of one offline trace file.
- * @param maxSize maximum size of complete offline trace in bytes.
- *.@param filenameTimestampBased filename to be created on timestamp based or index based
- * @return negative value if there was an error
- */
-extern DltReturnValue dlt_offline_trace_init(DltOfflineTrace *trace,
- const char *directory,
- int fileSize,
- int maxSize,
- int filenameTimestampBased);
-
/**
- * Uninitialise the offline trace
- * This function call closes currently used log file.
- * This function must be called after usage of offline trace
- * @param buf pointer to offline trace structure
- * @return negative value if there was an error
- */
-extern DltReturnValue dlt_offline_trace_free(DltOfflineTrace *buf);
-
-/**
- * Write data into offline trace
+ * Write data into offline traces.
* If the current used log file exceeds the max file size, new log file is created.
- * A check of the complete size of the offline trace is done before new file is created.
+ * A check of the complete size of the offline traces is done before new file is created.
* Old files are deleted, if there is not enough space left to create new file.
- * @param trace pointer to offline trace structure
- * @param data1 pointer to first data block to be written, null if not used
- * @param size1 size in bytes of first data block to be written, 0 if not used
- * @param data2 pointer to second data block to be written, null if not used
- * @param size2 size in bytes of second data block to be written, 0 if not used
- * @param data3 pointer to third data block to be written, null if not used
- * @param size3 size in bytes of third data block to be written, 0 if not used
- * @return negative value if there was an error
+ * @param trace pointer to MultipleFilesRingBuffer struct.
+ * @param data1 pointer to first data block to be written, null if not used.
+ * @param size1 size in bytes of first data block to be written, 0 if not used.
+ * @param data2 pointer to second data block to be written, null if not used.
+ * @param size2 size in bytes of second data block to be written, 0 if not used.
+ * @param data3 pointer to third data block to be written, null if not used.
+ * @param size3 size in bytes of third data block to be written, 0 if not used.
+ * @return negative value if there was an error.
*/
-extern DltReturnValue dlt_offline_trace_write(DltOfflineTrace *trace,
- unsigned char *data1,
+extern DltReturnValue dlt_offline_trace_write(MultipleFilesRingBuffer *trace,
+ const unsigned char *data1,
int size1,
- unsigned char *data2,
+ const unsigned char *data2,
int size2,
- unsigned char *data3,
+ const unsigned char *data3,
int size3);
-/**
- * Get size of currently used offline trace buffer
- * @return size in bytes
- */
-extern ssize_t dlt_offline_trace_get_total_size(DltOfflineTrace *trace);
-
-/**
- * Provides info about the offline logs storage directory
- * @param path path of the storage directory
- * @param file_name filename to search for
- * @param newest pointer to store newest filename
- * @param oldest pointer to store oldest filename
- * @return num of files in the directory
- */
-unsigned int dlt_offline_trace_storage_dir_info(char *path, char *file_name, char *newest, char *oldest);
-
-/**
- * creates filename with index
- * @param log_file_name file name created with index
- * @param length the maximum length of the log_file_name
- * @param name filename base
- * @param idx index to be used for file name creation
- */
-void dlt_offline_trace_file_name(char *log_file_name, size_t length,
- char *name, unsigned int idx);
-
-/**
- * generates index for log file name
- * @param file filename supplied to create index
- * @return the index to be used for log file name
- */
-unsigned int dlt_offline_trace_get_idx_of_log_file(char *file);
-
-
#endif /* DLT_OFFLINE_TRACE_H */
diff --git a/src/console/dlt-convert.c b/src/console/dlt-convert.c
index b97a11f..8c72b60 100644
--- a/src/console/dlt-convert.c
+++ b/src/console/dlt-convert.c
@@ -126,17 +126,6 @@ void usage()
printf(" -t Handling input compressed files (tar.gz)\n");
}
-char *get_filename_ext(const char *filename)
-{
- if (filename == NULL)
- fprintf(stderr, "ERROR: %s: invalid arguments\n", __FUNCTION__);
-
- char *dot = strrchr(filename, '.');
- if(!dot || dot == filename)
- return "";
- return dot + 1;
-}
-
void empty_dir(const char *dir)
{
struct dirent **files = { 0 };
diff --git a/src/daemon/CMakeLists.txt b/src/daemon/CMakeLists.txt
index f7aaa26..9916bb4 100644
--- a/src/daemon/CMakeLists.txt
+++ b/src/daemon/CMakeLists.txt
@@ -32,6 +32,7 @@ set(dlt_daemon_SRCS
${PROJECT_SOURCE_DIR}/src/lib/dlt_client.c
${PROJECT_SOURCE_DIR}/src/shared/dlt_common.c
${PROJECT_SOURCE_DIR}/src/shared/dlt_config_file_parser.c
+ ${PROJECT_SOURCE_DIR}/src/shared/dlt_multiple_files.c
${PROJECT_SOURCE_DIR}/src/shared/dlt_offline_trace.c
${PROJECT_SOURCE_DIR}/src/shared/dlt_protocol.c
${PROJECT_SOURCE_DIR}/src/shared/dlt_user_shared.c
diff --git a/src/daemon/dlt-daemon.c b/src/daemon/dlt-daemon.c
index c8a8506..ee0f772 100644
--- a/src/daemon/dlt-daemon.c
+++ b/src/daemon/dlt-daemon.c
@@ -46,6 +46,8 @@
#endif
#include <sys/stat.h>
#include <sys/time.h>
+#include <libgen.h>
+
#if defined(linux) && defined(__NR_statx)
# include <linux/stat.h>
#endif
@@ -338,7 +340,7 @@ int option_file_parser(DltDaemonLocal *daemon_local)
daemon_local->flags.offlineTraceDirectory[0] = 0;
daemon_local->flags.offlineTraceFileSize = 1000000;
daemon_local->flags.offlineTraceMaxSize = 4000000;
- daemon_local->flags.offlineTraceFilenameTimestampBased = 1;
+ daemon_local->flags.offlineTraceFilenameTimestampBased = true;
daemon_local->flags.loggingMode = DLT_LOG_TO_CONSOLE;
daemon_local->flags.loggingLevel = LOG_INFO;
@@ -356,6 +358,9 @@ int option_file_parser(DltDaemonLocal *daemon_local)
dlt_vlog(LOG_WARNING, "%s: snprintf truncation/error(%ld) %s\n",
__func__, n, daemon_local->flags.loggingFilename);
}
+ daemon_local->flags.enableLoggingFileLimit = false;
+ daemon_local->flags.loggingFileSize = 250000;
+ daemon_local->flags.loggingFileMaxSize = 1000000;
daemon_local->timeoutOnSend = 4;
daemon_local->RingbufferMinSize = DLT_DAEMON_RINGBUFFER_MIN_SIZE;
@@ -514,7 +519,7 @@ int option_file_parser(DltDaemonLocal *daemon_local)
}
else if (strcmp(token, "LoggingMode") == 0)
{
- daemon_local->flags.loggingMode = atoi(value);
+ daemon_local->flags.loggingMode = (DltLoggingMode)atoi(value);
/*printf("Option: %s=%s\n",token,value); */
}
else if (strcmp(token, "LoggingLevel") == 0)
@@ -530,6 +535,18 @@ int option_file_parser(DltDaemonLocal *daemon_local)
daemon_local->flags.loggingFilename[sizeof(daemon_local->flags.loggingFilename) - 1] = 0;
/*printf("Option: %s=%s\n",token,value); */
}
+ else if (strcmp(token, "EnableLoggingFileLimit") == 0)
+ {
+ daemon_local->flags.enableLoggingFileLimit = (bool)atoi(value);
+ }
+ else if (strcmp(token, "LoggingFileSize") == 0)
+ {
+ daemon_local->flags.loggingFileSize = atoi(value);
+ }
+ else if (strcmp(token, "LoggingFileMaxSize") == 0)
+ {
+ daemon_local->flags.loggingFileMaxSize = atoi(value);
+ }
else if (strcmp(token, "TimeOutOnSend") == 0)
{
daemon_local->timeoutOnSend = atoi(value);
@@ -578,7 +595,7 @@ int option_file_parser(DltDaemonLocal *daemon_local)
}
else if (strcmp(token, "OfflineTraceFileNameTimestampBased") == 0)
{
- daemon_local->flags.offlineTraceFilenameTimestampBased = atoi(value);
+ daemon_local->flags.offlineTraceFilenameTimestampBased = (bool)atoi(value);
/*printf("Option: %s=%s\n",token,value); */
}
else if (strcmp(token, "SendECUSoftwareVersion") == 0)
@@ -941,7 +958,10 @@ int main(int argc, char *argv[])
dlt_log_set_filename(daemon_local.flags.loggingFilename);
dlt_log_set_level(daemon_local.flags.loggingLevel);
DltReturnValue log_init_result =
- dlt_log_init(daemon_local.flags.loggingMode);
+ dlt_log_init_multiple_logfiles_support(daemon_local.flags.loggingMode,
+ daemon_local.flags.enableLoggingFileLimit,
+ daemon_local.flags.loggingFileSize,
+ daemon_local.flags.loggingFileMaxSize);
if (log_init_result != DLT_RETURN_OK) {
fprintf(stderr, "Failed to init internal logging\n");
@@ -1126,6 +1146,8 @@ int main(int argc, char *argv[])
dlt_log(LOG_NOTICE, "Leaving DLT daemon\n");
+ dlt_log_free();
+
return 0;
} /* main() */
@@ -1171,7 +1193,12 @@ int dlt_daemon_local_init_p1(DltDaemon *daemon, DltDaemonLocal *daemon_local, in
/* Re-Initialize internal logging facility after fork */
dlt_log_set_filename(daemon_local->flags.loggingFilename);
dlt_log_set_level(daemon_local->flags.loggingLevel);
- dlt_log_init(daemon_local->flags.loggingMode);
+ // 'free' dlt logging and corresponding file handle before re-initializing
+ dlt_log_free();
+ dlt_log_init_multiple_logfiles_support(daemon_local->flags.loggingMode,
+ daemon_local->flags.enableLoggingFileLimit,
+ daemon_local->flags.loggingFileSize,
+ daemon_local->flags.loggingFileMaxSize);
/* initialise structure to use DLT file */
ret = dlt_file_init(&(daemon_local->file), daemon_local->flags.vflag);
@@ -1218,11 +1245,14 @@ int dlt_daemon_local_init_p2(DltDaemon *daemon, DltDaemonLocal *daemon_local, in
/* init offline trace */
if (((daemon->mode == DLT_USER_MODE_INTERNAL) || (daemon->mode == DLT_USER_MODE_BOTH)) &&
daemon_local->flags.offlineTraceDirectory[0]) {
- if (dlt_offline_trace_init(&(daemon_local->offlineTrace),
- daemon_local->flags.offlineTraceDirectory,
- daemon_local->flags.offlineTraceFileSize,
- daemon_local->flags.offlineTraceMaxSize,
- daemon_local->flags.offlineTraceFilenameTimestampBased) == -1) {
+ if (multiple_files_buffer_init(&(daemon_local->offlineTrace),
+ daemon_local->flags.offlineTraceDirectory,
+ daemon_local->flags.offlineTraceFileSize,
+ daemon_local->flags.offlineTraceMaxSize,
+ daemon_local->flags.offlineTraceFilenameTimestampBased,
+ false,
+ DLT_OFFLINETRACE_FILENAME_BASE,
+ DLT_OFFLINETRACE_FILENAME_EXT) == -1) {
dlt_log(LOG_ERR, "Could not initialize offline trace\n");
return -1;
}
@@ -1786,7 +1816,7 @@ void dlt_daemon_local_cleanup(DltDaemon *daemon, DltDaemonLocal *daemon_local, i
/* free shared memory */
if (daemon_local->flags.offlineTraceDirectory[0])
- dlt_offline_trace_free(&(daemon_local->offlineTrace));
+ multiple_files_buffer_free(&(daemon_local->offlineTrace));
/* Ignore result */
dlt_file_free(&(daemon_local->file), daemon_local->flags.vflag);
diff --git a/src/daemon/dlt-daemon.h b/src/daemon/dlt-daemon.h
index 071562f..cc879cb 100644
--- a/src/daemon/dlt-daemon.h
+++ b/src/daemon/dlt-daemon.h
@@ -69,6 +69,7 @@
#include <limits.h> /* for NAME_MAX */
#include <sys/time.h>
+#include <stdarg.h>
#include "dlt_daemon_common.h"
#include "dlt_user_shared.h"
@@ -103,10 +104,13 @@ typedef struct
char offlineTraceDirectory[DLT_DAEMON_FLAG_MAX]; /**< (String: Directory) Store DLT messages to local directory (Default: /etc/dlt.conf) */
int offlineTraceFileSize; /**< (int) Maximum size in bytes of one trace file (Default: 1000000) */
int offlineTraceMaxSize; /**< (int) Maximum size of all trace files (Default: 4000000) */
- int offlineTraceFilenameTimestampBased; /**< (int) timestamp based or index based (Default: 1 Timestamp based) */
- int loggingMode; /**< (int) The logging console for internal logging of dlt-daemon (Default: 0) */
+ bool offlineTraceFilenameTimestampBased; /**< (Boolean) timestamp based or index based (Default: true=Timestamp based) */
+ DltLoggingMode loggingMode; /**< (int) The logging console for internal logging of dlt-daemon (Default: 0) */
int loggingLevel; /**< (int) The logging level for internal logging of dlt-daemon (Default: 6) */
char loggingFilename[DLT_DAEMON_FLAG_MAX]; /**< (String: Filename) The logging filename if internal logging mode is log to file (Default: /tmp/log) */
+ bool enableLoggingFileLimit; /**< (Boolean) Indicate whether size of logging file(s) is limited (Default: false) */
+ int loggingFileSize; /**< (int) Maximum size in bytes of one logging file (Default: 250000) */
+ int loggingFileMaxSize; /**< (int) Maximum size in bytes of all logging files (Default: 1000000) */
int sendECUSoftwareVersion; /**< (Boolean) Send ECU software version perdiodically */
char pathToECUSoftwareVersion[DLT_DAEMON_FLAG_MAX]; /**< (String: Filename) The file from which to read the ECU version from. */
int sendTimezone; /**< (Boolean) Send Timezone perdiodically */
@@ -154,7 +158,8 @@ typedef struct
DltShm dlt_shm; /**< Shared memory handling */
unsigned char *recv_buf_shm; /**< buffer for receive message from shm */
#endif
- DltOfflineTrace offlineTrace; /**< Offline trace handling */
+ MultipleFilesRingBuffer offlineTrace; /**< Offline trace handling */
+ MultipleFilesRingBuffer dltLogging; /**< Dlt logging handling */
int timeoutOnSend;
unsigned long RingbufferMinSize;
unsigned long RingbufferMaxSize;
diff --git a/src/daemon/dlt.conf b/src/daemon/dlt.conf
index 777aa82..11a5057 100644
--- a/src/daemon/dlt.conf
+++ b/src/daemon/dlt.conf
@@ -50,6 +50,15 @@ LoggingLevel = 6
# if WITH_DLT_FILE_LOGGING_SYSLOG_FALLBACK is set as compile flag
LoggingFilename = /tmp/dlt.log
+# Indicate whether size of logging file(s) is limited (Default: 0)
+EnableLoggingFileLimit = 0
+
+# Maximum size in bytes of one logging file (Default: 250000)
+# LoggingFileSize = 250000
+
+# Maximum size in bytes of all logging files (Default: 1000000)
+# LoggingFileMaxSize = 1000000
+
# Timeout on send to client (sec)
TimeOutOnSend = 4
diff --git a/src/gateway/dlt_gateway.c b/src/gateway/dlt_gateway.c
index 2b4f070..3d832ce 100644
--- a/src/gateway/dlt_gateway.c
+++ b/src/gateway/dlt_gateway.c
@@ -669,6 +669,9 @@ int dlt_gateway_configure(DltGateway *gateway, char *config_file, int verbose)
/* read configuration file */
file = dlt_config_file_init(config_file);
+ if(file == NULL) {
+ return DLT_RETURN_ERROR;
+ }
/* get number of entries and allocate memory to store information */
ret = dlt_config_file_get_num_sections(file, &num_sections);
diff --git a/src/lib/CMakeLists.txt b/src/lib/CMakeLists.txt
index 3293376..e6d6334 100644
--- a/src/lib/CMakeLists.txt
+++ b/src/lib/CMakeLists.txt
@@ -19,6 +19,7 @@ set(dlt_LIB_SRCS
dlt_filetransfer.c
dlt_env_ll.c
${PROJECT_SOURCE_DIR}/src/shared/dlt_common.c
+ ${PROJECT_SOURCE_DIR}/src/shared/dlt_multiple_files.c
${PROJECT_SOURCE_DIR}/src/shared/dlt_protocol.c
${PROJECT_SOURCE_DIR}/src/shared/dlt_user_shared.c
)
diff --git a/src/shared/dlt_common.c b/src/shared/dlt_common.c
index 97766f7..cbbe99a 100644
--- a/src/shared/dlt_common.c
+++ b/src/shared/dlt_common.c
@@ -31,6 +31,7 @@
#include <time.h> /* for localtime_r(), strftime() */
#include <limits.h> /* for NAME_MAX */
#include <inttypes.h> /* for PRI formatting macro */
+#include <libgen.h> /* dirname */
#include <stdarg.h>
#include <err.h>
@@ -41,6 +42,7 @@
#include "dlt_user_shared.h"
#include "dlt_common.h"
#include "dlt_common_cfg.h"
+#include "dlt_multiple_files.h"
#include "dlt_version.h"
@@ -84,6 +86,17 @@ static bool print_with_attributes = false;
int logging_mode = DLT_LOG_TO_STDERR;
FILE *logging_handle = NULL;
+//use ohandle as an indicator that multiple files logging is active
+MultipleFilesRingBuffer multiple_files_ring_buffer = {
+ .directory={0},
+ .filename={0},
+ .fileSize=0,
+ .maxSize=0,
+ .filenameTimestampBased=false,
+ .filenameBase={0},
+ .filenameExt={0},
+ .ohandle=-1};
+
char *message_type[] = { "log", "app_trace", "nw_trace", "control", "", "", "", "" };
char *log_info[] = { "", "fatal", "error", "warn", "info", "debug", "verbose", "", "", "", "", "", "", "", "", "" };
char *trace_type[] = { "", "variable", "func_in", "func_out", "state", "vfb", "", "", "", "", "", "", "", "", "", "" };
@@ -1816,34 +1829,114 @@ void dlt_print_with_attributes(bool state)
DltReturnValue dlt_log_init(int mode)
{
+ return dlt_log_init_multiple_logfiles_support((DltLoggingMode)mode, false, 0, 0);
+}
+
+DltReturnValue dlt_log_init_multiple_logfiles_support(const DltLoggingMode mode, const bool enable_multiple_logfiles,
+ const int logging_file_size, const int logging_files_max_size)
+{
if ((mode < DLT_LOG_TO_CONSOLE) || (mode > DLT_LOG_DROPPED)) {
- dlt_user_printf("Wrong parameter for mode: %d\n", mode);
+ dlt_vlog(LOG_WARNING, "Wrong parameter for mode: %d\n", mode);
return DLT_RETURN_WRONG_PARAMETER;
}
logging_mode = mode;
- if (logging_mode == DLT_LOG_TO_FILE) {
- /* internal logging to file */
- logging_handle = fopen(logging_filename, "a");
+ if (logging_mode != DLT_LOG_TO_FILE) {
+ return DLT_RETURN_OK;
+ }
- if (logging_handle == NULL) {
- dlt_user_printf("Internal log file %s cannot be opened!\n", logging_filename);
- return DLT_RETURN_ERROR;
+ if (enable_multiple_logfiles) {
+ dlt_user_printf("configure dlt logging using file limits\n");
+ int result = dlt_log_init_multiple_logfiles(logging_file_size, logging_files_max_size);
+ if (result == DLT_RETURN_OK) {
+ return DLT_RETURN_OK;
}
+ dlt_user_printf("dlt logging for limits fails with error code=%d, use logging without limits as fallback\n", result);
+ return dlt_log_init_single_logfile();
+ } else {
+ dlt_user_printf("configure dlt logging without file limits\n");
+ return dlt_log_init_single_logfile();
}
+}
+DltReturnValue dlt_log_init_single_logfile()
+{
+ /* internal logging to file */
+ errno = 0;
+ logging_handle = fopen(logging_filename, "a");
+
+ if (logging_handle == NULL) {
+ dlt_user_printf("Internal log file %s cannot be opened, error: %s\n", logging_filename, strerror(errno));
+ return DLT_RETURN_ERROR;
+ }
return DLT_RETURN_OK;
}
+DltReturnValue dlt_log_init_multiple_logfiles(const int logging_file_size, const int logging_files_max_size)
+{
+ char path_logging_filename[PATH_MAX + 1];
+ strncpy(path_logging_filename, logging_filename, PATH_MAX);
+ path_logging_filename[PATH_MAX] = 0;
+
+ const char *directory = dirname(path_logging_filename);
+ if (directory[0]) {
+ char basename_logging_filename[NAME_MAX + 1];
+ strncpy(basename_logging_filename, logging_filename, NAME_MAX);
+ basename_logging_filename[NAME_MAX] = 0;
+
+ const char *file_name = basename(basename_logging_filename);
+ char filename_base[NAME_MAX];
+ if (!dlt_extract_base_name_without_ext(file_name, filename_base, sizeof(filename_base))) return DLT_RETURN_ERROR;
+
+ const char *filename_ext = get_filename_ext(file_name);
+ if (!filename_ext) return DLT_RETURN_ERROR;
+
+ DltReturnValue result = multiple_files_buffer_init(
+ &multiple_files_ring_buffer,
+ directory,
+ logging_file_size,
+ logging_files_max_size,
+ false,
+ true,
+ filename_base,
+ filename_ext);
+
+ return result;
+ }
+
+ return DLT_RETURN_ERROR;
+}
+
void dlt_log_free(void)
{
- if (logging_mode == DLT_LOG_TO_FILE && logging_handle)
+ if (logging_mode == DLT_LOG_TO_FILE) {
+ if (dlt_is_log_in_multiple_files_active()) {
+ dlt_log_free_multiple_logfiles();
+ } else {
+ dlt_log_free_single_logfile();
+ }
+ }
+}
+
+void dlt_log_free_single_logfile()
+{
+ if (logging_handle)
fclose(logging_handle);
}
+void dlt_log_free_multiple_logfiles()
+{
+ if (DLT_RETURN_ERROR == multiple_files_buffer_free(&multiple_files_ring_buffer)) return;
+
+ // reset indicator of multiple files usage
+ multiple_files_ring_buffer.ohandle = -1;
+}
+
int dlt_user_printf(const char *format, ...)
{
+ if (format == NULL) return -1;
+
va_list args;
va_start(args, format);
@@ -1922,9 +2015,13 @@ DltReturnValue dlt_log(int prio, char *s)
#endif
break;
case DLT_LOG_TO_FILE:
-
/* log to file */
- if (logging_handle) {
+
+ if (dlt_is_log_in_multiple_files_active()) {
+ dlt_log_multiple_files_write(sFormatString, (unsigned int)sTimeSpec.tv_sec,
+ (unsigned int)(sTimeSpec.tv_nsec / 1000), getpid(), asSeverity[prio], s);
+ }
+ else if (logging_handle) {
fprintf(logging_handle, sFormatString, (unsigned int)sTimeSpec.tv_sec,
(unsigned int)(sTimeSpec.tv_nsec / 1000), getpid(), asSeverity[prio], s);
fflush(logging_handle);
@@ -4308,3 +4405,42 @@ int dlt_execute_command(char *filename, char *command, ...)
free(args);
return ret;
}
+
+char *get_filename_ext(const char *filename)
+{
+ if (filename == NULL) {
+ fprintf(stderr, "ERROR: %s: invalid arguments\n", __FUNCTION__);
+ return NULL;
+ }
+
+ char *dot = strrchr(filename, '.');
+ return (!dot || dot == filename) ? NULL : dot;
+}
+
+bool dlt_extract_base_name_without_ext(const char* const abs_file_name, char* base_name, long base_name_len) {
+ if (abs_file_name == NULL || base_name == NULL) return false;
+
+ const char* last_separator = strrchr(abs_file_name, '.');
+ if (!last_separator) return false;
+ long length = last_separator - abs_file_name;
+ length = length > base_name_len ? base_name_len : length;
+
+ strncpy(base_name, abs_file_name, length);
+ base_name[length] = '\0';
+ return true;
+}
+
+void dlt_log_multiple_files_write(const char* format, ...)
+{
+ char output_string[2048] = { 0 };
+ va_list args;
+ va_start (args, format);
+ vsnprintf(output_string, 2047, format, args);
+ va_end (args);
+ multiple_files_buffer_write(&multiple_files_ring_buffer, (unsigned char*)output_string, strlen(output_string));
+}
+
+bool dlt_is_log_in_multiple_files_active()
+{
+ return multiple_files_ring_buffer.ohandle > -1;
+}
diff --git a/src/shared/dlt_multiple_files.c b/src/shared/dlt_multiple_files.c
new file mode 100644
index 0000000..754fd1e
--- /dev/null
+++ b/src/shared/dlt_multiple_files.c
@@ -0,0 +1,499 @@
+/*
+ * SPDX license identifier: MPL-2.0
+ *
+ * Copyright (C) 2022, Daimler TSS GmbH
+ *
+ * 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 https://www.covesa.global/.
+ */
+
+/*!
+ * \author
+ * Oleg Tropmann <oleg.tropmann@daimler.com>
+ * Daniel Weber <daniel.w.weber@daimler.com>
+ *
+ * \copyright Copyright © 2022 Daimler TSS GmbH. \n
+ * License MPL-2.0: Mozilla Public License version 2.0 http://mozilla.org/MPL/2.0/.
+ *
+ * \file dlt_daemon_log.c
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <syslog.h>
+#include <errno.h>
+#include <stdarg.h>
+
+#include <dlt_multiple_files.h>
+#include "dlt_common.h"
+
+unsigned int multiple_files_buffer_storage_dir_info(const char *path, const char *file_name,
+ char *newest, char *oldest)
+{
+ int i = 0;
+ unsigned int num_log_files = 0;
+ struct dirent **files = { 0 };
+ char *tmp_old = NULL;
+ char *tmp_new = NULL;
+
+ if ((path == NULL) || (file_name == NULL) || (newest == NULL) || (oldest == NULL)) {
+ fprintf(stderr, "multiple_files_buffer_storage_dir_info: Invalid parameter(s)");
+ return 0;
+ }
+
+ const int file_cnt = scandir(path, &files, NULL, alphasort);
+ if (file_cnt <= 0) return 0;
+
+ for (i = 0; i < file_cnt; i++) {
+ int len = 0;
+ len = strlen(file_name);
+
+ if ((strncmp(files[i]->d_name, file_name, len) == 0) &&
+ (files[i]->d_name[len] == MULTIPLE_FILES_FILENAME_INDEX_DELIM[0])) {
+ num_log_files++;
+
+ if ((tmp_old == NULL) || (strlen(tmp_old) >= strlen(files[i]->d_name))) {
+ if (tmp_old == NULL) {
+ tmp_old = files[i]->d_name;
+ } else if (strlen(tmp_old) > strlen(files[i]->d_name)) {
+ /* when file name is smaller, it is older */
+ tmp_old = files[i]->d_name;
+ } else if (strcmp(tmp_old, files[i]->d_name) > 0) {
+ /* filename length is equal, do a string compare */
+ tmp_old = files[i]->d_name;
+ }
+ }
+
+ if ((tmp_new == NULL) || (strlen(tmp_new) <= strlen(files[i]->d_name))) {
+ if (tmp_new == NULL) {
+ tmp_new = files[i]->d_name;
+ } else if (strlen(tmp_new) < strlen(files[i]->d_name)) {
+ /* when file name is longer, it is younger */
+ tmp_new = files[i]->d_name;
+ } else if (strcmp(tmp_new, files[i]->d_name) < 0) {
+ tmp_new = files[i]->d_name;
+ }
+ }
+ }
+ }
+
+ if (num_log_files > 0) {
+ if ((tmp_old != NULL) && (strlen(tmp_old) < NAME_MAX)) {
+ strncpy(oldest, tmp_old, NAME_MAX);
+ oldest[NAME_MAX] = '\0';
+ } else if ((tmp_old != NULL) && (strlen(tmp_old) >= NAME_MAX)) {
+ printf("length mismatch of file %s\n", tmp_old);
+ }
+
+ if ((tmp_new != NULL) && (strlen(tmp_new) < NAME_MAX)) {
+ strncpy(newest, tmp_new, NAME_MAX);
+ oldest[NAME_MAX] = '\0';
+ } else if ((tmp_new != NULL) && (strlen(tmp_new) >= NAME_MAX)) {
+ printf("length mismatch of file %s\n", tmp_new);
+ }
+ }
+
+ /* free scandir result */
+ for (i = 0; i < file_cnt; i++) free(files[i]);
+
+ free(files);
+
+ return num_log_files;
+}
+
+void multiple_files_buffer_file_name(MultipleFilesRingBuffer *files_buffer, const size_t length, const unsigned int idx)
+{
+ char file_index[11]; /* UINT_MAX = 4294967295 -> 10 digits */
+ snprintf(file_index, sizeof(file_index), "%010u", idx);
+
+ /* create log file name */
+ char* file_name = files_buffer->filename;
+ memset(file_name, 0, length * sizeof(char));
+
+ const size_t size = length - strlen(file_name) - 1;
+ strncat(file_name, files_buffer->filenameBase, size);
+ strncat(file_name, MULTIPLE_FILES_FILENAME_INDEX_DELIM, size);
+ strncat(file_name, file_index, size);
+ strncat(file_name, files_buffer->filenameExt, size);
+}
+
+unsigned int multiple_files_buffer_get_idx_of_log_file(char *file)
+{
+ if ((file == NULL) || (file[0] == '\0')) return 0;
+
+ const char d[2] = MULTIPLE_FILES_FILENAME_INDEX_DELIM;
+ char *token;
+
+ token = strtok(file, d);
+ /* we are interested in 2. token because of log file name */
+ token = strtok(NULL, d);
+
+ return token != NULL ? strtol(token, NULL, 10) : 0;
+}
+
+DltReturnValue multiple_files_buffer_create_new_file(MultipleFilesRingBuffer *files_buffer)
+{
+ if (files_buffer == NULL) {
+ fprintf(stderr, "multiple files buffer not set\n");
+ return DLT_RETURN_ERROR;
+ }
+
+ time_t t;
+ struct tm tmp;
+ char file_path[PATH_MAX + 1];
+ unsigned int idx = 0;
+ int ret = 0;
+
+ /* set filename */
+ if (files_buffer->filenameTimestampBased) {
+ /* timestamp format: "yyyymmdd_hhmmss" */
+ char timestamp[16];
+ t = time(NULL);
+ tzset();
+ localtime_r(&t, &tmp);
+
+ strftime(timestamp, sizeof(timestamp), "%Y%m%d_%H%M%S", &tmp);
+
+ ret = snprintf(files_buffer->filename, sizeof(files_buffer->filename), "%s%s%s%s",
+ files_buffer->filenameBase,
+ MULTIPLE_FILES_FILENAME_TIMESTAMP_DELIM, timestamp,
+ files_buffer->filenameExt);
+
+ if ((ret < 0) || ((size_t)ret >= (int)sizeof(files_buffer->filename))) {
+ fprintf(stderr, "filename cannot be concatenated\n");
+ return DLT_RETURN_ERROR;
+ }
+
+ ret = snprintf(file_path, sizeof(file_path), "%s/%s",
+ files_buffer->directory, files_buffer->filename);
+
+ if ((ret < 0) || ((size_t)ret >= (int)sizeof(file_path))) {
+ fprintf(stderr, "file path cannot be concatenated\n");
+ return DLT_RETURN_ERROR;
+ }
+ }
+ else {
+ char newest[NAME_MAX + 1] = { 0 };
+ char oldest[NAME_MAX + 1] = { 0 };
+ /* targeting newest file, ignoring number of files in dir returned */
+ if (0 == multiple_files_buffer_storage_dir_info(files_buffer->directory,
+ files_buffer->filenameBase,
+ newest,
+ oldest)) {
+ printf("No multiple files found\n");
+ }
+
+ idx = multiple_files_buffer_get_idx_of_log_file(newest) + 1;
+
+ multiple_files_buffer_file_name(files_buffer, sizeof(files_buffer->filename), idx);
+ ret = snprintf(file_path, sizeof(file_path), "%s/%s",
+ files_buffer->directory, files_buffer->filename);
+
+ if ((ret < 0) || (ret >= NAME_MAX)) {
+ fprintf(stderr, "filename cannot be concatenated\n");
+ return DLT_RETURN_ERROR;
+ }
+ }
+
+ /* open DLT output file */
+ errno = 0;
+ files_buffer->ohandle = open(file_path, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR |
+ S_IRGRP | S_IROTH); /* mode: wb */
+
+ if (files_buffer->ohandle == -1) {
+ /* file cannot be opened */
+ fprintf(stderr, "file %s cannot be created, error: %s\n", file_path, strerror(errno));
+ return DLT_RETURN_ERROR;
+ }
+
+ return DLT_RETURN_OK;
+}
+
+ssize_t multiple_files_buffer_get_total_size(const MultipleFilesRingBuffer *files_buffer)
+{
+ if (files_buffer == NULL) {
+ fprintf(stderr, "multiple files buffer not set\n");
+ return -1;
+ }
+
+ struct dirent *dp;
+ char filename[PATH_MAX + 1];
+ ssize_t size = 0;
+ struct stat status;
+
+ /* go through all dlt files in directory */
+ DIR *dir = opendir(files_buffer->directory);
+ if (!dir) {
+ fprintf(stderr, "directory %s cannot be opened, error=%s\n", files_buffer->directory, strerror(errno));
+ return -1;
+ }
+
+ while ((dp = readdir(dir)) != NULL) {
+ // consider files matching with a specific base name and a particular extension
+ if (strstr(dp->d_name, files_buffer->filenameBase) && strstr(dp->d_name, files_buffer->filenameExt)) {
+ int res = snprintf(filename, sizeof(filename), "%s/%s", files_buffer->directory, dp->d_name);
+
+ /* if the total length of the string is greater than the buffer, silently forget it. */
+ /* snprintf: a return value of size or more means that the output was truncated */
+ /* if an output error is encountered, a negative value is returned. */
+ if (((unsigned int)res < sizeof(filename)) && (res > 0)) {
+ errno = 0;
+ if (0 == stat(filename, &status))
+ size += status.st_size;
+ else
+ fprintf(stderr, "file %s cannot be stat-ed, error=%s\n", filename, strerror(errno));
+ }
+ }
+ }
+
+ closedir(dir);
+
+ /* return size */
+ return size;
+}
+
+int multiple_files_buffer_delete_oldest_file(MultipleFilesRingBuffer *files_buffer)
+{
+ if (files_buffer == NULL) {
+ fprintf(stderr, "multiple files buffer not set\n");
+ return -1; /* ERROR */
+ }
+
+ struct dirent *dp;
+ char filename[PATH_MAX + 1];
+ char filename_oldest[PATH_MAX + 1];
+ unsigned long size_oldest = 0;
+ struct stat status;
+ time_t time_oldest = 0;
+ int index_oldest = INT_MAX;
+
+ filename[0] = 0;
+ filename_oldest[0] = 0;
+
+ /* go through all dlt files in directory */
+ DIR *dir = opendir(files_buffer->directory);
+
+ if(!dir)
+ return -1;
+
+ while ((dp = readdir(dir)) != NULL) {
+ if (strstr(dp->d_name, files_buffer->filenameBase) && strstr(dp->d_name, files_buffer->filenameExt)) {
+ int res = snprintf(filename, sizeof(filename), "%s/%s", files_buffer->directory, dp->d_name);
+
+ /* if the total length of the string is greater than the buffer, silently forget it. */
+ /* snprintf: a return value of size or more means that the output was truncated */
+ /* if an output error is encountered, a negative value is returned. */
+ if (((unsigned int) res >= sizeof(filename)) || (res <= 0)) {
+ printf("Filename for delete oldest too long. Skip file.\n");
+ continue;
+ }
+
+ if (files_buffer->filenameTimestampBased) {
+ errno = 0;
+ if (0 == stat(filename, &status)) {
+ if ((time_oldest == 0) || (status.st_mtime < time_oldest)) {
+ time_oldest = status.st_mtime;
+ size_oldest = status.st_size;
+ strncpy(filename_oldest, filename, PATH_MAX);
+ filename_oldest[PATH_MAX] = 0;
+ }
+ } else {
+ printf("Old file %s cannot be stat-ed, error=%s\n", filename, strerror(errno));
+ }
+ } else {
+ //index based
+ const int index = multiple_files_buffer_get_idx_of_log_file(filename);
+ if (index < index_oldest) {
+ index_oldest = index;
+ snprintf(filename, sizeof(filename), "%s/%s", files_buffer->directory, dp->d_name);
+ strncpy(filename_oldest, filename, PATH_MAX);
+ filename_oldest[PATH_MAX] = 0;
+ }
+ }
+ }
+ }
+
+ closedir(dir);
+
+ /* delete file */
+ if (filename_oldest[0]) {
+ if (remove(filename_oldest)) {
+ fprintf(stderr, "Remove file %s failed! error=%s\n", filename_oldest, strerror(errno));
+ return -1; /* ERROR */
+ }
+ } else {
+ fprintf(stderr, "No file to be removed!\n");
+ return -1; /* ERROR */
+ }
+
+ /* return size of deleted file*/
+ return size_oldest;
+}
+
+DltReturnValue multiple_files_buffer_check_size(MultipleFilesRingBuffer *files_buffer)
+{
+ if (files_buffer == NULL) {
+ fprintf(stderr, "multiple files buffer not set\n");
+ return DLT_RETURN_ERROR;
+ }
+
+ struct stat status;
+
+ /* check for existence of buffer files directory */
+ errno = 0;
+ if (stat(files_buffer->directory, &status) == -1) {
+ fprintf(stderr, "Buffer files directory: %s doesn't exist, error=%s\n", files_buffer->directory, strerror(errno));
+ return DLT_RETURN_ERROR;
+ }
+ /* check for accessibility of buffer files directory */
+ else if (access(files_buffer->directory, W_OK) != 0) {
+ fprintf(stderr, "Buffer files directory: %s doesn't have the write access \n", files_buffer->directory);
+ return DLT_RETURN_ERROR;
+ }
+
+ ssize_t total_size = 0;
+ /* check size of complete buffer file */
+ while ((total_size = multiple_files_buffer_get_total_size(files_buffer)) > (files_buffer->maxSize - files_buffer->fileSize)) {
+ /* remove the oldest files as long as new file will not fit in completely into complete multiple files buffer */
+ if (multiple_files_buffer_delete_oldest_file(files_buffer) < 0) return DLT_RETURN_ERROR;
+ }
+
+ return total_size == -1 ? DLT_RETURN_ERROR : DLT_RETURN_OK;
+}
+
+DltReturnValue multiple_files_buffer_open_file_for_append(MultipleFilesRingBuffer *files_buffer) {
+ if (files_buffer == NULL || files_buffer->filenameTimestampBased) return DLT_RETURN_ERROR;
+
+ char newest[NAME_MAX + 1] = {0};
+ char oldest[NAME_MAX + 1] = {0};
+ /* targeting the newest file, ignoring number of files in dir returned */
+
+ if (0 == multiple_files_buffer_storage_dir_info(files_buffer->directory,
+ files_buffer->filenameBase, newest, oldest) ) {
+ // no file for appending found. Create a new one
+ printf("No multiple files for appending found. Create a new one\n");
+ return multiple_files_buffer_create_new_file(files_buffer);
+ }
+
+ char file_path[PATH_MAX + 1];
+ int ret = snprintf(file_path, sizeof(file_path), "%s/%s",
+ files_buffer->directory, newest);
+
+ if ((ret < 0) || (ret >= NAME_MAX)) {
+ fprintf(stderr, "filename cannot be concatenated\n");
+ return DLT_RETURN_ERROR;
+ }
+
+ /* open DLT output file */
+ errno = 0;
+ files_buffer->ohandle = open(file_path, O_WRONLY | O_APPEND, S_IRUSR | S_IWUSR |
+ S_IRGRP | S_IROTH); /* mode: wb */
+
+ return files_buffer->ohandle == -1 ? DLT_RETURN_ERROR : DLT_RETURN_OK;
+}
+
+DltReturnValue multiple_files_buffer_init(MultipleFilesRingBuffer *files_buffer,
+ const char *directory,
+ const int file_size,
+ const int max_size,
+ const bool filename_timestamp_based,
+ const bool append,
+ const char *filename_base,
+ const char *filename_ext)
+{
+ if (files_buffer == NULL) {
+ fprintf(stderr, "multiple files buffer not set\n");
+ return DLT_RETURN_ERROR;
+ }
+
+ /* init parameters */
+ strncpy(files_buffer->directory, directory, NAME_MAX);
+ files_buffer->directory[NAME_MAX] = 0;
+ files_buffer->fileSize = file_size;
+ files_buffer->maxSize = max_size;
+ files_buffer->filenameTimestampBased = filename_timestamp_based;
+ strncpy(files_buffer->filenameBase, filename_base, NAME_MAX);
+ files_buffer->filenameBase[NAME_MAX] = 0;
+ strncpy(files_buffer->filenameExt, filename_ext, NAME_MAX);
+ files_buffer->filenameExt[NAME_MAX] = 0;
+
+ if (DLT_RETURN_ERROR == multiple_files_buffer_check_size(files_buffer)) return DLT_RETURN_ERROR;
+
+ return (!files_buffer->filenameTimestampBased && append)
+ ? multiple_files_buffer_open_file_for_append(files_buffer)
+ : multiple_files_buffer_create_new_file(files_buffer);
+}
+
+void multiple_files_buffer_rotate_file(MultipleFilesRingBuffer *files_buffer, const int size)
+{
+ /* check file size here */
+ if ((lseek(files_buffer->ohandle, 0, SEEK_CUR) + size) < files_buffer->fileSize) return;
+
+ /* close old file */
+ close(files_buffer->ohandle);
+ files_buffer->ohandle = -1;
+
+ /* check complete files size, remove old logs if needed */
+ if (DLT_RETURN_ERROR == multiple_files_buffer_check_size(files_buffer)) return;
+
+ /* create new file */
+ multiple_files_buffer_create_new_file(files_buffer);
+}
+
+DltReturnValue multiple_files_buffer_write_chunk(const MultipleFilesRingBuffer *files_buffer,
+ const unsigned char *data,
+ const int size)
+{
+ if (files_buffer == NULL) {
+ fprintf(stderr, "multiple files buffer not set\n");
+ return DLT_RETURN_ERROR;
+ }
+
+ if (data && (files_buffer->ohandle >= 0)) {
+ if (write(files_buffer->ohandle, data, size) != size) {
+ fprintf(stderr, "file write failed!\n");
+ return DLT_RETURN_ERROR;
+ }
+ }
+ return DLT_RETURN_OK;
+}
+
+DltReturnValue multiple_files_buffer_write(MultipleFilesRingBuffer *files_buffer,
+ const unsigned char *data,
+ const int size)
+{
+ if (files_buffer->ohandle < 0) return DLT_RETURN_ERROR;
+
+ multiple_files_buffer_rotate_file(files_buffer, size);
+
+ /* write data into log file */
+ return multiple_files_buffer_write_chunk(files_buffer, data, size);
+}
+
+DltReturnValue multiple_files_buffer_free(const MultipleFilesRingBuffer *files_buffer)
+{
+ if (files_buffer == NULL) {
+ fprintf(stderr, "multiple files buffer not set\n");
+ return DLT_RETURN_ERROR;
+ }
+
+ if (files_buffer->ohandle < 0) return DLT_RETURN_ERROR;
+
+ /* close last used log file */
+ close(files_buffer->ohandle);
+
+ return DLT_RETURN_OK;
+}
diff --git a/src/shared/dlt_offline_trace.c b/src/shared/dlt_offline_trace.c
index 2b1e0df..b8b9a85 100644
--- a/src/shared/dlt_offline_trace.c
+++ b/src/shared/dlt_offline_trace.c
@@ -61,393 +61,28 @@
#include <unistd.h>
#include <dirent.h>
#include <syslog.h>
+#include <errno.h>
#include <dlt_offline_trace.h>
-#include "dlt_common.h"
-
-unsigned int dlt_offline_trace_storage_dir_info(char *path, char *file_name, char *newest, char *oldest)
-{
- int i = 0;
- unsigned int num = 0;
- int cnt = 0;
- struct dirent **files = { 0 };
- char *tmp_old = NULL;
- char *tmp_new = NULL;
-
- if ((path == NULL) || (file_name == NULL) || (newest == NULL) || (oldest == NULL)) {
- printf("dlt_offline_trace_storage_dir_info: Invalid parameter(s)");
- return 0;
- }
-
- cnt = scandir(path, &files, NULL, alphasort);
-
- if (cnt < 0)
- return 0;
-
- for (i = 0; i < cnt; i++) {
- int len = 0;
- len = strlen(file_name);
-
- if ((strncmp(files[i]->d_name, file_name, len) == 0) &&
- (files[i]->d_name[len] == DLT_OFFLINETRACE_FILENAME_INDEX_DELI[0])) {
- num++;
-
- if ((tmp_old == NULL) || (strlen(tmp_old) >= strlen(files[i]->d_name))) {
- if (tmp_old == NULL)
- tmp_old = files[i]->d_name;
- /* when file name is smaller, it is older */
- else if (strlen(tmp_old) > strlen(files[i]->d_name))
- tmp_old = files[i]->d_name;
- else /* filename is equal, do a string compare */
- if (strcmp(tmp_old, files[i]->d_name) > 0)
- tmp_old = files[i]->d_name;
- }
-
- if ((tmp_new == NULL) || (strlen(tmp_new) <= strlen(files[i]->d_name))) {
- if (tmp_new == NULL)
- tmp_new = files[i]->d_name;
- /* when file name is longer, it is younger */
- else if (strlen(tmp_new) < strlen(files[i]->d_name))
- tmp_new = files[i]->d_name;
- else if (strcmp(tmp_new, files[i]->d_name) < 0)
- tmp_new = files[i]->d_name;
- }
- }
- }
-
- if (num > 0) {
- if ((tmp_old != NULL) && (strlen(tmp_old) < NAME_MAX)) {
- strncpy(oldest, tmp_old, NAME_MAX);
- oldest[NAME_MAX] = '\0';
- }
-
- if ((tmp_new != NULL) && (strlen(tmp_old) < NAME_MAX)) {
- strncpy(newest, tmp_new, NAME_MAX);
- oldest[NAME_MAX] = '\0';
- }
- }
-
- /* free scandir result */
- for (i = 0; i < cnt; i++)
- free(files[i]);
-
- free(files);
-
- return num;
-}
-
-void dlt_offline_trace_file_name(char *log_file_name, size_t length,
- char *name, unsigned int idx)
-{
- char file_index[11]; /* UINT_MAX = 4294967295 -> 10 digits */
- snprintf(file_index, sizeof(file_index), "%010u", idx);
-
- /* create log file name */
- memset(log_file_name, 0, length * sizeof(char));
- strncat(log_file_name, name, length - strlen(log_file_name) - 1);
- strncat(log_file_name, DLT_OFFLINETRACE_FILENAME_INDEX_DELI,
- length - strlen(log_file_name) - 1);
- strncat(log_file_name, file_index, length - strlen(log_file_name) - 1);
- strncat(log_file_name, DLT_OFFLINETRACE_FILENAME_EXT,
- length - strlen(log_file_name) - 1);
-}
-
-unsigned int dlt_offline_trace_get_idx_of_log_file(char *file)
-{
- const char d[2] = DLT_OFFLINETRACE_FILENAME_INDEX_DELI;
- char *token;
- unsigned int idx = 0;
-
- if (file[0] == '\0')
- return 0;
-
- token = strtok(file, d);
- /* we are interested in 2. token because of log file name */
- token = strtok(NULL, d);
-
- if (token != NULL)
- idx = strtol(token, NULL, 10);
- else
- idx = 0;
-
- return idx;
-}
-
-
-DltReturnValue dlt_offline_trace_create_new_file(DltOfflineTrace *trace)
-{
- time_t t;
- struct tm tmp;
- char file_path[PATH_MAX + 1];
- unsigned int idx = 0;
- int ret = 0;
-
- /* set filename */
- if (trace->filenameTimestampBased) {
- /* timestamp format: "yyyymmdd_hhmmss" */
- char timestamp[16];
- t = time(NULL);
- tzset();
- localtime_r(&t, &tmp);
-
- strftime(timestamp, sizeof(timestamp), "%Y%m%d_%H%M%S", &tmp);
-
- ret = snprintf(trace->filename, sizeof(trace->filename), "%s%s%s%s",
- DLT_OFFLINETRACE_FILENAME_BASE,
- DLT_OFFLINETRACE_FILENAME_TIMESTAMP_DELI, timestamp,
- DLT_OFFLINETRACE_FILENAME_EXT);
-
- if ((ret < 0) || ((size_t)ret >= (int)sizeof(trace->filename))) {
- printf("dlt_offlinetrace filename cannot be concatenated\n");
- return DLT_RETURN_ERROR;
- }
-
- ret = snprintf(file_path, sizeof(file_path), "%s/%s",
- trace->directory, trace->filename);
-
- if ((ret < 0) || ((size_t)ret >= (int)sizeof(file_path))) {
- printf("dlt_offlinetrace file path cannot be concatenated\n");
- return DLT_RETURN_ERROR;
- }
- }
- else {
- char newest[NAME_MAX + 1] = { 0 };
- char oldest[NAME_MAX + 1] = { 0 };
- /* targeting newest file, ignoring number of files in dir returned */
- dlt_offline_trace_storage_dir_info(trace->directory,
- DLT_OFFLINETRACE_FILENAME_BASE, newest, oldest);
- idx = dlt_offline_trace_get_idx_of_log_file(newest) + 1;
-
- dlt_offline_trace_file_name(trace->filename, sizeof(trace->filename),
- DLT_OFFLINETRACE_FILENAME_BASE, idx);
- ret = snprintf(file_path, sizeof(file_path), "%s/%s",
- trace->directory, trace->filename);
-
- if ((ret < 0) || (ret >= NAME_MAX)) {
- printf("filename cannot be concatenated\n");
- return DLT_RETURN_ERROR;
- }
- }
-
- /* open DLT output file */
- trace->ohandle = open(file_path, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR |
- S_IRGRP | S_IROTH); /* mode: wb */
-
- if (trace->ohandle == -1) {
- /* trace file cannot be opened */
- printf("Offline trace file %s cannot be created\n", file_path);
- return DLT_RETURN_ERROR;
- } /* if */
-
- return DLT_RETURN_OK; /* OK */
-}
-
-ssize_t dlt_offline_trace_get_total_size(DltOfflineTrace *trace)
-{
- struct dirent *dp;
- char filename[PATH_MAX + 1];
- ssize_t size = 0;
- struct stat status;
-
- /* go through all dlt files in directory */
- DIR *dir = opendir(trace->directory);
-
- if (!dir)
- return -1;
-
- while ((dp = readdir(dir)) != NULL)
- if (strstr(dp->d_name, DLT_OFFLINETRACE_FILENAME_BASE)) {
- int res = snprintf(filename, sizeof(filename), "%s/%s", trace->directory, dp->d_name);
-
- /* if the total length of the string is greater than the buffer, silently forget it. */
- /* snprintf: a return value of size or more means that the output was truncated */
- /* if an output error is encountered, a negative value is returned. */
- if (((unsigned int)res < sizeof(filename)) && (res > 0)) {
- if (0 == stat(filename, &status))
- size += status.st_size;
- else
- printf("Offline trace file %s cannot be stat-ed", filename);
- }
-
- /*else */
- /*{ */
- /* dlt_log(3, "dlt_offline_trace_get_total_size: long filename ignored"); */
- /*} */
- }
-
- closedir(dir);
-
- /* return size */
- return size;
-}
-
-int dlt_offline_trace_delete_oldest_file(DltOfflineTrace *trace)
-{
- struct dirent *dp;
- char filename[PATH_MAX + 1];
- char filename_oldest[PATH_MAX + 1];
- unsigned long size_oldest = 0;
- struct stat status;
- time_t time_oldest = 0;
-
- filename[0] = 0;
- filename_oldest[0] = 0;
-
- /* go through all dlt files in directory */
- DIR *dir = opendir(trace->directory);
-
- if(!dir)
- return -1;
-
- while ((dp = readdir(dir)) != NULL)
- if (strstr(dp->d_name, DLT_OFFLINETRACE_FILENAME_BASE)) {
- int res = snprintf(filename, sizeof(filename), "%s/%s", trace->directory, dp->d_name);
-
- /* if the total length of the string is greater than the buffer, silently forget it. */
- /* snprintf: a return value of size or more means that the output was truncated */
- /* if an output error is encountered, a negative value is returned. */
- if (((unsigned int)res < sizeof(filename)) && (res > 0)) {
- if (0 == stat(filename, &status)) {
- if ((time_oldest == 0) || (status.st_mtime < time_oldest)) {
- time_oldest = status.st_mtime;
- size_oldest = status.st_size;
- strncpy(filename_oldest, filename, PATH_MAX);
- filename_oldest[PATH_MAX] = 0;
- }
- }
- else {
- printf("Old offline trace file %s cannot be stat-ed", filename);
- }
- }
- }
-
- closedir(dir);
-
- /* delete file */
- if (filename_oldest[0]) {
- if (remove(filename_oldest)) {
- printf("Remove file %s failed!\n", filename_oldest);
- return -1; /* ERROR */
- }
- }
- else {
- printf("No file to be removed!\n");
- return -1; /* ERROR */
- }
-
- /* return size of deleted file*/
- return size_oldest;
-}
-
-DltReturnValue dlt_offline_trace_check_size(DltOfflineTrace *trace)
-{
-
- struct stat status;
-
- /* check for existence of offline trace directory */
- if (stat(trace->directory, &status) == -1) {
- dlt_vlog(LOG_ERR, "Offline trace directory: %s doesn't exist \n", trace->directory);
- return DLT_RETURN_ERROR;
- }
-
- /* check for accesibilty of offline trace directory */
- else if (access(trace->directory, W_OK) != 0)
- {
- dlt_vlog(LOG_ERR, "Offline trace directory: %s doesn't have the write access \n", trace->directory);
- return DLT_RETURN_ERROR;
- }
-
- ssize_t s = 0;
-
- /* check size of complete offline trace */
- while ((s = dlt_offline_trace_get_total_size(trace)) > (trace->maxSize - trace->fileSize))
- /* remove oldest files as long as new file will not fit in completely into complete offline trace */
- if (dlt_offline_trace_delete_oldest_file(trace) < 0)
- return DLT_RETURN_ERROR;
-
- if (s == -1)
- return DLT_RETURN_ERROR;
-
- return DLT_RETURN_OK; /* OK */
-}
-
-DltReturnValue dlt_offline_trace_init(DltOfflineTrace *trace,
- const char *directory,
- int fileSize,
- int maxSize,
- int filenameTimestampBased)
-{
-
- /* init parameters */
- strncpy(trace->directory, directory, NAME_MAX);
- trace->directory[NAME_MAX] = 0;
- trace->fileSize = fileSize;
- trace->maxSize = maxSize;
- trace->filenameTimestampBased = filenameTimestampBased;
- /* check complete offlien trace size, remove old logs if needed */
- dlt_offline_trace_check_size(trace);
-
- return dlt_offline_trace_create_new_file(trace);
-}
-
-DltReturnValue dlt_offline_trace_write(DltOfflineTrace *trace,
- unsigned char *data1,
- int size1,
- unsigned char *data2,
- int size2,
- unsigned char *data3,
- int size3)
+#include <dlt_multiple_files.h>
+
+DltReturnValue dlt_offline_trace_write(MultipleFilesRingBuffer *trace,
+ const unsigned char *data1,
+ const int size1,
+ const unsigned char *data2,
+ const int size2,
+ const unsigned char *data3,
+ const int size3)
{
- if (trace->ohandle < 0)
- return DLT_RETURN_ERROR;
-
- /* check file size here */
- if ((lseek(trace->ohandle, 0, SEEK_CUR) + size1 + size2 + size3) >= trace->fileSize) {
- /* close old file */
- close(trace->ohandle);
- trace->ohandle = -1;
-
- /* check complete offline trace size, remove old logs if needed */
- dlt_offline_trace_check_size(trace);
+ if (trace->ohandle < 0) return DLT_RETURN_ERROR;
- /* create new file */
- dlt_offline_trace_create_new_file(trace);
- }
+ multiple_files_buffer_rotate_file(trace, size1 + size2 + size3);
/* write data into log file */
- if (data1 && (trace->ohandle >= 0)) {
- if (write(trace->ohandle, data1, size1) != size1) {
- printf("Offline trace write failed!\n");
- return DLT_RETURN_ERROR;
- }
- }
-
- if (data2 && (trace->ohandle >= 0)) {
- if (write(trace->ohandle, data2, size2) != size2) {
- printf("Offline trace write failed!\n");
- return DLT_RETURN_ERROR;
- }
- }
-
- if (data3 && (trace->ohandle >= 0)) {
- if (write(trace->ohandle, data3, size3) != size3) {
- printf("Offline trace write failed!\n");
- return DLT_RETURN_ERROR;
- }
- }
-
- return DLT_RETURN_OK; /* OK */
-}
-
-DltReturnValue dlt_offline_trace_free(DltOfflineTrace *trace)
-{
-
- if (trace->ohandle < 0)
- return DLT_RETURN_ERROR;
-
- /* close last used log file */
- close(trace->ohandle);
+ if (multiple_files_buffer_write_chunk(trace, data1, size1) != DLT_RETURN_OK) return DLT_RETURN_ERROR;
+ if (multiple_files_buffer_write_chunk(trace, data2, size2) != DLT_RETURN_OK) return DLT_RETURN_ERROR;
+ if (multiple_files_buffer_write_chunk(trace, data3, size3) != DLT_RETURN_OK) return DLT_RETURN_ERROR;
- return DLT_RETURN_OK; /* OK */
+ return DLT_RETURN_OK;
}
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 6614e9b..be0889c 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -55,6 +55,7 @@ endforeach()
set(TARGET_LIST gtest_dlt_daemon_gateway
gtest_dlt_daemon_offline_log
gtest_dlt_daemon_event_handler
+ gtest_dlt_daemon_multiple_files_logging
)
if(WITH_DLT_SHM_ENABLE)
list(APPEND TARGET_LIST gtest_dlt_shm)
@@ -64,8 +65,9 @@ foreach(target IN LISTS TARGET_LIST)
set(target_SRCS ${target})
add_executable(${target} ${target_SRCS} ${systemd_SRCS})
target_link_libraries(${target} ${DLT_DAEMON_LIBRARIES})
- if(${target} STREQUAL "gtest_dlt_daemon_event_handler" OR
- ${target} STREQUAL "gtest_dlt_shm")
+ if(${target} STREQUAL "gtest_dlt_daemon_event_handler"
+ OR ${target} STREQUAL "gtest_dlt_shm"
+ OR ${target} STREQUAL "gtest_dlt_daemon_multiple_files_logging")
add_test(NAME ${target}
COMMAND ${target})
else()
diff --git a/tests/gtest_dlt_daemon_multiple_files_logging.cpp b/tests/gtest_dlt_daemon_multiple_files_logging.cpp
new file mode 100644
index 0000000..2da512e
--- /dev/null
+++ b/tests/gtest_dlt_daemon_multiple_files_logging.cpp
@@ -0,0 +1,277 @@
+/*
+ * 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/.
+ */
+
+/*!
+ * \author
+ * Oleg Tropmann <oleg.tropmann@daimler.com>
+ * Daniel Weber <daniel.w.weber@daimler.com>
+ *
+ * \copyright Copyright © 2022 Daimler TSS GmbH. \n
+ * License MPL-2.0: Mozilla Public License version 2.0 http://mozilla.org/MPL/2.0/.
+ *
+ * \file gtest_dlt_daemon_multiple_files_logging.ccpp
+ */
+
+#include <gtest/gtest.h>
+
+int connectServer(void);
+
+extern "C"
+{
+#include "dlt_common.h"
+#include <syslog.h>
+#include <dirent.h>
+#include <string.h>
+}
+
+// publish prototypes
+void configure(const char* path, const char* file_name, bool enable_limit, int file_size, int max_files_size);
+void write_log_message();
+void verify_multiple_files(const char* path, const char* file_name, int file_size, int max_files_size);
+void verify_single_file(const char* path, const char* file_name);
+void verify_in_one_file(const char* path, const char* file_name, const char* log1, const char* log2);
+bool file_contains_strings(const char* abs_file_path, const char* str1, const char* str2);
+int get_file_index(char* file_name);
+int compare_int(const void* a, const void* b);
+
+/**
+ * Configure dlt logging using file size limits.
+ */
+TEST(t_dlt_logging_multiple_files, normal)
+{
+ const char* path = "/tmp";
+ const char* file_name = "dlt.log";
+ const int file_size = 128;
+ const int max_file_size = 512;
+ configure(path, file_name, true, file_size, max_file_size);
+ write_log_message();
+ EXPECT_NO_THROW(dlt_log_free());
+ verify_multiple_files(path, file_name, file_size, max_file_size);
+}
+
+/**
+ * Configure dlt logging using file size limits.
+ * Though, due to an error during initialization dlt logging defaults to one file logging.
+ */
+TEST(t_dlt_logging_one_file_as_fallback, normal)
+{
+ const char* path = "/tmp";
+ const char* file_name = "dltlog";
+ configure(path, file_name, true, 128, 512);
+ write_log_message();
+ EXPECT_NO_THROW(dlt_log_free());
+ verify_single_file(path, file_name);
+}
+
+/**
+ * Configure dlt logging without file size limits resulting in one file logging.
+ */
+TEST(t_dlt_logging_one_file, normal)
+{
+ const char* path = "/tmp";
+ const char* file_name = "dlt.log";
+ configure(path, file_name, false, 128, 512);
+ write_log_message();
+ EXPECT_NO_THROW(dlt_log_free());
+ verify_single_file(path, file_name);
+}
+
+/**
+ * The dlt_daemon calls dlt_log_init multiple times. In the past, so we create
+ * unnecessary two files. The reinit have to append to the first file.
+ */
+TEST(t_dlt_logging_multiple_files_append_reinit, normal)
+{
+ const char* path = "/tmp";
+ const char* file_name = "dlt.log";
+ const int file_size = 256;
+ const int max_file_size = 512;
+
+ const char* log1 = "ONE\n";
+ const char* log2 = "TWO\n";
+
+ configure(path, file_name, true, file_size, max_file_size);
+ dlt_vlog(LOG_INFO, log1);
+ EXPECT_NO_THROW(dlt_log_free());
+
+ configure(path, file_name, true, file_size, max_file_size);
+ dlt_vlog(LOG_INFO, log2);
+ EXPECT_NO_THROW(dlt_log_free());
+ verify_in_one_file(path, file_name, log1, log2);
+}
+
+void configure(const char *path, const char* file_name, const bool enable_limit, const int file_size, const int max_files_size)
+{
+ char abs_file_path[PATH_MAX];
+ snprintf(abs_file_path, sizeof(abs_file_path), "%s/%s", path, file_name);
+ printf("debug test: %s\n", abs_file_path);
+
+ EXPECT_NO_THROW(dlt_log_set_filename(abs_file_path));
+ EXPECT_NO_THROW(dlt_log_set_level(6));
+ EXPECT_NO_THROW(dlt_log_init_multiple_logfiles_support(DLT_LOG_TO_FILE, enable_limit, file_size, max_files_size));
+}
+
+void write_log_message()
+{
+ for (unsigned int i = 0; i < 10; i++) {
+ dlt_vlog(LOG_INFO, "%d. Unit test logging into multiple files if configured.\n", i);
+ }
+}
+
+void verify_multiple_files(const char* path, const char* file_name, const int file_size, const int max_files_size)
+{
+ int sum_size = 0;
+ int num_files = 0;
+ int file_indices[100];
+
+ char filename[PATH_MAX + 1];
+ struct dirent *dp;
+ struct stat status;
+
+ char file_name_copy[NAME_MAX];
+ strncpy(file_name_copy, file_name, NAME_MAX);
+ char filename_base[NAME_MAX];
+ EXPECT_TRUE(dlt_extract_base_name_without_ext(file_name_copy, filename_base, sizeof(filename_base)));
+ const char *filename_ext = get_filename_ext(file_name);
+ EXPECT_TRUE(filename_ext);
+
+ DIR *dir = opendir(path);
+ while ((dp = readdir(dir)) != NULL) {
+ if (strstr(dp->d_name, filename_base) &&
+ strstr(dp->d_name, filename_ext)) {
+
+ snprintf(filename, sizeof(filename), "%s/%s", path, dp->d_name);
+
+ if (0 == stat(filename, &status)) {
+ EXPECT_LE(status.st_size, file_size);
+ EXPECT_GE(status.st_size, file_size/2);
+ sum_size += status.st_size;
+ file_indices[num_files++] = get_file_index(filename);
+ } else {
+ EXPECT_TRUE(false);
+ }
+ }
+ }
+
+ EXPECT_LE(sum_size, max_files_size);
+ EXPECT_GT(sum_size, 0);
+ EXPECT_GT(num_files, 0);
+
+ //check that file indices are successive in ascending order
+ qsort(file_indices, num_files, sizeof(int), compare_int);
+ int index = file_indices[0];
+ for (int i=1; i<num_files; i++) {
+ EXPECT_EQ(file_indices[i], ++index);
+ }
+}
+
+void verify_single_file(const char* path, const char* file_name)
+{
+ char abs_file_path[PATH_MAX];
+ snprintf(abs_file_path, sizeof(abs_file_path), "%s/%s", path, file_name);
+
+ struct stat status;
+ if (0 == stat(abs_file_path, &status)) {
+ EXPECT_GT(status.st_size, 0);
+ } else {
+ EXPECT_TRUE(false);
+ }
+}
+
+void verify_in_one_file(const char* path, const char* file_name, const char* log1, const char* log2)
+{
+ char abs_file_path[PATH_MAX + 1];
+ struct dirent *dp;
+
+ char file_name_copy[NAME_MAX];
+ strncpy(file_name_copy, file_name, NAME_MAX);
+ char filename_base[NAME_MAX];
+ EXPECT_TRUE(dlt_extract_base_name_without_ext(file_name_copy, filename_base, sizeof(filename_base)));
+ const char *filename_ext = get_filename_ext(file_name);
+ EXPECT_TRUE(filename_ext);
+
+ bool found = false;
+
+ DIR *dir = opendir(path);
+ while ((dp = readdir(dir)) != NULL) {
+ if (strstr(dp->d_name, filename_base) &&
+ strstr(dp->d_name, filename_ext)) {
+
+ snprintf(abs_file_path, sizeof(abs_file_path), "%s/%s", path, dp->d_name);
+
+ if (file_contains_strings(abs_file_path, log1, log2)) {
+ found = true;
+ break;
+ }
+ }
+ }
+
+ EXPECT_TRUE(found);
+}
+
+bool file_contains_strings(const char* abs_file_path, const char* str1, const char* str2)
+{
+ bool found = false;
+ FILE *file = fopen(abs_file_path, "r");
+ if (file != nullptr) {
+ fseek (file , 0 , SEEK_END);
+ long size = ftell (file);
+ rewind (file);
+
+ char* buffer = (char*) malloc(size);
+ long read_bytes = fread(buffer, 1, size, file);
+
+ EXPECT_EQ(size, read_bytes);
+
+ if ((strstr(buffer, str1) != nullptr) &&
+ (strstr(buffer, str2) != nullptr)) {
+
+ found = true;
+ }
+
+ fclose(file);
+ free(buffer);
+ }
+ return found;
+}
+
+int get_file_index(char* file_name)
+{
+ char *dot = strrchr(file_name, '.');
+ *dot = '\0';
+
+ //start with the first zero
+ char *iterator = strchr(file_name, '0');
+ do {} while (*(++iterator) == '0');
+ //now iterator points to the first character after 0
+
+ return atoi(iterator);
+}
+
+int compare_int(const void* a, const void* b)
+{
+ if (*((int*)a) == *((int*)b)) return 0;
+ else if (*((int*)a) < *((int*)b)) return -1;
+ else return 1;
+}
+
+int main(int argc, char **argv)
+{
+ ::testing::InitGoogleTest(&argc, argv);
+ ::testing::FLAGS_gtest_break_on_failure = true;
+ /*::testing::FLAGS_gtest_filter = "*.normal"; */
+ /*::testing::FLAGS_gtest_repeat = 10000; */
+ return RUN_ALL_TESTS();
+}