LCOV - code coverage report
Current view: top level - shared - dlt_multiple_files.c (source / functions) Hit Total Coverage
Test: dlt_final_coverage.info Lines: 133 193 68.9 %
Date: 2024-12-06 04:41:42 Functions: 13 13 100.0 %

          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          48 :                 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          32 :                 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           0 :         } 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           0 :         } 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          18 :                     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 1.14