Line data Source code
1 : /**
2 : * Copyright (C) 2015 Advanced Driver Information Technology.
3 : * This code is developed by Advanced Driver Information Technology.
4 : * Copyright of Advanced Driver Information Technology, Bosch and DENSO.
5 : *
6 : * DLT offline log storage functionality source file.
7 : *
8 : * \copyright
9 : * This Source Code Form is subject to the terms of the
10 : * Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with
11 : * this file, You can obtain one at http://mozilla.org/MPL/2.0/.
12 : *
13 : *
14 : * \author Christoph Lipka <clipka@jp.adit-jv.com> ADIT 2015
15 : * \author Syed Hameed <shameed@jp.adit-jv.com> ADIT 2015
16 : *
17 : * \file: dlt_offline_logstorage_behavior.c
18 : * For further information see http://www.covesa.org/.
19 : */
20 :
21 : #include <syslog.h>
22 : #include <limits.h>
23 : #include <dirent.h>
24 : #include <string.h>
25 : #include <sys/types.h>
26 : #include <sys/stat.h>
27 : #include <unistd.h>
28 : #include <stdlib.h>
29 : #include <errno.h>
30 : #include <libgen.h>
31 :
32 : #include "dlt_log.h"
33 : #include "dlt_offline_logstorage.h"
34 : #include "dlt_offline_logstorage_behavior.h"
35 : #include "dlt_offline_logstorage_behavior_internal.h"
36 :
37 : unsigned int g_logstorage_cache_size;
38 :
39 : /**
40 : * dlt_logstorage_concat
41 : *
42 : * Concatenates two strings but keeps the size of the result less than dst_size.
43 : *
44 : * @param dst The destination string
45 : * @param src The source string to concat
46 : */
47 67 : DLT_STATIC void dlt_logstorage_concat_logfile_name(char* log_file_name, const char* append)
48 : {
49 67 : size_t dst_len = strnlen(log_file_name, DLT_MOUNT_PATH_MAX);
50 67 : size_t src_len = strlen(append);
51 :
52 67 : if (dst_len < DLT_MOUNT_PATH_MAX) {
53 67 : size_t rem_len = DLT_MOUNT_PATH_MAX - dst_len + 1;
54 : strncat(log_file_name, append, rem_len);
55 : } else {
56 0 : dlt_vlog(LOG_ERR, "Log file name reached max len: %s [%d]\n", log_file_name, DLT_MOUNT_PATH_MAX);
57 : }
58 :
59 67 : if (src_len + dst_len >= DLT_MOUNT_PATH_MAX) {
60 0 : dlt_vlog(LOG_ERR, "Log file path too long. Truncated: %s", log_file_name);
61 : }
62 67 : }
63 :
64 : /**
65 : * dlt_logstorage_log_file_name
66 : *
67 : * Create log file name in the form configured by the user
68 : * \<filename\>\<delimiter\>\<index\>\<delimiter\>\<timestamp\>.dlt
69 : *
70 : * filename: given in configuration file
71 : * delimiter: Punctuation characters (configured in dlt.conf)
72 : * timestamp: yyyy-mm-dd-hh-mm-ss (enabled/disabled in dlt.conf)
73 : * index: Index len depends on wrap around value in dlt.conf
74 : * ex: wrap around = 99, index will 01..99
75 : * (enabled/disabled in dlt.conf)
76 : *
77 : * @param[out] log_file_name target buffer for the complete logfile name.
78 : * it needs to fit DLT_MOUNT_PATH_MAX chars
79 : * @param[in] file_config User configurations for log file
80 : * @param[in] name file name given in configuration file
81 : * @param[in] num_files max files given in configuration file
82 : * @param[in] idx continous index of log files
83 : * @ return None
84 : */
85 32 : void dlt_logstorage_log_file_name(
86 : char* log_file_name, DltLogStorageUserConfig* file_config, const DltLogStorageFilterConfig* filter_config,
87 : const char* name, const int num_files, const int idx)
88 : {
89 32 : if ((log_file_name == NULL) || (file_config == NULL) || (filter_config == NULL))
90 : return;
91 :
92 31 : const char delim = file_config->logfile_delimiter;
93 31 : int index_width = (int)file_config->logfile_counteridxlen;
94 :
95 31 : if (file_config->logfile_maxcounter == UINT_MAX) {
96 : index_width = 0;
97 : }
98 :
99 31 : const char* suffix = filter_config->gzip_compression == DLT_LOGSTORAGE_GZIP_ON ? ".dlt.gz" : ".dlt";
100 31 : const size_t smax = (size_t)DLT_MOUNT_PATH_MAX - strlen(suffix) - 1;
101 : size_t spos = 0;
102 31 : log_file_name[spos] = '\0';
103 : int rt;
104 :
105 : /* Append file name */
106 31 : spos += strlen(name);
107 31 : dlt_logstorage_concat_logfile_name(log_file_name, filter_config->file_name);
108 :
109 : /* Append index */
110 : /* Do not append if there is only one file and optional index mode is true*/
111 31 : if (!(num_files == 1 && file_config->logfile_optional_counter)) {
112 30 : rt = snprintf(log_file_name + spos, smax - spos, "%c%0*d", delim, index_width, idx);
113 30 : if ((size_t)rt >= smax - spos) {
114 0 : dlt_vlog(LOG_WARNING, "%s: snprintf truncation %s\n", __func__, log_file_name);
115 : spos = smax;
116 30 : } else if (rt < 0) {
117 0 : dlt_vlog(LOG_ERR, "%s: snprintf error rt=%d\n", __func__, rt);
118 : const char* fmt_err = "fmt_err";
119 : memcpy(log_file_name, fmt_err, strlen(fmt_err) + 1);
120 : spos = strlen(fmt_err) + 1;
121 : } else {
122 : spos += (size_t)rt;
123 : }
124 : }
125 :
126 : /* Add time stamp if user has configured */
127 31 : if (file_config->logfile_timestamp) {
128 5 : char stamp[DLT_OFFLINE_LOGSTORAGE_TIMESTAMP_LEN + 1] = {0};
129 5 : time_t t = time(NULL);
130 : struct tm tm_info;
131 : ssize_t n = 0;
132 5 : tzset();
133 5 : localtime_r(&t, &tm_info);
134 5 : n = snprintf(
135 : stamp, DLT_OFFLINE_LOGSTORAGE_TIMESTAMP_LEN + 1, "%c%04d%02d%02d-%02d%02d%02d", delim,
136 5 : 1900 + tm_info.tm_year, 1 + tm_info.tm_mon, tm_info.tm_mday, tm_info.tm_hour, tm_info.tm_min,
137 : tm_info.tm_sec);
138 5 : if (n < 0 || (size_t)n > (DLT_OFFLINE_LOGSTORAGE_TIMESTAMP_LEN + 1)) {
139 0 : dlt_vlog(LOG_WARNING, "%s: snprintf truncation %s\n", __func__, stamp);
140 : }
141 5 : dlt_logstorage_concat_logfile_name(log_file_name, stamp);
142 : }
143 :
144 31 : dlt_logstorage_concat_logfile_name(log_file_name, ".dlt");
145 31 : if (filter_config->gzip_compression == DLT_LOGSTORAGE_GZIP_ON) {
146 0 : dlt_logstorage_concat_logfile_name(log_file_name, ".gz");
147 : }
148 : }
149 :
150 : /**
151 : * dlt_logstorage_sort_file_name
152 : *
153 : * Sort the filenames with index based ascending order (bubble sort)
154 : *
155 : * @param head Log filename list
156 : * @ return The last (biggest) index
157 : */
158 49 : unsigned int dlt_logstorage_sort_file_name(DltLogStorageFileList** head)
159 : {
160 : int done = 0;
161 : unsigned int max_idx = 0;
162 :
163 49 : if ((head == NULL) || (*head == NULL) || ((*head)->next == NULL))
164 : return 0;
165 :
166 27 : while (!done) {
167 : /* "source" of the pointer to the current node in the list struct */
168 : DltLogStorageFileList** pv = head;
169 15 : DltLogStorageFileList* nd = *head; /* local iterator pointer */
170 15 : DltLogStorageFileList* nx = (*head)->next; /* local next pointer */
171 :
172 : done = 1;
173 :
174 37 : while (nx) {
175 22 : max_idx = nx->idx;
176 22 : if (nd->idx > nx->idx) {
177 : max_idx = nd->idx;
178 4 : nd->next = nx->next;
179 4 : nx->next = nd;
180 4 : *pv = nx;
181 :
182 : done = 0;
183 : }
184 :
185 22 : pv = &nd->next;
186 : nd = nx;
187 22 : nx = nx->next;
188 : }
189 : }
190 :
191 : return max_idx;
192 : }
193 :
194 : /**
195 : * dlt_logstorage_rearrange_file_name
196 : *
197 : * Rearrange the filenames in the order of latest and oldest
198 : *
199 : * @param head Log filename list
200 : * @ return None
201 : */
202 3 : void dlt_logstorage_rearrange_file_name(DltLogStorageFileList** head)
203 : {
204 : DltLogStorageFileList* n_prev = NULL;
205 : DltLogStorageFileList* tail = NULL;
206 : DltLogStorageFileList* wrap_pre = NULL;
207 : DltLogStorageFileList* wrap_post = NULL;
208 : DltLogStorageFileList* n = NULL;
209 :
210 3 : if ((head == NULL) || (*head == NULL) || ((*head)->next == NULL))
211 : return;
212 :
213 2 : if ((*head)->idx != 1) {
214 : /* Do not sort */
215 : return;
216 : }
217 :
218 4 : for (n = *head; n != NULL; n = n->next) {
219 : /* Compare the diff between n->idx and n_prev->idx only if
220 : * wrap_post and wrap_pre are not set yet. Otherwise continue the loop
221 : * until the tail */
222 3 : if (n && n_prev && !wrap_post && !wrap_pre) {
223 1 : if ((n->idx - n_prev->idx) != 1) {
224 : wrap_post = n;
225 : wrap_pre = n_prev;
226 : }
227 : }
228 :
229 : n_prev = n;
230 : }
231 :
232 : tail = n_prev;
233 :
234 1 : if (wrap_post && wrap_pre) {
235 1 : wrap_pre->next = NULL;
236 1 : tail->next = *head;
237 1 : *head = wrap_post;
238 : }
239 : }
240 :
241 : /**
242 : * dlt_logstorage_get_idx_of_log_file
243 : *
244 : * Extract index of log file name passed as input argument
245 : *
246 : * @param file_config User configurations for log file
247 : * @param config Filter configurations for log file
248 : * @param file file name to extract the index from
249 : * @return index on success, -1 if no index is found
250 : */
251 58 : unsigned int dlt_logstorage_get_idx_of_log_file(
252 : DltLogStorageUserConfig* file_config, DltLogStorageFilterConfig* config, char* file)
253 : {
254 58 : if (file_config == NULL || config == NULL || file == NULL)
255 : return (unsigned int)-1;
256 :
257 : long idx = 0;
258 : size_t basename_len;
259 : char *sptr, *eptr;
260 :
261 : /* Find the next delimiter after the first one:
262 : * Eg. base-log-name_<idx>_<timestamp>.dlt
263 : * ^ ^
264 : * | |
265 : * From here --+ +--- To this position
266 : */
267 57 : basename_len = strlen(config->file_name);
268 57 : sptr = file + basename_len + 1;
269 57 : eptr = strchr(sptr, file_config->logfile_delimiter);
270 57 : idx = strtol(sptr, &eptr, 10);
271 :
272 57 : if (idx == 0)
273 0 : dlt_log(LOG_ERR, "Unable to calculate index from log file name. Reset to 001.\n");
274 :
275 57 : return (unsigned int)idx;
276 : }
277 :
278 : /**
279 : * dlt_logstorage_storage_dir_info
280 : *
281 : * Read file names of storage directory.
282 : * Update the file list, arrange it in order of latest and oldest
283 : *
284 : * @param file_config User configurations for log file
285 : * @param path Path to storage directory
286 : * @param config DltLogStorageFilterConfig
287 : * @return 0 on success, -1 on error
288 : */
289 48 : int dlt_logstorage_storage_dir_info(DltLogStorageUserConfig* file_config, char* path, DltLogStorageFilterConfig* config)
290 : {
291 : int check = 0;
292 : int i = 0;
293 : int cnt = 0;
294 : int ret = 0;
295 : unsigned int max_idx = 0;
296 48 : struct dirent** files = {0};
297 : unsigned int current_idx = 0;
298 : DltLogStorageFileList* n = NULL;
299 : DltLogStorageFileList* n1 = NULL;
300 48 : char storage_path[DLT_OFFLINE_LOGSTORAGE_MAX_PATH_LEN + 1] = {'\0'};
301 48 : char file_name[DLT_OFFLINE_LOGSTORAGE_MAX_FILE_NAME_LEN + 1] = {'\0'};
302 : char* dir = NULL;
303 :
304 48 : if ((config == NULL) || (file_config == NULL) || (path == NULL) || (config->file_name == NULL))
305 : return -1;
306 :
307 : strncpy(storage_path, path, DLT_OFFLINE_LOGSTORAGE_MAX_PATH_LEN);
308 :
309 47 : if (strstr(config->file_name, "/") != NULL) {
310 : /* Append directory path */
311 0 : char tmpdir[DLT_OFFLINE_LOGSTORAGE_MAX_FILE_NAME_LEN + 1] = {'\0'};
312 0 : char tmpfile[DLT_OFFLINE_LOGSTORAGE_MAX_FILE_NAME_LEN + 1] = {'\0'};
313 : char* file;
314 : strncpy(tmpdir, config->file_name, DLT_OFFLINE_LOGSTORAGE_MAX_FILE_NAME_LEN);
315 : strncpy(tmpfile, config->file_name, DLT_OFFLINE_LOGSTORAGE_MAX_FILE_NAME_LEN);
316 0 : dir = dirname(tmpdir);
317 0 : file = basename(tmpfile);
318 0 : if ((strlen(path) + strlen(dir)) > DLT_OFFLINE_LOGSTORAGE_MAX_PATH_LEN) {
319 0 : dlt_vlog(LOG_ERR, "%s: Directory name [%s] is too long to store (file name [%s])\n", __func__, dir, file);
320 0 : return -1;
321 : }
322 0 : strncat(storage_path, dir, DLT_OFFLINE_LOGSTORAGE_MAX_PATH_LEN - strlen(storage_path));
323 : strncpy(file_name, file, DLT_OFFLINE_LOGSTORAGE_MAX_FILE_NAME_LEN);
324 : } else {
325 : strncpy(file_name, config->file_name, DLT_OFFLINE_LOGSTORAGE_MAX_FILE_NAME_LEN);
326 : }
327 :
328 47 : cnt = scandir(storage_path, &files, 0, alphasort);
329 :
330 47 : if (cnt < 0) {
331 0 : dlt_vlog(LOG_ERR, "%s: Failed to scan directory [%s] for file name [%s]\n", __func__, storage_path, file_name);
332 0 : return -1;
333 : }
334 :
335 47 : dlt_vlog(LOG_DEBUG, "%s: Scanned [%d] files from %s\n", __func__, cnt, storage_path);
336 :
337 : /* In order to have a latest status of file list,
338 : * the existing records must be deleted before updating
339 : */
340 47 : n = config->records;
341 47 : if (config->records) {
342 66 : while (n) {
343 : n1 = n;
344 38 : n = n->next;
345 38 : free(n1->name);
346 : n1->name = NULL;
347 38 : free(n1);
348 : n1 = NULL;
349 : }
350 28 : config->records = NULL;
351 : }
352 :
353 47 : char* suffix = config->gzip_compression == DLT_LOGSTORAGE_GZIP_ON ? ".dlt.gz" : ".dlt";
354 :
355 1113 : for (i = 0; i < cnt; i++) {
356 1066 : size_t len = strlen(file_name);
357 :
358 1066 : dlt_vlog(
359 1066 : LOG_DEBUG, "%s: Scanned file name=[%s], filter file name=[%s]\n", __func__, files[i]->d_name, file_name);
360 1066 : if (strncmp(files[i]->d_name, file_name, len) == 0) {
361 70 : if (config->num_files == 1 && file_config->logfile_optional_counter) {
362 : /* <filename>.dlt or <filename>_<tmsp>.dlt */
363 0 : if ((files[i]->d_name[len] == suffix[0])
364 0 : || (file_config->logfile_timestamp && (files[i]->d_name[len] == file_config->logfile_delimiter))) {
365 : current_idx = 1;
366 : } else {
367 28 : continue;
368 : }
369 : } else {
370 : /* <filename>_idx.dlt or <filename>_idx_<tmsp>.dlt */
371 70 : if (files[i]->d_name[len] == file_config->logfile_delimiter) {
372 42 : current_idx = dlt_logstorage_get_idx_of_log_file(file_config, config, files[i]->d_name);
373 : } else {
374 28 : continue;
375 : }
376 : }
377 :
378 : DltLogStorageFileList** tmp = NULL;
379 :
380 42 : if (config->records == NULL) {
381 31 : config->records = malloc(sizeof(DltLogStorageFileList));
382 :
383 31 : if (config->records == NULL) {
384 : ret = -1;
385 0 : dlt_log(LOG_ERR, "Memory allocation failed\n");
386 0 : break;
387 : }
388 :
389 31 : tmp = &config->records;
390 : } else {
391 11 : tmp = &config->records;
392 :
393 22 : while (*(tmp) != NULL)
394 11 : tmp = &(*tmp)->next;
395 :
396 11 : *tmp = malloc(sizeof(DltLogStorageFileList));
397 :
398 11 : if (*tmp == NULL) {
399 : ret = -1;
400 0 : dlt_log(LOG_ERR, "Memory allocation failed\n");
401 0 : break;
402 : }
403 : }
404 :
405 42 : char tmpfile[DLT_OFFLINE_LOGSTORAGE_MAX_LOG_FILE_LEN + 1] = {'\0'};
406 : size_t needed = 0;
407 42 : if (dir != NULL) {
408 : /* Append directory path */
409 0 : needed = strlen(dir) + 1 + strlen(files[i]->d_name);
410 : } else {
411 42 : needed = strlen(files[i]->d_name);
412 : }
413 :
414 42 : if (needed >= sizeof(tmpfile)) {
415 0 : dlt_vlog(
416 : LOG_ERR, "%s: File path too long (dir=[%s], file=[%s]), skipping\n", __func__, dir ? dir : "",
417 0 : files[i]->d_name);
418 0 : free(*tmp);
419 0 : *tmp = NULL;
420 : ret = -1;
421 0 : break;
422 : }
423 :
424 42 : if (dir != NULL) {
425 0 : size_t pos = strlen(dir);
426 : memcpy(tmpfile, dir, pos);
427 0 : tmpfile[pos++] = '/';
428 0 : memcpy(tmpfile + pos, files[i]->d_name, strlen(files[i]->d_name) + 1);
429 : } else {
430 42 : memcpy(tmpfile, files[i]->d_name, strlen(files[i]->d_name) + 1);
431 : }
432 42 : (*tmp)->name = strdup(tmpfile);
433 42 : (*tmp)->idx = current_idx;
434 42 : (*tmp)->next = NULL;
435 42 : check++;
436 : }
437 : }
438 :
439 47 : dlt_vlog(LOG_DEBUG, "%s: After dir scan: [%d] files of [%s]\n", __func__, check, file_name);
440 :
441 47 : if (ret == 0) {
442 47 : max_idx = dlt_logstorage_sort_file_name(&config->records);
443 :
444 : /* Fault tolerance:
445 : * In case there are some log files are removed but
446 : * the index is still not reaching maxcounter, no need
447 : * to perform rearrangement of filename.
448 : * This would help the log keeps growing until maxcounter is reached and
449 : * the maximum number of log files could be obtained.
450 : */
451 47 : if (max_idx == file_config->logfile_maxcounter)
452 0 : dlt_logstorage_rearrange_file_name(&config->records);
453 : }
454 :
455 : /* free scandir result */
456 1113 : for (i = 0; i < cnt; i++)
457 1066 : free(files[i]);
458 :
459 47 : free(files);
460 :
461 47 : return ret;
462 : }
463 :
464 : /**
465 : * dlt_logstorage_open_log_output_file
466 : *
467 : * Open a handle to the logfile
468 : *
469 : * @param config A pointer to the current DltLogStorageFilterConfig
470 : * @param fpath The file path
471 : * @param mode The mode to open the file with
472 : */
473 33 : DLT_STATIC void dlt_logstorage_open_log_output_file(
474 : DltLogStorageFilterConfig* config, const char* fpath, const char* mode)
475 : {
476 33 : FILE* file = fopen(fpath, mode);
477 33 : if (file == NULL) {
478 0 : dlt_vlog(LOG_DEBUG, "%s: could not open configuration file\n", __func__);
479 0 : return;
480 : }
481 33 : config->fd = fileno(file);
482 33 : if (config->gzip_compression == DLT_LOGSTORAGE_GZIP_ON) {
483 : #ifdef DLT_LOGSTORAGE_USE_GZIP
484 : dlt_vlog(LOG_DEBUG, "%s: Opening GZIP log file\n", __func__);
485 : config->gzlog = gzdopen(config->fd, mode);
486 : #endif
487 : } else {
488 33 : dlt_vlog(LOG_DEBUG, "%s: Opening log file\n", __func__);
489 33 : config->log = file;
490 : }
491 : }
492 :
493 : /**
494 : * dlt_logstorage_open_log_file
495 : *
496 : * Open a log file. Check storage directory for already created files and open
497 : * the oldest if there is enough space to store at least msg_size.
498 : * Otherwise create a new file, but take configured max number of files into
499 : * account and remove the oldest file if needed.
500 : *
501 : * @param config DltLogStorageFilterConfig
502 : * @param file_config User configurations for log file
503 : * @param dev_path Storage device path
504 : * @param msg_size Size of incoming message
505 : * @param is_update_required The file list needs to be updated
506 : * @return 0 on succes, -1 on error
507 : */
508 47 : int dlt_logstorage_open_log_file(
509 : DltLogStorageFilterConfig* config, DltLogStorageUserConfig* file_config, char* dev_path, int msg_size,
510 : bool is_update_required, bool is_sync)
511 : {
512 : int ret = 0;
513 47 : char absolute_file_path[DLT_OFFLINE_LOGSTORAGE_MAX_PATH_LEN + 1] = {'\0'};
514 47 : char storage_path[DLT_MOUNT_PATH_MAX + 1] = {'\0'};
515 47 : char file_name[DLT_OFFLINE_LOGSTORAGE_MAX_LOG_FILE_LEN + 1] = {'\0'};
516 : unsigned int num_log_files = 0;
517 : struct stat s;
518 : memset(&s, 0, sizeof(struct stat));
519 : DltLogStorageFileList** tmp = NULL;
520 : DltLogStorageFileList** newest = NULL;
521 :
522 47 : if (config == NULL)
523 : return -1;
524 :
525 46 : if (strlen(dev_path) > DLT_MOUNT_PATH_MAX) {
526 0 : dlt_vlog(LOG_ERR, "device path '%s' is too long to store\n", dev_path);
527 0 : return -1;
528 : }
529 :
530 46 : size_t dev_path_len = strnlen(dev_path, DLT_MOUNT_PATH_MAX - 2);
531 46 : if (dev_path_len + 2 > DLT_MOUNT_PATH_MAX) {
532 0 : dlt_vlog(LOG_ERR, "device path too long for storage_path buffer\n");
533 0 : return -1;
534 : }
535 : int written = snprintf(storage_path, DLT_MOUNT_PATH_MAX, "%s/", dev_path);
536 46 : if (written < 0 || (size_t)written >= DLT_MOUNT_PATH_MAX) {
537 0 : dlt_vlog(LOG_ERR, "snprintf truncated storage_path\n");
538 0 : return -1;
539 : }
540 :
541 : /* check if there are already files stored */
542 46 : if (config->records == NULL || is_update_required) {
543 46 : if (dlt_logstorage_storage_dir_info(file_config, storage_path, config) != 0)
544 : return -1;
545 : }
546 :
547 : /* obtain locations of newest, current file names, file count */
548 46 : tmp = &config->records;
549 :
550 88 : while (*(tmp) != NULL) {
551 42 : num_log_files += 1;
552 :
553 42 : if ((*tmp)->next == NULL)
554 : newest = tmp;
555 :
556 42 : tmp = &(*tmp)->next;
557 : }
558 :
559 : /* need new file*/
560 46 : if (num_log_files == 0) {
561 15 : dlt_logstorage_log_file_name(file_name, file_config, config, config->file_name, (int)config->num_files, 1);
562 :
563 : /* concatenate path and file and open absolute path */
564 : strcat(absolute_file_path, storage_path);
565 : strcat(absolute_file_path, file_name);
566 15 : config->working_file_name = strdup(file_name);
567 15 : dlt_logstorage_open_log_output_file(config, absolute_file_path, "a");
568 :
569 : /* Add file to file list */
570 15 : *tmp = malloc(sizeof(DltLogStorageFileList));
571 :
572 15 : if (*tmp == NULL) {
573 0 : dlt_log(LOG_ERR, "Memory allocation for file name failed\n");
574 0 : return -1;
575 : }
576 :
577 15 : (*tmp)->name = strdup(file_name);
578 15 : (*tmp)->idx = 1;
579 15 : (*tmp)->next = NULL;
580 : } else {
581 : strcat(absolute_file_path, storage_path);
582 :
583 : /* newest file available
584 : * Since the working file is already updated from newest file info
585 : * So if there is already wrap-up, the newest file will be the working file
586 : */
587 31 : if ((config->wrap_id == 0) || (config->working_file_name == NULL)) {
588 29 : if (config->working_file_name != NULL) {
589 28 : free(config->working_file_name);
590 28 : config->working_file_name = NULL;
591 : }
592 29 : config->working_file_name = strdup((*newest)->name);
593 : }
594 31 : size_t abs_len = strnlen(absolute_file_path, sizeof(absolute_file_path));
595 31 : size_t name_len = strnlen(config->working_file_name, sizeof(absolute_file_path) - abs_len - 1);
596 31 : if (abs_len + name_len >= sizeof(absolute_file_path)) {
597 0 : dlt_vlog(LOG_ERR, "absolute_file_path too small for working_file_name\n");
598 0 : return -1;
599 : }
600 31 : snprintf(absolute_file_path + abs_len, sizeof(absolute_file_path) - abs_len, "%s", config->working_file_name);
601 :
602 31 : dlt_vlog(
603 : LOG_DEBUG, "%s: Number of log files-newest file-wrap_id [%u]-[%s]-[%u]\n", __func__, num_log_files,
604 : config->working_file_name, config->wrap_id);
605 :
606 31 : ret = stat(absolute_file_path, &s);
607 :
608 : /* if file stats is read and, either
609 : * is_sync is true and (other than ON_MSG sync behavior and current size is less than configured size) or
610 : * msg_size fit into the size (ON_MSG or par of cache needs to be written into new file), open it */
611 31 : if ((ret == 0)
612 31 : && ((is_sync && (s.st_size < (long)config->file_size))
613 13 : || (!is_sync && (s.st_size + msg_size <= (long)config->file_size)))) {
614 18 : dlt_logstorage_open_log_output_file(config, absolute_file_path, "a");
615 18 : config->current_write_file_offset = (unsigned int)s.st_size;
616 : } else {
617 : /* no space in file or file stats cannot be read */
618 : unsigned int idx = 0;
619 :
620 : /* get index of newest log file */
621 13 : if (config->num_files == 1 && file_config->logfile_optional_counter) {
622 : idx = 1;
623 : } else {
624 13 : idx = dlt_logstorage_get_idx_of_log_file(file_config, config, config->working_file_name);
625 : }
626 :
627 : /* Check if file logging shall be stopped */
628 13 : if (config->overwrite == DLT_LOGSTORAGE_OVERWRITE_DISCARD_NEW) {
629 5 : dlt_vlog(
630 : LOG_DEBUG, "%s: num_files=%d, current_idx=%d (filename=%s)\n", __func__, config->num_files, idx,
631 : config->file_name);
632 :
633 5 : if (config->num_files == idx) {
634 2 : dlt_vlog(
635 : LOG_INFO, "%s: logstorage limit reached, stopping capture for filter: %s\n", __func__,
636 : config->file_name);
637 2 : config->skip = 1;
638 2 : return 0;
639 : }
640 : }
641 :
642 11 : idx += 1;
643 :
644 : /* wrap around if max index is reached or an error occurred
645 : * while calculating index from file name */
646 11 : if ((idx > file_config->logfile_maxcounter) || (idx == 0)) {
647 : idx = 1;
648 2 : config->wrap_id += 1;
649 : }
650 :
651 11 : dlt_logstorage_log_file_name(
652 11 : file_name, file_config, config, config->file_name, (int)config->num_files, (int)idx);
653 :
654 : /* concatenate path and file and open absolute path */
655 : memset(absolute_file_path, 0, sizeof(absolute_file_path) / sizeof(char));
656 : strcat(absolute_file_path, storage_path);
657 : strcat(absolute_file_path, file_name);
658 :
659 11 : if (config->working_file_name) {
660 11 : free(config->working_file_name);
661 11 : config->working_file_name = strdup(file_name);
662 : }
663 :
664 : /* If there is already wrap-up, check the existence of file
665 : * remove it and reopen it.
666 : * In this case number of log file won't be increased*/
667 11 : if (config->wrap_id && stat(absolute_file_path, &s) == 0) {
668 1 : remove(absolute_file_path);
669 1 : num_log_files -= 1;
670 1 : dlt_vlog(
671 : LOG_DEBUG, "%s: Remove '%s' (num_log_files: %u, config->num_files:%u)\n", __func__,
672 : absolute_file_path, num_log_files, config->num_files);
673 : }
674 :
675 11 : config->log = fopen(absolute_file_path, "w+");
676 :
677 11 : dlt_vlog(LOG_DEBUG, "%s: Filename and Index after updating [%s]-[%u]\n", __func__, file_name, idx);
678 :
679 : /* Add file to file list */
680 11 : *tmp = malloc(sizeof(DltLogStorageFileList));
681 :
682 11 : if (*tmp == NULL) {
683 0 : dlt_log(LOG_ERR, "Memory allocation for file name failed\n");
684 0 : return -1;
685 : }
686 :
687 11 : (*tmp)->name = strdup(file_name);
688 11 : (*tmp)->idx = idx;
689 11 : (*tmp)->next = NULL;
690 :
691 11 : num_log_files += 1;
692 :
693 : /* check if number of log files exceeds configured max value */
694 11 : if (num_log_files > config->num_files) {
695 5 : if (!(config->num_files == 1 && file_config->logfile_optional_counter)) {
696 : /* delete oldest */
697 : DltLogStorageFileList** head = &config->records;
698 5 : DltLogStorageFileList* n = *head;
699 : memset(absolute_file_path, 0, sizeof(absolute_file_path) / sizeof(char));
700 : strcat(absolute_file_path, storage_path);
701 5 : size_t abs_len2 = strnlen(absolute_file_path, sizeof(absolute_file_path));
702 5 : size_t head_name_len = strnlen((*head)->name, sizeof(absolute_file_path) - abs_len2 - 1);
703 5 : if (abs_len2 + head_name_len >= sizeof(absolute_file_path)) {
704 0 : dlt_vlog(LOG_ERR, "absolute_file_path too small for head->name\n");
705 0 : return -1;
706 : }
707 5 : snprintf(absolute_file_path + abs_len2, sizeof(absolute_file_path) - abs_len2, "%s", (*head)->name);
708 5 : dlt_vlog(
709 : LOG_DEBUG, "%s: Remove '%s' (num_log_files: %d, config->num_files:%d, file_name:%s)\n",
710 : __func__, absolute_file_path, num_log_files, config->num_files, config->file_name);
711 5 : if (remove(absolute_file_path) != 0)
712 0 : dlt_log(LOG_ERR, "Could not remove file\n");
713 :
714 5 : free((*head)->name);
715 5 : (*head)->name = NULL;
716 5 : *head = n->next;
717 : n->next = NULL;
718 5 : free(n);
719 : }
720 : }
721 : }
722 : }
723 :
724 : #ifdef DLT_LOGSTORAGE_USE_GZIP
725 : if (config->gzlog == NULL && config->log == NULL) {
726 : #else
727 44 : if (config->log == NULL) {
728 : #endif
729 0 : if (*tmp != NULL) {
730 0 : if ((*tmp)->name != NULL) {
731 0 : free((*tmp)->name);
732 0 : (*tmp)->name = NULL;
733 : }
734 0 : free(*tmp);
735 0 : *tmp = NULL;
736 : }
737 :
738 0 : if (config->working_file_name != NULL) {
739 0 : free(config->working_file_name);
740 0 : config->working_file_name = NULL;
741 : }
742 :
743 0 : dlt_vlog(LOG_ERR, "%s: Unable to open log file.\n", __func__);
744 0 : return -1;
745 : }
746 :
747 : return ret;
748 : }
749 :
750 : /**
751 : * dlt_logstorage_find_dlt_header
752 : *
753 : * search for dlt header in cache
754 : *
755 : * @param ptr cache starting position
756 : * @param offset offset
757 : * @param cnt count
758 : * @return index on success, -1 on error
759 : */
760 36 : DLT_STATIC int dlt_logstorage_find_dlt_header(void* ptr, unsigned int offset, unsigned int cnt)
761 : {
762 36 : const char magic[] = {'D', 'L', 'T', 0x01};
763 36 : const char* cache = (char*)ptr + offset;
764 :
765 : unsigned int i;
766 50 : for (i = 0; i < cnt; i++) {
767 48 : if ((cache[i] == 'D') && (strncmp(&cache[i], magic, 4) == 0))
768 34 : return (int)i;
769 : }
770 :
771 : return -1;
772 : }
773 :
774 : /**
775 : * dlt_logstorage_find_last_dlt_header
776 : *
777 : * search for last dlt header in cache
778 : *
779 : * @param ptr cache starting position
780 : * @param offset offset
781 : * @param cnt count
782 : * @return index on success, -1 on error
783 : */
784 8 : DLT_STATIC int dlt_logstorage_find_last_dlt_header(void* ptr, unsigned int offset, unsigned int cnt)
785 : {
786 8 : const char magic[] = {'D', 'L', 'T', 0x01};
787 8 : const char* cache = (char*)ptr + offset;
788 :
789 : int i;
790 452 : for (i = (int)cnt - (DLT_ID_SIZE - 1); i > 0; i--) {
791 448 : if ((cache[i] == 'D') && (strncmp(&cache[i], magic, 4) == 0))
792 4 : return i;
793 : }
794 :
795 : return -1;
796 : }
797 :
798 : /**
799 : * dlt_logstorage_write_to_log
800 : *
801 : * Write logdata to log storage file
802 : *
803 : * @param ptr A pointer to the data to write
804 : * @param size The size of the data blocks
805 : * @param nmemb The number of blocks to write
806 : * @param config A pointer to DltLogStorageFilterConfig
807 : */
808 5 : DLT_STATIC int dlt_logstorage_write_to_log(void* ptr, size_t size, size_t nmemb, DltLogStorageFilterConfig* config)
809 : {
810 : #ifdef DLT_LOGSTORAGE_USE_GZIP
811 : if (config->gzip_compression == DLT_LOGSTORAGE_GZIP_ON) {
812 : return (int)gzfwrite(ptr, size, nmemb, config->gzlog);
813 : } else {
814 : return (int)fwrite(ptr, size, nmemb, config->log);
815 : }
816 : #else
817 5 : return (int)fwrite(ptr, size, nmemb, config->log);
818 : #endif
819 : }
820 :
821 : /**
822 : * dlt_logstorage_check_write_ret
823 : *
824 : * check the return value of fwrite/gzfwrite
825 : *
826 : * @param config DltLogStorageFilterConfig
827 : * @param ret return value of fwrite/gzfwrite call
828 : */
829 29 : DLT_STATIC void dlt_logstorage_check_write_ret(DltLogStorageFilterConfig* config, int ret)
830 : {
831 29 : if (config == NULL) {
832 0 : dlt_vlog(LOG_ERR, "%s: cannot retrieve config information\n", __func__);
833 0 : return;
834 : }
835 :
836 29 : if (ret <= 0) {
837 0 : if (config->gzip_compression == DLT_LOGSTORAGE_GZIP_ON) {
838 : #ifdef DLT_LOGSTORAGE_USE_GZIP
839 : const char* msg = gzerror(config->gzlog, &ret);
840 : if (msg != NULL) {
841 : dlt_vlog(LOG_ERR, "%s: failed to write cache into log file: %s\n", __func__, msg);
842 : }
843 : #endif
844 : } else {
845 0 : if (ferror(config->log) != 0)
846 0 : dlt_vlog(LOG_ERR, "%s: failed to write cache into log file\n", __func__);
847 : }
848 : } else {
849 : /* force sync */
850 29 : if (config->gzip_compression == DLT_LOGSTORAGE_GZIP_ON) {
851 : #ifdef DLT_LOGSTORAGE_USE_GZIP
852 : if (gzflush(config->gzlog, Z_SYNC_FLUSH) != 0)
853 : dlt_vlog(LOG_ERR, "%s: failed to gzflush log file\n", __func__);
854 : #endif
855 : } else {
856 29 : if (fflush(config->log) != 0)
857 0 : dlt_vlog(LOG_ERR, "%s: failed to flush log file\n", __func__);
858 : }
859 :
860 29 : if (fsync(config->fd) != 0) {
861 : /* some filesystem doesn't support fsync() */
862 0 : if (errno != ENOSYS) {
863 0 : dlt_vlog(LOG_ERR, "%s: failed to sync log file\n", __func__);
864 : }
865 : }
866 : }
867 : }
868 :
869 : /**
870 : * dlt_logstorage_close_file
871 : *
872 : * Close open file handles if any exist in the provided
873 : * DltLogStorageFilterConfig
874 : *
875 : * @param config The DltLogStorageFilterConfig to operate on
876 : */
877 4 : DLT_STATIC void dlt_logstorage_close_file(DltLogStorageFilterConfig* config)
878 : {
879 : #ifdef DLT_LOGSTORAGE_USE_GZIP
880 : if (config->gzlog) {
881 : gzclose(config->gzlog);
882 : config->gzlog = NULL;
883 : }
884 : #endif
885 19 : if (config->log) {
886 37 : fclose(config->log);
887 37 : config->log = NULL;
888 : }
889 4 : }
890 :
891 : /**
892 : * dlt_logstorage_sync_to_file
893 : *
894 : * Write the log message to log file
895 : *
896 : * @param config DltLogStorageFilterConfig
897 : * @param file_config DltLogStorageUserConfig
898 : * @param dev_path Storage device mount point path
899 : * @param footer DltLogStorageCacheFooter
900 : * @param start_offset Start offset of the cache
901 : * @param end_offset End offset of the cache
902 : * @return 0 on success, -1 on error
903 : */
904 29 : DLT_STATIC int dlt_logstorage_sync_to_file(
905 : DltLogStorageFilterConfig* config, DltLogStorageUserConfig* file_config, char* dev_path,
906 : DltLogStorageCacheFooter* footer, unsigned int start_offset, unsigned int end_offset)
907 : {
908 : int ret = 0;
909 : int start_index = 0;
910 : int end_index = 0;
911 : int count = 0;
912 : unsigned int remain_file_size = 0;
913 :
914 29 : if ((config == NULL) || (file_config == NULL) || (dev_path == NULL) || (footer == NULL)) {
915 1 : dlt_vlog(LOG_ERR, "%s: cannot retrieve config information\n", __func__);
916 1 : return -1;
917 : }
918 :
919 28 : count = (int)(end_offset - start_offset);
920 :
921 : /* In case of cached-based strategy, the newest file information
922 : * must be updated everytime of synchronization.
923 : */
924 2 : dlt_logstorage_close_file(config);
925 28 : config->current_write_file_offset = 0;
926 :
927 28 : if (dlt_logstorage_open_log_file(config, file_config, dev_path, count, true, true) != 0) {
928 0 : dlt_vlog(LOG_ERR, "%s: failed to open log file\n", __func__);
929 0 : return -1;
930 : }
931 :
932 28 : if (config->skip == 1) {
933 0 : dlt_logstorage_close_file(config);
934 0 : return 0;
935 : }
936 :
937 28 : remain_file_size = (unsigned int)(config->file_size - config->current_write_file_offset);
938 :
939 28 : if ((unsigned int)count > remain_file_size) {
940 : /* Check if more than one message can fit into the remaining file */
941 6 : start_index = dlt_logstorage_find_dlt_header(config->cache, start_offset, remain_file_size);
942 6 : end_index = dlt_logstorage_find_last_dlt_header(
943 : config->cache, (unsigned int)start_offset + (unsigned int)start_index,
944 6 : remain_file_size - (unsigned int)start_index);
945 6 : count = (int)(end_index - start_index);
946 :
947 6 : if ((start_index >= 0) && (end_index > start_index) && (count > 0)
948 3 : && ((unsigned int)count <= remain_file_size)) {
949 3 : ret = dlt_logstorage_write_to_log(
950 3 : (uint8_t*)config->cache + start_offset + start_index, (size_t)count, 1, config);
951 3 : dlt_logstorage_check_write_ret(config, ret);
952 :
953 : /* Close log file */
954 0 : dlt_logstorage_close_file(config);
955 3 : config->current_write_file_offset = 0;
956 :
957 3 : footer->last_sync_offset = (unsigned int)start_offset + (unsigned int)count;
958 3 : start_offset = footer->last_sync_offset;
959 : } else {
960 : /* Close log file */
961 0 : dlt_logstorage_close_file(config);
962 3 : config->current_write_file_offset = 0;
963 : }
964 : }
965 :
966 28 : start_index = dlt_logstorage_find_dlt_header(config->cache, start_offset, (unsigned int)count);
967 28 : count = (int)(end_offset - start_offset - (unsigned int)start_index);
968 :
969 28 : if ((start_index >= 0) && (count > 0)) {
970 : /* Prepare log file */
971 : #ifdef DLT_LOGSTORAGE_USE_GZIP
972 : if (config->log == NULL && config->gzlog == NULL)
973 : #else
974 27 : if (config->log == NULL)
975 : #endif
976 : {
977 6 : if (dlt_logstorage_open_log_file(config, file_config, dev_path, count, true, false) != 0) {
978 0 : dlt_vlog(LOG_ERR, "%s: failed to open log file\n", __func__);
979 0 : dlt_logstorage_close_file(config);
980 0 : return -1;
981 : }
982 :
983 6 : if (config->skip == 1) {
984 0 : dlt_logstorage_close_file(config);
985 1 : return 0;
986 : }
987 : }
988 :
989 : ret =
990 26 : dlt_logstorage_write_to_log((uint8_t*)config->cache + start_offset + start_index, (size_t)count, 1, config);
991 26 : dlt_logstorage_check_write_ret(config, ret);
992 :
993 26 : config->current_write_file_offset += (unsigned int)count;
994 26 : footer->last_sync_offset = (unsigned int)end_offset;
995 : }
996 :
997 27 : footer->wrap_around_cnt = 0;
998 2 : dlt_logstorage_close_file(config);
999 2 : return 0;
1000 : }
1001 :
1002 : /**
1003 : * dlt_logstorage_prepare_on_msg
1004 : *
1005 : * Prepare the log file for a certain filer. If log file not open or log
1006 : * files max size reached, open a new file.
1007 : *
1008 : * @param config DltLogStorageFilterConfig
1009 : * @param file_config User configurations for log file
1010 : * @param dev_path Storage device path
1011 : * @param log_msg_size Size of log message
1012 : * @param newest_file_info Info of newest file for corresponding filename
1013 : * @return 0 on success, -1 on error
1014 : */
1015 56 : int dlt_logstorage_prepare_on_msg(
1016 : DltLogStorageFilterConfig* config, DltLogStorageUserConfig* file_config, char* dev_path, int log_msg_size,
1017 : DltNewestFileName* newest_file_info)
1018 : {
1019 : int ret = 0;
1020 : struct stat s;
1021 :
1022 56 : if ((config == NULL) || (file_config == NULL) || (dev_path == NULL) || (newest_file_info == NULL)) {
1023 1 : dlt_vlog(LOG_INFO, "%s: Wrong paratemters\n", __func__);
1024 1 : return -1;
1025 : }
1026 :
1027 : /* This is for ON_MSG/UNSET strategy */
1028 : /* Handle log file open for ON_MSG/UNSET strategy */
1029 : #ifdef DLT_LOGSTORAGE_USE_GZIP
1030 : if (config->log == NULL && config->gzlog == NULL) {
1031 : #else
1032 55 : if (config->log == NULL) {
1033 : #endif
1034 : /* Sync the wrap id and working file name before opening log file */
1035 6 : if (config->wrap_id < newest_file_info->wrap_id) {
1036 2 : config->wrap_id = newest_file_info->wrap_id;
1037 2 : if (config->working_file_name) {
1038 1 : free(config->working_file_name);
1039 1 : config->working_file_name = NULL;
1040 : }
1041 2 : config->working_file_name = strdup(newest_file_info->newest_file);
1042 : }
1043 :
1044 : /* open a new log file */
1045 6 : ret = dlt_logstorage_open_log_file(config, file_config, dev_path, log_msg_size, true, false);
1046 : } else { /* already open, check size and create a new file if needed */
1047 49 : ret = fstat(config->fd, &s);
1048 :
1049 49 : if (ret == 0) {
1050 : /* Check if adding new data do not exceed max file size
1051 : *
1052 : * This is inaccurate for gz compressed files but as long as log
1053 : * messages aren't gigantic it should be negligeble
1054 : *
1055 : * Also check if wrap id needs to be updated */
1056 44 : if ((s.st_size + log_msg_size > (int)config->file_size)
1057 40 : || (strcmp(config->working_file_name, newest_file_info->newest_file) != 0)
1058 40 : || (config->wrap_id < newest_file_info->wrap_id)) {
1059 : /* Sync only if on_msg */
1060 4 : if ((config->sync == DLT_LOGSTORAGE_SYNC_ON_MSG) || (config->sync == DLT_LOGSTORAGE_SYNC_UNSET)) {
1061 : #ifdef DLT_LOGSTORAGE_USE_GZIP
1062 : if (config->gzip_compression == DLT_LOGSTORAGE_GZIP_ON) {
1063 : if (fsync(config->fd) != 0) {
1064 : if (errno != ENOSYS) {
1065 : dlt_vlog(LOG_ERR, "%s: failed to sync gzip log file\n", __func__);
1066 : }
1067 : }
1068 : } else
1069 : #endif
1070 : {
1071 4 : if (fsync(fileno(config->log)) != 0) {
1072 0 : if (errno != ENOSYS) {
1073 0 : dlt_vlog(LOG_ERR, "%s: failed to sync log file\n", __func__);
1074 : }
1075 : }
1076 : }
1077 : }
1078 :
1079 0 : dlt_logstorage_close_file(config);
1080 :
1081 : /* Sync the wrap id and working file name before opening log file */
1082 4 : if (config->wrap_id <= newest_file_info->wrap_id) {
1083 4 : config->wrap_id = newest_file_info->wrap_id;
1084 4 : if (config->working_file_name) {
1085 4 : free(config->working_file_name);
1086 4 : config->working_file_name = NULL;
1087 : }
1088 4 : config->working_file_name = strdup(newest_file_info->newest_file);
1089 : }
1090 :
1091 4 : ret = dlt_logstorage_open_log_file(config, file_config, dev_path, log_msg_size, true, false);
1092 : } else { /*everything is prepared */
1093 : ret = 0;
1094 : }
1095 : } else {
1096 5 : dlt_vlog(LOG_ERR, "%s: stat() failed.\n", __func__);
1097 : ret = -1;
1098 : }
1099 : }
1100 :
1101 : return ret;
1102 : }
1103 :
1104 : /**
1105 : * dlt_logstorage_write_on_msg
1106 : *
1107 : * Write the log message.
1108 : *
1109 : * @param config DltLogStorageFilterConfig
1110 : * @param file_config DltLogStorageUserConfig
1111 : * @param dev_path Path to device
1112 : * @param data1 header
1113 : * @param size1 header size
1114 : * @param data2 storage header
1115 : * @param size2 storage header size
1116 : * @param data3 payload
1117 : * @param size3 payload size
1118 : * @return 0 on success, -1 on error
1119 : */
1120 47 : int dlt_logstorage_write_on_msg(
1121 : DltLogStorageFilterConfig* config, DltLogStorageUserConfig* file_config, char* dev_path, unsigned char* data1,
1122 : int size1, unsigned char* data2, int size2, unsigned char* data3, int size3)
1123 : {
1124 : int ret;
1125 :
1126 47 : if ((config == NULL) || (data1 == NULL) || (data2 == NULL) || (data3 == NULL) || (file_config == NULL)
1127 46 : || (dev_path == NULL)) {
1128 : return -1;
1129 : }
1130 :
1131 46 : ret = dlt_logstorage_write_to_log(data1, 1, (size_t)size1, config);
1132 :
1133 46 : if (ret != size1)
1134 0 : dlt_log(LOG_WARNING, "Wrote less data than specified\n");
1135 :
1136 46 : ret = dlt_logstorage_write_to_log(data2, 1, (size_t)size2, config);
1137 46 : if (ret != size2)
1138 0 : dlt_log(LOG_WARNING, "Wrote less data than specified\n");
1139 :
1140 46 : ret = dlt_logstorage_write_to_log(data3, 1, (size_t)size3, config);
1141 46 : if (ret != size3)
1142 0 : dlt_log(LOG_WARNING, "Wrote less data than specified\n");
1143 :
1144 : #ifdef DLT_LOGSTORAGE_USE_GZIP
1145 : if (config->gzip_compression == DLT_LOGSTORAGE_GZIP_ON) {
1146 : gzerror(config->gzlog, &ret);
1147 : return ret;
1148 : } else {
1149 : return ferror(config->log);
1150 : }
1151 : #else
1152 46 : return ferror(config->log);
1153 : #endif
1154 : }
1155 :
1156 : /**
1157 : * dlt_logstorage_sync_on_msg
1158 : *
1159 : * sync data to disk.
1160 : *
1161 : * @param config DltLogStorageFilterConfig
1162 : * @param file_config User configurations for log file
1163 : * @param dev_path Storage device path
1164 : * @param status Strategy flag
1165 : * @return 0 on success, -1 on error
1166 : */
1167 103 : int dlt_logstorage_sync_on_msg(
1168 : DltLogStorageFilterConfig* config, DltLogStorageUserConfig* file_config, char* dev_path, int status)
1169 : {
1170 : (void)file_config; /* satisfy compiler */
1171 : (void)dev_path;
1172 :
1173 103 : if (config == NULL)
1174 : return -1;
1175 :
1176 102 : if (status == DLT_LOGSTORAGE_SYNC_ON_MSG) { /* sync on every message */
1177 46 : if (config->gzip_compression == DLT_LOGSTORAGE_GZIP_ON) {
1178 : #ifdef DLT_LOGSTORAGE_USE_GZIP
1179 : if (gzflush(config->gzlog, Z_SYNC_FLUSH) != 0)
1180 : dlt_vlog(LOG_ERR, "%s: failed to gzflush log file\n", __func__);
1181 : #endif
1182 : } else {
1183 46 : if (fflush(config->log) != 0)
1184 0 : dlt_vlog(LOG_ERR, "%s: failed to flush log file\n", __func__);
1185 : }
1186 : }
1187 :
1188 : return 0;
1189 : }
1190 :
1191 : /**
1192 : * dlt_logstorage_prepare_msg_cache
1193 : *
1194 : * Prepare the log file for a certain filer. If log file not open or log
1195 : * files max size reached, open a new file.
1196 : * Create a memory area to cache data.
1197 : *
1198 : * @param config DltLogStorageFilterConfig
1199 : * @param file_config User configurations for log file
1200 : * @param dev_path Storage device path
1201 : * @param log_msg_size Size of log message
1202 : * @param newest_file_info Info of newest files for corresponding filename
1203 : * @return 0 on success, -1 on error
1204 : */
1205 199 : int dlt_logstorage_prepare_msg_cache(
1206 : DltLogStorageFilterConfig* config, DltLogStorageUserConfig* file_config, char* dev_path, int log_msg_size,
1207 : DltNewestFileName* newest_file_info)
1208 : {
1209 199 : if ((config == NULL) || (file_config == NULL) || (dev_path == NULL) || (newest_file_info == NULL))
1210 : return -1;
1211 :
1212 : /* check if newest file info is available
1213 : * + working file name is NULL => update directly to newest file
1214 : * + working file name is not NULL: check if
1215 : * ++ wrap_ids are different from each other or
1216 : * ++ newest file name <> working file name
1217 : */
1218 198 : if (newest_file_info->newest_file) {
1219 60 : if (config->working_file_name
1220 59 : && ((config->wrap_id != newest_file_info->wrap_id)
1221 59 : || (strcmp(newest_file_info->newest_file, config->working_file_name) != 0))) {
1222 0 : free(config->working_file_name);
1223 0 : config->working_file_name = NULL;
1224 : }
1225 60 : if (config->working_file_name == NULL) {
1226 1 : config->working_file_name = strdup(newest_file_info->newest_file);
1227 1 : config->wrap_id = newest_file_info->wrap_id;
1228 : }
1229 : }
1230 :
1231 : /* Combinations allowed: on Daemon_Exit with on Demand,File_Size with Daemon_Exit
1232 : * File_Size with on Demand, Specific_Size with Daemon_Exit,Specific_Size with on Demand
1233 : * Combination not allowed : File_Size with Specific_Size
1234 : */
1235 : /* check for combinations of specific_size and file_size strategy */
1236 198 : if ((DLT_OFFLINE_LOGSTORAGE_IS_STRATEGY_SET(config->sync, DLT_LOGSTORAGE_SYNC_ON_SPECIFIC_SIZE) > 0)
1237 43 : && ((DLT_OFFLINE_LOGSTORAGE_IS_STRATEGY_SET(config->sync, DLT_LOGSTORAGE_SYNC_ON_FILE_SIZE)) > 0)) {
1238 0 : dlt_log(LOG_WARNING, "wrong combination of sync strategies \n");
1239 0 : return -1;
1240 : }
1241 :
1242 : (void)log_msg_size; /* satisfy compiler */
1243 :
1244 : /* check specific size is smaller than file size */
1245 198 : if ((DLT_OFFLINE_LOGSTORAGE_IS_STRATEGY_SET(config->sync, DLT_LOGSTORAGE_SYNC_ON_SPECIFIC_SIZE) > 0)
1246 43 : && (config->specific_size > config->file_size)) {
1247 0 : dlt_log(
1248 : LOG_ERR,
1249 : "Cache size is larger than file size. "
1250 : "Cannot prepare log file for ON_SPECIFIC_SIZE sync\n");
1251 0 : return -1;
1252 : }
1253 :
1254 198 : if (config->cache == NULL) {
1255 : unsigned int cache_size = 0;
1256 : size_t total_size = 0;
1257 :
1258 : /* check for sync_specific_size strategy */
1259 11 : if (DLT_OFFLINE_LOGSTORAGE_IS_STRATEGY_SET(config->sync, DLT_LOGSTORAGE_SYNC_ON_SPECIFIC_SIZE) > 0) {
1260 2 : cache_size = config->specific_size;
1261 : } else /* other cache strategies */
1262 : {
1263 9 : cache_size = config->file_size;
1264 : }
1265 :
1266 : /* validate cache_size to prevent integer overflow on 32-bit targets */
1267 11 : if (cache_size == 0) {
1268 0 : dlt_vlog(
1269 : LOG_ERR, "%s: Invalid cache size 0. (ApId=[%s] CtId=[%s])\n", __func__, config->apids, config->ctids);
1270 0 : return -1;
1271 : }
1272 :
1273 : /* use size_t to avoid 32-bit overflow when adding footer size */
1274 11 : total_size = (size_t)cache_size + sizeof(DltLogStorageCacheFooter);
1275 :
1276 : /* check for overflow: if total_size wrapped or exceeds max, reject */
1277 : if (total_size < (size_t)cache_size) {
1278 : dlt_vlog(
1279 : LOG_ERR, "%s: Cache size overflow detected. (ApId=[%s] CtId=[%s])\n", __func__, config->apids,
1280 : config->ctids);
1281 : return -1;
1282 : }
1283 :
1284 : /* check total logstorage cache size */
1285 11 : if ((g_logstorage_cache_size + cache_size + sizeof(DltLogStorageCacheFooter)) > g_logstorage_cache_max) {
1286 0 : dlt_vlog(
1287 : LOG_ERR, "%s: Max size of Logstorage Cache already used. (ApId=[%s] CtId=[%s]) \n", __func__,
1288 : config->apids, config->ctids);
1289 0 : return -1;
1290 : } else {
1291 11 : dlt_vlog(
1292 : LOG_DEBUG, "%s: Logstorage total: %d , requested cache size: %d, max: %d (ApId=[%s] CtId=[%s])\n",
1293 : __func__, g_logstorage_cache_size, cache_size, g_logstorage_cache_max, config->apids, config->ctids);
1294 : }
1295 :
1296 : /* create cache */
1297 11 : config->cache = calloc(1, total_size);
1298 :
1299 11 : if (config->cache == NULL) {
1300 0 : dlt_log(LOG_CRIT, "Cannot allocate memory for filter ring buffer\n");
1301 : } else {
1302 : /* update current used cache size */
1303 11 : g_logstorage_cache_size += (unsigned int)(cache_size + sizeof(DltLogStorageCacheFooter));
1304 : }
1305 : }
1306 :
1307 : return 0;
1308 : }
1309 :
1310 : /**
1311 : * dlt_logstorage_write_msg_cache
1312 : *
1313 : * Write the log message.
1314 : *
1315 : * @param config DltLogStorageFilterConfig
1316 : * @param file_config User configurations for log file
1317 : * @param dev_path Storage device path
1318 : * @param data1 header
1319 : * @param size1 header size
1320 : * @param data2 storage header
1321 : * @param size2 storage header size
1322 : * @param data3 payload
1323 : * @param size3 payload size
1324 : * @return 0 on success, -1 on error
1325 : */
1326 199 : int dlt_logstorage_write_msg_cache(
1327 : DltLogStorageFilterConfig* config, DltLogStorageUserConfig* file_config, char* dev_path, unsigned char* data1,
1328 : int size1, unsigned char* data2, int size2, unsigned char* data3, int size3)
1329 : {
1330 : DltLogStorageCacheFooter* footer = NULL;
1331 : int msg_size;
1332 : int remain_cache_size;
1333 : uint8_t* curr_write_addr = NULL;
1334 : int ret = 0;
1335 : unsigned int cache_size;
1336 :
1337 199 : if ((config == NULL) || (data1 == NULL) || (size1 < 0) || (data2 == NULL) || (size2 < 0) || (data3 == NULL)
1338 198 : || (size3 < 0) || (config->cache == NULL) || (file_config == NULL) || (dev_path == NULL)) {
1339 : return -1;
1340 : }
1341 :
1342 198 : if (DLT_OFFLINE_LOGSTORAGE_IS_STRATEGY_SET(config->sync, DLT_LOGSTORAGE_SYNC_ON_SPECIFIC_SIZE) > 0) {
1343 43 : cache_size = config->specific_size;
1344 : } else {
1345 155 : cache_size = config->file_size;
1346 : }
1347 :
1348 : /* validate cache_size to prevent out-of-bounds access */
1349 198 : if (cache_size == 0) {
1350 : return -1;
1351 : }
1352 :
1353 198 : footer = (DltLogStorageCacheFooter*)((uint8_t*)config->cache + cache_size);
1354 198 : msg_size = size1 + size2 + size3;
1355 198 : remain_cache_size = (int)(cache_size - footer->offset);
1356 :
1357 198 : if (msg_size <= remain_cache_size) /* add at current position */
1358 : {
1359 177 : curr_write_addr = (uint8_t*)config->cache + footer->offset;
1360 177 : footer->offset += (unsigned int)msg_size;
1361 177 : if (footer->wrap_around_cnt < 1) {
1362 143 : footer->end_sync_offset = footer->offset;
1363 : }
1364 :
1365 : /* write data to cache */
1366 177 : memcpy(curr_write_addr, data1, (size_t)size1);
1367 177 : curr_write_addr += size1;
1368 177 : memcpy(curr_write_addr, data2, (size_t)size2);
1369 177 : curr_write_addr += size2;
1370 177 : memcpy(curr_write_addr, data3, (size_t)size3);
1371 : }
1372 :
1373 : /*
1374 : * In case the msg_size is equal to remaining cache size,
1375 : * the message is still written in cache.
1376 : * Then whole cache data is synchronized to file.
1377 : */
1378 198 : if (msg_size >= remain_cache_size) {
1379 : /*check for message size exceeds cache size for specific_size strategy */
1380 21 : if ((unsigned int)msg_size > cache_size) {
1381 0 : dlt_log(LOG_WARNING, "Message is larger than cache. Discard.\n");
1382 0 : return -1;
1383 : }
1384 :
1385 : /*sync to file for specific_size or file_size */
1386 21 : if (DLT_OFFLINE_LOGSTORAGE_IS_STRATEGY_SET(config->sync, DLT_LOGSTORAGE_SYNC_ON_FILE_SIZE) > 0) {
1387 4 : ret = config->dlt_logstorage_sync(config, file_config, dev_path, DLT_LOGSTORAGE_SYNC_ON_FILE_SIZE);
1388 4 : if (ret != 0) {
1389 0 : dlt_log(LOG_ERR, "dlt_logstorage_sync: Unable to sync.\n");
1390 0 : return -1;
1391 : }
1392 17 : } else if (DLT_OFFLINE_LOGSTORAGE_IS_STRATEGY_SET(config->sync, DLT_LOGSTORAGE_SYNC_ON_SPECIFIC_SIZE) > 0) {
1393 13 : ret = config->dlt_logstorage_sync(config, file_config, dev_path, DLT_LOGSTORAGE_SYNC_ON_SPECIFIC_SIZE);
1394 13 : if (ret != 0) {
1395 0 : dlt_log(LOG_ERR, "dlt_logstorage_sync: Unable to sync.\n");
1396 0 : return -1;
1397 : }
1398 4 : } else if (
1399 4 : (DLT_OFFLINE_LOGSTORAGE_IS_STRATEGY_SET(config->sync, DLT_LOGSTORAGE_SYNC_ON_DEMAND) > 0)
1400 3 : || (DLT_OFFLINE_LOGSTORAGE_IS_STRATEGY_SET(config->sync, DLT_LOGSTORAGE_SYNC_ON_DAEMON_EXIT) > 0)) {
1401 4 : footer->wrap_around_cnt += 1;
1402 : }
1403 :
1404 21 : if (msg_size > remain_cache_size) {
1405 : /* start writing from beginning */
1406 21 : footer->end_sync_offset = footer->offset;
1407 21 : curr_write_addr = config->cache;
1408 21 : footer->offset = (unsigned int)msg_size;
1409 :
1410 : /* write data to cache */
1411 21 : memcpy(curr_write_addr, data1, (size_t)size1);
1412 21 : curr_write_addr += size1;
1413 21 : memcpy(curr_write_addr, data2, (size_t)size2);
1414 21 : curr_write_addr += size2;
1415 21 : memcpy(curr_write_addr, data3, (size_t)size3);
1416 : }
1417 : }
1418 :
1419 :
1420 : return 0;
1421 : }
1422 :
1423 : /**
1424 : * dlt_logstorage_sync_msg_cache
1425 : *
1426 : * sync data to disk.
1427 : *
1428 : * @param config DltLogStorageFilterConfig
1429 : * @param file_config User configurations for log file
1430 : * @param dev_path Storage device path
1431 : * @param status Strategy flag
1432 : * @return 0 on success, -1 on error
1433 : */
1434 234 : int dlt_logstorage_sync_msg_cache(
1435 : DltLogStorageFilterConfig* config, DltLogStorageUserConfig* file_config, char* dev_path, int status)
1436 : {
1437 : unsigned int cache_size;
1438 :
1439 : DltLogStorageCacheFooter* footer = NULL;
1440 :
1441 234 : if ((config == NULL) || (file_config == NULL) || (dev_path == NULL)) {
1442 : return -1;
1443 : }
1444 :
1445 : /* sync only, if given strategy is set */
1446 233 : if (DLT_OFFLINE_LOGSTORAGE_IS_STRATEGY_SET(config->sync, status) > 0) {
1447 24 : if (config->cache == NULL) {
1448 0 : dlt_log(LOG_ERR, "Cannot copy cache to file. Cache is NULL\n");
1449 0 : return -1;
1450 : }
1451 :
1452 24 : if (DLT_OFFLINE_LOGSTORAGE_IS_STRATEGY_SET(config->sync, DLT_LOGSTORAGE_SYNC_ON_SPECIFIC_SIZE) > 0) {
1453 13 : cache_size = config->specific_size;
1454 : } else {
1455 11 : cache_size = config->file_size;
1456 : }
1457 :
1458 : /* validate cache_size to prevent out-of-bounds access */
1459 24 : if (cache_size == 0) {
1460 0 : dlt_log(LOG_ERR, "Cannot sync cache. Invalid cache size 0\n");
1461 0 : return -1;
1462 : }
1463 :
1464 24 : footer = (DltLogStorageCacheFooter*)((uint8_t*)config->cache + cache_size);
1465 :
1466 : /* sync cache data to file */
1467 24 : if (footer->wrap_around_cnt < 1) {
1468 : /* Sync whole cache */
1469 21 : dlt_logstorage_sync_to_file(
1470 : config, file_config, dev_path, footer, footer->last_sync_offset, footer->offset);
1471 :
1472 3 : } else if ((footer->wrap_around_cnt == 1) && (footer->offset < footer->last_sync_offset)) {
1473 : /* sync (1) footer->last_sync_offset to footer->end_sync_offset,
1474 : * and (2) footer->last_sync_offset (= 0) to footer->offset */
1475 0 : dlt_logstorage_sync_to_file(
1476 : config, file_config, dev_path, footer, footer->last_sync_offset, footer->end_sync_offset);
1477 0 : footer->last_sync_offset = 0;
1478 0 : dlt_logstorage_sync_to_file(
1479 : config, file_config, dev_path, footer, footer->last_sync_offset, footer->offset);
1480 : } else {
1481 : /* sync (1) footer->offset + index to footer->end_sync_offset,
1482 : * and (2) footer->last_sync_offset (= 0) to footer->offset */
1483 3 : dlt_logstorage_sync_to_file(config, file_config, dev_path, footer, footer->offset, footer->end_sync_offset);
1484 3 : footer->last_sync_offset = 0;
1485 3 : dlt_logstorage_sync_to_file(
1486 : config, file_config, dev_path, footer, footer->last_sync_offset, footer->offset);
1487 : }
1488 :
1489 : /* Initialize cache if needed */
1490 24 : if ((status == DLT_LOGSTORAGE_SYNC_ON_SPECIFIC_SIZE) || (status == DLT_LOGSTORAGE_SYNC_ON_FILE_SIZE)) {
1491 : /* clean ring buffer and reset footer information */
1492 17 : memset(config->cache, 0, cache_size + sizeof(DltLogStorageCacheFooter));
1493 : }
1494 :
1495 17 : if (status == DLT_LOGSTORAGE_SYNC_ON_FILE_SIZE) {
1496 : /* Close log file */
1497 0 : dlt_logstorage_close_file(config);
1498 4 : config->current_write_file_offset = 0;
1499 : }
1500 : }
1501 : return 0;
1502 : }
|