diff options
-rw-r--r-- | embedlog-sources.mk | 1 | ||||
l--------- | examples/el-flush.c | 1 | ||||
-rw-r--r-- | include/embedlog.h | 2 | ||||
-rw-r--r-- | man/Makefile.am | 4 | ||||
-rw-r--r-- | man/el_cleanup.3 | 2 | ||||
-rw-r--r-- | man/el_flush.3 | 80 | ||||
-rw-r--r-- | man/el_init.3 | 2 | ||||
-rw-r--r-- | man/el_oflush.3 | 1 | ||||
-rw-r--r-- | man/el_option.3 | 2 | ||||
-rw-r--r-- | man/el_overview.7 | 6 | ||||
-rw-r--r-- | man/el_print.3 | 2 | ||||
-rw-r--r-- | src/el-file.c | 163 | ||||
-rw-r--r-- | src/el-flush.c | 104 | ||||
-rw-r--r-- | src/el-private.h | 1 | ||||
l--------- | tst/el-flush.c | 1 | ||||
-rw-r--r-- | tst/test-el-file.c | 15 |
16 files changed, 318 insertions, 69 deletions
diff --git a/embedlog-sources.mk b/embedlog-sources.mk index ff750ec..c60f5ea 100644 --- a/embedlog-sources.mk +++ b/embedlog-sources.mk @@ -4,6 +4,7 @@ embedlog_sources = el-options.c \ el-print.c \ el-puts.c \ el-ts.c \ + el-flush.c \ snprintf.c if ENABLE_OUT_FILE diff --git a/examples/el-flush.c b/examples/el-flush.c new file mode 120000 index 0000000..3291726 --- /dev/null +++ b/examples/el-flush.c @@ -0,0 +1 @@ +../src/el-flush.c
\ No newline at end of file diff --git a/include/embedlog.h b/include/embedlog.h index dd86c12..70b2f8a 100644 --- a/include/embedlog.h +++ b/include/embedlog.h @@ -193,6 +193,7 @@ int el_perror(const char *file, size_t line, const char *func, int el_putb(const void *memory, size_t mlen); int el_pbinary(enum el_level level, const void *memory, size_t mlen); const struct el_options *el_get_options(void); +int el_flush(void); int el_oinit(struct el_options *options); int el_ocleanup(struct el_options *options); @@ -214,6 +215,7 @@ int el_operror(const char *file, size_t line, const char *func, int el_oputb(struct el_options *options, const void *memory, size_t mlen); int el_opbinary(enum el_level level, struct el_options *options, const void *memory, size_t mlen); +int el_oflush(struct el_options *options); #ifdef __cplusplus } diff --git a/man/Makefile.am b/man/Makefile.am index abfafb4..54a2186 100644 --- a/man/Makefile.am +++ b/man/Makefile.am @@ -14,6 +14,8 @@ man_MANS = el_cleanup.3 \ el_pmemory.3 \ el_print.3 \ el_puts.3 \ - el_vprint.3 + el_vprint.3 \ + el_flush.3 \ + el_oflush.3 EXTRA_DIST = $(man_MANS) diff --git a/man/el_cleanup.3 b/man/el_cleanup.3 index 082c10f..04dfd5e 100644 --- a/man/el_cleanup.3 +++ b/man/el_cleanup.3 @@ -45,6 +45,8 @@ Passed options object is not valid .BR el_overview (7), .BR el_option (3), .BR el_puts (3), +.BR el_flush (3), +.BR el_oflish (3), .BR el_print (3), .BR el_vprint (3), .BR el_perror (3), diff --git a/man/el_flush.3 b/man/el_flush.3 new file mode 100644 index 0000000..f8d37b5 --- /dev/null +++ b/man/el_flush.3 @@ -0,0 +1,80 @@ +.TH "el_flush" "3" "28 February 2019 (v0.5.0)" "bofc.pl" +.SH NAME +.PP +.BR el_flush ,\ el_oflush +- flush logs from all buffers to underlying devices. +.SH SYNOPSIS +.PP +.BI "#include <embedlog.h>" +.PP +.BI "int el_flush(void)" +.br +.BI "int el_oflush(struct el_options *" options ")" +.SH DESCRIPTION +.PP +.B embedlog +(and underlying drivers, operating systems, libraries etc) to improve +performance, may buffer message before commiting changes to actual device. +And it's ok, there is really no need to waste resources every time you want to +log 50 bytes of debug print on commiting message physically to the drive - or +send via network. +It's much more common to buffer messages in RAM until certain ammount of bytes +are ready to send - and then send a bunch of data in one quick burst. +But that behaviour is not always desired, like in situation in which we know we +won't be printing logs for long time. +For such situation explicit flush may in order. +.PP +.BR el_flush (3) +flushes logs from all buffers to underlying device. +For example, when logs are printed to file, logs are written through buffered +.B stdio +functions, which may result in some logs never being actually put into block +device in case of abnormal application crash - that of course depends on +settings like +.BR EL_FSYNC_EVERY . +This function will do everything what is in its power to make sure logs landed +on underlying device. +This applies to files, but also to logs with network output. +.PP +.BR el_oflush (3) +works just the same, but accepts custom +.I options +as argument. +.SH RETURN VALUE +.PP +Functions return 0 upon successful flush or -1 on errors. +When -1 was returned there is no guarantee that data has landed on underlying +device. +.SH ERRORS +.PP +.BR el_flush (3) +and +.BR el_oflush (3) +may return errors from functions like: +.BR fflush (),\ fileno (),\ fsync (),\ fseek (),\ ftell (),\ fopen () +and/or +.BR fclose (). +.PP +.BR el_oflush (3) +may additionally return: +.TP +.B EINVAL +Passed options object is not valid +.SH SEE ALSO +.PP +.BR el_init (3), +.BR el_overview (7), +.BR el_option (3), +.BR el_puts (3), +.BR el_print (3), +.BR el_vprint (3), +.BR el_perror (3), +.BR el_pmemory (3), +.BR el_pmemory_table (3), +.BR el_opmemory_table (3), +.BR el_ooption (3), +.BR el_oputs (3), +.BR el_oprint (3), +.BR el_ovprint (3), +.BR el_operror (3), +.BR el_opmemory (3). diff --git a/man/el_init.3 b/man/el_init.3 index bc260f5..934de6c 100644 --- a/man/el_init.3 +++ b/man/el_init.3 @@ -108,6 +108,8 @@ Note: error handling has been ommited for clarity sake .BR el_overview (7), .BR el_option (3), .BR el_puts (3), +.BR el_flush (3), +.BR el_oflish (3), .BR el_print (3), .BR el_vprint (3), .BR el_perror (3), diff --git a/man/el_oflush.3 b/man/el_oflush.3 new file mode 100644 index 0000000..0f1a0a9 --- /dev/null +++ b/man/el_oflush.3 @@ -0,0 +1 @@ +.so man3/el_flush.3 diff --git a/man/el_option.3 b/man/el_option.3 index c5218b4..abd1d53 100644 --- a/man/el_option.3 +++ b/man/el_option.3 @@ -645,6 +645,8 @@ informations .BR el_cleanup (3), .BR el_overview (7), .BR el_puts (3), +.BR el_flush (3), +.BR el_oflish (3), .BR el_print (3), .BR el_vprint (3), .BR el_perror (3), diff --git a/man/el_overview.7 b/man/el_overview.7 index c84542d..1cd4ffa 100644 --- a/man/el_overview.7 +++ b/man/el_overview.7 @@ -94,6 +94,8 @@ size_t " mlen ") .BI "int el_pbinary(enum el_level " level ", const void *" memory ", \ size_t " mlen ") .br +.BI "int el_flush(void)" +.br .B const struct el_options *el_get_options(void) .PP Each functions has its equivalent function but accepting @@ -139,6 +141,8 @@ const void *" memory ", size_t " mlen ")" .br .BI "int el_opbinary(enum el_level " level ", struct el_options *" options ", \ const void *" memory ", size_t " mlen ")" +.br +.BI "int el_oflush(struct el_options *" options ")" .PP For more information about a function open manual page with functions name from section 3 (ie. for el_oputs, you'd open @@ -257,6 +261,8 @@ for more details. .BR el_cleanup (3), .BR el_option (3), .BR el_puts (3), +.BR el_flush (3), +.BR el_oflish (3), .BR el_print (3), .BR el_vprint (3), .BR el_perror (3), diff --git a/man/el_print.3 b/man/el_print.3 index 4ab8709..bd4026f 100644 --- a/man/el_print.3 +++ b/man/el_print.3 @@ -507,6 +507,8 @@ and if file rotation is enabled also from .BR el_cleanup (3), .BR el_overview (7), .BR el_option (3), +.BR el_flush (3), +.BR el_oflish (3), .BR el_pmemory (3), .BR el_pmemory_table (3), .BR el_opmemory_table (3), diff --git a/src/el-file.c b/src/el-file.c index 2b44954..cd8de68 100644 --- a/src/el-file.c +++ b/src/el-file.c @@ -545,6 +545,97 @@ int el_file_open /* ========================================================================== + Does whatever it takes to make sure that logs are flushed from any + buffers to physical disk. It's not always possible, but boy, do we try. + ========================================================================== */ + + +int el_file_flush +( + struct el_options *options /* printing options */ +) +{ + /* file store operation has particular issue. You can like, write + * hundreds of megabytes into disk and that data indeed will land + * into your block device *but* metadata will not! That is if power + * lose occurs (even after those hundreds of megs), file metadata + * might not be updated and we will lose whole, I say it again + * *whole* file. To prevent data lose on power down we try to sync + * file and its metadata to block device. We do this every + * configured "sync_every" field since syncing every single write + * would simply kill performance - its up to user to decide how much + * data he is willing to lose. It is also possible that altough we + * sync our file and metadata, parent directory might not get + * flushed and there will not be entry for our file - meaning file + * will be lost too, but such situations are ultra rare and there + * isn't really much we can do about it here but praying. + */ + +#if HAVE_FSYNC && HAVE_FILENO + int fd; /* systems file descriptor for options->file */ + /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ +#endif /* HAVE_FSYNC && HAVE_FILENO */ + + VALID(EINVAL, options); + VALID(EBADF, options->current_log); + VALID(EBADF, options->current_log[0] != '\0'); + + /* + * first flush data from stdio library buffers into kernel + */ + + if (fflush(options->file) != 0) + { + return -1; + } + +#if HAVE_FSYNC && HAVE_FILENO + /* + * and then sync data into block device + */ + + if ((fd = fileno(options->file)) < 0) + { + return -1; + } + + if (fsync(fd) != 0) + { + return -1; + } +#else /* HAVE_FSYNC && HAVE_FILENO */ + /* if system does not implement fileno and fsync our only hope lies + * in closing (which should sync file to block device) and then + * opening file again. Yup, performance will suck, but hey, its + * about data safety! + */ + + fclose(options->file); + + if ((options->file = fopen(options->current_log, "a")) == NULL) + { + errno = EBADF; + return -1; + } + + fseek(options->file, 0, SEEK_END); + options->fpos = ftell(options->file); +#endif /* HAVE_FSYNC && HAVE_FILENO */ + +#ifdef RUN_TESTS + file_synced = 1; +#endif /* RUN_TESTS */ + + /* after syncing data, update written after sync field, so we + * don't trigger another flush right after this one + */ + + options->written_after_sync = 0; + return 0; +} + + +/* ========================================================================== writes memory block pointed by mem of size mlen into a file, if needed also rotates file ========================================================================== */ @@ -619,76 +710,12 @@ int el_file_putb if (options->written_after_sync >= options->file_sync_every || options->level_current_msg <= options->file_sync_level) { - /* - * file store operation has particular issue. You can like, write - * hundreds of megabytes into disk and that data indeed will land - * into your block device *but* metadata will not! That is if power - * lose occurs (even after those hundreds of megs), file metadata - * might not be updated and we will lose whole, I say it again - * *whole* file. To prevent data lose on power down we try to sync - * file and its metadata to block device. We do this every - * configured "sync_every" field since syncing every single write - * would simply kill performance - its up to user to decide how much - * data he is willing to lose. It is also possible that altough we - * sync our file and metadata, parent directory might not get - * flushed and there will not be entry for our file - meaning file - * will be lost too, but such situations are ultra rare and there - * isn't really much we can do about it here but praying. - */ - -#if HAVE_FSYNC && HAVE_FILENO - int fd; /* systems file descriptor for options->file */ - /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ -#endif /* HAVE_FSYNC && HAVE_FILENO */ - - /* - * first flush data from stdio library buffers into kernel - */ - - if (fflush(options->file) != 0) - { - return -1; - } - -#if HAVE_FSYNC && HAVE_FILENO - /* - * and then sync data into block device - */ - - if ((fd = fileno(options->file)) < 0) - { - return -1; - } - - if (fsync(fd) != 0) - { - return -1; - } -#else /* HAVE_FSYNC && HAVE_FILENO */ - /* - * if system does not implement fileno and fsync our only hope lies - * in closing (which should sync file to block device) and then - * opening file again. Yup, performance will suck, but hey, its - * about data safety! + /* we either written enough bytes to trigger flush, or log + * level is high enough it triggers log flush to block + * device */ - fclose(options->file); - - if ((options->file = fopen(options->current_log, "a")) == NULL) - { - errno = EBADF; - return -1; - } - - fseek(options->file, 0, SEEK_END); - options->fpos = ftell(options->file); -#endif /* HAVE_FSYNC && HAVE_FILENO */ - -#ifdef RUN_TESTS - file_synced = 1; -#endif /* RUN_TESTS */ - - options->written_after_sync = 0; + return el_file_flush(options); } return 0; diff --git a/src/el-flush.c b/src/el-flush.c new file mode 100644 index 0000000..baba6df --- /dev/null +++ b/src/el-flush.c @@ -0,0 +1,104 @@ +/* ========================================================================== + Licensed under BSD 2clause license See LICENSE file for more information + Author: Michał Łyszczek <michal.lyszczek@bofc.pl> + ========================================================================== + _____________________________________________________________ + / here we handle el_flush function which should make sure all \ + | logs are flushed from buffers and are received by | + \ underlying device / + ------------------------------------------------------------- + \ + \ \ + \ /\ + ( ) + .( o ). + ========================================================================== + _ __ __ ____ _ __ + (_)____ _____ / /__ __ ____/ /___ / __/(_)/ /___ _____ + / // __ \ / ___// // / / // __ // _ \ / /_ / // // _ \ / ___/ + / // / / // /__ / // /_/ // /_/ // __/ / __// // // __/(__ ) + /_//_/ /_/ \___//_/ \__,_/ \__,_/ \___/ /_/ /_//_/ \___//____/ + + ========================================================================== */ + + +#include "el-private.h" + +#include <errno.h> +#include <stdio.h> + + +/* ========================================================================== + __ __ _ ____ + ____ __ __ / /_ / /(_)_____ / __/__ __ ____ _____ _____ + / __ \ / / / // __ \ / // // ___/ / /_ / / / // __ \ / ___// ___/ + / /_/ // /_/ // /_/ // // // /__ / __// /_/ // / / // /__ (__ ) + / .___/ \__,_//_.___//_//_/ \___/ /_/ \__,_//_/ /_/ \___//____/ + /_/ + ========================================================================== */ + + +/* ========================================================================== + Does whatever it takes to make sure data is flushed from buffers and is + received by underlying devices (which may be block device or remote + server). + ========================================================================== */ + + +int el_flush +( + void +) +{ + return el_oflush(&g_options); +} + + +/* ========================================================================== + Same as el_flush() but takes options as argument + ========================================================================== */ + + +int el_oflush +( + struct el_options *options /* options defining printing style */ +) +{ + int rv; /* return value from function */ + /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + + VALID(EINVAL, options); + VALID(ENODEV, options->outputs != 0); + + rv = 0; + +#if ENABLE_OUT_STDERR + if (options->outputs & EL_OUT_STDERR) + { + rv |= fflush(stderr); + } +#endif + +#if ENABLE_OUT_STDERR + if (options->outputs & EL_OUT_STDOUT) + { + rv |= fflush(stdout); + } +#endif + +#if ENABLE_OUT_FILE + if (options->outputs & EL_OUT_FILE) + { + rv |= el_file_flush(options); + } +#endif + +#if 0 + if (options->outputs & EL_OUT_NET) + { + el_net_flush(options); + } +#endif + + return rv; +} diff --git a/src/el-private.h b/src/el-private.h index 1399e39..c36ecbe 100644 --- a/src/el-private.h +++ b/src/el-private.h @@ -292,6 +292,7 @@ int el_file_open(struct el_options *options); int el_file_puts(struct el_options *options, const char *s); int el_file_putb(struct el_options *options, const void *mem, size_t mlen); void el_file_cleanup(struct el_options *options); +int el_file_flush(struct el_options *options); #endif diff --git a/tst/el-flush.c b/tst/el-flush.c new file mode 120000 index 0000000..3291726 --- /dev/null +++ b/tst/el-flush.c @@ -0,0 +1 @@ +../src/el-flush.c
\ No newline at end of file diff --git a/tst/test-el-file.c b/tst/test-el-file.c index 371721a..852311b 100644 --- a/tst/test-el-file.c +++ b/tst/test-el-file.c @@ -1536,6 +1536,20 @@ static void file_sync_always(void) ========================================================================== */ +static void file_sync_via_flush_function(void) +{ + el_option(EL_FSYNC_EVERY, 16); + mt_fok(el_puts(s8)); + mt_fail(file_synced == 0); + mt_fok(el_flush()); + mt_fail(file_synced == 1); +} + + +/* ========================================================================== + ========================================================================== */ + + static void file_sync_periodic(void) { el_option(EL_FSYNC_EVERY, 8); @@ -1693,6 +1707,7 @@ void el_file_test_group(void) mt_run(file_sync_always); mt_run(file_sync_periodic); mt_run(file_sync_level); + mt_run(file_sync_via_flush_function); rmdir(WORKDIR); #endif |