diff options
author | Sehong Na <sehong.na@samsung.com> | 2014-05-31 12:35:53 +0900 |
---|---|---|
committer | Sehong Na <sehong.na@samsung.com> | 2014-05-31 12:35:53 +0900 |
commit | 951c077f8010db452abe48254fb0134ca0073613 (patch) | |
tree | 662671b7677a5ed34df3e69d958691b006d01b2d | |
download | dlog-951c077f8010db452abe48254fb0134ca0073613.tar.gz dlog-951c077f8010db452abe48254fb0134ca0073613.tar.bz2 dlog-951c077f8010db452abe48254fb0134ca0073613.zip |
Initialize Tizen 2.3submit/tizen_2.3/20140531.0654002.3a_release
42 files changed, 5184 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100755 index 0000000..8aee210 --- /dev/null +++ b/.gitignore @@ -0,0 +1,45 @@ +build_log +*.log +*.pyc +usr +opt +*.o +*.os +*.exe +packages +binaries +*.ipk +*~ +.deps/ +.libs/ +Makefile +Makefile.in +aclocal.m4 +build-stamp +config.guess +config.h +config.hin +config.status +config.sub +configure +debian/*.substvars +debian/*.install +debian/*.postinst +debian/files +debian/*.debhelper +debian/dlog-0-dbg/ +debian/dlog-dev/ +debian/dlogutil-bin/ +debian/libdlog-0/ +debian/tmp/ +depcomp +dlog.pc +dlogutil +install-sh +*.la +*.lo +m4/ +missing +stamp-h1 +libtool +ltmain.sh @@ -0,0 +1,3 @@ +juho son <juho80.son at samsung dot com> +Suchang Woo <suchang.woo at samsung dot com> +kyungmin Park <kyungmin Park at samsung dot com> diff --git a/LICENSE.Apache-2.0 b/LICENSE.Apache-2.0 new file mode 100755 index 0000000..8aa906c --- /dev/null +++ b/LICENSE.Apache-2.0 @@ -0,0 +1,205 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + + diff --git a/Makefile.am b/Makefile.am new file mode 100755 index 0000000..a822503 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,35 @@ +AM_CFLAGS = -I$(srcdir)/include $(DEBUG_CFLAGS) -Werror + +dlog_includedir = $(includedir)/dlog +dlog_include_HEADERS = \ + include/dlog.h + +lib_LTLIBRARIES = libdlog.la + +libdlog_la_SOURCES = \ + src/libdlog/log.c \ + include/dlog.h \ + src/libdlog/logconfig.c \ + include/logconfig.h \ + src/libdlog/loglimiter.c \ + include/loglimiter.h + +libdlog_la_LIBADD = -lpthread $(systemd_journal_LIBS) + +bin_PROGRAMS = dlogutil dlog_logger + +dlogutil_SOURCES = \ + src/shared/logprint.c \ + src/logutil/logutil.c \ + include/logger.h \ + include/logprint.h + +dlog_logger_SOURCES = \ + src/shared/logprint.c \ + src/logger/logger.c \ + include/logger.h \ + include/logprint.h + +# conf file +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = dlog.pc @@ -0,0 +1,4 @@ +Copyright (c) The Android Open Source Project +Copyright (c) Samsung Electronics Co., Ltd. All rights reserved. +Except as noted, this software is licensed under Apache License, Version 2. +Please, see the LICENSE.Apache-2.0 file for Apache License terms and conditions. diff --git a/TC/_export_env.sh b/TC/_export_env.sh new file mode 100755 index 0000000..b354434 --- /dev/null +++ b/TC/_export_env.sh @@ -0,0 +1,30 @@ +# Customize below path information +TET_INSTALL_PATH=/scratchbox/TETware + +TET_SIMUL_PATH=$TET_INSTALL_PATH/tetware-simulator +TET_TARGET_PATH=$TET_INSTALL_PATH/tetware-target +TET_MOUNTED_PATH=/mnt/nfs/TETware/tetware-target + +MACHINE=`echo $SBOX_UNAME_MACHINE` + +if [ $MACHINE = "i686" ] # Scratchbox i686 +then + export ARCH=simulator + export TET_ROOT=$TET_SIMUL_PATH +elif [ $MACHINE = "arm" ] # Scratchbox ARM +then + export ARCH=target + export TET_ROOT=$TET_TARGET_PATH +else + export ARCH=target + export TET_ROOT=$TET_MOUNTED_PATH +fi + +export PATH=$TET_ROOT/bin:$PATH +export LD_LIBRARY_PATH=$TET_ROOT/lib/tet3:$LD_LIBRARY_PATH + +set $(pwd) +export TET_SUITE_ROOT=$1 + +set $(date +%y%m%d_%H%M%S) +FILE_NAME_EXTENSION=$1 diff --git a/TC/build.sh b/TC/build.sh new file mode 100755 index 0000000..8518d22 --- /dev/null +++ b/TC/build.sh @@ -0,0 +1,20 @@ +#!/bin/sh + +export TET_INSTALL_PATH=/home/jeesun1981/TETware # local tetware path +export TET_TARGET_PATH=$TET_INSTALL_PATH/tetware-target +export PATH=$TET_TARGET_PATH/bin:$PATH +export LD_LIBRARY_PATH=$TET_TARGET_PATH/lib/tet3:$LD_LIBRARY_PATH +export TET_ROOT=$TET_TARGET_PATH + +export TET_SUITE_ROOT=`pwd` +FILE_NAME_EXTENSION=`date +%s` + +RESULT_DIR=results +HTML_RESULT=$RESULT_DIR/build-tar-result-$FILE_NAME_EXTENSION.html +JOURNAL_RESULT=$RESULT_DIR/build-tar-result-$FILE_NAME_EXTENSION.journal + +mkdir -p $RESULT_DIR + +tcc -c -p ./ +tcc -b -j $JOURNAL_RESULT -p ./ +grw -c 3 -f chtml -o $HTML_RESULT $JOURNAL_RESULT diff --git a/TC/execute.sh b/TC/execute.sh new file mode 100755 index 0000000..6720da0 --- /dev/null +++ b/TC/execute.sh @@ -0,0 +1,19 @@ +#!/bin/sh +export TET_INSTALL_PATH=/mnt/nfs/tetware +export TET_TARGET_PATH=$TET_INSTALL_PATH/tetware-target +export PATH=$TET_TARGET_PATH/bin:$PATH +export LD_LIBRARY_PATH=$TET_TARGET_PATH/lib/tet3:$LD_LIBRARY_PATH + +export TET_ROOT=$TET_TARGET_PATH + +export TET_SUITE_ROOT=`pwd` +FILE_NAME_EXTENSION=`date +%s` + +RESULT_DIR=results +HTML_RESULT=$RESULT_DIR/exec-tar-result-$FILE_NAME_EXTENSION.html +JOURNAL_RESULT=$RESULT_DIR/exec-tar-result-$FILE_NAME_EXTENSION.journal + +mkdir -p $RESULT_DIR + +tcc -e -j $JOURNAL_RESULT -p ./ +grw -c 3 -f chtml -o $HTML_RESULT $JOURNAL_RESULT diff --git a/TC/tet_code b/TC/tet_code new file mode 100755 index 0000000..a2cf6c1 --- /dev/null +++ b/TC/tet_code @@ -0,0 +1,12 @@ +# TET reserved codes +0 "PASS" +1 "FAIL" +2 "UNRESOLVED" +3 "NOTINUSE" +4 "UNSUPPORTED" +5 "UNTESTED" +6 "UNINITIATED" +7 "NORESULT" + +# Test suite additional codes +33 "INSPECT" diff --git a/TC/tet_scen b/TC/tet_scen new file mode 100755 index 0000000..43cbc9b --- /dev/null +++ b/TC/tet_scen @@ -0,0 +1,7 @@ +all + ^TEST +##### Scenarios for TEST ##### + +# Test scenario +TEST + :include:/unit/tslist diff --git a/TC/tetbuild.cfg b/TC/tetbuild.cfg new file mode 100755 index 0000000..6192c78 --- /dev/null +++ b/TC/tetbuild.cfg @@ -0,0 +1,3 @@ +TET_OUTPUT_CAPTURE=False +TET_BUILD_TOOL=make +TET_PASS_TC_NAME=True diff --git a/TC/tetclean.cfg b/TC/tetclean.cfg new file mode 100755 index 0000000..c66eda4 --- /dev/null +++ b/TC/tetclean.cfg @@ -0,0 +1,2 @@ +TET_OUTPUT_CAPTURE=False +TET_CLEAN_TOOL=make clean diff --git a/TC/tetexec.cfg b/TC/tetexec.cfg new file mode 100755 index 0000000..0d9d39a --- /dev/null +++ b/TC/tetexec.cfg @@ -0,0 +1 @@ +TET_OUTPUT_CAPTURE=False diff --git a/TC/unit/Makefile b/TC/unit/Makefile new file mode 100644 index 0000000..7be4c17 --- /dev/null +++ b/TC/unit/Makefile @@ -0,0 +1,23 @@ +CC ?= gcc + +TARGETS = utc_ApplicationFW___dlog_print_func \ + utc_ApplicationFW___dlog_vprint_func + +PKGS = dlog + +LDFLAGS = `pkg-config --libs $(PKGS)` +LDFLAGS += $(TET_ROOT)/lib/tet3/tcm_s.o +LDFLAGS += -L$(TET_ROOT)/lib/tet3 -ltcm_s +LDFLAGS += -L$(TET_ROOT)/lib/tet3 -lapi_s + +CFLAGS = -I. `pkg-config --cflags $(PKGS)` +CFLAGS += -I$(TET_ROOT)/inc/tet3 +CFLAGS += -Wall + +all: $(TARGETS) + +$(TARGETS): %: %.c + $(CC) -o $@ $< $(CFLAGS) $(LDFLAGS) + +clean: + rm -f $(TARGETS) diff --git a/TC/unit/tslist b/TC/unit/tslist new file mode 100644 index 0000000..0d9e63d --- /dev/null +++ b/TC/unit/tslist @@ -0,0 +1,2 @@ +/unit/utc_ApplicationFW___dlog_print_func +/unit/utc_ApplicationFW___dlog_vprint_func diff --git a/TC/unit/utc_ApplicationFW___dlog_print_func.c b/TC/unit/utc_ApplicationFW___dlog_print_func.c new file mode 100644 index 0000000..589c4f9 --- /dev/null +++ b/TC/unit/utc_ApplicationFW___dlog_print_func.c @@ -0,0 +1,66 @@ +#include <tet_api.h> +#include "dlog.h" +#define LOG_BUF_SIZE 1024 +static void startup(void); +static void cleanup(void); + +void (*tet_startup)(void) = startup; +void (*tet_cleanup)(void) = cleanup; + +static void utc_ApplicationFW___dlog_print_func_01(void); +static void utc_ApplicationFW___dlog_print_func_02(void); + +enum { + POSITIVE_TC_IDX = 0x01, + NEGATIVE_TC_IDX, +}; + +struct tet_testlist tet_testlist[] = { + { utc_ApplicationFW___dlog_print_func_01, POSITIVE_TC_IDX }, + { utc_ApplicationFW___dlog_print_func_02, NEGATIVE_TC_IDX }, + { NULL, 0 } +}; + +//static int pid; + +static void startup(void) +{ +} + +static void cleanup(void) +{ +} + +/** + * @brief Positive test case of __dlog_print() + */ +static void utc_ApplicationFW___dlog_print_func_01(void) +{ + int r = 0; + + r = __dlog_print(LOG_ID_MAIN, DLOG_DEBUG,"DLOG_TEST", "dlog test message for tetware\n"); + + if (r<0) { + tet_printf("__dlog_print() failed in positive test case"); + tet_result(TET_FAIL); + return; + } + tet_result(TET_PASS); +} + +/** + * @brief Negative test case of ug_init __dlog_print() + */ +static void utc_ApplicationFW___dlog_print_func_02(void) +{ + int r = 0; + + r = __dlog_print( LOG_ID_MAX , DLOG_DEBUG,"DLOG_TEST", "dlog test message for tetware\n"); + + if (r>=0) { + tet_printf("__dlog_print() failed in negative test case"); + tet_result(TET_FAIL); + return; + } + tet_result(TET_PASS); +} diff --git a/TC/unit/utc_ApplicationFW___dlog_vprint_func.c b/TC/unit/utc_ApplicationFW___dlog_vprint_func.c new file mode 100644 index 0000000..9e4a30b --- /dev/null +++ b/TC/unit/utc_ApplicationFW___dlog_vprint_func.c @@ -0,0 +1,76 @@ +#include <stdarg.h> +#include <tet_api.h> +#include "dlog.h" +#define LOG_BUF_SIZE 1024 +static void startup(void); +static void cleanup(void); + +void (*tet_startup)(void) = startup; +void (*tet_cleanup)(void) = cleanup; + +static void utc_ApplicationFW___dlog_vprint_func_01(void); +static void utc_ApplicationFW___dlog_vprint_func_02(void); + +enum { + POSITIVE_TC_IDX = 0x01, + NEGATIVE_TC_IDX, +}; + +struct tet_testlist tet_testlist[] = { + { utc_ApplicationFW___dlog_vprint_func_01, POSITIVE_TC_IDX }, + { utc_ApplicationFW___dlog_vprint_func_02, NEGATIVE_TC_IDX }, + { NULL, 0 } +}; + +static int pid; +char *fmt = "dlog test message for tetware\n"; + +static void startup(void) +{ +} + +static void cleanup(void) +{ +} +/** + * @brief Positive test case of __dlog_vprint() + */ +void utc_ApplicationFW___dlog_vprint_func_01(void) +{ + int r = 0; + char buf[LOG_BUF_SIZE]; + va_list ap; + + /* va_start(ap, fmt);*/ + + r = __dlog_vprint(LOG_ID_MAIN, DLOG_DEBUG, "DLOG_TEST", buf, ap ); + /* va_end(ap);*/ + + if (r<0) { + tet_printf("__dlog_vprint() failed in positive test case"); + tet_result(TET_FAIL); + return; + } + tet_result(TET_PASS); +} + +/** + * @brief Negative test case of ug_init __dlog_vprint() + */ +void utc_ApplicationFW___dlog_vprint_func_02(void) +{ + int r = 0; + char buf[LOG_BUF_SIZE]; + va_list ap; +// va_start(ap, fmt); + + r = __dlog_vprint(LOG_ID_MAX, DLOG_DEBUG,"DLOG_TEST", fmt, ap ); +// va_end(ap); + + if (r>=0) { + tet_printf("__dlog_vprint() failed in negative test case"); + tet_result(TET_FAIL); + return; + } + tet_result(TET_PASS); +} diff --git a/autogen.sh b/autogen.sh new file mode 100755 index 0000000..798eba2 --- /dev/null +++ b/autogen.sh @@ -0,0 +1,6 @@ +aclocal +libtoolize --copy +autoheader +autoconf +automake --add-missing --copy --foreign + diff --git a/configure.ac b/configure.ac new file mode 100755 index 0000000..a5f2a1e --- /dev/null +++ b/configure.ac @@ -0,0 +1,86 @@ +# -*- Autoconf -*- +# Process this file with autoconf to produce a configure script. + +AC_PREREQ(2.61) +AC_INIT([dlog], [1.0], yk.yun@samsung.com) +AM_INIT_AUTOMAKE([-Wall -Werror foreign]) +AC_CONFIG_HEADERS([config.h:config.hin]) + +AC_CONFIG_MACRO_DIR([m4]) +# Init XO +PLATFORM_INIT + +# Checks for programs. +dnl AC_PROG_CXX +m4_ifdef([AM_PROG_AR], [AM_PROG_AR]) +AC_PROG_CC +AC_PROG_GCC_TRADITIONAL +AC_PROG_LIBTOOL + +# check for libsystemd-journal +AC_ARG_WITH([systemd-journal], + AS_HELP_STRING([--with-systemd-journal], [check for systemd-journal]), + [], [with_systemd_journal=check]) +have_systemd_journal=no +if test "x$with_systemd_journal" != "xno"; then + PKG_CHECK_MODULES(systemd_journal, [libsystemd-journal], + [AC_DEFINE(HAVE_SYSTEMD_JOURNAL, 1, [Define if systemd journal is available]) + have_systemd_journal=yes], + have_systemd_journal=no) + if test "x$have_systemd_journal" = "xno" -a "x$with_systemd_journal" = "xyes"; then + AC_MSG_ERROR([systemd journal requested but libraries not found]) + fi +fi +AM_CONDITIONAL(HAVE_SYSTEMD_JOURNAL, [test "x$have_systemd_journal" = "xyes"]) +if test "x$have_systemd_journal" = "xyes" ; then + systemd_journal=systemd-journal +fi +AC_SUBST(systemd_journal) + +# check binary type for tizen engineer mode +AC_ARG_ENABLE([engineer_mode], + AS_HELP_STRING([--enable-engineer_mode Turn on engineer_mode]), + [engineer_mode=yes], + engineer_mode=no) +if test "x$engineer_mode" = "xyes" ; then + DEBUG_CFLAGS+=" -DTIZEN_ENGINEER_MODE" +fi +AC_ARG_ENABLE([debug_enable], + AS_HELP_STRING([--enable-debug_enable Turn on debug_enable]), + [debug_enable=yes], + debug_enable=no) +if test "x$debug_enable" = "xyes" ; then + DEBUG_CFLAGS+=" -DTIZEN_DEBUG_ENABLE" +fi +# check for fatal_on +AC_ARG_ENABLE([fatal_on], + AS_HELP_STRING([--enable-fatal_on Turn on fatal assertion]), + [fatal_on=yes], + fatal_on=no) +if test "x$fatal_on" = "xyes" ; then + DEBUG_CFLAGS+=" -DFATAL_ON" +fi +AC_SUBST(DEBUG_CFLAGS) +dnl AC_SUBST(ACLOCAL_AMFLAGS, "-I m4") +# Checks for libraries. +# Checks for header files. +AC_HEADER_STDC +AC_CHECK_HEADERS([stdlib.h unistd.h ]) + +# Checks for typedefs, structures, and compiler characteristics. +AC_HEADER_STDBOOL +AC_C_CONST +AC_C_INLINE +AC_STRUCT_TM +AC_TYPE_PID_T +AC_TYPE_SIZE_T +AC_TYPE_SSIZE_T + +# Checks for library functions. +AC_FUNC_MALLOC +AC_FUNC_STAT +AC_CHECK_FUNCS([memset]) + +# output files +AC_CONFIG_FILES([Makefile dlog.pc]) +AC_OUTPUT diff --git a/dlog.pc.in b/dlog.pc.in new file mode 100755 index 0000000..db6484a --- /dev/null +++ b/dlog.pc.in @@ -0,0 +1,12 @@ +prefix=/usr +exec_prefix=${prefix} +libdir=${exec_prefix}/lib +includedir=${prefix}/include + +Name: dlog +Description: logging service +Version: 1.0 +Requires: +Requires(post): @systemd_journal@ +Libs: -L${libdir} -ldlog -lpthread @systemd_journal_LIBS@ +Cflags: -I${includedir}/dlog diff --git a/image/SLP_Dlog_PG_image001.png b/image/SLP_Dlog_PG_image001.png Binary files differnew file mode 100755 index 0000000..7bdd14c --- /dev/null +++ b/image/SLP_Dlog_PG_image001.png diff --git a/include/SLP_Dlog_PG.h b/include/SLP_Dlog_PG.h new file mode 100755 index 0000000..a108b5f --- /dev/null +++ b/include/SLP_Dlog_PG.h @@ -0,0 +1,183 @@ +/** + @ingroup SLP_PG + @defgroup SLP_PG_Dlog dlog + @{ + +<h1 class="pg">Introduction</h1> +Dlog logging service support sending log message to circular log device + +<h1 class="pg">dlog Architecture</h1> +@image html SLP_Dlog_PG_image001.png System Architecture of dlog + +<h1 class="pg">dlog properties</h1> +Sending log message to circular buffer. dlog APIs include Priority and Tag. By using priority and Tag, we can easily filtered messages what we want to see. + <h2 class="pg">priority</h2> +priority level incdicates the urgency of log message + +<table> +<tr> + <th>Priority</th> + <th>Description</th> +</tr> +<tr> + <td>VERBOSE </td> + <td>Verbose message. - compiled into application and logged at runtime only when debug mode. on release mode, this log message is strip.</td> +</tr> +<tr> + <td>DEBUG</td> + <td>Debug messasge. - always compiled into application, but not logged at runtime by default on release mode. on debug mode, this message will be logged at runtime.</td> +</tr> +<tr> + <td>INFO</td> + <td>Information message - Normal operational messages. above of this priority will always be logged.</td> +</tr> +<tr> + <td>WARN</td> + <td>Warning messages - not an error, but indication that an error will occur if action is not taken</td> +</tr> +<tr> + <td>ERROR</td> + <td>Error message - indicate error. </td> +</tr> +<tr> + <td>FATAL</td> + <td>Fatal message - Should be corrected immediately ( not used yet )</td> +</tr> +</table> + + <h2 class="pg">Tag</h2> +Used to identify the source of a log message. +There is no naming limitation, but do not forget that tag is identification of module. Tag must be distinguishable from other tag. +Simplified macro like LOGV, LOGD, LOGI, LOGW, LOGE uses declared LOG_TAG constant, so declare a LOG_TAG constant before you use dlog macro is a good convention. +@code +#define LOG_TAG "MyApp" +@endcode + +<h1 class="pg">list of dlog macro</h1> +Macro name start with LOG prefix is for application. start with SLOG prefix is for framework, start with RLOG prefix is for radio. each macro write log message to separated log device such as main, system, radio. + +<h1 class="pg">sample code</h1> +Using simplified macro with current LOG_TAG + +@code +#define LOG_TAG "YOUR_APP" +#include <dlog.h> + +int function () { + LOGD( "debug message from YOUR_APP \n"); + LOGI( "information message from YOUR_APP \n"); + LOGW( "warning message from YOUR_APP \n"); + LOGE( "error message from YOUR_APP \n"); +} +@endcode + +Using log macro allows you to specify a priority and a tag + +@code +#include <dlog.h> + +#define TAG_DRM_SVC "drm_svc" +#define TAG_DRM_WM "drm_wm" +#define TAG_DRM_OEM "drm_oem" + +int function () { + LOG(LOG_DEBUG,TAG_DRM_SVC, "information message from drm_svc \n"); + LOG(LOG_WARN,TAG_DRM_WM, "warning message from drm_wm \n"); + LOG(LOG_ERROR,TAG_DRM_OEM, "error message from drm_oem \n"); +} +@endcode + +Using log macro allows you to pass in a varargs + +@code +#include <dlog.h> + +#define TAG_DRM_SVC "drm_svc" +#define TAG_DRM_WM "drm_wm" +#define TAG_DRM_OEM "drm_oem" + +int function (const char *fmt, ...) { + va_list ap; + va_start(ap, fmt); + LOG_VA(LOG_DEBUG,TAG_DRM_WM, fmt, args); + va_end(ap); +} +@endcode +<h1 class="pg">dlogutil</h1> + <h2 class="pg">Introduction</h2> +You can use dlogutil command to view and follow the contents of the log buffers. The general usage is : +@code +dlogutil [<option>] ¡¦ [<filter-spec>] ¡¦ +@endcode + + <h2 class="pg">Filtering log output</h2> +Every log message has a <I>tag</I> and a <I>priority</I> associated with it. +Filter expression follows this format <B><I>tag:priority</I></B> where <I>tag</I> indicates the tag of interest and <I>priority</I> indicates the minimum level of priority to report for that tag. You can add any number of tag:priority specifications in a single filter expression. +The tag of a log message is a short indicating the system component from which the message originates +The <I>priority</I> is one of the following character values, orderd from lowest to highest priority:<br> +V - verbose<br> +D - debug<br> +I - info<br> +W - warning<br> +E - Error<br> +F - Fatal<br> + +for example, if you want to see MyApp tag and above of debug priority, +@code +# dlogutil MyApp:D +@endcode +if you want to see all log message above of info priority also, +@code +# dlogutil MyApp:D *:E +@endcode + + <h2 class="pg">List of logutil command options</h2> + +<table> +<tr> + <th>Option</th> + <th>Description</th> +</tr> +<tr> + <td>-b <buffer> </td> + <td>Alternate log buffer. The main buffer is used by default buffer. </td> +</tr> +<tr> + <td>-c</td> + <td>Clears the entire log and exits</td> +</tr> +<tr> + <td>-d</td> + <td>Dumps the log and exits.</td> +</tr> +<tr> + <td>-f <filename></td> + <td>Writes log to <filename>. The default is stdout</td> +</tr> +<tr> + <td>-g</td> + <td>Print the size of the specified log buffer and exits.</td> +</tr> +<tr> + <td>-n <count></td> + <td>Sets the maximum number of rotated logs to <count>. The default value is 4. Requires the -r option</td> +</tr> +<tr> + <td>-r <Kbytes></td> + <td>Rotates the log file every <Kbytes> of output. The default value is 16. Requires the -f option.</td> +</tr> +<tr> + <td>-s</td> + <td>Sets the default filter spec to silent.</td> +</tr> +<tr> + <td>-v <format></td> + <td>Sets the output format for log messages. The default is brief format. </td> +</tr> + +</table> + + + +@} +**/ diff --git a/include/dlog.h b/include/dlog.h new file mode 100755 index 0000000..acc4656 --- /dev/null +++ b/include/dlog.h @@ -0,0 +1,439 @@ +/* + * DLOG + * Copyright (c) 2005-2008, The Android Open Source Project + * Copyright (c) 2012-2013 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file dlog.h + * @version 0.4 + * @brief This file is the header file of interface of dlog. + */ +/** + * @addtogroup CAPI_BASE_DLOG_MODULE + * @{ + * + */ +#ifndef _DLOG_H_ +#define _DLOG_H_ + +#include <stdarg.h> +#include <string.h> + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* + * This is the local tag used for the following simplified + * logging macros. You can change this preprocessor definition + * before using the other macros to change the tag. + */ +#ifndef LOG_TAG +#define LOG_TAG NULL +#endif + +#ifndef __MODULE__ +#define __MODULE__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__) +#endif + +#ifdef TIZEN_ENGINEER_MODE +#ifndef TIZEN_DEBUG_ENABLE +#define TIZEN_DEBUG_ENABLE +#endif +#endif + +/* + * log priority values, in ascending priority order. + */ +typedef enum { + DLOG_UNKNOWN = 0, + DLOG_DEFAULT, + DLOG_VERBOSE, + DLOG_DEBUG, + DLOG_INFO, + DLOG_WARN, + DLOG_ERROR, + DLOG_FATAL, + DLOG_SILENT, + DLOG_PRIO_MAX /* Keep this always at the end. */ +} log_priority; + +typedef enum { + LOG_ID_MAIN = 0, + LOG_ID_RADIO, + LOG_ID_SYSTEM, + LOG_ID_APPS, + LOG_ID_MAX +} log_id_t; + +#define CONDITION(cond) (__builtin_expect((cond) != 0, 0)) +#define NOP(fmt, arg...) ({ do { } while (0); }) + +// Macro inner work--------------------------------------------------------------- +#undef LOG_ +#ifdef TIZEN_DEBUG_ENABLE +#define LOG_(id, prio, tag, fmt, arg...) \ + ({ do { \ + __dlog_print(id, prio, tag, "%s: %s(%d) > " fmt, __MODULE__, __func__, __LINE__, ##arg); \ + } while (0); }) +#else +#define LOG_(id, prio, tag, fmt, arg...) \ + ({ do { \ + if ((int)prio != DLOG_DEBUG) { \ + __dlog_print(id, prio, tag, "%s: %s(%d) > " fmt, __MODULE__, __func__, __LINE__, ##arg); \ + } \ + } while (0); }) +#endif + +#undef SECURE_LOG_ +#ifdef TIZEN_DEBUG_ENABLE +#define SECURE_LOG_(id, prio, tag, fmt, arg...) \ + ({ do { \ + __dlog_print(id, prio, tag, "%s: %s(%d) > [SECURE_LOG] " fmt, __MODULE__, __func__, __LINE__, ##arg); \ + } while (0); }) +#else +#define SECURE_LOG_(id, prio, tag, fmt, arg...) NOP(fmt, ##arg) +#endif +// --------------------------------------------------------------------- +/** + * For Secure Log. + * Normally we strip Secure log from release builds. + * Please use this macros. + */ +/** + * For Application and etc. + * Simplified macro to send a main log message using the current LOG_TAG. + * Example: + * SECURE_LOGD("app debug %d", num); + * SECURE_LOGE("app error %d", num); + */ +#define SECURE_LOGD(format, arg...) SECURE_LOG_(LOG_ID_MAIN, DLOG_DEBUG, LOG_TAG, format, ##arg) +#define SECURE_LOGI(format, arg...) SECURE_LOG_(LOG_ID_MAIN, DLOG_INFO, LOG_TAG, format, ##arg) +#define SECURE_LOGW(format, arg...) SECURE_LOG_(LOG_ID_MAIN, DLOG_WARN, LOG_TAG, format, ##arg) +#define SECURE_LOGE(format, arg...) SECURE_LOG_(LOG_ID_MAIN, DLOG_ERROR, LOG_TAG, format, ##arg) +#define SECURE_LOGF(format, arg...) SECURE_LOG_(LOG_ID_MAIN, DLOG_FATAL, LOG_TAG, format, ##arg) +/** + * For Framework and system etc. + * Simplified macro to send a system log message using the current LOG_TAG. + * Example: + * SECURE_SLOGD("system debug %d", num); + * SECURE_SLOGE("system error %d", num); + */ +#define SECURE_SLOGD(format, arg...) SECURE_LOG_(LOG_ID_SYSTEM, DLOG_DEBUG, LOG_TAG, format, ##arg) +#define SECURE_SLOGI(format, arg...) SECURE_LOG_(LOG_ID_SYSTEM, DLOG_INFO, LOG_TAG, format, ##arg) +#define SECURE_SLOGW(format, arg...) SECURE_LOG_(LOG_ID_SYSTEM, DLOG_WARN, LOG_TAG, format, ##arg) +#define SECURE_SLOGE(format, arg...) SECURE_LOG_(LOG_ID_SYSTEM, DLOG_ERROR, LOG_TAG, format, ##arg) +#define SECURE_SLOGF(format, arg...) SECURE_LOG_(LOG_ID_SYSTEM, DLOG_FATAL, LOG_TAG, format, ##arg) +/** + * For Modem and radio etc. + * Simplified macro to send a radio log message using the current LOG_TAG. + * Example: + * SECURE_RLOGD("radio debug %d", num); + * SECURE_RLOGE("radio error %d", num); + */ +#define SECURE_RLOGD(format, arg...) SECURE_LOG_(LOG_ID_RADIO, DLOG_DEBUG, LOG_TAG, format, ##arg) +#define SECURE_RLOGI(format, arg...) SECURE_LOG_(LOG_ID_RADIO, DLOG_INFO, LOG_TAG, format, ##arg) +#define SECURE_RLOGW(format, arg...) SECURE_LOG_(LOG_ID_RADIO, DLOG_WARN, LOG_TAG, format, ##arg) +#define SECURE_RLOGE(format, arg...) SECURE_LOG_(LOG_ID_RADIO, DLOG_ERROR, LOG_TAG, format, ##arg) +#define SECURE_RLOGF(format, arg...) SECURE_LOG_(LOG_ID_RADIO, DLOG_FATAL, LOG_TAG, format, ##arg) +/** + * For Tizen OSP Application macro. + */ +#define SECURE_ALOGD(format, arg...) SECURE_LOG_(LOG_ID_APPS, DLOG_DEBUG, LOG_TAG, format, ##arg) +#define SECURE_ALOGI(format, arg...) SECURE_LOG_(LOG_ID_APPS, DLOG_INFO, LOG_TAG, format, ##arg) +#define SECURE_ALOGW(format, arg...) SECURE_LOG_(LOG_ID_APPS, DLOG_WARN, LOG_TAG, format, ##arg) +#define SECURE_ALOGE(format, arg...) SECURE_LOG_(LOG_ID_APPS, DLOG_ERROR, LOG_TAG, format, ##arg) +#define SECURE_ALOGF(format, arg...) SECURE_LOG_(LOG_ID_APPS, DLOG_FATAL, LOG_TAG, format, ##arg) +/** + * If you want use redefined macro. + * You can use this macro. + * This macro need priority and tag arguments. + */ +#define SECURE_LOG(priority, tag, format, arg...) SECURE_LOG_(LOG_ID_MAIN, D##priority, tag, format, ##arg) +#define SECURE_SLOG(priority, tag, format, arg...) SECURE_LOG_(LOG_ID_SYSTEM, D##priority, tag, format, ##arg) +#define SECURE_RLOG(priority, tag, format, arg...) SECURE_LOG_(LOG_ID_RADIO, D##priority, tag, format, ##arg) +#define SECURE_ALOG(priority, tag, format, arg...) SECURE_LOG_(LOG_ID_APPS, D##priority, tag, format, ##arg) + +/** + * For Application and etc. + * Simplified macro to send a main log message using the current LOG_TAG. + * Example: + * LOGD("app debug %d", num); + * LOGE("app error %d", num); + */ +#ifdef TIZEN_DEBUG_ENABLE +#define LOGD(format, arg...) LOG_(LOG_ID_MAIN, DLOG_DEBUG, LOG_TAG, format, ##arg) +#else +#define LOGD(format, arg...) NOP(format, ##arg) +#endif +#define LOGI(format, arg...) LOG_(LOG_ID_MAIN, DLOG_INFO, LOG_TAG, format, ##arg) +#define LOGW(format, arg...) LOG_(LOG_ID_MAIN, DLOG_WARN, LOG_TAG, format, ##arg) +#define LOGE(format, arg...) LOG_(LOG_ID_MAIN, DLOG_ERROR, LOG_TAG, format, ##arg) +#define LOGF(format, arg...) LOG_(LOG_ID_MAIN, DLOG_FATAL, LOG_TAG, format, ##arg) +/** + * For Framework and system etc. + * Simplified macro to send a system log message using the current LOG_TAG. + * Example: + * SLOGD("system debug %d", num); + * SLOGE("system error %d", num); + */ +#ifdef TIZEN_DEBUG_ENABLE +#define SLOGD(format, arg...) LOG_(LOG_ID_SYSTEM, DLOG_DEBUG, LOG_TAG, format, ##arg) +#else +#define SLOGD(format, arg...) NOP(format, ##arg) +#endif +#define SLOGI(format, arg...) LOG_(LOG_ID_SYSTEM, DLOG_INFO, LOG_TAG, format, ##arg) +#define SLOGW(format, arg...) LOG_(LOG_ID_SYSTEM, DLOG_WARN, LOG_TAG, format, ##arg) +#define SLOGE(format, arg...) LOG_(LOG_ID_SYSTEM, DLOG_ERROR, LOG_TAG, format, ##arg) +#define SLOGF(format, arg...) LOG_(LOG_ID_SYSTEM, DLOG_FATAL, LOG_TAG, format, ##arg) +/** + * For Modem and radio etc. + * Simplified macro to send a radio log message using the current LOG_TAG. + * Example: + * RLOGD("radio debug %d", num); + * RLOGE("radio error %d", num); + */ +#ifdef TIZEN_DEBUG_ENABLE +#define RLOGD(format, arg...) LOG_(LOG_ID_RADIO, DLOG_DEBUG, LOG_TAG, format, ##arg) +#else +#define RLOGD(format, arg...) NOP(format, ##arg) +#endif +#define RLOGI(format, arg...) LOG_(LOG_ID_RADIO, DLOG_INFO, LOG_TAG, format, ##arg) +#define RLOGW(format, arg...) LOG_(LOG_ID_RADIO, DLOG_WARN, LOG_TAG, format, ##arg) +#define RLOGE(format, arg...) LOG_(LOG_ID_RADIO, DLOG_ERROR, LOG_TAG, format, ##arg) +#define RLOGF(format, arg...) LOG_(LOG_ID_RADIO, DLOG_FATAL, LOG_TAG, format, ##arg) +/** + * For Tizen OSP Application macro. + */ +#ifdef TIZEN_DEBUG_ENABLE +#define ALOGD(format, arg...) LOG_(LOG_ID_APPS, DLOG_DEBUG, LOG_TAG, format, ##arg) +#else +#define ALOGD(format, arg...) NOP(format, ##arg) +#endif +#define ALOGI(format, arg...) LOG_(LOG_ID_APPS, DLOG_INFO, LOG_TAG, format, ##arg) +#define ALOGW(format, arg...) LOG_(LOG_ID_APPS, DLOG_WARN, LOG_TAG, format, ##arg) +#define ALOGE(format, arg...) LOG_(LOG_ID_APPS, DLOG_ERROR, LOG_TAG, format, ##arg) +#define ALOGF(format, arg...) LOG_(LOG_ID_APPS, DLOG_FATAL, LOG_TAG, format, ##arg) + +/** + * Basic log message macro that allows you to specify a priority and a tag + * if you want to use this macro directly, you must add this messages for unity of messages. + * (LOG(prio, tag, "%s: %s(%d) > " format, __MODULE__, __func__, __LINE__, ##arg)) + * + * Example: + * #define MYMACRO(prio, tag, format, arg...) \ + * LOG(prio, tag, format, ##arg) + * + * MYMACRO(LOG_DEBUG, MYTAG, "test mymacro %d", num); + * + */ +#ifndef LOG +#define LOG(priority, tag, format, arg...) LOG_(LOG_ID_MAIN, D##priority, tag, format, ##arg) +#endif +#define SLOG(priority, tag, format, arg...) LOG_(LOG_ID_SYSTEM, D##priority, tag, format, ##arg) +#define RLOG(priority, tag, format, arg...) LOG_(LOG_ID_RADIO, D##priority, tag, format, ##arg) +#define ALOG(priority, tag, format, arg...) LOG_(LOG_ID_APPS, D##priority, tag, format, ##arg) + +#define LOG_VA(priority, tag, fmt, args) \ + vprint_log(D##priority, tag, fmt, args) +#define ALOG_VA(priority, tag, fmt, args) \ + vprint_apps_log(D##priority, tag, fmt, args) +#define RLOG_VA(priority, tag, fmt, args) \ + vprint_radio_log(D##priority, tag, fmt, args) +#define SLOG_VA(priority, tag, fmt, args) \ + vprint_system_log(D##priority, tag, fmt, args) +#define print_apps_log(prio, tag, fmt...) \ + __dlog_print(LOG_ID_APPS, prio, tag, fmt) +#define vprint_apps_log(prio, tag, fmt...) \ + __dlog_vprint(LOG_ID_APPS, prio, tag, fmt) +#define print_log(prio, tag, fmt...) \ + __dlog_print(LOG_ID_MAIN, prio, tag, fmt) +#define vprint_log(prio, tag, fmt...) \ + __dlog_vprint(LOG_ID_MAIN, prio, tag, fmt) +#define print_radio_log(prio, tag, fmt...)\ + __dlog_print(LOG_ID_RADIO, prio, tag, fmt) +#define vprint_radio_log(prio, tag, fmt...) \ + __dlog_vprint(LOG_ID_RADIO, prio, tag, fmt) +#define print_system_log(prio, tag, fmt...)\ + __dlog_print(LOG_ID_SYSTEM, prio, tag, fmt) +#define vprint_system_log(prio, tag, fmt...) \ + __dlog_vprint(LOG_ID_SYSTEM, prio, tag, fmt) + +// --------------------------------------------------------------------- +// Don't use below macro no more!! It will be removed -- Verbose and Fatal priority macro will be removed -- +#define COMPATIBILITY_ON +#ifdef COMPATIBILITY_ON +/** + * Conditional Macro. + * Don't use this macro. It's just compatibility. + * It will be deprecated. + */ +#define LOGD_IF(cond, format, arg...) \ + ({ do { \ + if (CONDITION(cond)) { \ + LOGD(format, ##arg); \ + } \ + } while (0); }) +#define LOGI_IF(cond, format, arg...) \ + ({ do { \ + if (CONDITION(cond)) { \ + LOGI(format, ##arg); \ + } \ + } while (0); }) +#define LOGW_IF(cond, format, arg...) \ + ({ do { \ + if (CONDITION(cond)) { \ + LOGW(format, ##arg); \ + } \ + } while (0); }) +#define LOGE_IF(cond, format, arg...) \ + ({ do { \ + if (CONDITION(cond)) { \ + LOGE(format, ##arg); \ + } \ + } while (0); }) +#define LOGF_IF(cond, format, arg...) \ + ({ do { \ + if (CONDITION(cond)) { \ + LOGF(format, ##arg); \ + } \ + } while (0); }) +#define SLOGD_IF(cond, format, arg...) \ + ({ do { \ + if (CONDITION(cond)) { \ + SLOGD(format, ##arg); \ + } \ + } while (0); }) +#define SLOGI_IF(cond, format, arg...) \ + ({ do { \ + if (CONDITION(cond)) { \ + SLOGI(format, ##arg); \ + } \ + } while (0); }) +#define SLOGW_IF(cond, format, arg...) \ + ({ do { \ + if (CONDITION(cond)) { \ + SLOGW(format, ##arg); \ + } \ + } while (0); }) +#define SLOGE_IF(cond, format, arg...) \ + ({ do { \ + if (CONDITION(cond)) { \ + SLOGE(format, ##arg); \ + } \ + } while (0); }) +#define SLOGF_IF(cond, format, arg...) \ + ({ do { \ + if (CONDITION(cond)) { \ + SLOGF(format, ##arg); \ + } \ + } while (0); }) +#define RLOGD_IF(cond, format, arg...) \ + ({ do { \ + if (CONDITION(cond)) { \ + RLOGD(format, ##arg); \ + } \ + } while (0); }) +#define RLOGI_IF(cond, format, arg...) \ + ({ do { \ + if (CONDITION(cond)) { \ + RLOGI(format, ##arg); \ + } \ + } while (0); }) +#define RLOGW_IF(cond, format, arg...) \ + ({ do { \ + if (CONDITION(cond)) { \ + RLOGW(format, ##arg); \ + } \ + } while (0); }) +#define RLOGE_IF(cond, format, arg...) \ + ({ do { \ + if (CONDITION(cond)) { \ + RLOGE(format, ##arg); \ + } \ + } while (0); }) +#define RLOGF_IF(cond, format, arg...) \ + ({ do { \ + if (CONDITION(cond)) { \ + RLOGF(format, ##arg); \ + } \ + } while (0); }) +#define LOG_ON() ({ do { } while (0); }) +#define LOGV(format, arg...) NOP(format, ##arg) +#define SLOGV(format, arg...) NOP(format, ##arg) +#define RLOGV(format, arg...) NOP(format, ##arg) +#define ALOGV(format, arg...) NOP(format, ##arg) +#define LOGV_IF(cond, format, arg...) NOP(format, ##arg) +#define SLOGV_IF(cond, format, arg...) NOP(format, ##arg) +#define RLOGV_IF(cond, format, arg...) NOP(format, ##arg) +#define SECLOG(...) ({ do { } while (0); }) +#endif +// Don't use above macro no more!! It will be removed -Verbose, Warning and Fatal priority macro. +// --------------------------------------------------------------------- +/* + * The stuff in the rest of this file should not be used directly. + */ +/** + * @brief send log. must specify log_id ,priority, tag and format string. + * @pre none + * @post none + * @see __dlog_print + * @remarks you must not use this API directly. use macros instead. + * @param[in] log_id log device id + * @param[in] prio priority + * @param[in] tag tag + * @param[in] fmt format string + * @return Operation result + * @retval 0>= Success + * @retval -1 Error + * @code +// you have to use LOG(), SLOG(), RLOG() family not to use __dlog_print() directly +// so below example is just for passing Documentation Verification !!! +#define LOG_TAG USR_TAG +#include<dlog.h> + __dlog_print(LOG_ID_MAIN, DLOG_INFO, USR_TAG, "you must not use this API directly"); + * @endcode + */ +int __dlog_print(log_id_t log_id, int prio, const char *tag, const char *fmt, ...); + +/** + * @brief send log with va_list. must specify log_id ,priority, tag and format string. + * @pre none + * @post none + * @see __dlog_print + * @remarks you must not use this API directly. use macros instead. + * @param[in] log_id log device id + * @param[in] prio priority + * @param[in] tag tag + * @param[in] fmt format string + * @param[in] ap va_list + * @return Operation result + * @retval 0>= Success + * @retval -1 Error + * @code + // you have to use LOG_VA(), SLOG_VA(), RLOG_VA() family not to use __dlog_print() directly + // so below example is just for passing Documentation Verification !!! +#define LOG_TAG USR_TAG +#include<dlog.h> + __dlog_vprint(LOG_ID_MAIN, DLOG_INFO, USR_TAG, "you must not use this API directly", ap); + * @endcode + */ +int __dlog_vprint(log_id_t log_id, int prio, const char *tag, const char *fmt, va_list ap); +#ifdef __cplusplus +} +#endif /* __cplusplus */ +/** @} */ +#endif /* _DLOG_H_*/ diff --git a/include/logconfig.h b/include/logconfig.h new file mode 100644 index 0000000..c675622 --- /dev/null +++ b/include/logconfig.h @@ -0,0 +1,28 @@ +/* + * DLOG + * Copyright (c) 2013 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _LOGCONFIG_H_ +#define _LOGCONFIG_H_ + +struct log_config { + int lc_plog; /* Platform logging enable/disable */ + int lc_limiter; /* Log limiter enable/disable */ +}; + +int __log_config_read(const char* config_file, struct log_config* config); + +#endif /* _LOGCONFIG_H_ */
\ No newline at end of file diff --git a/include/logger.h b/include/logger.h new file mode 100755 index 0000000..db1b15b --- /dev/null +++ b/include/logger.h @@ -0,0 +1,55 @@ +/* + * DLOG + * Copyright (c) 2005-2008, The Android Open Source Project + * Copyright (c) 2012-2013 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _UTILS_LOGGER_H +#define _UTILS_LOGGER_H + +#include <stdint.h> + +struct logger_entry { + uint16_t len; /* length of the payload */ + uint16_t __pad; /* no matter what, we get 2 bytes of padding */ + int32_t pid; /* generating process's pid */ + int32_t tid; /* generating process's tid */ + int32_t sec; /* seconds since Epoch */ + int32_t nsec; /* nanoseconds */ + char msg[]; /* the entry's payload */ +}; + +#define LOGGER_LOG_MAIN "log_main" +#define LOGGER_LOG_RADIO "log_radio" +#define LOGGER_LOG_SYSTEM "log_system" +#define LOGGER_LOG_APPS "log_apps" + +#define LOGGER_ENTRY_MAX_LEN (4*1024) +#define LOGGER_ENTRY_MAX_PAYLOAD (LOGGER_ENTRY_MAX_LEN - sizeof(struct logger_entry)) + +//#ifdef HAVE_IOCTL + +#include <sys/ioctl.h> + +#define __LOGGERIO 0xAE + +#define LOGGER_GET_LOG_BUF_SIZE _IO(__LOGGERIO, 1) /* size of log */ +#define LOGGER_GET_LOG_LEN _IO(__LOGGERIO, 2) /* used log len */ +#define LOGGER_GET_NEXT_ENTRY_LEN _IO(__LOGGERIO, 3) /* next entry len */ +#define LOGGER_FLUSH_LOG _IO(__LOGGERIO, 4) /* flush log */ + +//#endif // HAVE_IOCTL + +#endif /* _UTILS_LOGGER_H */ diff --git a/include/loglimiter.h b/include/loglimiter.h new file mode 100644 index 0000000..3a4a366 --- /dev/null +++ b/include/loglimiter.h @@ -0,0 +1,35 @@ +/* + * DLOG + * Copyright (c) 2013 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _LOGLIMITER_H_ +#define _LOGLIMITER_H_ + +/* Define maximum logging speed, everything greater means unlimited. + * Maximum possible value is INT_MAX-2 */ +#define __LOG_LIMITER_LIMIT_MAX (10*1000) + +int __log_limiter_initialize(void); + +void __log_limiter_destroy(void); + +int __log_limiter_pass_log(const char* tag, int prio); + +int __log_limiter_add_rule(const char* tag, int prio, int limit); + +void __log_limiter_rules_purge(void); + +#endif /* _LOGLIMITER_H_ */ diff --git a/include/logprint.h b/include/logprint.h new file mode 100755 index 0000000..3cd79bc --- /dev/null +++ b/include/logprint.h @@ -0,0 +1,152 @@ +/* + * DLOG + * Copyright (c) 2005-2008, The Android Open Source Project + * Copyright (c) 2012-2013 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _LOGPRINT_H +#define _LOGPRINT_H + +#include <time.h> +#include <pthread.h> + +#include <logger.h> +#include <dlog.h> + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + FORMAT_OFF = 0, + FORMAT_BRIEF, + FORMAT_PROCESS, + FORMAT_TAG, + FORMAT_THREAD, + FORMAT_RAW, + FORMAT_TIME, + FORMAT_THREADTIME, + FORMAT_LONG, +} log_print_format; + +typedef struct log_format_t log_format; + +typedef struct log_entry_t { + time_t tv_sec; + long tv_nsec; + log_priority priority; + pid_t pid; + pthread_t tid; + const char * tag; + size_t messageLen; + const char * message; +} log_entry; + +log_format *log_format_new(); + +void log_format_free(log_format *p_format); + +void log_set_print_format(log_format *p_format, + log_print_format format); + +/** + * Returns FORMAT_OFF on invalid string + */ +log_print_format log_format_from_string(const char *s); + +/** + * filterExpression: a single filter expression + * eg "AT:d" + * + * returns 0 on success and -1 on invalid expression + * + * Assumes single threaded execution + * + */ + +int log_add_filter_rule(log_format *p_format, + const char *filterExpression); + + +/** + * filterString: a whitespace-separated set of filter expressions + * eg "AT:d *:i" + * + * returns 0 on success and -1 on invalid expression + * + * Assumes single threaded execution + * + */ + +int log_add_filter_string(log_format *p_format, + const char *filterString); + + +/** + * returns 1 if this log line should be printed based on its priority + * and tag, and 0 if it should not + */ +int log_should_print_line ( + log_format *p_format, const char *tag, log_priority pri); + + +/** + * Splits a wire-format buffer into an log_entry + * entry allocated by caller. Pointers will point directly into buf + * + * Returns 0 on success and -1 on invalid wire format (entry will be + * in unspecified state) + */ +int log_process_log_buffer(struct logger_entry *buf, + log_entry *entry); + +/** + * Formats a log message into a buffer + * + * Uses defaultBuffer if it can, otherwise malloc()'s a new buffer + * If return value != defaultBuffer, caller must call free() + * Returns NULL on malloc error + */ + +char *log_format_log_line ( + log_format *p_format, + char *defaultBuffer, + size_t defaultBufferSize, + const log_entry *p_line, + size_t *p_outLength); + + +/** + * Either print or do not print log line, based on filter + * + * Assumes single threaded execution + * + */ +int log_print_log_line( + log_format *p_format, + int fd, + const log_entry *entry); + +/** + * logprint test furction + * + */ +void logprint_run_tests(void); + +#ifdef __cplusplus +} +#endif + +#endif /*_LOGPRINT_H*/ diff --git a/packaging/dlog.changes b/packaging/dlog.changes new file mode 100644 index 0000000..8e4c40f --- /dev/null +++ b/packaging/dlog.changes @@ -0,0 +1,10 @@ +* Thu Jan 03 2013 Anas Nashif <anas.nashif@intel.com> accepted/trunk/20121212.102709@5168da3 +- fixed build with new compilers +- spec cleanup +- check for archiver in configure.ac + +* Thu Jun 28 2012 Patrick McCarty <patrick.mccarty@linux.intel.com> - 0.4.1 +- Fix initscript symlinks + +* Fri Jun 22 2012 William Douglas <william.douglas@intel.com> - 0.4.1 +- Add system unit files diff --git a/packaging/dlog.conf.in b/packaging/dlog.conf.in new file mode 100644 index 0000000..ea201a5 --- /dev/null +++ b/packaging/dlog.conf.in @@ -0,0 +1,65 @@ +# +# Dlog configuration file. +# +# Lines beginning with '#' are comments. +# +# Following file configures dlog library. +# First section contains global configuration for library. +# It allows to disable or enable platform logs and log limiter features. + +PlatformLogs on # Possible values are 'on' and 'off' +LogLimiter off + +# Second section controls filtering rules for dlog limiter. +# Each line represent a rule for a filter. Filtering is done +# at runtime, but configuration is read only at the boot time. +# Log lines filtered out are not written to log buffers. +# Filtering is done for "TAG"|priority pair, where TAG is string defined by application. +# Which should be quoted with '"' character. It may contain spaces but not TABs. +# Priority is one character from the following values: +# +# V or v or 1 - Verbose messages, +# D or d or 2 - Debug messages, +# I or i or 3 - Informational messages, +# W or w or 4 - Warning messages, +# E or e or 5 - Error messages, +# F or f or 6 - Fatal messages. +# +# Limiter can define rules 'for all' by using an '*' character. +# It's a wildcard replacing all TAGs not mentioned in this file. +# '*' could be used also as wildcard for priority. +# An asterisk is not evaluated, which means rules like "WOR*" are threated as a TAGs. +# Log filter works according to rules presented below: +# +# case 1) The "TAG"|priority pair is not presented on the list. +# - Use "TAG"|* rule to decide what to do. +# - If "TAG"|* is not defined, use "*"|priority rule to decide what to do. +# - If "*"|priority pair is not present, use "*"|* rule. +# - If rule "*"|* is not present, then allow logging. +# +# case 2) The "TAG"|priority pair is on the list (applies to +# "*" |priority and "*"|* rules). +# - If policy = ALLOW, then log speed is unlimited. +# - If policy = <number> then limit to <number> logs per minute. +# - If policy = DENY then forbid logging. +# +# Maximum <number> is 10000, which gives 10000 log line per minute. +# Please keep in mind that dlog uses constant size log storage, thus +# when the application writes many logs, it may cause older entries overwriting. +# +# The configuration file has following format: +# Lines beginning with # are threated as comments. +# TAG can contain any ASCII letter or digit and spaces, but +# not a TAB character. TAG has to be quoted using '"' character. +# TAG is separated from priority with pipe '|' character. +# Priority is one character long, the possible values are presented above. +# Policy is separated from the "TAG"|priority pair with TAB character. +# Policy is expressed as ALLOW, DENY or number from 0 to 10000 (not case sensitive). +# Setting policy as 0 is the same as DENY. +# Setting policy to more than 10000 is threated as allow. + + + + +# TAG|priority POLICY +"*"|* ALLOW diff --git a/packaging/dlog.spec b/packaging/dlog.spec new file mode 100755 index 0000000..545f669 --- /dev/null +++ b/packaging/dlog.spec @@ -0,0 +1,131 @@ +Name: dlog +Summary: Logging service +Version: 0.4.1 +Release: 15 +Group: System/Libraries +License: Apache License, Version 2.0 +Source0: %{name}-%{version}.tar.gz +Source101: packaging/dlogutil.manifest +Source102: packaging/libdlog.manifest +Source201: packaging/dlog.conf.in +Source202: packaging/dlog_logger.conf.in +Source203: packaging/dlog_logger.conf-micro.in +Source301: packaging/dlog_logger.service +BuildRequires: pkgconfig(libsystemd-journal) +Requires(post): coreutils + +%description +dlog API library + +%package -n libdlog +Summary: Logging service dlog API +Group: Development/Libraries +Requires(post): smack-utils + +%description -n libdlog +dlog API library + +%package -n libdlog-devel +Summary: Logging service dlog API +Group: Development/Libraries +Requires: lib%{name} = %{?epoch:%{epoch}:}%{version}-%{release} + +%description -n libdlog-devel +dlog API library + + +%package -n dlogutil +Summary: print log data to the screen +Group: Development/Libraries +Requires: lib%{name} = %{?epoch:%{epoch}:}%{version}-%{release} +Requires(post): /usr/bin/systemctl +Requires(postun): /usr/bin/systemctl +Requires(preun): /usr/bin/systemctl + +%description -n dlogutil +Utilities for print log data + +%prep +%setup -q + +%build +cp %{SOURCE101} . +cp %{SOURCE102} . +%autogen --disable-static +%configure --disable-static \ +%if 0%{?tizen_build_binary_release_type_daily} + --enable-fatal_on \ +%endif +%if 0%{?tizen_build_binary_release_type_eng} + --enable-engineer_mode \ +%endif +%if 0%{?sec_build_binary_debug_enable} + --enable-debug_enable \ +%endif + --without-systemd-journal +make %{?jobs:-j%jobs} + +%install +rm -rf %{buildroot} +%make_install +mkdir -p %{buildroot}/usr/bin/ +cp %{_builddir}/%{name}-%{version}/scripts/dlogctrl %{buildroot}/usr/bin/dlogctrl + +mkdir -p %{buildroot}%{_libdir}/systemd/system/multi-user.target.wants/ +install -m 0644 %SOURCE301 %{buildroot}%{_libdir}/systemd/system/ + +ln -s ../dlog_logger.service %{buildroot}%{_libdir}/systemd/system/multi-user.target.wants/dlog_logger.service + +mkdir -p %{buildroot}/usr/share/license +cp LICENSE.Apache-2.0 %{buildroot}/usr/share/license/%{name} +cp LICENSE.Apache-2.0 %{buildroot}/usr/share/license/libdlog +cp LICENSE.Apache-2.0 %{buildroot}/usr/share/license/dlogutil + +mkdir -p %{buildroot}/opt/etc +cp %SOURCE201 %{buildroot}/opt/etc/dlog.conf +%if "%{_repository}" == "wearable" +cp %SOURCE203 %{buildroot}/opt/etc/dlog_logger.conf +%else +cp %SOURCE202 %{buildroot}/opt/etc/dlog_logger.conf +%endif + +%preun -n dlogutil + +%post -n dlogutil +systemctl daemon-reload + +%postun -n dlogutil +systemctl daemon-reload + +%post -n libdlog +/sbin/ldconfig + +%postun -n libdlog +/sbin/ldconfig + +%files +/usr/share/license/%{name} + +%files -n dlogutil +%manifest dlogutil.manifest +/usr/share/license/dlogutil +%attr(755,root,app_logging) %{_bindir}/dlog_logger +%attr(755,root,app_logging) %{_bindir}/dlogutil +%attr(755,root,app_logging) %{_bindir}/dlogctrl +%attr(755,root,app_logging) /opt/etc/dlog_logger.conf +%{_libdir}/systemd/system/dlog_logger.service +%{_libdir}/systemd/system/multi-user.target.wants/dlog_logger.service + + +%files -n libdlog +%manifest libdlog.manifest +/usr/share/license/libdlog +%{_libdir}/libdlog.so.0 +%{_libdir}/libdlog.so.0.0.0 +%attr(664,root,app_logging) /opt/etc/dlog.conf + +%files -n libdlog-devel +%{_includedir}/dlog/dlog.h +%{_libdir}/pkgconfig/dlog.pc +%{_libdir}/libdlog.so + diff --git a/packaging/dlog_logger.conf-micro.in b/packaging/dlog_logger.conf-micro.in new file mode 100644 index 0000000..df7feb8 --- /dev/null +++ b/packaging/dlog_logger.conf-micro.in @@ -0,0 +1,3 @@ +dlogutil -b system -r 5120 -n 1 -f /var/log/dlog_system -v threadtime *:I +dlogutil -b main -r 3072 -n 1 -f /var/log/dlog_main -v threadtime *:W +dlogutil -b radio -r 1024 -n 1 -f /var/log/dlog_radio -v threadtime diff --git a/packaging/dlog_logger.conf.in b/packaging/dlog_logger.conf.in new file mode 100644 index 0000000..88cc06e --- /dev/null +++ b/packaging/dlog_logger.conf.in @@ -0,0 +1,4 @@ +dlogutil -b system -r 5120 -n 1 -f /var/log/dlog_system -v threadtime *:I +dlogutil -b main -r 3072 -n 1 -f /var/log/dlog_main -v threadtime *:W +dlogutil -b radio -f /var/log/seq.radio.log -v threadtime -r 2048 -n 4 +dlogutil -b system -r 1024 -n 1 -f /var/log/msg.log -v threadtime MSG_SERVICE diff --git a/packaging/dlog_logger.service b/packaging/dlog_logger.service new file mode 100644 index 0000000..17d9761 --- /dev/null +++ b/packaging/dlog_logger.service @@ -0,0 +1,10 @@ +[Unit] +Description=Dumps dlog logs onto disk +DefaultDependencies=no + +[Service] +ExecStart=/usr/bin/dlog_logger +Restart=always + +[Install] +WantedBy=multi-user.target diff --git a/packaging/dlogutil.manifest b/packaging/dlogutil.manifest new file mode 100644 index 0000000..fc0a1bf --- /dev/null +++ b/packaging/dlogutil.manifest @@ -0,0 +1,14 @@ +<manifest> + <define> + <domain name="dlogutil"/> + </define> + <assign> + <filesystem path="/usr/bin/dlog_logger" label="_" exec_label="none"/> + <filesystem path="/usr/bin/dlogutil" label="_" exec_label="none"/> + <filesystem path="/usr/bin/dlogctrl" label="_" exec_label="none"/> + <filesystem path="/opt/etc/dlog_logger.conf" label="_" exec_label="none"/> + </assign> + <request> + <domain name="dlogutil"/> + </request> +</manifest> diff --git a/packaging/libdlog.manifest b/packaging/libdlog.manifest new file mode 100644 index 0000000..6247853 --- /dev/null +++ b/packaging/libdlog.manifest @@ -0,0 +1,10 @@ +<manifest> + <assign> + <filesystem path="/usr/lib/libdlog.so.0" label="_" exec_label="none"/> + <filesystem path="/usr/lib/libdlog.so.0.0" label="_" exec_label="none"/> + <filesystem path="/opt/etc/dlog.conf" label="*" exec_label="none"/> + </assign> + <request> + <domain name="_"/> + </request> +</manifest> diff --git a/scripts/dlogctrl b/scripts/dlogctrl new file mode 100755 index 0000000..8a4e724 --- /dev/null +++ b/scripts/dlogctrl @@ -0,0 +1,64 @@ +#!/bin/sh + +MODE=none +OBJ=none +VAL=none +CONFIG_PATH="/opt/etc" + +function Usage() +{ + /bin/echo "Usage: dlogctrl {get|set} {platformlog | loglimiter } {on|off}" + exit 1 +} + +case "$1" in +get) + MODE=get +;; +set) + MODE=set +;; +*) + Usage +esac + +case "$2" in +platformlog) + OBJ=PlatformLogs +;; +loglimiter) + OBJ=LogLimiter +;; +*) + Usage +esac + +if [ "$MODE" = "set" ]; then + case "$3" in + 1|on) + VAL=on + ;; + 0|off) + VAL=off + ;; + *) + Usage + esac + + echo "Setting $OBJ=$VAL" + /bin/sed "s/$OBJ[\t ]*[onf]*/$OBJ $VAL/" < "$CONFIG_PATH/dlog.conf" > /tmp/dlog.conf.new + /bin/cp /tmp/dlog.conf.new "$CONFIG_PATH/dlog.conf" + /bin/sync + echo "You must reboot this target to apply the change!" +else + case "$(grep "$OBJ" "$CONFIG_PATH/dlog.conf" | awk '{ print $2 }')" in + on) + /bin/echo "1" + ;; + off) + /bin/echo "0" + ;; + esac +fi + +exit 0 diff --git a/src/libdlog/log.c b/src/libdlog/log.c new file mode 100755 index 0000000..d0f3ad4 --- /dev/null +++ b/src/libdlog/log.c @@ -0,0 +1,268 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: t -*- + * DLOG + * Copyright (c) 2005-2008, The Android Open Source Project + * Copyright (c) 2012-2013 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "config.h" +#include <pthread.h> +#include <stdlib.h> +#include <string.h> +#include <stdarg.h> +#include <fcntl.h> +#include <sys/uio.h> +#include <sys/syscall.h> +#include <unistd.h> +#include <stdio.h> +#include <errno.h> +#include <dlog.h> +#include "loglimiter.h" +#include "logconfig.h" +#ifdef HAVE_SYSTEMD_JOURNAL +#define SD_JOURNAL_SUPPRESS_LOCATION 1 +#include <syslog.h> +#include <systemd/sd-journal.h> +#endif +#ifdef FATAL_ON +#include <assert.h> +#endif + +#define LOG_BUF_SIZE 1024 + +#define LOG_MAIN "log_main" +#define LOG_RADIO "log_radio" +#define LOG_SYSTEM "log_system" +#define LOG_APPS "log_apps" + +#define VALUE_MAX 2 +#define LOG_CONFIG_FILE "/opt/etc/dlog.conf" + +#ifndef HAVE_SYSTEMD_JOURNAL +static int log_fds[(int)LOG_ID_MAX] = { -1, -1, -1, -1 }; +#endif +static int (*write_to_log)(log_id_t, log_priority, const char *tag, const char *msg) = NULL; +static pthread_mutex_t log_init_lock = PTHREAD_MUTEX_INITIALIZER; +static struct log_config config; + +#ifdef HAVE_SYSTEMD_JOURNAL +static inline int dlog_pri_to_journal_pri(log_priority prio) +{ + static int pri_table[DLOG_PRIO_MAX] = { + [DLOG_UNKNOWN] = LOG_DEBUG, + [DLOG_DEFAULT] = LOG_DEBUG, + [DLOG_VERBOSE] = LOG_DEBUG, + [DLOG_DEBUG] = LOG_DEBUG, + [DLOG_INFO] = LOG_INFO, + [DLOG_WARN] = LOG_WARNING, + [DLOG_ERROR] = LOG_ERR, + [DLOG_FATAL] = LOG_CRIT, + [DLOG_SILENT] = -1, + }; + + if (prio < 0 || prio >= DLOG_PRIO_MAX) + return -EINVAL; + + return pri_table[prio]; +} + +static inline const char* dlog_id_to_string(log_id_t log_id) +{ + static const char* id_table[LOG_ID_MAX] = { + [LOG_ID_MAIN] = LOG_MAIN, + [LOG_ID_RADIO] = LOG_RADIO, + [LOG_ID_SYSTEM] = LOG_SYSTEM, + [LOG_ID_APPS] = LOG_APPS, + }; + + if (log_id < 0 || log_id >= LOG_ID_MAX || !id_table[log_id]) + return "UNKNOWN"; + + return id_table[log_id]; +} + +static int __write_to_log_sd_journal(log_id_t log_id, log_priority prio, const char *tag, const char *msg) +{ + pid_t tid = (pid_t)syscall(SYS_gettid); + /* XXX: sd_journal_sendv() with manually filed iov-s might be faster */ + return sd_journal_send("MESSAGE=%s", msg, + "PRIORITY=%i", dlog_pri_to_journal_pri(prio), + "LOG_TAG=%s", tag, + "LOG_ID=%s", dlog_id_to_string(log_id), + "TID=%d", tid, + NULL); +} + +#else +static int __write_to_log_null(log_id_t log_id, log_priority prio, const char *tag, const char *msg) +{ + return -1; +} + +static int __write_to_log_kernel(log_id_t log_id, log_priority prio, const char *tag, const char *msg) +{ + ssize_t ret; + int log_fd; + struct iovec vec[3]; + + if (log_id < LOG_ID_MAX) + log_fd = log_fds[log_id]; + else + return -1; // for TC + + if (!tag) + tag = ""; + + if (!msg) + return -1; + + vec[0].iov_base = (unsigned char *) &prio; + vec[0].iov_len = 1; + vec[1].iov_base = (void *) tag; + vec[1].iov_len = strlen(tag) + 1; + vec[2].iov_base = (void *) msg; + vec[2].iov_len = strlen(msg) + 1; + + ret = writev(log_fd, vec, 3); + + return ret; +} +#endif + +static void __configure(void) +{ + if (0 > __log_config_read(LOG_CONFIG_FILE, &config)) { + config.lc_limiter = 0; + config.lc_plog = 0; + } + + if (config.lc_limiter) { + if (0 > __log_limiter_initialize()) { + config.lc_limiter = 0; + } + } +} + +static void __dlog_init(void) +{ + pthread_mutex_lock(&log_init_lock); + /* configuration */ + __configure(); +#ifdef HAVE_SYSTEMD_JOURNAL + write_to_log = __write_to_log_sd_journal; +#else + /* open device */ + log_fds[LOG_ID_MAIN] = open("/dev/"LOG_MAIN, O_WRONLY); + log_fds[LOG_ID_RADIO] = open("/dev/"LOG_RADIO, O_WRONLY); + log_fds[LOG_ID_SYSTEM] = open("/dev/"LOG_SYSTEM, O_WRONLY); + log_fds[LOG_ID_APPS] = open("/dev/"LOG_APPS, O_WRONLY); + if (log_fds[LOG_ID_MAIN] < 0 || log_fds[LOG_ID_RADIO] < 0) { + write_to_log = __write_to_log_null; + } else { + write_to_log = __write_to_log_kernel; + } + + if (log_fds[LOG_ID_SYSTEM] < 0) + log_fds[LOG_ID_SYSTEM] = log_fds[LOG_ID_MAIN]; + if (log_fds[LOG_ID_APPS] < 0) + log_fds[LOG_ID_APPS] = log_fds[LOG_ID_MAIN]; +#endif + pthread_mutex_unlock(&log_init_lock); +} + +void __dlog_fatal_assert(int prio) +{ +#ifdef FATAL_ON + assert(!(prio == DLOG_FATAL)); +#endif +} + +static int dlog_should_log(log_id_t log_id, const char* tag, int prio) +{ + int should_log; + +#ifndef TIZEN_DEBUG_ENABLE + if (prio <= DLOG_DEBUG) + return 0; +#endif + if (!tag) + return 0; + + if (log_id < 0 || LOG_ID_MAX <= log_id) + return 0; + + if (log_id != LOG_ID_APPS && !config.lc_plog) + return 0; + + if (config.lc_limiter) { + should_log = __log_limiter_pass_log(tag, prio); + + if (!should_log) { + return 0; + } else if (should_log < 0) { + write_to_log(log_id, prio, tag, + "Your log has been blocked due to limit of log lines per minute."); + return 0; + } + } + + return 1; +} + +int __dlog_vprint(log_id_t log_id, int prio, const char *tag, const char *fmt, va_list ap) +{ + int ret; + char buf[LOG_BUF_SIZE]; + + if (write_to_log == NULL) + __dlog_init(); + + if (!dlog_should_log(log_id, tag, prio)) + return 0; + + vsnprintf(buf, LOG_BUF_SIZE, fmt, ap); + ret = write_to_log(log_id, prio, tag, buf); +#ifdef FATAL_ON + __dlog_fatal_assert(prio); +#endif + return ret; +} + +int __dlog_print(log_id_t log_id, int prio, const char *tag, const char *fmt, ...) +{ + int ret; + va_list ap; + char buf[LOG_BUF_SIZE]; + + if (write_to_log == NULL) + __dlog_init(); + + if (!dlog_should_log(log_id, tag, prio)) + return 0; + + va_start(ap, fmt); + vsnprintf(buf, LOG_BUF_SIZE, fmt, ap); + va_end(ap); + + ret = write_to_log(log_id, prio, tag, buf); +#ifdef FATAL_ON + __dlog_fatal_assert(prio); +#endif + return ret; +} + +void __attribute__((destructor)) __dlog_fini(void) +{ + __log_limiter_destroy(); +} diff --git a/src/libdlog/logconfig.c b/src/libdlog/logconfig.c new file mode 100644 index 0000000..6c7eabc --- /dev/null +++ b/src/libdlog/logconfig.c @@ -0,0 +1,174 @@ +/* + * DLOG + * Copyright (c) 2013 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "logconfig.h" +#include "loglimiter.h" + +/* Functions possible return value */ +#define RET_ERROR (-1) +#define RET_SUCCESS 0 + +#define CONFIG_LINE_MAX_LEN 256 +#define CONFIG_OPTION_MAX_LEN 64 + +/* Shortcut macros */ +#define isCOMMENT(c) ('#' == c) +#define isNEWLINE(c) ('\n' == c || '\r' == c) + +/* Dlog options definition */ +#define LOG_PLATFORM_STRING "PlatformLogs" +#define LOG_LIMITER_STRING "LogLimiter" + +/* Define options possible values */ +#define ALLOW_STRING "allow" +#define DENY_STRING "deny" +#define ON_STRING "on" +#define OFF_STRING "off" +#define isYES(c) (c == '1') +#define isNO(c) (c == '0') + + +static int log_config_multiplex_opt(char* opt_str, char* val_str, int prio, + struct log_config* config) +{ + int value = 0; + + /* There are only two ways to interpret the lines, so make here a short circuit */ + if (0 < prio) { /* Is it a rule or an option ? */ + /* For the filtering rule ... */ + + if (!strncasecmp(ALLOW_STRING, val_str, sizeof(ALLOW_STRING))) { + value = __LOG_LIMITER_LIMIT_MAX + 1; + } else if (!strncasecmp(DENY_STRING, val_str, + sizeof(DENY_STRING))) { + value = 0; + } else { + char* endptr = NULL; + + value = strtoul(val_str, &endptr, 0); + if (*endptr != '\0') { + return RET_ERROR; + } + } + + return __log_limiter_add_rule(opt_str, prio, value); + + } else { /* It's an option then */ + if (isYES(*val_str)) { + value = 1; + } else if (isNO(*val_str)) { + value = 0; + } else if (!strncasecmp(ON_STRING, val_str, + sizeof(ON_STRING))) { + value = 1; + } else if (!strncasecmp(OFF_STRING, val_str, + sizeof(OFF_STRING))) { + value = 0; + } else { + return RET_ERROR; + } + + if (!strncasecmp(LOG_PLATFORM_STRING, opt_str, + sizeof(LOG_PLATFORM_STRING))) { + config->lc_plog = value; + } else if (!strncasecmp(LOG_LIMITER_STRING, opt_str, + sizeof(LOG_LIMITER_STRING))) { + config->lc_limiter = value; + } else { + return RET_ERROR; + } + } + + return RET_SUCCESS; +} + +/* Function returns 0 for success or -1 when error occurred */ +int __log_config_read(const char* config_file, struct log_config* config) +{ + FILE* fconfig = NULL; + char buf[CONFIG_LINE_MAX_LEN]; + char opt[CONFIG_OPTION_MAX_LEN]; + char opt_value[CONFIG_OPTION_MAX_LEN]; + int prio = (-1); + int ret = 0; + + /* Check input */ + if (NULL == config_file || NULL == config) { + return RET_ERROR; + } + + if (NULL == (fconfig = fopen(config_file, "r"))) { + return RET_ERROR; + } + + while (1) { + memset(buf, 0, CONFIG_LINE_MAX_LEN); + errno = 0; + if (NULL == fgets(buf, CONFIG_LINE_MAX_LEN, fconfig)) { + if (!errno) { + break; + } + goto bailout; + } + + /* We ignore comments and blank lines */ + if (isCOMMENT(*buf) || isNEWLINE(*buf)) { + continue; + } + + memset(opt, 0, sizeof(opt)); + memset(opt_value, 0, sizeof(opt_value)); + prio = (-1); + /* Read configure line, sscanf() should return two tokens, + * even for tag filtering rule */ + ret = sscanf(buf, "%[A-z0-9-]\t%[A-z0-9]", + opt, opt_value); + if (ret != 2) { /* The line is malformed ? */ + char c = 0; + /* This could be rule with space inside TAG */ + ret = sscanf(buf, "\"%[]A-z0-9*\x20_+:;/-]\"\t|\t%c\t%[A-z0-9]", + opt, &c, opt_value); + if (ret != 3) { + goto bailout; + } + prio = (int)c; + } + + + if (0 > log_config_multiplex_opt(opt, opt_value, prio, config)) { + goto bailout; + } + } + + fclose(fconfig); + return RET_SUCCESS; + +bailout: + /* These actions should warranty that + we cleanly handle initialization errors */ + fclose(fconfig); + /* Clean rules list to prevent log limiter initialization, + if configuration error occured. */ + __log_limiter_rules_purge(); + + return RET_ERROR; +} diff --git a/src/libdlog/loglimiter.c b/src/libdlog/loglimiter.c new file mode 100644 index 0000000..c83af75 --- /dev/null +++ b/src/libdlog/loglimiter.c @@ -0,0 +1,419 @@ +/* + * DLOG + * Copyright (c) 2013 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stddef.h> +#include <limits.h> +#include <sys/types.h> +#include <errno.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <fcntl.h> +#include <unistd.h> +#include <string.h> +#include <ctype.h> +#include <pthread.h> +#include <sys/time.h> +#include <time.h> + +/* Included for priorities level */ +#include <dlog.h> +#include "loglimiter.h" + +/* Defines maximal meaningful tag length */ +#define TAG_REASONABLE_LEN 32 + +/* Some random big odd number to make hash more diverse */ +#define HASH_MAGIC_THINGY 5237231 + +#define TIME_FRAME 60 + +struct rule{ + /* TODO: List element handle, the list could be embedded + into structure some day, like kernel lists */ + struct rule* prev; + + unsigned hash; + int prio; + int limit; + int hit; + time_t start; + char tag[TAG_REASONABLE_LEN]; +}; + +typedef int (*hash_cmp_func_t)(struct rule*, struct rule*); +typedef int (*hash_match_func_t)(struct rule*, unsigned, const char*, int); + +struct hashmap { + hash_cmp_func_t cmp; + hash_match_func_t match; + int size; + void* bucket[]; +}; + +struct hashmap* rules_hashmap = NULL; + +/* Keep rules table as single-linked list */ +struct rule* rules_table = NULL; + +#define HASHMAP_MASK(hm) ((int)(hm->size - 1)) +#define HASHMAP_LINEAR_PROBE_LEAP 1 + +static void rules_destroy(struct rule* rlist) +{ + struct rule* r; + + if (NULL == rlist) { + return; + } + + while ((r = rlist)) { + rlist = rlist->prev; + free(r); + } +} + +void __log_limiter_rules_purge(void) +{ + rules_destroy(rules_table); + rules_table = NULL; +} + +static int rule_compare(struct rule* r1, struct rule* r2) +{ + if (r1->hash == r2->hash) { + if (r1->prio == r2->prio) { + return strncmp(r1->tag, r2->tag, TAG_REASONABLE_LEN); + } else { + return (r1->prio > r2->prio ? 1 : (-1)); + } + } + + return (r1->hash > r2->hash ? 1 : (-1)); +} + +static int rule_match(struct rule* r1, unsigned key, const char* s, int prio) +{ + if (r1->hash == key) { + if (r1->prio == prio) { + return strncmp(r1->tag, s, TAG_REASONABLE_LEN); + } else { + return (r1->prio > prio ? 1 : (-1)); + } + } + + return (r1->hash > key ? 1 : (-1)); +} + +/* Translate fancy priority notation into common denominator */ +static int util_prio_to_char(int prio) +{ + static const char pri_table[DLOG_PRIO_MAX] = { + [DLOG_VERBOSE] = 'V', + [DLOG_DEBUG] = 'D', + [DLOG_INFO] = 'I', + [DLOG_WARN] = 'W', + [DLOG_ERROR] = 'E', + [DLOG_FATAL] = 'F', + [DLOG_SILENT] = 'S', + }; + + if (DLOG_PRIO_MAX > prio && prio >= 0) { + return pri_table[prio]; + } else { + switch (prio) { + case 'V': case 'v': case '1': + case 'D': case 'd': case '2': + case 'I': case 'i': case '3': + case 'W': case 'w': case '4': + case 'E': case 'e': case '5': + case 'F': case 'f': case '6': + case 'S': case 's': case '7': + case '*': + return prio; + + default: + ;; + } + } + + return '?'; +} + +/* The key is built from TAG and priority by DJB algorithm (Dan Bernstein). + * Key is only 31 bits long. Negative values are keep to hold NULL bucket */ +static unsigned util_hash_key(const char* s, int c) +{ + unsigned hash = (unsigned)c; + + hash = ((hash << 5) + hash) + c; + + if (!s || !s[0]) { + goto finish; + } + + while ('\0' != (c = *s++)) { + hash = ((hash << 5) + hash) + c; + } + +finish: + /* Makes the hash more diverse */ + hash *= HASH_MAGIC_THINGY; + + return hash; +} + +/* Create hashmap, it's internal interface. */ +static struct hashmap* hashmap_create(int size, hash_cmp_func_t cmp_func, + hash_match_func_t match_func) +{ + struct hashmap* hm = NULL; + /* please keep hashmap fill ratio around 50% */ + int internal_size = size << 1; + + if (!cmp_func || !match_func || !size) { + return NULL; + } + + + /* Round up the lines counter to next power of two. */ + internal_size--; + internal_size |= internal_size >> 1; + internal_size |= internal_size >> 2; + internal_size |= internal_size >> 4; + internal_size |= internal_size >> 8; + internal_size |= internal_size >> 16; + internal_size++; + + hm = malloc(sizeof(*hm) + internal_size * sizeof(void*)); + if (!hm) { + return NULL; + } + + hm->size = internal_size; + hm->cmp = cmp_func; + hm->match = match_func; + /* Initialize hash field to correct value */ + memset((void*)hm + sizeof(*hm), 0, hm->size * sizeof(void*)); + + return hm; +} + +static void hashmap_destroy(struct hashmap* hm) +{ + if (hm) { + hm->size = 0; + free(hm); + } +} + +static void hashmap_add(struct hashmap* hm, struct rule* r) +{ + unsigned b = (r->hash & HASHMAP_MASK(hm)); + + while (hm->bucket[b]) { + if (!hm->cmp(r, (struct rule*)hm->bucket[b])) { + break; + } + b = (b + 1) & HASHMAP_MASK(hm); + } + + hm->bucket[b] = r; +} + +static struct rule* hashmap_search(struct hashmap* hm, unsigned key, + const char* tag, int prio) +{ + unsigned b = (key & HASHMAP_MASK(hm)); + unsigned b0 = b; + + while (hm->bucket[b]) { + if (!hm->match(hm->bucket[b], key, tag, prio)) { + break; + } + + b = (b + 1) & HASHMAP_MASK(hm); + + if (b0 == b) { + return NULL; + } + } + + if (!hm->bucket[b]) { + return NULL; + } + + return hm->bucket[b]; +} + +/* Must be always executed after __log_config_read() */ +int __log_limiter_initialize(void) +{ + int hm_size; + struct rule* rlist = NULL; + + /* logconfig.c module had to initialize this correctly */ + if (NULL == rules_table) { + return (-1); + } + + /* Count rules in the table */ + hm_size = 0; + rlist = rules_table; + while (rlist) { + hm_size++; + rlist = rlist->prev; + } + + /* Allocate hashmap */ + rules_hashmap = (struct hashmap*) hashmap_create(hm_size, + &rule_compare, + &rule_match); + if (NULL == rules_hashmap || !rules_hashmap->size) { + goto bailout; + } + + /* Add rule to hashmap */ + rlist = rules_table; + while (rlist) { + hashmap_add(rules_hashmap, rlist); + rlist = rlist->prev; + } + + return 0; + +bailout: + hashmap_destroy(rules_hashmap); + rules_destroy(rules_table); + rules_table = NULL; + rules_hashmap = NULL; + + return (-1); +} + +void __log_limiter_destroy(void) +{ + hashmap_destroy(rules_hashmap); + rules_destroy(rules_table); + rules_table = NULL; + rules_hashmap = NULL; +} + +int __log_limiter_add_rule(const char* tag, int prio, int limit) +{ + struct rule* r; + + if (!tag) { + return (-1); + } + + r = (struct rule*) malloc(sizeof(*r)); + if (NULL == r) { + return (-1); + } + memset(r, 0, sizeof(*r)); + + strncpy(r->tag, tag, TAG_REASONABLE_LEN); + r->prio = util_prio_to_char(prio); + r->hash = util_hash_key(tag, r->prio); + r->limit = limit; + r->start = time(NULL); + r->hit = 0; + + r->prev = rules_table; + rules_table = r; + + return 0; +} + + +/* Function implement logic needed to decide, + whenever message is written to log or not. + + Possible return values are: + 0 - to indicate that message is deny to write into log. + (-1) - to indicate that limit of the messages is reached. + 1 - to indicate that message is allowed to write into log. +*/ +int __log_limiter_pass_log(const char* tag, int prio) +{ + unsigned key = 0; + struct rule* r = NULL; + time_t now = 0; + + key = util_hash_key(tag, util_prio_to_char(prio)); + r = hashmap_search(rules_hashmap, key, tag, util_prio_to_char(prio)); + + if (!r) { + /* Rule not found, let's check general rule TAG:* */ + key = util_hash_key(tag, '*'); + r = hashmap_search(rules_hashmap, key, tag, '*'); + if (!r) { + /* Rule TAG:* not found, + let check general rule *:priority */ + key = util_hash_key("*", util_prio_to_char(prio)); + r = hashmap_search(rules_hashmap, key, "*", + util_prio_to_char(prio)); + if (!r) { + /* All known paths were exhausted, + use global rule *:* */ + key = util_hash_key("*", '*'); + r = hashmap_search(rules_hashmap, key, "*", '*'); + + /* *:* is not defined, so pass message through */ + if (!r) { + return 1; + } + } + } + } + + if (!r->limit) { + return 0; + } else if (__LOG_LIMITER_LIMIT_MAX < r->limit) { + return 1; + } + + /* Decide, if it should go through or stop */ + now = time(NULL); + + if (0 > now) { + return 1; + } + + if (now - r->start <= TIME_FRAME) { + if (r->hit >= 0) { + if (r->hit < r->limit) { + r->hit++; + return 1; + } + r->hit = INT_MIN+1; + return (-1); + } else { + r->hit++; + return 0; + } + + } else { + r->start = now; + r->hit = 0; + return 1; + } + + /* If everything failed, then pass message through */ + return 1; +} diff --git a/src/logger/logger.c b/src/logger/logger.c new file mode 100644 index 0000000..d3d5fc6 --- /dev/null +++ b/src/logger/logger.c @@ -0,0 +1,941 @@ +/* + * Copyright (c) 2005-2008, The Android Open Source Project + * Copyright (c) 2009-2013, Samsung Electronics Co., Ltd. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#define _GNU_SOURCE + +#include <stdio.h> +#include <stdlib.h> +#include <stdbool.h> +#include <stdarg.h> +#include <string.h> +#include <unistd.h> +#include <limits.h> +#include <signal.h> +#include <fcntl.h> +#include <time.h> +#include <sys/time.h> +#include <ctype.h> +#include <errno.h> +#include <assert.h> +#include <sys/stat.h> +#include <arpa/inet.h> + +#include <logger.h> +#include <logprint.h> + +#ifdef DEBUG_ON +#define _D(...) printf(__VA_ARGS__) +#else +#define _D(...) do { } while (0) +#endif +#define _E(...) fprintf(stderr, __VA_ARGS__) + +#define COMMAND_MAX 5 +#define DELIMITER " " +#define FILE_PERMS (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH) +#define MAX_ARGS 16 +#define MAX_ROTATED 4 + +#define CONFIG_FILE "/opt/etc/dlog_logger.conf" + +#define ARRAY_SIZE(name) (sizeof(name)/sizeof(name[0])) + +struct queued_entry { + union { + unsigned char buf[LOGGER_ENTRY_MAX_LEN + 1] __attribute__((aligned(4))); + struct logger_entry entry __attribute__((aligned(4))); + }; + struct queued_entry *next; +}; + +struct log_command { + char *filename; + int option_file; + int option_buffer; + int rotate_size; + int max_rotated; + int devices[LOG_ID_MAX]; + log_format *format; +}; + +struct log_work { + char *filename; + bool printed; + int fd; + int size; + int rotate_size; + int max_rotated; + log_format *format; + struct log_work *next; +}; + +struct log_task_link { + struct log_work *work; + struct log_task_link *next; +}; + +struct log_device { + int id; + int fd; + struct queued_entry *queue; + struct log_task_link *task; + struct log_device *next; +}; + +static const char *device_path_table[] = { + [LOG_ID_MAIN] = "/dev/log_main", + [LOG_ID_RADIO] = "/dev/log_radio", + [LOG_ID_SYSTEM] = "/dev/log_system", + [LOG_ID_APPS] = "/dev/log_apps", + [LOG_ID_MAX] = NULL +}; + +static struct log_work *works; +static struct log_device *devices; +static int device_list[] = { + [LOG_ID_MAIN] = false, + [LOG_ID_RADIO] = false, + [LOG_ID_SYSTEM] = false, + [LOG_ID_APPS] = false, + [LOG_ID_MAX] = false, +}; + +/* + * get log device id from device path table by device name + */ +static int get_device_id_by_name(const char *name) +{ + int i; + + if (name == NULL) + return -1; + for (i = 0; i < ARRAY_SIZE(device_path_table); i++) { + if (strstr(device_path_table[i], name) != NULL) + return i; + } + + return -1; +} + +/* + * check device registration on watch device list + */ +static int check_device(int id) +{ + if (id < 0 || LOG_ID_MAX < id) + return 0; + + return (device_list[id] == true) ? 0 : -1; +} + +/* + * register device to watch device list + */ +static int register_device(int id) +{ + if (id < 0 || LOG_ID_MAX < id) + return -1; + device_list[id] = true; + + return 0; +} + +/* + * comparison function to distinct entries by time + */ +static int cmp(struct queued_entry *a, struct queued_entry *b) +{ + int n = a->entry.sec - b->entry.sec; + if (n != 0) + return n; + + return a->entry.nsec - b->entry.nsec; +} + +/* + * enqueueing the log_entry into the log_device + */ +static void enqueue(struct log_device *device, struct queued_entry *entry) +{ + if (device->queue == NULL) { + device->queue = entry; + } else { + struct queued_entry **e = &device->queue; + while (*e && cmp(entry, *e) >= 0) + e = &((*e)->next); + entry->next = *e; + *e = entry; + } +} + +/* + * open file + */ +static int open_work(const char *path) +{ + return open(path, O_WRONLY | O_APPEND | O_CREAT, FILE_PERMS); +} + +/* + * rotate log files + */ +static void rotate_logs(struct log_work *logwork) +{ + int i, ret; + char *filename; + char file0[NAME_MAX]; + char file1[NAME_MAX]; + + close(logwork->fd); + filename = logwork->filename; + for (i = logwork->max_rotated ; i > 0 ; i--) { + snprintf(file1, NAME_MAX, "%s.%d", filename, i); + if (i - 1 == 0) + snprintf(file0, NAME_MAX, "%s", filename); + else + snprintf(file0, NAME_MAX, "%s.%d", filename, i - 1); + ret = rename(file0, file1); + if (ret < 0 && errno != ENOENT) + _E("while rotating log works"); + } + /* open log file again */ + logwork->fd = open_work(filename); + if (logwork->fd < 0) { + _E("couldn't open log file"); + exit(EXIT_FAILURE); + } + logwork->size = 0; + + return; +} + +/* + * process to print log + * and check the log file size to rotate + */ +static void process_buffer(struct log_device *dev, struct logger_entry *buf) +{ + int bytes_written, err; + log_entry entry; + struct log_work *logwork; + struct log_task_link *task; + + err = log_process_log_buffer(buf, &entry); + + if (err < 0) + goto exit; + + for (task = dev->task; task; task = task->next) { + logwork = task->work; + if (log_should_print_line(logwork->format, + entry.tag, entry.priority)) { + bytes_written = + log_print_log_line(logwork->format, + logwork->fd, &entry); + if (bytes_written < 0) { + _E("work error"); + exit(EXIT_FAILURE); + } + logwork->size += bytes_written; + } + if (logwork->rotate_size > 0 && + (logwork->size / 1024) >= logwork->rotate_size) { + rotate_logs(logwork); + } + } +exit: + return; +} + +/* + * choose first device by log_entry + */ +static void choose_first(struct log_device *dev, struct log_device **firstdev) +{ + for (*firstdev = NULL; dev != NULL; dev = dev->next) { + if (dev->queue != NULL && + (*firstdev == NULL || + cmp(dev->queue, + (*firstdev)->queue) < 0)) { + *firstdev = dev; + } + } +} + +/* + * print beginnig string into the log files + */ +static void maybe_print_start(struct log_device *dev) +{ + struct log_work *logwork; + struct log_task_link *task; + char buf[1024]; + + for (task = dev->task; task; task = task->next) { + logwork = task->work; + if (!logwork->printed) { + logwork->printed = true; + snprintf(buf, sizeof(buf), + "--------- beginning of %s\n", + device_path_table[dev->id]); + if (write(logwork->fd, buf, strlen(buf)) < 0) { + _E("maybe work error"); + exit(EXIT_FAILURE); + } + } + } +} + +/* + * skip log_entry + */ +static void skip_next_entry(struct log_device *dev) +{ + maybe_print_start(dev); + struct queued_entry *entry = dev->queue; + dev->queue = entry->next; + free(entry); +} + +/* + * print log_entry + */ +static void print_next_entry(struct log_device *dev) +{ + maybe_print_start(dev); + process_buffer(dev, &dev->queue->entry); + skip_next_entry(dev); +} + +/* + * do logging + */ +static void do_logger(struct log_device *dev) +{ + struct log_device *pdev; + int ret, result; + fd_set readset; + bool sleep = false; + int queued_lines = 0; + int max = 0; + + for (pdev = dev; pdev; pdev = pdev->next) { + if (pdev->fd > max) + max = pdev->fd; + } + + while (1) { + do { + struct timeval timeout = { 0, 5000 /* 5ms */ }; + FD_ZERO(&readset); + for (pdev = dev; pdev; pdev = pdev->next) + FD_SET(pdev->fd, &readset); + result = select(max + 1, &readset, NULL, NULL, + sleep ? NULL : &timeout); + } while (result == -1 && errno == EINTR); + + if (result < 0) + continue; + + for (pdev = dev; pdev; pdev = pdev->next) { + if (FD_ISSET(pdev->fd, &readset)) { + struct queued_entry *entry = + (struct queued_entry *) + malloc(sizeof(struct queued_entry)); + if (entry == NULL) { + _E("failed to malloc queued_entry\n"); + goto exit;//exit(EXIT_FAILURE); + } + entry->next = NULL; + ret = read(pdev->fd, entry->buf, + LOGGER_ENTRY_MAX_LEN); + if (ret < 0) { + if (errno == EINTR) { + free(entry); + goto next; + } + if (errno == EAGAIN) { + free(entry); + break; + } + _E("dlogutil read"); + goto exit;//exit(EXIT_FAILURE); + } else if (!ret) { + free(entry); + _E("read: Unexpected EOF!\n"); + exit(EXIT_FAILURE); + } else if (entry->entry.len != + ret - sizeof(struct logger_entry)) { + free(entry); + _E("unexpected length. Expected %d, got %d\n", + entry->entry.len, + ret - sizeof(struct logger_entry)); + goto exit;//exit(EXIT_FAILURE); + } + + entry->entry.msg[entry->entry.len] = '\0'; + + enqueue(pdev, entry); + ++queued_lines; + } + } + + if (result == 0) { + sleep = true; + while (true) { + choose_first(dev, &pdev); + if (pdev == NULL) + break; + print_next_entry(pdev); + --queued_lines; + } + } else { + /* print all that aren't the last in their list */ + sleep = false; + while (true) { + choose_first(dev, &pdev); + if (pdev == NULL || pdev->queue->next == NULL) + break; + print_next_entry(pdev); + --queued_lines; + } + } +next: + ; + } +exit: + exit(EXIT_FAILURE); +} + + +/* + * create a work + */ +static struct log_work *work_new(void) +{ + struct log_work *work; + + work = malloc(sizeof(*work)); + if (work == NULL) { + _E("failed to malloc log_work\n"); + return NULL; + } + work->filename = NULL; + work->fd = -1; + work->printed = false; + work->size = 0; + work->next = NULL; + _D("work alloc %p\n", work); + + return work; +} + +/* + * add a new log_work to the tail of chain + */ +static int work_add_to_tail(struct log_work *work, struct log_work *nwork) +{ + struct log_work *tail = work; + + if (!nwork) + return -1; + + if (work == NULL) { + work = nwork; + return 0; + } + + while (tail->next) + tail = tail->next; + tail->next = nwork; + + return 0; +} + +/* + * add a new work task to the tail of chain + */ +static void work_add_to_device(struct log_device *dev, struct log_work *work) +{ + struct log_task_link *tail; + + if (!dev || !work) + return; + _D("dev %p work %p\n", dev, work); + if (dev->task == NULL) { + dev->task = + (struct log_task_link *) + malloc(sizeof(struct log_task_link)); + if (dev->task == NULL) { + _E("failed to malloc log_task_link\n"); + return; + } + tail = dev->task; + } else { + tail = dev->task; + while (tail->next) + tail = tail->next; + tail->next = + (struct log_task_link *) + malloc(sizeof(struct log_task_link)); + if (tail->next == NULL) { + _E("failed to malloc log_task_link\n"); + return; + } + tail = tail->next; + } + tail->work = work; + tail->next = NULL; +} + +/* + * free work file descriptor + */ +static void work_free(struct log_work *work) +{ + if (!work) + return; + if (work->filename) { + free(work->filename); + work->filename = NULL; + if (work->fd != -1) { + close(work->fd); + work->fd = -1; + } + } + log_format_free(work->format); + work->format = NULL; + free(work); + work = NULL; +} + +/* + * free all the nodes after the "work" and includes itself + */ +static void work_chain_free(struct log_work *work) +{ + if (!work) + return; + while (work->next) { + struct log_work *tmp = work->next; + work->next = tmp->next; + work_free(tmp); + } + work_free(work); + work = NULL; +} + +/* + * create a new log_device instance + * and open device + */ +static struct log_device *device_new(int id) +{ + struct log_device *dev; + + if (LOG_ID_MAX < id) + return NULL; + dev = malloc(sizeof(*dev)); + if (dev == NULL) { + _E("failed to malloc log_device\n"); + return NULL; + } + dev->id = id; + dev->fd = open(device_path_table[id], O_RDONLY); + if (dev->fd < 0) { + _E("Unable to open log device '%s': %s\n", + device_path_table[id], + strerror(errno)); + return NULL; + } + _D("device new id %d fd %d\n", dev->id, dev->fd); + dev->task = NULL; + dev->queue = NULL; + dev->next = NULL; + + return dev; +} + +/* + * add a new log_device to the tail of chain + */ +static int device_add_to_tail(struct log_device *dev, struct log_device *ndev) +{ + struct log_device *tail = dev; + + if (!dev || !ndev) + return -1; + + while (tail->next) + tail = tail->next; + tail->next = ndev; + + return 0; +} + +/* + * add a new log_device or add to the tail of chain + */ +static void device_add(int id) +{ + if (check_device(id) < 0) + return; + + if (!devices) { + devices = device_new(id); + if (devices == NULL) { + _E("failed to device_new: %s\n", + device_path_table[id]); + exit(EXIT_FAILURE); + } + } else { + if (device_add_to_tail(devices, device_new(id)) < 0) { + _E("failed to device_add_to_tail: %s\n", + device_path_table[id]); + exit(EXIT_FAILURE); + } + } + return; +} + +/* + * free one log_device and it doesn't take care of chain so it + * may break the chain list + */ +static void device_free(struct log_device *dev) +{ + if (!dev) + return; + if (dev->queue) { + while (dev->queue->next) { + struct queued_entry *tmp = + dev->queue->next; + dev->queue->next = tmp->next; + free(tmp); + } + free(dev->queue); + dev->queue = NULL; + } + if (dev->task) { + while (dev->task->next) { + struct log_task_link *tmp = + dev->task->next; + dev->task->next = tmp->next; + free(tmp); + } + free(dev->task); + dev->task = NULL; + } + free(dev); + dev = NULL; +} + +/* + * free all the nodes after the "dev" and includes itself + */ +static void device_chain_free(struct log_device *dev) +{ + if (!dev) + return; + while (dev->next) { + struct log_device *tmp = dev->next; + dev->next = tmp->next; + device_free(tmp); + } + device_free(dev); + dev = NULL; +} + +/* + * parse command line + * using getopt function + */ +static int parse_command_line(char *linebuffer, struct log_command *cmd) +{ + int i, ret, id, argc; + char *argv[MAX_ARGS]; + char *tok, *cmdline; + + if (linebuffer == NULL || cmd == NULL) + return -1; + /* copy command line */ + cmdline = strdup(linebuffer); + tok = strtok(cmdline, DELIMITER); + /* check the availability of command line + by comparing first word with dlogutil*/ + if (!tok || strcmp(tok, "dlogutil")) { + _D("Ignore this line (%s)\n", linebuffer); + free(cmdline); + return -1; + } + _D("Parsing this line (%s)\n", linebuffer); + /* fill the argc and argv + for extract option from command line */ + argc = 0; + while (tok && (argc < MAX_ARGS)) { + argv[argc] = strdup(tok); + tok = strtok(NULL, DELIMITER); + argc++; + } + free(cmdline); + + /* initialize the command struct with the default value */ + memset(cmd->devices, 0, sizeof(cmd->devices)); + cmd->option_file = false; + cmd->option_buffer = false; + cmd->filename = NULL; + cmd->rotate_size = 0; + cmd->max_rotated = MAX_ROTATED; + cmd->format = (log_format *)log_format_new(); + + /* get option and fill the command struct */ + while ((ret = getopt(argc, argv, "f:r:n:v:b:")) != -1) { + switch (ret) { + case 'f': + cmd->filename = strdup(optarg); + _D("command filename %s\n", cmd->filename); + cmd->option_file = true; + break; + case 'b': + id = get_device_id_by_name(optarg); + _D("command device name %s id %d\n", optarg, id); + if (id < 0 || LOG_ID_MAX < id) + break; + cmd->option_buffer = true; + /* enable to log in device on/off struct */ + cmd->devices[id] = true; + /* enable to open in global device on/off struct */ + register_device(id); + break; + case 'r': + if (!isdigit(optarg[0])) + goto exit_free; + cmd->rotate_size = atoi(optarg); + _D("command rotate size %d\n", cmd->rotate_size); + break; + case 'n': + if (!isdigit(optarg[0])) + goto exit_free; + cmd->max_rotated = atoi(optarg); + _D("command max rotated %d\n", cmd->max_rotated); + break; + case 'v': + { + log_print_format print_format; + print_format = log_format_from_string(optarg); + if (print_format == FORMAT_OFF) { + _E("failed to add format\n"); + goto exit_free; + } + _D("command format %s\n", optarg); + log_set_print_format(cmd->format, print_format); + } + break; + default: + break; + } + } + /* add filter string, when command line have tags */ + if (argc != optind) { + for (i = optind ; i < argc ; i++) { + ret = log_add_filter_string(cmd->format, argv[i]); + _D("command add fileter string %s\n", argv[i]); + if (ret < 0) { + _E("Invalid filter expression '%s'\n", argv[i]); + goto exit_free; + } + } + } else { + ret = log_add_filter_string(cmd->format, "*:d"); + if (ret < 0) { + _E("Invalid silent filter expression\n"); + goto exit_free; + } + } + /* free argv */ + for (i = 0; i < argc; i++) + free(argv[i]); + + if (cmd->option_file == false) + goto exit_free; + /* If it have not the -b option, + set the default devices to open and log */ + if (cmd->option_buffer == false) { + _D("set default device\n", cmd->filename); + cmd->devices[LOG_ID_MAIN] = true; + cmd->devices[LOG_ID_SYSTEM] = true; + register_device(LOG_ID_MAIN); + register_device(LOG_ID_SYSTEM); + } + /* for use getopt again */ + optarg = NULL; + optind = 1; + optopt = 0; + + return 0; + +exit_free: + if (cmd->filename) + free(cmd->filename); + return -1; +} + +/* + * parse command from configuration file + * and return the command list with number of command + * if an command was successfully parsed, then it returns number of parsed command. + * on error or not founded, 0 is returned + */ +static int parse_command(struct log_command *command_list) +{ + FILE *fp; + int ncmd; + char *line_p; + char linebuffer[1024]; + + fp = fopen(CONFIG_FILE, "re"); + if (!fp) { + _E("no config file\n"); + goto exit; + } + ncmd = 0; + while (fgets(linebuffer, sizeof(linebuffer), fp) != NULL) { + line_p = strchr(linebuffer, '\n'); + if (line_p != NULL) + *line_p = '\0'; + if (parse_command_line(linebuffer, &command_list[ncmd]) == 0) + ncmd++; + if (COMMAND_MAX <= ncmd) + break; + } + fclose(fp); + + return ncmd; + +exit: + return 0; +} + +/* + * free dynamically allocated memory + */ +static void cleanup(void) +{ + work_chain_free(works); + device_chain_free(devices); +} + +/* + * SIGINT, SIGTERM, SIGQUIT signal handler + */ +static void sig_handler(int signo) +{ + _D("sig_handler\n"); + cleanup(); + exit(EXIT_SUCCESS); +} + +int main(int argc, char **argv) +{ + int i, ncmd; + struct stat statbuf; + struct log_device *dev; + struct log_work *work; + struct log_command command_list[COMMAND_MAX]; + struct sigaction act; + + /* set the signal handler for free dynamically allocated memory. */ + memset(&act, 0, sizeof(act)); + sigemptyset(&act.sa_mask); + act.sa_handler = (void *)sig_handler; + act.sa_flags = 0; + + if (sigaction(SIGQUIT, &act, NULL) < 0) + _E("failed to sigaction for SIGQUIT"); + if (sigaction(SIGINT, &act, NULL) < 0) + _E("failed to sigaction for SIGINT"); + if (sigaction(SIGTERM, &act, NULL) < 0) + _E("failed to sigaction for SIGTERM"); + + /* parse command from command configuration file. */ + ncmd = parse_command(command_list); + + /* If it have nothing command, exit. */ + if (!ncmd) + goto exit; + + /* create log device */ + device_add(LOG_ID_MAIN); + device_add(LOG_ID_SYSTEM); + device_add(LOG_ID_RADIO); + + /* create work from the parsed command */ + for (i = 0; i < ncmd; i++) { + work = work_new(); + _D("work new\n"); + if (work == NULL) { + _E("failed to work_new\n"); + goto clean_exit; + } + /* attatch the work to global works variable */ + if (work_add_to_tail(works, work) < 0) { + _E("failed to work_add_to_tail\n"); + goto clean_exit; + } + /* 1. set filename, fd and file current size */ + work->filename = command_list[i].filename; + if (work->filename == NULL) { + _E("file name is NULL"); + goto clean_exit; + } + work->fd = open_work(work->filename); + if (work->fd < 0) { + _E("failed to open log file"); + exit(EXIT_FAILURE); + } + if (fstat(work->fd, &statbuf) == -1) + work->size = 0; + else + work->size = statbuf.st_size; + + /* 2. set size limits for log files */ + work->rotate_size = command_list[i].rotate_size; + + /* 3. set limit on the number of rotated log files */ + work->max_rotated = command_list[i].max_rotated; + + /* 4. set log_format to filter logs*/ + work->format = command_list[i].format; + + /* 5. attatch the work to device task for logging */ + dev = devices; + while (dev) { + if (command_list[i].devices[dev->id] == true) { + work_add_to_device(dev, work); + } + dev = dev->next; + } + } + + /* do log */ + do_logger(devices); + +clean_exit: + work_chain_free(works); + device_chain_free(devices); +exit: + return 0; +} diff --git a/src/logutil/logutil.c b/src/logutil/logutil.c new file mode 100755 index 0000000..d421451 --- /dev/null +++ b/src/logutil/logutil.c @@ -0,0 +1,840 @@ +/* + * Copyright (c) 2005-2008, The Android Open Source Project + * Copyright (c) 2009-2013, Samsung Electronics Co., Ltd. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#define _GNU_SOURCE + +#include <stdio.h> +#include <stdlib.h> +#include <stdbool.h> +#include <stdarg.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> +#include <time.h> +#include <sys/time.h> +#include <ctype.h> +#include <errno.h> +#include <assert.h> +#include <sys/stat.h> +#include <arpa/inet.h> + + +#include <logger.h> +#include <logprint.h> + +#define DEFAULT_LOG_ROTATE_SIZE_KBYTES 16 +#define DEFAULT_MAX_ROTATED_LOGS 4 + +#define LOG_FILE_DIR "/dev/log_" + +static log_format* g_logformat; +static bool g_nonblock = false; +static int g_tail_lines = 0; + +static const char * g_output_filename = NULL; +static int g_log_rotate_size_kbytes = 0; // 0 means "no log rotation" +static int g_max_rotated_logs = DEFAULT_MAX_ROTATED_LOGS; // 0 means "unbounded" +static int g_outfd = -1; +static off_t g_out_byte_count = 0; +static int g_dev_count = 0; + +struct queued_entry_t { + union { + unsigned char buf[LOGGER_ENTRY_MAX_LEN + 1] __attribute__((aligned(4))); + struct logger_entry entry __attribute__((aligned(4))); + }; + struct queued_entry_t* next; +}; + +static int cmp(struct queued_entry_t* a, struct queued_entry_t* b) +{ + int n = a->entry.sec - b->entry.sec; + if (n != 0) + { + return n; + } + return a->entry.nsec - b->entry.nsec; +} + + +struct log_device_t { + char* device; + int fd; + bool printed; + struct queued_entry_t* queue; + struct log_device_t* next; +}; + +static void enqueue(struct log_device_t* device, struct queued_entry_t* entry) +{ + if( device->queue == NULL) + { + device->queue = entry; + } + else + { + struct queued_entry_t** e = &device->queue; + while(*e && cmp(entry, *e) >= 0 ) + { + e = &((*e)->next); + } + entry->next = *e; + *e = entry; + } +} + +static int open_logfile (const char *pathname) +{ + return open(pathname, O_WRONLY | O_APPEND | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH); +} + +static void rotate_logs() +{ + int err; + int i; + char file0[256]={0}; + char file1[256]={0}; + + // Can't rotate logs if we're not outputting to a file + if (g_output_filename == NULL) { + return; + } + + close(g_outfd); + + for (i = g_max_rotated_logs ; i > 0 ; i--) + { + snprintf(file1, 255, "%s.%d", g_output_filename, i); + + if (i - 1 == 0) { + snprintf(file0, 255, "%s", g_output_filename); + } else { + snprintf(file0, 255, "%s.%d", g_output_filename, i - 1); + } + + err = rename (file0, file1); + + if (err < 0 && errno != ENOENT) { + perror("while rotating log files"); + } + } + + g_outfd = open_logfile (g_output_filename); + + if (g_outfd < 0) { + perror ("couldn't open output file"); + exit(-1); + } + + g_out_byte_count = 0; + +} + + +static void processBuffer(struct log_device_t* dev, struct logger_entry *buf) +{ + int bytes_written = 0; + int err; + log_entry entry; + char mgs_buf[1024]; + + err = log_process_log_buffer(buf, &entry); + + if (err < 0) { + goto error; + } + + if (log_should_print_line(g_logformat, entry.tag, entry.priority)) { + if (false && g_dev_count > 1) { + // FIXME + mgs_buf[0] = dev->device[0]; + mgs_buf[1] = ' '; + bytes_written = write(g_outfd, mgs_buf, 2); + if (bytes_written < 0) + { + perror("output error"); + exit(-1); + } + } + + bytes_written = log_print_log_line(g_logformat, g_outfd, &entry); + + if (bytes_written < 0) + { + perror("output error"); + exit(-1); + } + } + + g_out_byte_count += bytes_written; + + if (g_log_rotate_size_kbytes > 0 && (g_out_byte_count / 1024) >= g_log_rotate_size_kbytes) { + if (g_nonblock) { + exit(0); + } else { + rotate_logs(); + } + } + +error: + //fprintf (stderr, "Error processing record\n"); + return; +} + +static void chooseFirst(struct log_device_t* dev, struct log_device_t** firstdev) +{ + for (*firstdev = NULL; dev != NULL; dev = dev->next) { + if (dev->queue != NULL && (*firstdev == NULL || cmp(dev->queue, (*firstdev)->queue) < 0)) + { + *firstdev = dev; + } + } +} + +static void maybePrintStart(struct log_device_t* dev) { + if (!dev->printed) { + dev->printed = true; + if (g_dev_count > 1 ) { + char buf[1024]; + snprintf(buf, sizeof(buf), "--------- beginning of %s\n", dev->device); + if (write(g_outfd, buf, strlen(buf)) < 0) { + perror("output error"); + exit(-1); + } + } + } +} + +static void skipNextEntry(struct log_device_t* dev) { + maybePrintStart(dev); + struct queued_entry_t* entry = dev->queue; + dev->queue = entry->next; + free(entry); +} + +static void printNextEntry(struct log_device_t* dev) +{ + maybePrintStart(dev); + processBuffer(dev, &dev->queue->entry); + skipNextEntry(dev); +} + + +static void read_log_lines(struct log_device_t* devices) +{ + struct log_device_t* dev; + int max = 0; + int ret; + int queued_lines = 0; + bool sleep = false; // for exit immediately when log buffer is empty and g_nonblock value is true. + + int result; + fd_set readset; + + for (dev=devices; dev; dev = dev->next) { + if (dev->fd > max) { + max = dev->fd; + } + } + + while (1) { + do { + struct timeval timeout = { 0, 5000 /* 5ms */ }; // If we oversleep it's ok, i.e. ignore EINTR. + FD_ZERO(&readset); + for (dev=devices; dev; dev = dev->next) { + FD_SET(dev->fd, &readset); + } + result = select(max + 1, &readset, NULL, NULL, sleep ? NULL : &timeout); + } while (result == -1 && errno == EINTR); + + if (result >= 0) { + for (dev=devices; dev; dev = dev->next) { + if (FD_ISSET(dev->fd, &readset)) { + struct queued_entry_t* entry = (struct queued_entry_t *)malloc(sizeof( struct queued_entry_t)); + if (entry == NULL) { + fprintf(stderr,"Can't malloc queued_entry\n"); + exit(-1); + } + entry->next = NULL; + + /* NOTE: driver guarantees we read exactly one full entry */ + ret = read(dev->fd, entry->buf, LOGGER_ENTRY_MAX_LEN); + if (ret < 0) { + if (errno == EINTR) { + free(entry); + goto next; + } + if (errno == EAGAIN) { + free(entry); + break; + } + perror("dlogutil read"); + exit(EXIT_FAILURE); + } + else if (!ret) { + free(entry); + fprintf(stderr, "read: Unexpected EOF!\n"); + exit(EXIT_FAILURE); + } + else if (entry->entry.len != ret - sizeof(struct logger_entry)) { + free(entry); + fprintf(stderr, "read: unexpected length. Expected %d, got %d\n", + entry->entry.len, ret - sizeof(struct logger_entry)); + exit(EXIT_FAILURE); + } + + + entry->entry.msg[entry->entry.len] = '\0'; + + enqueue(dev, entry); + ++queued_lines; + } + } + + if (result == 0) { + // we did our short timeout trick and there's nothing new + // print everything we have and wait for more data + sleep = true; + while (true) { + chooseFirst(devices, &dev); + if (dev == NULL) { + break; + } + if (g_tail_lines == 0 || queued_lines <= g_tail_lines) { + printNextEntry(dev); + } else { + skipNextEntry(dev); + } + --queued_lines; + } + + // the caller requested to just dump the log and exit + if (g_nonblock) { + exit(0); + } + } else { + // print all that aren't the last in their list + sleep = false; + while (g_tail_lines == 0 || queued_lines > g_tail_lines) { + chooseFirst(devices, &dev); + if (dev == NULL || dev->queue->next == NULL) { + break; + } + if (g_tail_lines == 0) { + printNextEntry(dev); + } else { + skipNextEntry(dev); + } + --queued_lines; + } + } + } +next: + ; + } +} + + +static int clear_log(int logfd) +{ + return ioctl(logfd, LOGGER_FLUSH_LOG); +} + +/* returns the total size of the log's ring buffer */ +static int get_log_size(int logfd) +{ + return ioctl(logfd, LOGGER_GET_LOG_BUF_SIZE); +} + +/* returns the readable size of the log's ring buffer (that is, amount of the log consumed) */ +static int get_log_readable_size(int logfd) +{ + return ioctl(logfd, LOGGER_GET_LOG_LEN); +} + +static void setup_output() +{ + + if (g_output_filename == NULL) { + g_outfd = STDOUT_FILENO; + + } else { + struct stat statbuf; + + g_outfd = open_logfile (g_output_filename); + + if (g_outfd < 0) { + perror ("couldn't open output file"); + exit(-1); + } + if (fstat(g_outfd, &statbuf) == -1) + g_out_byte_count = 0; + else + g_out_byte_count = statbuf.st_size; + } +} + +static int set_log_format(const char * formatString) +{ + static log_print_format format; + + format = log_format_from_string(formatString); + + if (format == FORMAT_OFF) { + // FORMAT_OFF means invalid string + return -1; + } + + log_set_print_format(g_logformat, format); + + return 0; +} + +static void show_help(const char *cmd) +{ + fprintf(stderr,"Usage: %s [options] [filterspecs]\n", cmd); + + fprintf(stderr, "options include:\n" + " -s Set default filter to silent.\n" + " Like specifying filterspec '*:s'\n" + " -f <filename> Log to file. Default to stdout\n" + " -r [<kbytes>] Rotate log every kbytes. (16 if unspecified). Requires -f\n" + " -n <count> Sets max number of rotated logs to <count>, default 4\n" + " -v <format> Sets the log print format, where <format> is one of:\n\n" + " brief(by default) process tag thread raw time threadtime long\n\n" + " -c clear (flush) the entire log and exit, conflicts with '-g'\n" + " -d dump the log and then exit (don't block)\n" + " -t <count> print only the most recent <count> lines (implies -d)\n" + " -g get the size of the log's ring buffer and exit, conflicts with '-c'\n" + " -b <buffer> request alternate ring buffer\n" + " ('main' (default), 'radio', 'system')"); + + + fprintf(stderr,"\nfilterspecs are a series of \n" + " <tag>[:priority]\n\n" + "where <tag> is a log component tag (or * for all) and priority is:\n" + " V Verbose\n" + " D Debug\n" + " I Info\n" + " W Warn\n" + " E Error\n" + " F Fatal\n" + " S Silent (supress all output)\n" + "\n'*' means '*:D' and <tag> by itself means <tag>:V\n" + "If no filterspec is found, filter defaults to '*:I'\n\n"); +} + + +/* + * free one log_device_t and it doesn't take care of chain so it + * may break the chain list + */ +static void log_devices_free(struct log_device_t *dev) +{ + if (!dev) + return; + + if (dev->device) + free(dev->device); + + if (dev->queue) { + while (dev->queue->next) { + struct queued_entry_t *tmp = dev->queue->next; + dev->queue->next = tmp->next; + free(tmp); + } + free(dev->queue); + } + + free(dev); + dev = NULL; +} + + +/* + * free all the nodes after the "dev" and includes itself + */ +static void log_devices_chain_free(struct log_device_t *dev) +{ + if (!dev) + return; + + while (dev->next) { + struct log_device_t *tmp = dev->next; + dev->next = tmp->next; + log_devices_free(tmp); + } + + log_devices_free(dev); + dev = NULL; +} + + +/* + * create a new log_device_t instance but don't care about + * the device node accessable or not + */ +static struct log_device_t *log_devices_new(const char *path) +{ + struct log_device_t *new; + + if (!path || strlen(path) <= 0) + return NULL; + + new = malloc(sizeof(*new)); + if (!new) { + fprintf(stderr, "out of memory\n"); + return NULL; + } + + new->device = strdup(path); + new->fd = -1; + new->printed = false; + new->queue = NULL; + new->next = NULL; + + return new; +} + + +/* + * add a new device to the tail of chain + */ +static int log_devices_add_to_tail(struct log_device_t *devices, struct log_device_t *new) +{ + struct log_device_t *tail = devices; + + if (!devices || !new) + return -1; + + while (tail->next) + tail = tail->next; + + tail->next = new; + g_dev_count++; + + return 0; +} + +int main(int argc, char **argv) +{ + int err; + int has_set_log_format = 0; + int is_clear_log = 0; + int getLogSize = 0; + int mode = O_RDONLY; + int i; +// const char *forceFilters = NULL; + struct log_device_t* devices = NULL; + struct log_device_t* dev; + + g_logformat = (log_format *)log_format_new(); + + if (argc == 2 && 0 == strcmp(argv[1], "--test")) { + logprint_run_tests(); + exit(0); + } + + if (argc == 2 && 0 == strcmp(argv[1], "--help")) { + show_help(argv[0]); + exit(0); + } + + for (;;) { + int ret; + + ret = getopt(argc, argv, "cdt:gsf:r:n:v:b:D"); + + if (ret < 0) { + break; + } + + switch(ret) { + case 's': + // default to all silent + log_add_filter_rule(g_logformat, "*:s"); + break; + + case 'c': + is_clear_log = 1; + mode = O_WRONLY; + break; + + case 'd': + g_nonblock = true; + break; + + case 't': + g_nonblock = true; + g_tail_lines = atoi(optarg); + break; + + + case 'g': + getLogSize = 1; + break; + + case 'b': { + char *buf; + if (asprintf(&buf, LOG_FILE_DIR "%s", optarg) == -1) { + fprintf(stderr,"Can't malloc LOG_FILE_DIR\n"); + exit(-1); + } + + dev = log_devices_new(buf); + if (dev == NULL) { + fprintf(stderr,"Can't add log device: %s\n", buf); + exit(-1); + } + if (devices) { + if (log_devices_add_to_tail(devices, dev)) { + fprintf(stderr, "Open log device %s failed\n", buf); + exit(-1); + } + } else { + devices = dev; + g_dev_count = 1; + } + } + break; + + case 'f': + // redirect output to a file + + g_output_filename = optarg; + + break; + + case 'r': +// if (optarg == NULL) { +// fprintf(stderr,"optarg == null\n"); + // g_log_rotate_size_kbytes = DEFAULT_LOG_ROTATE_SIZE_KBYTES; + // } else { + //long logRotateSize; + //char *lastDigit; + + if (!isdigit(optarg[0])) { + fprintf(stderr,"Invalid parameter to -r\n"); + show_help(argv[0]); + exit(-1); + } + g_log_rotate_size_kbytes = atoi(optarg); + // } + break; + + case 'n': + if (!isdigit(optarg[0])) { + fprintf(stderr,"Invalid parameter to -r\n"); + show_help(argv[0]); + exit(-1); + } + + g_max_rotated_logs = atoi(optarg); + break; + + case 'v': + err = set_log_format (optarg); + if (err < 0) { + fprintf(stderr,"Invalid parameter to -v\n"); + show_help(argv[0]); + exit(-1); + } + + has_set_log_format = 1; + break; + + default: + fprintf(stderr,"Unrecognized Option\n"); + show_help(argv[0]); + exit(-1); + break; + } + } + + /* get log size conflicts with write mode */ + if (getLogSize && mode != O_RDONLY) { + show_help(argv[0]); + exit(-1); + } + + if (!devices) { + devices = log_devices_new("/dev/"LOGGER_LOG_MAIN); + if (devices == NULL) { + fprintf(stderr,"Can't add log device: %s\n", LOGGER_LOG_MAIN); + exit(-1); + } + g_dev_count = 1; + + int accessmode = + (mode == O_RDONLY) ? R_OK : (mode == O_WRONLY) ? W_OK : 0; + + // only add this if it's available + if (0 == access("/dev/"LOGGER_LOG_SYSTEM, accessmode)) { + if (log_devices_add_to_tail(devices, log_devices_new("/dev/"LOGGER_LOG_SYSTEM))) { + fprintf(stderr,"Can't add log device: %s\n", LOGGER_LOG_SYSTEM); + exit(-1); + } + } + if (0 == access("/dev/"LOGGER_LOG_APPS, accessmode)) { + if (log_devices_add_to_tail(devices, log_devices_new("/dev/"LOGGER_LOG_APPS))) { + fprintf(stderr,"Can't add log device: %s\n", LOGGER_LOG_APPS); + exit(-1); + } + } + +/* + // only add this if it's available + int fd; + if ((fd = open("/dev/"LOGGER_LOG_SYSTEM, mode)) != -1) { + devices->next = (struct log_device_t *)malloc( sizeof(struct log_device_t)); + devices->next->device = strdup("/dev/"LOGGER_LOG_SYSTEM); + devices->next->fd = -1; + devices->next->printed = false; + devices->next->queue = NULL; + devices->next->next = NULL; + g_dev_count ++; + + close(fd); + } +*/ + } + + if (g_log_rotate_size_kbytes != 0 && g_output_filename == NULL) + { + fprintf(stderr,"-r requires -f as well\n"); + show_help(argv[0]); + exit(-1); + } + + setup_output(); + + + if (has_set_log_format == 0) { + err = set_log_format("brief"); + } +/* + const char* logFormat = getenv("DLOG_PRINTF_LOG"); + + if (logFormat != NULL) { + err = set_log_format("brief"); + + if (err < 0) { + fprintf(stderr, "invalid format in DLOG_PRINTF_LOG '%s'\n", logFormat); + } + } + } + if (forceFilters) { + err = log_add_filter_string(g_logformat, forceFilters); + if (err < 0) { + fprintf (stderr, "Invalid filter expression in -logcat option\n"); + exit(0); + } + } else if (argc == optind) { + // Add from environment variable + char *env_tags_orig = getenv("DLOG_LOG_TAGS"); + + if (env_tags_orig != NULL) { + err = log_add_filter_string(g_logformat, env_tags_orig); + + if (err < 0) { + fprintf(stderr, "Invalid filter expression in DLOG_LOG_TAGS\n"); + show_help(argv[0]); + exit(-1); + } + } + } else { + // Add from commandline +*/ + fprintf(stderr,"arc = %d, optind = %d ,Kb %d, rotate %d\n", argc, optind,g_log_rotate_size_kbytes,g_max_rotated_logs); + + if(argc == optind ) + { + // Add from environment variable + //char *env_tags_orig = getenv("DLOG_TAGS"); + log_add_filter_string(g_logformat, "*:d"); + } + else + { + + for (i = optind ; i < argc ; i++) { + err = log_add_filter_string(g_logformat, argv[i]); + + if (err < 0) { + fprintf (stderr, "Invalid filter expression '%s'\n", argv[i]); + show_help(argv[0]); + exit(-1); + } + } + } +/* + } +*/ + dev = devices; + while (dev) { + dev->fd = open(dev->device, mode); + if (dev->fd < 0) { + fprintf(stderr, "Unable to open log device '%s': %s\n", + dev->device, strerror(errno)); + exit(EXIT_FAILURE); + } + + if (is_clear_log) { + int ret; + ret = clear_log(dev->fd); + if (ret) { + perror("ioctl"); + exit(EXIT_FAILURE); + } + } + + if (getLogSize) { + int size, readable; + + size = get_log_size(dev->fd); + if (size < 0) { + perror("ioctl"); + exit(EXIT_FAILURE); + } + + readable = get_log_readable_size(dev->fd); + if (readable < 0) { + perror("ioctl"); + exit(EXIT_FAILURE); + } + + printf("%s: ring buffer is %dKb (%dKb consumed), " + "max entry is %db, max payload is %db\n", dev->device, + size / 1024, readable / 1024, + (int) LOGGER_ENTRY_MAX_LEN, (int) LOGGER_ENTRY_MAX_PAYLOAD); + } + + dev = dev->next; + } + + if (getLogSize) { + return 0; + } + + if (is_clear_log) { + return 0; + } + + read_log_lines(devices); + + log_devices_chain_free(devices); + + return 0; +} diff --git a/src/shared/logprint.c b/src/shared/logprint.c new file mode 100755 index 0000000..449732c --- /dev/null +++ b/src/shared/logprint.c @@ -0,0 +1,682 @@ +/* + * DLOG + * Copyright (c) 2005-2008, The Android Open Source Project + * Copyright (c) 2012-2013 Samsung Electronics Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> +#include <errno.h> +#include <ctype.h> +#include <unistd.h> + +#include <logprint.h> + + +typedef struct FilterInfo_t { + char *mTag; + log_priority mPri; + struct FilterInfo_t *p_next; +} FilterInfo; + +struct log_format_t { + log_priority global_pri; + FilterInfo *filters; + log_print_format format; +}; + +static FilterInfo * filterinfo_new(const char *tag, log_priority pri) +{ + FilterInfo *p_ret; + p_ret = (FilterInfo *)calloc(1, sizeof(FilterInfo)); + p_ret->mTag = strdup(tag); + p_ret->mPri = pri; + + return p_ret; +} + +static void filterinfo_free(FilterInfo *p_info) +{ + if (p_info == NULL) { + return; + } + + free(p_info->mTag); + p_info->mTag = NULL; +} + +/* + * Note: also accepts 0-9 priorities + * returns DLOG_UNKNOWN if the character is unrecognized + */ +static log_priority filter_char_to_pri (char c) +{ + log_priority pri; + + c = tolower(c); + + if (c >= '0' && c <= '9') { + if (c >= ('0'+DLOG_SILENT)) { + pri = DLOG_VERBOSE; + } else { + pri = (log_priority)(c - '0'); + } + } else if (c == 'v') { + pri = DLOG_VERBOSE; + } else if (c == 'd') { + pri = DLOG_DEBUG; + } else if (c == 'i') { + pri = DLOG_INFO; + } else if (c == 'w') { + pri = DLOG_WARN; + } else if (c == 'e') { + pri = DLOG_ERROR; + } else if (c == 'f') { + pri = DLOG_FATAL; + } else if (c == 's') { + pri = DLOG_SILENT; + } else if (c == '*') { + pri = DLOG_DEFAULT; + } else { + pri = DLOG_UNKNOWN; + } + + return pri; +} + +static char filter_pri_to_char (log_priority pri) +{ + switch (pri) { + case DLOG_VERBOSE: + return 'V'; + case DLOG_DEBUG: + return 'D'; + case DLOG_INFO: + return 'I'; + case DLOG_WARN: + return 'W'; + case DLOG_ERROR: + return 'E'; + case DLOG_FATAL: + return 'F'; + case DLOG_SILENT: + return 'S'; + case DLOG_DEFAULT: + case DLOG_UNKNOWN: + default: + return '?'; + } +} + +static log_priority filter_pri_for_tag(log_format *p_format, const char *tag) +{ + FilterInfo *p_curFilter; + + for (p_curFilter = p_format->filters; p_curFilter != NULL; p_curFilter = p_curFilter->p_next ) + { + if (0 == strcmp(tag, p_curFilter->mTag)) + { + if (p_curFilter->mPri == DLOG_DEFAULT) { + return p_format->global_pri; + } else { + return p_curFilter->mPri; + } + } + } + return p_format->global_pri; +} + +/** for debugging */ +void dump_filters(log_format *p_format) +{ + FilterInfo *p_fi; + + for (p_fi = p_format->filters ; p_fi != NULL ; p_fi = p_fi->p_next) { + char cPri = filter_pri_to_char(p_fi->mPri); + if (p_fi->mPri == DLOG_DEFAULT) { + cPri = filter_pri_to_char(p_format->global_pri); + } + fprintf(stderr, "%s:%c\n", p_fi->mTag, cPri); + } + + fprintf(stderr, "*:%c\n", filter_pri_to_char(p_format->global_pri)); + +} + +/** + * returns 1 if this log line should be printed based on its priority + * and tag, and 0 if it should not + */ +int log_should_print_line (log_format *p_format, const char *tag, log_priority pri) +{ + return pri >= filter_pri_for_tag(p_format, tag); +} + +log_format *log_format_new() +{ + log_format *p_ret; + + p_ret = calloc(1, sizeof(log_format)); + + p_ret->global_pri = DLOG_SILENT; + p_ret->format = FORMAT_BRIEF; + + return p_ret; +} + +void log_format_free(log_format *p_format) +{ + FilterInfo *p_info, *p_info_old; + + p_info = p_format->filters; + + while (p_info != NULL) { + p_info_old = p_info; + p_info = p_info->p_next; + filterinfo_free(p_info_old); + } + + free(p_format); +} + +void log_set_print_format(log_format *p_format,log_print_format format) +{ + p_format->format=format; +} + +/** + * Returns FORMAT_OFF on invalid string + */ +log_print_format log_format_from_string(const char * formatString) +{ + static log_print_format format; + + if (strcmp(formatString, "brief") == 0) + format = FORMAT_BRIEF; + else if (strcmp(formatString, "process") == 0) + format = FORMAT_PROCESS; + else if (strcmp(formatString, "tag") == 0) + format = FORMAT_TAG; + else if (strcmp(formatString, "thread") == 0) + format = FORMAT_THREAD; + else if (strcmp(formatString, "raw") == 0) + format = FORMAT_RAW; + else if (strcmp(formatString, "time") == 0) + format = FORMAT_TIME; + else if (strcmp(formatString, "threadtime") == 0) + format = FORMAT_THREADTIME; + else if (strcmp(formatString, "long") == 0) + format = FORMAT_LONG; + else format = FORMAT_OFF; + + return format; +} + +/** + * filterExpression: a single filter expression + * eg "AT:d" + * + * returns 0 on success and -1 on invalid expression + * + * Assumes single threaded execution + */ +int log_add_filter_rule(log_format *p_format, + const char *filterExpression) +{ + size_t tagNameLength; + log_priority pri = DLOG_DEFAULT; + + tagNameLength = strcspn(filterExpression, ":"); + + if (tagNameLength == 0) { + goto error; + } + + if(filterExpression[tagNameLength] == ':') { + pri = filter_char_to_pri(filterExpression[tagNameLength+1]); + + if (pri == DLOG_UNKNOWN) { + goto error; + } + } + + if(0 == strncmp("*", filterExpression, tagNameLength)) { + /* This filter expression refers to the global filter + * The default level for this is DEBUG if the priority + * is unspecified + */ + if (pri == DLOG_DEFAULT) { + pri = DLOG_DEBUG; + } + + p_format->global_pri = pri; + } else { + /* for filter expressions that don't refer to the global + * filter, the default is verbose if the priority is unspecified + */ + if (pri == DLOG_DEFAULT) { + pri = DLOG_VERBOSE; + } + + char *tagName; + tagName = strndup(filterExpression, tagNameLength); + + FilterInfo *p_fi = filterinfo_new(tagName, pri); + free(tagName); + + p_fi->p_next = p_format->filters; + p_format->filters = p_fi; + } + + return 0; +error: + return -1; +} + +/** + * filterString: a comma/whitespace-separated set of filter expressions + * + * eg "AT:d *:i" + * + * returns 0 on success and -1 on invalid expression + * + * Assumes single threaded execution + * + */ +int log_add_filter_string(log_format *p_format, + const char *filterString) +{ + char *filterStringCopy = strdup (filterString); + char *p_cur = filterStringCopy; + char *p_ret; + int err; + + while (NULL != (p_ret = strsep(&p_cur, " \t,"))) { + /* ignore whitespace-only entries */ + if(p_ret[0] != '\0') { + err = log_add_filter_rule(p_format, p_ret); + + if (err < 0) { + goto error; + } + } + } + + free (filterStringCopy); + return 0; +error: + free (filterStringCopy); + return -1; +} + +static inline char * strip_end(char *str) +{ + char *end = str + strlen(str) - 1; + + while (end >= str && isspace(*end)) + *end-- = '\0'; + return str; +} + +/** + * Splits a wire-format buffer into an LogEntry + * entry allocated by caller. Pointers will point directly into buf + * + * Returns 0 on success and -1 on invalid wire format (entry will be + * in unspecified state) + */ +int log_process_log_buffer(struct logger_entry *buf,log_entry *entry) +{ + size_t tag_len; + + entry->tv_sec = buf->sec; + entry->tv_nsec = buf->nsec; + entry->pid = buf->pid; + entry->tid = buf->tid; + + if (buf->msg[0] < 0 || buf->msg[0] > DLOG_SILENT) { /* char can be signed too */ + + /* There is no tag in this message - which is an error, but it might + * happen when sombody redirects stdout/err to /dev/log_*. + * + * Pick ERROR priority as this shouldn't happen. + */ + entry->priority = DLOG_ERROR; + entry->tag = "[NO TAG]"; + entry->messageLen = buf->len; + entry->message = buf->msg; + } else { + entry->priority = buf->msg[0]; + entry->tag = buf->msg + 1; + tag_len = strlen(entry->tag); + entry->messageLen = buf->len - tag_len - 3; + entry->message = entry->tag + tag_len + 1; + } + + return 0; +} + +/** + * Formats a log message into a buffer + * + * Uses defaultBuffer if it can, otherwise malloc()'s a new buffer + * If return value != defaultBuffer, caller must call free() + * Returns NULL on malloc error + */ +char *log_format_log_line ( + log_format *p_format, + char *defaultBuffer, + size_t defaultBufferSize, + const log_entry *entry, + size_t *p_outLength) +{ +#if defined(HAVE_LOCALTIME_R) + struct tm tmBuf; +#endif + struct tm* ptm; + char timeBuf[32]; + char prefixBuf[128], suffixBuf[128]; + char priChar; + int prefixSuffixIsHeaderFooter = 0; + char * ret = NULL; + + priChar = filter_pri_to_char(entry->priority); + + /* + * Get the current date/time in pretty form + * + * It's often useful when examining a log with "less" to jump to + * a specific point in the file by searching for the date/time stamp. + * For this reason it's very annoying to have regexp meta characters + * in the time stamp. Don't use forward slashes, parenthesis, + * brackets, asterisks, or other special chars here. + */ +#if defined(HAVE_LOCALTIME_R) + ptm = localtime_r(&(entry->tv_sec), &tmBuf); +#else + ptm = localtime(&(entry->tv_sec)); +#endif + strftime(timeBuf, sizeof(timeBuf), "%m-%d %H:%M:%S", ptm); + + /* + * Construct a buffer containing the log header and log message. + */ + size_t prefixLen, suffixLen; + + switch (p_format->format) { + case FORMAT_TAG: + prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), + "%c/%-8s: ", priChar, entry->tag); + strcpy(suffixBuf, "\n"); suffixLen = 1; + break; + case FORMAT_PROCESS: + prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), + "%c(%5d) ", priChar, (int)entry->pid); + suffixLen = snprintf(suffixBuf, sizeof(suffixBuf), + " (%s)\n", entry->tag); + break; + case FORMAT_THREAD: + prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), + "%c(%5d:%5d) ", priChar, (int)entry->pid, (int)entry->tid); + strcpy(suffixBuf, "\n"); + suffixLen = 1; + break; + case FORMAT_RAW: + prefixBuf[0] = 0; + prefixLen = 0; + strcpy(suffixBuf, "\n"); + suffixLen = 1; + break; + case FORMAT_TIME: + prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), + "%s.%03ld %c/%-8s(%5d): ", timeBuf, entry->tv_nsec / 1000000, + priChar, entry->tag, (int)entry->pid); + strcpy(suffixBuf, "\n"); + suffixLen = 1; + break; + case FORMAT_THREADTIME: + prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), + "%s.%03ld %5d %5d %c %-8s: ", timeBuf, entry->tv_nsec / 1000000, + (int)entry->pid, (int)entry->tid, priChar, entry->tag); + strcpy(suffixBuf, "\n"); + suffixLen = 1; + break; + case FORMAT_LONG: + prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), + "[ %s.%03ld %5d:%5d %c/%-8s ]\n", + timeBuf, entry->tv_nsec / 1000000, (int)entry->pid, + (int)entry->tid, priChar, entry->tag); + strcpy(suffixBuf, "\n\n"); + suffixLen = 2; + prefixSuffixIsHeaderFooter = 1; + break; + case FORMAT_BRIEF: + default: + prefixLen = snprintf(prefixBuf, sizeof(prefixBuf), + "%c/%-8s(%5d): ", priChar, entry->tag, (int)entry->pid); + strcpy(suffixBuf, "\n"); + suffixLen = 1; + break; + } + /* snprintf has a weird return value. It returns what would have been + * written given a large enough buffer. In the case that the prefix is + * longer then our buffer(128), it messes up the calculations below + * possibly causing heap corruption. To avoid this we double check and + * set the length at the maximum (size minus null byte) + */ + if(prefixLen >= sizeof(prefixBuf)) + prefixLen = sizeof(prefixBuf) - 1; + if(suffixLen >= sizeof(suffixBuf)) + suffixLen = sizeof(suffixBuf) - 1; + + /* the following code is tragically unreadable */ + + size_t numLines; + char *p; + size_t bufferSize; + const char *pm; + + if (prefixSuffixIsHeaderFooter) { + /* we're just wrapping message with a header/footer */ + numLines = 1; + } else { + pm = entry->message; + numLines = 0; + + /* The line-end finding here must match the line-end finding + * in for ( ... numLines...) loop below + */ + while (pm < (entry->message + entry->messageLen)) { + if (*pm++ == '\n') numLines++; + } + /* plus one line for anything not newline-terminated at the end */ + if (pm > entry->message && *(pm-1) != '\n') numLines++; + } + + /* this is an upper bound--newlines in message may be counted + * extraneously + */ + bufferSize = (numLines * (prefixLen + suffixLen)) + entry->messageLen + 1; + + if (defaultBufferSize >= bufferSize) { + ret = defaultBuffer; + } else { + ret = (char *)malloc(bufferSize); + + if (ret == NULL) { + return ret; + } + } + + ret[0] = '\0'; /* to start strcat off */ + + p = ret; + pm = entry->message; + + if (prefixSuffixIsHeaderFooter) { + strcat(p, prefixBuf); + p += prefixLen; + strncat(p, entry->message, entry->messageLen); + p += entry->messageLen; + strcat(p, suffixBuf); + p += suffixLen; + } else { + while (pm < (entry->message + entry->messageLen)) { + const char *lineStart; + size_t lineLen; + + lineStart = pm; + + /* Find the next end-of-line in message */ + while (pm < (entry->message + entry->messageLen) + && *pm != '\n') pm++; + lineLen = pm - lineStart; + + strcat(p, prefixBuf); + p += prefixLen; + strncat(p, lineStart, lineLen); + p += lineLen; + strcat(p, suffixBuf); + p += suffixLen; + + if (*pm == '\n') + pm++; + } + } + + if (p_outLength != NULL) { + *p_outLength = p - ret; + } + + return ret; +} + +/** + * Either print or do not print log line, based on filter + * + * Returns count bytes written + */ + +int log_print_log_line( + log_format *p_format, + int fd, + const log_entry *entry) +{ + int ret; + char defaultBuffer[512]; + char *outBuffer = NULL; + size_t totalLen; + + outBuffer = log_format_log_line(p_format, + defaultBuffer, sizeof(defaultBuffer), entry, &totalLen); + + if (!outBuffer) + return -1; + + do { + ret = write(fd, outBuffer, totalLen); + } while (ret < 0 && errno == EINTR); + + if (ret < 0) { + fprintf(stderr, "+++ LOG: write failed (errno=%d)\n", errno); + ret = 0; + goto done; + } + + if (((size_t)ret) < totalLen) { + fprintf(stderr, "+++ LOG: write partial (%d of %d)\n", ret, + (int)totalLen); + goto done; + } + +done: + if (outBuffer != defaultBuffer) { + free(outBuffer); + } + + return ret; +} + + + +void logprint_run_tests() +{ + int err; + const char *tag; + log_format *p_format; + + p_format = log_format_new(); + + fprintf(stderr, "running tests\n"); + + tag = "random"; + + log_add_filter_rule(p_format,"*:i"); + + assert (DLOG_INFO == filter_pri_for_tag(p_format, "random")); + assert(log_should_print_line(p_format, tag, DLOG_DEBUG) == 0); + log_add_filter_rule(p_format, "*"); + assert (DLOG_DEBUG == filter_pri_for_tag(p_format, "random")); + assert(log_should_print_line(p_format, tag, DLOG_DEBUG) > 0); + log_add_filter_rule(p_format, "*:v"); + assert (DLOG_VERBOSE == filter_pri_for_tag(p_format, "random")); + assert(log_should_print_line(p_format, tag, DLOG_DEBUG) > 0); + log_add_filter_rule(p_format, "*:i"); + assert (DLOG_INFO == filter_pri_for_tag(p_format, "random")); + assert(log_should_print_line(p_format, tag, DLOG_DEBUG) == 0); + + log_add_filter_rule(p_format, "random"); + assert (DLOG_VERBOSE == filter_pri_for_tag(p_format, "random")); + assert(log_should_print_line(p_format, tag, DLOG_DEBUG) > 0); + log_add_filter_rule(p_format, "random:v"); + assert (DLOG_VERBOSE == filter_pri_for_tag(p_format, "random")); + assert(log_should_print_line(p_format, tag, DLOG_DEBUG) > 0); + log_add_filter_rule(p_format, "random:d"); + assert (DLOG_DEBUG == filter_pri_for_tag(p_format, "random")); + assert(log_should_print_line(p_format, tag, DLOG_DEBUG) > 0); + log_add_filter_rule(p_format, "random:w"); + assert (DLOG_WARN == filter_pri_for_tag(p_format, "random")); + assert(log_should_print_line(p_format, tag, DLOG_DEBUG) == 0); + + log_add_filter_rule(p_format, "crap:*"); + assert (DLOG_VERBOSE== filter_pri_for_tag(p_format, "crap")); + assert(log_should_print_line(p_format, "crap", DLOG_VERBOSE) > 0); + + /* invalid expression */ + err = log_add_filter_rule(p_format, "random:z"); + assert (err < 0); + assert (DLOG_WARN == filter_pri_for_tag(p_format, "random")); + assert(log_should_print_line(p_format, tag, DLOG_DEBUG) == 0); + + /* Issue #550946 */ + err = log_add_filter_string(p_format, " "); + assert(err == 0); + assert(DLOG_WARN == filter_pri_for_tag(p_format, "random")); + + /* note trailing space */ + err = log_add_filter_string(p_format, "*:s random:d "); + assert(err == 0); + assert(DLOG_DEBUG == filter_pri_for_tag(p_format, "random")); + + err = log_add_filter_string(p_format, "*:s random:z"); + assert(err < 0); + + log_format_free(p_format); + + fprintf(stderr, "tests complete\n"); +} + |