diff options
-rw-r--r-- | doc/dlt.conf.5.md | 20 | ||||
-rw-r--r-- | include/dlt/dlt_common.h | 55 | ||||
-rw-r--r-- | include/dlt/dlt_multiple_files.h | 154 | ||||
-rw-r--r-- | include/dlt/dlt_offline_trace.h | 103 | ||||
-rw-r--r-- | src/console/dlt-convert.c | 11 | ||||
-rw-r--r-- | src/daemon/CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/daemon/dlt-daemon.c | 52 | ||||
-rw-r--r-- | src/daemon/dlt-daemon.h | 11 | ||||
-rw-r--r-- | src/daemon/dlt.conf | 9 | ||||
-rw-r--r-- | src/gateway/dlt_gateway.c | 3 | ||||
-rw-r--r-- | src/lib/CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/shared/dlt_common.c | 156 | ||||
-rw-r--r-- | src/shared/dlt_multiple_files.c | 499 | ||||
-rw-r--r-- | src/shared/dlt_offline_trace.c | 397 | ||||
-rw-r--r-- | tests/CMakeLists.txt | 6 | ||||
-rw-r--r-- | tests/gtest_dlt_daemon_multiple_files_logging.cpp | 277 |
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(); +} |