diff options
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) @@ -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, ¶m, 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 = ¶m1 }; + bool is_ok = spawn_wait(av, NULL, ¶m0, 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 |