summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt11
-rwxr-xr-xLICENSE205
-rw-r--r--cmake/ProcessM4.cmake11
-rwxr-xr-xdump_scripts/module_log.sh19
-rwxr-xr-xdump_scripts/move_dump.sh12
-rwxr-xr-xdump_scripts/system_log.sh12
-rw-r--r--include/CMakeLists.txt4
-rw-r--r--include/defs.h.in8
-rw-r--r--packaging/log_dump.conf26
-rw-r--r--packaging/log_dump.manifest8
-rw-r--r--packaging/log_dump.spec87
-rw-r--r--packaging/log_dump_system-tests.manifest10
-rw-r--r--packaging/log_dump_system-tests.spec41
-rw-r--r--src/log_dump/CMakeLists.txt48
-rw-r--r--src/log_dump/dbus-handler.c238
-rw-r--r--src/log_dump/dbus-handler.h26
-rw-r--r--src/log_dump/log_dump.c315
-rw-r--r--src/log_dump/log_dump.conf3
-rw-r--r--src/log_dump/log_dump.h36
-rw-r--r--src/log_dump/log_dump.service.m411
-rw-r--r--src/log_dump/org.tizen.system.crash.service4
-rw-r--r--src/shared/config.c60
-rw-r--r--src/shared/config.h43
-rw-r--r--src/shared/log.h65
-rw-r--r--src/shared/spawn.c210
-rw-r--r--src/shared/spawn.h58
-rw-r--r--src/shared/util.c150
-rw-r--r--src/shared/util.h41
-rw-r--r--tests/system/CMakeLists.txt19
-rw-r--r--tests/system/log_dump_crash_root_path/log_dump_crash_root_path.sh.template32
-rw-r--r--tests/system/log_dump_normal/log_dump_normal.sh.template28
-rw-r--r--tests/system/log_dump_service/log_dump_service.sh.template33
-rw-r--r--tests/system/log_dump_short/log_dump_short.sh.template32
33 files changed, 1906 insertions, 0 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000..0a6a63d
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,11 @@
+CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
+PROJECT(log_dump C)
+
+SET(PREFIX ${CMAKE_INSTALL_PREFIX})
+
+ADD_DEFINITIONS(-D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE=1)
+
+ADD_SUBDIRECTORY(include)
+INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include)
+
+ADD_SUBDIRECTORY(src/log_dump)
diff --git a/LICENSE b/LICENSE
new file mode 100755
index 0000000..8aa906c
--- /dev/null
+++ b/LICENSE
@@ -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/cmake/ProcessM4.cmake b/cmake/ProcessM4.cmake
new file mode 100644
index 0000000..09ba1c9
--- /dev/null
+++ b/cmake/ProcessM4.cmake
@@ -0,0 +1,11 @@
+MACRO(PROCESS_M4 _defines _input _output)
+ GET_FILENAME_COMPONENT(_name ${_output} NAME)
+ ADD_CUSTOM_COMMAND(
+ OUTPUT ${_output}
+ COMMAND m4
+ ARGS -P ${_defines} ${_input} > ${_output}
+ DEPENDS ${_input}
+ VERBATIM)
+ ADD_CUSTOM_TARGET(M4_${_name} DEPENDS ${_output})
+ ADD_DEPENDENCIES(${PROJECT_NAME} M4_${_name})
+ENDMACRO(PROCESS_M4)
diff --git a/dump_scripts/module_log.sh b/dump_scripts/module_log.sh
new file mode 100755
index 0000000..4c73e33
--- /dev/null
+++ b/dump_scripts/module_log.sh
@@ -0,0 +1,19 @@
+#!/bin/sh
+#
+# Dump module log
+#
+PATH=/bin:/usr/bin:/sbin:/usr/sbin
+
+DUMP_DEST=$1/log/module_log
+DUMP_SCRIPT_DIR=/opt/etc/dump.d/module.d
+
+mkdir -p ${DUMP_DEST}
+
+if [ -d ${DUMP_SCRIPT_DIR} ]
+then
+ SCRIPTS=`/bin/ls ${DUMP_SCRIPT_DIR}`
+
+ for SCRIPT in ${SCRIPTS}; do
+ /bin/sh ${DUMP_SCRIPT_DIR}/${SCRIPT} ${DUMP_DEST}
+ done
+fi
diff --git a/dump_scripts/move_dump.sh b/dump_scripts/move_dump.sh
new file mode 100755
index 0000000..94d09c4
--- /dev/null
+++ b/dump_scripts/move_dump.sh
@@ -0,0 +1,12 @@
+#!/bin/sh
+
+PATH=/bin:/usr/bin:/sbin:/usr/sbin
+
+. /etc/tizen-platform.conf
+
+set -ex
+
+DEST="$1/dump"
+mkdir -p "$DEST"
+mv -f "${TZ_SYS_CRASH}"/* "$DEST"/
+
diff --git a/dump_scripts/system_log.sh b/dump_scripts/system_log.sh
new file mode 100755
index 0000000..36beeb1
--- /dev/null
+++ b/dump_scripts/system_log.sh
@@ -0,0 +1,12 @@
+#!/bin/sh
+#
+# Dump system log
+#
+PATH=/bin:/usr/bin:/sbin:/usr/sbin
+
+DUMP_DEST=$1/log/system_log
+
+mkdir -p ${DUMP_DEST}
+
+/bin/cp -fr /opt/var/log/* ${DUMP_DEST}
+/bin/cp -fr /run/systemd/journal ${DUMP_DEST}
diff --git a/include/CMakeLists.txt b/include/CMakeLists.txt
new file mode 100644
index 0000000..bc03a77
--- /dev/null
+++ b/include/CMakeLists.txt
@@ -0,0 +1,4 @@
+CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
+PROJECT(log_dump C)
+
+CONFIGURE_FILE(defs.h.in defs.h @ONLY)
diff --git a/include/defs.h.in b/include/defs.h.in
new file mode 100644
index 0000000..50f117e
--- /dev/null
+++ b/include/defs.h.in
@@ -0,0 +1,8 @@
+#ifndef __DEFS_H__
+#define __DEFS_H__
+
+#define CRASH_ROOT_PATH "@CRASH_ROOT_PATH@"
+#define LOG_DUMP_CONFIG_PATH "@LOG_DUMP_CONFIG_PATH@"
+#define DUMP_SYSTEMSTATE_BIN_PATH "@DUMP_SYSTEMSTATE_BIN_PATH@"
+
+#endif /* __DEFS_H__ */
diff --git a/packaging/log_dump.conf b/packaging/log_dump.conf
new file mode 100644
index 0000000..4c82d0e
--- /dev/null
+++ b/packaging/log_dump.conf
@@ -0,0 +1,26 @@
+<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-Bus Bus Configuration 1.0//EN"
+ "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
+<busconfig>
+ <policy user="root">
+ <allow own="org.tizen.system.crash"/>
+ <allow send_destination="org.tizen.system.crash"
+ send_interface="org.tizen.system.crash.Crash"
+ send_member="dump_log"/>
+ <allow send_destination="org.tizen.system.crash"
+ send_interface="org.tizen.system.crash.Crash"
+ send_member="delete_dump"/>
+ </policy>
+ <policy user="system">
+ <allow send_destination="org.tizen.system.crash"
+ send_interface="org.tizen.system.crash.Crash"
+ send_member="dump_log"/>
+ <allow send_destination="org.tizen.system.crash"
+ send_interface="org.tizen.system.crash.Crash"
+ send_member="delete_dump"/>
+ </policy>
+
+ <policy context="default">
+ <deny own="org.tizen.system.crash"/>
+ <deny send_destination="org.tizen.system.crash"/>
+ </policy>
+</busconfig>
diff --git a/packaging/log_dump.manifest b/packaging/log_dump.manifest
new file mode 100644
index 0000000..a340d2e
--- /dev/null
+++ b/packaging/log_dump.manifest
@@ -0,0 +1,8 @@
+<manifest>
+ <request>
+ <domain name="_"/>
+ </request>
+ <assign>
+ <filesystem path="/usr/bin/log_dump" label="System" exec_label="System"/>
+ </assign>
+</manifest>
diff --git a/packaging/log_dump.spec b/packaging/log_dump.spec
new file mode 100644
index 0000000..6b0ead0
--- /dev/null
+++ b/packaging/log_dump.spec
@@ -0,0 +1,87 @@
+# "on_off foo" wil turn into "ON" or "OFF"
+%define on_off() %{expand:%%{?with_%{1}:ON}%%{!?with_%{1}:OFF}}
+
+# NOTE: To disable coredump set DumpCore=0 in configuration file
+
+Name: log_dump
+Summary: log_dump
+Version: 5.5.23
+Release: 1
+Group: Framework/system
+License: Apache-2.0
+Source0: %{name}-%{version}.tar.gz
+Source1001: log_dump.manifest
+Source1002: packaging/log_dump.conf
+BuildRequires: pkgconfig(dlog)
+BuildRequires: pkgconfig(libtzplatform-config)
+BuildRequires: pkgconfig(iniparser)
+BuildRequires: pkgconfig(capi-system-info)
+BuildRequires: pkgconfig(glib-2.0)
+BuildRequires: cmake
+
+Requires: crash-worker >= 5.5.23
+Conflicts: crash-worker < 5.5.23
+
+%description
+log_dump
+
+%prep
+%setup -q
+
+#Path to store logs and coredump files
+%define crash_root_path %{TZ_SYS_CRASH_ROOT}
+
+#Path for log_dump module
+%define crash_all_log %{TZ_SYS_ALLLOGS}
+%define crash_dump_gen %{TZ_SYS_DUMPGEN}
+
+# %define upgrade_script_path %{TZ_SYS_RO_SHARE}/upgrade/scripts
+
+%build
+cp %{SOURCE1001} .
+export CFLAGS+=" -Werror"
+
+%cmake . \
+ -DCMAKE_INSTALL_PREFIX=%{_prefix} \
+ -DLOG_DUMP_CONFIG_PATH=%{_sysconfdir}/log_dump.conf \
+ -DDUMP_SYSTEMSTATE_BIN_PATH=%{_bindir}/dump_systemstate \
+ -DCRASH_ROOT_PATH=%{crash_root_path}
+
+make %{?jobs:-j%jobs}
+
+%install
+rm -rf %{buildroot}
+
+%make_install
+mkdir -p %{buildroot}%{crash_root_path}
+
+# log_dump dir
+mkdir -p %{buildroot}%{crash_all_log}
+mkdir -p %{buildroot}%{crash_dump_gen}
+cp dump_scripts/* %{buildroot}%{crash_dump_gen}
+install -D -m 0655 %SOURCE1002 %{buildroot}%{_sysconfdir}/dbus-1/system.d/log_dump.conf
+chmod 755 %{buildroot}%{crash_dump_gen}/*
+
+%post
+/usr/bin/chsmack -a "System" -t %{crash_dump_gen}
+/usr/bin/chsmack -a "System" -t %{crash_dump_gen}/module.d
+/usr/bin/chsmack -a "System::Shared" -t %{crash_all_log}
+/usr/bin/chsmack -a "_" %{crash_dump_gen}/module.d/*
+
+%files
+%license LICENSE
+%manifest log_dump.manifest
+%defattr(-,system_fw,system_fw,-)
+%dir %{crash_root_path}
+%{_sysconfdir}/log_dump.conf
+
+%dir %{crash_all_log}
+%{crash_dump_gen}/*
+%attr(-,root,root) %{_unitdir}/log_dump.service
+%attr(-,root,root) %{_sysconfdir}/dbus-1/system.d/log_dump.conf
+%attr(-,root,root) %{_datadir}/dbus-1/system-services/org.tizen.system.crash.service
+%attr(0750,system_fw,system_fw) %{_bindir}/log_dump
+%attr(-,root,root) %{_sysconfdir}/log_dump.conf
+
+#upgrade script
+# %attr(-,root,root) %{upgrade_script_path}/500.log_dump-upgrade.sh
diff --git a/packaging/log_dump_system-tests.manifest b/packaging/log_dump_system-tests.manifest
new file mode 100644
index 0000000..4bce03d
--- /dev/null
+++ b/packaging/log_dump_system-tests.manifest
@@ -0,0 +1,10 @@
+<manifest>
+ <request>
+ <domain name="_"/>
+ </request>
+ <assign>
+ <filesystem path="/usr/lib/crash-worker_system-tests/log_dump_crash_root_path/log_dump_crash_root_path.sh" label="User::Shell" exec_label="User::Shell"/>
+ <filesystem path="/usr/lib/crash-worker_system-tests/log_dump_normal/log_dump_normal.sh" label="User::Shell" exec_label="System"/>
+ <filesystem path="/usr/lib/crash-worker_system-tests/log_dump_short/log_dump_short.sh" label="User::Shell" exec_label="User::Shell"/>
+ </assign>
+</manifest>
diff --git a/packaging/log_dump_system-tests.spec b/packaging/log_dump_system-tests.spec
new file mode 100644
index 0000000..d072e49
--- /dev/null
+++ b/packaging/log_dump_system-tests.spec
@@ -0,0 +1,41 @@
+Name: log_dump_system-tests
+Summary: Package with scripts for log_dump system tests
+Version: 5.5.23
+Release: 1
+Group: Framework/system
+License: Apache-2.0
+Source0: %{name}-%{version}.tar.gz
+Source1001: log_dump_system-tests.manifest
+BuildRequires: cmake
+
+Requires: log_dump
+Requires: crash-worker_system-tests >= 5.5.23
+Conflicts: crash-worker_system-tests < 5.5.23
+
+%description
+This package contains installable tests in Bash.
+
+%prep
+%setup -q
+
+%build
+cp %{SOURCE1001} .
+
+cd tests/system
+%cmake . -DCRASH_SYSTEM_TESTS_PATH=%{_libdir}/crash-worker_system-tests
+
+make %{?jobs:-j%jobs}
+
+%install
+rm -rf %{buildroot}
+cd tests/system
+
+%make_install
+
+%files -f debugfiles.list
+%manifest %{name}.manifest
+%defattr(0750,system_fw,system_fw)
+%{_libdir}/crash-worker_system-tests/log_dump_crash_root_path/log_dump_crash_root_path.sh
+%{_libdir}/crash-worker_system-tests/log_dump_normal/log_dump_normal.sh
+%{_libdir}/crash-worker_system-tests/log_dump_short/log_dump_short.sh
+%defattr(-,root,root)
diff --git a/src/log_dump/CMakeLists.txt b/src/log_dump/CMakeLists.txt
new file mode 100644
index 0000000..021823f
--- /dev/null
+++ b/src/log_dump/CMakeLists.txt
@@ -0,0 +1,48 @@
+CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
+PROJECT(log_dump C)
+
+INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/src)
+SET(LOG_DUMP_SRCS
+ log_dump.c
+ dbus-handler.c
+ ${CMAKE_SOURCE_DIR}/src/shared/util.c
+ ${CMAKE_SOURCE_DIR}/src/shared/spawn.c
+ ${CMAKE_SOURCE_DIR}/src/shared/config.c
+ )
+
+INCLUDE(FindPkgConfig)
+pkg_check_modules(log_dump_pkgs REQUIRED
+ dlog
+ capi-system-info
+ libtzplatform-config
+ iniparser
+ gio-2.0
+ )
+
+INCLUDE(${CMAKE_SOURCE_DIR}/cmake/ProcessM4.cmake)
+
+FOREACH(flag ${log_dump_pkgs_CFLAGS})
+ SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} ${flag}")
+ENDFOREACH(flag)
+
+SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_CFLAGS} -fPIE")
+
+ADD_EXECUTABLE(${PROJECT_NAME} ${LOG_DUMP_SRCS})
+TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${log_dump_pkgs_LDFLAGS} -pie)
+
+PROCESS_M4("${M4_DEFINES}"
+ "${CMAKE_CURRENT_SOURCE_DIR}/log_dump.service.m4"
+ "${CMAKE_CURRENT_SOURCE_DIR}/log_dump.service")
+
+INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/org.tizen.system.crash.service
+ DESTINATION /usr/share/dbus-1/system-services)
+
+INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/log_dump.conf
+ DESTINATION /etc)
+
+INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/log_dump.service DESTINATION /usr/lib/systemd/system
+ PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ)
+
+INSTALL(TARGETS ${PROJECT_NAME} DESTINATION bin
+ PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE
+ GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
diff --git a/src/log_dump/dbus-handler.c b/src/log_dump/dbus-handler.c
new file mode 100644
index 0000000..2eecde6
--- /dev/null
+++ b/src/log_dump/dbus-handler.c
@@ -0,0 +1,238 @@
+/*
+ * log_dump
+ *
+ * Copyright (c) 2016 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 <stdbool.h>
+#include <gio/gio.h>
+#include "log_dump.h"
+#include "dbus-handler.h"
+
+/* Dbus activation */
+#define CRASH_BUS_NAME "org.tizen.system.crash"
+#define CRASH_OBJECT_PATH "/Org/Tizen/System/Crash/Crash"
+
+/* Log dump signal */
+#define LOG_DUMP_BUS_NAME "org.tizen.system.logdump"
+#define LOG_DUMP_OBJECT_PATH "/Org/Tizen/System/LogDump"
+#define LOG_DUMP_INTERFACE_NAME LOG_DUMP_BUS_NAME
+#define LOG_DUMP_START_SIGNAL "Start"
+#define LOG_DUMP_FINISH_SIGNAL "Finish"
+
+#define TIMEOUT_INTERVAL 30
+
+static GMainLoop *loop;
+static GMutex timeout_mutex;
+static guint timeout_id;
+static GDBusNodeInfo *introspection_data;
+static const gchar introspection_xml[] =
+"<node>"
+" <interface name='org.tizen.system.crash.Crash'>"
+" <method name='dump_log'>"
+" <arg type='s' name='arg' direction='in'/>"
+" <arg type='i' name='response' direction='out'/>"
+" </method>"
+" <method name='delete_dump'>"
+" <arg type='i' name='response' direction='out'/>"
+" </method>"
+" </interface>"
+"</node>";
+
+static int timeout_cb(gpointer data)
+{
+ _I("Time out!");
+ g_main_loop_quit((GMainLoop *)data);
+
+ return 0;
+}
+
+static void add_timeout(void)
+{
+ g_mutex_lock(&timeout_mutex);
+
+ if (timeout_id)
+ g_source_remove(timeout_id);
+ timeout_id = g_timeout_add_seconds(TIMEOUT_INTERVAL, timeout_cb, loop);
+
+ g_mutex_unlock(&timeout_mutex);
+ _I("Add loop timeout (%d)", TIMEOUT_INTERVAL);
+}
+
+static void remove_timeout(void)
+{
+ g_mutex_lock(&timeout_mutex);
+
+ if (timeout_id) {
+ g_source_remove(timeout_id);
+ timeout_id = 0;
+ }
+
+ g_mutex_unlock(&timeout_mutex);
+ _I("Remove loop timeout");
+}
+
+static void method_call_handler(GDBusConnection *conn,
+ const gchar *sender,
+ const gchar *object_path,
+ const gchar *iface_name,
+ const gchar *method_name,
+ GVariant *parameters,
+ GDBusMethodInvocation *invocation,
+ gpointer user_data)
+{
+ int ret = -1;
+ const gchar *arg;
+
+ remove_timeout();
+
+ if (g_strcmp0(method_name, "dump_log") == 0) {
+ g_variant_get(parameters, "(&s)", &arg);
+ if (g_strcmp0(arg, "normal") == 0)
+ ret = log_dump(OPT_NORMAL);
+ else if (g_strcmp0(arg, "short") == 0)
+ ret = log_dump(OPT_SHORT);
+ else
+ _E("Wrong option for log_dump");
+ } else if (g_strcmp0(method_name, "delete_dump") == 0) {
+ ret = delete_dump();
+ }
+
+ g_dbus_method_invocation_return_value(invocation,
+ g_variant_new("(i)", ret));
+
+ add_timeout();
+}
+
+static const GDBusInterfaceVTable interface_vtable = {
+ method_call_handler,
+ NULL,
+ NULL
+};
+
+static void on_bus_acquired(GDBusConnection *conn,
+ const gchar *name,
+ gpointer user_data)
+{
+ guint registration_id;
+
+ registration_id = g_dbus_connection_register_object(conn,
+ CRASH_OBJECT_PATH, introspection_data->interfaces[0],
+ &interface_vtable, NULL, NULL, NULL);
+ if (registration_id == 0)
+ _E("Failed to g_dbus_connection_register_object");
+}
+
+static void on_name_acquired(GDBusConnection *conn,
+ const gchar *name,
+ gpointer user_data)
+{
+ _D("Acquired the name %s on the system bus", name);
+}
+
+static void on_name_lost(GDBusConnection *conn,
+ const gchar *name,
+ gpointer user_data)
+{
+ _D("Lost the name %s on the system bus", name);
+}
+
+static void dbus_init(void)
+{
+ GDBusConnection *conn = NULL;
+ GError *error = NULL;
+
+ introspection_data =
+ g_dbus_node_info_new_for_xml(introspection_xml, NULL);
+ if (introspection_data == NULL) {
+ _E("Failed to init g_dbus_info_new_for_xml");
+ return;
+ }
+
+ conn = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &error);
+ if (!conn) {
+ _E("Failed to get dbus");
+ return;
+ }
+
+ if (error) {
+ _E("Failed to get dbus: %s", error->message);
+ g_error_free(error);
+ return;
+ }
+
+ g_bus_own_name(G_BUS_TYPE_SYSTEM, CRASH_BUS_NAME,
+ G_BUS_NAME_OWNER_FLAGS_NONE, on_bus_acquired,
+ on_name_acquired, on_name_lost, NULL, NULL);
+}
+
+int log_dump_dbus(void)
+{
+ loop = g_main_loop_new(NULL, false);
+
+ dbus_init();
+
+ g_mutex_init(&timeout_mutex);
+ add_timeout();
+
+ _I("log_dump_dbus activated");
+ g_main_loop_run(loop);
+
+ if (introspection_data)
+ g_dbus_node_info_unref(introspection_data);
+ g_mutex_clear(&timeout_mutex);
+ g_main_loop_unref(loop);
+
+ return 0;
+}
+
+static int broadcast_logdump(const char *signal)
+{
+ GDBusConnection *conn;
+ GError *error = NULL;
+
+ _I("broadcast signal: %s", signal);
+ conn = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &error);
+ if (error) {
+ _E("Failed to get dbus: %s", error->message);
+ g_error_free(error);
+ return -1;
+ }
+
+ g_dbus_connection_emit_signal(conn,
+ NULL,
+ LOG_DUMP_OBJECT_PATH,
+ LOG_DUMP_INTERFACE_NAME,
+ signal,
+ NULL,
+ &error);
+ if (error) {
+ _E("Failed to emit signal: %s", error->message);
+ g_error_free(error);
+ return -1;
+ }
+
+ return 0;
+}
+
+int broadcast_logdump_start(void)
+{
+ return broadcast_logdump(LOG_DUMP_START_SIGNAL);
+}
+
+int broadcast_logdump_finish(void)
+{
+ return broadcast_logdump(LOG_DUMP_FINISH_SIGNAL);
+}
diff --git a/src/log_dump/dbus-handler.h b/src/log_dump/dbus-handler.h
new file mode 100644
index 0000000..a936f31
--- /dev/null
+++ b/src/log_dump/dbus-handler.h
@@ -0,0 +1,26 @@
+/*
+ * log_dump
+ *
+ * Copyright (c) 2016 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 __LOGDUMP_DBUS_H__
+#define __LOGDUMP_DBUS_H__
+
+int log_dump_dbus(void);
+int broadcast_logdump_start(void);
+int broadcast_logdump_finish(void);
+
+#endif
diff --git a/src/log_dump/log_dump.c b/src/log_dump/log_dump.c
new file mode 100644
index 0000000..551900b
--- /dev/null
+++ b/src/log_dump/log_dump.c
@@ -0,0 +1,315 @@
+/*
+ * log_dump: dump current system states
+ *
+ * Copyright (c) 2016, 2018 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 <assert.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <libgen.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <system_info.h>
+#include <tzplatform_config.h>
+
+#include "defs.h"
+#include "dbus-handler.h"
+#include "log_dump.h"
+#include "shared/config.h"
+#include "shared/spawn.h"
+#include "shared/util.h"
+
+#define SYSTEM_INFO_KEY_BUILD_STRING "http://tizen.org/system/build.string"
+#define DIR_UMASK 0022
+
+static const struct option opts[] = {
+ { "normal", no_argument, 0, OPT_NORMAL },
+ { "short", no_argument, 0, OPT_SHORT },
+ { "dbus", no_argument, 0, OPT_DBUS },
+ { 0, 0, 0, 0 }
+};
+
+/* crash worker configuration - for admin-selectable CrashRootPath */
+config_t config;
+/* tzplaform vars */
+char *dir_scripts;
+/* dynamic vars */
+char *dir_dump;
+char *dir_log;
+char *dir_debug;
+char *dir_temp; // temp rootdir
+char *dir_temp_logdump; // rootdir for this log_dump invocation
+char *version_string;
+/* timestamp */
+const char timestamp_format[] = "%Y%m%d%H%M%S";
+char timestamp_string[20]; /* as per format above */
+
+static bool init_temp_dir(char *const temp_root, char **temp_dir)
+{
+ assert(temp_root);
+ assert(temp_dir);
+
+ char *template = NULL, *path = NULL;
+ if (asprintf(&template, "%s/log.XXXXXX", temp_root) > 0)
+ path = mkdtemp(template);
+
+ if (!path) {
+ _E("Unable to create temporary directory at mkdtemp(%s): %m", template);
+ free(template);
+ return false;
+ }
+
+ *temp_dir = path;
+ return true;
+}
+
+static char *crash_root_get(void)
+{
+ return config.crash_root_path;
+}
+
+static bool init_vars(const char *crash_root)
+{
+ if (!crash_root)
+ return false;
+
+ if (asprintf(&dir_log, "%s/log", crash_root) <= 0
+ || asprintf(&dir_debug, "%s/debug", crash_root) <= 0
+ || asprintf(&dir_dump, "%s/dump", crash_root) <= 0
+ || asprintf(&dir_temp, "%s/temp", crash_root) <= 0)
+ goto fail;
+
+ make_dir(crash_root, "temp", DIR_UMASK);
+ if (!init_temp_dir(dir_temp, &dir_temp_logdump))
+ goto fail;
+
+ make_dir(dir_temp_logdump, "log", DIR_UMASK);
+ make_dir(crash_root, "debug", DIR_UMASK);
+
+ _D("config: dir_log is %s", dir_log);
+ _D("config: dir_dump is %s", dir_dump);
+ _D("config: dir_debug is %s", dir_debug);
+ _D("config: dir_temp is %s", dir_temp);
+ _D("config: dir_temp_logdump is %s", dir_temp_logdump);
+
+ dir_scripts = strdup(tzplatform_getenv(TZ_SYS_DUMPGEN));
+ _D("config: dir_scripts is %s", dir_scripts);
+
+ if (system_info_get_platform_string(SYSTEM_INFO_KEY_BUILD_STRING, &version_string) != SYSTEM_INFO_ERROR_NONE) {
+ _W("Failed to system_info_get_platform_string for " SYSTEM_INFO_KEY_BUILD_STRING);
+ version_string = NULL;
+ }
+ _D("version_string is %s", version_string);
+
+ time_t cur_time;
+ struct tm loc_tm;
+ cur_time = time(NULL);
+ localtime_r(&cur_time, &loc_tm);
+ strftime(timestamp_string, sizeof(timestamp_string), timestamp_format, &loc_tm);
+ _D("timestamp_string is %s", timestamp_string);
+
+ assert(dir_log);
+ assert(dir_dump);
+ assert(dir_debug);
+ assert(dir_temp);
+ assert(dir_temp_logdump);
+ return true;
+
+fail:
+ free(dir_log);
+ free(dir_dump);
+ free(dir_debug);
+ free(dir_temp);
+ free(dir_temp_logdump);
+ return false;
+}
+
+static void usage(void)
+{
+ printf("Usage: log_dump {OPTION}\n"
+ "Options:\n"
+ " --normal dump all logs (uses scripts from %s)\n"
+ " --short dump_systemstate logs only\n"
+ " --dbus become dbus service\n",
+ dir_scripts);
+}
+
+static bool dump_scripts(char *const workdir, char *const scriptsdir)
+{
+ struct dirent **dir_list = NULL;
+ char command[PATH_MAX];
+ int script_num, i;
+
+ script_num = scandir(scriptsdir, &dir_list, NULL, NULL);
+ if (script_num < 0) {
+ _E("scandir %s: %m", scriptsdir);
+ return false;
+ }
+
+ for (i = 0; i < script_num; i++) {
+ const char *const name = dir_list[i]->d_name;
+ int type = dir_list[i]->d_type;
+
+ if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0)
+ continue;
+
+ if (type != DT_REG) {
+ _D("Ignoring: not a regular file: %s", name);
+ continue;
+ }
+ snprintf(command, sizeof(command), "%s/%s", scriptsdir, name);
+ if (access(command, X_OK) != 0) {
+ _W("Ignoring: file not executable: %s", command);
+ continue;
+ }
+
+ _D("Calling scriptlet: %s", command);
+
+ char *const av[] = {command, workdir, NULL};
+ (void)spawn_wait(av, NULL, NULL, 0, NULL);
+ }
+
+ for (i = 0; i < script_num; i++)
+ free(dir_list[i]);
+ free(dir_list);
+
+ return true;
+}
+
+static bool dump_systemstate(const char *const destdir, const char *const timestr, int *exit_code)
+{
+ char *dump_path = NULL;
+
+ if (asprintf(&dump_path, "%s/log/dump_systemstate_%s.log", destdir, timestr) < 0) {
+ _E("asprintf: %m");
+ return false;
+ }
+
+ char *av[] = {DUMP_SYSTEMSTATE_BIN_PATH, "-k", "-d", "-j", "-e", "-f", dump_path, NULL};
+ spawn_param_s param = { .fn = spawn_setstdout, .u.int_val = STDERR_FILENO };
+ bool is_ok = spawn_wait(av, NULL, &param, 0, exit_code);
+
+ free(dump_path);
+
+ return is_ok;
+}
+
+static bool compress(char *const destdir, char *const tempdir, char *const versionstr, char *const timestr, int *exit_code)
+{
+ char *archive_path = NULL;
+
+ if (asprintf(&archive_path, "%s/log_dump_%s%s.zip", destdir, versionstr ?: "", timestr) < 0) {
+ _E("asprintf: %m");
+ return false;
+ }
+
+ _D("compress tempdir is %s", tempdir);
+ char *av[] = {"/bin/zip", "-qyr", archive_path, ".", NULL};
+ spawn_param_s param1 = { .fn = spawn_nullstdfds };
+ spawn_param_s param0 = { .fn = spawn_chdir, .u.char_ptr = tempdir, .next = &param1 };
+ bool is_ok = spawn_wait(av, NULL, &param0, 0, exit_code);
+
+ _I("Storing report at %s", archive_path);
+
+ fsync_path(archive_path);
+ free(archive_path);
+
+ return is_ok;
+}
+
+int log_dump(int option)
+{
+ broadcast_logdump_start();
+
+ int ret = -1;
+
+ if (!dump_systemstate(dir_temp_logdump, timestamp_string, NULL))
+ goto out;
+
+ if (option == OPT_NORMAL)
+ (void)dump_scripts(dir_temp_logdump, dir_scripts);
+
+ compress(dir_debug, dir_temp_logdump, version_string, timestamp_string, NULL);
+
+ /* cleanup */
+ ret = remove_dir(dir_temp_logdump, 1);
+ if (ret < 0)
+ _W("Failed to delete dump directory at %s", dir_temp_logdump);
+
+ ret = 0;
+out:
+ broadcast_logdump_finish();
+
+ return ret;
+}
+
+int delete_dump(void)
+{
+ _D("delete_dump called");
+
+ remove_dir(dir_log, 0);
+ remove_dir(dir_debug, 1);
+ remove_dir(dir_dump, 0);
+ remove_dir(dir_temp, 0);
+
+ return 0;
+}
+
+static void die(void)
+{
+ usage();
+ exit(EXIT_FAILURE);
+}
+
+int main(int argc, char *argv[])
+{
+ int c, ret;
+ int option;
+
+ if (!config_init(&config, LOG_DUMP_CONFIG_PATH))
+ return false;
+
+ /* need to do this first, because even usage() uses the vars */
+ if (!init_vars(crash_root_get())) {
+ printf("Unable to initialize - please check program logs");
+ exit(EXIT_FAILURE);
+ }
+
+ if (argc < 2)
+ die();
+
+ option = -1;
+ while ((c = getopt_long_only(argc, argv, "", opts, NULL)) != -1) {
+ if (option >= 0 || c < _OPT_MIN || c > _OPT_MAX)
+ die();
+ option = c;
+ }
+
+ if (option == OPT_DBUS)
+ ret = log_dump_dbus();
+ else
+ ret = log_dump(option);
+
+ config_free(&config);
+
+ return ret < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+}
diff --git a/src/log_dump/log_dump.conf b/src/log_dump/log_dump.conf
new file mode 100644
index 0000000..e30e366
--- /dev/null
+++ b/src/log_dump/log_dump.conf
@@ -0,0 +1,3 @@
+[LogDump]
+# CrashRootPath=/usr/opt/share/crash/
+
diff --git a/src/log_dump/log_dump.h b/src/log_dump/log_dump.h
new file mode 100644
index 0000000..a6c87c7
--- /dev/null
+++ b/src/log_dump/log_dump.h
@@ -0,0 +1,36 @@
+/*
+ * log_dump
+ *
+ * Copyright (c) 2016, 2018 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 __LOGDUMP_H__
+#define __LOGDUMP_H__
+
+#define LOG_TAG "LOG_DUMP"
+#include "shared/log.h"
+
+enum {
+ _OPT_MIN,
+ OPT_NORMAL = _OPT_MIN,
+ OPT_SHORT,
+ OPT_DBUS,
+ _OPT_MAX = OPT_DBUS,
+};
+
+int log_dump(int option);
+int delete_dump(void);
+
+#endif
diff --git a/src/log_dump/log_dump.service.m4 b/src/log_dump/log_dump.service.m4
new file mode 100644
index 0000000..be46f63
--- /dev/null
+++ b/src/log_dump/log_dump.service.m4
@@ -0,0 +1,11 @@
+[Unit]
+Description=log_dump service
+
+[Service]
+Type=dbus
+BusName=org.tizen.system.crash
+ExecStart=/usr/bin/log_dump --dbus
+SmackProcessLabel=System
+SupplementaryGroups=log systemd-journal
+Nice=-5
+KillMode=process
diff --git a/src/log_dump/org.tizen.system.crash.service b/src/log_dump/org.tizen.system.crash.service
new file mode 100644
index 0000000..5dfa56b
--- /dev/null
+++ b/src/log_dump/org.tizen.system.crash.service
@@ -0,0 +1,4 @@
+[D-BUS Service]
+Name=org.tizen.system.crash
+Exec=/bin/false
+SystemdService=log_dump.service
diff --git a/src/shared/config.c b/src/shared/config.c
new file mode 100644
index 0000000..56f930a
--- /dev/null
+++ b/src/shared/config.c
@@ -0,0 +1,60 @@
+/*
+ * crash-manager
+ * Copyright (c) 2019 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 <assert.h>
+#include <iniparser.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "config.h"
+#include "defs.h"
+#include "log.h"
+#include "util.h"
+
+bool config_init(config_t *c, const char *const path)
+{
+ assert(c);
+ assert(path);
+
+ dictionary *ini = iniparser_load(path);
+ if (!ini) {
+ _E("Failed to load config file %s", path);
+ return false;
+ }
+
+ bool ret = false;
+
+#define GET(type, key, defval) iniparser_get##type(ini, LOG_DUMP_SECTION ":" key, defval)
+
+ c->crash_root_path = strdup(GET(string, "CrashRootPath", CRASH_ROOT_PATH));
+ if (!c->crash_root_path)
+ goto out;
+
+#undef GET
+
+ ret = true;
+out:
+ iniparser_freedict(ini);
+ return ret;
+}
+
+void config_free(config_t *c)
+{
+ assert(c);
+
+ free(c->crash_root_path);
+}
diff --git a/src/shared/config.h b/src/shared/config.h
new file mode 100644
index 0000000..db606fc
--- /dev/null
+++ b/src/shared/config.h
@@ -0,0 +1,43 @@
+/*
+ * crash-manager
+ * Copyright (c) 2019 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 __DEF_CONFIG_H_
+#define __DEF_CONFIG_H_
+
+#include <stdbool.h>
+
+#define LOG_DUMP_SECTION "LogDump"
+
+typedef struct config {
+ char *crash_root_path;
+} config_t;
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+bool config_init(config_t *c, const char *const path);
+void config_free(config_t *c);
+
+#ifdef __cplusplus
+}
+#endif
+
+/**
+ * @}
+ */
+#endif
diff --git a/src/shared/log.h b/src/shared/log.h
new file mode 100644
index 0000000..bee783b
--- /dev/null
+++ b/src/shared/log.h
@@ -0,0 +1,65 @@
+/*
+ * crash-manager
+ * 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 __CRASH_LOG_H__
+#define __CRASH_LOG_H__
+
+#ifndef LOG_TAG
+#define LOG_TAG "CRASH_MANAGER"
+#endif
+#include <dlog.h>
+
+#ifdef LOG_FILE
+#include <sys/types.h>
+#include <unistd.h>
+#undef LOG_
+static inline char __dlog_prio(int prio)
+{
+ static const char pri_table[DLOG_PRIO_MAX] = {
+ [DLOG_UNKNOWN] = '?',
+ [DLOG_DEFAULT] = '?',
+ [DLOG_VERBOSE] = 'V',
+ [DLOG_DEBUG] = 'D',
+ [DLOG_INFO] = 'I',
+ [DLOG_WARN] = 'W',
+ [DLOG_ERROR] = 'E',
+ [DLOG_FATAL] = 'F',
+ [DLOG_SILENT] = 'S',
+ };
+ return pri_table[(prio >= 0 && prio < DLOG_PRIO_MAX) ? prio : DLOG_UNKNOWN];
+}
+#define LOG_(id, prio, tag, fmt, arg...) \
+ ({ do { \
+ fprintf(LOG_FILE, "%c/%-8s(%5d): %s: %s(%d) > " fmt "\n", \
+ __dlog_prio(prio), tag, getpid(), \
+ __MODULE__, __func__, __LINE__, ##arg); \
+ } while (0); })
+#endif
+
+#define _D(fmt, arg...) SLOGD(fmt, ##arg)
+#define _I(fmt, arg...) SLOGI(fmt, ##arg)
+#define _W(fmt, arg...) SLOGW(fmt, ##arg)
+#define _E(fmt, arg...) SLOGE(fmt, ##arg)
+#define _SD(fmt, arg...) SECURE_SLOGD(fmt, ##arg)
+#define _SI(fmt, arg...) SECURE_SLOGI(fmt, ##arg)
+#define _SW(fmt, arg...) SECURE_SLOGW(fmt, ##arg)
+#define _SE(fmt, arg...) SECURE_SLOGE(fmt, ##arg)
+
+#endif
+/* __CRASH_LOG_H__ */
diff --git a/src/shared/spawn.c b/src/shared/spawn.c
new file mode 100644
index 0000000..392a053
--- /dev/null
+++ b/src/shared/spawn.c
@@ -0,0 +1,210 @@
+/* Utilities for spawning sub-processes
+ *
+ * Copyright (c) 2018-2019 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 <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <poll.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <wait.h>
+
+#include "log.h"
+#include "util.h"
+#include "spawn.h"
+
+/* spawn prepare function(s) - to be called in child process */
+
+int spawn_setstdout(spawn_param_s *param)
+{
+ assert(param);
+ return dup2(param->u.int_val, STDOUT_FILENO) < 0 ? -1 : 0;
+}
+
+int spawn_setstderr(spawn_param_s *param)
+{
+ assert(param);
+ return dup2(param->u.int_val, STDERR_FILENO) < 0 ? -1 : 0;
+}
+
+int spawn_nullstdfds(spawn_param_s *param)
+{
+ int fd = open("/dev/null", O_RDWR);
+ if (fd < 0)
+ return -1;
+
+ int ret = dup2(fd, STDIN_FILENO) < 0 || dup2(fd, STDOUT_FILENO) < 0 || dup2(fd, STDERR_FILENO) < 0 ? -1 : 0;
+ if (fd != STDIN_FILENO && fd != STDOUT_FILENO && fd != STDERR_FILENO)
+ close(fd);
+ return ret;
+}
+
+int spawn_chdir(spawn_param_s *param)
+{
+ assert(param);
+ return chdir(param->u.char_ptr);
+}
+
+int spawn_umask(spawn_param_s *param)
+{
+ assert(param);
+ (void)umask(param->u.mode_t_val);
+ return 0;
+}
+
+/* spawn api */
+
+bool wait_for_pid(pid_t pid, int *exit_code)
+{
+ int status = 0;
+ int r = 0;
+ bool is_ok;
+
+ if (pid < 0)
+ return false;
+
+ do {
+ r = waitpid(pid, &status, 0);
+ is_ok = r >= 0;
+ } while (!is_ok && errno == EINTR);
+
+ if (!is_ok) {
+ _E("Error while waiting for child %d status: %m", (int)pid);
+ return false;
+ }
+
+ /* child has terminated */
+ if (WIFEXITED(status))
+ r = WEXITSTATUS(status);
+ else if (WIFSIGNALED(status))
+ r = WTERMSIG(status);
+ else if (WIFSTOPPED(status))
+ r = WSTOPSIG(status);
+
+ _D("Child with pid %d terminated with exit code %d", (int)pid, r);
+ if (exit_code)
+ *exit_code = r;
+
+ return true;
+}
+
+static int spawn_child(char *const av[], char *const ev[], spawn_param_s *param)
+{
+ static const int spawn_error = 127;
+
+ while (param) {
+ assert(param->fn);
+
+ int r = param->fn(param);
+ if (r < 0)
+ return spawn_error;
+
+ param = param->next;
+ };
+
+ execve(av[0], av, ev);
+ return spawn_error;
+}
+
+bool spawn(char *const av[], char *const ev[], spawn_param_s *param, pid_t *childpid, int *childfd)
+{
+ int pipefd[2];
+ if (pipe(pipefd) < 0) {
+ _E("pipe: %m");
+ return false;
+ }
+
+ pid_t pid = fork();
+ if (pid < 0) {
+ _E("fork: %m");
+ close(pipefd[0]);
+ close(pipefd[1]);
+ return false;
+ } else if (pid == 0) {
+ close(pipefd[0]);
+ _exit(spawn_child(av, ev, param));
+ }
+ close(pipefd[1]);
+
+ char *cmd = concatenate(av);
+ char *env = concatenate(ev);
+ _D("Spawned child with pid %d to execute command <%s> with environment <%s>", (int)pid, cmd, env);
+ free(cmd);
+ free(env);
+
+ // parent
+ if (childpid)
+ *childpid = pid;
+
+ if (childfd) {
+ _D("Leaving childfd status descriptor %d open for child status notification", pipefd[0]);
+ *childfd = pipefd[0];
+ } else
+ close(pipefd[0]);
+
+ return true;
+}
+
+/* returns true if child terminated */
+static bool wait_and_kill(pid_t childpid, int childfd, int timeout_ms)
+{
+ struct pollfd pfd = { .fd = childfd, .events = POLLIN | POLLERR | POLLHUP };
+ int r;
+
+ _D("Beginning to wait %dms for child pid %d", timeout_ms, (int)childpid);
+ do {
+ r = poll(&pfd, 1, timeout_ms);
+ } while (r < 0 && errno == EINTR);
+
+ if (r < 0) {
+ _E("Internal error while trying to wait for child pid %d: %m", (int)childpid);
+ return false;
+ } else if (r == 0) {
+ _W("Timeout %dms for child pid %d expired. Killing.", timeout_ms, (int)childpid);
+ kill(childpid, SIGKILL);
+ } else
+ _D("Child pid %d terminated before timeout.", (int)childpid);
+
+ return true;
+}
+
+bool spawn_wait(char *const av[], char *const ev[], spawn_param_s *param, int timeout_ms, int *exit_code)
+{
+ pid_t childpid;
+ int childfd;
+
+ if (!spawn(av, ev, param, &childpid, &childfd)) {
+ _E("spawn() returned an error - aborting waiting");
+ return false;
+ }
+
+ if (timeout_ms > 0)
+ (void)wait_and_kill(childpid, childfd, timeout_ms);
+
+ bool wait_ok = wait_for_pid(childpid, exit_code);
+
+ close(childfd);
+
+ return wait_ok;
+}
diff --git a/src/shared/spawn.h b/src/shared/spawn.h
new file mode 100644
index 0000000..431b173
--- /dev/null
+++ b/src/shared/spawn.h
@@ -0,0 +1,58 @@
+/* Utilities for spawning sub-processes
+ *
+ * Copyright (c) 2018-2019 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 __SPAWN_H__
+#define __SPAWN_H__
+
+#include <stdbool.h>
+#include <sys/types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define DEFAULT_COMMAND_TIMEOUT_MS (60*1000) /* 60sec */
+
+struct spawn_param;
+
+typedef int (*spawn_prepare_fn)(struct spawn_param *param);
+
+typedef struct spawn_param {
+ spawn_prepare_fn fn;
+
+ struct spawn_param *next;
+ union {
+ int int_val;
+ mode_t mode_t_val;
+ char *char_ptr;
+ } u;
+} spawn_param_s;
+
+int spawn_setstdout(spawn_param_s *param);
+int spawn_setstderr(spawn_param_s *param);
+int spawn_nullstdfds(spawn_param_s *param);
+int spawn_chdir(spawn_param_s *param);
+int spawn_umask(spawn_param_s *param);
+
+bool wait_for_pid(pid_t pid, int *exit_code);
+bool spawn(char *const av[], char *const ev[], spawn_param_s *param, pid_t *childpid, int *childfd);
+bool spawn_wait(char *const av[], char *const ev[], spawn_param_s *param, int timeout_ms, int *exit_code);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/shared/util.c b/src/shared/util.c
new file mode 100644
index 0000000..b149009
--- /dev/null
+++ b/src/shared/util.c
@@ -0,0 +1,150 @@
+/*
+ * crash-manager
+ * Copyright (c) 2012-2019 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 <sys/types.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "util.h"
+#include "log.h"
+
+int fsync_path(char *const path)
+{
+ int fd, ret;
+
+ ret = fd = open(path, O_RDONLY);
+ if (fd >= 0) {
+ ret = fsync(fd);
+ close(fd);
+ }
+
+ if (ret < 0)
+ _E("Unable to fsync %s: %m", path);
+
+ return ret;
+}
+
+int make_dir(const char *path, const char *name, int mode)
+{
+ int r = -1;
+
+ DIR *dir = opendir(path);
+ if (dir) {
+ int dfd = dirfd(dir);
+ r = mkdirat(dfd, name, mode);
+ closedir(dir);
+ }
+
+ return r == 0 || (r == -1 && errno == EEXIST) ? 0 : -1;
+}
+
+static int remove_dir_internal(int fd)
+{
+ DIR *dir;
+ struct dirent *de;
+ int subfd, ret = 0;
+
+ dir = fdopendir(fd);
+ if (!dir)
+ return -1;
+
+ while ((de = readdir(dir))) {
+ if (de->d_type == DT_DIR) {
+ if (!strncmp(de->d_name, ".", 2) || !strncmp(de->d_name, "..", 3))
+ continue;
+ subfd = openat(fd, de->d_name, O_RDONLY | O_DIRECTORY);
+ if (subfd < 0) {
+ _SE("Couldn't openat %s: %d\n", de->d_name, errno);
+ ret = -1;
+ continue;
+ }
+ if (remove_dir_internal(subfd))
+ ret = -1;
+ close(subfd);
+ if (unlinkat(fd, de->d_name, AT_REMOVEDIR) < 0) {
+ _SE("Couldn't unlinkat %s: %d\n", de->d_name, errno);
+ ret = -1;
+ }
+ } else {
+ if (unlinkat(fd, de->d_name, 0) < 0) {
+ _SE("Couldn't unlinkat %s: %d\n", de->d_name, errno);
+ ret = -1;
+ }
+ }
+ }
+ closedir(dir);
+ return ret;
+}
+
+int remove_dir(const char *path, int del_dir)
+{
+ int fd, ret = 0;
+
+ if (!path)
+ return -1;
+ fd = open(path, O_RDONLY | O_NONBLOCK | O_DIRECTORY | O_CLOEXEC | O_NOFOLLOW);
+ if (fd < 0) {
+ _SE("Couldn't opendir %s: %d\n", path, errno);
+ return -errno;
+ }
+ ret = remove_dir_internal(fd);
+ close(fd);
+
+ if (del_dir) {
+ if (rmdir(path)) {
+ _SE("Couldn't rmdir %s: %d\n", path, errno);
+ ret = -1;
+ }
+ }
+ return ret;
+}
+
+/* This function is supposed to accept same data as passed to execve
+ * (argv and envp), which can be arrays of strings as well as NULL
+ * pointer.
+ */
+char* concatenate(char *const vec[])
+{
+ size_t length = 0;
+ for (char *const *p = vec; p && *p; p++)
+ length += strlen(*p) + 1;
+
+ if (length == 0)
+ return strdup("");
+
+ char *str = (char *)malloc(length);
+ if (!str)
+ return NULL;
+
+ char *destp = str;
+ char *const *vecp = vec;
+ while (*vecp) {
+ destp = stpcpy(destp, *(vecp++));
+ if (*vecp)
+ destp = stpcpy(destp, " ");
+ }
+
+ return str;
+}
+
+/**
+ * @}
+ */
diff --git a/src/shared/util.h b/src/shared/util.h
new file mode 100644
index 0000000..3b2d643
--- /dev/null
+++ b/src/shared/util.h
@@ -0,0 +1,41 @@
+/*
+ * crash-manager
+ * 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 __DEF_UTIL_H__
+#define __DEF_UTIL_H__
+
+#define ARRAY_SIZE(name) (sizeof(name)/sizeof(name[0]))
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int fsync_path(char *const path);
+
+int make_dir(const char *path, const char *name, int mode);
+
+int remove_dir(const char *path, int del_dir);
+
+char* concatenate(char *const vec[]);
+
+#ifdef __cplusplus
+}
+#endif
+
+/**
+ * @}
+ */
+#endif
diff --git a/tests/system/CMakeLists.txt b/tests/system/CMakeLists.txt
new file mode 100644
index 0000000..c73b933
--- /dev/null
+++ b/tests/system/CMakeLists.txt
@@ -0,0 +1,19 @@
+cmake_minimum_required(VERSION 2.6)
+
+macro(CONFIGURE_TEST_FILE dir_name file_name)
+ configure_file("${dir_name}/${file_name}.sh.template" "${dir_name}/${file_name}.sh" @ONLY)
+ INSTALL(DIRECTORY ${dir_name}/ DESTINATION ${CRASH_SYSTEM_TESTS_PATH}/${dir_name} FILES_MATCHING PATTERN "*sh")
+endmacro()
+
+macro(CONFIGURE_TEST test_name)
+ set(FILES_LIST ${test_name})
+ list(APPEND FILES_LIST ${ARGN})
+
+ foreach(test ${FILES_LIST})
+ configure_test_file(${test_name} ${test})
+ endforeach()
+endmacro()
+
+configure_test("log_dump_short")
+configure_test("log_dump_normal")
+configure_test("log_dump_crash_root_path")
diff --git a/tests/system/log_dump_crash_root_path/log_dump_crash_root_path.sh.template b/tests/system/log_dump_crash_root_path/log_dump_crash_root_path.sh.template
new file mode 100644
index 0000000..8c3604b
--- /dev/null
+++ b/tests/system/log_dump_crash_root_path/log_dump_crash_root_path.sh.template
@@ -0,0 +1,32 @@
+#!/bin/bash
+
+# Custom report path test
+
+if [ -z "${CRASH_WORKER_SYSTEM_TESTS}" ]; then
+ CRASH_WORKER_SYSTEM_TESTS="@CRASH_SYSTEM_TESTS_PATH@"
+fi
+
+. ${CRASH_WORKER_SYSTEM_TESTS}/utils/minicore-utils.sh
+
+tmpdir=$(mktemp -d /tmp/logdump-crash-root-path-test.XXXXXX)
+
+LOG_DUMP_CONF=/etc/log_dump.conf
+mount -o rw,remount /
+backup_file ${LOG_DUMP_CONF}
+sed -ie 's,^CrashRootPath=.*,,g' ${LOG_DUMP_CONF}
+echo "CrashRootPath=$tmpdir" >> ${LOG_DUMP_CONF}
+
+log_dump --short
+
+restore_file ${LOG_DUMP_CONF}
+
+logfile="${tmpdir}"/debug/* # there shall be only one file
+
+check_file_exists "$logfile"
+
+num=`unzip -qql "$logfile" | wc -l`
+if [ $num -ne 2 ]; then
+ fail "'log_dump --short' report contains $num files - 2 expected"
+fi
+
+exit_ok
diff --git a/tests/system/log_dump_normal/log_dump_normal.sh.template b/tests/system/log_dump_normal/log_dump_normal.sh.template
new file mode 100644
index 0000000..ef70cae
--- /dev/null
+++ b/tests/system/log_dump_normal/log_dump_normal.sh.template
@@ -0,0 +1,28 @@
+#!/bin/bash
+
+# Custom report path test
+
+if [ -z "${CRASH_WORKER_SYSTEM_TESTS}" ]; then
+ CRASH_WORKER_SYSTEM_TESTS="@CRASH_SYSTEM_TESTS_PATH@"
+fi
+
+. ${CRASH_WORKER_SYSTEM_TESTS}/utils/minicore-utils.sh
+
+clean_logdump
+dummy=$(mktemp $CRASH_DUMP_PATH/logdump-normal-test.XXXXXX)
+
+log_dump --normal
+logfile="${LOGDUMP_RESULT_PATH}"/* # there shall be only one file
+
+check_file_exists "$logfile"
+check_zip_contains "$logfile" 'log/dump_systemstate.*log$'
+check_zip_contains "$logfile" 'log/system_log/$'
+check_zip_contains "$logfile" 'log/system_log/dlog/kernel$'
+check_zip_contains "$logfile" 'log/module_log/$'
+check_zip_contains "$logfile" 'dump/$'
+
+check_file_not_exists "$dummy"
+
+clean_logdump
+
+exit_ok
diff --git a/tests/system/log_dump_service/log_dump_service.sh.template b/tests/system/log_dump_service/log_dump_service.sh.template
new file mode 100644
index 0000000..13252f0
--- /dev/null
+++ b/tests/system/log_dump_service/log_dump_service.sh.template
@@ -0,0 +1,33 @@
+#!/bin/bash
+
+# log_dump service test
+
+if [ -z "${CRASH_WORKER_SYSTEM_TESTS}" ]; then
+ CRASH_WORKER_SYSTEM_TESTS="@CRASH_SYSTEM_TESTS_PATH@"
+fi
+
+. ${CRASH_WORKER_SYSTEM_TESTS}/utils/minicore-utils.sh
+
+clean_logdump
+
+dummy=$(mktemp $CRASH_DUMP_PATH/logdump-short-test.XXXXXX)
+
+log_dump --short
+logfile="${LOGDUMP_RESULT_PATH}"/* # there shall be only one file
+
+check_file_exists "$logfile"
+
+num=`unzip -qql "$logfile" | wc -l`
+if [ $num -ne 2 ]; then
+ fail "'log_dump --short' report contains $num files - 2 expected"
+fi
+
+check_zip_contains "$logfile" 'log/dump_systemstate.*log$'
+check_zip_contains "$logfile" 'log/$'
+
+check_file_exists "$dummy"
+
+clean_logdump
+
+exit_ok
+
diff --git a/tests/system/log_dump_short/log_dump_short.sh.template b/tests/system/log_dump_short/log_dump_short.sh.template
new file mode 100644
index 0000000..38d7885
--- /dev/null
+++ b/tests/system/log_dump_short/log_dump_short.sh.template
@@ -0,0 +1,32 @@
+#!/bin/bash
+
+# Custom report path test
+
+if [ -z "${CRASH_WORKER_SYSTEM_TESTS}" ]; then
+ CRASH_WORKER_SYSTEM_TESTS="@CRASH_SYSTEM_TESTS_PATH@"
+fi
+
+. ${CRASH_WORKER_SYSTEM_TESTS}/utils/minicore-utils.sh
+
+clean_logdump
+
+dummy=$(mktemp $CRASH_DUMP_PATH/logdump-short-test.XXXXXX)
+
+log_dump --short
+logfile="${LOGDUMP_RESULT_PATH}"/* # there shall be only one file
+
+check_file_exists "$logfile"
+
+num=`unzip -qql "$logfile" | wc -l`
+if [ $num -ne 2 ]; then
+ fail "'log_dump --short' report contains $num files - 2 expected"
+fi
+
+check_zip_contains "$logfile" 'log/dump_systemstate.*log$'
+check_zip_contains "$logfile" 'log/$'
+
+check_file_exists "$dummy"
+
+clean_logdump
+
+exit_ok