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 398 : for (i = 0; i < file_cnt; i++) {
62 : int len = 0;
63 386 : len = (int)strlen(file_name);
64 :
65 386 : if ((strncmp(files[i]->d_name, file_name, (size_t)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 398 : 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 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 : size_t bufsize = sizeof(files_buffer->filename);
126 : memset(file_name, 0, bufsize);
127 :
128 : int written = snprintf(file_name, bufsize, "%s%s%s%s",
129 9 : files_buffer->filenameBase,
130 : MULTIPLE_FILES_FILENAME_INDEX_DELIM,
131 : file_index,
132 9 : files_buffer->filenameExt);
133 9 : if (written < 0 || (size_t)written >= bufsize) {
134 0 : file_name[bufsize - 1] = '\0';
135 : }
136 9 : }
137 :
138 53 : unsigned int multiple_files_buffer_get_idx_of_log_file(char *file)
139 : {
140 53 : if ((file == NULL) || (file[0] == '\0')) return 0;
141 :
142 53 : const char d[2] = MULTIPLE_FILES_FILENAME_INDEX_DELIM;
143 : char *token;
144 :
145 53 : token = strtok(file, d);
146 : /* we are interested in 2. token because of log file name */
147 53 : token = strtok(NULL, d);
148 :
149 53 : return token != NULL ? (unsigned int)strtol(token, NULL, 10) : 0;
150 : }
151 :
152 9 : DltReturnValue multiple_files_buffer_create_new_file(MultipleFilesRingBuffer *files_buffer)
153 : {
154 9 : if (files_buffer == NULL) {
155 0 : fprintf(stderr, "multiple files buffer not set\n");
156 0 : return DLT_RETURN_ERROR;
157 : }
158 :
159 : time_t t;
160 : struct tm tmp;
161 : char file_path[PATH_MAX + 1];
162 : unsigned int idx = 0;
163 : int ret = 0;
164 :
165 : /* set filename */
166 9 : if (files_buffer->filenameTimestampBased) {
167 : /* timestamp format: "yyyymmdd_hhmmss" */
168 : char timestamp[16];
169 0 : t = time(NULL);
170 0 : tzset();
171 0 : localtime_r(&t, &tmp);
172 :
173 0 : strftime(timestamp, sizeof(timestamp), "%Y%m%d_%H%M%S", &tmp);
174 :
175 0 : ret = snprintf(files_buffer->filename, sizeof(files_buffer->filename), "%s%s%s%s",
176 0 : files_buffer->filenameBase,
177 : MULTIPLE_FILES_FILENAME_TIMESTAMP_DELIM, timestamp,
178 0 : files_buffer->filenameExt);
179 :
180 0 : if ((ret < 0) || ((size_t)ret >= (int)sizeof(files_buffer->filename))) {
181 0 : fprintf(stderr, "filename cannot be concatenated\n");
182 0 : return DLT_RETURN_ERROR;
183 : }
184 :
185 : ret = snprintf(file_path, sizeof(file_path), "%s/%s",
186 0 : files_buffer->directory, files_buffer->filename);
187 :
188 0 : if ((ret < 0) || ((size_t)ret >= (int)sizeof(file_path))) {
189 0 : fprintf(stderr, "file path cannot be concatenated\n");
190 0 : return DLT_RETURN_ERROR;
191 : }
192 : }
193 : else {
194 9 : char newest[NAME_MAX + 1] = { 0 };
195 9 : char oldest[NAME_MAX + 1] = { 0 };
196 : /* targeting newest file, ignoring number of files in dir returned */
197 9 : if (0 == multiple_files_buffer_storage_dir_info(files_buffer->directory,
198 9 : files_buffer->filenameBase,
199 : newest,
200 : oldest)) {
201 : printf("No multiple files found\n");
202 : }
203 :
204 9 : idx = multiple_files_buffer_get_idx_of_log_file(newest) + 1;
205 :
206 9 : multiple_files_buffer_file_name(files_buffer, idx);
207 : ret = snprintf(file_path, sizeof(file_path), "%s/%s",
208 9 : files_buffer->directory, files_buffer->filename);
209 :
210 9 : if ((ret < 0) || (ret >= NAME_MAX)) {
211 0 : fprintf(stderr, "filename cannot be concatenated\n");
212 0 : return DLT_RETURN_ERROR;
213 : }
214 : }
215 :
216 : /* open DLT output file */
217 9 : errno = 0;
218 9 : files_buffer->ohandle = open(file_path, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR |
219 : S_IRGRP | S_IROTH); /* mode: wb */
220 :
221 9 : if (files_buffer->ohandle == -1) {
222 : /* file cannot be opened */
223 0 : fprintf(stderr, "file %s cannot be created, error: %s\n", file_path, strerror(errno));
224 0 : return DLT_RETURN_ERROR;
225 : }
226 :
227 : return DLT_RETURN_OK;
228 : }
229 :
230 21 : ssize_t multiple_files_buffer_get_total_size(const MultipleFilesRingBuffer *files_buffer)
231 : {
232 21 : if (files_buffer == NULL) {
233 0 : fprintf(stderr, "multiple files buffer not set\n");
234 0 : return -1;
235 : }
236 :
237 : struct dirent *dp;
238 : char filename[PATH_MAX + 1];
239 : ssize_t size = 0;
240 : struct stat status;
241 :
242 : /* go through all dlt files in directory */
243 21 : DIR *dir = opendir(files_buffer->directory);
244 21 : if (!dir) {
245 0 : fprintf(stderr, "directory %s cannot be opened, error=%s\n", files_buffer->directory, strerror(errno));
246 0 : return -1;
247 : }
248 :
249 716 : while ((dp = readdir(dir)) != NULL) {
250 : // consider files matching with a specific base name and a particular extension
251 695 : if (strstr(dp->d_name, files_buffer->filenameBase) && strstr(dp->d_name, files_buffer->filenameExt)) {
252 : int res = snprintf(filename, sizeof(filename), "%s/%s", files_buffer->directory, dp->d_name);
253 :
254 : /* if the total length of the string is greater than the buffer, silently forget it. */
255 : /* snprintf: a return value of size or more means that the output was truncated */
256 : /* if an output error is encountered, a negative value is returned. */
257 80 : if (((unsigned int)res < sizeof(filename)) && (res > 0)) {
258 80 : errno = 0;
259 80 : if (0 == stat(filename, &status))
260 80 : size += status.st_size;
261 : else
262 0 : fprintf(stderr, "file %s cannot be stat-ed, error=%s\n", filename, strerror(errno));
263 : }
264 : }
265 : }
266 :
267 21 : closedir(dir);
268 :
269 : /* return size */
270 21 : return size;
271 : }
272 :
273 9 : int multiple_files_buffer_delete_oldest_file(MultipleFilesRingBuffer *files_buffer)
274 : {
275 9 : if (files_buffer == NULL) {
276 0 : fprintf(stderr, "multiple files buffer not set\n");
277 0 : return -1; /* ERROR */
278 : }
279 :
280 : struct dirent *dp;
281 : char filename[PATH_MAX + 1];
282 : char filename_oldest[PATH_MAX + 1];
283 : unsigned long size_oldest = 0;
284 : struct stat status;
285 : time_t time_oldest = 0;
286 : int index_oldest = INT_MAX;
287 :
288 9 : filename[0] = 0;
289 9 : filename_oldest[0] = 0;
290 :
291 : /* go through all dlt files in directory */
292 9 : DIR *dir = opendir(files_buffer->directory);
293 :
294 9 : if(!dir)
295 : return -1;
296 :
297 318 : while ((dp = readdir(dir)) != NULL) {
298 309 : if (strstr(dp->d_name, files_buffer->filenameBase) && strstr(dp->d_name, files_buffer->filenameExt)) {
299 : int res = snprintf(filename, sizeof(filename), "%s/%s", files_buffer->directory, dp->d_name);
300 :
301 : /* if the total length of the string is greater than the buffer, silently forget it. */
302 : /* snprintf: a return value of size or more means that the output was truncated */
303 : /* if an output error is encountered, a negative value is returned. */
304 44 : if (((unsigned int) res >= sizeof(filename)) || (res <= 0)) {
305 : printf("Filename for delete oldest too long. Skip file.\n");
306 0 : continue;
307 : }
308 :
309 44 : if (files_buffer->filenameTimestampBased) {
310 0 : errno = 0;
311 0 : if (0 == stat(filename, &status)) {
312 0 : if ((time_oldest == 0) || (status.st_mtime < time_oldest)) {
313 0 : time_oldest = status.st_mtime;
314 0 : size_oldest = (unsigned long)status.st_size;
315 : strncpy(filename_oldest, filename, PATH_MAX);
316 0 : filename_oldest[PATH_MAX] = 0;
317 : }
318 : } else {
319 0 : printf("Old file %s cannot be stat-ed, error=%s\n", filename, strerror(errno));
320 : }
321 : } else {
322 : //index based
323 44 : const unsigned int index = multiple_files_buffer_get_idx_of_log_file(filename);
324 44 : if (index < (unsigned int)index_oldest) {
325 23 : index_oldest = (int)index;
326 : snprintf(filename, sizeof(filename), "%s/%s", files_buffer->directory, dp->d_name);
327 : strncpy(filename_oldest, filename, PATH_MAX);
328 23 : filename_oldest[PATH_MAX] = 0;
329 : }
330 : }
331 : }
332 : }
333 :
334 9 : closedir(dir);
335 :
336 : /* delete file */
337 9 : if (filename_oldest[0]) {
338 9 : if (remove(filename_oldest)) {
339 0 : fprintf(stderr, "Remove file %s failed! error=%s\n", filename_oldest, strerror(errno));
340 0 : return -1; /* ERROR */
341 : }
342 : } else {
343 0 : fprintf(stderr, "No file to be removed!\n");
344 0 : return -1; /* ERROR */
345 : }
346 :
347 : /* return size of deleted file*/
348 9 : return (int)size_oldest;
349 : }
350 :
351 12 : DltReturnValue multiple_files_buffer_check_size(MultipleFilesRingBuffer *files_buffer)
352 : {
353 12 : if (files_buffer == NULL) {
354 0 : fprintf(stderr, "multiple files buffer not set\n");
355 0 : return DLT_RETURN_ERROR;
356 : }
357 :
358 : struct stat status;
359 :
360 : /* check for existence of buffer files directory */
361 12 : errno = 0;
362 12 : if (stat(files_buffer->directory, &status) == -1) {
363 0 : fprintf(stderr, "Buffer files directory: %s doesn't exist, error=%s\n", files_buffer->directory, strerror(errno));
364 0 : return DLT_RETURN_ERROR;
365 : }
366 : /* check for accessibility of buffer files directory */
367 12 : else if (access(files_buffer->directory, W_OK) != 0) {
368 0 : fprintf(stderr, "Buffer files directory: %s doesn't have the write access \n", files_buffer->directory);
369 0 : return DLT_RETURN_ERROR;
370 : }
371 :
372 : ssize_t total_size = 0;
373 : /* check size of complete buffer file */
374 21 : while ((total_size = multiple_files_buffer_get_total_size(files_buffer)) > (files_buffer->maxSize - files_buffer->fileSize)) {
375 : /* remove the oldest files as long as new file will not fit in completely into complete multiple files buffer */
376 9 : if (multiple_files_buffer_delete_oldest_file(files_buffer) < 0) return DLT_RETURN_ERROR;
377 : }
378 :
379 12 : return total_size == -1 ? DLT_RETURN_ERROR : DLT_RETURN_OK;
380 : }
381 :
382 3 : DltReturnValue multiple_files_buffer_open_file_for_append(MultipleFilesRingBuffer *files_buffer) {
383 3 : if (files_buffer == NULL || files_buffer->filenameTimestampBased) return DLT_RETURN_ERROR;
384 :
385 3 : char newest[NAME_MAX + 1] = {0};
386 3 : char oldest[NAME_MAX + 1] = {0};
387 : /* targeting the newest file, ignoring number of files in dir returned */
388 :
389 3 : if (0 == multiple_files_buffer_storage_dir_info(files_buffer->directory,
390 3 : files_buffer->filenameBase, newest, oldest) ) {
391 : // no file for appending found. Create a new one
392 : printf("No multiple files for appending found. Create a new one\n");
393 0 : return multiple_files_buffer_create_new_file(files_buffer);
394 : }
395 :
396 : char file_path[PATH_MAX + 1];
397 : int ret = snprintf(file_path, sizeof(file_path), "%s/%s",
398 : files_buffer->directory, newest);
399 :
400 3 : if ((ret < 0) || (ret >= NAME_MAX)) {
401 0 : fprintf(stderr, "filename cannot be concatenated\n");
402 0 : return DLT_RETURN_ERROR;
403 : }
404 :
405 : /* open DLT output file */
406 3 : errno = 0;
407 3 : files_buffer->ohandle = open(file_path, O_WRONLY | O_APPEND); /* mode: wb */
408 :
409 3 : return files_buffer->ohandle == -1 ? DLT_RETURN_ERROR : DLT_RETURN_OK;
410 : }
411 :
412 3 : DltReturnValue multiple_files_buffer_init(MultipleFilesRingBuffer *files_buffer,
413 : const char *directory,
414 : const int file_size,
415 : const int max_size,
416 : const bool filename_timestamp_based,
417 : const bool append,
418 : const char *filename_base,
419 : const char *filename_ext)
420 : {
421 3 : if (files_buffer == NULL) {
422 0 : fprintf(stderr, "multiple files buffer not set\n");
423 0 : return DLT_RETURN_ERROR;
424 : }
425 :
426 : /* init parameters */
427 3 : strncpy(files_buffer->directory, directory, NAME_MAX);
428 3 : files_buffer->directory[NAME_MAX] = 0;
429 3 : files_buffer->fileSize = file_size;
430 3 : files_buffer->maxSize = max_size;
431 3 : files_buffer->filenameTimestampBased = filename_timestamp_based;
432 3 : strncpy(files_buffer->filenameBase, filename_base, NAME_MAX);
433 3 : files_buffer->filenameBase[NAME_MAX] = 0;
434 3 : strncpy(files_buffer->filenameExt, filename_ext, NAME_MAX);
435 3 : files_buffer->filenameExt[NAME_MAX] = 0;
436 :
437 3 : if (DLT_RETURN_ERROR == multiple_files_buffer_check_size(files_buffer)) return DLT_RETURN_ERROR;
438 :
439 3 : return (!files_buffer->filenameTimestampBased && append)
440 3 : ? multiple_files_buffer_open_file_for_append(files_buffer)
441 3 : : multiple_files_buffer_create_new_file(files_buffer);
442 : }
443 :
444 12 : void multiple_files_buffer_rotate_file(MultipleFilesRingBuffer *files_buffer, const int size)
445 : {
446 : /* check file size here */
447 12 : if ((lseek(files_buffer->ohandle, 0, SEEK_CUR) + size) < files_buffer->fileSize) return;
448 :
449 : /* close old file */
450 9 : close(files_buffer->ohandle);
451 9 : files_buffer->ohandle = -1;
452 :
453 : /* check complete files size, remove old logs if needed */
454 9 : if (DLT_RETURN_ERROR == multiple_files_buffer_check_size(files_buffer)) return;
455 :
456 : /* create new file */
457 9 : multiple_files_buffer_create_new_file(files_buffer);
458 : }
459 :
460 12 : DltReturnValue multiple_files_buffer_write_chunk(const MultipleFilesRingBuffer *files_buffer,
461 : const unsigned char *data,
462 : const int size)
463 : {
464 12 : if (files_buffer == NULL) {
465 0 : fprintf(stderr, "multiple files buffer not set\n");
466 0 : return DLT_RETURN_ERROR;
467 : }
468 :
469 12 : if (data && (files_buffer->ohandle >= 0)) {
470 12 : if (write(files_buffer->ohandle, data, (size_t)size) != (ssize_t)size) {
471 0 : fprintf(stderr, "file write failed!\n");
472 0 : return DLT_RETURN_ERROR;
473 : }
474 : }
475 : return DLT_RETURN_OK;
476 : }
477 :
478 12 : DltReturnValue multiple_files_buffer_write(MultipleFilesRingBuffer *files_buffer,
479 : const unsigned char *data,
480 : const int size)
481 : {
482 12 : if (files_buffer->ohandle < 0) return DLT_RETURN_ERROR;
483 :
484 12 : multiple_files_buffer_rotate_file(files_buffer, size);
485 :
486 : /* write data into log file */
487 12 : return multiple_files_buffer_write_chunk(files_buffer, data, size);
488 : }
489 :
490 3 : DltReturnValue multiple_files_buffer_free(const MultipleFilesRingBuffer *files_buffer)
491 : {
492 3 : if (files_buffer == NULL) {
493 0 : fprintf(stderr, "multiple files buffer not set\n");
494 0 : return DLT_RETURN_ERROR;
495 : }
496 :
497 3 : if (files_buffer->ohandle < 0) return DLT_RETURN_ERROR;
498 :
499 : /* close last used log file */
500 3 : close(files_buffer->ohandle);
501 :
502 3 : return DLT_RETURN_OK;
503 : }
|