LCOV - code coverage report
Current view: top level - shared - dlt_multiple_files.c (source / functions) Coverage Total Hit
Test: dlt_final_coverage.info Lines: 69.3 % 189 131
Test Date: 2025-03-25 20:53:42 Functions: 100.0 % 13 13

            Line data    Source code
       1              : /*
       2              :  * SPDX license identifier: MPL-2.0
       3              :  *
       4              :  * Copyright (C) 2022, Daimler TSS GmbH
       5              :  *
       6              :  * This file is part of COVESA Project DLT - Diagnostic Log and Trace.
       7              :  *
       8              :  * This Source Code Form is subject to the terms of the
       9              :  * Mozilla Public License (MPL), v. 2.0.
      10              :  * If a copy of the MPL was not distributed with this file,
      11              :  * You can obtain one at http://mozilla.org/MPL/2.0/.
      12              :  *
      13              :  * For further information see https://www.covesa.global/.
      14              :  */
      15              : 
      16              : /*!
      17              :  * \author
      18              :  * Oleg Tropmann <oleg.tropmann@daimler.com>
      19              :  * Daniel Weber <daniel.w.weber@daimler.com>
      20              :  *
      21              :  * \copyright Copyright © 2022 Daimler TSS GmbH. \n
      22              :  * License MPL-2.0: Mozilla Public License version 2.0 http://mozilla.org/MPL/2.0/.
      23              :  *
      24              :  * \file dlt_daemon_log.c
      25              :  */
      26              : 
      27              : #include <stdio.h>
      28              : #include <stdlib.h>
      29              : #include <string.h>
      30              : #include <time.h>
      31              : #include <sys/types.h>
      32              : #include <sys/stat.h>
      33              : #include <fcntl.h>
      34              : #include <unistd.h>
      35              : #include <dirent.h>
      36              : #include <syslog.h>
      37              : #include <errno.h>
      38              : #include <stdarg.h>
      39              : 
      40              : #include "dlt_multiple_files.h"
      41              : #include "dlt_common.h"
      42              : #include "dlt_log.h"
      43              : 
      44           12 : unsigned int multiple_files_buffer_storage_dir_info(const char *path, const char *file_name,
      45              :                                                     char *newest, char *oldest)
      46              : {
      47              :     int i = 0;
      48              :     unsigned int num_log_files = 0;
      49           12 :     struct dirent **files = { 0 };
      50              :     char *tmp_old = NULL;
      51              :     char *tmp_new = NULL;
      52              : 
      53           12 :     if ((path == NULL) || (file_name == NULL) || (newest == NULL) || (oldest == NULL)) {
      54            0 :         fprintf(stderr, "multiple_files_buffer_storage_dir_info: Invalid parameter(s)");
      55            0 :         return 0;
      56              :     }
      57              : 
      58           12 :     const int file_cnt = scandir(path, &files, NULL, alphasort);
      59           12 :     if (file_cnt <= 0) return 0;
      60              : 
      61          434 :     for (i = 0; i < file_cnt; i++) {
      62              :         int len = 0;
      63          422 :         len = strlen(file_name);
      64              : 
      65          422 :         if ((strncmp(files[i]->d_name, file_name, len) == 0) &&
      66          110 :             (files[i]->d_name[len] == MULTIPLE_FILES_FILENAME_INDEX_DELIM[0])) {
      67           48 :             num_log_files++;
      68              : 
      69           48 :             if ((tmp_old == NULL) || (strlen(tmp_old) >= strlen(files[i]->d_name))) {
      70              :                 if (tmp_old == NULL) {
      71              :                     tmp_old = files[i]->d_name;
      72           36 :                 } else if (strlen(tmp_old) > strlen(files[i]->d_name)) {
      73              :                     /* when file name is smaller, it is older */
      74              :                     tmp_old = files[i]->d_name;
      75           20 :                 } else if (strcmp(tmp_old, files[i]->d_name) > 0) {
      76              :                     /* filename length is equal, do a string compare */
      77              :                     tmp_old = files[i]->d_name;
      78              :                 }
      79              :             }
      80              : 
      81           48 :             if ((tmp_new == NULL) || (strlen(tmp_new) <= strlen(files[i]->d_name))) {
      82              :                 if (tmp_new == NULL) {
      83              :                     tmp_new = files[i]->d_name;
      84           20 :                 } else if (strlen(tmp_new) < strlen(files[i]->d_name)) {
      85              :                     /* when file name is longer, it is younger */
      86              :                     tmp_new = files[i]->d_name;
      87           20 :                 } else if (strcmp(tmp_new, files[i]->d_name) < 0) {
      88              :                     tmp_new = files[i]->d_name;
      89              :                 }
      90              :             }
      91              :         }
      92              :     }
      93              : 
      94           12 :     if (num_log_files > 0) {
      95           12 :         if ((tmp_old != NULL) && (strlen(tmp_old) < NAME_MAX)) {
      96              :             strncpy(oldest, tmp_old, NAME_MAX);
      97           12 :             oldest[NAME_MAX] = '\0';
      98              :         } else if ((tmp_old != NULL) && (strlen(tmp_old) >=  NAME_MAX)) {
      99              :             printf("length mismatch of file %s\n", tmp_old);
     100              :         }
     101              : 
     102           12 :         if ((tmp_new != NULL) && (strlen(tmp_new) < NAME_MAX)) {
     103              :             strncpy(newest, tmp_new, NAME_MAX);
     104           12 :             newest[NAME_MAX] = '\0';
     105              :         } else if ((tmp_new != NULL) && (strlen(tmp_new) >=  NAME_MAX)) {
     106              :             printf("length mismatch of file %s\n", tmp_new);
     107              :         }
     108              :     }
     109              : 
     110              :     /* free scandir result */
     111          434 :     for (i = 0; i < file_cnt; i++) free(files[i]);
     112              : 
     113           12 :     free(files);
     114              : 
     115           12 :     return num_log_files;
     116              : }
     117              : 
     118            9 : void multiple_files_buffer_file_name(MultipleFilesRingBuffer *files_buffer, const size_t length, const unsigned int idx)
     119              : {
     120              :     char file_index[11]; /* UINT_MAX = 4294967295 -> 10 digits */
     121              :     snprintf(file_index, sizeof(file_index), "%010u", idx);
     122              : 
     123              :     /* create log file name */
     124            9 :     char* file_name = files_buffer->filename;
     125              :     memset(file_name, 0, length * sizeof(char));
     126              : 
     127            9 :     const size_t size = length - strlen(file_name) - 1;
     128            9 :     strncat(file_name, files_buffer->filenameBase, size);
     129              :     strncat(file_name, MULTIPLE_FILES_FILENAME_INDEX_DELIM, size);
     130              :     strncat(file_name, file_index, size);
     131            9 :     strncat(file_name, files_buffer->filenameExt, size);
     132            9 : }
     133              : 
     134           53 : unsigned int multiple_files_buffer_get_idx_of_log_file(char *file)
     135              : {
     136           53 :     if ((file == NULL) || (file[0] == '\0')) return 0;
     137              : 
     138           53 :     const char d[2] = MULTIPLE_FILES_FILENAME_INDEX_DELIM;
     139              :     char *token;
     140              : 
     141           53 :     token = strtok(file, d);
     142              :     /* we are interested in 2. token because of log file name */
     143           53 :     token = strtok(NULL, d);
     144              : 
     145           53 :     return token != NULL ? strtol(token, NULL, 10) : 0;
     146              : }
     147              : 
     148            9 : DltReturnValue multiple_files_buffer_create_new_file(MultipleFilesRingBuffer *files_buffer)
     149              : {
     150            9 :     if (files_buffer == NULL) {
     151            0 :         fprintf(stderr, "multiple files buffer not set\n");
     152            0 :         return DLT_RETURN_ERROR;
     153              :     }
     154              : 
     155              :     time_t t;
     156              :     struct tm tmp;
     157              :     char file_path[PATH_MAX + 1];
     158              :     unsigned int idx = 0;
     159              :     int ret = 0;
     160              : 
     161              :     /* set filename */
     162            9 :     if (files_buffer->filenameTimestampBased) {
     163              :         /* timestamp format: "yyyymmdd_hhmmss" */
     164              :         char timestamp[16];
     165            0 :         t = time(NULL);
     166            0 :         tzset();
     167            0 :         localtime_r(&t, &tmp);
     168              : 
     169            0 :         strftime(timestamp, sizeof(timestamp), "%Y%m%d_%H%M%S", &tmp);
     170              : 
     171            0 :         ret = snprintf(files_buffer->filename, sizeof(files_buffer->filename), "%s%s%s%s",
     172            0 :                        files_buffer->filenameBase,
     173              :                        MULTIPLE_FILES_FILENAME_TIMESTAMP_DELIM, timestamp,
     174            0 :                        files_buffer->filenameExt);
     175              : 
     176            0 :         if ((ret < 0) || ((size_t)ret >= (int)sizeof(files_buffer->filename))) {
     177            0 :             fprintf(stderr, "filename cannot be concatenated\n");
     178            0 :             return DLT_RETURN_ERROR;
     179              :         }
     180              : 
     181              :         ret = snprintf(file_path, sizeof(file_path), "%s/%s",
     182            0 :                        files_buffer->directory, files_buffer->filename);
     183              : 
     184            0 :         if ((ret < 0) || ((size_t)ret >= (int)sizeof(file_path))) {
     185            0 :             fprintf(stderr, "file path cannot be concatenated\n");
     186            0 :             return DLT_RETURN_ERROR;
     187              :         }
     188              :     }
     189              :     else {
     190            9 :         char newest[NAME_MAX + 1] = { 0 };
     191            9 :         char oldest[NAME_MAX + 1] = { 0 };
     192              :         /* targeting newest file, ignoring number of files in dir returned */
     193            9 :         if (0 == multiple_files_buffer_storage_dir_info(files_buffer->directory,
     194            9 :                                                         files_buffer->filenameBase,
     195              :                                                         newest,
     196              :                                                         oldest)) {
     197              :             printf("No multiple files found\n");
     198              :         }
     199              : 
     200            9 :         idx = multiple_files_buffer_get_idx_of_log_file(newest) + 1;
     201              : 
     202            9 :         multiple_files_buffer_file_name(files_buffer, sizeof(files_buffer->filename), idx);
     203              :         ret = snprintf(file_path, sizeof(file_path), "%s/%s",
     204            9 :                        files_buffer->directory, files_buffer->filename);
     205              : 
     206            9 :         if ((ret < 0) || (ret >= NAME_MAX)) {
     207            0 :             fprintf(stderr, "filename cannot be concatenated\n");
     208            0 :             return DLT_RETURN_ERROR;
     209              :         }
     210              :     }
     211              : 
     212              :     /* open DLT output file */
     213            9 :     errno = 0;
     214            9 :     files_buffer->ohandle = open(file_path, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR |
     215              :                                                                 S_IRGRP | S_IROTH); /* mode: wb */
     216              : 
     217            9 :     if (files_buffer->ohandle == -1) {
     218              :         /* file cannot be opened */
     219            0 :         fprintf(stderr, "file %s cannot be created, error: %s\n", file_path, strerror(errno));
     220            0 :         return DLT_RETURN_ERROR;
     221              :     }
     222              : 
     223              :     return DLT_RETURN_OK;
     224              : }
     225              : 
     226           21 : ssize_t multiple_files_buffer_get_total_size(const MultipleFilesRingBuffer *files_buffer)
     227              : {
     228           21 :     if (files_buffer == NULL) {
     229            0 :         fprintf(stderr, "multiple files buffer not set\n");
     230            0 :         return -1;
     231              :     }
     232              : 
     233              :     struct dirent *dp;
     234              :     char filename[PATH_MAX + 1];
     235              :     ssize_t size = 0;
     236              :     struct stat status;
     237              : 
     238              :     /* go through all dlt files in directory */
     239           21 :     DIR *dir = opendir(files_buffer->directory);
     240           21 :     if (!dir) {
     241            0 :         fprintf(stderr, "directory %s cannot be opened, error=%s\n", files_buffer->directory, strerror(errno));
     242            0 :         return -1;
     243              :     }
     244              : 
     245          779 :     while ((dp = readdir(dir)) != NULL) {
     246              :         // consider files matching with a specific base name and a particular extension
     247          758 :         if (strstr(dp->d_name, files_buffer->filenameBase)  && strstr(dp->d_name, files_buffer->filenameExt)) {
     248              :             int res = snprintf(filename, sizeof(filename), "%s/%s", files_buffer->directory, dp->d_name);
     249              : 
     250              :             /* if the total length of the string is greater than the buffer, silently forget it. */
     251              :             /* snprintf: a return value of size  or more means that the output was truncated */
     252              :             /*           if an output error is encountered, a negative value is returned. */
     253           80 :             if (((unsigned int)res < sizeof(filename)) && (res > 0)) {
     254           80 :                 errno = 0;
     255           80 :                 if (0 == stat(filename, &status))
     256           80 :                     size += status.st_size;
     257              :                 else
     258            0 :                     fprintf(stderr, "file %s cannot be stat-ed, error=%s\n", filename, strerror(errno));
     259              :             }
     260              :         }
     261              :     }
     262              : 
     263           21 :     closedir(dir);
     264              : 
     265              :     /* return size */
     266           21 :     return size;
     267              : }
     268              : 
     269            9 : int multiple_files_buffer_delete_oldest_file(MultipleFilesRingBuffer *files_buffer)
     270              : {
     271            9 :     if (files_buffer == NULL) {
     272            0 :         fprintf(stderr, "multiple files buffer not set\n");
     273            0 :         return -1;  /* ERROR */
     274              :     }
     275              : 
     276              :     struct dirent *dp;
     277              :     char filename[PATH_MAX + 1];
     278              :     char filename_oldest[PATH_MAX + 1];
     279              :     unsigned long size_oldest = 0;
     280              :     struct stat status;
     281              :     time_t time_oldest = 0;
     282              :     int index_oldest = INT_MAX;
     283              : 
     284            9 :     filename[0] = 0;
     285            9 :     filename_oldest[0] = 0;
     286              : 
     287              :     /* go through all dlt files in directory */
     288            9 :     DIR *dir = opendir(files_buffer->directory);
     289              : 
     290            9 :     if(!dir)
     291              :         return -1;
     292              : 
     293          345 :     while ((dp = readdir(dir)) != NULL) {
     294          336 :         if (strstr(dp->d_name, files_buffer->filenameBase) && strstr(dp->d_name, files_buffer->filenameExt)) {
     295              :             int res = snprintf(filename, sizeof(filename), "%s/%s", files_buffer->directory, dp->d_name);
     296              : 
     297              :             /* if the total length of the string is greater than the buffer, silently forget it. */
     298              :             /* snprintf: a return value of size  or more means that the output was truncated */
     299              :             /*           if an output error is encountered, a negative value is returned. */
     300           44 :             if (((unsigned int) res >= sizeof(filename)) || (res <= 0)) {
     301              :                 printf("Filename for delete oldest too long. Skip file.\n");
     302            0 :                 continue;
     303              :             }
     304              : 
     305           44 :             if (files_buffer->filenameTimestampBased) {
     306            0 :                 errno = 0;
     307            0 :                 if (0 == stat(filename, &status)) {
     308            0 :                     if ((time_oldest == 0) || (status.st_mtime < time_oldest)) {
     309            0 :                         time_oldest = status.st_mtime;
     310            0 :                         size_oldest = status.st_size;
     311              :                         strncpy(filename_oldest, filename, PATH_MAX);
     312            0 :                         filename_oldest[PATH_MAX] = 0;
     313              :                     }
     314              :                 } else {
     315            0 :                     printf("Old file %s cannot be stat-ed, error=%s\n", filename, strerror(errno));
     316              :                 }
     317              :             } else {
     318              :                 //index based
     319           44 :                 const int index = multiple_files_buffer_get_idx_of_log_file(filename);
     320           44 :                 if (index < index_oldest) {
     321              :                     index_oldest = index;
     322              :                     snprintf(filename, sizeof(filename), "%s/%s", files_buffer->directory, dp->d_name);
     323              :                     strncpy(filename_oldest, filename, PATH_MAX);
     324           24 :                     filename_oldest[PATH_MAX] = 0;
     325              :                 }
     326              :             }
     327              :         }
     328              :     }
     329              : 
     330            9 :     closedir(dir);
     331              : 
     332              :     /* delete file */
     333            9 :     if (filename_oldest[0]) {
     334            9 :         if (remove(filename_oldest)) {
     335            0 :             fprintf(stderr, "Remove file %s failed! error=%s\n", filename_oldest, strerror(errno));
     336            0 :             return -1; /* ERROR */
     337              :         }
     338              :     } else {
     339            0 :         fprintf(stderr, "No file to be removed!\n");
     340            0 :         return -1; /* ERROR */
     341              :     }
     342              : 
     343              :     /* return size of deleted file*/
     344            9 :     return size_oldest;
     345              : }
     346              : 
     347           12 : DltReturnValue multiple_files_buffer_check_size(MultipleFilesRingBuffer *files_buffer)
     348              : {
     349           12 :     if (files_buffer == NULL) {
     350            0 :         fprintf(stderr, "multiple files buffer not set\n");
     351            0 :         return DLT_RETURN_ERROR;
     352              :     }
     353              : 
     354              :     struct stat status;
     355              : 
     356              :     /* check for existence of buffer files directory */
     357           12 :     errno = 0;
     358           12 :     if (stat(files_buffer->directory, &status) == -1) {
     359            0 :         fprintf(stderr, "Buffer files directory: %s doesn't exist, error=%s\n", files_buffer->directory, strerror(errno));
     360            0 :         return DLT_RETURN_ERROR;
     361              :     }
     362              :     /* check for accessibility of buffer files directory */
     363           12 :     else if (access(files_buffer->directory, W_OK) != 0) {
     364            0 :         fprintf(stderr, "Buffer files directory: %s doesn't have the write access \n", files_buffer->directory);
     365            0 :         return DLT_RETURN_ERROR;
     366              :     }
     367              : 
     368              :     ssize_t total_size = 0;
     369              :     /* check size of complete buffer file */
     370           21 :     while ((total_size = multiple_files_buffer_get_total_size(files_buffer)) > (files_buffer->maxSize - files_buffer->fileSize)) {
     371              :         /* remove the oldest files as long as new file will not fit in completely into complete multiple files buffer */
     372            9 :         if (multiple_files_buffer_delete_oldest_file(files_buffer) < 0) return DLT_RETURN_ERROR;
     373              :     }
     374              : 
     375           12 :     return total_size == -1 ? DLT_RETURN_ERROR : DLT_RETURN_OK;
     376              : }
     377              : 
     378            3 : DltReturnValue multiple_files_buffer_open_file_for_append(MultipleFilesRingBuffer *files_buffer) {
     379            3 :     if (files_buffer == NULL || files_buffer->filenameTimestampBased) return DLT_RETURN_ERROR;
     380              : 
     381            3 :     char newest[NAME_MAX + 1] = {0};
     382            3 :     char oldest[NAME_MAX + 1] = {0};
     383              :     /* targeting the newest file, ignoring number of files in dir returned */
     384              : 
     385            3 :     if (0 == multiple_files_buffer_storage_dir_info(files_buffer->directory,
     386            3 :                                                    files_buffer->filenameBase, newest, oldest) ) {
     387              :         // no file for appending found. Create a new one
     388              :         printf("No multiple files for appending found. Create a new one\n");
     389            0 :         return multiple_files_buffer_create_new_file(files_buffer);
     390              :     }
     391              : 
     392              :     char file_path[PATH_MAX + 1];
     393              :     int ret = snprintf(file_path, sizeof(file_path), "%s/%s",
     394              :                          files_buffer->directory, newest);
     395              : 
     396            3 :     if ((ret < 0) || (ret >= NAME_MAX)) {
     397            0 :         fprintf(stderr, "filename cannot be concatenated\n");
     398            0 :         return DLT_RETURN_ERROR;
     399              :     }
     400              : 
     401              :     /* open DLT output file */
     402            3 :     errno = 0;
     403            3 :     files_buffer->ohandle = open(file_path, O_WRONLY | O_APPEND); /* mode: wb */
     404              : 
     405            3 :     return files_buffer->ohandle == -1 ? DLT_RETURN_ERROR : DLT_RETURN_OK;
     406              : }
     407              : 
     408            3 : DltReturnValue multiple_files_buffer_init(MultipleFilesRingBuffer *files_buffer,
     409              :                                           const char *directory,
     410              :                                           const int file_size,
     411              :                                           const int max_size,
     412              :                                           const bool filename_timestamp_based,
     413              :                                           const bool append,
     414              :                                           const char *filename_base,
     415              :                                           const char *filename_ext)
     416              : {
     417            3 :     if (files_buffer == NULL) {
     418            0 :         fprintf(stderr, "multiple files buffer not set\n");
     419            0 :         return DLT_RETURN_ERROR;
     420              :     }
     421              : 
     422              :     /* init parameters */
     423            3 :     strncpy(files_buffer->directory, directory, NAME_MAX);
     424            3 :     files_buffer->directory[NAME_MAX] = 0;
     425            3 :     files_buffer->fileSize = file_size;
     426            3 :     files_buffer->maxSize = max_size;
     427            3 :     files_buffer->filenameTimestampBased = filename_timestamp_based;
     428            3 :     strncpy(files_buffer->filenameBase, filename_base, NAME_MAX);
     429            3 :     files_buffer->filenameBase[NAME_MAX] = 0;
     430            3 :     strncpy(files_buffer->filenameExt, filename_ext, NAME_MAX);
     431            3 :     files_buffer->filenameExt[NAME_MAX] = 0;
     432              : 
     433            3 :     if (DLT_RETURN_ERROR == multiple_files_buffer_check_size(files_buffer)) return DLT_RETURN_ERROR;
     434              : 
     435            3 :     return (!files_buffer->filenameTimestampBased && append)
     436            3 :         ? multiple_files_buffer_open_file_for_append(files_buffer)
     437            3 :         : multiple_files_buffer_create_new_file(files_buffer);
     438              : }
     439              : 
     440           12 : void multiple_files_buffer_rotate_file(MultipleFilesRingBuffer *files_buffer, const int size)
     441              : {
     442              :     /* check file size here */
     443           12 :     if ((lseek(files_buffer->ohandle, 0, SEEK_CUR) + size) < files_buffer->fileSize) return;
     444              : 
     445              :     /* close old file */
     446            9 :     close(files_buffer->ohandle);
     447            9 :     files_buffer->ohandle = -1;
     448              : 
     449              :     /* check complete files size, remove old logs if needed */
     450            9 :     if (DLT_RETURN_ERROR == multiple_files_buffer_check_size(files_buffer)) return;
     451              : 
     452              :     /* create new file */
     453            9 :     multiple_files_buffer_create_new_file(files_buffer);
     454              : }
     455              : 
     456           12 : DltReturnValue multiple_files_buffer_write_chunk(const MultipleFilesRingBuffer *files_buffer,
     457              :                                                  const unsigned char *data,
     458              :                                                  const int size)
     459              : {
     460           12 :     if (files_buffer == NULL) {
     461            0 :         fprintf(stderr, "multiple files buffer not set\n");
     462            0 :         return DLT_RETURN_ERROR;
     463              :     }
     464              : 
     465           12 :     if (data && (files_buffer->ohandle >= 0)) {
     466           12 :         if (write(files_buffer->ohandle, data, size) != size) {
     467            0 :             fprintf(stderr, "file write failed!\n");
     468            0 :             return DLT_RETURN_ERROR;
     469              :         }
     470              :     }
     471              :     return DLT_RETURN_OK;
     472              : }
     473              : 
     474           12 : DltReturnValue multiple_files_buffer_write(MultipleFilesRingBuffer *files_buffer,
     475              :                                            const unsigned char *data,
     476              :                                            const int size)
     477              : {
     478           12 :     if (files_buffer->ohandle < 0) return DLT_RETURN_ERROR;
     479              : 
     480           12 :     multiple_files_buffer_rotate_file(files_buffer, size);
     481              : 
     482              :     /* write data into log file */
     483           12 :     return multiple_files_buffer_write_chunk(files_buffer, data, size);
     484              : }
     485              : 
     486            3 : DltReturnValue multiple_files_buffer_free(const MultipleFilesRingBuffer *files_buffer)
     487              : {
     488            3 :     if (files_buffer == NULL) {
     489            0 :         fprintf(stderr, "multiple files buffer not set\n");
     490            0 :         return DLT_RETURN_ERROR;
     491              :     }
     492              : 
     493            3 :     if (files_buffer->ohandle < 0) return DLT_RETURN_ERROR;
     494              : 
     495              :     /* close last used log file */
     496            3 :     close(files_buffer->ohandle);
     497              : 
     498            3 :     return DLT_RETURN_OK;
     499              : }
        

Generated by: LCOV version 2.0-1