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