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