diff options
author | Michał Łyszczek <michal.lyszczek@bofc.pl> | 2018-04-17 23:10:11 +0200 |
---|---|---|
committer | Michał Łyszczek <michal.lyszczek@bofc.pl> | 2018-04-17 23:10:11 +0200 |
commit | 021965eedc03dd17b98d56f2bb45cac9b391b7a2 (patch) | |
tree | 803806fbdd844bc1c7a17162a323ae3f39a12247 | |
parent | a34a7315472f825df64d553b69f30693a38a955e (diff) | |
download | embedlog-021965eedc03dd17b98d56f2bb45cac9b391b7a2.tar.gz embedlog-021965eedc03dd17b98d56f2bb45cac9b391b7a2.tar.bz2 embedlog-021965eedc03dd17b98d56f2bb45cac9b391b7a2.zip |
add: logging of binary data
-rw-r--r-- | configure.ac | 19 | ||||
-rw-r--r-- | embedlog-sources.mk | 7 | ||||
-rw-r--r-- | include/embedlog.h | 6 | ||||
-rw-r--r-- | man/el_print.3 | 119 | ||||
-rw-r--r-- | src/el-decode-number.c | 86 | ||||
-rw-r--r-- | src/el-encode-number.c | 112 | ||||
-rw-r--r-- | src/el-file.c | 54 | ||||
-rw-r--r-- | src/el-file.h | 15 | ||||
-rw-r--r-- | src/el-options.c | 11 | ||||
-rw-r--r-- | src/el-options.h | 18 | ||||
-rw-r--r-- | src/el-pbinary.c | 223 | ||||
-rw-r--r-- | src/el-perror.c | 3 | ||||
-rw-r--r-- | src/el-pmemory.c | 5 | ||||
-rw-r--r-- | src/el-print.c | 209 | ||||
-rw-r--r-- | src/el-private.h (renamed from src/config-priv.h) | 92 | ||||
-rw-r--r-- | src/el-puts.c | 77 | ||||
-rw-r--r-- | src/el-syslog.c | 9 | ||||
-rw-r--r-- | src/el-ts.c | 249 | ||||
-rw-r--r-- | src/el-tty.c | 4 | ||||
-rw-r--r-- | src/el-tty.h | 16 | ||||
-rw-r--r-- | tst/Makefile.am | 1 | ||||
l--------- | tst/el-decode-number.c | 1 | ||||
l--------- | tst/el-encode-number.c | 1 | ||||
l--------- | tst/el-pbinary.c | 1 | ||||
l--------- | tst/el-ts.c | 1 | ||||
-rw-r--r-- | tst/main.c | 2 | ||||
-rw-r--r-- | tst/test-el-file.c | 21 | ||||
-rw-r--r-- | tst/test-el-options.c | 3 | ||||
-rw-r--r-- | tst/test-el-pbinary.c | 670 | ||||
-rw-r--r-- | tst/test-el-print.c | 3 | ||||
-rw-r--r-- | tst/test-group-list.h | 1 |
31 files changed, 1703 insertions, 336 deletions
diff --git a/configure.ac b/configure.ac index 74694a4..30b7149 100644 --- a/configure.ac +++ b/configure.ac @@ -223,6 +223,24 @@ AS_IF([test "x$enable_clock" = "xyes"], ### +# --enable-binary +# + + +AC_ARG_ENABLE([binary-logs], + AS_HELP_STRING([--enable-binary-logs], [Enable printing binary logs]), + [], [enable_binary_logs="no"]) + +AM_CONDITIONAL([ENABLE_BINARY_LOGS], [test "x$enable_binary_logs" = "xyes"]) +AS_IF([test "x$enable_binary_logs" = "xyes"], +[ + AC_DEFINE([ENABLE_PREFIX], [1], [Enable printing binary logs]) + AC_CHECK_FUNCS([memcpy],, + AC_MSG_ERROR(not found, needed by --enable-binary-logs)) +]) + + +### # --enable-prefix # @@ -387,6 +405,7 @@ echo "clock() timestamp........... : $enable_clock" echo "print prefix with each msg.. : $enable_prefix" echo "print file info with log.... : $enable_finfo" echo "force portable snprintf......: $enable_portable_snprintf" +echo "binary logs..................: $enable_binary_logs" echo "colorize output............. : $enable_colors" echo "extended colors............. : $enable_colors_extended" echo "reentrant functions......... : $enable_reentrant" diff --git a/embedlog-sources.mk b/embedlog-sources.mk index eb779c0..591111e 100644 --- a/embedlog-sources.mk +++ b/embedlog-sources.mk @@ -3,6 +3,9 @@ embedlog_sources = el-options.c \ el-pmemory.c \ el-print.c \ el-puts.c \ + el-ts.c \ + el-decode-number.c \ + el-encode-number.c \ snprintf.c if ENABLE_OUT_FILE @@ -12,3 +15,7 @@ endif if ENABLE_OUT_TTY embedlog_sources += el-tty.c endif + +if ENABLE_BINARY_LOGS +embedlog_sources += el-pbinary.c +endif diff --git a/include/embedlog.h b/include/embedlog.h index 29cbba8..ad69196 100644 --- a/include/embedlog.h +++ b/include/embedlog.h @@ -170,7 +170,8 @@ int el_pmemory(const char *file, size_t line, enum el_level level, const void *memory, size_t mlen); int el_perror(const char *file, size_t line, enum el_level level, const char *fmt, ...); - +int el_putb(const void *memory, size_t mlen); +int el_pbinary(enum el_level level, const void *memory, size_t mlen); int el_oinit(struct el_options *options); int el_ocleanup(struct el_options *options); @@ -184,6 +185,9 @@ int el_opmemory(const char *file, size_t line, enum el_level level, struct el_options *options, const void *memory, size_t mlen); int el_operror(const char *file, size_t line, enum el_level level, struct el_options *options, const char *fmt, ...); +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); #ifdef __cplusplus } diff --git a/man/el_print.3 b/man/el_print.3 index e2d3a22..d15f696 100644 --- a/man/el_print.3 +++ b/man/el_print.3 @@ -13,6 +13,8 @@ .PP .BI "int el_puts(const char *" message ")" .br +.BI "int el_putb(const void *" memory ", size_t " mlen ")" +.br .BI "int el_print(const char *" file ", size_t " line ", \ enum el_level " level ", const char *" fmt ", " ... ")" .br @@ -24,9 +26,15 @@ enum el_level " level ", const char *" fmt ", " ... ")" .br .BI "int el_pmemory(const char *" file ", size_t " line ", \ enum el_level " level ", const void *" memory ", size_t " mlen ") +.br +.BI "int el_pbinary(enum el_level " level ", const void *" memory", \ +size_t " mlen ") .PP .BI "int el_oputs(struct el_options *" options ", const char *" message ")" .br +.BI "int el_putb(struct el_options *" options ", const void *" memory ", \ +size_t " mlen ")" +.br .BI "int el_oprint(const char *" file ", size_t " line ", \ enum el_level " level ", struct el_options *" options ", \ const char *" fmt ", " ... ")" @@ -42,6 +50,9 @@ const char *" fmt ", " ... ")" .BI "int el_pmemory(const char *" file ", size_t " line ", \ enum el_level " level ", struct el_options *" options ", \ const void *" memory ", size_t " mlen ")" +.br +.BI "int el_pbinary(enum el_level " level ", struct el_options *" options ", \ +const void *" memory ", size_t " mlen ")" .PP .BI "#define ELF " __FILE__ ", " __LINE__ ", " EL_FATAL .br @@ -70,7 +81,7 @@ __ISOC99_SOURCE .SH DESCRIPTION .PP Functions print log into configured outputs. - +.PP .BR el_puts (3) function prints .I message @@ -81,6 +92,23 @@ log nor timestamp is printed - even if they are enabled. Logs printed with this functions are not filtered by log level, and will always be printed. Prints can still be suppressed by disabling output. .PP +.BR el_putb (3) +function works same as +.BR el_puts (3) +but binary data can be passed. +That binary data will not be printed in ASCII like it it with +.BR el_pmemory (3). +If +.I memory +contains +.B NULL +characters, they will be stored to file. +If +.I memory +is for example +.B char buf[] = { 0xb0, 0xfc, 0x13, 0x37 } +then exactly these bytes will be stored into file. +.PP .BR el_print (3) function behaves similar to standard .BR printf () @@ -153,6 +181,95 @@ values. ------ ----------------------------------------------- ---------------- .EE .PP +.BR el_pbinary (3) +is used to log binary data into block device. +Both metadata and data itself are treated as binary data. +Such files cannot be read with text tools like +.BR less , +but rather needs special program that will parse the file. +This may be usefull when you need, for example to log every message on CAN bus. +If there are a lot of messages, it may be better to store them in binary format +as this may save *a lot* of space. +.PP +.BR el_pbinary (3) +uses specific format to store metadata to save as much space as possible. +All numerical values in metadata are variadic in size. Order of fields are as +follows: +.PP +.EX + +-------+------------+--------------+-------------+------+ + | flags | ts_seconds | ts_fractions | data_length | data | + +-------+------------+--------------+-------------+------+ +.EE +.PP +The only mandatory fields are +.BR flags ", " data_length " and " data . +.B flags +determin what fields are present. +.B flags +field is always 1 byte in size and its format is +.PP +.EX + +------+-------+-----------------------------------------------------------+ + | bits | value | description | + +------+-------+-----------------------------------------------------------+ + | 0 | 0 | both ts_seconds and ts_fraction will not appear | + | +-------+-----------------------------------------------------------+ + | | 1 | at least ts_seconds will appear, ts_fraction appearance | + | | | depends on 1..2 bits values | + +------+-------+-----------------------------------------------------------+ + | 1..2 | 0 | ts_fractions will not appear | + | +-------+-----------------------------------------------------------+ + | | 1 | ts_fractions will hold milliseconds value | + | +-------+-----------------------------------------------------------+ + | | 2 | ts_fractions will hold microseconds value | + | +-------+-----------------------------------------------------------+ + | | 3 | ts_fractions will hold nanoseconds value | + +------+-------+-----------------------------------------------------------+ + | 3..5 | 0..7 | severity of the log, 0 is the highest and 7 is the lowest | + +------+-------+-----------------------------------------------------------+ + | 6..7 | 0..3 | reserved | + +------+-------+-----------------------------------------------------------+ +.EE +.PP +.BR ts_seconds ", " ts_fractions " and " data_length +are numerical values with dynamic size. +Each byte of a numerical value can hold number up to 127 and oldest bit is used +as continuation bit, if that bit is set, program should treat next byte as next +part of the same numerical value. +Below is table with example decimal values and it's encoded counterpart. +.PP +.EX + +---------------+--------------------------+ + | decimal value | encoded hex value | + +---------------+--------------------------+ + | 0 | 0x00 | + | 1 | 0x01 | + | 2 | 0x02 | + | 127 | 0x7f | + | 128 | 0x80 0x81 | + | 129 | 0x81 0x01 | + | 255 | 0xff 0x01 | + | 256 | 0x80 0x02 | + | 257 | 0x81 0x02 | + | 16383 | 0xff 0x7f | + | 16384 | 0x80 0x80 0x01 | + | 16385 | 0x81 0x80 0x01 | + | 438478374 | 0xa6 0xcc 0x8a 0xd1 0x01 | + | 2147483647 | 0xff 0xff 0xff 0xff 0x07 | + | 4294967295 | 0xff 0xff 0xff 0xff 0x0f | + +---------------+--------------------------+ +.EE +.PP +Encoded number are always +.BR little-endian , +that is first byte is always the least significant byte. +.PP +.B data +is be whatever you want of any size. +.BR el_pbinary (3) +uses only timestamp and log level, rest of the options are simply ignores. +.PP .BR el_print (3), .BR el_vprint (3), .BR el_perror (3) diff --git a/src/el-decode-number.c b/src/el-decode-number.c new file mode 100644 index 0000000..5a8be82 --- /dev/null +++ b/src/el-decode-number.c @@ -0,0 +1,86 @@ +/* ========================================================================== + Licensed under BSD 2clause license See LICENSE file for more information + Author: Michał Łyszczek <michal.lyszczek@bofc.pl> + ========================================================================== + _ __ __ ____ _ __ + (_)____ _____ / /__ __ ____/ /___ / __/(_)/ /___ _____ + / // __ \ / ___// // / / // __ // _ \ / /_ / // // _ \ / ___/ + / // / / // /__ / // /_/ // /_/ // __/ / __// // // __/(__ ) + /_//_/ /_/ \___//_/ \__,_/ \__,_/ \___/ /_/ /_//_/ \___//____/ + + ========================================================================== */ + + +#include <limits.h> + +#include "el-private.h" + + +/* ========================================================================== + __ __ _ + ____ __ __ / /_ / /(_)_____ + / __ \ / / / // __ \ / // // ___/ + / /_/ // /_/ // /_/ // // // /__ + / .___/ \__,_//_.___//_//_/ \___/ + /_/ + ____ __ _ + / __/__ __ ____ _____ / /_ (_)____ ____ _____ + / /_ / / / // __ \ / ___// __// // __ \ / __ \ / ___/ + / __// /_/ // / / // /__ / /_ / // /_/ // / / /(__ ) + /_/ \__,_//_/ /_/ \___/ \__//_/ \____//_/ /_//____/ + + ========================================================================== */ + + +/* ========================================================================== + Decodes encoded 'number' and stores value into 'out'. Check description + of el_encode_number for more information. + + returns number of bytes processed from 'number' + ========================================================================== */ + + +size_t el_decode_number +( + const void *number, /* number to decode */ +#ifdef LLONG_MAX + unsigned long long *out /* decoded number */ +#else + unsigned long *out /* decoded number */ +#endif +) +{ + const unsigned char *n; /* just a 'number' as unsigned cahr */ + size_t i; /* temporary index */ + /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + + + i = 0; + *out = 0; + n = number; + + do + { + /* + * multiple stuff is happening in this one-liner + * + * - first we take 7 bits of number + * - calculate weigth of current number + * - set current number into right position of out + */ + + *out |= (n[i] & 0x7f) << (i * 7); + + /* + * we do this until number lacks of continuation bit, which means + * we are done + */ + } + while (n[i++] & 0x80); + + /* + * return number of bytes processed from 'number' + */ + + return i; +} diff --git a/src/el-encode-number.c b/src/el-encode-number.c new file mode 100644 index 0000000..82a81ac --- /dev/null +++ b/src/el-encode-number.c @@ -0,0 +1,112 @@ +/* ========================================================================== + Licensed under BSD 2clause license See LICENSE file for more information + Author: Michał Łyszczek <michal.lyszczek@bofc.pl> + ========================================================================== + _ __ __ ____ _ __ + (_)____ _____ / /__ __ ____/ /___ / __/(_)/ /___ _____ + / // __ \ / ___// // / / // __ // _ \ / /_ / // // _ \ / ___/ + / // / / // /__ / // /_/ // /_/ // __/ / __// // // __/(__ ) + /_//_/ /_/ \___//_/ \__,_/ \__,_/ \___/ /_/ /_//_/ \___//____/ + + ========================================================================== */ + + +#include <limits.h> + +#include "el-private.h" + + +/* ========================================================================== + __ __ _ + ____ __ __ / /_ / /(_)_____ + / __ \ / / / // __ \ / // // ___/ + / /_/ // /_/ // /_/ // // // /__ + / .___/ \__,_//_.___//_//_/ \___/ + /_/ + ____ __ _ + / __/__ __ ____ _____ / /_ (_)____ ____ _____ + / /_ / / / // __ \ / ___// __// // __ \ / __ \ / ___/ + / __// /_/ // / / // /__ / /_ / // /_/ // / / /(__ ) + /_/ \__,_//_/ /_/ \___/ \__//_/ \____//_/ /_//____/ + + ========================================================================== */ + + +/* ========================================================================== + This will code 'number' to into as small buffer as possible. Each byte + of encoded value can hold 7 bits of data and 1 bit (oldest) of + continuation information. If continuation bit is set, that means next + byte contains next 7bit of number information. This allows us to store + value up to 127 in one byte and values up to 16383 in two bytes and so + on. 'out' must be long enough to hold enough encoded bytes or undefined + behaviour will occur. + + returns number of bytes stored into 'out' buffer. + ========================================================================== */ + + +size_t el_encode_number +( +#ifdef LLONG_MAX + unsigned long long number, /* value to encode */ +#else + unsigned long number, /* value to encode */ +#endif + void *out /* memory where encoded value will be set */ +) +{ + unsigned char *o; /* just 'out' as unsigned char */ + size_t n; /* number of bytes stored in 'out' */ + /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + + o = out; + n = 0; + + do + { + /* + * put only youngest 7 bits into out - that's a max number single + * byte can hold + */ + + o[n] = number & 0x7f; + + /* + * remove those 7 bits from number, they are used and no longer + * needed + */ + + number >>= 7; + + /* + * if we didn't process whole number, set oldest bit to 1, this is + * continuation bit, it will tell decoder that next bytes will hold + * next 7bits of number + */ + + if (number) + { + o[n] |= 0x80; + } + + /* + * increment n to indicate we store a byte in 'out' buffer + */ + + ++n; + + /* + * do this until there is anyting else left in number + */ + } + while (number); + + /* + * after job's done, return number of bytes stored in 'out' + */ + + return n; +} + + + diff --git a/src/el-file.c b/src/el-file.c index 80bb3bd..8c051e7 100644 --- a/src/el-file.c +++ b/src/el-file.c @@ -42,7 +42,7 @@ # include "config.h" #endif -#include "el-options.h" +#include "el-private.h" #if HAVE_UNISTD_H # include <unistd.h> @@ -545,16 +545,14 @@ int el_file_open ========================================================================== */ -int el_file_puts +int el_file_putb ( - struct el_options *options, /* printing options */ - const char *s /* string to 'put' into file */ + struct el_options *options, /* printing options */ + const void *mem, /* memory to 'put' into file */ + size_t mlen /* size of buffer 'mlen' */ + ) { - int sl; /* size of the s message */ - /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ - - if (options->current_log && options->current_log[0] == '\0') { /* @@ -565,11 +563,9 @@ int el_file_puts return -1; } - sl = strlen(s); - if (options->frotate_number) { - if (options->fpos != 0 && options->fpos + sl > options->frotate_size) + if (options->fpos != 0 && options->fpos + mlen > options->frotate_size) { /* * we get here only when frotate is enabled, and writing to @@ -585,7 +581,7 @@ int el_file_puts } } - if (sl > options->frotate_size) + if (mlen > options->frotate_size) { /* * we can't fit message even in an empty file, in such case we @@ -593,7 +589,7 @@ int el_file_puts * configured frotate_size */ - sl = options->frotate_size; + mlen = options->frotate_size; } } @@ -622,13 +618,13 @@ int el_file_puts options->fpos = 0; } - if (fwrite(s, sl, 1, options->file) != 1) + if (fwrite(mem, mlen, 1, options->file) != 1) { return -1; } - options->fpos += sl; - options->written_after_sync += sl; + options->fpos += mlen; + options->written_after_sync += mlen; if (options->written_after_sync >= options->file_sync_every || options->level_current_msg <= options->file_sync_level) @@ -647,7 +643,7 @@ int el_file_puts * 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 prying. + * isn't really much we can do about it here but praying. */ #if HAVE_FSYNC && HAVE_FILENO @@ -681,7 +677,7 @@ int el_file_puts #else /* HAVE_FSYNC && HAVE_FILENO */ /* * if system does not implement fileno and fsync our only hope lies - * int closing (which should sync file to block device) and then + * in closing (which should sync file to block device) and then * opening file again. Yup, performance will suck, but hey, its * about data safety! */ @@ -693,11 +689,11 @@ int el_file_puts errno = EBADF; return -1; } +#endif /* HAVE_FSYNC && HAVE_FILENO */ #ifdef RUN_TESTS file_synced = 1; #endif /* RUN_TESTS */ -#endif /* HAVE_FSYNC && HAVE_FILENO */ options->written_after_sync = 0; } @@ -707,6 +703,26 @@ int el_file_puts /* ========================================================================== + Puts string 's' into file if needed rotates file + ========================================================================== */ + + +int el_file_puts +( + struct el_options *options, /* printing options */ + const char *s /* string to 'put' into file */ +) +{ + size_t slen; /* size of string 's' */ + /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + + + slen = strlen(s); + return el_file_putb(options, (unsigned char *)s, slen); +} + + +/* ========================================================================== free resources allocated by this module ========================================================================== */ diff --git a/src/el-file.h b/src/el-file.h deleted file mode 100644 index 42687b6..0000000 --- a/src/el-file.h +++ /dev/null @@ -1,15 +0,0 @@ -/* ========================================================================== - Licensed under BSD 2clause license See LICENSE file for more information - Author: Michał Łyszczek <michal.lyszczek@bofc.pl> - ========================================================================== */ - -#ifndef EL_FILE_H -#define EL_FILE_H 1 - -#include "el-options.h" - -int el_file_open(struct el_options *); -int el_file_puts(struct el_options *, const char *); -void el_file_cleanup(struct el_options *); - -#endif diff --git a/src/el-options.c b/src/el-options.c index a43dd89..a4ba538 100644 --- a/src/el-options.c +++ b/src/el-options.c @@ -39,20 +39,11 @@ #include "config.h" #endif -#include "el-options.h" -#include "embedlog.h" -#include "valid.h" +#include "el-private.h" #include <errno.h> #include <string.h> -#if ENABLE_OUT_FILE -#include "el-file.h" -#endif - -#if ENABLE_OUT_TTY -#include "el-tty.h" -#endif /* ========================================================================== __ __ __ diff --git a/src/el-options.h b/src/el-options.h deleted file mode 100644 index 3149615..0000000 --- a/src/el-options.h +++ /dev/null @@ -1,18 +0,0 @@ -/* ========================================================================== - Licensed under BSD 2clause license. See LICENSE file for more information - Author: Michał Łyszczek <michal.lyszczek@bofc.pl> - ========================================================================== */ - - -#ifndef EL_OPTIONS_H -#define EL_OPTIONS_H 1 - - -#include "embedlog.h" - - -extern struct el_options g_options; - -int el_log_allowed(struct el_options *, enum el_level); - -#endif diff --git a/src/el-pbinary.c b/src/el-pbinary.c new file mode 100644 index 0000000..a8f967b --- /dev/null +++ b/src/el-pbinary.c @@ -0,0 +1,223 @@ +/* ========================================================================== + Licensed under BSD 2clause license See LICENSE file for more information + Author: Michał Łyszczek <michal.lyszczek@bofc.pl> + ========================================================================== + ------------------------------------------------------------- + / Module reponsible for printing binary logs. And by binary I \ + | don't mean only metadata and logs in ASCII, but full binary | + | including data. This may be useful when one need to log ie. | + | every message it saw on CAN. It would be a waste of space | + | to log it in ASCII when binary data can be less by an order | + \ of magnitude / + ------------------------------------------------------------- + \ + \ , _ ___.--'''`--''//-,-_--_. + \`"' ` || \\ \ \\/ / // / ,-\\`,_ + /'` \ \ || Y | \|/ / // / - |__ `-, + /@"\ ` \ `\ | | ||/ // | \/ \ `-._`-,_., + / _.-. `.-\,___/\ _/|_/_\_\/|_/ | `-._._) + `-'``/ / | // \__/\__ / \__/ \ + `-' /-\/ | -| \__ \ |-' | + __/\ / _/ \/ __,-' ) ,' _|' + (((__/(((_.' ((___..-'((__,' + ========================================================================== */ + + +/* ========================================================================== + _ __ __ ____ _ __ + (_)____ _____ / /__ __ ____/ /___ / __/(_)/ /___ _____ + / // __ \ / ___// // / / // __ // _ \ / /_ / // // _ \ / ___/ + / // / / // /__ / // /_/ // /_/ // __/ / __// // // __/(__ ) + /_//_/ /_/ \___//_/ \__,_/ \__,_/ \___/ /_/ /_//_/ \___//____/ + + ========================================================================== */ + + +#if HAVE_CONFIG_H +# include "config.h" +#endif + +#include "el-private.h" + +#include <errno.h> +#include <string.h> +#include <time.h> + + +/* ========================================================================== + _ __ __ + ____ _____ (_)_ __ ____ _ / /_ ___ / /_ __ __ ____ ___ _____ + / __ \ / ___// /| | / // __ `// __// _ \ / __// / / // __ \ / _ \ / ___/ + / /_/ // / / / | |/ // /_/ // /_ / __/ / /_ / /_/ // /_/ // __/(__ ) + / .___//_/ /_/ |___/ \__,_/ \__/ \___/ \__/ \__, // .___/ \___//____/ +/_/ /____//_/ + ========================================================================== */ + + +#define FLAG_TS (0x01) + +#define FLAG_TS_FRACT_NONE (0x00) +#define FLAG_TS_FRACT_NSEC (0x01) +#define FLAG_TS_FRACT_USEC (0x02) +#define FLAG_TS_FRACT_MSEC (0x03) +#define FLAG_TS_FRACT_MASK (0x03) +#define FLAG_TS_FRACT_SHIFT (1) + +#define FLAG_LEVEL_MASK (0x07) +#define FLAG_LEVEL_SHIFT (3) + + +/* ========================================================================== + _ __ + ____ _____ (_)_ __ ____ _ / /_ ___ + / __ \ / ___// /| | / // __ `// __// _ \ + / /_/ // / / / | |/ // /_/ // /_ / __/ + / .___//_/ /_/ |___/ \__,_/ \__/ \___/ + /_/ + ____ __ _ + / __/__ __ ____ _____ / /_ (_)____ ____ _____ + / /_ / / / // __ \ / ___// __// // __ \ / __ \ / ___/ + / __// /_/ // / / // /__ / /_ / // /_/ // / / /(__ ) + /_/ \__,_//_/ /_/ \___/ \__//_/ \____//_/ /_//____/ + + ========================================================================== */ + + +/* ========================================================================== + Puts flags with log information to buf. Flags is single byte with fields + + bit 0 timestamp enabled/disabled + bit 1-2 fraction timestamp (off, nsec, usec, msec) + bit 3-5 severity of the log + bit 6-7 reserved + ========================================================================== */ + + +static size_t el_flags +( + enum el_level level, + struct el_options *options, + unsigned char *buf +) +{ + *buf = 0; + + if (options->timestamp != EL_TS_OFF) + { + *buf |= FLAG_TS; + + /* + * fraction of seconds can be printed only when timestamp is on + */ + + if (options->timestamp_useconds) + { + *buf |= FLAG_TS_FRACT_USEC << FLAG_TS_FRACT_SHIFT; + } + } + + *buf |= level << FLAG_LEVEL_SHIFT; + + return 1; +} + + +/* ========================================================================== + __ __ _ + ____ __ __ / /_ / /(_)_____ + / __ \ / / / // __ \ / // // ___/ + / /_/ // /_/ // /_/ // // // /__ + / .___/ \__,_//_.___//_//_/ \___/ + /_/ + ____ __ _ + / __/__ __ ____ _____ / /_ (_)____ ____ _____ + / /_ / / / // __ \ / ___// __// // __ \ / __ \ / ___/ + / __// /_/ // / / // /__ / /_ / // /_/ // / / /(__ ) + /_/ \__,_//_/ /_/ \___/ \__//_/ \____//_/ /_//____/ + + ========================================================================== */ + + +int el_opbinary +( + enum el_level level, + struct el_options *options, + const void *memory, + size_t mlen +) +{ + unsigned char buf[EL_BUF_MAX]; /* buffer for message to print */ + size_t l; /* length of encoded mlen */ + size_t w; /* bytes written to buf */ + int e; /* cache for errno */ + /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + + + VALID(EINVAL, mlen); + VALID(EINVAL, memory); + VALID(EINVAL, options); + VALID(ENODEV, options->outputs); + VALID(ERANGE, el_log_allowed(options, level)); + + e = 0; + w = el_flags(level, options, buf); + w += el_timestamp(options, buf + w, TS_BINARY); + + /* + * encode mlen to know how much bytes we are going to need + */ + + l = el_encode_number(mlen, buf + w); + + if (w + l + mlen > sizeof(buf)) + { + /* + * user tries to print more that than we can hold in our buffer, + * buffer overflow attack? Not going to happen! We truncate it. + */ + + mlen = EL_BUF_MAX - w - l; + e = ENOBUFS; + } + + /* + * know that we know real value of mlen, we can encode mlen again + */ + + w += el_encode_number(mlen, buf + w); + memcpy(buf + w, memory, mlen); + options->level_current_msg = level; + w += mlen; + + if (el_oputb(options, buf, w) != 0) + { + options->level_current_msg = EL_DBG; + return -1; + } + + options->level_current_msg = EL_DBG; + + if (e) + { + errno = e; + return -1; + } + + return 0; +} + + +/* ========================================================================== + Same as el_opbinary but uses global options object + ========================================================================== */ + + +int el_pbinary +( + enum el_level level, + const void *memory, + size_t mlen +) +{ + return el_opbinary(level, &g_options, memory, mlen); +} diff --git a/src/el-perror.c b/src/el-perror.c index 4b7ccb7..b761c08 100644 --- a/src/el-perror.c +++ b/src/el-perror.c @@ -28,8 +28,7 @@ #include "config.h" #endif -#include "el-options.h" -#include "embedlog.h" +#include "el-private.h" #include <errno.h> #include <stdarg.h> diff --git a/src/el-pmemory.c b/src/el-pmemory.c index c67650a..408086d 100644 --- a/src/el-pmemory.c +++ b/src/el-pmemory.c @@ -42,10 +42,7 @@ #include "config.h" #endif -#include "config-priv.h" -#include "el-options.h" -#include "embedlog.h" -#include "valid.h" +#include "el-private.h" #include <ctype.h> #include <stdio.h> diff --git a/src/el-print.c b/src/el-print.c index c513e3a..6f08c74 100644 --- a/src/el-print.c +++ b/src/el-print.c @@ -48,14 +48,7 @@ #include "config.h" #endif -#include "config-priv.h" -#include "el-options.h" -#include "embedlog.h" -#include "valid.h" - -#if ENABLE_TIMESTAMP -#include <time.h> -#endif +#include "el-private.h" #include <errno.h> #include <stdarg.h> @@ -189,203 +182,6 @@ static size_t el_color /* ========================================================================== - returns seconds and microseconds calculated from clock() function - ========================================================================== */ - - -#if ENABLE_TIMESTAMP - -#if ENABLE_CLOCK - -static void el_ts_clock -( - time_t *s, /* seconds will be stored here */ - long *us /* microseconds will be stored here */ -) -{ - clock_t clk; /* clock value */ - /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ - - clk = clock(); - - *s = clk / CLOCKS_PER_SEC; - *us = clk % CLOCKS_PER_SEC; - *us *= 1000000 / CLOCKS_PER_SEC; -} - -#endif /* ENABLE_CLOCK */ - -/* ========================================================================== - returns seconds and microseconds calculated from time() function. - ========================================================================== */ - - -static void el_ts_time -( - time_t *s, /* seconds will be stored here */ - long *us /* microseconds will be stored here */ -) -{ - *s = (long)time(NULL); - *us = 0; -} - - -/* ========================================================================== - returns seconds and microseconds calculated from clock_gettime function - ========================================================================== */ - - -#if ENABLE_REALTIME || ENABLE_MONOTONIC - -static void el_ts_clock_gettime -( - time_t *s, /* seconds will be stored here */ - long *us, /* microseconds will be stored here */ - clockid_t clkid /* clock id */ -) -{ - struct timespec tp; /* clock value */ - /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ - - clock_gettime(clkid, &tp); - - *s = tp.tv_sec; - *us = tp.tv_nsec / 1000; -} - -#endif - -#endif /* ENABLE_TIMESTAMP */ - - -/* ========================================================================== - returns current timestamp in 'buf' location. Depending on global - settings timestamp can be in long or short format. Function will return - number of bytes copied to 'buf'. If timestamp has been disabled during - compilation time or in runtime during settings, function will return 0. - ========================================================================== */ - - -static size_t el_timestamp -( - struct el_options *options, /* options defining printing style */ - char *buf /* buffer where timestamp will be stored */ -) -{ -#if ENABLE_TIMESTAMP - time_t s; /* timestamp seconds */ - long us; /* timestamp microseconds */ - size_t tl; /* timestamp length */ - /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ - - if (options->timestamp == EL_TS_OFF) - { - /* - * user doesn't want us to print timestamp, that's fine - */ - - return 0; - } - - /* - * first we get seconds and microseconds from proper timer - */ - - switch (options->timestamp_timer) - { -#if ENABLE_REALTIME - - case EL_TS_TM_REALTIME: - el_ts_clock_gettime(&s, &us, CLOCK_REALTIME); - break; - -#endif - -#if ENABLE_MONOTONIC - - case EL_TS_TM_MONOTONIC: - el_ts_clock_gettime(&s, &us, CLOCK_MONOTONIC); - break; - -#endif - - case EL_TS_TM_TIME: - el_ts_time(&s, &us); - break; - -#if ENABLE_CLOCK - - case EL_TS_TM_CLOCK: - el_ts_clock(&s, &us); - break; - -#endif - - default: - /* - * bad timer means no timer - */ - - return 0; - } - - /* - * then convert retrieved time into string timestamp - */ - - if (options->timestamp == EL_TS_LONG) - { - struct tm tm; /* timestamp splitted */ - struct tm *tmp; /* timestamp splitted pointer */ - /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ - -# if ENABLE_REENTRANT - tmp = gmtime_r(&s, &tm); -# else - tmp = gmtime(&s); -# endif - - tl = strftime(buf, 21, "[%Y-%m-%d %H:%M:%S", tmp); - } - else - { -#ifdef LLONG_MAX - tl = sprintf(buf, "[%lld", (long long)s); -#else - /* - * if long long is not available, code may be suscible to 2038 bug. - * If you are sure your compiler does support long long type, but - * doesn't define LLONG_MAX, define this value yourself to enable - * long long. - */ - tl = sprintf(buf, "[%ld", (long)s); -#endif - } - - if (options->timestamp_useconds) - { - tl += sprintf(buf + tl, ".%06ld]", us); - } - else - { - /* - * if micro seconds are not printed we simply add ending ']' - */ - - buf[tl] = ']'; - tl++; - } - - return tl; - -#else - return 0; -#endif /* ENABLE_TIMESTAMP */ -} - - -/* ========================================================================== returns pointer to where basename of 's' starts Examples: @@ -613,7 +409,7 @@ int el_ovprint buf[0] = '\0'; w = el_color(options, buf, level); - w += el_timestamp(options, buf + w); + w += el_timestamp(options, buf + w, TS_STRING); w += el_finfo(options, buf + w, file, num); if (w != 0 && buf[w - 1] == ']') @@ -727,3 +523,4 @@ int el_ovprint return 0; } + diff --git a/src/config-priv.h b/src/el-private.h index aba78ff..54c4a96 100644 --- a/src/config-priv.h +++ b/src/el-private.h @@ -1,12 +1,14 @@ /* ========================================================================== - Licensed under BSD 2clause license. See LICENSE file for more information + Licensed under BSD 2clause license See LICENSE file for more information Author: Michał Łyszczek <michal.lyszczek@bofc.pl> ========================================================================== */ +/* + * this file contains stuff internal to the library + */ -#ifndef EL_CONFIGPRIV_H -#define EL_CONFIGPRIV_H 1 - +#ifndef EL_PRIVATE_H +#define EL_PRIVATE_H 1 /* ========================================================================== _ __ __ ____ _ __ @@ -17,20 +19,47 @@ ========================================================================== */ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "embedlog.h" +#include "valid.h" -#if HAVE_CONFIG_H -#include "config.h" +#ifdef HAVE_TERMIOS_H +# include <termios.h> #endif +#include <limits.h> -/* ==== library private macros ============================================== */ +/* ========================================================================== + __ __ __ + ____ _ / /____ / /_ ____ _ / / _ __ ____ _ _____ _____ + / __ `// // __ \ / __ \ / __ `// / | | / // __ `// ___// ___/ + / /_/ // // /_/ // /_/ // /_/ // / | |/ // /_/ // / (__ ) + \__, //_/ \____//_.___/ \__,_//_/ |___/ \__,_//_/ /____/ + /____/ + ========================================================================== */ -/* ==== defines for el_mprint function ====================================== */ +extern struct el_options g_options; /* ========================================================================== - single line of el_print_mem will be similar to this + __ __ + _____ ____ ____ _____ / /_ ____ _ ____ / /_ _____ + / ___// __ \ / __ \ / ___// __// __ `// __ \ / __// ___/ + / /__ / /_/ // / / /(__ )/ /_ / /_/ // / / // /_ (__ ) + \___/ \____//_/ /_//____/ \__/ \__,_//_/ /_/ \__//____/ + + ========================================================================== */ + + +/* ==== defines for el_pmemory function ===================================== */ + + +/* ========================================================================== + single line of el_pmemory will be similar to this 0xNNNN HH HH HH HH HH HH HH HH HH HH HH HH HH HH HH HH CCCCCCCCCCCCCCC ========================================================================== */ @@ -182,4 +211,49 @@ #define EL_BUF_MAX (EL_PRE_LEN + (EL_LOG_MAX) + EL_COLORS_LEN) +/* ========================================================================== + Defines if timestamp should be stored into buffer as string or binary + ========================================================================== */ + + +#define TS_STRING (0) +#define TS_BINARY (1) + + +/* ========================================================================== + ____ __ _ + / __/__ __ ____ _____ / /_ (_)____ ____ _____ + / /_ / / / // __ \ / ___// __// // __ \ / __ \ / ___/ + / __// /_/ // / / // /__ / /_ / // /_/ // / / /(__ ) + /_/ \__,_//_/ /_/ \___/ \__//_/ \____//_/ /_//____/ + + ========================================================================== */ + + +#ifdef ENABLE_OUT_FILE +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); +#endif + + +#ifdef ENABLE_OUT_TTY +int el_tty_open(struct el_options *options, const char *dev, speed_t speed); +int el_tty_puts(struct el_options *options, const char *s); +int el_tty_close(struct el_options *options); +#endif + +int el_log_allowed(struct el_options *options, enum el_level level); +size_t el_timestamp(struct el_options *options, char *buf, int binary); + +#ifdef LLONG_MAX +size_t el_encode_number(unsigned long long number, void *out); +size_t el_decode_number(const void *number, unsigned long long *out); +#else +size_t el_encode_number(unsigned long number, void *out); +size_t el_decode_number(const void *number, unsigned long *out); +#endif + + #endif diff --git a/src/el-puts.c b/src/el-puts.c index 97e90f5..9ba9d1e 100644 --- a/src/el-puts.c +++ b/src/el-puts.c @@ -34,20 +34,11 @@ #include "config.h" #endif +#include "el-private.h" + #include <errno.h> #include <stdio.h> -#include "el-options.h" -#include "embedlog.h" -#include "valid.h" - -#if ENABLE_OUT_FILE -#include "el-file.h" -#endif - -#if ENABLE_OUT_TTY -#include "el-tty.h" -#endif /* ========================================================================== __ __ _ @@ -144,3 +135,67 @@ int el_oputs return rv; } + + +/* ========================================================================== + puts memory 'mem' to all supported output facilities specified by + default options. Not all outputs support printing binary data + ========================================================================== */ + + +int el_putb +( + const void *mem, /* memory location to 'print' */ + size_t mlen /* size of the mem buffer */ +) +{ + return el_oputb(&g_options, mem, mlen); +} + + +/* ========================================================================== + Puts binary data 'mem' of size 'mlen' to enabled output facilities + specified by 'options'. No all outputs support printing binary data + ========================================================================== */ + + +int el_oputb +( + struct el_options *options, /* options defining printing style */ + const void *mem, /* memory location to 'print' */ + size_t mlen /* size of the mem buffer */ +) +{ + int rv; /* return value from function */ + int called; /* at least one output was called */ + /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + + + VALID(EINVAL, options); + VALID(EINVAL, mem); + VALID(EINVAL, mlen); + VALID(ENODEV, options->outputs != 0); + + rv = 0; + called = 0; + +#if ENABLE_OUT_FILE + if (options->outputs & EL_OUT_FILE) + { + called = 1; + rv |= el_file_putb(options, mem, mlen); + } +#endif + + if (called == 0) + { + /* + * couldn't find any supported output for binary logging + */ + + errno = ENODEV; + return -1; + } + + return rv; +} diff --git a/src/el-syslog.c b/src/el-syslog.c index c222a40..259beb1 100644 --- a/src/el-syslog.c +++ b/src/el-syslog.c @@ -15,12 +15,10 @@ #if HAVE_CONFIG_H -#include "config.h" +# include "config.h" #endif -#include "el-options.h" -#include "el-tty.h" -#include "valid.h" +#include "el-private.h" #include <errno.h> #include <fcntl.h> @@ -28,9 +26,10 @@ #include <sys/types.h> #include <unistd.h> #if HAVE_TERMIOS_H -#include <termios.h> +# include <termios.h> #endif + /* ========================================================================== __ __ _ ____ __ __ / /_ / /(_)_____ diff --git a/src/el-ts.c b/src/el-ts.c new file mode 100644 index 0000000..949a39d --- /dev/null +++ b/src/el-ts.c @@ -0,0 +1,249 @@ +/* ========================================================================== + Licensed under BSD 2clause license See LICENSE file for more information + Author: Michał Łyszczek <michal.lyszczek@bofc.pl> + ========================================================================== */ + + +/* ========================================================================== + _ __ __ ____ _ __ + (_)____ _____ / /__ __ ____/ /___ / __/(_)/ /___ _____ + / // __ \ / ___// // / / // __ // _ \ / /_ / // // _ \ / ___/ + / // / / // /__ / // /_/ // /_/ // __/ / __// // // __/(__ ) + /_//_/ /_/ \___//_/ \__,_/ \__,_/ \___/ /_/ /_//_/ \___//____/ + + ========================================================================== */ + + +#if HAVE_CONFIG_H +# include "config.h" +#endif + +#include "el-private.h" + +#include <time.h> + + +/* ========================================================================== + returns seconds and microseconds calculated from clock() function + ========================================================================== */ + + +#if ENABLE_TIMESTAMP +#if ENABLE_CLOCK +static void el_ts_clock +( + time_t *s, /* seconds will be stored here */ + long *us /* microseconds will be stored here */ +) +{ + clock_t clk; /* clock value */ + /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + + clk = clock(); + + *s = clk / CLOCKS_PER_SEC; + *us = clk % CLOCKS_PER_SEC; + *us *= 1000000 / CLOCKS_PER_SEC; +} +#endif /* ENABLE_CLOCK */ +#endif /* ENABLE_TIMESTAMP */ + +/* ========================================================================== + returns seconds and microseconds calculated from time() function. + ========================================================================== */ + + +#if ENABLE_TIMESTAMP +static void el_ts_time +( + time_t *s, /* seconds will be stored here */ + long *us /* microseconds will be stored here */ +) +{ + *s = (long)time(NULL); + *us = 0; +} +#endif /* ENABLE_TIMESTAMP */ + +/* ========================================================================== + returns seconds and microseconds calculated from clock_gettime function + ========================================================================== */ + + +#if ENABLE_TIMESTAMP +#if ENABLE_REALTIME || ENABLE_MONOTONIC +static void el_ts_clock_gettime +( + time_t *s, /* seconds will be stored here */ + long *us, /* microseconds will be stored here */ + clockid_t clkid /* clock id */ +) +{ + struct timespec tp; /* clock value */ + /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + + clock_gettime(clkid, &tp); + + *s = tp.tv_sec; + *us = tp.tv_nsec / 1000; +} +#endif /* ENABLE_REALTIME || ENABLE_MONOTONIC */ +#endif /* ENABLE_TIMESTAMP */ + + +/* ========================================================================== + returns current timestamp in 'buf' location. Depending on global + settings timestamp can be in long or short format. Function will return + number of bytes copied to 'buf'. If timestamp has been disabled during + compilation time or in runtime during settings, function will return 0. + ========================================================================== */ + + +size_t el_timestamp +( + struct el_options *options, /* options defining printing style */ + char *buf, /* buffer where timestamp will be stored */ + int binary /* 1 if timestamp should be binary */ +) +{ +#if ENABLE_TIMESTAMP + time_t s; /* timestamp seconds */ + long us; /* timestamp microseconds */ + size_t tl; /* timestamp length */ + /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + + if (options->timestamp == EL_TS_OFF) + { + /* + * user doesn't want us to print timestamp, that's fine + */ + + return 0; + } + + /* + * first we get seconds and microseconds from proper timer + */ + + switch (options->timestamp_timer) + { +# if ENABLE_REALTIME + + case EL_TS_TM_REALTIME: + el_ts_clock_gettime(&s, &us, CLOCK_REALTIME); + break; + +# endif + +# if ENABLE_MONOTONIC + + case EL_TS_TM_MONOTONIC: + el_ts_clock_gettime(&s, &us, CLOCK_MONOTONIC); + break; + +# endif + + case EL_TS_TM_TIME: + el_ts_time(&s, &us); + break; + +# if ENABLE_CLOCK + + case EL_TS_TM_CLOCK: + el_ts_clock(&s, &us); + break; + +# endif + + default: + /* + * bad timer means no timer + */ + + return 0; + } + + if (binary) + { + /* + * put encoded seconds timestamp in buf + */ + +# ifdef LLONG_MAX + tl = el_encode_number((unsigned long long)s, buf); +# else + tl = el_encode_number((unsigned long)s, buf); +# endif + + /* + * put encoded nano/micro/milli seconds in buf if enabled + */ + + if (options->timestamp_useconds) + { +# ifdef LLONG_MAX + tl += el_encode_number((unsigned long long)us, buf + tl); +# else + tl += el_encode_number((unsigned long)us, buf + tl); +# endif + } + + return tl; + } + else + { + /* + * then convert retrieved time into string timestamp + */ + + if (options->timestamp == EL_TS_LONG) + { + struct tm tm; /* timestamp splitted */ + struct tm *tmp; /* timestamp splitted pointer */ + /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + +# if ENABLE_REENTRANT + tmp = gmtime_r(&s, &tm); +# else + tmp = gmtime(&s); +# endif + + tl = strftime(buf, 21, "[%Y-%m-%d %H:%M:%S", tmp); + } + else + { +# ifdef LLONG_MAX + tl = sprintf(buf, "[%lld", (long long)s); +# else + /* + * if long long is not available, code may be suscible to 2038 + * bug. If you are sure your compiler does support long long + * type, but doesn't define LLONG_MAX, define this value to + * enable long long. + */ + + tl = sprintf(buf, "[%ld", (long)s); +# endif + } + + if (options->timestamp_useconds) + { + tl += sprintf(buf + tl, ".%06ld]", us); + } + else + { + /* + * if micro seconds are not printed we simply add ending ']' + */ + + buf[tl] = ']'; + tl++; + } + + return tl; + } + +#else /* ENABLE_TIMESTAMP */ + return 0; +#endif /* ENABLE_TIMESTAMP */ +} diff --git a/src/el-tty.c b/src/el-tty.c index c222a40..0b0791d 100644 --- a/src/el-tty.c +++ b/src/el-tty.c @@ -18,9 +18,7 @@ #include "config.h" #endif -#include "el-options.h" -#include "el-tty.h" -#include "valid.h" +#include "el-private.h" #include <errno.h> #include <fcntl.h> diff --git a/src/el-tty.h b/src/el-tty.h deleted file mode 100644 index 2d56561..0000000 --- a/src/el-tty.h +++ /dev/null @@ -1,16 +0,0 @@ -/* ========================================================================== - Licensed under BSD 2clause license See LICENSE file for more information - Author: Michał Łyszczek <michal.lyszczek@bofc.pl> - ========================================================================== */ - -#ifndef EL_TTY_H -#define EL_TTY_H 1 - -#include "el-options.h" -#include <termios.h> - -int el_tty_open(struct el_options *, const char *, speed_t); -int el_tty_puts(struct el_options *, const char *); -int el_tty_close(struct el_options *); - -#endif diff --git a/tst/Makefile.am b/tst/Makefile.am index b07cfbc..1866f7b 100644 --- a/tst/Makefile.am +++ b/tst/Makefile.am @@ -9,6 +9,7 @@ test_SOURCES += main.c \ test-el-file.c \ test-el-perror.c \ test-el-pmemory.c \ + test-el-pbinary.c \ mtest.h \ test-group-list.h diff --git a/tst/el-decode-number.c b/tst/el-decode-number.c new file mode 120000 index 0000000..268c6a8 --- /dev/null +++ b/tst/el-decode-number.c @@ -0,0 +1 @@ +../src/el-decode-number.c
\ No newline at end of file diff --git a/tst/el-encode-number.c b/tst/el-encode-number.c new file mode 120000 index 0000000..c90c2c0 --- /dev/null +++ b/tst/el-encode-number.c @@ -0,0 +1 @@ +../src/el-encode-number.c
\ No newline at end of file diff --git a/tst/el-pbinary.c b/tst/el-pbinary.c new file mode 120000 index 0000000..3e46c13 --- /dev/null +++ b/tst/el-pbinary.c @@ -0,0 +1 @@ +../src/el-pbinary.c
\ No newline at end of file diff --git a/tst/el-ts.c b/tst/el-ts.c new file mode 120000 index 0000000..478d0cd --- /dev/null +++ b/tst/el-ts.c @@ -0,0 +1 @@ +../src/el-ts.c
\ No newline at end of file @@ -3,7 +3,6 @@ Author: Michał Łyszczek <michal.lyszczek@bofc.pl> ========================================================================== */ -#include "embedlog.h" #include "mtest.h" #include "test-group-list.h" @@ -16,5 +15,6 @@ int main(void) el_file_test_group(); el_perror_test_group(); el_pmemory_test_group(); + el_pbinary_test_group(); mt_return(); } diff --git a/tst/test-el-file.c b/tst/test-el-file.c index 4fa7980..31f0768 100644 --- a/tst/test-el-file.c +++ b/tst/test-el-file.c @@ -53,17 +53,11 @@ int file_synced; /* ========================================================================== - _ __ - ____ _____ (_)_ __ ____ _ / /_ ___ - / __ \ / ___// /| | / // __ `// __// _ \ - / /_/ // / / / | |/ // /_/ // /_ / __/ - / .___//_/ /_/ |___/ \__,_/ \__/ \___/ - /_/ - _ __ __ - _ __ ____ _ _____ (_)____ _ / /_ / /___ _____ - | | / // __ `// ___// // __ `// __ \ / // _ \ / ___/ - | |/ // /_/ // / / // /_/ // /_/ // // __/(__ ) - |___/ \__,_//_/ /_/ \__,_//_.___//_/ \___//____/ + __ __ + _____ ____ ____ _____ / /_ ____ _ ____ / /_ _____ + / ___// __ \ / __ \ / ___// __// __ `// __ \ / __// ___/ + / /__ / /_/ // / / /(__ )/ /_ / /_/ // / / // /_ (__ ) + \___/ \____//_/ /_//____/ \__/ \__,_//_/ /_/ \__//____/ ========================================================================== */ @@ -74,6 +68,11 @@ int file_synced; #define s5 "qwert" #define s3 "asd" #define s1 "a" +static unsigned char d1[] = { 0x01 }; +static unsigned char d2[] = { 0x53, 0x10 }; +static unsigned char d3[] = { 0x00, 0x10, 0x12 }; +static unsigned char d5[] = { 'f', 'o', 'o', 0x00, 'b' }; +static unsigned char d8[] = { 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00 }; mt_defs_ext(); diff --git a/tst/test-el-options.c b/tst/test-el-options.c index be1899b..47c8fee 100644 --- a/tst/test-el-options.c +++ b/tst/test-el-options.c @@ -16,8 +16,7 @@ #include <string.h> #include <errno.h> -#include "embedlog.h" -#include "el-options.h" +#include "el-private.h" #include "mtest.h" #include "test-group-list.h" #include "config.h" diff --git a/tst/test-el-pbinary.c b/tst/test-el-pbinary.c new file mode 100644 index 0000000..23e0620 --- /dev/null +++ b/tst/test-el-pbinary.c @@ -0,0 +1,670 @@ +/* ========================================================================== + Licensed under BSD 2clause license See LICENSE file for more information + Author: Michał Łyszczek <michal.lyszczek@bofc.pl> + ========================================================================== */ + + +/* ========================================================================== + _ __ __ ____ _ __ + (_)____ _____ / /__ __ ____/ /___ / __/(_)/ /___ _____ + / // __ \ / ___// // / / // __ // _ \ / /_ / // // _ \ / ___/ + / // / / // /__ / // /_/ // /_/ // __/ / __// // // __/(__ ) + /_//_/ /_/ \___//_/ \__,_/ \__,_/ \___/ /_/ /_//_/ \___//____/ + + ========================================================================== */ + + +#include <ctype.h> +#include <errno.h> +#include <libgen.h> +#include <rb.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/mman.h> +#include <unistd.h> +#include <sys/types.h> +#include <fcntl.h> +#include <time.h> +#include <stdlib.h> + +#include "mtest.h" +#include "el-private.h" + + +/* ========================================================================== + _ __ __ + ____ _____ (_)_ __ ____ _ / /_ ___ / /_ __ __ ____ ___ _____ + / __ \ / ___// /| | / // __ `// __// _ \ / __// / / // __ \ / _ \ / ___/ + / /_/ // / / / | |/ // /_/ // /_ / __/ / /_ / /_/ // /_/ // __/(__ ) + / .___//_/ /_/ |___/ \__,_/ \__/ \___/ \__/ \__, // .___/ \___//____/ +/_/ /____//_/ + + ========================================================================== */ + + +struct log_message +{ + const unsigned char *msg; + size_t msglen; + int level; +}; + + +/* ========================================================================== + _ __ + ____ _____ (_)_ __ ____ _ / /_ ___ + / __ \ / ___// /| | / // __ `// __// _ \ + / /_/ // / / / | |/ // /_/ // /_ / __/ + / .___//_/ /_/ |___/ \__,_/ \__/ \___/ + /_/ + _ __ __ + _ __ ____ _ _____ (_)____ _ / /_ / /___ _____ + | | / // __ `// ___// // __ `// __ \ / // _ \ / ___/ + | |/ // /_/ // / / // /_/ // /_/ // // __/(__ ) + |___/ \__,_//_/ /_/ \__,_//_.___//_/ \___//____/ + + ========================================================================== */ + + +mt_defs_ext(); +static const char *logpath = "/tmp/embedlog-pbinary-test"; +static struct rb *expected_logs; /* array of expected logs */ +static int truncate_test; + +static unsigned char d1[] = { 0x01 }; +static unsigned char d2[] = { 0x53, 0x10 }; +static unsigned char d3[] = { 0x00, 0x10, 0x12 }; +static unsigned char d5[] = { 'f', 'o', 'o', 0x00, 'b' }; +static unsigned char d8[] = { 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00 }; + + +/* ========================================================================== + _ __ + ____ _____ (_)_ __ ____ _ / /_ ___ + / __ \ / ___// /| | / // __ `// __// _ \ + / /_/ // / / / | |/ // /_/ // /_ / __/ + / .___//_/ /_/ |___/ \__,_/ \__/ \___/ + /_/ + ____ __ _ + / __/__ __ ____ _____ / /_ (_)____ ____ _____ + / /_ / / / // __ \ / ___// __// // __ \ / __ \ / ___/ + / __// /_/ // / / // /__ / /_ / // /_/ // / / /(__ ) + /_/ \__,_//_/ /_/ \___/ \__//_/ \____//_/ /_//____/ + + ========================================================================== */ + + +/* ========================================================================== + Checks if el_print function prints everything in well-defined format. + This function cross-checks logbuf buffer where el_print function prints + message into, and expected_logs with raw information about what should + be printed. + ========================================================================== */ + + +static int pbinary_check(void) +{ + struct log_message expected; + struct stat st; + unsigned char *msg; + unsigned char *msgsave; + int fd; + unsigned char tmp[1024]; + int i; + int slevel; + size_t msglen; + size_t len; + unsigned char flags; + unsigned long long val; + /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + + + stat(logpath, &st); + fd = open(logpath, O_RDONLY); + msg = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); + msgsave = msg; + + while (rb_read(expected_logs, &expected, 1) == 1) + { + slevel = expected.level > EL_DBG ? EL_DBG : expected.level; + + if (expected.level > g_options.level) + { + /* + * log should not have been printed due to current log level + * restriction. We just continue here, because if log was indeed + * printed, next checks will fail anyway. + */ + + continue; + } + + /* + * read flags + */ + + flags = *msg++; + + /* + * check if level is added to flags + */ + + if ((flags >> 3) != expected.level) + { + /* + * no, its not set correctly + */ + + goto error; + } + + /* + * check printing timestamp + */ + + if (g_options.timestamp != EL_TS_OFF) + { + unsigned long long tmp; + /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + + len = el_decode_number(msg, &tmp); + msg += len; + + /* + * we don't know exact time embedlog put into log file, so we + * have to trust the size + */ + + if (flags & 0x01 != 0x01) + { + /* + * expected timestamp flag to be set + */ + + goto error; + } + + /* + * fraction of seconds checks makes sense only when ts is set + */ + + if (g_options.timestamp_useconds) + { + unsigned long long usec; + /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + + len = el_decode_number(msg, &usec); + msg += len; + + if (usec > 999999) + { + /* + * we can't check exact value of microseconds, but we surely + * can check if this value is not too big (and thus corrupt) + */ + + goto error; + } + + if (flags & 0x04 != 0x04) + { + /* + * usec not set, and should be + */ + + goto error; + } + } + else + { + if (flags & 0x06) + { + /* + * fraction flags set, and should not be + */ + + goto error; + } + } + + + } + else + { + if (flags & 0x01) + { + /* + * we didn't expect timestamp flag to be set + */ + + goto error; + } + + if (flags & 0x06) + { + /* + * when timestamp is not set, we expect fraction of seconds + * to not be set as well + */ + + goto error; + } + } + + /* + * now we can check printed data + */ + + if (truncate_test) + { + int i; + int written; + char expect[EL_BUF_MAX]; + /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + + for (i = 0; i != sizeof(expect); ++i) + { + expect[i] = (unsigned char)i; + } + + written = msg - msgsave; + + len = el_decode_number(msg, &val); + + if (val != EL_BUF_MAX - written - len) + { + /* + * truncated message has wrong length + */ + + return -1; + } + + msg += len; + + if (memcmp(msg, expect, val) != 0) + { + /* + * data is incorrect + */ + + return -1; + } + + /* + * point to next message + */ + + msg += val; + } + else + { + len = el_decode_number(msg, &val); + msg += len; + + if (memcmp(msg, expected.msg, val) != 0) + { + /* + * data different than expected + */ + + goto error; + } + + /* + * finally set msg to point to next message + */ + + msg += val; + } + } + + munmap(msgsave, st.st_size); + close(fd); + return 0; + +error: + munmap(msgsave, st.st_size); + close(fd); + return 1; +} + + +/* ========================================================================== + adds log to array of expected logs and then sends passed log message + into el_print + ========================================================================== */ + + +static void add_log +( + enum el_level level, + const void *mem, + size_t mlen +) +{ + struct log_message expected; + /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + + + expected.level = level; + expected.msg = mem; + expected.msglen = mlen; + rb_write(expected_logs, &expected, 1); + el_pbinary(level, mem, mlen); +} + + +/* ========================================================================== + Called before every test, initializes embedlog to known state, and + allocates memory for expected_logs buffer + ========================================================================== */ + + +static void test_prepare(void) +{ + el_init(); + unlink(logpath); + el_option(EL_PRINT_LEVEL, 0); + el_option(EL_OUT, EL_OUT_FILE); + el_option(EL_PREFIX, NULL); + el_option(EL_COLORS, 0); + el_option(EL_TS, EL_TS_OFF); + el_option(EL_TS_USEC, 0); + el_option(EL_PRINT_LEVEL, 0); + el_option(EL_FROTATE_NUMBER, 0); + el_option(EL_FILE_SYNC_EVERY, 1024); + el_option(EL_FPATH, logpath); + truncate_test = 0; + expected_logs = rb_new(1024, sizeof(struct log_message), 0); +} + + +/* ========================================================================== + Called after every test. Frees all memory allocated by test_prepare + ========================================================================== */ + + +static void test_cleanup(void) +{ + el_cleanup(); + rb_destroy(expected_logs); + unlink(logpath); +} + + +/* ========================================================================== + __ __ + / /_ ___ _____ / /_ _____ + / __// _ \ / ___// __// ___/ + / /_ / __/(__ )/ /_ (__ ) + \__/ \___//____/ \__//____/ + + ========================================================================== */ + + +static void pbinary_simple_message(void) +{ + add_log(EL_FATAL, d5, 5); + mt_fok(pbinary_check()); +} + + +/* ========================================================================== + ========================================================================== */ + + +static void pbinary_simple_multiple_message(void) +{ + add_log(EL_FATAL, d1, 1); + add_log(EL_FATAL, d2, 2); + add_log(EL_FATAL, d5, 5); + add_log(EL_FATAL, d8, 8); + mt_fok(pbinary_check()); +} + + +/* ========================================================================== + ========================================================================== */ + + +static void pbinary_ts_without_fractions(void) +{ + el_option(EL_TS_USEC, 1); + add_log(EL_FATAL, d1, 1); + add_log(EL_FATAL, d2, 2); + add_log(EL_FATAL, d5, 5); + add_log(EL_FATAL, d8, 8); + mt_fok(pbinary_check()); +} + + +/* ========================================================================== + ========================================================================== */ + + +static void pbinary_timestamp_short(void) +{ + el_option(EL_TS, EL_TS_SHORT); + add_log(EL_FATAL, d2, 2); + add_log(EL_FATAL, d5, 5); + add_log(EL_FATAL, d8, 8); + mt_fok(pbinary_check()); +} + + +/* ========================================================================== + ========================================================================== */ + + +static void pbinary_timestamp_long(void) +{ + el_option(EL_TS, EL_TS_LONG); + add_log(EL_FATAL, d2, 2); + add_log(EL_FATAL, d5, 5); + add_log(EL_FATAL, d8, 8); + mt_fok(pbinary_check()); +} + + +/* ========================================================================== + ========================================================================== */ + + +static void pbinary_timestamp_short_no_useconds(void) +{ + el_option(EL_TS_USEC, 0); + el_option(EL_TS, EL_TS_SHORT); + add_log(EL_FATAL, d2, 2); + add_log(EL_FATAL, d5, 5); + add_log(EL_FATAL, d8, 8); + mt_fok(pbinary_check()); +} + + +/* ========================================================================== + ========================================================================== */ + + +static void pbinary_timestamp_long_no_useconds(void) +{ + el_option(EL_TS_USEC, 0); + el_option(EL_TS, EL_TS_LONG); + add_log(EL_FATAL, d2, 2); + add_log(EL_FATAL, d5, 5); + add_log(EL_FATAL, d8, 8); + mt_fok(pbinary_check()); +} + + +/* ========================================================================== + ========================================================================== */ + + +static void pbinary_different_clocks(void) +{ + int i; + /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + + + for (i = EL_TS_TM_CLOCK; i != EL_TS_TM_ERROR; ++i) + { + test_prepare(); + el_option(EL_TS_TM, i); + el_option(EL_TS, EL_TS_LONG); + add_log(EL_FATAL, d8, 8); + add_log(EL_FATAL, d8, 8); + mt_fok(pbinary_check()); + test_cleanup(); + } +} + + +/* ========================================================================== + ========================================================================== */ + + +static void pbinary_mix_of_everything(void) +{ + int level; + int timestamp; + int printlevel; + int finfo; + int colors; + int prefix; + int usec; + int nl; + /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + + + for (level = EL_FATAL; level <= EL_DBG; ++level) + for (timestamp = EL_TS_OFF; timestamp != EL_TS_ERROR; ++timestamp) + for (printlevel = 0; printlevel <= 1; ++printlevel) + for (finfo = 0; finfo <= 1; ++finfo) + for (colors = 0; colors <= 1; ++colors) + for (prefix = 0; prefix <= 1; ++prefix) + for (usec = 0; usec <= 1; ++usec) + for (nl = 0; nl <= 1; ++nl) + { + test_prepare(); + el_option(EL_LEVEL, level); + el_option(EL_TS, timestamp); + el_option(EL_PRINT_LEVEL, printlevel); + el_option(EL_PRINT_NL, nl); + el_option(EL_FINFO, finfo); + el_option(EL_COLORS, colors); + el_option(EL_PREFIX, prefix ? "prefix" : NULL); + + add_log(EL_FATAL, d1, 1); + add_log(EL_ALERT, d1, 1); + add_log(EL_CRIT, d1, 1); + add_log(EL_ERROR, d5, 5); + add_log(EL_WARN, d1, 1); + add_log(EL_NOTICE, d2, 2); + add_log(EL_INFO, d1, 1); + add_log(EL_DBG, d8, 8); + + /* + * cleanup so embedlog closes test file, which will flush data into + * disk so pbinary_check() can read that data + */ + + el_cleanup(); + mt_fok(pbinary_check()); + + test_cleanup(); + } +} + + +/* ========================================================================== + ========================================================================== */ + + +static void pbinary_with_no_output_available(void) +{ + el_option(EL_OUT, EL_OUT_NONE); + mt_ferr(el_pbinary(EL_INFO, d2, 2), ENODEV); +} + + +/* ========================================================================== + ========================================================================== */ + + +static void pbinary_level_not_high_enough(void) +{ + mt_ferr(el_pbinary(EL_DBG, d8, 8), ERANGE); +} + + +/* ========================================================================== + ========================================================================== */ + + +static void pbinary_null(void) +{ + mt_ferr(el_pbinary(EL_ALERT, NULL, 0), EINVAL); + mt_ferr(el_pbinary(EL_ALERT, NULL, 5), EINVAL); + mt_ferr(el_pbinary(EL_ALERT, d5, 0), EINVAL); +} + + +/* ========================================================================== + ========================================================================== */ + + +static void pbinary_truncate(void) +{ + int i; + char msg[EL_BUF_MAX + 3]; + /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + + for (i = 0; i != sizeof(msg); ++i) + { + msg[i] = (unsigned char)i; + } + + el_option(EL_TS, EL_TS_LONG); + el_option(EL_TS_USEC, 1); + add_log(EL_INFO, msg, sizeof(msg)); + + /* + * tell checker that this is truncate test + */ + + truncate_test = 1; + mt_fok(pbinary_check()); +} + +/* ========================================================================== + __ __ + / /_ ___ _____ / /_ ____ _ _____ ____ __ __ ____ + / __// _ \ / ___// __/ / __ `// ___// __ \ / / / // __ \ + / /_ / __/(__ )/ /_ / /_/ // / / /_/ // /_/ // /_/ / + \__/ \___//____/ \__/ \__, //_/ \____/ \__,_// .___/ + /____/ /_/ + ========================================================================== */ + + +void el_pbinary_test_group(void) +{ + mt_run(pbinary_different_clocks); + mt_run(pbinary_mix_of_everything); + + mt_prepare_test = &test_prepare; + mt_cleanup_test = &test_cleanup; + + mt_run(pbinary_simple_message); + mt_run(pbinary_simple_multiple_message); + mt_run(pbinary_ts_without_fractions); + mt_run(pbinary_timestamp_short); + mt_run(pbinary_timestamp_long); + mt_run(pbinary_timestamp_short_no_useconds); + mt_run(pbinary_timestamp_long_no_useconds); + mt_run(pbinary_with_no_output_available); + mt_run(pbinary_level_not_high_enough); + mt_run(pbinary_null); + mt_run(pbinary_truncate); +}
\ No newline at end of file diff --git a/tst/test-el-print.c b/tst/test-el-print.c index 3af3e52..f8d23c3 100644 --- a/tst/test-el-print.c +++ b/tst/test-el-print.c @@ -24,8 +24,7 @@ #include "stdlib.h" #include "embedlog.h" -#include "config-priv.h" -#include "el-options.h" +#include "el-private.h" /* ========================================================================== diff --git a/tst/test-group-list.h b/tst/test-group-list.h index 4429a42..cd478cf 100644 --- a/tst/test-group-list.h +++ b/tst/test-group-list.h @@ -8,6 +8,7 @@ void el_options_test_group(void); void el_print_test_group(void); +void el_pbinary_test_group(void); void el_file_test_group(void); void el_perror_test_group(void); void el_pmemory_test_group(void); |