/* * @licence app begin@ * 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/. * @licence end@ */ /*! * \author Alexander Wenzel * * \copyright Copyright © 2011-2015 BMW AG. \n * License MPL-2.0: Mozilla Public License version 2.0 http://mozilla.org/MPL/2.0/. * * \file dlt_offline_trace.c */ /******************************************************************************* ** ** ** SRC-MODULE: dlt_offline_trace.c ** ** ** ** TARGET : linux ** ** ** ** PROJECT : DLT ** ** ** ** AUTHOR : Alexander Wenzel Alexander.AW.Wenzel@bmw.de ** ** ** ** PURPOSE : ** ** ** ** REMARKS : ** ** ** ** PLATFORM DEPENDANT [yes/no]: yes ** ** ** ** TO BE CHANGED BY USER [yes/no]: no ** ** ** *******************************************************************************/ /******************************************************************************* ** Author Identity ** ******************************************************************************** ** ** ** Initials Name Company ** ** -------- ------------------------- ---------------------------------- ** ** aw Alexander Wenzel BMW ** *******************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #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] == '.')) { 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) { if (strlen(tmp_old) < DLT_OFFLINETRACE_FILENAME_MAX_SIZE) { strncpy(oldest, tmp_old, DLT_OFFLINETRACE_FILENAME_MAX_SIZE); } } if (tmp_new != NULL) { if (strlen(tmp_old) < DLT_OFFLINETRACE_FILENAME_MAX_SIZE) { strncpy(newest, tmp_new, DLT_OFFLINETRACE_FILENAME_MAX_SIZE); } } } /* 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, char *name, unsigned int idx) { char file_index[11]; // UINT_MAX = 4294967295 -> 10 digits sprintf(file_index, "%010u", idx); // create log file name memset(log_file_name, 0, DLT_OFFLINETRACE_FILENAME_MAX_SIZE * sizeof(char)); strncat(log_file_name, name,sizeof(DLT_OFFLINETRACE_FILENAME_BASE)); strncat(log_file_name, DLT_OFFLINETRACE_FILENAME_DELI,sizeof(DLT_OFFLINETRACE_FILENAME_DELI)); strncat(log_file_name, file_index,sizeof(file_index)); strncat(log_file_name, DLT_OFFLINETRACE_FILENAME_EXT,sizeof(DLT_OFFLINETRACE_FILENAME_EXT)); } unsigned int dlt_offline_trace_get_idx_of_log_file(char *file) { const char d[2] = "."; 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 outstr[200]; char newest[DLT_OFFLINETRACE_FILENAME_MAX_SIZE] = {0}; char oldest[DLT_OFFLINETRACE_FILENAME_MAX_SIZE] = {0}; unsigned int idx = 0; /* set filename */ if(trace->filenameTimestampBased) { int ret = 0; t = time(NULL); tmp = localtime(&t); if (NULL == tmp) { printf("dlt_offline_trace_create_new_file: pointer to tmp is NULL!"); return DLT_RETURN_ERROR; } if (strftime(outstr, sizeof(outstr),"%Y%m%d_%H%M%S", tmp) == 0) { } ret = snprintf(trace->filename, NAME_MAX , "%s/dlt_offlinetrace_%s.dlt", trace->directory, outstr); if ((ret < 0) || (ret >= NAME_MAX)) { printf("dlt_offlinetrace filename cannot be concatenated\n"); return DLT_RETURN_ERROR; } } else { int ret = 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(outstr, DLT_OFFLINETRACE_FILENAME_BASE, idx); ret = snprintf(trace->filename, NAME_MAX, "%s/%s", trace->directory, outstr); if ((ret < 0) || (ret >= NAME_MAX)) { printf("filename cannot be concatenated\n"); return DLT_RETURN_ERROR; } } /* open DLT output file */ trace->ohandle = open(trace->filename,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",trace->filename); return DLT_RETURN_ERROR; } /* if */ return DLT_RETURN_OK; /* OK */ } unsigned long dlt_offline_trace_get_total_size(DltOfflineTrace *trace) { struct dirent *dp; char filename[256]; unsigned long size = 0; struct stat status; /* go through all dlt files in directory */ DIR *dir = opendir(trace->directory); 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)res0 ) { 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); while ((dp=readdir(dir)) != NULL) { if(strstr(dp->d_name,DLT_OFFLINETRACE_FILENAME_TO_COMPARE)) { 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)res0 ) { 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; } /* check size of complete offline trace */ while((int)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; } } 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) { 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); /* create new file */ dlt_offline_trace_create_new_file(trace); } /* 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); return DLT_RETURN_OK; /* OK */ }