summaryrefslogtreecommitdiff
path: root/src/shared/dlt_multiple_files.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/shared/dlt_multiple_files.c')
-rw-r--r--src/shared/dlt_multiple_files.c499
1 files changed, 499 insertions, 0 deletions
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;
+}