aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichał Łyszczek <michal.lyszczek@bofc.pl>2019-05-14 22:53:59 +0200
committerMichał Łyszczek <michal.lyszczek@bofc.pl>2019-05-14 23:00:08 +0200
commitf7f2eaa207d5cf79fc834881ce7e8ba23e24227d (patch)
tree3fc54d5ae354430fec011f4af56aba7a29d5edae
parent1902eed479506d650cc1f0e310bfd5f971dde086 (diff)
downloadembedlog-f7f2eaa207d5cf79fc834881ce7e8ba23e24227d.tar.gz
embedlog-f7f2eaa207d5cf79fc834881ce7e8ba23e24227d.tar.bz2
embedlog-f7f2eaa207d5cf79fc834881ce7e8ba23e24227d.zip
add el_new and el_destroy functions for stable ABIstable-abi
el_new will allocate on heap "el" object, so user does not have to know any internals of "struct el". This will make ABI stable in case when anything in "struct el" changes. el_oinit can still be used to allocate memory on stack, but with it ABI may be broken, so it should be used only on systems when user can afford to recompile all programs that use embedlog lib. * include/embedlog.h.in add 2 new functions, el_init and el_destroy * man/ add description for 2 new functions, describe notes about stable ABI and stack allocated methods. * tst/ add test for 2 new functions Signed-off-by: Michał Łyszczek <michal.lyszczek@bofc.pl>
-rw-r--r--include/embedlog.h.in3
-rw-r--r--man/el_cleanup.312
-rw-r--r--man/el_destroy.31
-rw-r--r--man/el_flush.32
-rw-r--r--man/el_init.354
-rw-r--r--man/el_new.31
-rw-r--r--man/el_option.32
-rw-r--r--man/el_overview.777
-rw-r--r--man/el_print.32
-rw-r--r--src/el-options.c52
-rw-r--r--tst/test-el-file.c27
-rw-r--r--tst/test-el-options.c5
12 files changed, 234 insertions, 4 deletions
diff --git a/include/embedlog.h.in b/include/embedlog.h.in
index 53525bb..a3deeb1 100644
--- a/include/embedlog.h.in
+++ b/include/embedlog.h.in
@@ -219,6 +219,9 @@ int el_pbinary(enum el_level level, const void *memory, size_t mlen);
const struct el *el_get_el(void);
int el_flush(void);
+struct el * el_new(void);
+int el_destroy(struct el *el);
+
int el_oinit(struct el *el);
int el_ocleanup(struct el *el);
int el_ooption(struct el *el, int option, ...);
diff --git a/man/el_cleanup.3 b/man/el_cleanup.3
index 20ff67e..19c9742 100644
--- a/man/el_cleanup.3
+++ b/man/el_cleanup.3
@@ -14,6 +14,8 @@ calls.
.BI "int el_cleanup(void)"
.br
.BI "int el_ocleanup(struct el *" el ")"
+.br
+.BI "int el_destroy(struct el *" el ")"
.SH DESCRIPTION
.PP
.BR el_cleanup (3)
@@ -25,6 +27,13 @@ After clean up, library calls cannot be used without initialization.
works just the same, but accepts custom
.I el
object as argument.
+.PP
+.BR el_destroy (3)
+works just the same, but must be used when
+.I el
+object has been created with
+.BR el_new (3)
+function.
.SH RETURN VALUE
.PP
Functions return 0 upon successful cleanup or -1 on errors.
@@ -35,6 +44,8 @@ used.
.BR el_cleanup (3)
function cannot fail.
.BR el_ocleanup (3)
+and
+.BR el_destroy (3)
may return:
.TP
.B EINVAL
@@ -53,6 +64,7 @@ Passed el object is not valid
.BR el_pmemory (3),
.BR el_pmemory_table (3),
.BR el_opmemory_table (3),
+.BR el_new (3),
.BR el_ooption (3),
.BR el_oputs (3),
.BR el_oprint (3),
diff --git a/man/el_destroy.3 b/man/el_destroy.3
new file mode 100644
index 0000000..f5c1d77
--- /dev/null
+++ b/man/el_destroy.3
@@ -0,0 +1 @@
+.so man3/el_cleanup.3
diff --git a/man/el_flush.3 b/man/el_flush.3
index 3ae99b2..e3788d1 100644
--- a/man/el_flush.3
+++ b/man/el_flush.3
@@ -72,6 +72,8 @@ Passed el object is not valid
.BR el_pmemory (3),
.BR el_pmemory_table (3),
.BR el_opmemory_table (3),
+.BR el_new (3),
+.BR el_destroy (3),
.BR el_ooption (3),
.BR el_oputs (3),
.BR el_oprint (3),
diff --git a/man/el_init.3 b/man/el_init.3
index 34dfb83..2056bf5 100644
--- a/man/el_init.3
+++ b/man/el_init.3
@@ -10,6 +10,8 @@
.BI "int el_init(void)"
.br
.BI "int el_oinit(struct el *" el ")"
+.br
+.BI "struct el * el_new(void)"
.SH DESCRIPTION
.PP
There are two types of functions in
@@ -63,14 +65,53 @@ object may be used.
If you want to have multiple embedlog instances (ie. one for program logs,
and one for queries) that stores logs differently - this is the function you
want to use.
+Object initialized with this function must be deinitialized with
+.BR el_ocleanup (3)
+function.
+.PP
+.BR el_new (3)
+Works in the same way as
+.BR el_oinit (3)
+but function returns newly heap-allocated pointer to
+.I el
+struct.
+Object created with this function must be destroyed with
+.BR el_destroy (3)
+function.
+.SH NOTES
+Keeps in mind that using
+.BR el_oinit (3)
+function is suscible to ABI breakage.
+If stable ABI is important to you, use
+.BR el_new (3)
+function.
+Please check
+.BR el_overview (3)
+for more information about this.
.SH RETURN VALUE
.PP
-Both functions will return 0 upon success and -1 on errors.
+.BR el_init (3)
+cannot fail and always returns 0.
+.BR el_oinit (3)
+function will return 0 upon success and -1 on errors.
+.BR el_new (3)
+returns valid pointer on success and
+.B NULL
+when error occurs.
.SH ERRORS
+.PP
+.BR el_oinit (3)
+can return:
.TP
.B EINVAL
.I el
object is invalid (null).
+.PP
+.BR el_new (3)
+can return:
+.TP
+.B ENOMEM
+Not enough memory in the system to perform necessary memory allocations.
.SH EXAMPLE
.PP
Note: error handling has been ommited for clarity sake
@@ -80,11 +121,12 @@ Note: error handling has been ommited for clarity sake
int main(void)
{
- struct el el;
+ struct el el, *elp;
- /* initialize both default and custom el objects */
+ /* initialize default and two custom el objects */
el_init();
el_oinit(&el);
+ elp = el_new();
/* make el to print to file and stderr */
el_ooption(&el, EL_OUT, EL_OUT_FILE | EL_OUT_STDERR);
@@ -93,8 +135,10 @@ Note: error handling has been ommited for clarity sake
/* print messages */
el_print(ELI, "will print to stderr");
el_oprint(ELI, &el, "will print to file /tmp/test.log and stderr");
+ el_oprint(ELN, elp, "print to stderr");
/* cleanup after any initialization code (like fopen) */
+ el_destroy(elp);
el_ocleanup(&el);
el_cleanup();
@@ -109,13 +153,15 @@ Note: error handling has been ommited for clarity sake
.BR el_option (3),
.BR el_puts (3),
.BR el_flush (3),
-.BR el_oflish (3),
+.BR el_oflush (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_new (3),
+.BR el_oinit (3),
.BR el_ocleanup (3),
.BR el_ooption (3),
.BR el_oputs (3),
diff --git a/man/el_new.3 b/man/el_new.3
new file mode 100644
index 0000000..3dbf2b6
--- /dev/null
+++ b/man/el_new.3
@@ -0,0 +1 @@
+.so man3/el_init.3
diff --git a/man/el_option.3 b/man/el_option.3
index 511a1a5..a7ddae7 100644
--- a/man/el_option.3
+++ b/man/el_option.3
@@ -653,6 +653,8 @@ informations
.BR el_pmemory (3),
.BR el_pmemory_table (3),
.BR el_opmemory_table (3),
+.BR el_new (3),
+.BR el_destroy (3),
.BR el_ocleanup (3),
.BR el_oputs (3),
.BR el_oprint (3),
diff --git a/man/el_overview.7 b/man/el_overview.7
index cb9813e..4dde0c9 100644
--- a/man/el_overview.7
+++ b/man/el_overview.7
@@ -111,6 +111,10 @@ for more information.
.br
.BI "int el_ocleanup(struct el *" el ")"
.br
+.B struct el * el_new(void)
+.br
+.BI "int el_destroy(struct el *" el ")"
+.br
.BI "int el_ooption(struct el *" el ", enum el_option " option ", \
\&...)"
.br
@@ -232,6 +236,77 @@ You can simply call it like
.EX
el_oprint(OELN, "Notice message");
.EE
+.SH "STABLE ABI CONSIDERATION"
+.PP
+Since
+.B embedlog
+is versatile and can work on both hardcore embedded systems (nuttx, freertos,
+bare metals) and on big CPUs that can run Linux, library must provide a way
+for user to use stack-allocated objects and stable ABI.
+Unfortunately there is no way to provide both at the same time, so
+.B embedlog
+provides two solutions so everybody is happy.
+.PP
+Note that library versioning follows
+.B STABLE ABI
+way of using library.
+So if feature is added that adds new field to
+.BR "struct el",
+then this will not be treated as ABI breakage, because if you are using
+.BR el_new (3)
+or
+.BR el_init (3)
+functions, ABI will not be broken.
+Only when using
+.BR el_oinit (3)
+ABI will be broken.
+.SS "STABLE ABI"
+.PP
+When stable ABI is required, user must initialize
+.B embedlog
+either with
+.BR el_init (3)
+or
+.BR el_new (3)
+function.
+User also must not access internal fields directly and all operations on
+.I el
+object must be done through the API functions.
+.BR el_new (3)
+will malloc necessary memory for the user which must be freed with
+.BR el_destroy (3)
+function.
+Under no circumstances use
+.B sizeof
+on the object nor struct.
+Operation will succeed, but may return invalid results when library is updated.
+To put it in as few word as possible - you don't know anything about internals
+of
+.BR "struct el" .
+.SS "STACK ALLOCATED OBJECT"
+.PP
+When you use library in hardcore embedded environment, where there is not
+dynamic linking involved, you are save to ignore any ABI changes, since
+you always build your programs alongside the library.
+But thanks to that you can avoid
+.BR malloc ()
+call and allocate everything on the stack.
+To allocate object on stack you must use
+.BR el_oinit (3)
+function, and deinitialize object with
+.BR el_ocleanup (3).
+.PP
+If you are using
+.BR el_oinit (3)
+on systems with dynamic library support you are suscible to ABI breakage and
+undefined behaviour when only new features are added, or even when bugfix
+introduces new field to
+.BR "struct el" .
+Heck, even recompilation with different flags can break ABI here.
+Do not use this approach if your system have dynamic library support unless
+you can rebuild all programs against new version of library.
+.PP
+.B You've been warned!
.SH EXAMPLE
.PP
Initial setup is very trivial.
@@ -274,6 +349,8 @@ for more details.
.BR el_opmemory_table (3),
.BR el_oinit (3),
.BR el_ocleanup (3),
+.BR el_new (3),
+.BR el_destroy (3),
.BR el_ooption (3),
.BR el_oputs (3),
.BR el_oprint (3),
diff --git a/man/el_print.3 b/man/el_print.3
index 26ed93e..8bddf80 100644
--- a/man/el_print.3
+++ b/man/el_print.3
@@ -513,6 +513,8 @@ and if file rotation is enabled also from
.BR el_oflish (3),
.BR el_pmemory (3),
.BR el_pmemory_table (3),
+.BR el_new (3),
+.BR el_destroy (3),
.BR el_opmemory_table (3),
.BR el_ocleanup (3),
.BR el_ooption (3),
diff --git a/src/el-options.c b/src/el-options.c
index 9b30f5c..67d9548 100644
--- a/src/el-options.c
+++ b/src/el-options.c
@@ -38,6 +38,7 @@
#include "el-private.h"
#include <errno.h>
+#include <stdlib.h>
#include <string.h>
@@ -435,6 +436,36 @@ int el_oinit
/* ==========================================================================
+ Same as el_init but struct el is initialized on heap, so user does not
+ have to know anything about internal structure of 'el'.
+
+ errno
+ ENOMEM not enough memory in the system to allocate data
+ ========================================================================== */
+
+
+struct el *el_new
+(
+ void
+)
+{
+ struct el *el;
+ /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+
+
+ el = malloc(sizeof(*el));
+ if (el == NULL)
+ {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ el_oinit(el);
+ return el;
+}
+
+
+/* ==========================================================================
cleans up whatever has been initialized/reserved by el_init
========================================================================== */
@@ -481,6 +512,27 @@ int el_ocleanup
/* ==========================================================================
+ Same as el_ocleanup but can be used only with object created on heap
+ with el_new().
+ ========================================================================== */
+
+
+int el_destroy
+(
+ struct el *el /* el object */
+)
+{
+ if (el_ocleanup(el) != 0)
+ {
+ return -1;
+ }
+
+ free(el);
+ return 0;
+}
+
+
+/* ==========================================================================
checks wheter log print is allowed if not. Print is not allowed when log
level is no high enough or no output has been configured.
========================================================================== */
diff --git a/tst/test-el-file.c b/tst/test-el-file.c
index 852311b..da114fb 100644
--- a/tst/test-el-file.c
+++ b/tst/test-el-file.c
@@ -1263,6 +1263,32 @@ static void file_rotate_and_directory_reappear(void)
========================================================================== */
+static void file_multi_message_on_el_new(void)
+{
+ struct el *el;
+ /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+
+
+ mt_assert((el = el_new()) != NULL);
+ el_ooption(el, EL_OUT, EL_OUT_FILE);
+ el_ooption(el, EL_FSYNC_EVERY, 0);
+ el_ooption(el, EL_FROTATE_NUMBER, 0);
+ mt_fok(el_ooption(el, EL_FPATH, WORKDIR"/log"));
+
+ el_oputs(el, s9);
+ el_oputs(el, s8);
+ el_oputs(el, s5);
+ mt_fok(file_check(WORKDIR"/log", s9 s8 s5));
+
+ unlink(WORKDIR"/log");
+ mt_fok(el_destroy(el));
+}
+
+
+/* ==========================================================================
+ ========================================================================== */
+
+
static void file_no_dir_for_logs(void)
{
mt_ferr(el_option(EL_FPATH, WORKDIR"/i-dont/exist"), ENOENT);
@@ -1652,6 +1678,7 @@ void el_file_test_group(void)
mt_run(file_rotate_write_after_failed_open_to_existing_file_with_holes2);
mt_run(file_rotate_write_after_failed_open_to_existing_file_with_holes3);
mt_run(file_rotate_and_directory_reappear);
+ mt_run(file_multi_message_on_el_new);
mt_prepare_test = &test_prepare;
mt_cleanup_test = &test_cleanup;
diff --git a/tst/test-el-options.c b/tst/test-el-options.c
index 9c98f3e..09ad7a1 100644
--- a/tst/test-el-options.c
+++ b/tst/test-el-options.c
@@ -88,6 +88,7 @@ static void options_init(void)
{
struct el default_el; /* expected default el */
struct el el; /* custom el to init */
+ struct el *elp; /* heap allocated el */
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
@@ -122,6 +123,10 @@ static void options_init(void)
mt_fail(el_init() == 0);
mt_fail(memcmp(&g_el, &default_el, sizeof(default_el)) == 0);
mt_fail(el_cleanup() == 0);
+
+ mt_fail((elp = el_new()) != NULL);
+ mt_fail(memcmp(elp, &default_el, sizeof(*elp)) == 0);
+ mt_fok(el_destroy(elp));
}