This source file includes following definitions.
- _sir_addfile
- _sir_updatefile
- _sir_remfile
- _sirfile_create
- _sirfile_open
- _sirfile_close
- _sirfile_write
- _sirfile_writeheader
- _sirfile_needsroll
- _sirfile_roll
- _sirfile_rollifneeded
- _sirfile_archive
- _sirfile_splitpath
- _sirfile_destroy
- _sirfile_validate
- _sirfile_update
- _sir_fcache_add
- _sir_fcache_update
- _sir_fcache_rem
- _sir_fcache_shift
- _sir_fcache_pred_path
- _sir_fcache_pred_id
- _sir_fcache_find
- _sir_fcache_destroy
- _sir_fcache_dispatch
- _sir_fflush
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 #include "sir/filecache.h"
35 #include "sir/filesystem.h"
36 #include "sir/internal.h"
37 #include "sir/defaults.h"
38
39 sirfileid _sir_addfile(const char* path, sir_levels levels, sir_options opts) {
40 (void)_sir_seterror(_SIR_E_NOERROR);
41
42 if (!_sir_sanity())
43 return 0U;
44
45 _SIR_LOCK_SECTION(sirfcache, sfc, SIRMI_FILECACHE, 0U);
46
47 _sir_defaultlevels(&levels, sir_file_def_lvls);
48 _sir_defaultopts(&opts, sir_file_def_opts);
49
50 sirfileid retval = _sir_fcache_add(sfc, path, levels, opts);
51 _SIR_UNLOCK_SECTION(SIRMI_FILECACHE);
52
53 return retval;
54 }
55
56 bool _sir_updatefile(sirfileid id, const sir_update_config_data* data) {
57 (void)_sir_seterror(_SIR_E_NOERROR);
58
59 if (!_sir_sanity() || !_sir_validfileid(id) || !_sir_validupdatedata(data))
60 return false;
61
62 _SIR_LOCK_SECTION(const sirfcache, sfc, SIRMI_FILECACHE, false);
63 bool retval = _sir_fcache_update(sfc, id, data);
64 _SIR_UNLOCK_SECTION(SIRMI_FILECACHE);
65
66 return retval;
67 }
68
69 bool _sir_remfile(sirfileid id) {
70 (void)_sir_seterror(_SIR_E_NOERROR);
71
72 if (!_sir_sanity() || !_sir_validfileid(id))
73 return false;
74
75 _SIR_LOCK_SECTION(sirfcache, sfc, SIRMI_FILECACHE, false);
76 bool retval = _sir_fcache_rem(sfc, id);
77 _SIR_UNLOCK_SECTION(SIRMI_FILECACHE);
78
79 return retval;
80 }
81
82 sirfile* _sirfile_create(const char* path, sir_levels levels, sir_options opts) {
83 if (!_sir_validstr(path) || !_sir_validlevels(levels) || !_sir_validopts(opts))
84 return NULL;
85
86 sirfile* sf = (sirfile*)calloc(1, sizeof(sirfile));
87 if (!sf) {
88 (void)_sir_handleerr(errno);
89 return NULL;
90 }
91
92 sf->path = strndup(path, strnlen(path, SIR_MAXPATH));
93 if (!sf->path) {
94 (void)_sir_handleerr(errno);
95 _sir_safefree(&sf);
96 return NULL;
97 }
98
99 sf->levels = levels;
100 sf->opts = opts;
101
102 if (!_sirfile_open(sf) || !_sirfile_validate(sf)) {
103 _sirfile_destroy(&sf);
104 return NULL;
105 }
106
107 return sf;
108 }
109
110 bool _sirfile_open(sirfile* sf) {
111 bool retval = _sir_validptr(sf) && _sir_validstr(sf->path);
112
113 if (retval) {
114 FILE* f = NULL;
115 retval = _sir_openfile(&f, sf->path, SIR_FOPENMODE, SIR_PATH_REL_TO_CWD);
116 if (retval && NULL != f) {
117 _sirfile_close(sf);
118
119 sf->f = f;
120 sf->id = FNV32_1a((const uint8_t*)sf->path, strnlen(sf->path, SIR_MAXPATH));
121 }
122 }
123
124 return retval;
125 }
126
127 void _sirfile_close(sirfile* sf) {
128 if (_sir_validptrnofail(sf))
129 _sir_safefclose(&sf->f);
130 }
131
132 bool _sirfile_write(sirfile* sf, const char* output) {
133 bool retval = _sirfile_validate(sf) && _sir_validstr(output);
134
135 if (retval) {
136 if (sf->writes_since_size_chk++ > SIR_FILE_CHK_SIZE_WRITES) {
137 sf->writes_since_size_chk = 0;
138 _sirfile_rollifneeded(sf);
139 }
140
141 size_t writeLen = strnlen(output, SIR_MAXOUTPUT);
142 size_t wrote = fwrite(output, sizeof(char), writeLen, sf->f);
143
144 SIR_ASSERT(wrote == writeLen);
145
146 if (wrote < writeLen) {
147 (void)_sir_handleerr(errno);
148 clearerr(sf->f);
149 }
150
151 retval = wrote == writeLen;
152 }
153
154 return retval;
155 }
156
157 bool _sirfile_writeheader(sirfile* sf, const char* msg) {
158 bool retval = _sirfile_validate(sf) && _sir_validstr(msg);
159
160 if (retval) {
161 time_t now = -1;
162 (void)time(&now);
163
164 char timestamp[SIR_MAXTIME] = {0};
165 bool fmttime = _sir_formattime(now, timestamp, SIR_FHTIMEFORMAT);
166 if (!fmttime)
167 return false;
168
169 char header[SIR_MAXFHEADER] = {0};
170 (void)snprintf(header, SIR_MAXFHEADER, SIR_FHFORMAT, msg, timestamp);
171
172 retval = _sirfile_write(sf, header);
173 }
174
175 return retval;
176 }
177
178 bool _sirfile_needsroll(sirfile* sf) {
179 bool retval = _sirfile_validate(sf);
180
181 if (retval) {
182 struct stat st = {0};
183 int getstat = fstat(fileno(sf->f), &st);
184
185 if (0 != getstat) {
186 getstat = stat(sf->path, &st);
187 if (0 != getstat)
188 return _sir_handleerr(errno);
189 }
190
191 retval = st.st_size + BUFSIZ >= SIR_FROLLSIZE ||
192 SIR_FROLLSIZE - (st.st_size + BUFSIZ) <= BUFSIZ;
193 }
194
195 return retval;
196 }
197
198 bool _sirfile_roll(sirfile* sf, char** newpath) {
199 if (!_sirfile_validate(sf) || !_sir_validptrptr(newpath))
200 return false;
201
202 bool retval = false;
203 char* name = NULL;
204 char* ext = NULL;
205
206 bool split = _sirfile_splitpath(sf, &name, &ext);
207 SIR_ASSERT(split);
208
209 if (split) {
210 time_t now = -1;
211
212 (void)time(&now);
213 SIR_ASSERT(-1 != now);
214
215 if (-1 != now) {
216 char timestamp[SIR_MAXTIME] = {0};
217 bool fmttime = _sir_formattime(now, timestamp, SIR_FNAMETIMEFORMAT);
218 SIR_ASSERT(fmttime);
219
220 if (fmttime) {
221 *newpath = (char*)calloc(SIR_MAXPATH, sizeof(char));
222
223 if (_sir_validptr(*newpath)) {
224 char seqbuf[7] = {0};
225 bool exists = false;
226 bool resolved = false;
227 uint16_t sequence = 0U;
228
229 do {
230 (void)snprintf(*newpath, SIR_MAXPATH, SIR_FNAMEFORMAT, name,
231 timestamp, (sequence > 0U ? seqbuf : ""),
232 _sir_validstrnofail(ext) ? ext : "");
233
234
235
236
237
238 if (!_sir_pathexists(*newpath, &exists, SIR_PATH_REL_TO_CWD)) {
239
240
241
242
243 break;
244 } else if (exists) {
245
246
247 _sir_selflog("path: '%s' already exists; incrementing sequence", *newpath);
248 sequence++;
249 } else {
250 _sir_selflog("found good path: '%s'", *newpath);
251 resolved = true;
252 break;
253 }
254
255 if (sequence > 0)
256 (void)snprintf(seqbuf, 7, SIR_FNAMESEQFORMAT, sequence);
257
258 } while (sequence <= 999U);
259
260 if (!resolved)
261 _sir_selflog("error: unable to determine suitable path for '%s';"
262 " not rolling!", sf->path);
263
264 retval = resolved && _sirfile_archive(sf, *newpath);
265 }
266 }
267 }
268 }
269
270 _sir_safefree(&name);
271 _sir_safefree(&ext);
272
273 return retval;
274 }
275
276 void _sirfile_rollifneeded(sirfile* sf) {
277 if (_sirfile_validate(sf) && _sirfile_needsroll(sf)) {
278 bool rolled = false;
279 char* newpath = NULL;
280
281 _sir_selflog("file (path: '%s', id: %"PRIx32") reached ~%d bytes in size;"
282 " rolling...", sf->path, sf->id, SIR_FROLLSIZE);
283
284 _sir_fflush(sf->f);
285
286 if (_sirfile_roll(sf, &newpath)) {
287 char header[SIR_MAXFHEADER] = {0};
288 (void)snprintf(header, SIR_MAXFHEADER, SIR_FHROLLED, newpath);
289 rolled = _sirfile_writeheader(sf, header);
290 }
291
292 _sir_safefree(&newpath);
293 if (!rolled)
294 _sir_selflog("error: failed to roll file (path: '%s', id: %"PRIx32")!",
295 sf->path, sf->id);
296 }
297 }
298
299 bool _sirfile_archive(sirfile* sf, const char* newpath) {
300 bool retval = _sirfile_validate(sf) && _sir_validstr(newpath);
301
302 if (retval) {
303 #if defined(__WIN__)
304
305 _sirfile_close(sf);
306 #endif
307 if (0 != rename(sf->path, newpath)) {
308 retval = _sir_handleerr(errno);
309 } else {
310 retval = _sirfile_open(sf);
311 if (retval)
312 _sir_selflog("archived '%s' " SIR_R_ARROW " '%s'", sf->path, newpath);
313 }
314 }
315
316 return retval;
317 }
318
319 bool _sirfile_splitpath(const sirfile* sf, char** name, char** ext) {
320 if (_sir_validptrptr(name))
321 *name = NULL;
322 if (_sir_validptrptr(ext))
323 *ext = NULL;
324
325 bool retval = _sirfile_validate(sf) && _sir_validptrptr(name) && _sir_validptrptr(ext);
326
327 if (retval) {
328 char* tmp = strndup(sf->path, strnlen(sf->path, SIR_MAXPATH));
329 if (!tmp)
330 return _sir_handleerr(errno);
331
332 const char* fullstop = strrchr(tmp, '.');
333 if (fullstop && fullstop != tmp) {
334 uintptr_t namesize = fullstop - tmp;
335 SIR_ASSERT(namesize < SIR_MAXPATH);
336
337 tmp[namesize] = '\0';
338 *name = (char*)calloc(namesize + 1, sizeof(char));
339 if (!*name) {
340 _sir_safefree(&tmp);
341 return _sir_handleerr(errno);
342 }
343
344 (void)_sir_strncpy(*name, namesize + 1, tmp, namesize);
345 *ext = strndup(sf->path + namesize, strnlen(sf->path + namesize, SIR_MAXPATH));
346 } else {
347 fullstop = NULL;
348 *name = strndup(sf->path, strnlen(sf->path, SIR_MAXPATH));
349 }
350
351 _sir_safefree(&tmp);
352 retval = _sir_validstrnofail(*name) && (!fullstop || _sir_validstrnofail(*ext));
353 }
354
355 return retval;
356 }
357
358 void _sirfile_destroy(sirfile** sf) {
359 if (sf && *sf) {
360 _sirfile_close(*sf);
361 _sir_safefree(&(*sf)->path);
362 _sir_safefree(sf);
363 }
364 }
365
366 bool _sirfile_validate(const sirfile* sf) {
367 return _sir_validptrnofail(sf) && _sir_validptrnofail(sf->f) &&
368 _sir_validstrnofail(sf->path) && _sir_validfileid(sf->id);
369 }
370
371 bool _sirfile_update(sirfile* sf, const sir_update_config_data* data) {
372 bool retval = _sirfile_validate(sf);
373
374 if (retval) {
375 bool updated = false;
376 if (_sir_bittest(data->fields, SIRU_LEVELS)) {
377 if (sf->levels != *data->levels) {
378 _sir_selflog("updating file (id: %"PRIx32") levels from %04"PRIx16
379 " to %04"PRIx16, sf->id, sf->levels, *data->levels);
380 sf->levels = *data->levels;
381 } else {
382 _sir_selflog("skipped superfluous update of file (id: %"PRIx32")"
383 " levels: %04"PRIx16, sf->id, sf->levels);
384 }
385
386 updated = true;
387 }
388
389 if (_sir_bittest(data->fields, SIRU_OPTIONS)) {
390 if (sf->opts != *data->opts) {
391 _sir_selflog("updating file (id: %"PRIx32") options from %08"PRIx32
392 " to %08"PRIx32, sf->id, sf->opts, *data->opts);
393 sf->opts = *data->opts;
394 } else {
395 _sir_selflog("skipped superfluous update of file (id: %"PRIx32")"
396 " options: %08"PRIx32, sf->id, sf->opts);
397 }
398
399 updated = true;
400 }
401
402 retval = updated;
403 }
404
405 return retval;
406 }
407
408 sirfileid _sir_fcache_add(sirfcache* sfc, const char* path, sir_levels levels,
409 sir_options opts) {
410 if (!_sir_validptr(sfc) || !_sir_validstr(path) || !_sir_validlevels(levels) ||
411 !_sir_validopts(opts))
412 return 0U;
413
414 if (sfc->count >= SIR_MAXFILES)
415 return _sir_seterror(_SIR_E_NOROOM);
416
417 const sirfile* existing = _sir_fcache_find(sfc, (const void*)path, _sir_fcache_pred_path);
418 if (NULL != existing) {
419 _sir_selflog("error: already have file (path: '%s', id: %"PRIx32")",
420 path, existing->id);
421 return _sir_seterror(_SIR_E_DUPITEM);
422 }
423
424 sirfile* sf = _sirfile_create(path, levels, opts);
425 if (_sirfile_validate(sf)) {
426 _sir_selflog("adding file (path: '%s', id: %"PRIx32"); count = %zu",
427 sf->path, sf->id, sfc->count + 1);
428
429 sfc->files[sfc->count++] = sf;
430
431 if (!_sir_bittest(sf->opts, SIRO_NOHDR) && !_sirfile_writeheader(sf, SIR_FHBEGIN))
432 _sir_selflog("warning: failed to write file header (path: '%s', id: %"PRIx32")",
433 sf->path, sf->id);
434
435 return sf->id;
436 }
437
438 _sir_safefree(&sf);
439
440 return 0U;
441 }
442
443 bool _sir_fcache_update(const sirfcache* sfc, sirfileid id, const sir_update_config_data* data) {
444 bool retval = _sir_validptr(sfc) && _sir_validfileid(id) && _sir_validupdatedata(data);
445
446 if (retval) {
447 sirfile* found = _sir_fcache_find(sfc, (const void*)&id, _sir_fcache_pred_id);
448 retval = found ? _sirfile_update(found, data) : _sir_seterror(_SIR_E_NOITEM);
449 }
450
451 return retval;
452 }
453
454 bool _sir_fcache_rem(sirfcache* sfc, sirfileid id) {
455 bool retval = _sir_validptr(sfc) && _sir_validfileid(id);
456
457 if (retval) {
458 bool found = false;
459 for (size_t n = 0; n < sfc->count; n++) {
460 SIR_ASSERT(_sirfile_validate(sfc->files[n]));
461
462 if (sfc->files[n]->id == id) {
463 _sir_selflog("removing file (path: '%s', id: %"PRIx32"); count = %zu",
464 sfc->files[n]->path, sfc->files[n]->id, sfc->count - 1);
465
466 _sirfile_destroy(&sfc->files[n]);
467 _sir_fcache_shift(sfc, n);
468
469 sfc->count--;
470 found = true;
471 break;
472 }
473 }
474
475 if (!found)
476 retval = _sir_seterror(_SIR_E_NOITEM);
477 }
478
479 return retval;
480 }
481
482 void _sir_fcache_shift(sirfcache* sfc, size_t idx) {
483 if (_sir_validptr(sfc) && sfc->count <= SIR_MAXFILES) {
484 for (size_t n = idx; n < sfc->count - 1; n++) {
485 sfc->files[n] = sfc->files[n + 1];
486 sfc->files[n + 1] = NULL;
487 }
488 }
489 }
490
491 bool _sir_fcache_pred_path(const void* match, const sirfile* iter) {
492 const char* path = (const char*)match;
493 bool equal = false;
494
495 _sir_selflog("comparing '%s' == '%s'", path, iter->path);
496
497 #if !defined(__WIN__)
498
499
500
501 char resolved1[SIR_MAXPATH] = {0};
502 char resolved2[SIR_MAXPATH] = {0};
503 struct stat st1 = {0};
504 struct stat st2 = {0};
505
506 const char* real1 = realpath(path, resolved1);
507 const char* real2 = realpath(iter->path, resolved2);
508
509 SIR_UNUSED(real1);
510 SIR_UNUSED(real2);
511
512 if ((!stat(resolved1, &st1) && !stat(resolved2, &st2)) ||
513 (!stat(path, &st1) && !stat(iter->path, &st2))) {
514 equal = st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino;
515 _sir_selflog("returning %d for '%s' == '%s'", equal, path, iter->path);
516 } else {
517 _sir_selflog("falling back to canonical path string compare");
518 equal = 0 == strncmp(resolved1, resolved2, SIR_MAXPATH);
519 _sir_selflog("returning %d for '%s' == '%s'", equal, resolved1, resolved2);
520 }
521
522 return equal;
523 #else
524
525
526
527 DWORD sh_flags = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
528 DWORD open_type = OPEN_EXISTING;
529 DWORD attr_flags = FILE_ATTRIBUTE_NORMAL;
530 BY_HANDLE_FILE_INFORMATION fi1 = {0};
531 BY_HANDLE_FILE_INFORMATION fi2 = {0};
532
533 HANDLE h1 = CreateFileA(path, 0, sh_flags, NULL, open_type, attr_flags, NULL);
534 HANDLE h2 = CreateFileA(iter->path,0, sh_flags, NULL, open_type, attr_flags, NULL);
535
536 if (INVALID_HANDLE_VALUE != h1 && INVALID_HANDLE_VALUE != h2 &&
537 GetFileInformationByHandle(h1, &fi1) && GetFileInformationByHandle(h2, &fi2)) {
538 equal = fi1.dwVolumeSerialNumber == fi2.dwVolumeSerialNumber &&
539 fi1.nFileIndexLow == fi2.nFileIndexLow &&
540 fi1.nFileIndexHigh == fi2.nFileIndexHigh;
541 _sir_selflog("returning %d for '%s' == '%s'", equal, path, iter->path);
542 } else {
543 _sir_selflog("falling back to canonical path string compare");
544 char resolved1[SIR_MAXPATH] = {0}, resolved2[SIR_MAXPATH] = {0};
545 if (GetFullPathNameA(path, SIR_MAXPATH, resolved1, NULL) &&
546 GetFullPathNameA(iter->path, SIR_MAXPATH, resolved2, NULL))
547 equal = 0 == StrCmpIA(resolved1, resolved2);
548 _sir_selflog("returning %d for '%s' == '%s'", equal, resolved1, resolved2);
549 }
550
551 if (INVALID_HANDLE_VALUE != h1)
552 CloseHandle(h1);
553
554 if (INVALID_HANDLE_VALUE != h2)
555 CloseHandle(h2);
556
557 return equal;
558 #endif
559 }
560
561 bool _sir_fcache_pred_id(const void* match, const sirfile* iter) {
562 const sirfileid* id = (const sirfileid*)match;
563 return iter->id == *id;
564 }
565
566 sirfile* _sir_fcache_find(const sirfcache* sfc, const void* match, sir_fcache_pred pred) {
567 bool valid = _sir_validptr(sfc) && _sir_validptr(match) && _sir_validfnptr(pred);
568
569 if (valid) {
570 for (size_t n = 0; n < sfc->count; n++) {
571 if (pred(match, sfc->files[n]))
572 return sfc->files[n];
573 }
574 }
575
576 return NULL;
577 }
578
579 bool _sir_fcache_destroy(sirfcache* sfc) {
580 bool retval = _sir_validptr(sfc);
581
582 if (retval) {
583 while (sfc->count > 0) {
584 size_t idx = sfc->count - 1;
585 SIR_ASSERT(_sirfile_validate(sfc->files[idx]));
586 _sirfile_destroy(&sfc->files[idx]);
587 sfc->files[idx] = NULL;
588 sfc->count--;
589 }
590
591 (void)_sir_explicit_memset(sfc, 0, sizeof(sirfcache));
592 }
593
594 return retval;
595 }
596
597 bool _sir_fcache_dispatch(const sirfcache* sfc, sir_level level, sirbuf* buf,
598 size_t* dispatched, size_t* wanted) {
599 bool retval = _sir_validptr(sfc) && _sir_validlevel(level) &&
600 _sir_validptr(buf) && _sir_validptr(dispatched) &&
601 _sir_validptr(wanted);
602
603 if (retval) {
604 const char* wrote = NULL;
605 sir_options lastopts = 0U;
606
607 *dispatched = 0;
608 *wanted = 0;
609
610 for (size_t n = 0; n < sfc->count; n++) {
611 SIR_ASSERT(_sirfile_validate(sfc->files[n]));
612
613 if (!_sir_bittest(sfc->files[n]->levels, level)) {
614 _sir_selflog("level %04"PRIx16" not set in level mask (%04"PRIx16
615 ") for file (path: '%s', id: %"PRIx32"); skipping",
616 level, sfc->files[n]->levels, sfc->files[n]->path,
617 sfc->files[n]->id);
618 continue;
619 }
620
621 (*wanted)++;
622
623 if (!wrote || sfc->files[n]->opts != lastopts) {
624 wrote = _sir_format(false, sfc->files[n]->opts, buf);
625 SIR_ASSERT(wrote);
626 lastopts = sfc->files[n]->opts;
627 }
628
629 if (wrote && _sirfile_write(sfc->files[n], wrote)) {
630 (*dispatched)++;
631 } else {
632 _sir_selflog("error: write to file (path: '%s', id: %"PRIx32") failed!",
633 sfc->files[n]->path, sfc->files[n]->id);
634 }
635 }
636
637 retval = (*dispatched == *wanted);
638 }
639
640 return retval;
641 }
642
643 void _sir_fflush(FILE* f) {
644 if (_sir_validptr(f) && 0 != fflush(f))
645 (void)_sir_handleerr(errno);
646 }