diff options
author | Michał Łyszczek <michal.lyszczek@bofc.pl> | 2018-04-23 00:30:25 +0200 |
---|---|---|
committer | Michał Łyszczek <michal.lyszczek@bofc.pl> | 2018-04-23 00:30:25 +0200 |
commit | abae42af13181ebb130cd570e1acf37f763dbde0 (patch) | |
tree | baa99fa0de7752bbb4b1368e45145c0a814e1adb | |
parent | f27539bc6456930bd7e76eb894e8953e31cc122c (diff) | |
parent | 3ab8e312386833c1d3afbb46503661f596c406bb (diff) | |
download | embedlog-abae42af13181ebb130cd570e1acf37f763dbde0.tar.gz embedlog-abae42af13181ebb130cd570e1acf37f763dbde0.tar.bz2 embedlog-abae42af13181ebb130cd570e1acf37f763dbde0.zip |
Merge branch 'nuttx-support'
44 files changed, 3843 insertions, 553 deletions
diff --git a/configure.ac b/configure.ac index 1328e3d..163be32 100644 --- a/configure.ac +++ b/configure.ac @@ -78,7 +78,6 @@ AC_ARG_ENABLE([out-stderr], AS_IF([test "x$enable_out_stderr" = "xyes"], [ AC_DEFINE([ENABLE_OUT_STDERR], [1], [Enable printing to stderr]) - AC_CHECK_FUNCS(access) AC_CHECK_FUNCS([fputs],, AC_MSG_ERROR(not found, needed by --enable-out-stderr)) ]) @@ -97,6 +96,7 @@ AM_CONDITIONAL([ENABLE_OUT_FILE], [test "x$enable_out_file" = "xyes"]) AS_IF([test "x$enable_out_file" = "xyes"], [ AC_DEFINE([ENABLE_OUT_FILE], [1], [Enable printing to file]) + AC_CHECK_FUNCS(access stat fsync fileno) AC_CHECK_FUNCS([fopen fclose fwrite remove rename],, AC_MSG_ERROR(not found, needed by --enable-out-file)) ]) @@ -115,6 +115,7 @@ AM_CONDITIONAL([ENABLE_OUT_TTY], [test "x$enable_out_tty" = "xyes"]) AS_IF([test "x$enable_out_tty" = "xyes"], [ AC_DEFINE([ENABLE_OUT_TTY], [1], [Enable printing to tty]) + AC_CHECK_HEADERS(termios.h) AC_CHECK_FUNCS([open tcgetattr cfsetispeed tcsetattr close],, AC_MSG_ERROR(not found, needed by --enable-out-tty)) ]) @@ -150,12 +151,29 @@ AC_ARG_ENABLE([timestamp], AS_IF([test "x$enable_timestamp" = "xyes"], [ AC_DEFINE([ENABLE_TIMESTAMP], [1], [Enable printing timestamp with log]) - AC_CHECK_FUNCS([clock time],, + AC_CHECK_FUNCS([clock time sprintf],, AC_MSG_ERROR(not found, needed by --enable-timestamp)) ]) ### +# --enable-fractions +# + + +AC_ARG_ENABLE([fractions], + AS_HELP_STRING([--enable-fractions], [Enable printing fractions with log]), + [], [enable_fractions="yes"]) + +AS_IF([test "x$enable_fractions" = "xyes"], +[ + AC_DEFINE([ENABLE_FRACTIONS], [1], [Enable printing fractions with log]) + AC_CHECK_FUNCS([sprintf],, + AC_MSG_ERROR(not found, needed by --enable-fractions)) +]) + + +### # --enable-realtime # @@ -200,6 +218,63 @@ AS_IF([test "x$enable_monotonic" = "xyes"], ### +# --enable-clock +# + + +AC_ARG_ENABLE([clock], + AS_HELP_STRING([--enable-clock], [Enable using clock() as tme source in log]), + [], [enable_clock="yes"]) + +AS_IF([test "x$enable_clock" = "xyes"], +[ + AS_IF([test "x$enable_timestamp" != "xyes"], + [ + AC_MSG_WARN(--enable-clock without --enable-timestamp has no effect) + ]) + + AC_DEFINE([ENABLE_CLOCK], [1], [Enable using clock() as time source in log]) + AC_CHECK_FUNCS([clock],, + AC_MSG_ERROR(not found, needed by --enable-clock)) +]) + + +### +# --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_BINARY_LOGS], [1], [Enable printing binary logs]) + AC_CHECK_FUNCS([memcpy],, + AC_MSG_ERROR(not found, needed by --enable-binary-logs)) +]) + + +### +# --enable-prefix +# + + +AC_ARG_ENABLE([prefix], + AS_HELP_STRING([--enable-prefix], [Enable printing prefix to each message]), + [], [enable_prefix="yes"]) + +AS_IF([test "x$enable_prefix" = "xyes"], +[ + AC_DEFINE([ENABLE_PREFIX], [1], [Enable printing prefix to each message]) + AC_CHECK_FUNCS([strncat strlen],, + AC_MSG_ERROR(not found, needed by --enable-prefix)) +]) + + +### # --enable-finfo # @@ -234,6 +309,21 @@ AS_IF([test "x$enable_colors" = "xyes"], ### +# --enable-colors-extended +# + + +AC_ARG_ENABLE([colors-extended], + AS_HELP_STRING([--enable-colors-extended], [Enable more colors in logs]), + [], [enable_colors_extended="no"]) + +AS_IF([test "x$enable_colors_extended" = "xyes"], +[ + AC_DEFINE([ENABLE_COLORS_EXTENDED], [1], [Enable more colors in logs]) +]) + + +### # --enable-reentrant # @@ -254,6 +344,15 @@ AS_IF([test "x$enable_reentrant" = "xyes"], # VARIABLES=value options # +### +# EL_PREFIX_MAX +# + + +AC_ARG_VAR([EL_PREFIX_MAX], [Maximum size of prefix]) +AS_IF([test "x$EL_PREFIX_MAX" = x], [EL_PREFIX_MAX="128"]) +AC_DEFINE_UNQUOTED([EL_PREFIX_MAX], [$EL_PREFIX_MAX], [Maximum size of prefix]) + ### # EL_LOG_MAX @@ -317,12 +416,18 @@ echo "print to file............... : $enable_out_file" echo "print to tty................ : $enable_out_tty" echo "print to custom routine..... : $enable_out_custom" echo "print timestamp enabled..... : $enable_timestamp" +echo "print fractions of seconds.. : $enable_fractions" echo "clock_realtime timestamp.... : $enable_realtime" echo "clock_monotonic timestamp... : $enable_monotonic" +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" +echo "maximum prefix length....... : $EL_PREFIX_MAX" echo "maximum file length......... : $EL_FLEN_MAX" echo "maximum log message......... : $EL_LOG_MAX" echo "maximum path length......... : $MAX_PATH" 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/examples/el-decode-number.c b/examples/el-decode-number.c new file mode 120000 index 0000000..268c6a8 --- /dev/null +++ b/examples/el-decode-number.c @@ -0,0 +1 @@ +../src/el-decode-number.c
\ No newline at end of file diff --git a/examples/el-encode-number.c b/examples/el-encode-number.c new file mode 120000 index 0000000..c90c2c0 --- /dev/null +++ b/examples/el-encode-number.c @@ -0,0 +1 @@ +../src/el-encode-number.c
\ No newline at end of file diff --git a/examples/el-pbinary.c b/examples/el-pbinary.c new file mode 120000 index 0000000..3e46c13 --- /dev/null +++ b/examples/el-pbinary.c @@ -0,0 +1 @@ +../src/el-pbinary.c
\ No newline at end of file diff --git a/examples/el-ts.c b/examples/el-ts.c new file mode 120000 index 0000000..478d0cd --- /dev/null +++ b/examples/el-ts.c @@ -0,0 +1 @@ +../src/el-ts.c
\ No newline at end of file diff --git a/examples/print-options.c b/examples/print-options.c index c9e02c0..2c619cb 100644 --- a/examples/print-options.c +++ b/examples/print-options.c @@ -5,13 +5,17 @@ #include "embedlog.h" +#define EL_OPTIONS_OBJECT &opts + int main(void) { + struct el_options opts; + el_init(); el_option(EL_OUT, EL_OUT_STDERR); el_option(EL_PRINT_LEVEL, 0); - el_print(ELI, "We can disable information about log level"); + el_print(ELI, "We can disable information about log level\b"); el_print(ELF, "message still will be filtered by log level"); el_print(ELA, "but there is no way to tell what level message is"); el_print(ELD, "like this message will not be printed"); @@ -28,11 +32,21 @@ int main(void) el_print(ELF, "if higher precision is needed we can use CLOCK_REALTIME"); el_option(EL_TS, EL_TS_SHORT); el_print(ELF, "we can also mix REALTIME with short format"); + el_option(EL_TS_FRACT, EL_TS_FRACT_OFF); + el_print(ELF, "and iff you don't need high resolution"); + el_print(ELF, "you can disable fractions of seconds to save space!"); + el_option(EL_TS_FRACT, EL_TS_FRACT_MS); + el_print(ELF, "or enable only millisecond resolution"); + el_option(EL_TS_FRACT, EL_TS_FRACT_US); + el_print(ELF, "or enable only microsecond resolution"); + el_option(EL_TS_FRACT, EL_TS_FRACT_NS); + el_print(ELF, "or enable only nanosecond resolution"); el_option(EL_TS, EL_TS_LONG); el_option(EL_TS_TM, EL_TS_TM_CLOCK); el_print(ELF, "or long with clock() if you desire"); el_option(EL_TS, EL_TS_OFF); el_print(ELF, "no time information, if your heart desire it"); + el_option(EL_TS_FRACT, EL_TS_FRACT_NS); el_option(EL_FINFO, 1); el_print(ELF, "log location is very usefull for debuging"); @@ -43,6 +57,12 @@ int main(void) el_print(ELF, "Different scenarios need different options"); el_print(ELA, "So we can mix options however we want"); + el_option(EL_PRINT_NL, 0); + el_print(ELF, "you can also remove printing new line "); + el_puts("to join el_print and el_puts in a single "); + el_puts("long line as needed\n"); + el_option(EL_PRINT_NL, 1); + el_option(EL_COLORS, 1); el_option(EL_LEVEL, EL_DBG); el_print(ELD, "And if we have"); @@ -54,5 +74,22 @@ int main(void) el_print(ELA, "glance into"); el_print(ELF, "log file"); + el_option(EL_PREFIX, "embedlog: "); + el_print(ELI, "you can also use prefixes"); + el_print(ELI, "to every message you send"); + + el_option(EL_PREFIX, NULL); + el_print(ELI, "set prefix to null to disable it"); + el_cleanup(); + + + el_oinit(&opts); + el_ooption(&opts, EL_OUT, EL_OUT_STDERR); + el_oprint(ELI, &opts, "you can do same thing as about with custom"); + el_oprint(ELI, &opts, "options object for two or more logger."); + el_oprint(OELE, "and if you define EL_OPTIONS_OBJECT you will be"); + el_oprint(OELF, "able to print messages without passing options"); + el_oprint(OELW, "each time to print functions"); + el_ocleanup(&opts); } diff --git a/examples/print-to-file.c b/examples/print-to-file.c index aa2418e..961b73e 100644 --- a/examples/print-to-file.c +++ b/examples/print-to-file.c @@ -26,7 +26,7 @@ int main(void) goto error; } - if (el_option(EL_FNAME, WORKDIR"/log") != 0) + if (el_option(EL_FPATH, WORKDIR"/log") != 0) { /* * embedlog will try to open file now, this may fail for various of diff --git a/examples/print-tty.c b/examples/print-tty.c index a0c53af..e38cf57 100644 --- a/examples/print-tty.c +++ b/examples/print-tty.c @@ -27,7 +27,7 @@ int main(void) * 8N1 by default. Baudrate should be taken from termios (3). */ - if (el_option(EL_TTY_DEV, "/dev/ttyUSB1", B115200) != 0) + if (el_option(EL_TTY_DEV, "/dev/ttyUSB1", B9600) != 0) { perror("tty set failed"); return 1; diff --git a/include/embedlog.h b/include/embedlog.h index fe22fe3..8615020 100644 --- a/include/embedlog.h +++ b/include/embedlog.h @@ -11,6 +11,10 @@ #include <stdarg.h> #include <stdio.h> +#ifdef __cplusplus +extern "C" { +#endif + #ifdef NOFINFO # define ELF NULL, 0, EL_FATAL # define ELA NULL, 0, EL_ALERT @@ -20,6 +24,15 @@ # define ELN NULL, 0, EL_NOTICE # define ELI NULL, 0, EL_INFO # define ELD NULL, 0, EL_DBG + +# define OELF NULL, 0, EL_FATAL, EL_OPTIONS_OBJECT +# define OELA NULL, 0, EL_ALERT, EL_OPTIONS_OBJECT +# define OELC NULL, 0, EL_CRIT, EL_OPTIONS_OBJECT +# define OELE NULL, 0, EL_ERROR, EL_OPTIONS_OBJECT +# define OELW NULL, 0, EL_WARN, EL_OPTIONS_OBJECT +# define OELN NULL, 0, EL_NOTICE, EL_OPTIONS_OBJECT +# define OELI NULL, 0, EL_INFO, EL_OPTIONS_OBJECT +# define OELD NULL, 0, EL_DBG, EL_OPTIONS_OBJECT #else # define ELF __FILE__, __LINE__, EL_FATAL # define ELA __FILE__, __LINE__, EL_ALERT @@ -29,6 +42,15 @@ # define ELN __FILE__, __LINE__, EL_NOTICE # define ELI __FILE__, __LINE__, EL_INFO # define ELD __FILE__, __LINE__, EL_DBG + +# define OELF __FILE__, __LINE__, EL_FATAL, EL_OPTIONS_OBJECT +# define OELA __FILE__, __LINE__, EL_ALERT, EL_OPTIONS_OBJECT +# define OELC __FILE__, __LINE__, EL_CRIT, EL_OPTIONS_OBJECT +# define OELE __FILE__, __LINE__, EL_ERROR, EL_OPTIONS_OBJECT +# define OELW __FILE__, __LINE__, EL_WARN, EL_OPTIONS_OBJECT +# define OELN __FILE__, __LINE__, EL_NOTICE, EL_OPTIONS_OBJECT +# define OELI __FILE__, __LINE__, EL_INFO, EL_OPTIONS_OBJECT +# define OELD __FILE__, __LINE__, EL_DBG, EL_OPTIONS_OBJECT #endif #if (__STDC_VERSION__ >= 199901L) @@ -70,14 +92,19 @@ enum el_option EL_COLORS, EL_TS, EL_TS_TM, + EL_TS_FRACT, EL_PRINT_LEVEL, + EL_PRINT_NL, EL_FINFO, EL_CUSTOM_PUTS, EL_TTY_DEV, + EL_PREFIX, - EL_FNAME, + EL_FPATH, EL_FROTATE_NUMBER, EL_FROTATE_SIZE, + EL_FILE_SYNC_EVERY, + EL_FILE_SYNC_LEVEL, EL_OPT_ERROR /* internal use only */ }; @@ -93,33 +120,51 @@ enum el_option_timestamp enum el_option_timestamp_timer { - EL_TS_TM_CLOCK, EL_TS_TM_TIME, + EL_TS_TM_CLOCK, EL_TS_TM_REALTIME, EL_TS_TM_MONOTONIC, EL_TS_TM_ERROR /* internal use only */ }; +enum el_option_timestamp_fractions +{ + EL_TS_FRACT_OFF, + EL_TS_FRACT_MS, + EL_TS_FRACT_US, + EL_TS_FRACT_NS, + + EL_TS_FRACT_ERROR /* internal use onsly */ +}; + typedef int (*el_custom_puts)(const char *s); struct el_options { - int outputs; - int level; - int colors; - int timestamp; - int timestamp_timer; - int print_log_level; + unsigned int outputs:6; + unsigned int colors:1; + unsigned int timestamp:2; + unsigned int timestamp_timer:3; + unsigned int timestamp_fractions:2; + unsigned int print_log_level:1; + unsigned int print_newline:1; + unsigned int finfo:1; + unsigned int level:3; + unsigned int level_current_msg:3; + unsigned int file_sync_level:3; int serial_fd; - int finfo; - int frotate_number; - int fcurrent_rotate; - long frotate_size; - long fpos; + unsigned int frotate_number; + unsigned int fcurrent_rotate; + unsigned long frotate_size; + unsigned long file_sync_every; + unsigned long written_after_sync; + unsigned long fpos; FILE *file; + char *current_log; const char *fname; + const char *prefix; el_custom_puts custom_puts; }; @@ -135,7 +180,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); @@ -149,6 +195,12 @@ 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 +} +#endif #endif diff --git a/man/el_init.3 b/man/el_init.3 index 18552b8..dca5c64 100644 --- a/man/el_init.3 +++ b/man/el_init.3 @@ -76,7 +76,7 @@ Note: error handling has been ommited for clarity sake /* make opts to print to file */ el_ooption(&opts, EL_OUT, EL_OUT_STDERR); - el_ooption(&opts, EL_FNAME, "/tmp/test.log"); + el_ooption(&opts, EL_FPATH, "/tmp/test.log"); /* print messages */ el_print(ELI, "will print to stderr"); diff --git a/man/el_option.3 b/man/el_option.3 index b961e31..c94a340 100644 --- a/man/el_option.3 +++ b/man/el_option.3 @@ -29,7 +29,7 @@ Value inside parenthesis determines argument types of variadic arguments sets what the current logging level shall be. Altough it accepts . B enum el_level -type, this can be whatever number from range <0, INT_MAX>. +type, this can be whatever number from range <0, 7>. The higher the level, the lower priority of the message. All messages that have lower priority (higher number) then currently set . I level @@ -54,34 +54,14 @@ There are 8 predefined levels, sorted by priority (highest first): . B EL_DBG . RE . PP -If 8 print levels are not enough, one can define new levels, knowing that -. B EL_FATAL -has int value of 0, and -. B EL_DBG -has int value of 7. -User can also use debug level and add an integer like: -. B EL_DBG -+ -. I n -- where -. I n -can be any number from range <0, INT_MAX - 8>. -. PP -If messages are printed with lower priority than -. B EL_DBG -they all will be treated as messages printed with level -. BR EL_DBG , -that means message printed with level -. B EL_DBG -and -. BR "EL_DBG + 3" , -will be printed exactly the same, so same color (if colors are enabled) and if -log level printig is enabled, such messages will have same 'd/' prefix no matter -the level. -. PP . B ERRORS . RS -Setting log level cannot fail +. B EINVAL +. RS +specified +. I level +is invalid (bigger than EL_DBG). +. RE . RE .RE .PP @@ -119,7 +99,7 @@ POSIX.1-2001 and any syslog daemon . RS Messages will be printed to file. To work user should set output file with -. BR EL_FNAME . +. BR EL_FPATH . Log rotation can be configured with . B EL_FROTATE_NUMBER and @@ -184,6 +164,32 @@ is not implemented on current system (support was not compiled into library) . RE .RE .PP +.BI "EL_PREFIX (" const " " char " " * "prefix)" +.RS +You can configure logger to add +. I prefix +to every message you print. +. I prefix +will be printed between log level info "i/" and you message. +No spaces are added. +If you set your +. I prefix +to simple +. BR PREFIX , +printed message will be +. BR "i/PREFIXmy log message" , +so you might want to add space there. +If +. I prefix +is bigger than +. B EL_PREFIX_LEN +prefix will be truncated. +To disable prefixing, simply set +. I prefix +to +. BR NULL . +.RE +.PP .BI "EL_COLORS (" int " colors)" .RS If this is set to 1, output will be enriched with ANSI colors depending on the @@ -310,6 +316,26 @@ Specified timer source was not compiled in and is not available . RE .RE .PP +.BI "EL_TS_FRACT (" enum " " el_option_timestamp_fraction " fraction) +.RS +This options controls how to display fractions of seconds. +If high resolution is not needed or not supported, it's best to set this to +lowest resolution possible. +Table will show exacly what this is about. +Example uses long timestamp, interesting part is after date after dot '.'. +.PP +.EX + +-----------------+-------------------------------+ + | value | resulting timestamp string | + +-----------------+-------------------------------+ + | EL_TS_FRACT_OFF | 2018-04-17 22:02:57 | + | EL_TS_FRACT_MS | 2018-04-17 22:02:57.070 | + | EL_TS_FRACT_US | 2018-04-17 22:02:57.070518 | + | EL_TS_FRACT_NS | 2018-04-17 22:02:57.070518782 | + +-----------------+-------------------------------+ +.EE +.RE +.PP .BI "EL_PRINT_LEVEL (" int " print)" .RS If this is set to 1, each log will have log level information prefix in format @@ -369,7 +395,7 @@ It is still mandatory to enable custom printing with . BR el_option (3) .RE .PP -.BI "EL_FNAME (" const " " char " " * "path)" +.BI "EL_FPATH (" const " " char " " * "path)" .RS Sets the .I path @@ -407,7 +433,7 @@ specified file without size limit. The only size limit is the one presented by the filesystem and architecture. . PP If this value is bigger than 0, file rotation will be enabled. All files will -have suffixes added to name set in EL_FNAME. For example, +have suffixes added to name set in EL_FPATH. For example, . IR program.log.0 . Files are enumareted from . I .0 @@ -428,7 +454,7 @@ will be renamed to . I log.2 will be renamed to . I log.1 -and so on. +and so on). . PP User can also pass 1 here, but if file reaches its size limit, it will be deleted and printing will continue from the empty file @@ -465,6 +491,64 @@ Value is less than 1 . RE .RE .PP +.BI "EL_FILE_SYNC_EVERY (" long " size)" +.RS +Simply writing data to file descriptor is not enough, metadata also has to be +synced or you are facing data loose on unexpected power loose. +Yes, data will be on block device, but entry to it in file system will not be +updated, thus system will think file did not change. +To prevent that one must sync data and metadata periodically. +Data will be synced every +.I size +of bytes written. +It basically translates to 'how much data am I ready to loose?'. +Set this too high and you may loose a significant ammout of data on power lose. +Set this too low and your performance goes out of the window. +. PP +. B ERRORS +. RS +. B EINVAL +. RS +Value is less than 0 +. RE +. RE +.RE +.PP +.BI "EL_FILE_SYNC_LEVEL (" enum " " el_level " level)" +.RS +If level of printed message is +. I level +or less (that is higher priority), it will be synced to disk every single time +regardless of +. B EL_FILE_SYNC_EVERY +option. +Messages with level +. B EL_FATAL +(that is also default value for level sync) are synced always regardless of any +options set. +Messages printed with functions that don't take log level, will be treated as +messages with +. B EL_DBG +level. +If +. I level +is set to +. B EL_DBG +every message printed with any function will be immediately synced to drive +(careful with that, this will impact performance a great deal if you print a lot +of debug messages). +. PP +. B ERRORS +. RS +. B EINVAL +. RS +specified +. I level +is invalid (bigger than EL_DBG). +. RE +. RE +.RE +.PP .BI "EL_TTY_DEV (" const\ char\ * "dev, " speed_t " speed)" .RS Tells @@ -479,6 +563,12 @@ Logger uses only transmit pin (TX) and will translate all .B LF into .BR CR-LF . +If +.I speed +is configured to +.BR B0, +serial port settings will not be altered, library will simply open port and +will happily work on current serial settings. . PP . B ERRORS . RS diff --git a/man/el_print.3 b/man/el_print.3 index c7d6c78..3249ecd 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 () @@ -121,6 +149,11 @@ only information about errno will be printed. It is similar to .BR perror () function. +It is guaranteed that +.BR el_perror (3) +functions will not modify +.B errno +value. .PP .BR el_pmemory (3) is designed to print memory location in a nice hex+ascii format. @@ -153,6 +186,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) @@ -244,7 +366,7 @@ Any of the input parameters is invalid. .TP .B EBADF Loggig to file is enabled and filename was not set with -.B EL_FNAME +.B EL_FPATH option .TP .B EBADF @@ -11,8 +11,10 @@ systems, it is general **c/c++** logger that can be used in any application. Implemented features are: * printing to different outputs (simultaneously) like: + * syslog + * directly to serial device (like /dev/ttyS0) * standard error (stderr) - * file (with optional log rotate) + * file (with optional log rotate, and syncing to prevent data loss) * custom routine - can be anything **embedlog** just calls custom function with string to print * appending timestamp to every message @@ -21,7 +23,7 @@ Implemented features are: * CLOCK_REALTIME (requires POSIX) * CLOCK_MONOTONIC (requires POSIX) * print location of printed log (file and line) -* 8 predefinied log levels and (sizeof(int) - 8) custom log levels ;-) +* 8 predefinied log levels (total rip off from syslog(2)) * colorful output (ansi colors) for easy error spotting * print memory block in wireshark-like output diff --git a/src/Makefile.am b/src/Makefile.am index 4de0eaf..b42a7cc 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -3,10 +3,7 @@ include $(top_srcdir)/embedlog-sources.mk lib_LTLIBRARIES = libembedlog.la libembedlog_la_SOURCES = $(embedlog_sources) -libembedlog_la_SOURCES += config-priv.h \ - el-file.h \ - el-options.h \ - el-tty.h \ +libembedlog_la_SOURCES += el-private.h \ valid.h libembedlog_la_LDFLAGS = -version-info 2:2:2 libembedlog_la_CFLAGS = -I$(top_srcdir)/include @@ -20,7 +17,7 @@ MOSTLYCLEANFILES = $(analyze_plists) $(analyze_plists): %.plist: %.c @echo " CCSA " $@ - @clang --analyze -I.. $< -o $@ + @clang --analyze -I.. -DHAVE_CONFIG_H=1 $< -o $@ analyze: $(analyze_plists) 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 1f61600..cafa331 100644 --- a/src/el-file.c +++ b/src/el-file.c @@ -39,22 +39,57 @@ ========================================================================== */ #if HAVE_CONFIG_H -#include "config.h" +# include "config.h" #endif -#include "el-options.h" +#include "el-private.h" #if HAVE_UNISTD_H -#include <unistd.h> +# include <unistd.h> #endif + +#if HAVE_STAT +# include <sys/types.h> +# include <sys/stat.h> +#endif + #include <errno.h> #include <limits.h> #include <stdint.h> #include <stdio.h> +#include <stdlib.h> #include <string.h> /* ========================================================================== + __ __ __ + ____ _ / /____ / /_ ____ _ / / + / __ `// // __ \ / __ \ / __ `// / + / /_/ // // /_/ // /_/ // /_/ // / + \__, //_/ \____//_.___/ \__,_//_/ + /____/ + _ __ __ + _ __ ____ _ _____ (_)____ _ / /_ / /___ _____ + | | / // __ `// ___// // __ `// __ \ / // _ \ / ___/ + | |/ // /_/ // / / // /_/ // /_/ // // __/(__ ) + |___/ \__,_//_/ /_/ \__,_//_.___//_/ \___//____/ + + ========================================================================== */ + + +#ifdef RUN_TESTS + +/* + * there is no easy way to check if file has been fsynced to disk or not, + * so we introduce this helper variable to know if we executed syncing + * code or not + */ +extern int file_synced; + +#endif + + +/* ========================================================================== _ __ ____ _____ (_)_ __ ____ _ / /_ ___ / __ \ / ___// /| | / // __ `// __// _ \ @@ -80,8 +115,6 @@ #endif -static char current_log[PATH_MAX + 1]; /* full path to current log file */ - /* ========================================================================== _ __ @@ -101,15 +134,40 @@ static char current_log[PATH_MAX + 1]; /* full path to current log file */ static int el_file_exists ( - void + const char *path /* file to check */ ) { #if HAVE_ACCESS - return access(current_log, F_OK) == 0; + /* + * access is the fastest + */ + + return access(path, F_OK) == 0; + +#elif HAVE_STAT + /* + * but if we don't have access (some embedded systems like nuttx don't + * have users and will always return OK when access is called - wrongly + */ + + struct stat st; + /* + * if stat return 0 we are sure file exists, -1 is returned for when + * file doesn't exist or there is other error, in any case we assume + * file doesn't exist + */ + + return stat(path, &st) == 0; + #else + /* + * slowest, worst but highly portable solution, for when there is + * nothing left but you still want to work + */ + FILE *f; - if ((f = fopen(current_log, "r")) == NULL) + if ((f = fopen(path, "r")) == NULL) { return 0; } @@ -134,7 +192,7 @@ static int el_file_rotate struct el_options *options ) { - int i; /* simple iterator for loop */ + unsigned int i; /* simple iterator for loop */ /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ @@ -212,9 +270,10 @@ skip_rotate: * passes there, it will pass here as well */ - sprintf(current_log, "%s.%d", options->fname, options->fcurrent_rotate); + sprintf(options->current_log, "%s.%d", + options->fname, options->fcurrent_rotate); - if ((options->file = fopen(current_log, "w")) == NULL) + if ((options->file = fopen(options->current_log, "w")) == NULL) { options->fcurrent_rotate--; return -1; @@ -225,6 +284,7 @@ skip_rotate: return 0; } + /* ========================================================================== __ __ _ ____ __ __ / /_ / /(_)_____ @@ -252,12 +312,29 @@ int el_file_open struct el_options *options /* options with file information */ ) { + if (options->current_log == NULL) + { + /* + * yes, we need to dynamically allocate memory here. It's because we + * need to keep current log privately in each of el_options objects + * or there will be problems in embedded systems that use flat + * memory. Since when working with files, OS will always make some + * dynamic allocation, we will be doing one too. + */ + + if ((options->current_log = malloc(PATH_MAX + 1)) == NULL) + { + errno = ENOMEM; + return -1; + } + } + if (options->file) { /* * to prevent any memory leak in case of double open, we first * close already opened file Such situation may happen when library - * user changes file name using EL_FNAME option, + * user changes file name using EL_FPATH option, */ fclose(options->file); @@ -282,7 +359,7 @@ int el_file_open for (i = options->frotate_number - 1; i >= 0; --i) { - pathl = snprintf(current_log, PATH_MAX + 1, "%s.%d", + pathl = snprintf(options->current_log, PATH_MAX + 1, "%s.%d", options->fname, i); if (pathl > PATH_MAX) @@ -293,12 +370,90 @@ int el_file_open * could result in some data lose on the disk. */ - current_log[0] = '\0'; + options->current_log[0] = '\0'; errno = ENAMETOOLONG; return -1; } - if ((f = fopen(current_log, "a")) == NULL) +#if HAVE_STAT + + if (i != 0) + { + struct stat st; + /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + + /* + * if i is 0, then this is last file to check (there are no + * logs from embed log in directory) and there is no need to + * check if file exists or not - we open it unconditionally, + * thus this path is not taken in such case. + */ + + if (el_file_exists(options->current_log) == 0) + { + /* + * current log file does not exist, this is not the file + * you are looking for + */ + + continue; + } + + if (stat(options->current_log, &st) != 0) + { + /* + * error while stating file, probably don't have access + * to that file, or directory, or whatever, something + * bad has happend and we don't want to pursue this, + * exit with error from stat. + */ + + options->current_log[0] = '\0'; + return -1; + } + + if (st.st_size == 0) + { + /* + * there is an empty file, maybe we created it and then + * crashed? Or maybe someone is trying to pull our legs + * and drops bombs under our feets. Either way, this is + * definitely not oldest file. We also remove it so it + * doesn't botter us later + */ + + remove(options->current_log); + continue; + } + } + + /* + * we got our file, let's open it for writing and let's call it + * a day + */ + + if ((f = fopen(options->current_log, "a")) == NULL) + { + /* + * couldn't open file, probably directory doesn't exist, or + * we have no permissions to create file here + */ + + return -1; + } + + /* + * we need to check for currently opened file size once again, + * as if i equal 0 here, we never called stat() on our file and + * we don't know the size of it + */ + + fseek(f, 0, SEEK_END); + fsize = ftell(f); + +#else /* HAVE_STAT */ + + if ((f = fopen(options->current_log, "a")) == NULL) { /* * if we cannot open file, that means there is some kind of @@ -306,7 +461,7 @@ int el_file_open * it's pointless to continue */ - current_log[0] = '\0'; + options->current_log[0] = '\0'; return -1; } @@ -342,10 +497,12 @@ int el_file_open */ fclose(f); - remove(current_log); + remove(options->current_log); continue; } +#endif /* HAVE_STAT */ + /* * oldest file found, file is already opened so we simply return * from the function @@ -364,19 +521,27 @@ int el_file_open if (strlen(options->fname) > PATH_MAX) { - current_log[0] = '\0'; + options->current_log[0] = '\0'; errno = ENAMETOOLONG; return -1; } - strcpy(current_log, options->fname); + strcpy(options->current_log, options->fname); - if ((options->file = fopen(current_log, "a")) == NULL) + if ((options->file = fopen(options->current_log, "a")) == NULL) { - current_log[0] = '\0'; + /* + * we couldn't open file, but don't set clear options->current_log, + * we will try to reopen this file each time we print to file. This + * is usefull when user tries to open log file in not-yet existing + * directory. Error is of course returned to user is aware of this + * whole situation + */ + return -1; } + fseek(options->file, 0, SEEK_END); options->fpos = ftell(options->file); return 0; } @@ -387,31 +552,37 @@ 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 */ - /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + VALID(EINVAL, mem); + VALID(EINVAL, mlen); + VALID(EINVAL, options); + VALID(EBADF, options->current_log); + VALID(EBADF, options->current_log[0] != '\0'); - if (current_log[0] == '\0') - { - /* - * file has not been opened, prevent segfault - */ + /* + * we need to reopen our file if it wasn't opened yet, or was removed + * due to error or deliberate acion of user + */ - errno = EBADF; - return -1; + if (el_file_exists(options->current_log) == 0 || options->file == NULL) + { + if (el_file_open(options) != 0) + { + 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 @@ -427,7 +598,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 @@ -435,51 +606,118 @@ int el_file_puts * configured frotate_size */ - sl = options->frotate_size; + mlen = options->frotate_size; } } - if (el_file_exists() == 0) + if (fwrite(mem, mlen, 1, options->file) != 1) + { + return -1; + } + + 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) { /* - * file doesn't exist, it may happen when someone unlinks currently - * opened file. Even tough user unlinked our log file, we still have - * file opened, so this file exists in the system, but not in the - * file system tree and as such, every write to such file is - * effectively writing to /dev/null. To prevent that, we close and - * reopen our current log file + * 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 + * configures "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 (options->file) +#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) { - fclose(options->file); + return -1; } - if ((options->file = fopen(current_log, "a")) == NULL) +#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; } - options->fpos = 0; - } + fseek(options->file, 0, SEEK_END); + options->fpos = ftell(options->file); +#endif /* HAVE_FSYNC && HAVE_FILENO */ - if (fwrite(s, sl, 1, options->file) != 1) - { - return -1; - } +#ifdef RUN_TESTS + file_synced = 1; +#endif /* RUN_TESTS */ - if (fflush(options->file) != 0) - { - return -1; + options->written_after_sync = 0; } - options->fpos += sl; return 0; } /* ========================================================================== + 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 ========================================================================== */ @@ -495,5 +733,11 @@ void el_file_cleanup } options->file = NULL; - current_log[0] = '\0'; + + if (options->current_log) + { + options->current_log[0] = '\0'; + free(options->current_log); + options->current_log = NULL; + } } 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 b14b379..22b7e1a 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 /* ========================================================================== __ __ __ @@ -162,9 +153,16 @@ static int el_vooption { case EL_LEVEL: value_int = va_arg(ap, int); + VALID(EINVAL, value_int <= 7); options->level = value_int; return 0; + case EL_FILE_SYNC_LEVEL: + value_int = va_arg(ap, int); + VALID(EINVAL, value_int <= 7); + options->file_sync_level = value_int; + return 0; + case EL_OUT: value_int = va_arg(ap, int); VALID(EINVAL, (value_int & ~EL_OUT_ALL) == 0x00); @@ -179,7 +177,23 @@ static int el_vooption options->print_log_level = value_int; return 0; - #if ENABLE_COLORS + case EL_PRINT_NL: + value_int = va_arg(ap, int); + VALID(EINVAL, (value_int & ~1) == 0); + + options->print_newline = value_int; + return 0; + +# if ENABLE_PREFIX + + case EL_PREFIX: + value_str = va_arg(ap, const char *); + options->prefix = value_str; + return 0; + +# endif /* ENABLE_PREFIX */ + +# if ENABLE_COLORS case EL_COLORS: /* @@ -192,9 +206,9 @@ static int el_vooption options->colors = value_int; return 0; - #endif /* ENABLE_COLORS */ +# endif /* ENABLE_COLORS */ - #if ENABLE_TIMESTAMP +# if ENABLE_TIMESTAMP case EL_TS: value_int = va_arg(ap, int); @@ -203,24 +217,40 @@ static int el_vooption options->timestamp = value_int; return 0; +# if ENABLE_FRACTIONS + + case EL_TS_FRACT: + value_int = va_arg(ap, int); + VALID(EINVAL, 0 <= value_int && value_int < EL_TS_FRACT_ERROR); + + options->timestamp_fractions = value_int; + return 0; + +# endif /* ENABLE_FRACTIONS */ + case EL_TS_TM: value_int = va_arg(ap, int); VALID(EINVAL, 0 <= value_int && value_int < EL_TS_TM_ERROR); -#ifndef ENABLE_REALTIME + +# if ENABLE_REALTIME == 0 VALID(ENODEV, value_int != EL_TS_TM_REALTIME); -#endif +# endif -#ifndef ENABLE_MONOTONIC +# if ENABLE_MONOTONIC == 0 VALID(ENODEV, value_int != EL_TS_TM_MONOTONIC); -#endif +# endif + +# if ENABLE_CLOCK == 0 + VALID(ENODEV, value_int != EL_TS_TM_CLOCK); +# endif options->timestamp_timer = value_int; return 0; - #endif /* ENABLE_TIMESTAMP */ +# endif /* ENABLE_TIMESTAMP */ - #if ENABLE_FINFO +# if ENABLE_FINFO case EL_FINFO: value_int = va_arg(ap, int); @@ -229,11 +259,11 @@ static int el_vooption options->finfo = value_int; return 0; - #endif /* ENABLE_FINFO */ +# endif /* ENABLE_FINFO */ - #if ENABLE_OUT_FILE +# if ENABLE_OUT_FILE - case EL_FNAME: + case EL_FPATH: value_str = va_arg(ap, const char *); VALID(EINVAL, value_str); options->fname = value_str; @@ -272,9 +302,15 @@ static int el_vooption options->frotate_size = value_long; return 0; - #endif /* ENABLE_OUT_FILE */ + case EL_FILE_SYNC_EVERY: + value_long = va_arg(ap, long); + VALID(EINVAL, value_long >= 0); + options->file_sync_every = value_long; + return 0; + +# endif /* ENABLE_OUT_FILE */ - #if ENABLE_OUT_TTY +# if ENABLE_OUT_TTY case EL_TTY_DEV: { @@ -288,15 +324,15 @@ static int el_vooption return el_tty_open(options, value_str, speed); } - #endif /* ENABLE_OUT_TTY */ +# endif /* ENABLE_OUT_TTY */ - #if ENABLE_OUT_CUSTOM +# if ENABLE_OUT_CUSTOM case EL_CUSTOM_PUTS: options->custom_puts = va_arg(ap, el_custom_puts); return 0; - #endif /* ENABLE_OUT_CUSTOM */ +# endif /* ENABLE_OUT_CUSTOM */ default: /* @@ -361,8 +397,12 @@ int el_oinit memset(options, 0, sizeof(struct el_options)); options->print_log_level = 1; + options->print_newline = 1; options->level = EL_INFO; + options->level_current_msg = EL_DBG; + options->file_sync_level = EL_FATAL; options->serial_fd = -1; + options->file_sync_every = 32768; return 0; } 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..ae636ca --- /dev/null +++ b/src/el-pbinary.c @@ -0,0 +1,224 @@ +/* ========================================================================== + 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 ENABLE_TIMESTAMP + if (options->timestamp != EL_TS_OFF) + { + *buf |= FLAG_TS; + +# if ENABLE_FRACTIONS + /* + * fraction of seconds can be printed only when timestamp is on + */ + + *buf |= options->timestamp_fractions << FLAG_TS_FRACT_SHIFT; +# endif + } +#endif + + *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..a6dfa74 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> @@ -89,6 +88,12 @@ static int el_ovperror rc |= el_oprint(file, num, level, options, "errno num: %lu, strerror: %s", e, strerror(e)); + /* + * in case errno has been modified return it to value from before this + * call + */ + + errno = e; return rc; } 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 c0dda59..61d9a8e 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> @@ -89,6 +82,13 @@ static const char char_level[8] = { 'f', 'a', 'c', 'e', 'w', 'n', 'i', 'd' }; * colors indexes are synced with log level */ +#if ENABLE_COLORS_EXTENDED + +/* + * for those that like more colors, there are definitions with more colors! + * this will enable light version of some levels, but this is not supported + * on all terminal! You have been warned! + */ static const char *color[] = { @@ -103,7 +103,30 @@ static const char *color[] = "\e[0m" /* remove all formats */ }; -#endif +#else + +/* + * not all terminal can support extended colors with light version of them, + * for those who want to be more standard compliant there is this shortened + * version of colors. On downside is that some level will have same colors. + */ + +static const char *color[] = +{ + "\e[31m", /* fatal red */ + "\e[31m", /* alert red */ + "\e[35m", /* critical magenta */ + "\e[35m", /* error magenta */ + "\e[33m", /* warning yellow */ + "\e[32m", /* notice green */ + "\e[32m", /* information green */ + "\e[34m", /* debug blue */ + "\e[0m" /* remove all formats */ +}; + +#endif /* COLORS_EXTENDED */ + +#endif /* ENABLE_COLORS */ /* ========================================================================== @@ -159,184 +182,6 @@ static size_t el_color /* ========================================================================== - returns seconds and microseconds calculated from clock() function - ========================================================================== */ - - -#if ENABLE_TIMESTAMP - -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; -} - - -/* ========================================================================== - 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; - - case EL_TS_TM_CLOCK: - el_ts_clock(&s, &us); - break; - - 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 - } - - tl += sprintf(buf + tl, ".%06ld]", us); - - return tl; - -#else - return 0; -#endif /* ENABLE_TIMESTAMP */ -} - - -/* ========================================================================== returns pointer to where basename of 's' starts Examples: @@ -561,8 +406,9 @@ int el_ovprint * add preamble and colors to log line buf */ + 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] == ']') @@ -581,6 +427,31 @@ int el_ovprint w += sprintf(buf + w, "%c/", char_level[level]); } +#if ENABLE_PREFIX + if (options->prefix) + { + /* + * there is a case where buf[w] will point to something different + * than '\0'. This is not wrong but will confuse strncat function + * and logs will be printed incorectly. + */ + + buf[w] = '\0'; + strncat(buf + w, options->prefix, EL_PREFIX_LEN); + + if ((flen = strlen(options->prefix)) > EL_PREFIX_LEN) + { + /* + * dodge a bullet - overflow would have occured + */ + + flen = EL_PREFIX_LEN; + } + + w += flen; + } +#endif + /* * add requested log from format, we add + 1 to include null termination */ @@ -607,18 +478,42 @@ int el_ovprint w += el_color(options, buf + w, 8 /* reset colors */); + if (options->print_newline) + { + /* + * add new line to log + */ + + buf[w++] = '\n'; + } + + buf[w++] = '\0'; + /* - * make sure buf is always null terminated and contains new line character + * some modules (like el-file) needs to know level of message they are + * printing, and such information may not be available from string, thus + * we set it here in a 'object global' variable */ - buf[w++] = '\n'; - buf[w++] = '\0'; + options->level_current_msg = level; if (el_oputs(options, buf) != 0) { + options->level_current_msg = EL_DBG; return -1; } + /* + * after message is printed set current messasge level to debug, as next + * call might be using el_puts, which does not contain log level, and we + * thread all el_puts messages as they were debug. Note, it does not + * apply to log filtering, el_puts does not filter messages, but modules + * like el-file, will need this information to determin wheter fsync() + * data to file or not. + */ + + options->level_current_msg = EL_DBG; + if (e) { errno = e; @@ -627,3 +522,4 @@ int el_ovprint return 0; } + diff --git a/src/config-priv.h b/src/el-private.h index f662da1..c1c2d9b 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 @@ ========================================================================== */ - #if HAVE_CONFIG_H -#include "config.h" +# include "config.h" +#endif + +#include "embedlog.h" +#include "valid.h" + +#if 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 ========================================================================== */ @@ -75,30 +104,52 @@ /* ========================================================================== length of long timestamp in a single log. Timestamp format is - [yyyy-mm-dd hh:mm:ss.uuuuuu] + [yyyy-mm-dd hh:mm:ss] - which is 28 bytes long + which is 21 bytes long ========================================================================== */ -#if ENABLE_TIMESTAMP - #define EL_PRE_TS_LONG_LEN 28 -#else - #define EL_PRE_TS_LONG_LEN 0 -#endif +#define EL_PRE_TS_LONG_LEN 21 /* ========================================================================== length of short timestamp in a single log. Timestamp format is - [ssssssssss.uuuuuu] + [ssssssssss] - which is 19 bytes long. This is maximum value for short timestamp, as it + which is 12 bytes long. This is maximum value for short timestamp, as it can be shorter. ========================================================================== */ -#define EL_PRE_TS_SHORT_LEN 19 +#define EL_PRE_TS_SHORT_LEN 12 + + +/* ========================================================================== + Size of the fractions of seconds, that is part after seconds like: + + [ssssssssss.fffffffff] + + or + + [yyyy-mm-dd hh:mm:ss.fffffffff] + ========================================================================== */ + + +#define EL_PRE_TS_FRACT_LEN 10 + + +/* ========================================================================== + Calculate what is the minimum needed length to hold longest timestamp + ========================================================================== */ + + +#if ENABLE_TIMESTAMP +# define EL_PRE_TS_MAX (EL_PRE_TS_LONG_LEN + EL_PRE_TS_FRACT_LEN) +#else +# define EL_PRE_TS_MAX 0 +#endif /* ========================================================================== @@ -124,9 +175,9 @@ #if ENABLE_FINFO - #define EL_PRE_FINFO_LEN ((EL_FLEN_MAX) + 3 + EL_PRE_FINFO_LINE_MAX_LEN) +# define EL_PRE_FINFO_LEN ((EL_FLEN_MAX) + 3 + EL_PRE_FINFO_LINE_MAX_LEN) #else - #define EL_PRE_FINFO_LEN 0 +# define EL_PRE_FINFO_LEN 0 #endif @@ -144,11 +195,18 @@ /* ========================================================================== maximum length of preamble which may look like this - [2017-05-24 14:43:10.123456][source-file.c:12345] e/ + [2017-05-24 14:43:10.123456][source-file.c:12345] e/prefix ========================================================================== */ -#define EL_PRE_LEN (EL_PRE_TS_LONG_LEN + EL_PRE_FINFO_LEN + EL_PRE_LEVEL_LEN) +#if ENABLE_PREFIX +# define EL_PREFIX_LEN EL_PREFIX_MAX +#else +# define EL_PREFIX_LEN 0 +#endif + +#define EL_PRE_LEN (EL_PRE_TS_MAX + EL_PRE_FINFO_LEN + EL_PREFIX_LEN + \ + EL_PRE_LEVEL_LEN) /* ========================================================================== @@ -159,9 +217,9 @@ #if ENABLE_COLORS - #define EL_COLORS_LEN (5 + 4) +# define EL_COLORS_LEN (5 + 4) #else - #define EL_COLORS_LEN 0 +# define EL_COLORS_LEN 0 #endif @@ -175,4 +233,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) + + +/* ========================================================================== + ____ __ _ + / __/__ __ ____ _____ / /_ (_)____ ____ _____ + / /_ / / / // __ \ / ___// __// // __ \ / __ \ / ___/ + / __// /_/ // / / // /__ / /_ / // /_/ // / / /(__ ) + /_/ \__,_//_/ /_/ \___/ \__//_/ \____//_/ /_//____/ + + ========================================================================== */ + + +#if 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 + + +#if 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, void *buf, int binary); + +#if 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 c6d7119..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 /* ========================================================================== __ __ _ @@ -107,10 +98,10 @@ int el_oputs } #endif -#if 0 /* TODO */ +#if ENABLE_OUT_SYSLOG if (options->outputs & EL_OUT_SYSLOG) { - el_puts_syslog(s); + syslog(options->level, s); } #endif @@ -131,7 +122,7 @@ int el_oputs #if ENABLE_OUT_TTY if (options->outputs & EL_OUT_TTY) { - el_tty_puts(options, s); + rv |= el_tty_puts(options, s); } #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 new file mode 100644 index 0000000..259beb1 --- /dev/null +++ b/src/el-syslog.c @@ -0,0 +1,170 @@ +/* ========================================================================== + 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 <errno.h> +#include <fcntl.h> +#include <string.h> +#include <sys/types.h> +#include <unistd.h> +#if HAVE_TERMIOS_H +# include <termios.h> +#endif + + +/* ========================================================================== + __ __ _ + ____ __ __ / /_ / /(_)_____ + / __ \ / / / // __ \ / // // ___/ + / /_/ // /_/ // /_/ // // // /__ + / .___/ \__,_//_.___//_//_/ \___/ + /_/ + ____ __ _ + / __/__ __ ____ _____ / /_ (_)____ ____ _____ + / /_ / / / // __ \ / ___// __// // __ \ / __ \ / ___/ + / __// /_/ // / / // /__ / /_ / // /_/ // / / /(__ ) + /_/ \__,_//_/ /_/ \___/ \__//_/ \____//_/ /_//____/ + + ========================================================================== */ + + +/* ========================================================================== + Open tty device and configure it for output only + ========================================================================== */ + + +int el_tty_open +( + struct el_options *options, /* store serial file descriptor here */ + const char *dev, /* device to open like /dev/ttyS0 */ + speed_t speed /* serial port baud rate */ +) +{ + struct termios tty; /* serial port settings */ + int e; /* holder for errno */ + /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + + + /* + * it is possible to reopen serial device (like changing output device + * with EL_TTY_DEV again) during runtime, so we need to close previous + * socket first + */ + + el_tty_close(options); + + if ((options->serial_fd = open(dev, O_WRONLY | O_NOCTTY | O_SYNC)) < 0) + { + return -1; + } + +#if HAVE_TERMIOS_H + /* + * if termios is not available, simply open device without reconfiguring + * it. Some embedded OSes might configure tty during compilation and + * have termios disabled to save memory. + */ + + if (speed == B0) + { + /* + * normally with B0 we should terminate transmission, it is used + * with modem lines, but since we do not use modem lines, and in + * logger terminating connection does not make any sense, we use + * this to tell embedlog *not* to change any settings but only open + * serial + */ + + return 0; + } + + if (tcgetattr(options->serial_fd, &tty) != 0) + { + goto error; + } + + if (cfsetispeed(&tty, speed) != 0) + { + goto error; + } + + tty.c_cflag &= ~CSIZE; /* apply size mask */ + tty.c_cflag |= CS8; /* 8bit transmission */ + tty.c_cflag &= ~PARENB; /* no parity check */ + tty.c_cflag &= ~CSTOPB; /* set one stop bit */ + + tty.c_cflag |= CLOCAL; /* ignore modem lines */ + tty.c_cflag &= ~CREAD; /* disable receiver - we only send data */ + tty.c_oflag |= OPOST | ONLCR; /* enable output post-processing by OS */ + + if (tcsetattr(options->serial_fd, TCSANOW, &tty) != 0) + { + goto error; + } + + return 0; + +error: + e = errno; + close(options->serial_fd); + options->serial_fd = -1; + errno = e; + return -1; + +#else + (void)tty; + (void)e; + return 0; +#endif +} + + +/* ========================================================================== + Yup, you guessed right - it closes serial. + ========================================================================== */ + + +int el_tty_close +( + struct el_options *options /* options object with serial descriptor */ +) +{ + VALID(EINVAL, options->serial_fd != -1); + + close(options->serial_fd); + options->serial_fd = -1; + return 0; +} + + +/* ========================================================================== + Simply sends string pointed by s to serial port configured in options + ========================================================================== */ + + +int el_tty_puts +( + struct el_options *options, /* options object with serial descriptor */ + const char *s /* string to send */ +) +{ + return write(options->serial_fd, s, strlen(s)); +} diff --git a/src/el-ts.c b/src/el-ts.c new file mode 100644 index 0000000..503c437 --- /dev/null +++ b/src/el-ts.c @@ -0,0 +1,300 @@ +/* ========================================================================== + 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 nanoseconds calculated from clock() function + ========================================================================== */ + + +#if ENABLE_TIMESTAMP +#if ENABLE_CLOCK +static void el_ts_clock +( + time_t *s, /* seconds will be stored here */ + long *ns /* nanoseconds will be stored here */ +) +{ + clock_t clk; /* clock value */ + /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + + clk = clock(); + + *s = clk / CLOCKS_PER_SEC; + *ns = clk % CLOCKS_PER_SEC; /* [cps] */ + *ns *= (1000000L / CLOCKS_PER_SEC); /* [ms] */ + *ns *= 1000L; /* [ns] */ +} +#endif /* ENABLE_CLOCK */ +#endif /* ENABLE_TIMESTAMP */ + +/* ========================================================================== + returns seconds and nanoseconds calculated from time() function. + ========================================================================== */ + + +#if ENABLE_TIMESTAMP +static void el_ts_time +( + time_t *s, /* seconds will be stored here */ + long *ns /* nanoseconds will be stored here */ +) +{ + *s = time(NULL); + *ns = 0; +} +#endif /* ENABLE_TIMESTAMP */ + +/* ========================================================================== + returns seconds and nanoseconds 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 *ns, /* nanoseconds will be stored here */ + clockid_t clkid /* clock id */ +) +{ + struct timespec tp; /* clock value */ + /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + + clock_gettime(clkid, &tp); + + *s = tp.tv_sec; + *ns = tp.tv_nsec; +} +#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 */ + void *b, /* buffer where timestamp will be stored */ + int binary /* 1 if timestamp should be binary */ +) +{ +#if ENABLE_TIMESTAMP + time_t s; /* timestamp seconds */ + long ns; /* timestamp nanoseconds */ + size_t tl; /* timestamp length */ + char *buf; /* b threated as known type */ + /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + + if (options->timestamp == EL_TS_OFF) + { + /* + * user doesn't want us to print timestamp, that's fine + */ + + return 0; + } + + buf = b; + + /* + * first we get seconds and nanoseconds from proper timer + */ + + switch (options->timestamp_timer) + { +# if ENABLE_REALTIME + + case EL_TS_TM_REALTIME: + el_ts_clock_gettime(&s, &ns, CLOCK_REALTIME); + break; + +# endif + +# if ENABLE_MONOTONIC + + case EL_TS_TM_MONOTONIC: + el_ts_clock_gettime(&s, &ns, CLOCK_MONOTONIC); + break; + +# endif + + case EL_TS_TM_TIME: + el_ts_time(&s, &ns); + break; + +# if ENABLE_CLOCK + + case EL_TS_TM_CLOCK: + el_ts_clock(&s, &ns); + 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, (unsigned char *)buf); +# else + tl = el_encode_number((unsigned long)s, (unsigned char*)buf); +# endif + + /* + * put encoded nano/micro/milli seconds in buf if enabled + */ +# if ENABLE_FRACTIONS + + switch (options->timestamp_fractions) + { + case EL_TS_FRACT_OFF: + /* + * we don't add fractions, so simply return what has been + * already stored (only seconds) + */ + + return tl; + + case EL_TS_FRACT_MS: + tl += el_encode_number(ns / 1000000, buf + tl); + return tl; + + case EL_TS_FRACT_US: + tl += el_encode_number(ns / 1000, buf + tl); + return tl; + + case EL_TS_FRACT_NS: + tl += el_encode_number(ns, buf + tl); + return tl; + + default: + /* + * something went somehere seriously wrong, act like no + * timestamp has been configured + */ + + return 0; + } +#else /* ENABLE_FRACTIONS */ + return tl; +#endif /* ENABLE_FRACTIONS */ + } + 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 requested, add proper fractions of seconds + */ +# if ENABLE_FRACTIONS + switch (options->timestamp_fractions) + { + case EL_TS_FRACT_OFF: + /* + * we don't add fractions, so simply close opening bracker '[' + */ + + buf[tl++] = ']'; + return tl; + + case EL_TS_FRACT_MS: + tl += sprintf(buf + tl, ".%03ld]", ns / 1000000); + return tl; + + case EL_TS_FRACT_US: + tl += sprintf(buf + tl, ".%06ld]", ns / 1000); + return tl; + + case EL_TS_FRACT_NS: + tl += sprintf(buf + tl, ".%09ld]", ns); + return tl; + + default: + /* + * something went somehere seriously wrong, act like no + * timestamp has been configured + */ + + return 0; + } +# else /* ENABLE_FRACTIONS */ + buf[tl++] = ']'; + return tl; +# endif /* ENABLE_FRACTIONS */ + } + +#else /* ENABLE_TIMESTAMP */ + return 0; +#endif /* ENABLE_TIMESTAMP */ +} diff --git a/src/el-tty.c b/src/el-tty.c index 6e8300f..0b0791d 100644 --- a/src/el-tty.c +++ b/src/el-tty.c @@ -18,17 +18,16 @@ #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> #include <string.h> #include <sys/types.h> -#include <termios.h> #include <unistd.h> - +#if HAVE_TERMIOS_H +#include <termios.h> +#endif /* ========================================================================== __ __ _ @@ -59,7 +58,6 @@ int el_tty_open ) { struct termios tty; /* serial port settings */ - int sfd; /* serial port file descriptor */ int e; /* holder for errno */ /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ @@ -72,12 +70,32 @@ int el_tty_open el_tty_close(options); - if ((sfd = open(dev, O_WRONLY | O_NOCTTY | O_SYNC)) < 0) + if ((options->serial_fd = open(dev, O_WRONLY | O_NOCTTY | O_SYNC)) < 0) { return -1; } - if (tcgetattr(sfd, &tty) != 0) +#if HAVE_TERMIOS_H + /* + * if termios is not available, simply open device without reconfiguring + * it. Some embedded OSes might configure tty during compilation and + * have termios disabled to save memory. + */ + + if (speed == B0) + { + /* + * normally with B0 we should terminate transmission, it is used + * with modem lines, but since we do not use modem lines, and in + * logger terminating connection does not make any sense, we use + * this to tell embedlog *not* to change any settings but only open + * serial + */ + + return 0; + } + + if (tcgetattr(options->serial_fd, &tty) != 0) { goto error; } @@ -96,19 +114,25 @@ int el_tty_open tty.c_cflag &= ~CREAD; /* disable receiver - we only send data */ tty.c_oflag |= OPOST | ONLCR; /* enable output post-processing by OS */ - if (tcsetattr(sfd, TCSANOW, &tty) != 0) + if (tcsetattr(options->serial_fd, TCSANOW, &tty) != 0) { goto error; } - options->serial_fd = sfd; return 0; error: e = errno; - close(sfd); + close(options->serial_fd); + options->serial_fd = -1; errno = e; return -1; + +#else + (void)tty; + (void)e; + return 0; +#endif } 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/src/snprintf.c b/src/snprintf.c index 2eb2926..f26389c 100644 --- a/src/snprintf.c +++ b/src/snprintf.c @@ -411,10 +411,12 @@ int portable_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap); /* declarations */ +#if !defined(HAVE_SNPRINTF) static char credits[] = "\n\ @(#)snprintf.c, v2.2: Mark Martinec, <mark.martinec@ijs.si>\n\ @(#)snprintf.c, v2.2: Copyright 1999, Mark Martinec. Frontier Artistic License applies.\n\ @(#)snprintf.c, v2.2: http://www.ijs.si/software/snprintf/\n"; +#endif #if defined(NEED_ASPRINTF) int asprintf(char **ptr, const char *fmt, /*args*/ ...) { diff --git a/test-compilation.sh b/test-compilation.sh index 8db2336..ca6093c 100755 --- a/test-compilation.sh +++ b/test-compilation.sh @@ -4,10 +4,14 @@ options=(--disable-out-file \ --disable-out-stderr \ --disable-out-tty \ --disable-timestamp \ + --disable-fractions \ --disable-realtime \ --disable-monotonic \ --disable-finfo \ - --disable-colors) + --disable-colors \ + --disable-prefix \ + --disable-reentrant \ + --enable-binary-logs) iterations=$((2**${#options[@]} - 1)) for i in `seq 0 1 $iterations` diff --git a/tst/Makefile.am b/tst/Makefile.am index fe72bc6..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 @@ -16,6 +17,7 @@ test_CFLAGS = -I$(top_srcdir)/include \ -I$(top_srcdir)/src \ -I/usr/local/include \ -I/usr/include \ + -DRUN_TESTS=1 \ $(COVERAGE_CFLAGS) test_LDFLAGS = -L/usr/local/lib \ 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 2464de9..dd13683 100644 --- a/tst/test-el-file.c +++ b/tst/test-el-file.c @@ -20,6 +20,7 @@ #include <errno.h> #include <fcntl.h> #include <limits.h> +#include <stdlib.h> #include <string.h> #include <sys/stat.h> #include <sys/types.h> @@ -30,12 +31,12 @@ /* ========================================================================== - _ __ - ____ _____ (_)_ __ ____ _ / /_ ___ - / __ \ / ___// /| | / // __ `// __// _ \ - / /_/ // / / / | |/ // /_/ // /_ / __/ - / .___//_/ /_/ |___/ \__,_/ \__/ \___/ - /_/ + __ __ __ + ____ _ / /____ / /_ ____ _ / / + / __ `// // __ \ / __ \ / __ `// / + / /_/ // // /_/ // /_/ // /_/ // / + \__, //_/ \____//_.___/ \__,_//_/ + /____/ _ __ __ _ __ ____ _ _____ (_)____ _ / /_ / /___ _____ | | / // __ `// ___// // __ `// __ \ / // _ \ / ___/ @@ -45,11 +46,29 @@ ========================================================================== */ +/* + * variable is set in el-file.c when we successfully executed fsync path of + * the code + */ +int file_synced; + + +/* ========================================================================== + __ __ + _____ ____ ____ _____ / /_ ____ _ ____ / /_ _____ + / ___// __ \ / __ \ / ___// __// __ `// __ \ / __// ___/ + / /__ / /_/ // / / /(__ )/ /_ / /_/ // / / // /_ (__ ) + \___/ \____//_/ /_//____/ \__/ \__,_//_/ /_/ \__//____/ + + ========================================================================== */ + + #define WORKDIR "./embedlog-tests" #define s9 "123456789" #define s8 "qwertyui" #define s5 "qwert" #define s3 "asd" +#define s1 "a" mt_defs_ext(); @@ -106,8 +125,9 @@ static void test_prepare(void) el_option(EL_OUT, EL_OUT_FILE); el_option(EL_FROTATE_SIZE, 16); el_option(EL_FROTATE_NUMBER, 0); - el_option(EL_FNAME, WORKDIR"/log"); - + el_option(EL_FPATH, WORKDIR"/log"); + el_option(EL_FILE_SYNC_EVERY, 0); + file_synced = 0; } @@ -190,7 +210,8 @@ static void file_reopen(void) el_option(EL_OUT, EL_OUT_FILE); el_option(EL_FROTATE_SIZE, 16); el_option(EL_FROTATE_NUMBER, 0); - el_option(EL_FNAME, WORKDIR"/log"); + el_option(EL_FPATH, WORKDIR"/log"); + el_option(EL_FILE_SYNC_EVERY, 0); el_puts(s8); mt_fok(file_check(WORKDIR"/log", s9 s8)); @@ -210,7 +231,8 @@ static void file_reopen_different_file(void) el_option(EL_OUT, EL_OUT_FILE); el_option(EL_FROTATE_SIZE, 16); el_option(EL_FROTATE_NUMBER, 0); - el_option(EL_FNAME, WORKDIR"/log-another"); + el_option(EL_FPATH, WORKDIR"/log-another"); + el_option(EL_FILE_SYNC_EVERY, 0); el_puts(s8); mt_fok(file_check(WORKDIR"/log", s9)); @@ -235,6 +257,104 @@ static void file_unexpected_third_party_delete(void) ========================================================================== */ +static void file_directory_deleted(void) +{ + el_puts(s9); + unlink(WORKDIR"/log"); + rmdir(WORKDIR); + mt_ferr(el_puts(s9), ENOENT); + mkdir(WORKDIR, 0755); +} + + +/* ========================================================================== + ========================================================================== */ + + +static void file_directory_reappear_after_delete(void) +{ + el_puts(s9); + unlink(WORKDIR"/log"); + rmdir(WORKDIR); + mt_ferr(el_puts(s9), ENOENT); + mkdir(WORKDIR, 0755); + mt_fok(el_puts(s9)); + mt_fok(file_check(WORKDIR"/log", s9)); +} + + +/* ========================================================================== + ========================================================================== */ + + +static void file_write_after_failed_open(void) +{ + /* + * mt_prepare_test not running here + */ + + el_init(); + el_option(EL_OUT, EL_OUT_FILE); + el_option(EL_FILE_SYNC_EVERY, 0); + + rmdir(WORKDIR); + mt_ferr(el_option(EL_FPATH, WORKDIR"/log"), ENOENT); + mkdir(WORKDIR, 0755); + mt_fok(el_puts(s9)); + mt_fok(file_check(WORKDIR"/log", s9)); + + unlink(WORKDIR"/log"); + el_cleanup(); +} + + +/* ========================================================================== + ========================================================================== */ + + +static void file_write_after_failed_open_to_existing_file(void) +{ + /* + * mt_prepare_test not running here + */ + + el_init(); + el_option(EL_OUT, EL_OUT_FILE); + el_option(EL_FILE_SYNC_EVERY, 0); + + rmdir(WORKDIR); + mt_ferr(el_option(EL_FPATH, WORKDIR"/log"), ENOENT); + mkdir(WORKDIR, 0755); + mt_fok(system("echo test > \""WORKDIR"/log\"")); + mt_fok(el_puts(s9)); + mt_fok(file_check(WORKDIR"/log", "test\n"s9)); + + unlink(WORKDIR"/log"); + el_cleanup(); +} + + +/* ========================================================================== + ========================================================================== */ + + +static void file_and_directory_reapear(void) +{ + el_puts(s9); + unlink(WORKDIR"/log"); + rmdir(WORKDIR); + mt_ferr(el_puts(s9), ENOENT); + mkdir(WORKDIR, 0755); + mt_fok(system("echo test > \""WORKDIR"/log\"")); + mt_fok(el_puts(s9)); + mt_fok(file_check(WORKDIR"/log", "test\n"s9)); +} + + +/* ========================================================================== + ========================================================================== */ + + static void file_filename_too_long(void) { char path[8192]; @@ -243,7 +363,7 @@ static void file_filename_too_long(void) memset(path, 'a', sizeof(path)); path[sizeof(path) - 1] = '\0'; - mt_ferr(el_option(EL_FNAME, path), ENAMETOOLONG); + mt_ferr(el_option(EL_FPATH, path), ENAMETOOLONG); } @@ -265,7 +385,7 @@ static void file_path_too_long(void) path[sizeof(path) - 3] = 'l'; path[sizeof(path) - 2] = 'e'; path[sizeof(path) - 1] = '\0'; - mt_ferr(el_option(EL_FNAME, path), ENAMETOOLONG); + mt_ferr(el_option(EL_FPATH, path), ENAMETOOLONG); } @@ -289,9 +409,11 @@ static void file_print_after_cleanup(void) el_option(EL_OUT, EL_OUT_FILE); el_option(EL_FROTATE_SIZE, 16); el_option(EL_FROTATE_NUMBER, 0); - el_option(EL_FNAME, WORKDIR"/log"); + el_option(EL_FPATH, WORKDIR"/log"); + el_option(EL_FILE_SYNC_EVERY, 0); el_cleanup(); mt_ferr(el_puts("whatev"), ENODEV); + unlink(WORKDIR"/log"); } @@ -305,6 +427,7 @@ static void file_print_without_setting_file(void) el_option(EL_OUT, EL_OUT_FILE); el_option(EL_FROTATE_SIZE, 16); el_option(EL_FROTATE_NUMBER, 0); + el_option(EL_FILE_SYNC_EVERY, 0); mt_ferr(el_puts("no file set"), EBADF); el_cleanup(); } @@ -393,7 +516,8 @@ static void file_rotate_1_reopen(void) el_option(EL_OUT, EL_OUT_FILE); el_option(EL_FROTATE_SIZE, 16); el_option(EL_FROTATE_NUMBER, 1); - el_option(EL_FNAME, WORKDIR"/log"); + el_option(EL_FPATH, WORKDIR"/log"); + el_option(EL_FILE_SYNC_EVERY, 0); el_puts(s8); mt_fok(file_check(WORKDIR"/log.0", s5 s8)); @@ -535,7 +659,8 @@ static void file_rotate_2_reopen(void) el_option(EL_OUT, EL_OUT_FILE); el_option(EL_FROTATE_SIZE, 16); el_option(EL_FROTATE_NUMBER, 2); - el_option(EL_FNAME, WORKDIR"/log"); + el_option(EL_FPATH, WORKDIR"/log"); + el_option(EL_FILE_SYNC_EVERY, 0); el_puts(s5); mt_fok(file_check(WORKDIR"/log.0", s9)); @@ -716,7 +841,8 @@ static void file_rotate_5_reopen(void) el_option(EL_OUT, EL_OUT_FILE); el_option(EL_FROTATE_SIZE, 16); el_option(EL_FROTATE_NUMBER, 5); - el_option(EL_FNAME, WORKDIR"/log"); + el_option(EL_FPATH, WORKDIR"/log"); + el_option(EL_FILE_SYNC_EVERY, 0); el_puts(s9); el_puts(s8); @@ -754,20 +880,22 @@ static void file_rotate_5_hole_in_log_rotate(void) el_puts("123"); mt_fok(file_check(WORKDIR"/log.0", "qaz")); - mt_fok(file_check(WORKDIR"/log.1", "edc")); - mt_fok(file_check(WORKDIR"/log.2", "rfv")); + mt_fok(file_check(WORKDIR"/log.2", "edc")); + mt_fok(file_check(WORKDIR"/log.3", "rfv")); mt_fok(file_check(WORKDIR"/log.4", "123")); el_puts("456"); - mt_fok(file_check(WORKDIR"/log.0", "edc")); - mt_fok(file_check(WORKDIR"/log.1", "rfv")); + mt_fok(file_check(WORKDIR"/log.0", "qaz")); + mt_fok(file_check(WORKDIR"/log.1", "edc")); + mt_fok(file_check(WORKDIR"/log.2", "rfv")); mt_fok(file_check(WORKDIR"/log.3", "123")); mt_fok(file_check(WORKDIR"/log.4", "456")); el_puts("789"); - mt_fok(file_check(WORKDIR"/log.0", "rfv")); + mt_fok(file_check(WORKDIR"/log.0", "edc")); + mt_fok(file_check(WORKDIR"/log.1", "rfv")); mt_fok(file_check(WORKDIR"/log.2", "123")); mt_fok(file_check(WORKDIR"/log.3", "456")); mt_fok(file_check(WORKDIR"/log.4", "789")); @@ -804,7 +932,7 @@ static void file_rotate_5_rename_file_halfway(void) el_puts("rfv"); el_puts("tgb"); - el_option(EL_FNAME, WORKDIR"/log-another"); + el_option(EL_FPATH, WORKDIR"/log-another"); el_puts("123"); el_puts("456"); @@ -830,10 +958,313 @@ static void file_rotate_5_rename_file_halfway(void) ========================================================================== */ +static void file_rotate_directory_deleted(void) +{ + /* + * mt_prepare_test not running here + */ + + el_init(); + el_option(EL_OUT, EL_OUT_FILE); + el_option(EL_FILE_SYNC_EVERY, 0); + el_option(EL_FROTATE_SIZE, 3); + el_option(EL_FROTATE_NUMBER, 5); + mt_fok(el_option(EL_FPATH, WORKDIR"/log")); + + el_puts("qaz"); + el_puts("wsx"); + el_puts("edc"); + + unlink(WORKDIR"/log.0"); + unlink(WORKDIR"/log.1"); + unlink(WORKDIR"/log.2"); + rmdir(WORKDIR); + mt_ferr(el_puts("rfv"), ENOENT); + + mkdir(WORKDIR, 0755); +} + + +/* ========================================================================== + ========================================================================== */ + + +static void file_rotate_directory_reappear_after_delete(void) +{ + /* + * mt_prepare_test not running here + */ + + el_init(); + el_option(EL_OUT, EL_OUT_FILE); + el_option(EL_FILE_SYNC_EVERY, 0); + el_option(EL_FROTATE_SIZE, 3); + el_option(EL_FROTATE_NUMBER, 5); + mt_fok(el_option(EL_FPATH, WORKDIR"/log")); + + el_puts("qaz"); + el_puts("wsx"); + el_puts("edc"); + + unlink(WORKDIR"/log.0"); + unlink(WORKDIR"/log.1"); + unlink(WORKDIR"/log.2"); + rmdir(WORKDIR); + mt_ferr(el_puts("rfv"), ENOENT); + + mkdir(WORKDIR, 0755); + + mt_fok(el_puts("rfv")); + mt_fok(el_puts("tgb")); + mt_fok(el_puts("yhn")); + mt_fok(el_puts("ujm")); + mt_fok(file_check(WORKDIR"/log.0", "rfv")); + mt_fok(file_check(WORKDIR"/log.1", "tgb")); + mt_fok(file_check(WORKDIR"/log.2", "yhn")); + mt_fok(file_check(WORKDIR"/log.3", "ujm")); + unlink(WORKDIR"/log.0"); + unlink(WORKDIR"/log.1"); + unlink(WORKDIR"/log.2"); + unlink(WORKDIR"/log.3"); + unlink(WORKDIR"/log.4"); +} + + +/* ========================================================================== + ========================================================================== */ + + +static void file_rotate_write_after_failed_open(void) +{ + /* + * mt_prepare_test not running here + */ + + el_init(); + el_option(EL_OUT, EL_OUT_FILE); + el_option(EL_FILE_SYNC_EVERY, 0); + el_option(EL_FROTATE_SIZE, 3); + el_option(EL_FROTATE_NUMBER, 5); + + rmdir(WORKDIR); + mt_ferr(el_option(EL_FPATH, WORKDIR"/log"), ENOENT); + mkdir(WORKDIR, 0755); + mt_fok(el_puts("qaz")); + mt_fok(file_check(WORKDIR"/log.0", "qaz")); + + unlink(WORKDIR"/log.0"); + el_cleanup(); +} + + +/* ========================================================================== + ========================================================================== */ + + +static void file_rotate_write_after_failed_open_to_existing_file(void) +{ + /* + * mt_prepare_test not running here + */ + + el_init(); + el_option(EL_OUT, EL_OUT_FILE); + el_option(EL_FILE_SYNC_EVERY, 0); + el_option(EL_FROTATE_SIZE, 3); + el_option(EL_FROTATE_NUMBER, 5); + + rmdir(WORKDIR); + mt_ferr(el_option(EL_FPATH, WORKDIR"/log"), ENOENT); + mkdir(WORKDIR, 0755); + mt_fok(system("echo -n qaz > \""WORKDIR"/log.0\"")); + mt_fok(system("echo -n ws > \""WORKDIR"/log.1\"")); + mt_fok(el_puts("edc")); + mt_fok(file_check(WORKDIR"/log.0", "qaz")); + mt_fok(file_check(WORKDIR"/log.1", "ws")); + mt_fok(file_check(WORKDIR"/log.2", "edc")); + + unlink(WORKDIR"/log.0"); + unlink(WORKDIR"/log.1"); + unlink(WORKDIR"/log.2"); + el_cleanup(); +} + + +/* ========================================================================== + ========================================================================== */ + + +static void file_rotate_write_after_failed_open_to_existing_file_with_holes(void) +{ + /* + * mt_prepare_test not running here + */ + + el_init(); + el_option(EL_OUT, EL_OUT_FILE); + el_option(EL_FILE_SYNC_EVERY, 0); + el_option(EL_FROTATE_SIZE, 3); + el_option(EL_FROTATE_NUMBER, 5); + + rmdir(WORKDIR); + mt_ferr(el_option(EL_FPATH, WORKDIR"/log"), ENOENT); + mkdir(WORKDIR, 0755); + mt_fok(system("echo -n qaz > \""WORKDIR"/log.0\"")); + mt_fok(system("echo -n ws > \""WORKDIR"/log.2\"")); + mt_fok(system("echo -n e > \""WORKDIR"/log.4\"")); + mt_fok(el_puts("123")); + mt_fok(el_puts("456")); + + mt_fok(file_check(WORKDIR"/log.0", "ws")); + mt_fok(file_check(WORKDIR"/log.2", "e")); + mt_fok(file_check(WORKDIR"/log.3", "123")); + mt_fok(file_check(WORKDIR"/log.4", "456")); + + unlink(WORKDIR"/log.0"); + unlink(WORKDIR"/log.1"); + unlink(WORKDIR"/log.2"); + unlink(WORKDIR"/log.3"); + unlink(WORKDIR"/log.4"); + el_cleanup(); +} + + +/* ========================================================================== + ========================================================================== */ + + +static void file_rotate_write_after_failed_open_to_existing_file_with_holes2(void) +{ + /* + * mt_prepare_test not running here + */ + + el_init(); + el_option(EL_OUT, EL_OUT_FILE); + el_option(EL_FILE_SYNC_EVERY, 0); + el_option(EL_FROTATE_SIZE, 3); + el_option(EL_FROTATE_NUMBER, 5); + + rmdir(WORKDIR); + mt_ferr(el_option(EL_FPATH, WORKDIR"/log"), ENOENT); + mkdir(WORKDIR, 0755); + mt_fok(system("echo -n qaz > \""WORKDIR"/log.0\"")); + mt_fok(system("echo -n ws > \""WORKDIR"/log.3\"")); + mt_fok(system("echo -n e > \""WORKDIR"/log.4\"")); + mt_fok(el_puts("123")); + mt_fok(el_puts("456")); + + mt_fok(file_check(WORKDIR"/log.0", "qaz")); + mt_fok(file_check(WORKDIR"/log.1", "ws")); + mt_fok(file_check(WORKDIR"/log.2", "e")); + mt_fok(file_check(WORKDIR"/log.3", "123")); + mt_fok(file_check(WORKDIR"/log.4", "456")); + + unlink(WORKDIR"/log.0"); + unlink(WORKDIR"/log.1"); + unlink(WORKDIR"/log.2"); + unlink(WORKDIR"/log.3"); + unlink(WORKDIR"/log.4"); + el_cleanup(); +} + + +/* ========================================================================== + ========================================================================== */ + + +static void file_rotate_write_after_failed_open_to_existing_file_with_holes3(void) +{ + /* + * mt_prepare_test not running here + */ + + el_init(); + el_option(EL_OUT, EL_OUT_FILE); + el_option(EL_FILE_SYNC_EVERY, 0); + el_option(EL_FROTATE_SIZE, 3); + el_option(EL_FROTATE_NUMBER, 5); + + rmdir(WORKDIR); + mt_ferr(el_option(EL_FPATH, WORKDIR"/log"), ENOENT); + mkdir(WORKDIR, 0755); + mt_fok(system("echo -n qaz > \""WORKDIR"/log.1\"")); + mt_fok(system("echo -n ws > \""WORKDIR"/log.3\"")); + mt_fok(system("echo -n e > \""WORKDIR"/log.4\"")); + mt_fok(el_puts("123")); + mt_fok(el_puts("456")); + mt_fok(el_puts("789")); + mt_fok(file_check(WORKDIR"/log.0", "ws")); + mt_fok(file_check(WORKDIR"/log.1", "e")); + mt_fok(file_check(WORKDIR"/log.2", "123")); + mt_fok(file_check(WORKDIR"/log.3", "456")); + mt_fok(file_check(WORKDIR"/log.4", "789")); + + unlink(WORKDIR"/log.0"); + unlink(WORKDIR"/log.1"); + unlink(WORKDIR"/log.2"); + unlink(WORKDIR"/log.3"); + unlink(WORKDIR"/log.4"); + el_cleanup(); +} + + +/* ========================================================================== + ========================================================================== */ + + +static void file_rotate_and_directory_reappear(void) +{ + /* + * mt_prepare_test not running here + */ + + el_init(); + el_option(EL_OUT, EL_OUT_FILE); + el_option(EL_FILE_SYNC_EVERY, 0); + el_option(EL_FROTATE_SIZE, 3); + el_option(EL_FROTATE_NUMBER, 5); + mt_fok(el_option(EL_FPATH, WORKDIR"/log")); + + mt_fok(el_puts("123")); + mt_fok(el_puts("456")); + mt_fok(el_puts("789")); + + unlink(WORKDIR"/log.0"); + unlink(WORKDIR"/log.1"); + unlink(WORKDIR"/log.2"); + + rmdir(WORKDIR); + mt_ferr(el_puts(s9), ENOENT); + mkdir(WORKDIR, 0755); + + mt_fok(system("echo -n 123 > \""WORKDIR"/log.0\"")); + mt_fok(system("echo -n 456 > \""WORKDIR"/log.1\"")); + mt_fok(system("echo -n 789 > \""WORKDIR"/log.2\"")); + + mt_fok(el_puts("qaz")); + mt_fok(file_check(WORKDIR"/log.0", "123")); + mt_fok(file_check(WORKDIR"/log.1", "456")); + mt_fok(file_check(WORKDIR"/log.2", "789")); + mt_fok(file_check(WORKDIR"/log.3", "qaz")); + + unlink(WORKDIR"/log.0"); + unlink(WORKDIR"/log.1"); + unlink(WORKDIR"/log.2"); + unlink(WORKDIR"/log.3"); + unlink(WORKDIR"/log.4"); + el_cleanup(); +} + + +/* ========================================================================== + ========================================================================== */ + + static void file_no_dir_for_logs(void) { - mt_ferr(el_option(EL_FNAME, "/tmp/i-dont/exist"), ENOENT); - mt_ferr(el_puts("whatever"), EBADF); + mt_ferr(el_option(EL_FPATH, "/tmp/i-dont/exist"), ENOENT); + mt_ferr(el_puts("whatever"), ENOENT); } @@ -846,7 +1277,7 @@ static void file_dir_removed_after_open_then_created_back_again(void) mt_fok(el_puts(s8)); unlink(WORKDIR"/log"); rmdir(WORKDIR); - mt_ferr(el_puts(s3), EBADF); + mt_ferr(el_puts(s3), ENOENT); mkdir(WORKDIR, 0755); mt_fok(el_puts(s8)); } @@ -865,14 +1296,14 @@ static void file_dir_no_access(void) * root just doesn't give a fuck about no-write-permissions */ - mt_fok(el_option(EL_FNAME, "/tmp/embedlog-no-write/log")); + mt_fok(el_option(EL_FPATH, "/tmp/embedlog-no-write/log")); mt_fok(el_puts(s3)); mt_fok(file_check("/tmp/embedlog-no-write/log", s3)); } else { - mt_ferr(el_option(EL_FNAME, "/tmp/embedlog-no-write/log"), EACCES); - mt_ferr(el_puts(s3), EBADF); + mt_ferr(el_option(EL_FPATH, "/tmp/embedlog-no-write/log"), EACCES); + mt_ferr(el_puts(s3), EACCES); } unlink("/tmp/embedlog-no-write/log"); rmdir("/tmp/embedlog-no-write"); @@ -895,14 +1326,14 @@ static void file_no_access_to_file(void) if (getuid() == 0) { - mt_fok(el_option(EL_FNAME, "/tmp/embedlog-no-write/log")); + mt_fok(el_option(EL_FPATH, "/tmp/embedlog-no-write/log")); mt_fok(el_puts(s5)); mt_fok(file_check("/tmp/embedlog-no-write/log", s5)); } else { - mt_ferr(el_option(EL_FNAME, "/tmp/embedlog-no-write/log"), EACCES); - mt_ferr(el_puts("whatever"), EBADF); + mt_ferr(el_option(EL_FPATH, "/tmp/embedlog-no-write/log"), EACCES); + mt_ferr(el_puts("whatever"), EACCES); } unlink("/tmp/embedlog-no-write/log"); rmdir("/tmp/embedlog-no-write"); @@ -916,8 +1347,8 @@ static void file_no_access_to_file(void) static void file_rotate_no_dir_for_logs(void) { el_option(EL_FROTATE_NUMBER, 5); - mt_ferr(el_option(EL_FNAME, "/tmp/i-dont/exist"), ENOENT); - mt_ferr(el_puts("whatever"), EBADF); + mt_ferr(el_option(EL_FPATH, "/tmp/i-dont/exist"), ENOENT); + mt_ferr(el_puts("whatever"), ENOENT); } @@ -938,34 +1369,34 @@ static void file_rotate_dir_removed_after_open_then_created_back_again(void) unlink(WORKDIR"/log.1"); unlink(WORKDIR"/log.2"); rmdir(WORKDIR); - mt_ferr(el_puts(s3), EBADF); + mt_ferr(el_puts(s3), ENOENT); mkdir(WORKDIR, 0755); mt_fok(el_puts(s8)); mt_fok(el_puts(s5)); - mt_fok(file_check(WORKDIR"/log.2", s8 s5)); + mt_fok(file_check(WORKDIR"/log.0", s8 s5)); mt_fok(el_puts(s8)); mt_fok(el_puts(s3)); mt_fok(el_puts(s5)); - mt_fok(file_check(WORKDIR"/log.2", s8 s5)); - mt_fok(file_check(WORKDIR"/log.3", s8 s3 s5)); + mt_fok(file_check(WORKDIR"/log.0", s8 s5)); + mt_fok(file_check(WORKDIR"/log.1", s8 s3 s5)); mt_fok(el_puts(s9)); mt_fok(el_puts(s5)); - mt_fok(file_check(WORKDIR"/log.2", s8 s5)); - mt_fok(file_check(WORKDIR"/log.3", s8 s3 s5)); - mt_fok(file_check(WORKDIR"/log.4", s9 s5)); + mt_fok(file_check(WORKDIR"/log.0", s8 s5)); + mt_fok(file_check(WORKDIR"/log.1", s8 s3 s5)); + mt_fok(file_check(WORKDIR"/log.2", s9 s5)); mt_fok(el_puts(s3)); mt_fok(el_puts(s8)); - mt_fok(file_check(WORKDIR"/log.1", s8 s5)); - mt_fok(file_check(WORKDIR"/log.2", s8 s3 s5)); - mt_fok(file_check(WORKDIR"/log.3", s9 s5)); - mt_fok(file_check(WORKDIR"/log.4", s3 s8)); + mt_fok(file_check(WORKDIR"/log.0", s8 s5)); + mt_fok(file_check(WORKDIR"/log.1", s8 s3 s5)); + mt_fok(file_check(WORKDIR"/log.2", s9 s5)); + mt_fok(file_check(WORKDIR"/log.3", s3 s8)); } @@ -980,14 +1411,14 @@ static void file_rotate_dir_no_access(void) if (getuid() == 0) { - mt_fok(el_option(EL_FNAME, "/tmp/embedlog-no-write/log")); + mt_fok(el_option(EL_FPATH, "/tmp/embedlog-no-write/log")); mt_fok(el_puts(s3)); mt_fok(file_check("/tmp/embedlog-no-write/log.0", s3)); } else { - mt_ferr(el_option(EL_FNAME, "/tmp/embedlog-no-write/log"), EACCES); - mt_ferr(el_puts(s3), EBADF); + mt_ferr(el_option(EL_FPATH, "/tmp/embedlog-no-write/log"), EACCES); + mt_ferr(el_puts(s3), EACCES); } unlink("/tmp/embedlog-no-write/log.0"); @@ -1012,14 +1443,14 @@ static void file_rotate_no_access_to_file(void) if (getuid() == 0) { - mt_fok(el_option(EL_FNAME, "/tmp/embedlog-no-write/log")); + mt_fok(el_option(EL_FPATH, "/tmp/embedlog-no-write/log")); mt_fok(el_puts(s8)); mt_fok(file_check("/tmp/embedlog-no-write/log.0", s8)); } else { - mt_ferr(el_option(EL_FNAME, "/tmp/embedlog-no-write/log"), EACCES); - mt_ferr(el_puts("whatever"), EBADF); + mt_ferr(el_option(EL_FPATH, "/tmp/embedlog-no-write/log"), EACCES); + mt_ferr(el_puts("whatever"), EACCES); } unlink("/tmp/embedlog-no-write/log.0"); @@ -1040,7 +1471,7 @@ static void file_rotate_filename_too_long(void) memset(path, 'a', sizeof(path)); path[sizeof(path) - 1] = '\0'; el_option(EL_FROTATE_NUMBER, 5); - mt_ferr(el_option(EL_FNAME, path), ENAMETOOLONG); + mt_ferr(el_option(EL_FPATH, path), ENAMETOOLONG); } @@ -1063,7 +1494,7 @@ static void file_rotate_path_too_long(void) path[sizeof(path) - 2] = 'e'; path[sizeof(path) - 1] = '\0'; el_option(EL_FROTATE_NUMBER, 5); - mt_ferr(el_option(EL_FNAME, path), ENAMETOOLONG); + mt_ferr(el_option(EL_FPATH, path), ENAMETOOLONG); } @@ -1083,7 +1514,97 @@ static void file_rotate_fail(void) mkdir(WORKDIR, 0755); mt_fok(el_puts(s8)); - mt_fok(file_check(WORKDIR"/log.1", s8)); + mt_fok(file_check(WORKDIR"/log.0", s8)); +} + + +/* ========================================================================== + ========================================================================== */ + + +static void file_sync_always(void) +{ + el_option(EL_FILE_SYNC_EVERY, 0); + mt_fok(el_puts(s8)); + mt_fail(file_synced == 1); +} + + +/* ========================================================================== + ========================================================================== */ + + +static void file_sync_periodic(void) +{ + el_option(EL_FILE_SYNC_EVERY, 8); + mt_fok(el_puts(s5)); + mt_fail(file_synced == 0); + mt_fok(el_puts(s3)); + mt_fail(file_synced == 1); + file_synced = 0; + + mt_fok(el_puts(s5)); + mt_fail(file_synced == 0); + mt_fok(el_puts(s8)); + mt_fail(file_synced == 1); + file_synced = 0; + + mt_fok(el_puts(s5)); + mt_fail(file_synced == 0); + mt_fok(el_puts(s5)); + mt_fail(file_synced == 1); +} + + +/* ========================================================================== + ========================================================================== */ + + +static void file_sync_level(void) +{ + el_option(EL_FILE_SYNC_EVERY, 1024); + el_option(EL_FILE_SYNC_LEVEL, EL_ERROR); + el_option(EL_LEVEL, EL_DBG); + mt_fok(el_print(ELW, s8)); + mt_fail(file_synced == 0); + mt_fok(el_print(ELW, s8)); + mt_fail(file_synced == 0); + mt_fok(el_puts(s5)); + mt_fail(file_synced == 0); + + mt_fok(el_print(ELE, s1)); + mt_fail(file_synced == 1); + file_synced = 0; + + mt_fok(el_print(ELC, s1)); + mt_fail(file_synced == 1); + file_synced = 0; + + mt_fok(el_print(ELA, s1)); + mt_fail(file_synced == 1); + file_synced = 0; + + mt_fok(el_print(ELF, s1)); + mt_fail(file_synced == 1); + file_synced = 0; + + mt_fok(el_print(ELW, s1)); + mt_fail(file_synced == 0); + + mt_fok(el_print(ELN, s1)); + mt_fail(file_synced == 0); + + mt_fok(el_print(ELI, s1)); + mt_fail(file_synced == 0); + + mt_fok(el_print(ELD, s1)); + mt_fail(file_synced == 0); + + mt_fok(el_pmemory(ELI, s8, sizeof(s8))); + mt_fail(file_synced == 0); + + mt_fok(el_pmemory(ELF, s8, sizeof(s8))); + mt_fail(file_synced == 1); } @@ -1105,6 +1626,16 @@ void el_file_test_group(void) mt_run(file_print_without_init); mt_run(file_print_after_cleanup); mt_run(file_print_without_setting_file); + mt_run(file_write_after_failed_open); + mt_run(file_write_after_failed_open_to_existing_file); + mt_run(file_rotate_directory_deleted); + mt_run(file_rotate_directory_reappear_after_delete); + mt_run(file_rotate_write_after_failed_open); + mt_run(file_rotate_write_after_failed_open_to_existing_file); + mt_run(file_rotate_write_after_failed_open_to_existing_file_with_holes); + 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_prepare_test = &test_prepare; mt_cleanup_test = &test_cleanup; @@ -1114,6 +1645,9 @@ void el_file_test_group(void) mt_run(file_reopen); mt_run(file_reopen_different_file); mt_run(file_unexpected_third_party_delete); + mt_run(file_directory_deleted); + mt_run(file_directory_reappear_after_delete); + mt_run(file_and_directory_reapear); mt_run(file_filename_too_long); mt_run(file_path_too_long); mt_run(file_rotate_1_no_rotate); @@ -1154,6 +1688,9 @@ void el_file_test_group(void) mt_run(file_rotate_filename_too_long); mt_run(file_rotate_path_too_long); mt_run(file_rotate_fail); + mt_run(file_sync_always); + mt_run(file_sync_periodic); + mt_run(file_sync_level); rmdir(WORKDIR); #endif diff --git a/tst/test-el-options.c b/tst/test-el-options.c index b964f8d..153e699 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" @@ -93,22 +92,27 @@ static void options_init(void) memset(&default_options, 0, sizeof(default_options)); - default_options.outputs = 0; - default_options.level = EL_INFO; - default_options.colors = 0; - default_options.timestamp = EL_TS_OFF; - default_options.timestamp_timer = EL_TS_TM_CLOCK; - default_options.print_log_level = 1; - default_options.custom_puts = NULL; - default_options.serial_fd = -1; - - default_options.finfo = 0; - default_options.frotate_number = 0; - default_options.fcurrent_rotate = 0; - default_options.frotate_size = 0; - default_options.fpos = 0; - default_options.file = NULL; - default_options.fname = NULL; + default_options.outputs = 0; + default_options.level = EL_INFO; + default_options.file_sync_level = EL_FATAL; + default_options.level_current_msg = EL_DBG; + default_options.colors = 0; + default_options.timestamp = EL_TS_OFF; + default_options.timestamp_timer = EL_TS_TM_TIME; + default_options.timestamp_fractions = EL_TS_FRACT_OFF; + default_options.print_log_level = 1; + default_options.print_newline = 1; + default_options.custom_puts = NULL; + default_options.serial_fd = -1; + + default_options.finfo = 0; + default_options.frotate_number = 0; + default_options.fcurrent_rotate = 0; + default_options.frotate_size = 0; + default_options.fpos = 0; + default_options.file = NULL; + default_options.file_sync_every = 32768; + default_options.fname = NULL; mt_fail(el_oinit(&options) == 0); mt_fail(memcmp(&options, &default_options, sizeof(options)) == 0); @@ -142,10 +146,18 @@ static void options_level_set(void) /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ - for (i = 0; i != 32; ++i) + for (i = 0; i != 16; ++i) { - mt_fail(el_option(EL_LEVEL, i) == 0); - mt_fail(g_options.level == i); + if (i <= EL_DBG) + { + mt_fail(el_option(EL_LEVEL, i) == 0); + mt_fail(g_options.level == i); + } + else + { + mt_ferr(el_option(EL_LEVEL, i), EINVAL); + mt_fail(g_options.level == EL_DBG); + } } } @@ -154,13 +166,36 @@ static void options_level_set(void) ========================================================================== */ -static void options_output(void) +static void options_file_sync_level_set(void) { - int current_outputs; int i; /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ - current_outputs = 0; + + for (i = 0; i != 16; ++i) + { + if (i <= EL_DBG) + { + mt_fail(el_option(EL_FILE_SYNC_LEVEL, i) == 0); + mt_fail(g_options.file_sync_level == i); + } + else + { + mt_ferr(el_option(EL_FILE_SYNC_LEVEL, i), EINVAL); + mt_fail(g_options.file_sync_level == EL_DBG); + } + } +} + + +/* ========================================================================== + ========================================================================== */ + + +static void options_output(void) +{ + int i; + /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ for (i = 0; i != EL_OUT_ALL; ++i) { @@ -341,6 +376,14 @@ static void options_opt_timestamp_timer(void) } # endif +# if ENABLE_CLOCK == 0 + if (i == EL_TS_TM_CLOCK) + { + mt_ferr(el_option(EL_TS_TM, i), ENODEV); + continue; + } +# endif + mt_fok(el_option(EL_TS_TM, i)); mt_fail(g_options.timestamp_timer == i); #else @@ -356,6 +399,29 @@ static void options_opt_timestamp_timer(void) } +static void options_opt_timestamp_fraction(void) +{ + int i; + /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + + + for (i = EL_TS_FRACT_OFF; i != EL_TS_FRACT_ERROR; ++i) + { +#if ENABLE_FRACTIONS && ENABLE_TIMESTAMP + mt_fok(el_option(EL_TS_FRACT, i)); + mt_fail(g_options.timestamp_fractions == i); +#else + mt_ferr(el_option(EL_TS_FRACT, i), ENOSYS); +#endif + } + +#if ENABLE_FRACTIONS && ENABLE_TIMESTAMP + mt_ferr(el_option(EL_TS_FRACT, i), EINVAL); +#else + mt_ferr(el_option(EL_TS_FRACT, i), ENOSYS); +#endif +} + /* ========================================================================== ========================================================================== */ @@ -367,10 +433,10 @@ static void options_ooption_test(void) #if ENABLE_TIMESTAMP - el_ooption(&opts, EL_TS_TM, EL_TS_TM_CLOCK); - mt_fail(opts.timestamp_timer == EL_TS_TM_CLOCK); + el_ooption(&opts, EL_TS_TM, EL_TS_TM_TIME); + mt_fail(opts.timestamp_timer == EL_TS_TM_TIME); #else - mt_ferr(el_ooption(&opts, EL_TS_TM, EL_TS_TM_CLOCK), ENOSYS); + mt_ferr(el_ooption(&opts, EL_TS_TM, EL_TS_TM_TIME), ENOSYS); #endif } @@ -379,6 +445,24 @@ static void options_ooption_test(void) ========================================================================== */ +static void options_prefix(void) +{ + el_option(EL_PREFIX, "prefix"); +#if ENABLE_PREFIX + mt_fok(strcmp("prefix", g_options.prefix)); +#else + mt_fail(g_options.prefix == NULL); +#endif + + el_option(EL_PREFIX, NULL); + mt_fail(g_options.prefix == NULL); +} + + +/* ========================================================================== + ========================================================================== */ + + static void options_einval(void) { mt_ferr(el_option(10000, 5), EINVAL); @@ -404,12 +488,15 @@ void el_options_test_group(void) mt_cleanup_test = &test_cleanup; mt_run(options_level_set); + mt_run(options_file_sync_level_set); mt_run(options_output); mt_run(options_log_allowed); mt_run(options_opt_print_level); mt_run(options_opt_colors); mt_run(options_opt_timestamp); mt_run(options_opt_timestamp_timer); + mt_run(options_opt_timestamp_fraction); mt_run(options_ooption_test); mt_run(options_einval); + mt_run(options_prefix); } diff --git a/tst/test-el-pbinary.c b/tst/test-el-pbinary.c new file mode 100644 index 0000000..320be31 --- /dev/null +++ b/tst/test-el-pbinary.c @@ -0,0 +1,725 @@ +/* ========================================================================== + Licensed under BSD 2clause license See LICENSE file for more information + Author: Michał Łyszczek <michal.lyszczek@bofc.pl> + ========================================================================== */ + + +/* ========================================================================== + _ __ __ ____ _ __ + (_)____ _____ / /__ __ ____/ /___ / __/(_)/ /___ _____ + / // __ \ / ___// // / / // __ // _ \ / /_ / // // _ \ / ___/ + / // / / // /__ / // /_/ // /_/ // __/ / __// // // __/(__ ) + /_//_/ /_/ \___//_/ \__,_/ \__,_/ \___/ /_/ /_//_/ \___//____/ + + ========================================================================== */ + + +#include "config.h" + +#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" + +#if ENABLE_BINARY_LOGS + +/* ========================================================================== + _ __ __ + ____ _____ (_)_ __ ____ _ / /_ ___ / /_ __ __ ____ ___ _____ + / __ \ / ___// /| | / // __ `// __// _ \ / __// / / // __ \ / _ \ / ___/ + / /_/ // / / / | |/ // /_/ // /_ / __/ / /_ / /_/ // /_/ // __/(__ ) + / .___//_/ /_/ |___/ \__,_/ \__/ \___/ \__/ \__, // .___/ \___//____/ +/_/ /____//_/ + + ========================================================================== */ + + +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_fractions) + { + 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_FRACT, EL_TS_FRACT_OFF); + 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_FRACT, EL_TS_FRACT_OFF); + 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_fractions_ms(void) +{ + el_option(EL_TS_FRACT, EL_TS_FRACT_MS); + 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_fractions_us(void) +{ + el_option(EL_TS_FRACT, EL_TS_FRACT_US); + 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_fractions_ns(void) +{ + el_option(EL_TS_FRACT, EL_TS_FRACT_NS); + 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_fractions(void) +{ + el_option(EL_TS_FRACT, EL_TS_FRACT_OFF); + 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_fractions(void) +{ + el_option(EL_TS_FRACT, EL_TS_FRACT_OFF); + 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 fract; + int nl; + /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + + + for (fract = EL_TS_FRACT_OFF; fract != EL_TS_FRACT_ERROR; ++fract) + 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 (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); + el_option(EL_TS_FRACT, fract); + + 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_FRACT, EL_TS_FRACT_NS); + add_log(EL_INFO, msg, sizeof(msg)); + + /* + * tell checker that this is truncate test + */ + + truncate_test = 1; + mt_fok(pbinary_check()); +} + +#endif +/* ========================================================================== + __ __ + / /_ ___ _____ / /_ ____ _ _____ ____ __ __ ____ + / __// _ \ / ___// __/ / __ `// ___// __ \ / / / // __ \ + / /_ / __/(__ )/ /_ / /_/ // / / /_/ // /_/ // /_/ / + \__/ \___//____/ \__/ \__, //_/ \____/ \__,_// .___/ + /____/ /_/ + ========================================================================== */ + + +void el_pbinary_test_group(void) +{ +#if ENABLE_BINARY_LOGS + 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_ts_fractions_ms); + mt_run(pbinary_ts_fractions_us); + mt_run(pbinary_ts_fractions_ns); + mt_run(pbinary_timestamp_short_no_fractions); + mt_run(pbinary_timestamp_long_no_fractions); + mt_run(pbinary_with_no_output_available); + mt_run(pbinary_level_not_high_enough); + mt_run(pbinary_null); + mt_run(pbinary_truncate); +#endif +} diff --git a/tst/test-el-print.c b/tst/test-el-print.c index 6094c0f..2af6c92 100644 --- a/tst/test-el-print.c +++ b/tst/test-el-print.c @@ -19,13 +19,14 @@ #include <ctype.h> #include <errno.h> #include <libgen.h> +#include <stdint.h> +#include <math.h> #include "mtest.h" #include "stdlib.h" #include "embedlog.h" -#include "config-priv.h" -#include "el-options.h" +#include "el-private.h" /* ========================================================================== @@ -122,6 +123,7 @@ static int print_check(void) int i; int slevel; size_t msglen; +#if ENABLE_COLORS_EXTENDED static const char *color[] = { "\e[91m", /* fatal light red */ @@ -134,6 +136,20 @@ static int print_check(void) "\e[34m", /* debug blue */ "\e[0m" /* remove all formats */ }; +#else + static const char *color[] = + { + "\e[31m", /* fatal light red */ + "\e[31m", /* alert red */ + "\e[35m", /* critical light magenta */ + "\e[35m", /* error magenta */ + "\e[33m", /* warning light yellow */ + "\e[32m", /* notice light green */ + "\e[32m", /* information green */ + "\e[34m", /* debug blue */ + "\e[0m" /* remove all formats */ + }; +#endif /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ msg = logbuf; @@ -196,29 +212,46 @@ static int print_check(void) IS_CHAR(':'); IS_DIGIT(); IS_DIGIT(); - IS_CHAR('.'); - for (i = 0; i != 6; ++i) + if (g_options.timestamp_fractions) { - IS_DIGIT(); + IS_CHAR('.'); + + for (i = 0; i != 3 * g_options.timestamp_fractions; ++i) + { + IS_DIGIT(); + } } + IS_CHAR(']'); } else if (g_options.timestamp == EL_TS_SHORT) { IS_CHAR('['); - while (*msg != '.' ) + + if (g_options.timestamp_fractions) { - IS_DIGIT(); - } + while (*msg != '.') + { + IS_DIGIT(); + } - ++msg; /* skip the '.' character */ + ++msg; /* skip the '.' character */ - for (i = 0; i != 6; ++i) + for (i = 0; i != 3 * g_options.timestamp_fractions; ++i) + { + IS_DIGIT(); + } + } + else { - IS_DIGIT(); + while (*msg != ']') + { + IS_DIGIT(); + } } + IS_CHAR(']'); } else if (g_options.timestamp == EL_TS_OFF) @@ -315,8 +348,8 @@ static int print_check(void) g_options.timestamp != EL_TS_OFF) { /* - * file info or timestamp information is enabled, in that case - * we check for additional space between info and log message + * prefix or timestamp information is enabled, in that case + * we check for additional space between info and log message */ if (*msg++ != ' ') @@ -342,6 +375,27 @@ static int print_check(void) } } + /* + * check for prefix + */ + + if (g_options.prefix) + { + char expected_prefix[EL_PREFIX_LEN + 1] = {0}; + size_t expected_prefix_len; + /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + + strncat(expected_prefix, g_options.prefix, EL_PREFIX_LEN); + expected_prefix_len = strlen(expected_prefix); + + if (strncmp(expected_prefix, msg, expected_prefix_len) != 0) + { + return -1; + } + + msg += expected_prefix_len; + } + msglen = strlen(expected.msg); if (strncmp(msg, expected.msg, msglen) != 0) @@ -369,20 +423,25 @@ static int print_check(void) msg += 4; } - if (*msg != '\n') + if (g_options.print_newline) { + if (*msg != '\n') + { + /* + * when new line is enabled, log should end with new line + */ + + return -1; + } + /* - * all logs should be ended with new line + * set msg to point to next message, there is no need to move + * msg pointer if newline is not printed as msg already points + * to next message */ - return -1; + ++msg; } - - /* - * set msg to point to next message - */ - - ++msg; } return 0; @@ -469,6 +528,19 @@ static void print_simple_message(void) ========================================================================== */ +static void print_simple_message_no_newline(void) +{ + el_option(EL_PRINT_NL, 0); + add_log(ELF, "print simple no newline"); + add_log(ELF, "print simple no newline second"); + mt_fok(print_check()); +} + + +/* ========================================================================== + ========================================================================== */ + + static void print_simple_multiple_message(void) { add_log(ELF, "print_simple_multiple_message first"); @@ -505,7 +577,7 @@ static void print_log_level(void) static void print_colorful_output(void) { el_option(EL_COLORS, 1); - el_option(EL_LEVEL, EL_DBG + 2); + el_option(EL_LEVEL, EL_DBG); add_log(ELF, "print_colorful_output fatal message"); add_log(ELA, "print_colorful_output alert message"); add_log(ELC, "print_colorful_output critical message"); @@ -527,7 +599,7 @@ static void print_colorful_output(void) static void print_custom_log_level(void) { el_option(EL_PRINT_LEVEL, 1); - el_option(EL_LEVEL, EL_DBG + 5); + el_option(EL_LEVEL, EL_DBG); add_log(ELD + 4, "print_custom_log_level custom debug 4"); add_log(ELD + 5, "print_custom_log_level custom debug 5"); add_log(ELD + 6, "print_custom_log_level custom debug 6"); @@ -565,6 +637,118 @@ static void print_timestamp_long(void) ========================================================================== */ +static void print_timestamp_short_no_fractions(void) +{ + el_option(EL_TS_FRACT, EL_TS_FRACT_OFF); + el_option(EL_TS, EL_TS_SHORT); + add_log(ELF, "first meaningless message"); + add_log(ELF, "second stupid log"); + mt_fok(print_check()); +} + + +/* ========================================================================== + ========================================================================== */ + + +static void print_timestamp_long_no_fractions(void) +{ + el_option(EL_TS_FRACT, EL_TS_FRACT_OFF); + el_option(EL_TS, EL_TS_LONG); + add_log(ELF, "they don't even care"); + add_log(ELF, "what I put in here"); + mt_fok(print_check()); +} + + +/* ========================================================================== + ========================================================================== */ + + +static void print_timestamp_short_fractions_ms(void) +{ + el_option(EL_TS_FRACT, EL_TS_FRACT_MS); + el_option(EL_TS, EL_TS_SHORT); + add_log(ELF, "first meaningless message"); + add_log(ELF, "second stupid log"); + mt_fok(print_check()); +} + + +/* ========================================================================== + ========================================================================== */ + + +static void print_timestamp_long_fractions_ms(void) +{ + el_option(EL_TS_FRACT, EL_TS_FRACT_MS); + el_option(EL_TS, EL_TS_LONG); + add_log(ELF, "they don't even care"); + add_log(ELF, "what I put in here"); + mt_fok(print_check()); +} + + +/* ========================================================================== + ========================================================================== */ + + +static void print_timestamp_short_fractions_us(void) +{ + el_option(EL_TS_FRACT, EL_TS_FRACT_US); + el_option(EL_TS, EL_TS_SHORT); + add_log(ELF, "first meaningless message"); + add_log(ELF, "second stupid log"); + mt_fok(print_check()); +} + + +/* ========================================================================== + ========================================================================== */ + + +static void print_timestamp_long_fractions_us(void) +{ + el_option(EL_TS_FRACT, EL_TS_FRACT_US); + el_option(EL_TS, EL_TS_LONG); + add_log(ELF, "they don't even care"); + add_log(ELF, "what I put in here"); + mt_fok(print_check()); +} + + +/* ========================================================================== + ========================================================================== */ + + +static void print_timestamp_short_fractions_ns(void) +{ + el_option(EL_TS_FRACT, EL_TS_FRACT_NS); + el_option(EL_TS, EL_TS_SHORT); + add_log(ELF, "first meaningless message"); + add_log(ELF, "second stupid log"); + mt_fok(print_check()); +} + + +/* ========================================================================== + ========================================================================== */ + + +static void print_timestamp_long_fractions_ns(void) +{ + el_option(EL_TS_FRACT, EL_TS_FRACT_NS); + el_option(EL_TS, EL_TS_LONG); + add_log(ELF, "they don't even care"); + add_log(ELF, "what I put in here"); + mt_fok(print_check()); +} + + +/* ========================================================================== + ========================================================================== */ + + static void print_finfo(void) { el_option(EL_FINFO, 1); @@ -607,21 +791,29 @@ static void print_mix_of_everything(void) int printlevel; int finfo; int colors; + int prefix; + int fract; + int nl; /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ - for (level = EL_FATAL; level <= EL_DBG; ++level) + for (fract = EL_TS_FRACT_OFF; fract != EL_TS_FRACT_ERROR; ++fract) for (timestamp = EL_TS_OFF; timestamp != EL_TS_ERROR; ++timestamp) + for (level = EL_FATAL; level <= EL_DBG; ++level) 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 (finfo = 0; finfo <= 1; ++finfo) + 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(ELF, "fatal message"); add_log(ELA, "alert message"); @@ -631,6 +823,7 @@ static void print_mix_of_everything(void) add_log(ELN, "notice message"); add_log(ELI, "info message"); add_log(ELD, "debug message"); + mt_fok(print_check()); test_cleanup(); @@ -653,7 +846,41 @@ static void print_too_long_print_truncate(void) msg[sizeof(msg) - 2] = '3'; msg[sizeof(msg) - 3] = '2'; msg[sizeof(msg) - 4] = '1'; - msg[sizeof(msg) - 4] = '0'; + msg[sizeof(msg) - 5] = '0'; + + add_log(ELI, "not truncated"); + add_log(ELI, msg); + + /* + * while el_print will make copy of msg, our test print_check function + * will just use pointer to our msg here, and since we expect message to + * be truncated, we truncate it here and print_check will take this + * truncated message as expected one. + */ + + msg[sizeof(msg) - 3] = '\0'; + + mt_fok(print_check()); +} + + +/* ========================================================================== + ========================================================================== */ + + +static void print_too_long_print_truncate_no_newline(void) +{ + char msg[EL_LOG_MAX + 3]; + /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + + + el_option(EL_PRINT_NL, 0); + memset(msg, 'a', sizeof(msg)); + msg[sizeof(msg) - 1] = '\0'; + msg[sizeof(msg) - 2] = '3'; + msg[sizeof(msg) - 3] = '2'; + msg[sizeof(msg) - 4] = '1'; + msg[sizeof(msg) - 5] = '0'; add_log(ELI, "not truncated"); add_log(ELI, msg); @@ -704,14 +931,26 @@ static void print_truncate_with_date(void) static void print_truncate_with_all_options(void) { - char msg[EL_LOG_MAX + 3]; + char msg[EL_LOG_MAX + 3]; + char finfo[EL_FLEN_MAX + 1]; + char prefix[EL_PREFIX_MAX + 1]; + size_t fline; /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ el_option(EL_TS, EL_TS_LONG); + el_option(EL_TS_FRACT, EL_TS_FRACT_NS); + el_option(EL_TS_TM, EL_TS_TM_REALTIME); el_option(EL_FINFO, 1); + el_option(EL_COLORS, 1); + el_option(EL_PREFIX, prefix); el_option(EL_PRINT_LEVEL, 1); memset(msg, 'a', sizeof(msg)); + memset(finfo, 'b', sizeof(finfo)); + memset(prefix, 'c', sizeof(prefix)); + finfo[sizeof(finfo) - 1] = '\0'; + prefix[sizeof(prefix) - 1] = '\0'; + fline = (size_t)pow(10, EL_PRE_FINFO_LINE_MAX_LEN) - 1; msg[sizeof(msg) - 1] = '\0'; msg[sizeof(msg) - 2] = '3'; msg[sizeof(msg) - 3] = '2'; @@ -719,7 +958,7 @@ static void print_truncate_with_all_options(void) msg[sizeof(msg) - 4] = '0'; add_log(ELI, "not truncated"); - add_log(ELI, msg); + add_log(finfo, fline, EL_FATAL, msg); msg[sizeof(msg) - 3] = '\0'; @@ -781,6 +1020,59 @@ static void print_null(void) mt_ferr(el_print(ELA, NULL), EINVAL); } + +/* ========================================================================== + ========================================================================== */ + + +static void print_prefix(void) +{ + el_option(EL_PREFIX, "prefix"); + add_log(ELI, "message with prefix"); + mt_fok(print_check()); +} + + +/* ========================================================================== + ========================================================================== */ + + +static void print_prefix_full(void) +{ + char p[EL_PREFIX_LEN + 1] = {0}; + int i; + + for (i = 0; i != EL_PREFIX_LEN; ++i) + { + p[i] = '0' + (i % 32); + } + + el_option(EL_PREFIX, p); + add_log(ELI, "message with fill prefix"); + mt_fok(print_check()); +} + + +/* ========================================================================== + ========================================================================== */ + + +static void print_prefix_overflow(void) +{ + char p[EL_PREFIX_LEN + 10] = {0}; + int i; + + for (i = 0; i != sizeof(p) - 1; ++i) + { + p[i] = '0' + (i % 32); + } + + el_option(EL_PREFIX, p); + add_log(ELI, "message with overflown prefix"); + mt_fok(print_check()); +} + + /* ========================================================================== __ __ / /_ ___ _____ / /_ ____ _ _____ ____ __ __ ____ @@ -800,14 +1092,24 @@ void el_print_test_group(void) mt_cleanup_test = &test_cleanup; mt_run(print_simple_message); + mt_run(print_simple_message_no_newline); mt_run(print_simple_multiple_message); mt_run(print_log_level); mt_run(print_colorful_output); mt_run(print_custom_log_level); mt_run(print_timestamp_short); mt_run(print_timestamp_long); + mt_run(print_timestamp_short_no_fractions); + mt_run(print_timestamp_long_no_fractions); + mt_run(print_timestamp_short_fractions_ms); + mt_run(print_timestamp_long_fractions_ms); + mt_run(print_timestamp_short_fractions_us); + mt_run(print_timestamp_long_fractions_us); + mt_run(print_timestamp_short_fractions_ns); + mt_run(print_timestamp_long_fractions_ns); mt_run(print_finfo); mt_run(print_too_long_print_truncate); + mt_run(print_too_long_print_truncate_no_newline); mt_run(print_truncate_with_date); mt_run(print_truncate_with_all_options); mt_run(print_with_no_output_available); @@ -815,4 +1117,7 @@ void el_print_test_group(void) mt_run(print_finfo_path); mt_run(print_nofinfo); mt_run(print_null); + mt_run(print_prefix); + mt_run(print_prefix_full); + mt_run(print_prefix_overflow); } 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); |