From 95ac66464d844f08069a918aa332d2692a49eaa0 Mon Sep 17 00:00:00 2001 From: Mateusz Moscicki Date: Thu, 31 Oct 2019 13:24:09 +0100 Subject: [PATCH 2/9] Add dumpsys API library Signed-off-by: Mateusz Moscicki Change-Id: I4f27610c7facfc48bc532bb5a943beb699a41dfd --- CMakeLists.txt | 10 ++ LICENSE | 205 +++++++++++++++++++++++ packaging/dumpsys-system_tests.spec | 35 ++++ packaging/dumpsys.spec | 55 ++++++ src/client-api/CMakeLists.txt | 33 ++++ src/client-api/dumpsys-system.c | 304 +++++++++++++++++++++++++++++++++ src/client-api/dumpsys-system.h | 51 ++++++ src/client-api/dumpsys-system.pc.in | 11 ++ src/dumpsys/CMakeLists.txt | 34 ++++ src/dumpsys/dumpsys-run.conf | 6 + src/dumpsys/dumpsys.c | 326 ++++++++++++++++++++++++++++++++++++ src/dumpsys/dumpsys.conf | 9 + src/shared/common.h | 25 +++ tests/CMakeLists.txt | 9 + tests/system/util/CMakeLists.txt | 28 ++++ tests/system/util/test-app.c | 70 ++++++++ 16 files changed, 1211 insertions(+) create mode 100644 CMakeLists.txt create mode 100644 LICENSE create mode 100644 packaging/dumpsys-system_tests.spec create mode 100644 packaging/dumpsys.spec create mode 100644 src/client-api/CMakeLists.txt create mode 100644 src/client-api/dumpsys-system.c create mode 100644 src/client-api/dumpsys-system.h create mode 100644 src/client-api/dumpsys-system.pc.in create mode 100644 src/dumpsys/CMakeLists.txt create mode 100644 src/dumpsys/dumpsys-run.conf create mode 100644 src/dumpsys/dumpsys.c create mode 100644 src/dumpsys/dumpsys.conf create mode 100644 src/shared/common.h create mode 100644 tests/CMakeLists.txt create mode 100644 tests/system/util/CMakeLists.txt create mode 100644 tests/system/util/test-app.c diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..086094f --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,10 @@ +CMAKE_MINIMUM_REQUIRED(VERSION 2.6) +PROJECT(dumpsys-project C) + +SET(PREFIX ${CMAKE_INSTALL_PREFIX}) + +ADD_DEFINITIONS(-D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE=1) + +# Sub modules +ADD_SUBDIRECTORY(src/client-api) +ADD_SUBDIRECTORY(src/dumpsys) diff --git a/LICENSE b/LICENSE new file mode 100644 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/packaging/dumpsys-system_tests.spec b/packaging/dumpsys-system_tests.spec new file mode 100644 index 0000000..3084f8a --- /dev/null +++ b/packaging/dumpsys-system_tests.spec @@ -0,0 +1,35 @@ +Name: dumpsys-system_tests +Summary: Dumpsys is a framework to get logs from services and applications. +Version: 0.0.2 +Release: 1 +Group: Framework/system +License: Apache-2.0 +Source0: %{name}-%{version}.tar.gz +BuildRequires: pkgconfig(pkgmgr-info) +BuildRequires: pkgconfig(glib-2.0) +BuildRequires: pkgconfig(dlog) +BuildRequires: pkgconfig(capi-base-common) +BuildRequires: pkgconfig(dumpsys-system) +BuildRequires: cmake + +%description +This package provides dumpsys utility and libraries to allow collecting logs from services and applications. + +%prep +%setup -q + +%build +export CFLAGS+=" -Werror" +cd tests +%cmake . \ + -DDUMPSYS_SYSTEM_TESTS_PATH=%{_libdir}/dumpsys_system_tests/ +make %{?jobs:-j%jobs} + +%install +rm -rf %{buildroot} +cd tests +%make_install + +%files +%license LICENSE +%attr(755,root,root) %{_libdir}/dumpsys_system_tests/utils/test-app diff --git a/packaging/dumpsys.spec b/packaging/dumpsys.spec new file mode 100644 index 0000000..a300d2c --- /dev/null +++ b/packaging/dumpsys.spec @@ -0,0 +1,55 @@ +Name: dumpsys +Summary: Dumpsys is a framework to get logs from services and applications. +Version: 0.0.2 +Release: 1 +Group: Framework/system +License: Apache-2.0 +Source0: %{name}-%{version}.tar.gz +BuildRequires: pkgconfig(glib-2.0) +BuildRequires: pkgconfig(dlog) +# BuildRequires: pkgconfig(capi-base-common) +BuildRequires: cmake + +%description +This package provides dumpsys utility and libraries to allow collecting logs from services and applications. + +%package -n libdumpsys-system +Summary: Package with dumpsys API library for services. +%description -n libdumpsys-system +This pakcage provides dumpsys API library for services. + +%package -n libdumpsys-system-devel +Requires: libdumpsys-system +Summary: libdumpsys-system development package. +%description -n libdumpsys-system-devel +This package provides library and header files. + +%prep +%setup -q + +%build +export CFLAGS+=" -Werror -fvisibility=hidden" + +%cmake . \ + -DVERSION=%{version} + +make %{?jobs:-j%jobs} + +%install +rm -rf %{buildroot} +%make_install + +%files -n libdumpsys-system +%{_libdir}/libdumpsys-system.so.* + +%files -n libdumpsys-system-devel +%{_includedir}/dumpsys-system.h +%{_libdir}/libdumpsys-system.so +%{_libdir}/pkgconfig/dumpsys-system.pc + +%files +%license LICENSE +%defattr(-,root,root) +%{_bindir}/dumpsys +%attr(0644,root,root) /usr/lib/tmpfiles.d/dumpsys-run.conf +%attr(0644,root,root) /etc/dbus-1/system.d/dumpsys.conf diff --git a/src/client-api/CMakeLists.txt b/src/client-api/CMakeLists.txt new file mode 100644 index 0000000..b07609c --- /dev/null +++ b/src/client-api/CMakeLists.txt @@ -0,0 +1,33 @@ +CMAKE_MINIMUM_REQUIRED(VERSION 2.6) +PROJECT(dumpsys-system C) + +INCLUDE(GNUInstallDirs) +INCLUDE(FindPkgConfig) +pkg_check_modules(dumpsys-system_pkgs REQUIRED + dlog + gio-2.0 + gio-unix-2.0) +INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/src/shared) +FOREACH(flag ${dumpsys-system_pkgs_CFLAGS}) + SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} ${flag}") +ENDFOREACH(flag) + +SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_CFLAGS} -fPIE -Wno-unused-function -Wno-unused-const-variable") + +SET(DCA_SRCS dumpsys-system.c) + +ADD_LIBRARY(libdumpsys-system SHARED dumpsys-system.c) +SET_TARGET_PROPERTIES(libdumpsys-system PROPERTIES + SOVERSION 1 + PUBLIC_HEADER dumpsys-system.h + OUTPUT_NAME dumpsys-system) +TARGET_LINK_LIBRARIES(libdumpsys-system PUBLIC ${dumpsys-system_pkgs_LIBRARIES}) + +LINK_DIRECTORIES(${CMAKE_BINARY_DIR}) + +CONFIGURE_FILE(dumpsys-system.pc.in dumpsys-system.pc @ONLY) + +INSTALL(TARGETS libdumpsys-system LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) +INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/dumpsys-system.pc + DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig) diff --git a/src/client-api/dumpsys-system.c b/src/client-api/dumpsys-system.c new file mode 100644 index 0000000..ffb4f24 --- /dev/null +++ b/src/client-api/dumpsys-system.c @@ -0,0 +1,304 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dumpsys-system.h" +#include "common.h" + +#ifndef LOG_TAG +#define LOG_TAG "DUMPSYS_SYSTEM" +#endif + +#include + +#define DBUS_BUS_NAME_MAX_LEN 255 + +#define DCA_ERROR 1 +#define DCA_ERROR_UNSPECIFIED_ERROR 1 +#define DCA_ERROR_GETEXEPATH 2 +#define DCA_ERROR_GETAPPID 3 +#define DCA_ERROR_UNIXFDLISTEMPTY 4 + +static dumpsys_dump_cb dump_cb; +static GDBusNodeInfo *introspection_data; +static GDBusConnection *connection; +static guint reg_id, own_id; +static char *service_name; + +#define ERROR_OK 0 +#define ERROR_INTERNAL 1 +#define ERROR_BINARY_NAME_ERROR 2 +#define ERROR_APPLICATION_NAME_ERROR 3 + +#define API_FUNCTION __attribute__((visibility("default"))) + +static const gchar introspection_xml[] = +"" + "" + "" + "" + "" + "" + "" +""; + +struct DumpData { + int fd; + int argc; + char **argv; +}; + +static int get_name(char **name) +{ + assert(name); + + if (service_name != NULL) { + *name = strdup(service_name); + return ERROR_OK; + } + + return ERROR_APPLICATION_NAME_ERROR; +} + +static void dump_thread_cb(GTask *task, gpointer source_object, gpointer task_data, GCancellable *cancellable) +{ + assert(task_data); + + struct DumpData *dump_data = (struct DumpData*)task_data; + + int result = dump_cb(dump_data->fd, dump_data->argc, dump_data->argv); + + close(dump_data->fd); + free(dump_data); + g_task_return_int(task, result); +} + +static int get_args(GVariant *vargs, gchar ***argv) +{ + assert(vargs); + assert(argv); + + gsize count = -1; + + GVariant *array = g_variant_get_child_value(vargs, 0); + *argv = g_variant_dup_strv(array, &count); + return count; +} + +static void dump_handler(GDBusMethodInvocation *invocation) +{ + assert(invocation); + assert(dump_cb); + + GDBusMessage *msg = g_dbus_method_invocation_get_message(invocation); + if (msg == NULL) { + LOGE("g_dbus_method_invocation_get_message() error"); + return; + } + + GUnixFDList *fd_list = g_dbus_message_get_unix_fd_list(msg); + if (fd_list == NULL) { + LOGE("No file descriptor provided"); + g_dbus_method_invocation_return_error(invocation, + DCA_ERROR, + DCA_ERROR_UNIXFDLISTEMPTY, + "No file descriptor provided"); + return; + } + + GError *error = NULL; + + int fd = g_unix_fd_list_get(fd_list, 0, &error); + if (fd == -1) { + LOGE("g_unix_fd_list_get() error: %s", error ? error->message : "(none)"); + g_dbus_method_invocation_return_gerror(invocation, error); + if (error) + g_error_free(error); + return; + } + + GVariant *body = g_dbus_message_get_body(msg); + + struct DumpData *dump_data = malloc(sizeof(struct DumpData)); + dump_data->fd = fd; + dump_data->argc = get_args(body, &dump_data->argv); + + GTask *task = g_task_new(NULL, NULL, NULL, NULL); + g_task_set_task_data(task, dump_data, NULL); + g_task_run_in_thread(task, dump_thread_cb); + g_object_unref(task); + g_dbus_method_invocation_return_value(invocation, g_variant_new("(i)", ERROR_OK)); +} + +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) +{ + if (g_strcmp0(method_name, "Dump") == 0) + dump_handler(invocation); +} + +static const GDBusInterfaceVTable interface_vtable = { + method_call_handler, NULL, NULL +}; + +static bool register_object(GDBusConnection *conn) +{ + GError *error = NULL; + reg_id = g_dbus_connection_register_object(conn, + DUMPSYS_PATH, + introspection_data->interfaces[0], + &interface_vtable, + NULL, + NULL, + &error); + + if (!reg_id) { + LOGE("g_dbus_connection_register_object() error: %s", error ? error->message : "(none)"); + if (error) + g_error_free(error); + return false; + } + return true; +} + +static bool dbus_init() +{ + introspection_data = g_dbus_node_info_new_for_xml(introspection_xml, NULL); + if (introspection_data == NULL) { + LOGE("g_dbus_node_info_new_for_xml() error"); + return false; + } + + GError *error = NULL; + + GBusType default_bus_type = G_BUS_TYPE_SYSTEM; + connection = g_bus_get_sync(default_bus_type, NULL, &error); + + if (!connection || error != NULL) { + LOGE("connect to the bus error: %s", error ? error->message : "(none)"); + if (error) + g_error_free(error); + return false; + } + + char name_with_prefix[DBUS_BUS_NAME_MAX_LEN]; + int ret = sprintf(name_with_prefix, "%s.%s", DUMPSYS_SERVICE_NAME_PREFIX, service_name); + if (ret < 0 || ret >= sizeof(name_with_prefix)) { + LOGE("sprintf error: %m"); + return false; + } + + own_id = g_bus_own_name_on_connection(connection, + name_with_prefix, + G_BUS_NAME_OWNER_FLAGS_NONE, + NULL, + NULL, + NULL, + NULL); + + return register_object(connection); +} + +static bool dbus_close() +{ + if (!connection) + return false; + + bool res = g_dbus_connection_unregister_object(connection, reg_id); + g_bus_unown_name(own_id); + g_object_unref(connection); + g_dbus_node_info_unref(introspection_data); + connection = NULL; + reg_id = 0; + own_id = 0; + return res; +} + +static int register_callback(dumpsys_dump_cb callback, const char *name) +{ + assert(callback); + if (name == NULL) { + int ret = get_name(&service_name); + if (ret != ERROR_OK) { + LOGE("get_name error"); + return TIZEN_ERROR_INVALID_OPERATION; + } + } else { + service_name = strdup(name); + if (service_name == NULL) { + LOGE("strdup error: %m"); + return TIZEN_ERROR_INVALID_OPERATION; + } + } + + bool result = dbus_init(); + if (result) + dump_cb = callback; + return result ? TIZEN_ERROR_NONE : TIZEN_ERROR_IO_ERROR; + +} + +int API_FUNCTION dumpsys_system_register_dump_cb(dumpsys_dump_cb callback, const char *name, void **handler) +{ + if (dump_cb != NULL) { + LOGE("register_dump_cb(): already registered"); + return TIZEN_ERROR_ALREADY_IN_PROGRESS; + } + + if (callback == NULL || name == NULL || handler == NULL) { + LOGE("register_me(): invalid parameter"); + return TIZEN_ERROR_INVALID_PARAMETER; + } + + int res = register_callback(callback, name); + if (res == TIZEN_ERROR_NONE) + *handler = (void*)callback; + return res; +} + +int API_FUNCTION dumpsys_system_unregister_dump_cb(void *handler) +{ + if (handler == NULL) { + LOGE("unregister_dump_cb(): parameter is NULL"); + return TIZEN_ERROR_INVALID_PARAMETER; + } + + if (handler != (void*)dump_cb) { + LOGE("unregister_dump_cb(): handler not registered"); + return TIZEN_ERROR_ALREADY_IN_PROGRESS; + } + + dump_cb = NULL; + service_name = NULL; + return dbus_close() ? TIZEN_ERROR_NONE : TIZEN_ERROR_IO_ERROR; +} diff --git a/src/client-api/dumpsys-system.h b/src/client-api/dumpsys-system.h new file mode 100644 index 0000000..579bb3f --- /dev/null +++ b/src/client-api/dumpsys-system.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2016-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 __DUMPSYS_SYSTEM_H__ +#define __DUMPSYS_SYSTEM_H__ + +/** + * @brief Callback function + * @param[in] fd File descriptor to write + * @param[in] argc Argument count + * @param[in] argv Array of arguments + */ +typedef int (*dumpsys_dump_cb)(const int fd, const int argc, char **argv); + +/** + * @brief Register the callback. + * @param[in] callback A callback for data retrieval + * @param[in] name Service name + * @param[out] handler Handler to unregister specific callback + * @retval TIZEN_ERROR_NONE Success + * @retval TIZEN_ERROR_ALREADY_IN_PROGRESS Callback already registered + * @retval TIZEN_ERROR_INVALID_PARAMETER Provided paramter is invalid + * @retval TIZEN_ERROR_INVALID_OPERATION Internal error + * @retval TIZEN_ERROR_IO_ERROR Error during DBus registration + */ +extern int dumpsys_system_register_dump_cb(dumpsys_dump_cb callback, const char *name, void **handler); + +/** + * @brief Unregister the callback. + * @param[in] handler Handler returned by dumpsys_system_register_dump_cb() + * @retval TIZEN_ERROR_NONE Success + * @retval TIZEN_ERROR_INVALID_PARAMETER Invalid handler + * @retval TIZEN_ERROR_ALREADY_IN_PROGRESS Handler is not registered + * @retval TIZEN_ERROR_IO_ERROR Error during DBus deregistration + */ +extern int dumpsys_system_unregister_dump_cb(void *handler); + +#endif // __DUMPSYS_SYSTEM_H__ diff --git a/src/client-api/dumpsys-system.pc.in b/src/client-api/dumpsys-system.pc.in new file mode 100644 index 0000000..b6c20fb --- /dev/null +++ b/src/client-api/dumpsys-system.pc.in @@ -0,0 +1,11 @@ +prefix=/usr +exec_prefix=${prefix} +includedir=${prefix}/include +libdir=${exec_prefix}/lib + +Name: dumpsys-system +Description: The crash-manager library +Version: @VERSION@ +Cflags: -I${includedir} +Libs: -L${libdir} -ldumpsys-system + diff --git a/src/dumpsys/CMakeLists.txt b/src/dumpsys/CMakeLists.txt new file mode 100644 index 0000000..19af459 --- /dev/null +++ b/src/dumpsys/CMakeLists.txt @@ -0,0 +1,34 @@ +CMAKE_MINIMUM_REQUIRED(VERSION 2.6) +PROJECT(dumpsys C) + +INCLUDE(GNUInstallDirs) +INCLUDE(FindPkgConfig) +find_package(PkgConfig REQUIRED) +pkg_check_modules(dumpsys_pkgs REQUIRED + gio-2.0 + glib-2.0 + gio-unix-2.0) + +INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/src/shared) +FOREACH(flag ${dumpsys_pkgs_CFLAGS}) + SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} ${flag}") +ENDFOREACH(flag) + +FOREACH(flag ${dumpsys_pkgs_LDFLAGS}) + SET(EXTRA_LDFLAGS "${EXTRA_CFLAGS} ${flag}") +ENDFOREACH(flag) + +SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_CFLAGS} -fPIE -Wno-unused-function -Wno-unused-const-variable") + +SET(DUMPSYS dumpsys.c) + +LINK_DIRECTORIES(${CMAKE_BINARY_DIR}) +ADD_EXECUTABLE(dumpsys ${DUMPSYS}) +TARGET_LINK_LIBRARIES(dumpsys PUBLIC ${dumpsys_pkgs_LIBRARIES}) + +INSTALL (TARGETS dumpsys DESTINATION ${CMAKE_INSTALL_BINDIR} + PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE) +INSTALL (FILES dumpsys-run.conf DESTINATION /usr/lib/tmpfiles.d/ + PERMISSIONS OWNER_READ OWNER_WRITE) +INSTALL (FILES dumpsys.conf DESTINATION /etc/dbus-1/system.d/ + PERMISSIONS OWNER_READ OWNER_WRITE) diff --git a/src/dumpsys/dumpsys-run.conf b/src/dumpsys/dumpsys-run.conf new file mode 100644 index 0000000..9cf3269 --- /dev/null +++ b/src/dumpsys/dumpsys-run.conf @@ -0,0 +1,6 @@ +d /run/dumpsys 0755 log log - - +d /run/dumpsys/priv 0700 log log - - +t /run/dumpsys/priv 0700 log log - security.SMACK64=System +d /run/dumpsys/priv/fifo 0700 log log - - +t /run/dumpsys/priv/fifo 0700 log log - security.SMACK64TRANSMUTE=TRUE +t /run/dumpsys/priv/fifo 0700 log log - security.SMACK64=User::App::Shared diff --git a/src/dumpsys/dumpsys.c b/src/dumpsys/dumpsys.c new file mode 100644 index 0000000..7d4a746 --- /dev/null +++ b/src/dumpsys/dumpsys.c @@ -0,0 +1,326 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "common.h" + +#define BUFF_SIZE 4096 +#define FIFO_BASE_DIR "/run/dumpsys/priv/fifo" +#define METHOD_DUMP "Dump" +#define SERVICE_NAME_MAX_LEN 255 + +static int out_fd = -1; +static GVariant *dump_args; + +static void* copy_data(void *arg) +{ + int source_fd = ((int*)arg)[0]; + int dest_fd = ((int*)arg)[1]; + + ssize_t read_count = 0; + while (read_count >= 0) { + char buff[BUFF_SIZE]; + read_count = read(source_fd, buff, sizeof(buff)); + if (read_count == 0) + break; + + if (read_count == -1) { + if (errno == EAGAIN) { + read_count = 0; + sleep(0.1); + continue; + } + printf("read error: %m\n"); + goto out; + } + ssize_t bytes_to_write = read_count; + while (bytes_to_write > 0) { + ssize_t write_count = write(dest_fd, buff, read_count); + if (write_count == -1) { + printf("write error: %m\n"); + goto out; + } + bytes_to_write -= write_count; + } + } +out: + return NULL; +} + +static bool make_fifo(int *write_fd, int *read_fd) +{ + bool result = false; + char *fifo_path = NULL; + + if (asprintf(&fifo_path, "%s/dumpsys_XXXXXX", FIFO_BASE_DIR) == -1) { + printf("asprintf error: %m\n"); + return result; + } + uid_t ruid, euid, suid; + if (getresuid(&ruid, &euid, &suid) == -1) { + printf("getresuid error: %m\n"); + return false; + } + + if (seteuid(suid) == -1) { + printf("setuid error: %m\n"); + return false; + } + + if (mktemp(fifo_path) == NULL) { + printf("mktemp error: %m\n"); + goto end; + } + + if (mkfifo(fifo_path, 0600) == -1) { + printf("mkfifo error: %m\n"); + goto end; + } + + /* + * We must open for reading before open for writing, because of "No such device or address" error. + * Read must have O_NONBLOCK flag, otherwise open will block us until + * someone open fifo for writing. + */ + *read_fd = open(fifo_path, O_RDONLY | O_NONBLOCK); + if (*read_fd == -1) { + printf("open fifo for reading error: %m\n"); + goto end; + } + + *write_fd = open(fifo_path, O_WRONLY | O_NONBLOCK); + if (*write_fd == -1) { + printf("open fifo for writing error: %m\n"); + close(*read_fd); + goto end; + } + + if (unlink(fifo_path) == -1) { + printf("unlink '%s' error: %m\n", fifo_path); + close(*read_fd); + close(*write_fd); + goto end; + } + + int old_flags = fcntl(*read_fd, F_GETFL); + if (old_flags == -1) { + printf("fcntl() GETFL error: %m\n"); + close(*read_fd); + close(*write_fd); + goto end; + } + + fcntl(*read_fd, F_SETFL, old_flags & ~O_NONBLOCK); + + result = true; +end: + free(fifo_path); + if (seteuid(euid) == -1) { + printf("setuid error: %m\n"); + result = false; + } + return result; +} + +static void call_dump(const gchar *name, const gchar *application_name, GDBusConnection *connection) +{ + GError *error = NULL; + + GDBusMessage *method_call_message = g_dbus_message_new_method_call(name, + DUMPSYS_PATH, + DUMPSYS_NAME, + METHOD_DUMP); + + g_dbus_message_set_body(method_call_message, dump_args); + + GUnixFDList *fd_list = g_unix_fd_list_new(); + + int write_fd, read_fd; + if (!make_fifo(&write_fd, &read_fd)) { + return; + } + + pthread_t copy_thread; + int fd_pair2[2]; + fd_pair2[0] = read_fd; + fd_pair2[1] = out_fd; + if (pthread_create(©_thread, NULL, copy_data, &fd_pair2[0]) != 0) { + printf("pthread_create error: %m\n"); + return; + } + g_unix_fd_list_append(fd_list, write_fd, &error); + close(write_fd); + if (error != NULL) { + dprintf(STDERR_FILENO, "g_unix_fd_list_append() error: %s\n", error->message); + return; + } + + g_dbus_message_set_unix_fd_list(method_call_message, fd_list); + + GDBusMessage *repl = g_dbus_connection_send_message_with_reply_sync(connection, + method_call_message, + G_DBUS_SEND_MESSAGE_FLAGS_NONE, + -1, + NULL, + NULL, + &error); + + if (repl != NULL && g_dbus_message_get_message_type(repl) == G_DBUS_MESSAGE_TYPE_ERROR) + g_dbus_message_to_gerror(repl, &error); + + if (error != NULL) { + printf("Send message error: %s\n", error ? error->message : "(unspecified)"); + g_error_free(error); + } + + g_object_unref(fd_list); + g_object_unref(method_call_message); + g_object_unref(repl); + + if (pthread_join(copy_thread, NULL) != 0) + printf("pthread_join error: %m\n"); + + close(read_fd); +} + +static void call_dump_for_list_of_services(GDBusConnection *connection, GList* list_of_services) +{ + GList *element = g_list_first(list_of_services); + + while (element != NULL) { + char *name = (gchar *)element->data; + char service_name[SERVICE_NAME_MAX_LEN]; + + snprintf(service_name, sizeof(service_name), "%s.%s", DUMPSYS_SERVICE_NAME_PREFIX, name); + call_dump(service_name, name, connection); + + element = g_list_next(element); + } +} + + +static GDBusConnection* make_connection(GBusType bus_type) +{ + GError *error = NULL; + GDBusConnection *connection = NULL; + + connection = g_bus_get_sync(bus_type, NULL, &error); + if (connection == NULL || error != NULL) + printf("make_connection error: %s\n", error->message); + + return connection; +} + +static void process_bus(GBusType bus_type, GList *list_of_services) +{ + GDBusConnection *connection = make_connection(bus_type); + if (connection == NULL) { + printf("can't connect\n"); + return; + } + + call_dump_for_list_of_services(connection, list_of_services); + + g_object_unref(connection); +} + +static void process_each_bus(GList *list_of_services) +{ + uid_t ruid, euid, suid; + if (getresuid(&ruid, &euid, &suid) == -1) { + printf("getresuid error: %m\n"); + return; + } + + process_bus(G_BUS_TYPE_SYSTEM, list_of_services); +} + +static void set_dump_args(int start, int argc, char *argv[]) +{ + GVariantBuilder builder; + g_variant_builder_init(&builder, G_VARIANT_TYPE("(as)")); + g_variant_builder_open(&builder, G_VARIANT_TYPE("as")); + + for (int i = start; i < argc; i++) + g_variant_builder_add(&builder, "s", argv[i]); + + g_variant_builder_close(&builder); + dump_args = g_variant_builder_end(&builder); +} + +static void print_help(char **argv) +{ + printf("Usage:\n"); + printf("\t%s [-o filename] -- [service arguments]\n\n", argv[0]); +} + +int main(int argc, char *argv[]) +{ + int opt; + char *filename = NULL; + out_fd = STDOUT_FILENO; + + while ((opt = getopt(argc, argv, "o:")) != -1) { + switch(opt) { + case 'o': + filename = optarg; + break; + } + } + + if (optind >= argc) { + print_help(argv); + return EXIT_FAILURE; + } + + if (filename != NULL) { + out_fd = open(filename, O_WRONLY | O_CREAT, 0600); + if (out_fd == -1) { + printf("Can not open file '%s': %m\n", filename); + return EXIT_FAILURE; + } + } + + GList *list_of_services = NULL; + + int i = optind; + + if (i < argc) + list_of_services = g_list_append(list_of_services, strdup(argv[i])); + + set_dump_args(i+1, argc, argv); + + process_each_bus(list_of_services); + + g_list_free_full(list_of_services, free); + + return EXIT_SUCCESS; +} diff --git a/src/dumpsys/dumpsys.conf b/src/dumpsys/dumpsys.conf new file mode 100644 index 0000000..de9b8ee --- /dev/null +++ b/src/dumpsys/dumpsys.conf @@ -0,0 +1,9 @@ + + + + + + + + diff --git a/src/shared/common.h b/src/shared/common.h new file mode 100644 index 0000000..d622012 --- /dev/null +++ b/src/shared/common.h @@ -0,0 +1,25 @@ +/* + * 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 __COMMON_H__ +#define __COMMON_H__ + +#define DUMPSYS_PATH "/Org/Tizen/Dumpsys" +#define DUMPSYS_NAME "org.tizen.dumpsys" +#define DUMPSYS_INTERFACE DUMPSYS_NAME +#define DUMPSYS_SERVICE_NAME_PREFIX DUMPSYS_NAME + +#endif diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 0000000..a79f14e --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,9 @@ +CMAKE_MINIMUM_REQUIRED(VERSION 2.6) +PROJECT(dumpsys-project C) + +SET(PREFIX ${CMAKE_INSTALL_PREFIX}) + +ADD_DEFINITIONS(-D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE=1) + +# Sub modules +ADD_SUBDIRECTORY(system/util) diff --git a/tests/system/util/CMakeLists.txt b/tests/system/util/CMakeLists.txt new file mode 100644 index 0000000..a7444ae --- /dev/null +++ b/tests/system/util/CMakeLists.txt @@ -0,0 +1,28 @@ +CMAKE_MINIMUM_REQUIRED(VERSION 2.6) +PROJECT(tests-utils C) + +INCLUDE(GNUInstallDirs) +INCLUDE(FindPkgConfig) +pkg_check_modules(dumpsys-system_pkgs REQUIRED + capi-base-common + dlog + gio-2.0 + gio-unix-2.0 + dumpsys-system + pkgmgr-info) + +INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/../src/shared) +FOREACH(flag ${dumpsys-system_pkgs_CFLAGS}) + SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} ${flag}") +ENDFOREACH(flag) + +SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_CFLAGS} -fPIE -Wno-unused-function -Wno-unused-const-variable") + +ADD_EXECUTABLE(test-app test-app.c) + +TARGET_LINK_LIBRARIES(test-app PUBLIC ${dumpsys-system_pkgs_LIBRARIES}) + +LINK_DIRECTORIES(${CMAKE_BINARY_DIR}) + +INSTALL(TARGETS test-app DESTINATION ${DUMPSYS_SYSTEM_TESTS_PATH}/utils + PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE) diff --git a/tests/system/util/test-app.c b/tests/system/util/test-app.c new file mode 100644 index 0000000..a9189f3 --- /dev/null +++ b/tests/system/util/test-app.c @@ -0,0 +1,70 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include "common.h" + +#include + +char *message; +GMainLoop *loop; + +int dump(const int fd, const int argc, char **argv) +{ + for (int i = 0; i < argc; i++) + printf("[%d] arg: %s\n", i+1, argv[i]); + g_main_loop_quit(loop); + return write(fd, message, strlen(message)); +} + +int main(int argc, char *argv[]) +{ + char *name = NULL; + + for (int i = optind; i < argc; i++) { + if (i == argc-2) + name = argv[i]; + else if (i == argc-1) + message = argv[i]; + } + + if (name == NULL || message == NULL) { + printf("Why didn't you specify a required arguments?! ;~~~()\n"); + printf("\t%s \n", argv[0]); + return EXIT_FAILURE; + } + + int *handler; + int ret = dumpsys_system_register_dump_cb(dump, name, (void**)&handler); + if (ret != TIZEN_ERROR_NONE) { + printf("Some error :(. Check dlog.\n"); + return EXIT_FAILURE; + } + + loop = g_main_loop_new(NULL, false); + g_main_loop_run(loop); + g_main_loop_unref(loop); + + dumpsys_system_unregister_dump_cb((void*)handler); + + return EXIT_SUCCESS; +} -- 2.7.4 From bb5afc237868973b3acd6a1873c2e32bb119e78e Mon Sep 17 00:00:00 2001 From: Karol Lewandowski Date: Tue, 11 Feb 2020 16:26:08 +0100 Subject: [PATCH 3/9] Do not leak fifo_path Change-Id: Ib8ed78d09c3ab0438f6786d91c86c8ae6d1f1f2f --- src/dumpsys/dumpsys.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/dumpsys/dumpsys.c b/src/dumpsys/dumpsys.c index 7d4a746..93d1cd1 100644 --- a/src/dumpsys/dumpsys.c +++ b/src/dumpsys/dumpsys.c @@ -87,12 +87,12 @@ static bool make_fifo(int *write_fd, int *read_fd) uid_t ruid, euid, suid; if (getresuid(&ruid, &euid, &suid) == -1) { printf("getresuid error: %m\n"); - return false; + goto end_free; } if (seteuid(suid) == -1) { printf("setuid error: %m\n"); - return false; + goto end_free; } if (mktemp(fifo_path) == NULL) { @@ -142,11 +142,13 @@ static bool make_fifo(int *write_fd, int *read_fd) result = true; end: - free(fifo_path); if (seteuid(euid) == -1) { printf("setuid error: %m\n"); result = false; } +end_free: + free(fifo_path); + return result; } -- 2.7.4 From 02bad8f7abeb5fa91c43837f231b0b70b36be525 Mon Sep 17 00:00:00 2001 From: Karol Lewandowski Date: Fri, 14 Feb 2020 14:09:45 +0100 Subject: [PATCH 4/9] Do not leak write_fd in call_dump error path Change-Id: I1ac5ca3a3987c2334e013bd52f304aa5fa0f9f58 --- src/dumpsys/dumpsys.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/dumpsys/dumpsys.c b/src/dumpsys/dumpsys.c index 93d1cd1..6f4e4ac 100644 --- a/src/dumpsys/dumpsys.c +++ b/src/dumpsys/dumpsys.c @@ -170,19 +170,19 @@ static void call_dump(const gchar *name, const gchar *application_name, GDBusCon return; } + g_unix_fd_list_append(fd_list, write_fd, &error); + if (error != NULL) { + dprintf(STDERR_FILENO, "g_unix_fd_list_append() error: %s\n", error->message); + goto out_close; + } + pthread_t copy_thread; int fd_pair2[2]; fd_pair2[0] = read_fd; fd_pair2[1] = out_fd; if (pthread_create(©_thread, NULL, copy_data, &fd_pair2[0]) != 0) { printf("pthread_create error: %m\n"); - return; - } - g_unix_fd_list_append(fd_list, write_fd, &error); - close(write_fd); - if (error != NULL) { - dprintf(STDERR_FILENO, "g_unix_fd_list_append() error: %s\n", error->message); - return; + goto out_unref_fdlist; } g_dbus_message_set_unix_fd_list(method_call_message, fd_list); @@ -203,13 +203,16 @@ static void call_dump(const gchar *name, const gchar *application_name, GDBusCon g_error_free(error); } - g_object_unref(fd_list); g_object_unref(method_call_message); g_object_unref(repl); if (pthread_join(copy_thread, NULL) != 0) printf("pthread_join error: %m\n"); +out_unref_fdlist: + g_object_unref(fd_list); +out_close: + close(write_fd); close(read_fd); } -- 2.7.4 From 0aae5ae8eb0dbce70daac42dd6c169c982888227 Mon Sep 17 00:00:00 2001 From: Karol Lewandowski Date: Fri, 14 Feb 2020 14:20:38 +0100 Subject: [PATCH 5/9] Fix: drop duplicate test Change-Id: Ie048642cb7bc5a6d0448843d776e33efc6b21225 --- src/dumpsys/dumpsys.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dumpsys/dumpsys.c b/src/dumpsys/dumpsys.c index 6f4e4ac..819a7b5 100644 --- a/src/dumpsys/dumpsys.c +++ b/src/dumpsys/dumpsys.c @@ -199,7 +199,7 @@ static void call_dump(const gchar *name, const gchar *application_name, GDBusCon g_dbus_message_to_gerror(repl, &error); if (error != NULL) { - printf("Send message error: %s\n", error ? error->message : "(unspecified)"); + printf("Send message error: %s\n", error->message); g_error_free(error); } -- 2.7.4 From 594b13c93d4c754631fe7f67bb5fbc07e6f4c382 Mon Sep 17 00:00:00 2001 From: Mateusz Moscicki Date: Tue, 21 Jan 2020 16:12:48 +0100 Subject: [PATCH 6/9] Modify dumpsys architecture This commit introduces a dumpsys-service which is responsible for communication between dumpsys tool and service or application. dumpsys-service creates a fifo pipe, and sends one of its ends to dumpsys-agents. If agent confirms that requested application exists on its bus, then dumpsys-service returns second end of pipe to dumpsys tool. After that dumpsys tool can read the data written by requested application or service. Change-Id: I40545c260d5c9367da6af4454151293f7e3d1a3b --- CMakeLists.txt | 3 + packaging/dumpsys-system_tests.spec | 35 --- packaging/dumpsys.manifest | 5 + packaging/dumpsys.spec | 64 +++- src/agent/CMakeLists.txt | 44 +++ src/agent/dumpsys-agent.c | 40 +++ src/agent/dumpsys-agent.conf | 12 + src/agent/dumpsys-session-agent.service | 6 + src/agent/libdumpsys-agent.c | 316 ++++++++++++++++++++ src/agent/libdumpsys-agent.h | 26 ++ src/client-api/dumpsys-system.c | 82 +++--- src/client-api/dumpsys-system.h | 6 +- src/dumpsys/CMakeLists.txt | 39 ++- src/dumpsys/dumpsys.c | 277 ++--------------- src/dumpsys/dumpsys.conf | 9 - src/dumpsys/dumpsys.pc.in | 11 + src/dumpsys/libdumpsys.c | 144 +++++++++ src/dumpsys/libdumpsys.h | 22 ++ src/service/CMakeLists.txt | 38 +++ src/service/dumpsys-service.c | 508 ++++++++++++++++++++++++++++++++ src/service/dumpsys-service.conf | 19 ++ src/service/dumpsys-service.service | 9 + src/shared/common.h | 23 +- tests/system/util/CMakeLists.txt | 9 +- tests/system/util/test-app.c | 89 ++++-- 25 files changed, 1457 insertions(+), 379 deletions(-) delete mode 100644 packaging/dumpsys-system_tests.spec create mode 100644 packaging/dumpsys.manifest create mode 100644 src/agent/CMakeLists.txt create mode 100644 src/agent/dumpsys-agent.c create mode 100644 src/agent/dumpsys-agent.conf create mode 100644 src/agent/dumpsys-session-agent.service create mode 100644 src/agent/libdumpsys-agent.c create mode 100644 src/agent/libdumpsys-agent.h delete mode 100644 src/dumpsys/dumpsys.conf create mode 100644 src/dumpsys/dumpsys.pc.in create mode 100644 src/dumpsys/libdumpsys.c create mode 100644 src/dumpsys/libdumpsys.h create mode 100644 src/service/CMakeLists.txt create mode 100644 src/service/dumpsys-service.c create mode 100644 src/service/dumpsys-service.conf create mode 100644 src/service/dumpsys-service.service diff --git a/CMakeLists.txt b/CMakeLists.txt index 086094f..665b768 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,3 +8,6 @@ ADD_DEFINITIONS(-D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE=1) # Sub modules ADD_SUBDIRECTORY(src/client-api) ADD_SUBDIRECTORY(src/dumpsys) +ADD_SUBDIRECTORY(src/agent) +ADD_SUBDIRECTORY(src/service) +ADD_SUBDIRECTORY(tests) diff --git a/packaging/dumpsys-system_tests.spec b/packaging/dumpsys-system_tests.spec deleted file mode 100644 index 3084f8a..0000000 --- a/packaging/dumpsys-system_tests.spec +++ /dev/null @@ -1,35 +0,0 @@ -Name: dumpsys-system_tests -Summary: Dumpsys is a framework to get logs from services and applications. -Version: 0.0.2 -Release: 1 -Group: Framework/system -License: Apache-2.0 -Source0: %{name}-%{version}.tar.gz -BuildRequires: pkgconfig(pkgmgr-info) -BuildRequires: pkgconfig(glib-2.0) -BuildRequires: pkgconfig(dlog) -BuildRequires: pkgconfig(capi-base-common) -BuildRequires: pkgconfig(dumpsys-system) -BuildRequires: cmake - -%description -This package provides dumpsys utility and libraries to allow collecting logs from services and applications. - -%prep -%setup -q - -%build -export CFLAGS+=" -Werror" -cd tests -%cmake . \ - -DDUMPSYS_SYSTEM_TESTS_PATH=%{_libdir}/dumpsys_system_tests/ -make %{?jobs:-j%jobs} - -%install -rm -rf %{buildroot} -cd tests -%make_install - -%files -%license LICENSE -%attr(755,root,root) %{_libdir}/dumpsys_system_tests/utils/test-app diff --git a/packaging/dumpsys.manifest b/packaging/dumpsys.manifest new file mode 100644 index 0000000..75b0fa5 --- /dev/null +++ b/packaging/dumpsys.manifest @@ -0,0 +1,5 @@ + + + + + diff --git a/packaging/dumpsys.spec b/packaging/dumpsys.spec index a300d2c..e32522d 100644 --- a/packaging/dumpsys.spec +++ b/packaging/dumpsys.spec @@ -1,22 +1,28 @@ Name: dumpsys Summary: Dumpsys is a framework to get logs from services and applications. -Version: 0.0.2 +Version: 0.0.3 Release: 1 Group: Framework/system License: Apache-2.0 Source0: %{name}-%{version}.tar.gz +Source1001: dumpsys.manifest BuildRequires: pkgconfig(glib-2.0) BuildRequires: pkgconfig(dlog) -# BuildRequires: pkgconfig(capi-base-common) BuildRequires: cmake %description This package provides dumpsys utility and libraries to allow collecting logs from services and applications. +%package -n dumpsys-devel +Summary: dumpsys development package. +Requires: dumpsys +%description -n dumpsys-devel +This package provides library and header files. + %package -n libdumpsys-system Summary: Package with dumpsys API library for services. %description -n libdumpsys-system -This pakcage provides dumpsys API library for services. +This package provides dumpsys API library for services. %package -n libdumpsys-system-devel Requires: libdumpsys-system @@ -24,14 +30,20 @@ Summary: libdumpsys-system development package. %description -n libdumpsys-system-devel This package provides library and header files. +%package tests +Summary: Contains scripts for testing dumpsys +%description tests + %prep %setup -q %build +cp %{SOURCE1001} . export CFLAGS+=" -Werror -fvisibility=hidden" %cmake . \ - -DVERSION=%{version} + -DVERSION=%{version} \ + -DDUMPSYS_SYSTEM_TESTS_PATH=%{_libdir}/dumpsys_system_tests/ make %{?jobs:-j%jobs} @@ -39,17 +51,49 @@ make %{?jobs:-j%jobs} rm -rf %{buildroot} %make_install +mkdir -p %{buildroot}/%{_unitdir}/basic.target.wants/ +ln -s ../dumpsys-service.service %{buildroot}/%{_unitdir}/basic.target.wants/dumpsys-service.service + +mkdir -p %{buildroot}/%{_unitdir_user}/delayed.target.wants/ +ln -s ../dumpsys-session-agent.service %{buildroot}/%{_unitdir_user}/delayed.target.wants/dumpsys-session-agent.service + +%files +%license LICENSE +%manifest %{name}.manifest + +%defattr(0755,root,root) +%{_bindir}/dumpsys +%{_libdir}/libdumpsys.so.* +%{_bindir}/dumpsys-service +%{_bindir}/dumpsys-session-agent +%{_libdir}/libdumpsys-agent.so.* + +%defattr(0655,root,root) +%{_unitdir}/basic.target.wants/dumpsys-service.service +%{_unitdir}/dumpsys-service.service +%{_unitdir_user}/delayed.target.wants/dumpsys-session-agent.service +%{_unitdir_user}/dumpsys-session-agent.service +%{_libdir}/tmpfiles.d/dumpsys-run.conf +%{_sysconfdir}/dbus-1/system.d/dumpsys-service.conf +%{_sysconfdir}/dbus-1/system.d/dumpsys-agent.conf + +%files -n dumpsys-devel +%manifest %{name}.manifest +%{_includedir}/libdumpsys.h +%{_libdir}/libdumpsys.so +%{_libdir}/libdumpsys-agent.so +%{_libdir}/pkgconfig/dumpsys.pc + %files -n libdumpsys-system +%manifest %{name}.manifest %{_libdir}/libdumpsys-system.so.* %files -n libdumpsys-system-devel +%manifest %{name}.manifest %{_includedir}/dumpsys-system.h %{_libdir}/libdumpsys-system.so %{_libdir}/pkgconfig/dumpsys-system.pc -%files -%license LICENSE -%defattr(-,root,root) -%{_bindir}/dumpsys -%attr(0644,root,root) /usr/lib/tmpfiles.d/dumpsys-run.conf -%attr(0644,root,root) /etc/dbus-1/system.d/dumpsys.conf +%files tests +%manifest %{name}.manifest +%attr(0755,root,root) %{_libdir}/dumpsys_system_tests/utils/test-app diff --git a/src/agent/CMakeLists.txt b/src/agent/CMakeLists.txt new file mode 100644 index 0000000..54aa7a0 --- /dev/null +++ b/src/agent/CMakeLists.txt @@ -0,0 +1,44 @@ +CMAKE_MINIMUM_REQUIRED(VERSION 2.6) +PROJECT(DUMPSYS_AGENT C) + +INCLUDE(GNUInstallDirs) +INCLUDE(FindPkgConfig) + +SET(AGENT_BIN "dumpsys-session-agent") +SET(AGENT_LIB "libdumpsys-agent") + +find_package(PkgConfig REQUIRED) +pkg_check_modules(libagent_pkgs REQUIRED + dlog + gio-2.0 + glib-2.0 + gio-unix-2.0) + +INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/src/shared) +FOREACH(flag ${libagent_pkgs_CFLAGS}) + SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} ${flag}") +ENDFOREACH(flag) + +FOREACH(flag ${libagent_pkgs_LDFLAGS}) + SET(EXTRA_LDFLAGS "${EXTRA_CFLAGS} ${flag}") +ENDFOREACH(flag) + +SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_CFLAGS} -Wall -Wextra -Werror -fPIE -Wno-unused-function -Wno-unused-const-variable") + +ADD_LIBRARY(${AGENT_LIB} SHARED libdumpsys-agent.c) +TARGET_LINK_LIBRARIES(${AGENT_LIB} PUBLIC ${libagent_pkgs_LIBRARIES}) +SET_TARGET_PROPERTIES(${AGENT_LIB} PROPERTIES + SOVERSION 1 + OUTPUT_NAME dumpsys-agent) + +LINK_DIRECTORIES(${CMAKE_BINARY_DIR}) +ADD_EXECUTABLE(${AGENT_BIN} dumpsys-agent.c) +TARGET_LINK_LIBRARIES(${AGENT_BIN} PUBLIC ${AGENT_LIB}) + +INSTALL (TARGETS ${AGENT_BIN} DESTINATION ${CMAKE_INSTALL_BINDIR} + PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE) +INSTALL (TARGETS ${AGENT_LIB} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}) +INSTALL (FILES dumpsys-agent.conf DESTINATION /etc/dbus-1/system.d/ + PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE) +INSTALL (FILES dumpsys-session-agent.service DESTINATION /usr/lib/systemd/user/ + PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE) diff --git a/src/agent/dumpsys-agent.c b/src/agent/dumpsys-agent.c new file mode 100644 index 0000000..07b27d9 --- /dev/null +++ b/src/agent/dumpsys-agent.c @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2019-2020 Samsung Electronics Co., Ltd. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include "libdumpsys-agent.h" + +int main() +{ + int result = EXIT_SUCCESS; + GMainLoop *loop = g_main_loop_new(NULL, false); + + assert(loop); + + if (!dumpsys_agent_run(loop, G_BUS_TYPE_SESSION)) { + result = EXIT_FAILURE; + goto out; + } + + g_main_loop_run(loop); +out: + dumpsys_agent_stop(); + g_main_loop_unref(loop); + + return result; +} diff --git a/src/agent/dumpsys-agent.conf b/src/agent/dumpsys-agent.conf new file mode 100644 index 0000000..3dbe693 --- /dev/null +++ b/src/agent/dumpsys-agent.conf @@ -0,0 +1,12 @@ + + + + + + + + + + + diff --git a/src/agent/dumpsys-session-agent.service b/src/agent/dumpsys-session-agent.service new file mode 100644 index 0000000..4dff996 --- /dev/null +++ b/src/agent/dumpsys-session-agent.service @@ -0,0 +1,6 @@ +[Unit] +Description=Dumpsys agent +After=dbus.socket + +[Service] +ExecStart=/usr/bin/dumpsys-session-agent diff --git a/src/agent/libdumpsys-agent.c b/src/agent/libdumpsys-agent.c new file mode 100644 index 0000000..6714ff6 --- /dev/null +++ b/src/agent/libdumpsys-agent.c @@ -0,0 +1,316 @@ +/* + * Copyright (c) 2019-2020 Samsung Electronics Co., Ltd. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef LOG_TAG +#define LOG_TAG "LIBDUMPSYS_AGENT" +#endif + +#include + +#include "common.h" + +static GDBusConnection *conn_system, *conn_session; +static GMainLoop *main_loop; +static guint name_ident; +static GDBusNodeInfo *introspection_data; +static const gchar introspection_xml[] = + +"" + "" + "" + "" + "" + "" + "" + "" +""; + +static const char *uniq_name; + +static void free_objects() +{ + if (introspection_data) { + g_dbus_node_info_unref(introspection_data); + introspection_data = NULL; + } + + if (name_ident != 0) { + g_bus_unown_name(name_ident); + name_ident = 0; + } + + if (conn_system) { + g_object_unref(conn_system); + conn_system = NULL; + } + + if (conn_session) { + g_object_unref(conn_session); + conn_session = NULL; + } +} + +static GDBusMessage* prepare_message(GVariant *parameters, GUnixFDList *fd_list) +{ + assert(parameters); + assert(fd_list); + + GVariant *args = g_variant_get_child_value(parameters, 0); + GVariant *name = g_variant_get_child_value(parameters, 1); + + const char *name_str = g_variant_get_string(name, NULL); + char *new_name = NULL; + if (asprintf(&new_name, "%s.%s", DUMPSYS_SERVICE_NAME_PREFIX, name_str) == -1) { + LOGE("[%s] asprintf() error: %m\n", uniq_name); + return NULL; + } + + GDBusMessage *message = g_dbus_message_new_method_call(new_name, + DUMPSYS_PATH, + DUMPSYS_NAME, + METHOD_DUMP); + + g_variant_unref(name); + + GVariant *args_tuple = g_variant_new_tuple(&args, 1); + g_variant_unref(args); + + g_dbus_message_set_body(message, args_tuple); + g_dbus_message_set_unix_fd_list(message, fd_list); + + free(new_name); + return message; +} + +static void dump_call_handler(GVariant *parameters, + GDBusMethodInvocation *invocation) +{ + bool result = true; + if (!g_variant_is_of_type(parameters, G_VARIANT_TYPE("(ass)"))) { + LOGE("[%s] Parameters are not of the correct type (ass)\n", uniq_name); + g_dbus_method_invocation_return_error( + invocation, + CS_ERROR, + CS_ERR_PARAM, + "%s", + "Parameters are not of the correct type (ass)"); + return; + } + + GDBusMessage *incoming_message = g_dbus_method_invocation_get_message(invocation); + GUnixFDList *fd_list = g_dbus_message_get_unix_fd_list(incoming_message); + GDBusMessage *message = prepare_message(parameters, fd_list); + if (message == NULL) { + LOGE("[%s] Message preparing error", uniq_name); + goto out; + } + + GError *error = NULL; + GDBusMessage *reply = g_dbus_connection_send_message_with_reply_sync( + conn_session, + message, + G_DBUS_SEND_MESSAGE_FLAGS_NONE, + -1, + NULL, + NULL, + &error); + g_object_unref(message); + + if (reply != NULL && g_dbus_message_get_message_type(reply) == G_DBUS_MESSAGE_TYPE_ERROR) + g_dbus_message_to_gerror(reply, &error); + + g_object_unref(reply); + + if (error != NULL) { + if (error->domain == g_dbus_error_quark() && + error->code == G_DBUS_ERROR_SERVICE_UNKNOWN) { + LOGD("[%s] Service not found on my bus\n", uniq_name); + result = false; + } else { + LOGE("[%s] Dump method call error: %s\n", uniq_name, ERR_TEXT(error)); + g_dbus_method_invocation_return_gerror(invocation, error); + goto out; + } + } + + g_dbus_method_invocation_return_value(invocation, + g_variant_new("(i)", + result ? DUMP_SUCCESS : DUMP_FAIL)); +out: + if (error) + g_error_free(error); +} + +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) +{ + (void) conn; + (void) sender; + (void) object_path; + (void) iface_name; + (void) user_data; + if (g_strcmp0(method_name, METHOD_DUMP) == 0) + dump_call_handler(parameters, invocation); +} + +static const GDBusInterfaceVTable interface_vtable = { + method_call_handler, NULL, NULL, {} +}; + +static void on_bus_acquired(GDBusConnection *conn, + const gchar *name, + gpointer user_data) +{ + (void) name; + (void) user_data; + guint registration_id; + GError *error = NULL; + + registration_id = g_dbus_connection_register_object(conn, + AGENT_OBJECT_PATH, + introspection_data->interfaces[0], + &interface_vtable, NULL, NULL, &error); + + if (registration_id == 0 || error) { + LOGE("[%s] Failed to g_dbus_connection_register_object: %s\n", + uniq_name, + ERR_TEXT(error)); + if (error) + g_error_free(error); + } +} + +static void name_lost_handler(GDBusConnection *conn, + const gchar *name, + gpointer user_data) +{ + (void) conn; + (void) name; + (void) user_data; + LOGE("[%s] DBus name lost. Exiting.", uniq_name); + free_objects(); + g_main_loop_quit(main_loop); +} + +static bool register_agent(GDBusConnection *conn) +{ + GError *error = NULL; + + GVariant *v_result = g_dbus_connection_call_sync(conn, + SERVICE_BUS_NAME, + SERVICE_OBJECT_PATH, + SERVICE_INTERFACE, + METHOD_REGISTER, + NULL, + G_VARIANT_TYPE("(b)"), + G_DBUS_CALL_FLAGS_NONE, + SERVICE_TIMEOUT_MS, + NULL, + &error); + + if (v_result == NULL || error != NULL) { + LOGE("[%s] Registration error: %s\n", uniq_name, ERR_TEXT(error)); + if (error) + g_error_free(error); + return false; + } + bool result; + g_variant_get(v_result, "(b)", &result); + + if (result) { + LOGD("[%s] Registration success\n", uniq_name); + return true; + } else { + LOGD("[%s] Registration failed\n", uniq_name); + return true; + } +} + +static GDBusConnection* dbus_connect(GBusType bus_type) { + GError *error = NULL; + GDBusConnection *conn = g_bus_get_sync(bus_type, NULL, &error); + if (!conn || error) { + LOGE("Failed to get the %s bus: %s\n", bus_type == G_BUS_TYPE_SESSION ? "session" : "system", + ERR_TEXT(error)); + if (error) + g_error_free(error); + return NULL; + } + return conn; +} + +static bool dbus_init(GBusType bus_type) +{ + introspection_data = g_dbus_node_info_new_for_xml(introspection_xml, NULL); + if (introspection_data == NULL) { + LOGE("Failed to init g_dbus_node_info_new_for_xml\n"); + return false; + } + + conn_system = dbus_connect(G_BUS_TYPE_SYSTEM); + if (!conn_system) + return false; + + uniq_name = g_dbus_connection_get_unique_name(conn_system); + if (!uniq_name) { + LOGE("Failed to get unique name\n"); + return false; + } + + conn_session = dbus_connect(bus_type); + if (!conn_session) + return false; + + name_ident = g_bus_own_name(G_BUS_TYPE_SYSTEM, AGENT_BUS_NAME, G_BUS_NAME_OWNER_FLAGS_NONE, + on_bus_acquired, NULL, name_lost_handler, NULL, NULL); + + return register_agent(conn_system); +} + +API_FUNCTION bool dumpsys_agent_run(GMainLoop *loop, GBusType bus_type) +{ + main_loop = loop; + bool result = dbus_init(bus_type); + return result; +} + +API_FUNCTION void dumpsys_agent_stop() +{ + free_objects(); +} diff --git a/src/agent/libdumpsys-agent.h b/src/agent/libdumpsys-agent.h new file mode 100644 index 0000000..14796ca --- /dev/null +++ b/src/agent/libdumpsys-agent.h @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2019-2020 Samsung Electronics Co., Ltd. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LIBDUMPSYS_AGENT_H__ +#define __LIBDUMPSYS_AGENT_H__ + +#include +#include + +bool dumpsys_agent_run(GMainLoop *loop, GBusType bus_type); +void dumpsys_agent_stop(); + +#endif // __LIBDUMPSYS_AGENT_H__ diff --git a/src/client-api/dumpsys-system.c b/src/client-api/dumpsys-system.c index ffb4f24..a3c7fb2 100644 --- a/src/client-api/dumpsys-system.c +++ b/src/client-api/dumpsys-system.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019 Samsung Electronics Co., Ltd. + * Copyright (c) 2019-2020 Samsung Electronics Co., Ltd. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the License); * you may not use this file except in compliance with the License. @@ -43,19 +43,12 @@ #define DCA_ERROR_GETAPPID 3 #define DCA_ERROR_UNIXFDLISTEMPTY 4 -static dumpsys_dump_cb dump_cb; +static dumpsys_system_dump_cb dump_cb; static GDBusNodeInfo *introspection_data; static GDBusConnection *connection; static guint reg_id, own_id; static char *service_name; -#define ERROR_OK 0 -#define ERROR_INTERNAL 1 -#define ERROR_BINARY_NAME_ERROR 2 -#define ERROR_APPLICATION_NAME_ERROR 3 - -#define API_FUNCTION __attribute__((visibility("default"))) - static const gchar introspection_xml[] = "" "" @@ -72,16 +65,20 @@ struct DumpData { char **argv; }; -static int get_name(char **name) +static bool get_name(char **name) { assert(name); - if (service_name != NULL) { - *name = strdup(service_name); - return ERROR_OK; + if (service_name == NULL) + return false; + + *name = strdup(service_name); + if (*name == NULL) { + LOGE("malloc error: %m\n"); + return false; } - return ERROR_APPLICATION_NAME_ERROR; + return true; } static void dump_thread_cb(GTask *task, gpointer source_object, gpointer task_data, GCancellable *cancellable) @@ -116,13 +113,13 @@ static void dump_handler(GDBusMethodInvocation *invocation) GDBusMessage *msg = g_dbus_method_invocation_get_message(invocation); if (msg == NULL) { - LOGE("g_dbus_method_invocation_get_message() error"); + LOGE("g_dbus_method_invocation_get_message() error\n"); return; } GUnixFDList *fd_list = g_dbus_message_get_unix_fd_list(msg); if (fd_list == NULL) { - LOGE("No file descriptor provided"); + LOGE("No file descriptor provided\n"); g_dbus_method_invocation_return_error(invocation, DCA_ERROR, DCA_ERROR_UNIXFDLISTEMPTY, @@ -134,7 +131,7 @@ static void dump_handler(GDBusMethodInvocation *invocation) int fd = g_unix_fd_list_get(fd_list, 0, &error); if (fd == -1) { - LOGE("g_unix_fd_list_get() error: %s", error ? error->message : "(none)"); + LOGE("g_unix_fd_list_get() error: %s\n", error ? error->message : "(none)"); g_dbus_method_invocation_return_gerror(invocation, error); if (error) g_error_free(error); @@ -144,6 +141,14 @@ static void dump_handler(GDBusMethodInvocation *invocation) GVariant *body = g_dbus_message_get_body(msg); struct DumpData *dump_data = malloc(sizeof(struct DumpData)); + if (dump_data == NULL) { + LOGE("malloc error: %m\n"); + g_dbus_method_invocation_return_error(invocation, + DCA_ERROR, + DCA_ERROR_UNSPECIFIED_ERROR, + "memory allocation error"); + return; + } dump_data->fd = fd; dump_data->argc = get_args(body, &dump_data->argv); @@ -151,7 +156,8 @@ static void dump_handler(GDBusMethodInvocation *invocation) g_task_set_task_data(task, dump_data, NULL); g_task_run_in_thread(task, dump_thread_cb); g_object_unref(task); - g_dbus_method_invocation_return_value(invocation, g_variant_new("(i)", ERROR_OK)); + g_dbus_method_invocation_return_value(invocation, + g_variant_new("(i)", DUMP_SUCCESS)); } static void method_call_handler(GDBusConnection *conn, @@ -163,7 +169,7 @@ static void method_call_handler(GDBusConnection *conn, GDBusMethodInvocation *invocation, gpointer user_data) { - if (g_strcmp0(method_name, "Dump") == 0) + if (g_strcmp0(method_name, METHOD_DUMP) == 0) dump_handler(invocation); } @@ -183,7 +189,7 @@ static bool register_object(GDBusConnection *conn) &error); if (!reg_id) { - LOGE("g_dbus_connection_register_object() error: %s", error ? error->message : "(none)"); + LOGE("g_dbus_connection_register_object() error: %s\n", error ? error->message : "(none)"); if (error) g_error_free(error); return false; @@ -191,30 +197,29 @@ static bool register_object(GDBusConnection *conn) return true; } -static bool dbus_init() +static bool dbus_init(const GBusType bus_type) { introspection_data = g_dbus_node_info_new_for_xml(introspection_xml, NULL); if (introspection_data == NULL) { - LOGE("g_dbus_node_info_new_for_xml() error"); + LOGE("g_dbus_node_info_new_for_xml() error\n"); return false; } GError *error = NULL; - GBusType default_bus_type = G_BUS_TYPE_SYSTEM; - connection = g_bus_get_sync(default_bus_type, NULL, &error); + connection = g_bus_get_sync(bus_type, NULL, &error); if (!connection || error != NULL) { - LOGE("connect to the bus error: %s", error ? error->message : "(none)"); + LOGE("connect to the bus error: %s\n", error ? error->message : "(none)"); if (error) g_error_free(error); return false; } char name_with_prefix[DBUS_BUS_NAME_MAX_LEN]; - int ret = sprintf(name_with_prefix, "%s.%s", DUMPSYS_SERVICE_NAME_PREFIX, service_name); - if (ret < 0 || ret >= sizeof(name_with_prefix)) { - LOGE("sprintf error: %m"); + int ret = snprintf(name_with_prefix, sizeof(name_with_prefix), "%s.%s", DUMPSYS_SERVICE_NAME_PREFIX, service_name); + if (ret < 0) { + LOGE("snprintf error: %m\n"); return false; } @@ -244,39 +249,38 @@ static bool dbus_close() return res; } -static int register_callback(dumpsys_dump_cb callback, const char *name) +static int register_callback(dumpsys_system_dump_cb callback, const char *name) { assert(callback); if (name == NULL) { - int ret = get_name(&service_name); - if (ret != ERROR_OK) { - LOGE("get_name error"); + if (!get_name(&service_name)) { + LOGE("get_name error\n"); return TIZEN_ERROR_INVALID_OPERATION; } } else { service_name = strdup(name); if (service_name == NULL) { - LOGE("strdup error: %m"); + LOGE("strdup error: %m\n"); return TIZEN_ERROR_INVALID_OPERATION; } } - bool result = dbus_init(); + bool result = dbus_init(G_BUS_TYPE_SYSTEM); if (result) dump_cb = callback; return result ? TIZEN_ERROR_NONE : TIZEN_ERROR_IO_ERROR; } -int API_FUNCTION dumpsys_system_register_dump_cb(dumpsys_dump_cb callback, const char *name, void **handler) +int API_FUNCTION dumpsys_system_register_dump_cb(dumpsys_system_dump_cb callback, const char *name, void **handler) { if (dump_cb != NULL) { - LOGE("register_dump_cb(): already registered"); + LOGE("register_dump_cb(): already registered\n"); return TIZEN_ERROR_ALREADY_IN_PROGRESS; } if (callback == NULL || name == NULL || handler == NULL) { - LOGE("register_me(): invalid parameter"); + LOGE("register_me(): invalid parameter\n"); return TIZEN_ERROR_INVALID_PARAMETER; } @@ -289,12 +293,12 @@ int API_FUNCTION dumpsys_system_register_dump_cb(dumpsys_dump_cb callback, const int API_FUNCTION dumpsys_system_unregister_dump_cb(void *handler) { if (handler == NULL) { - LOGE("unregister_dump_cb(): parameter is NULL"); + LOGE("unregister_dump_cb(): parameter is NULL\n"); return TIZEN_ERROR_INVALID_PARAMETER; } if (handler != (void*)dump_cb) { - LOGE("unregister_dump_cb(): handler not registered"); + LOGE("unregister_dump_cb(): handler not registered\n"); return TIZEN_ERROR_ALREADY_IN_PROGRESS; } diff --git a/src/client-api/dumpsys-system.h b/src/client-api/dumpsys-system.h index 579bb3f..1abadad 100644 --- a/src/client-api/dumpsys-system.h +++ b/src/client-api/dumpsys-system.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2019 Samsung Electronics Co., Ltd. + * Copyright (c) 2019-2020 Samsung Electronics Co., Ltd. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the License); * you may not use this file except in compliance with the License. @@ -23,7 +23,7 @@ * @param[in] argc Argument count * @param[in] argv Array of arguments */ -typedef int (*dumpsys_dump_cb)(const int fd, const int argc, char **argv); +typedef int (*dumpsys_system_dump_cb)(const int fd, const int argc, char **argv); /** * @brief Register the callback. @@ -36,7 +36,7 @@ typedef int (*dumpsys_dump_cb)(const int fd, const int argc, char **argv); * @retval TIZEN_ERROR_INVALID_OPERATION Internal error * @retval TIZEN_ERROR_IO_ERROR Error during DBus registration */ -extern int dumpsys_system_register_dump_cb(dumpsys_dump_cb callback, const char *name, void **handler); +extern int dumpsys_system_register_dump_cb(dumpsys_system_dump_cb callback, const char *name, void **handler); /** * @brief Unregister the callback. diff --git a/src/dumpsys/CMakeLists.txt b/src/dumpsys/CMakeLists.txt index 19af459..44f5dfc 100644 --- a/src/dumpsys/CMakeLists.txt +++ b/src/dumpsys/CMakeLists.txt @@ -3,32 +3,47 @@ PROJECT(dumpsys C) INCLUDE(GNUInstallDirs) INCLUDE(FindPkgConfig) + +SET(DUMPSYS_BIN "dumpsys") +SET(DUMPSYS_LIB "libdumpsys") + find_package(PkgConfig REQUIRED) -pkg_check_modules(dumpsys_pkgs REQUIRED +pkg_check_modules(libdumpsys_pkgs REQUIRED + dlog gio-2.0 glib-2.0 gio-unix-2.0) INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/src/shared) -FOREACH(flag ${dumpsys_pkgs_CFLAGS}) +INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/src/dumpsys) + +FOREACH(flag ${libdumpsys_pkgs_CFLAGS}) SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} ${flag}") ENDFOREACH(flag) -FOREACH(flag ${dumpsys_pkgs_LDFLAGS}) +FOREACH(flag ${libdumpsys_pkgs_LDFLAGS}) SET(EXTRA_LDFLAGS "${EXTRA_CFLAGS} ${flag}") ENDFOREACH(flag) -SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_CFLAGS} -fPIE -Wno-unused-function -Wno-unused-const-variable") +SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_CFLAGS} -Wall -Wextra -Werror -Wno-unused-function -Wno-unused-const-variable") -SET(DUMPSYS dumpsys.c) +ADD_LIBRARY(${DUMPSYS_LIB} SHARED libdumpsys.c) +TARGET_LINK_LIBRARIES(${DUMPSYS_LIB} PUBLIC ${libdumpsys_pkgs_LIBRARIES}) +SET_TARGET_PROPERTIES(${DUMPSYS_LIB} PROPERTIES + SOVERSION 1 + PUBLIC_HEADER libdumpsys.h + OUTPUT_NAME dumpsys) -LINK_DIRECTORIES(${CMAKE_BINARY_DIR}) -ADD_EXECUTABLE(dumpsys ${DUMPSYS}) -TARGET_LINK_LIBRARIES(dumpsys PUBLIC ${dumpsys_pkgs_LIBRARIES}) +ADD_EXECUTABLE(${DUMPSYS_BIN} dumpsys.c) +TARGET_COMPILE_OPTIONS(${DUMPSYS_BIN} PUBLIC -fPIE) +TARGET_LINK_LIBRARIES(${DUMPSYS_BIN} PUBLIC libdumpsys -pie) -INSTALL (TARGETS dumpsys DESTINATION ${CMAKE_INSTALL_BINDIR} +CONFIGURE_FILE(dumpsys.pc.in dumpsys.pc @ONLY) + +INSTALL (TARGETS ${DUMPSYS_BIN} DESTINATION ${CMAKE_INSTALL_BINDIR} PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE) -INSTALL (FILES dumpsys-run.conf DESTINATION /usr/lib/tmpfiles.d/ - PERMISSIONS OWNER_READ OWNER_WRITE) -INSTALL (FILES dumpsys.conf DESTINATION /etc/dbus-1/system.d/ +INSTALL (TARGETS ${DUMPSYS_LIB} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) +INSTALL (FILES dumpsys.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig) +INSTALL (FILES dumpsys-run.conf DESTINATION ${CMAKE_INSTALL_LIBDIR}/tmpfiles.d/ PERMISSIONS OWNER_READ OWNER_WRITE) diff --git a/src/dumpsys/dumpsys.c b/src/dumpsys/dumpsys.c index 819a7b5..c4049ae 100644 --- a/src/dumpsys/dumpsys.c +++ b/src/dumpsys/dumpsys.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019 Samsung Electronics Co., Ltd. + * Copyright (c) 2019-2020 Samsung Electronics Co., Ltd. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the License); * you may not use this file except in compliance with the License. @@ -14,38 +14,32 @@ * limitations under the License. */ -#include -#include -#include -#include +#include +#include +#include +#include #include #include #include +#include +#include +#include +#include #include -#include #include -#include -#include -#include -#include -#include +#include +#include +#include +#include #include "common.h" #define BUFF_SIZE 4096 -#define FIFO_BASE_DIR "/run/dumpsys/priv/fifo" -#define METHOD_DUMP "Dump" -#define SERVICE_NAME_MAX_LEN 255 - -static int out_fd = -1; -static GVariant *dump_args; -static void* copy_data(void *arg) +static bool copy_data(int dest_fd, int source_fd) { - int source_fd = ((int*)arg)[0]; - int dest_fd = ((int*)arg)[1]; - ssize_t read_count = 0; + while (read_count >= 0) { char buff[BUFF_SIZE]; read_count = read(source_fd, buff, sizeof(buff)); @@ -58,227 +52,20 @@ static void* copy_data(void *arg) sleep(0.1); continue; } - printf("read error: %m\n"); - goto out; + printf("Read error: %m\n"); + return false; } ssize_t bytes_to_write = read_count; while (bytes_to_write > 0) { ssize_t write_count = write(dest_fd, buff, read_count); if (write_count == -1) { - printf("write error: %m\n"); - goto out; + printf("Write error: %m\n"); + return false; } bytes_to_write -= write_count; } } -out: - return NULL; -} - -static bool make_fifo(int *write_fd, int *read_fd) -{ - bool result = false; - char *fifo_path = NULL; - - if (asprintf(&fifo_path, "%s/dumpsys_XXXXXX", FIFO_BASE_DIR) == -1) { - printf("asprintf error: %m\n"); - return result; - } - uid_t ruid, euid, suid; - if (getresuid(&ruid, &euid, &suid) == -1) { - printf("getresuid error: %m\n"); - goto end_free; - } - - if (seteuid(suid) == -1) { - printf("setuid error: %m\n"); - goto end_free; - } - - if (mktemp(fifo_path) == NULL) { - printf("mktemp error: %m\n"); - goto end; - } - - if (mkfifo(fifo_path, 0600) == -1) { - printf("mkfifo error: %m\n"); - goto end; - } - - /* - * We must open for reading before open for writing, because of "No such device or address" error. - * Read must have O_NONBLOCK flag, otherwise open will block us until - * someone open fifo for writing. - */ - *read_fd = open(fifo_path, O_RDONLY | O_NONBLOCK); - if (*read_fd == -1) { - printf("open fifo for reading error: %m\n"); - goto end; - } - - *write_fd = open(fifo_path, O_WRONLY | O_NONBLOCK); - if (*write_fd == -1) { - printf("open fifo for writing error: %m\n"); - close(*read_fd); - goto end; - } - - if (unlink(fifo_path) == -1) { - printf("unlink '%s' error: %m\n", fifo_path); - close(*read_fd); - close(*write_fd); - goto end; - } - - int old_flags = fcntl(*read_fd, F_GETFL); - if (old_flags == -1) { - printf("fcntl() GETFL error: %m\n"); - close(*read_fd); - close(*write_fd); - goto end; - } - - fcntl(*read_fd, F_SETFL, old_flags & ~O_NONBLOCK); - - result = true; -end: - if (seteuid(euid) == -1) { - printf("setuid error: %m\n"); - result = false; - } -end_free: - free(fifo_path); - - return result; -} - -static void call_dump(const gchar *name, const gchar *application_name, GDBusConnection *connection) -{ - GError *error = NULL; - - GDBusMessage *method_call_message = g_dbus_message_new_method_call(name, - DUMPSYS_PATH, - DUMPSYS_NAME, - METHOD_DUMP); - - g_dbus_message_set_body(method_call_message, dump_args); - - GUnixFDList *fd_list = g_unix_fd_list_new(); - - int write_fd, read_fd; - if (!make_fifo(&write_fd, &read_fd)) { - return; - } - - g_unix_fd_list_append(fd_list, write_fd, &error); - if (error != NULL) { - dprintf(STDERR_FILENO, "g_unix_fd_list_append() error: %s\n", error->message); - goto out_close; - } - - pthread_t copy_thread; - int fd_pair2[2]; - fd_pair2[0] = read_fd; - fd_pair2[1] = out_fd; - if (pthread_create(©_thread, NULL, copy_data, &fd_pair2[0]) != 0) { - printf("pthread_create error: %m\n"); - goto out_unref_fdlist; - } - - g_dbus_message_set_unix_fd_list(method_call_message, fd_list); - - GDBusMessage *repl = g_dbus_connection_send_message_with_reply_sync(connection, - method_call_message, - G_DBUS_SEND_MESSAGE_FLAGS_NONE, - -1, - NULL, - NULL, - &error); - - if (repl != NULL && g_dbus_message_get_message_type(repl) == G_DBUS_MESSAGE_TYPE_ERROR) - g_dbus_message_to_gerror(repl, &error); - - if (error != NULL) { - printf("Send message error: %s\n", error->message); - g_error_free(error); - } - - g_object_unref(method_call_message); - g_object_unref(repl); - - if (pthread_join(copy_thread, NULL) != 0) - printf("pthread_join error: %m\n"); - -out_unref_fdlist: - g_object_unref(fd_list); -out_close: - close(write_fd); - close(read_fd); -} - -static void call_dump_for_list_of_services(GDBusConnection *connection, GList* list_of_services) -{ - GList *element = g_list_first(list_of_services); - - while (element != NULL) { - char *name = (gchar *)element->data; - char service_name[SERVICE_NAME_MAX_LEN]; - - snprintf(service_name, sizeof(service_name), "%s.%s", DUMPSYS_SERVICE_NAME_PREFIX, name); - call_dump(service_name, name, connection); - - element = g_list_next(element); - } -} - - -static GDBusConnection* make_connection(GBusType bus_type) -{ - GError *error = NULL; - GDBusConnection *connection = NULL; - - connection = g_bus_get_sync(bus_type, NULL, &error); - if (connection == NULL || error != NULL) - printf("make_connection error: %s\n", error->message); - - return connection; -} - -static void process_bus(GBusType bus_type, GList *list_of_services) -{ - GDBusConnection *connection = make_connection(bus_type); - if (connection == NULL) { - printf("can't connect\n"); - return; - } - - call_dump_for_list_of_services(connection, list_of_services); - - g_object_unref(connection); -} - -static void process_each_bus(GList *list_of_services) -{ - uid_t ruid, euid, suid; - if (getresuid(&ruid, &euid, &suid) == -1) { - printf("getresuid error: %m\n"); - return; - } - - process_bus(G_BUS_TYPE_SYSTEM, list_of_services); -} - -static void set_dump_args(int start, int argc, char *argv[]) -{ - GVariantBuilder builder; - g_variant_builder_init(&builder, G_VARIANT_TYPE("(as)")); - g_variant_builder_open(&builder, G_VARIANT_TYPE("as")); - - for (int i = start; i < argc; i++) - g_variant_builder_add(&builder, "s", argv[i]); - - g_variant_builder_close(&builder); - dump_args = g_variant_builder_end(&builder); + return true; } static void print_help(char **argv) @@ -291,7 +78,8 @@ int main(int argc, char *argv[]) { int opt; char *filename = NULL; - out_fd = STDOUT_FILENO; + int out_fd = STDOUT_FILENO; + bool result = false; while ((opt = getopt(argc, argv, "o:")) != -1) { switch(opt) { @@ -303,29 +91,24 @@ int main(int argc, char *argv[]) if (optind >= argc) { print_help(argv); - return EXIT_FAILURE; + goto end; } if (filename != NULL) { out_fd = open(filename, O_WRONLY | O_CREAT, 0600); if (out_fd == -1) { printf("Can not open file '%s': %m\n", filename); - return EXIT_FAILURE; + goto end; } } - GList *list_of_services = NULL; - int i = optind; + char *service_name = argv[i]; - if (i < argc) - list_of_services = g_list_append(list_of_services, strdup(argv[i])); - - set_dump_args(i+1, argc, argv); + int in_fd; + if (dumpsys_dump(service_name, argc-i-1, &argv[i+1], &in_fd) == TIZEN_ERROR_NONE) + result = copy_data(out_fd, in_fd); - process_each_bus(list_of_services); - - g_list_free_full(list_of_services, free); - - return EXIT_SUCCESS; +end: + return result ? EXIT_SUCCESS : EXIT_FAILURE; } diff --git a/src/dumpsys/dumpsys.conf b/src/dumpsys/dumpsys.conf deleted file mode 100644 index de9b8ee..0000000 --- a/src/dumpsys/dumpsys.conf +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - diff --git a/src/dumpsys/dumpsys.pc.in b/src/dumpsys/dumpsys.pc.in new file mode 100644 index 0000000..cba8db6 --- /dev/null +++ b/src/dumpsys/dumpsys.pc.in @@ -0,0 +1,11 @@ +prefix=/usr +exec_prefix=${prefix} +includedir=${prefix}/include +libdir=${exec_prefix}/lib + +Name: dumpsys +Description: Library for operating on process-provided diagnostic data +Version: @VERSION@ +Cflags: -I${includedir} +Libs: -L${libdir} -ldumpsys + diff --git a/src/dumpsys/libdumpsys.c b/src/dumpsys/libdumpsys.c new file mode 100644 index 0000000..5875b03 --- /dev/null +++ b/src/dumpsys/libdumpsys.c @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2019-2020 Samsung Electronics Co., Ltd. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef LOG_TAG +#define LOG_TAG "LIBDUMPSYS" +#endif + +#include + +#include "common.h" + +static GVariantBuilder* prepare_dump_args(int argc, char *argv[]) +{ + GVariantBuilder *dump_args = g_variant_builder_new(G_VARIANT_TYPE("as")); + + assert(dump_args); + + for (int i = 0; i < argc; i++) + g_variant_builder_add(dump_args, "s", argv[i]); + + return dump_args; +} + +static int call_dump(GDBusConnection *conn, + const char *name, + GVariantBuilder *args, + int *out_fd) +{ + int result = TIZEN_ERROR_NONE; + *out_fd = -1; + GError *error = NULL; + + GDBusMessage *message = g_dbus_message_new_method_call(SERVICE_BUS_NAME, + SERVICE_OBJECT_PATH, + SERVICE_INTERFACE, + METHOD_DUMP); + + GVariant *message_tuple = g_variant_new("(ass)", args, name); + g_dbus_message_set_body(message, message_tuple); + + GDBusMessage *repl = g_dbus_connection_send_message_with_reply_sync(conn, + message, + G_DBUS_SEND_MESSAGE_FLAGS_NONE, + SERVICE_TIMEOUT_MS, + NULL, + NULL, + &error); + + if (repl != NULL && g_dbus_message_get_message_type(repl) == G_DBUS_MESSAGE_TYPE_ERROR) + g_dbus_message_to_gerror(repl, &error); + + if (error != NULL) { + LOGE("Send message error: %s\n", ERR_TEXT(error)); + g_error_free(error); + result = TIZEN_ERROR_NETWORK_UNREACHABLE; + goto end; + } + + int call_result; + GVariant *body_value = g_dbus_message_get_body(repl); + g_variant_get(body_value, "(i)", &call_result); + if (call_result == DUMP_FAIL) { + LOGE("Dump failed\n"); + result = TIZEN_ERROR_NO_ROUTE_TO_HOST; + goto end; + } + + GUnixFDList *fd_list = g_dbus_message_get_unix_fd_list(repl); + if (fd_list == NULL) { + LOGE("Reply doesn't contain file descriptor\n"); + result = TIZEN_ERROR_INVALID_OPERATION; + goto end; + } + + *out_fd = g_unix_fd_list_get(fd_list, 0, &error); + if (error != NULL) { + LOGE("File descriptor error: %s\n", error->message); + g_error_free(error); + result = TIZEN_ERROR_INVALID_OPERATION; + } + + g_object_unref(fd_list); + +end: + g_object_unref(message); + + return result; +} + +static GDBusConnection* make_connection() +{ + GError *error = NULL; + GDBusConnection *connection = NULL; + + connection = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &error); + if (connection == NULL || error != NULL) + LOGE("Connection error: %s\n", ERR_TEXT(error)); + + return connection; +} + +API_FUNCTION int dumpsys_dump(char *service_name, int argc, char **argv, int *out_fd) +{ + if (service_name == NULL || argc < 0 || argv == NULL || out_fd == NULL) + return TIZEN_ERROR_INVALID_PARAMETER; + + GDBusConnection *conn = make_connection(); + if (conn == NULL) + return TIZEN_ERROR_CONNECTION_REFUSED; + + GVariantBuilder *args = prepare_dump_args(argc, argv); + return call_dump(conn, service_name, args, out_fd); +} diff --git a/src/dumpsys/libdumpsys.h b/src/dumpsys/libdumpsys.h new file mode 100644 index 0000000..9cf80f2 --- /dev/null +++ b/src/dumpsys/libdumpsys.h @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2019-2020 Samsung Electronics Co., Ltd. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __LIBDUMPSYS_H__ +#define __LIBDUMPSYS_H__ + +int dumpsys_dump(char *service_name, int argc, char **argv, int *out_fd); + +#endif // __LIBDUMPSYS_H__ diff --git a/src/service/CMakeLists.txt b/src/service/CMakeLists.txt new file mode 100644 index 0000000..6d443c1 --- /dev/null +++ b/src/service/CMakeLists.txt @@ -0,0 +1,38 @@ +CMAKE_MINIMUM_REQUIRED(VERSION 2.6) +PROJECT(DUMPSYS_SERVICE C) + +INCLUDE(GNUInstallDirs) +INCLUDE(FindPkgConfig) +find_package(PkgConfig REQUIRED) +pkg_check_modules(dumpsys_service_pkgs REQUIRED + dlog + gio-2.0 + glib-2.0 + gio-unix-2.0) + +INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/src/shared) +INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/src/agent) +FOREACH(flag ${dumpsys_service_pkgs_CFLAGS}) + SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} ${flag}") +ENDFOREACH(flag) + +FOREACH(flag ${dumpsys_service_pkgs_LDFLAGS}) + SET(EXTRA_LDFLAGS "${EXTRA_CFLAGS} ${flag}") +ENDFOREACH(flag) + +SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_CFLAGS} -pthread -fPIE -Wno-unused-function -Wno-unused-const-variable") + +SET(DUMPSYS_SERVICE dumpsys-service.c) + +LINK_DIRECTORIES(${CMAKE_BINARY_DIR}) +LINK_DIRECTORIES(${CMAKE_SOURCE_DIR}/src/agent) +ADD_EXECUTABLE(dumpsys-service ${DUMPSYS_SERVICE}) +ADD_DEPENDENCIES(dumpsys-service libdumpsys-agent) +TARGET_LINK_LIBRARIES(dumpsys-service PUBLIC ${dumpsys_service_pkgs_LIBRARIES} -ldumpsys-agent) + +INSTALL (TARGETS dumpsys-service DESTINATION ${CMAKE_INSTALL_BINDIR} + PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE) +INSTALL (FILES dumpsys-service.conf DESTINATION /etc/dbus-1/system.d/ + PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE) +INSTALL (FILES dumpsys-service.service DESTINATION ${CMAKE_INSTALL_LIBDIR}/systemd/system/ + PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE) diff --git a/src/service/dumpsys-service.c b/src/service/dumpsys-service.c new file mode 100644 index 0000000..7e7c54d --- /dev/null +++ b/src/service/dumpsys-service.c @@ -0,0 +1,508 @@ +/* + * Copyright (c) 2019-2020 Samsung Electronics Co., Ltd. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "libdumpsys-agent.h" + +#ifndef LOG_TAG +#define LOG_TAG "DUMPSYS_SERVICE" +#endif + +#include + +#include "common.h" + +#define FIFO_BASE_DIR "/run/dumpsys/priv/fifo" + +static GDBusNodeInfo *introspection_data; +static const gchar introspection_xml[] = +"" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" +""; + +static GMainLoop* loop; +static GList *agents_list; +static GTask *agent_task; +static bool agent_success; + +static bool reset_fd_flag(int fd, int flag) +{ + assert(fd >= 0); + int old_flags = fcntl(fd, F_GETFL); + if (old_flags == -1) { + LOGE("fcntl() GETFL error: %m\n"); + return false; + } + if (fcntl(fd, F_SETFL, old_flags & ~flag) == -1) { + LOGE("fcntl() SETFL error: %m\n"); + return false; + } + return true; +} + +static bool make_fifo(int *write_fd, int *read_fd) +{ + assert(write_fd); + assert(read_fd); + *write_fd = -1; + *read_fd = -1; + +#define CLOSE_AND_RESET(fd) { \ + if (*fd >= 0) { \ + close(*fd); \ + *fd = -1; \ + } \ + } + + bool result = false; + char *fifo_path = NULL; + + if (asprintf(&fifo_path, "%s/dumpsys_XXXXXX", FIFO_BASE_DIR) == -1) { + LOGE("asprintf error: %m\n"); + return result; + } + + if (mktemp(fifo_path) == NULL) { + LOGE("mktemp error: %m\n"); + goto end; + } + + if (mkfifo(fifo_path, 0600) == -1) { + LOGE("mkfifo error: %m\n"); + goto end; + } + + /* + * We must open for reading before open for writing, because of "No such device or address" error. + * Read must have O_NONBLOCK flag, otherwise open will block us until + * someone open fifo for writing. + */ + bool open_error = false; + *read_fd = open(fifo_path, O_RDONLY | O_NONBLOCK); + if (*read_fd == -1) { + LOGE("open fifo for reading error: %m\n"); + open_error = true; + } else { + *write_fd = open(fifo_path, O_WRONLY | O_NONBLOCK); + if (*write_fd == -1) { + LOGE("open fifo for writing error: %m\n"); + CLOSE_AND_RESET(read_fd); + open_error = true; + } + } + + if (unlink(fifo_path) == -1) { + LOGE("unlink '%s' error: %m\n", fifo_path); + CLOSE_AND_RESET(read_fd); + CLOSE_AND_RESET(write_fd); + goto end; + } + + if (open_error) + goto end; + + int old_flags = fcntl(*read_fd, F_GETFL); + if (old_flags == -1) { + LOGE("fcntl() GETFL error: %m\n"); + CLOSE_AND_RESET(read_fd); + CLOSE_AND_RESET(write_fd); + goto end; + } + + /* + * After all we want blocking file descriptors so that clients don't + * have to handle EAGAIN error + */ + if (!reset_fd_flag(*read_fd, O_NONBLOCK) || + !reset_fd_flag(*write_fd, O_NONBLOCK)) { + CLOSE_AND_RESET(read_fd); + CLOSE_AND_RESET(write_fd); + goto end; + } + + result = true; +end: + free(fifo_path); + + return result; +#undef CLOSE_AND_RESET +} + +static GUnixFDList* prepare_unix_fd_list(int fd) +{ + assert(fd >= 0); + + GError *error = NULL; + GUnixFDList *fd_list = g_unix_fd_list_new(); + + assert(fd_list); + + if (g_unix_fd_list_append(fd_list, fd, &error) == -1) { + LOGE("g_unix_fd_list_append error: %s\n", ERR_TEXT(error)); + return NULL; + } + return fd_list; +} + +static bool forward_dump(GDBusConnection *conn, GVariant *body, int write_fd) +{ + assert(conn); + assert(body); + assert(write_fd >= 0); + + GList *element = agents_list; + bool sent = false; + + while (element != NULL && !sent) { + bool invalid_agent = false; + + LOGD("Forwarding the dump call to agent %s\n", (char *)element->data); + + GUnixFDList *fd_list_write = prepare_unix_fd_list(write_fd); + if (fd_list_write == NULL) + goto end; + + GError *error = NULL; + GVariant *reply_value = g_dbus_connection_call_with_unix_fd_list_sync( + conn, + (char *)element->data, + AGENT_OBJECT_PATH, + AGENT_INTERFACE, + METHOD_DUMP, + body, + G_VARIANT_TYPE("(i)"), + G_DBUS_SEND_MESSAGE_FLAGS_NONE, + SERVICE_TIMEOUT_MS, + fd_list_write, + NULL, + NULL, + &error); + g_object_unref(fd_list_write); + + if (error == NULL) { + int result = DUMP_FAIL; + + if (reply_value != NULL) + g_variant_get(reply_value, "(i)", &result); + + sent = result == DUMP_SUCCESS; + } else { + if (error->domain == g_dbus_error_quark() && + error->code == G_DBUS_ERROR_SERVICE_UNKNOWN) + invalid_agent = true; + else + LOGE("Dump method call error: %s\n", ERR_TEXT(error)); + + g_error_free(error); + } + + if (sent) { + char *service_name; + g_variant_get(body, "(ass)", NULL, &service_name); + LOGD("Service %s was found by %s agent.\n", + service_name, + (char *)element->data); + free(service_name); + } else { + GList *prev_element = element; + element = g_list_next(element); + + if (invalid_agent) { + LOGD("Removing agent %s\n", (char *)prev_element->data); + agents_list = g_list_remove_link(agents_list, prev_element); + + g_list_free_full(prev_element, free); + } + } + } +end: + return sent; +} + +static void register_call_handler(GDBusMethodInvocation *invocation, const gchar *agent) +{ + assert(invocation); + assert(agent); + + LOGD("The new agent has been registered: %s\n", agent); + char *name = strdup(agent); + + if (name == NULL) + LOGE("strdup error: %m\n"); + else + agents_list = g_list_append(agents_list, name); + + g_dbus_method_invocation_return_value(invocation, g_variant_new("(b)", name != NULL)); +} + +static bool attach_unix_fd(GDBusMessage *message, int fd) +{ + assert(message); + assert(fd >= 0); + + GUnixFDList *fd_list = prepare_unix_fd_list(fd); + + if (fd_list == NULL) + return false; + + g_dbus_message_set_unix_fd_list(message, fd_list); + g_object_unref(fd_list); + + return true; +} + +static void send_reply(GDBusConnection *conn, + GDBusMethodInvocation *invocation, + GDBusMessage *incoming_message, + int fd, + bool result) +{ + GDBusMessage *message = g_dbus_message_new_method_reply(incoming_message); + + if (result && !attach_unix_fd(message, fd)) { + g_dbus_method_invocation_return_error(invocation, + CS_ERROR, + CS_ERR_INTERNAL, + "Prepare fd list error"); + goto end; + } + + GError *error = NULL; + g_dbus_message_set_body(message, g_variant_new("(i)", result ? DUMP_SUCCESS : DUMP_FAIL)); + + if (!g_dbus_connection_send_message(conn, + message, + G_DBUS_SEND_MESSAGE_FLAGS_NONE, + NULL, + &error)) { + LOGE("Send reply message error: %s\n", ERR_TEXT(error)); + if (error) + g_error_free(error); + } + +end: + g_object_unref(message); +} + +static void dump_call_handler(GDBusConnection *conn, + GVariant *parameters, + GDBusMethodInvocation *invocation) +{ + if (!g_variant_is_of_type(parameters, G_VARIANT_TYPE("(ass)"))) { + LOGE("Parameters are not of the correct type (ass)\n"); + g_dbus_method_invocation_return_error(invocation, + CS_ERROR, + CS_ERR_PARAM, + "Parameters are not of the correct type (ass)"); + return; + } + + int read_fd = -1, write_fd = -1; + make_fifo(&write_fd, &read_fd); + + if (write_fd == -1 || read_fd == -1) { + g_dbus_method_invocation_return_error(invocation, + CS_ERROR, + CS_ERR_INTERNAL, + "Can not make fifo"); + goto end; + } + + GDBusMessage *incoming_message = g_dbus_method_invocation_get_message(invocation); + GVariant *body = g_dbus_message_get_body(incoming_message); + bool result = forward_dump(conn, body, write_fd); + + send_reply(conn, invocation, incoming_message, read_fd, result); +end: + if (write_fd >= 0) + close(write_fd); + if (read_fd >= 0) + close(read_fd); +} + +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) +{ + if (g_strcmp0(method_name, METHOD_REGISTER) == 0) + register_call_handler(invocation, sender); + else if (g_strcmp0(method_name, METHOD_DUMP) == 0) + dump_call_handler(conn, parameters, invocation); +} + +static void run_agent_thread(GTask *task, + gpointer source_object, + gpointer task_data, + GCancellable *cancellable) +{ + bool result = true; + GMainContext *context = g_main_context_new(); + GMainLoop *agent_loop = g_main_loop_new(context, false); + + g_main_context_push_thread_default(context); + + if (dumpsys_agent_run(loop, G_BUS_TYPE_SYSTEM)) { + g_main_loop_run(agent_loop); + } else { + LOGE("Failed to run agent\n"); + result = false; + } + + g_main_context_pop_thread_default(context); + g_main_loop_unref(agent_loop); + g_main_context_unref(context); + g_task_return_boolean(task, result); +} + +static bool run_agent_finish(GAsyncResult *res, + GError **error) +{ + g_return_val_if_fail(g_task_is_valid(res, NULL), false); + return g_task_propagate_boolean(G_TASK(res), error); +} + +static void run_agent_callback(GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + GError *error = NULL; + bool result = run_agent_finish(res, &error); + if (!result) { + LOGE("Agent thread failed - exiting: %s\n", ERR_TEXT(error)); + if (error) + g_error_free(error); + g_main_loop_quit(loop); + } else { + LOGD("Agent thread success\n"); + agent_success = true; + } +} + +static void on_name_acquired(GDBusConnection *conn, + const gchar *name, + gpointer user_data) +{ + agent_task = g_task_new(NULL, NULL, run_agent_callback, NULL); + g_task_run_in_thread(agent_task, run_agent_thread); +} + +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; + + GError *error = NULL; + registration_id = g_dbus_connection_register_object(conn, + SERVICE_OBJECT_PATH, + introspection_data->interfaces[0], + &interface_vtable, NULL, NULL, &error); + if (registration_id == 0 || error) { + LOGE("Failed to g_dbus_connection_register_object: %s\n", ERR_TEXT(error)); + if (error) + g_error_free(error); + } +} + +static bool dbus_init(void) +{ + GError *error = NULL; + introspection_data = g_dbus_node_info_new_for_xml(introspection_xml, NULL); + if (introspection_data == NULL) { + LOGE("Failed to init g_dbus_node_info_new_for_xml\n"); + return false; + } + + GDBusConnection *conn = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &error); + if (!conn || error) { + LOGE("Failed to get dbus: %s\n", ERR_TEXT(error)); + if (error) + g_error_free(error); + return false; + } + + g_bus_own_name(G_BUS_TYPE_SYSTEM, + SERVICE_BUS_NAME, + G_BUS_NAME_OWNER_FLAGS_NONE, + on_bus_acquired, + on_name_acquired, + NULL, NULL, NULL); + return true; +} + +static void free_agents() +{ + GList *element = agents_list; + while (element != NULL) { + element = g_list_next(element); + g_list_free_full(g_list_previous(element), free); + } + g_list_free_full(agents_list, free); +} + +int main(int argc, char *argv[]) +{ + agent_success = false; + loop = g_main_loop_new(NULL, false); + + if (!dbus_init()) + goto end; + + g_main_loop_run(loop); +end: + g_main_loop_unref(loop); + + if (introspection_data != NULL) + g_dbus_node_info_unref(introspection_data); + + free_agents(); + + return agent_success ? EXIT_SUCCESS : EXIT_FAILURE; +} diff --git a/src/service/dumpsys-service.conf b/src/service/dumpsys-service.conf new file mode 100644 index 0000000..f5851f8 --- /dev/null +++ b/src/service/dumpsys-service.conf @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + diff --git a/src/service/dumpsys-service.service b/src/service/dumpsys-service.service new file mode 100644 index 0000000..b6ea95a --- /dev/null +++ b/src/service/dumpsys-service.service @@ -0,0 +1,9 @@ +[Unit] +Description=Dumpsys service +DefaultDependencies=false +After=dbus.socket systemd-tmpfiles-setup.service + +[Service] +Type=simple +User=log +ExecStart=/usr/bin/dumpsys-service diff --git a/src/shared/common.h b/src/shared/common.h index d622012..4384377 100644 --- a/src/shared/common.h +++ b/src/shared/common.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019 Samsung Electronics Co., Ltd. + * Copyright (c) 2019-2020 Samsung Electronics Co., Ltd. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the License); * you may not use this file except in compliance with the License. @@ -17,9 +17,30 @@ #ifndef __COMMON_H__ #define __COMMON_H__ +#define API_FUNCTION __attribute__((visibility("default"))) + #define DUMPSYS_PATH "/Org/Tizen/Dumpsys" #define DUMPSYS_NAME "org.tizen.dumpsys" #define DUMPSYS_INTERFACE DUMPSYS_NAME #define DUMPSYS_SERVICE_NAME_PREFIX DUMPSYS_NAME +#define AGENT_BUS_NAME "org.tizen.dumpsys.agent" +#define AGENT_INTERFACE "org.tizen.dumpsys.agent" +#define AGENT_OBJECT_PATH "/Org/Tizen/Dumpsys/Agent" +#define SERVICE_BUS_NAME "org.tizen.dumpsys.service" +#define SERVICE_INTERFACE "org.tizen.dumpsys.service" +#define SERVICE_OBJECT_PATH "/Org/Tizen/Dumpsys/Service" +#define SERVICE_TIMEOUT_MS 5000 +#define METHOD_DUMP "Dump" +#define METHOD_REGISTER "Register" + +#define CS_ERROR 1 +#define CS_ERR_PARAM 1 +#define CS_ERR_INTERNAL 2 + +#define DUMP_SUCCESS 1 +#define DUMP_FAIL 0 + +#define ERR_TEXT(error) (error ? error->message : "unknown error") + #endif diff --git a/tests/system/util/CMakeLists.txt b/tests/system/util/CMakeLists.txt index a7444ae..fd37c13 100644 --- a/tests/system/util/CMakeLists.txt +++ b/tests/system/util/CMakeLists.txt @@ -7,18 +7,17 @@ pkg_check_modules(dumpsys-system_pkgs REQUIRED capi-base-common dlog gio-2.0 - gio-unix-2.0 - dumpsys-system - pkgmgr-info) + gio-unix-2.0) -INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/../src/shared) +INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/src/shared) +INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/src/client-api) FOREACH(flag ${dumpsys-system_pkgs_CFLAGS}) SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} ${flag}") ENDFOREACH(flag) SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_CFLAGS} -fPIE -Wno-unused-function -Wno-unused-const-variable") -ADD_EXECUTABLE(test-app test-app.c) +ADD_EXECUTABLE(test-app test-app.c ${CMAKE_SOURCE_DIR}/src/client-api/dumpsys-system.c) TARGET_LINK_LIBRARIES(test-app PUBLIC ${dumpsys-system_pkgs_LIBRARIES}) diff --git a/tests/system/util/test-app.c b/tests/system/util/test-app.c index a9189f3..57c246c 100644 --- a/tests/system/util/test-app.c +++ b/tests/system/util/test-app.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019 Samsung Electronics Co., Ltd. + * Copyright (c) 2019-2020 Samsung Electronics Co., Ltd. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the License); * you may not use this file except in compliance with the License. @@ -14,48 +14,101 @@ * limitations under the License. */ -#include -#include +#include +#include #include -#include -#include +#include +#include #include +#include +#include +#include #include -#include "common.h" - +#include #include +#include "common.h" + char *message; +char *filename; GMainLoop *loop; int dump(const int fd, const int argc, char **argv) { + int result = -1; + printf("Arguments:\n"); for (int i = 0; i < argc; i++) - printf("[%d] arg: %s\n", i+1, argv[i]); + printf("[%d] arg: %s\n", i, argv[i]); + if (filename != NULL) { + int myfd = open(filename, O_RDONLY); + if (myfd < 0) { + printf("File open error: %m\n"); + return -1; + } + + char buff[4096]; + ssize_t readed, readed_all = 0; + + while ((readed = read(myfd, buff, sizeof(buff))) > 0) { + do { + result = write(fd, buff, readed); + } while (result == -1 && errno == EAGAIN); + if (result < 0) { + printf("Write error: %d %m\n", errno); + break; + } + readed_all += readed; + } + close(myfd); + printf("bytes copied: %zd\n", readed_all); + } else { + result = write(fd, message, strlen(message)); + printf("bytes written: %d\n", result); + } g_main_loop_quit(loop); - return write(fd, message, strlen(message)); + return result; +} + +static void print_help(const char *app_name) +{ + printf("Why didn't you specify required arguments?! ;~~~()\n"); + printf("\t%s \n", app_name); } int main(int argc, char *argv[]) { char *name = NULL; + message = NULL; + filename = NULL; - for (int i = optind; i < argc; i++) { - if (i == argc-2) - name = argv[i]; - else if (i == argc-1) - message = argv[i]; + int opt; + while ((opt = getopt(argc, argv, "f:")) != -1) { + switch(opt) { + case 'f': + filename = optarg; + break; + } } - if (name == NULL || message == NULL) { - printf("Why didn't you specify a required arguments?! ;~~~()\n"); - printf("\t%s \n", argv[0]); + if (optind != argc - 1 && optind != argc - 2) { + print_help(argv[0]); + return EXIT_FAILURE; + } + + if (optind + 1 <= argc) + name = argv[optind]; + + if (optind + 2 <= argc) + message = argv[optind+1]; + + if (name == NULL || (message == NULL && filename == NULL)) { + print_help(argv[0]); return EXIT_FAILURE; } int *handler; int ret = dumpsys_system_register_dump_cb(dump, name, (void**)&handler); - if (ret != TIZEN_ERROR_NONE) { + if (ret != TIZEN_ERROR_NONE) { printf("Some error :(. Check dlog.\n"); return EXIT_FAILURE; } -- 2.7.4 From 2a6ec4665bb5278b60d6feaa02874cceb1b2da95 Mon Sep 17 00:00:00 2001 From: Mateusz Moscicki Date: Thu, 27 Feb 2020 17:58:01 +0100 Subject: [PATCH 7/9] Add library providing API for user app services. Change-Id: Idd1c4b763a6377912d62fa38a0d79a83a09b5122 --- packaging/dumpsys.spec | 22 ++ src/client-api/CMakeLists.txt | 17 +- src/client-api/dumpsys-user.c | 427 ++++++++++++++++++++++++++++++++++++++ src/client-api/dumpsys-user.h | 80 +++++++ src/client-api/dumpsys-user.pc.in | 11 + src/service/dumpsys-service.conf | 2 + 6 files changed, 558 insertions(+), 1 deletion(-) create mode 100644 src/client-api/dumpsys-user.c create mode 100644 src/client-api/dumpsys-user.h create mode 100644 src/client-api/dumpsys-user.pc.in diff --git a/packaging/dumpsys.spec b/packaging/dumpsys.spec index e32522d..760b45a 100644 --- a/packaging/dumpsys.spec +++ b/packaging/dumpsys.spec @@ -30,6 +30,18 @@ Summary: libdumpsys-system development package. %description -n libdumpsys-system-devel This package provides library and header files. +%package -n libdumpsys-user +Summary: Package with dumpsys API library for services. +BuildRequires: pkgconfig(pkgmgr-info) +%description -n libdumpsys-user +This package provides dumpsys API library for services. + +%package -n libdumpsys-user-devel +Requires: libdumpsys-user +Summary: libdumpsys-user development package. +%description -n libdumpsys-user-devel +This package provides library and header files. + %package tests Summary: Contains scripts for testing dumpsys %description tests @@ -94,6 +106,16 @@ ln -s ../dumpsys-session-agent.service %{buildroot}/%{_unitdir_user}/delayed.tar %{_libdir}/libdumpsys-system.so %{_libdir}/pkgconfig/dumpsys-system.pc +%files -n libdumpsys-user +%manifest %{name}.manifest +%{_libdir}/libdumpsys-user.so.* + +%files -n libdumpsys-user-devel +%manifest %{name}.manifest +%{_includedir}/dumpsys-user.h +%{_libdir}/libdumpsys-user.so +%{_libdir}/pkgconfig/dumpsys-user.pc + %files tests %manifest %{name}.manifest %attr(0755,root,root) %{_libdir}/dumpsys_system_tests/utils/test-app diff --git a/src/client-api/CMakeLists.txt b/src/client-api/CMakeLists.txt index b07609c..14484db 100644 --- a/src/client-api/CMakeLists.txt +++ b/src/client-api/CMakeLists.txt @@ -6,7 +6,8 @@ INCLUDE(FindPkgConfig) pkg_check_modules(dumpsys-system_pkgs REQUIRED dlog gio-2.0 - gio-unix-2.0) + gio-unix-2.0 + pkgmgr-info) INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/src/shared) FOREACH(flag ${dumpsys-system_pkgs_CFLAGS}) SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} ${flag}") @@ -23,11 +24,25 @@ SET_TARGET_PROPERTIES(libdumpsys-system PROPERTIES OUTPUT_NAME dumpsys-system) TARGET_LINK_LIBRARIES(libdumpsys-system PUBLIC ${dumpsys-system_pkgs_LIBRARIES}) + +ADD_LIBRARY(libdumpsys-user SHARED dumpsys-user.c) +SET_TARGET_PROPERTIES(libdumpsys-user PROPERTIES + SOVERSION 1 + PUBLIC_HEADER dumpsys-user.h + OUTPUT_NAME dumpsys-user) +TARGET_LINK_LIBRARIES(libdumpsys-user PUBLIC ${dumpsys-system_pkgs_LIBRARIES}) + LINK_DIRECTORIES(${CMAKE_BINARY_DIR}) CONFIGURE_FILE(dumpsys-system.pc.in dumpsys-system.pc @ONLY) +CONFIGURE_FILE(dumpsys-user.pc.in dumpsys-user.pc @ONLY) INSTALL(TARGETS libdumpsys-system LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/dumpsys-system.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig) + +INSTALL(TARGETS libdumpsys-user LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) +INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/dumpsys-user.pc + DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig) diff --git a/src/client-api/dumpsys-user.c b/src/client-api/dumpsys-user.c new file mode 100644 index 0000000..5fbf5e4 --- /dev/null +++ b/src/client-api/dumpsys-user.c @@ -0,0 +1,427 @@ +/* + * Copyright (c) 2019-2020 Samsung Electronics Co., Ltd. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dumpsys-user.h" +#include "common.h" + +#ifndef LOG_TAG +#define LOG_TAG "DUMPSYS_USER" +#endif + +#include + +#define APPID_MAX 128 +#define DBUS_BUS_NAME_MAX_LEN 255 + +#define DCA_ERROR 1 +#define DCA_ERROR_UNSPECIFIED_ERROR 1 +#define DCA_ERROR_GETEXEPATH 2 +#define DCA_ERROR_GETAPPID 3 +#define DCA_ERROR_UNIXFDLISTEMPTY 4 + +static dumpsys_dump_cb dump_cb; +static GDBusNodeInfo *introspection_data; +static GDBusConnection *connection; +static guint reg_id, own_id; +static char *service_name; + +static const gchar introspection_xml[] = +"" + "" + "" + "" + "" + "" + "" +""; + +struct dumpsys_dump_data { + int fd; + int argc; + char **argv; +}; + +typedef struct dumpsys_dump_data* dumpsys_dump_h; + +static int appinfo_get_appid_func(pkgmgrinfo_appinfo_h handle, void *user_data) +{ + char *str = NULL; + + int ret = pkgmgrinfo_appinfo_get_appid(handle, &str); + if (ret == PMINFO_R_OK && str) + (*(char **)user_data) = strdup(str); + + return ret; +} + +static bool get_exepath(char *exepath, int len) +{ + int fd = open("/proc/self/cmdline", O_RDONLY); + + if (fd == -1) { + LOGE("open() cmdline error: %m\n"); + return false; + } + + ssize_t res; + + if ((res = read(fd, exepath, len)) == -1) { + LOGE("read() cmdline error: %m\n"); + close(fd); + return false; + } + + close(fd); + + exepath[res] = '\0'; + return true; +} + +static bool get_appid(char *exepath, char *appid, int len) +{ + pkgmgrinfo_appinfo_filter_h handle = NULL; + int count, ret; + char *aid = NULL; + bool result = false; + + ret = pkgmgrinfo_appinfo_filter_create(&handle); + if (ret != PMINFO_R_OK) { + LOGE("pkgmgrinfo_appinfo_filter_create() error: %d\n", ret); + goto out; + } + + ret = pkgmgrinfo_appinfo_filter_add_string(handle, PMINFO_APPINFO_PROP_APP_EXEC, exepath); + if (ret != PMINFO_R_OK) { + LOGE("pkgmgrinfo_appinfo_filter_add_string() error: %d\n", ret); + goto out_free; + } + + ret = pkgmgrinfo_appinfo_filter_count(handle, &count); + if (ret != PMINFO_R_OK) { + LOGE("pkgmgrinfo_appinfo_filter_count() error: %d\n", ret); + goto out_free; + } + + if (count < 1) { + LOGI("appid not found for %s\n", exepath); + goto out_free; + } + + ret = pkgmgrinfo_appinfo_filter_foreach_appinfo(handle, appinfo_get_appid_func, &aid); + if (ret != PMINFO_R_OK) { + LOGE("pkgmgrinfo_appinfo_filter_foreach_appinfo() error: %d\n", ret); + goto out_free; + } + + if (aid) { + snprintf(appid, len, "%s", aid); + result = true; + free(aid); + } + +out_free: + pkgmgrinfo_appinfo_filter_destroy(handle); +out: + return result; +} + +static bool get_name(char **name) +{ + assert(name); + + char app_id[APPID_MAX]; + char exe_path[PATH_MAX]; + + if (!get_exepath(exe_path, sizeof(exe_path))) + return false; + + if (get_appid(exe_path, app_id, sizeof(app_id))) { + *name = strdup(app_id); + return true; + } + + return false; +} + +static int get_args(GVariant *vargs, gchar ***argv) +{ + assert(vargs); + assert(argv); + + gsize count = -1; + + GVariant *array = g_variant_get_child_value(vargs, 0); + *argv = g_variant_dup_strv(array, &count); + return count; +} + +static void dump_handler(GDBusMethodInvocation *invocation) +{ + assert(invocation); + assert(dump_cb); + + GDBusMessage *msg = g_dbus_method_invocation_get_message(invocation); + if (msg == NULL) { + LOGE("g_dbus_method_invocation_get_message() error"); + return; + } + + GUnixFDList *fd_list = g_dbus_message_get_unix_fd_list(msg); + if (fd_list == NULL) { + LOGE("No file descriptor provided"); + g_dbus_method_invocation_return_error(invocation, + DCA_ERROR, + DCA_ERROR_UNIXFDLISTEMPTY, + "No file descriptor provided"); + return; + } + + GError *error = NULL; + + int fd = g_unix_fd_list_get(fd_list, 0, &error); + if (fd == -1) { + LOGE("g_unix_fd_list_get() error: %s", error ? error->message : "(none)"); + g_dbus_method_invocation_return_gerror(invocation, error); + if (error) + g_error_free(error); + return; + } + + GVariant *body = g_dbus_message_get_body(msg); + + struct dumpsys_dump_data *dump_data = malloc(sizeof(*dump_data)); + if (dump_data == NULL) { + LOGE("malloc error: %m"); + return; + } + dump_data->fd = fd; + dump_data->argc = get_args(body, &dump_data->argv); + + GTask *task = g_task_new(NULL, NULL, NULL, NULL); + g_task_set_task_data(task, dump_data, NULL); + + g_object_unref(task); + g_dbus_method_invocation_return_value(invocation, + g_variant_new("(i)", DUMP_SUCCESS)); + dump_cb(dump_data); + close(dump_data->fd); + free(dump_data); +} + +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) +{ + if (g_strcmp0(method_name, METHOD_DUMP) == 0) + dump_handler(invocation); +} + +static const GDBusInterfaceVTable interface_vtable = { + method_call_handler, NULL, NULL +}; + +static bool register_object(GDBusConnection *conn) +{ + GError *error = NULL; + reg_id = g_dbus_connection_register_object(conn, + DUMPSYS_PATH, + introspection_data->interfaces[0], + &interface_vtable, + NULL, + NULL, + &error); + + if (!reg_id) { + LOGE("g_dbus_connection_register_object() error: %s", error ? error->message : "(none)"); + if (error) + g_error_free(error); + return false; + } + return true; +} + +static GBusType get_bus_type() +{ + return (getuid() == 0) ? G_BUS_TYPE_SYSTEM : G_BUS_TYPE_SESSION; +} + +static bool dbus_init() +{ + introspection_data = g_dbus_node_info_new_for_xml(introspection_xml, NULL); + if (introspection_data == NULL) { + LOGE("g_dbus_node_info_new_for_xml() error"); + return false; + } + + GError *error = NULL; + + GBusType default_bus_type = get_bus_type(); + connection = g_bus_get_sync(default_bus_type, NULL, &error); + + if (!connection || error != NULL) { + LOGE("connect to the bus error: %s", error ? error->message : "(none)"); + if (error) + g_error_free(error); + return false; + } + + char name_with_prefix[DBUS_BUS_NAME_MAX_LEN]; + int ret = snprintf(name_with_prefix, sizeof(name_with_prefix), "%s.%s", DUMPSYS_SERVICE_NAME_PREFIX, service_name); + if (ret < 0) { + LOGE("snprintf error: %m"); + return false; + } + + own_id = g_bus_own_name_on_connection(connection, + name_with_prefix, + G_BUS_NAME_OWNER_FLAGS_NONE, + NULL, + NULL, + NULL, + NULL); + + return register_object(connection); +} + +static bool dbus_close() +{ + if (!connection) + return false; + + bool res = g_dbus_connection_unregister_object(connection, reg_id); + g_bus_unown_name(own_id); + g_object_unref(connection); + g_dbus_node_info_unref(introspection_data); + connection = NULL; + reg_id = 0; + own_id = 0; + return res; +} + +static int register_callback(dumpsys_dump_cb callback, const char *name) +{ + assert(callback); + + if (name) { + service_name = strdup(name); + if (!service_name) { + LOGE("strdup error: %m\n"); + return TIZEN_ERROR_INVALID_OPERATION; + } + } else if (!get_name(&service_name)) { + LOGE("get_name error\n"); + return TIZEN_ERROR_INVALID_OPERATION; + } + + bool result = dbus_init(); + if (result) + dump_cb = callback; + return result ? TIZEN_ERROR_NONE : TIZEN_ERROR_IO_ERROR; + +} + +static int dumpsys_register_dump_name_cb(dumpsys_dump_cb callback, const char *name, void **handler) +{ + if (dump_cb != NULL) { + LOGE("register_dump_cb(): already registered\n"); + return TIZEN_ERROR_ALREADY_IN_PROGRESS; + } + + if (callback == NULL || handler == NULL) { + LOGE("register_me(): invalid parameter\n"); + return TIZEN_ERROR_INVALID_PARAMETER; + } + + int res = register_callback(callback, name); + if (res == TIZEN_ERROR_NONE) + *handler = (void*)callback; + return res; +} + +int API_FUNCTION dumpsys_register_dump_cb(dumpsys_dump_cb callback, void **handler) +{ + return dumpsys_register_dump_name_cb(callback, NULL, handler); +} + +int API_FUNCTION dumpsys_unregister_dump_cb(void *handler) +{ + if (handler == NULL) { + LOGE("dumpsys_unregister_dump_cb(): parameter is NULL\n"); + return TIZEN_ERROR_INVALID_PARAMETER; + } + + if (handler != (void*)dump_cb) { + LOGE("dumpsys_unregister_dump_cb(): handler not registered\n"); + return TIZEN_ERROR_ALREADY_IN_PROGRESS; + } + + dump_cb = NULL; + free(service_name); + return dbus_close() ? TIZEN_ERROR_NONE : TIZEN_ERROR_IO_ERROR; +} + +int API_FUNCTION dumpsys_get_args_count(dumpsys_dump_h dump_handler, int *args_count) +{ + if (dump_handler == NULL || args_count == NULL) { + LOGE("dumpsys_get_args_count(): invalid parameter\n"); + return TIZEN_ERROR_INVALID_PARAMETER; + } + + *args_count = dump_handler->argc; + + return TIZEN_ERROR_NONE; +} + +int API_FUNCTION dumpsys_get_args_array(dumpsys_dump_h dump_handler, char ***args_array) +{ + if (dump_handler == NULL || args_array == NULL) { + LOGE("dumpsys_get_args_array(): invalid parameter\n"); + return TIZEN_ERROR_INVALID_PARAMETER; + } + + *args_array = dump_handler->argv; + + return TIZEN_ERROR_NONE; +} + +int API_FUNCTION dumpsys_write(struct dumpsys_dump_data *dump_handler, const void *buff, size_t count) +{ + if (dump_handler == NULL || buff == NULL) { + LOGE("dumpsys_write(): invalid parameter\n"); + return TIZEN_ERROR_INVALID_PARAMETER; + } + return write(dump_handler->fd, buff, count) >= 0 ? TIZEN_ERROR_NONE : TIZEN_ERROR_IO_ERROR; +} diff --git a/src/client-api/dumpsys-user.h b/src/client-api/dumpsys-user.h new file mode 100644 index 0000000..7d22993 --- /dev/null +++ b/src/client-api/dumpsys-user.h @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2019-2020 Samsung Electronics Co., Ltd. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __DUMPSYS_USER_H__ +#define __DUMPSYS_USER_H__ + +#include + +typedef struct dumpsys_dump_data* dumpsys_dump_h; +/** + * @brief Callback function. + * @param[in] dump_context It is freed automatically after returning. + * @retval The result of the callback (currently not used). + */ +typedef int (*dumpsys_dump_cb)(dumpsys_dump_h dump_context); + +/** + * @brief Register the callback. + * @param[in] callback A callback for data retrieval. + * @param[out] context Value used to unregister specific callback. + * @retval TIZEN_ERROR_NONE Success. + * @retval TIZEN_ERROR_ALREADY_IN_PROGRESS Callback already registered. + * @retval TIZEN_ERROR_INVALID_PARAMETER Provided parameter is invalid. + * @retval TIZEN_ERROR_INVALID_OPERATION Internal error. + * @retval TIZEN_ERROR_IO_ERROR Error during DBus registration. + */ +extern int dumpsys_register_dump_cb(dumpsys_dump_cb callback, void **context); + +/** + * @brief Unregister the callback. + * @param[in] context Context returned by dumpsys_register_dump_cb(). + * @retval TIZEN_ERROR_NONE Success. + * @retval TIZEN_ERROR_INVALID_PARAMETER Invalid context. + * @retval TIZEN_ERROR_ALREADY_IN_PROGRESS Context is not registered. + * @retval TIZEN_ERROR_IO_ERROR Error during DBus deregistration. + */ +extern int dumpsys_unregister_dump_cb(void *context); + +/** + * @brief Send data to the dump caller. + * @param[in] dump_context Dump context provided as callback argument. + * @param[in] buf A buffer containing data to write. + * @param[in] count Buffer size. + * @retval TIZEN_ERROR_NONE Success. + * @retval TIZEN_ERROR_INVALID_PARAMETER Invalid parameter value. + * @retval TIZEN_ERROR_IO_ERROR Data write error. + */ +extern int dumpsys_write(dumpsys_dump_h dump_context, const void *buf, size_t count); + +/** + * @brief Get arguments count + * @param[in] dump_context A value provided as callback argument + * @param[out] args_count Number of arguments + * @retval TIZEN_ERROR_NONE Success. + * @retval TIZEN_ERROR_INVALID_PARAMETER Invalid parameter value. + */ +extern int dumpsys_get_args_count(dumpsys_dump_h dump_context, int *args_count); + +/** + * @brief Get arguments array + * @param[in] dump_handler A handler provided as callback argument + * @param[out] args_array Array of null-terminated strings + * @retval TIZEN_ERROR_NONE Success. + * @retval TIZEN_ERROR_INVALID_PARAMETER Invalid parameter value. + */ +extern int dumpsys_get_args_array(dumpsys_dump_h dump_context, char ***args_array); +#endif // __DUMPSYS_USER_H__ diff --git a/src/client-api/dumpsys-user.pc.in b/src/client-api/dumpsys-user.pc.in new file mode 100644 index 0000000..068be52 --- /dev/null +++ b/src/client-api/dumpsys-user.pc.in @@ -0,0 +1,11 @@ +prefix=/usr +exec_prefix=${prefix} +includedir=${prefix}/include +libdir=${exec_prefix}/lib + +Name: dumpsys-user +Description: The crash-manager library +Version: @VERSION@ +Cflags: -I${includedir} +Libs: -L${libdir} -ldumpsys-user + diff --git a/src/service/dumpsys-service.conf b/src/service/dumpsys-service.conf index f5851f8..cf7382a 100644 --- a/src/service/dumpsys-service.conf +++ b/src/service/dumpsys-service.conf @@ -14,6 +14,8 @@ + + -- 2.7.4 From 4aa8e6ebbefe4e1cc9ce6869256f5ed0a985652c Mon Sep 17 00:00:00 2001 From: Mateusz Moscicki Date: Wed, 29 Jan 2020 14:02:03 +0100 Subject: [PATCH 8/9] Add system tests for dumpsys Change-Id: I8f42f0a37f8b94656b8485d71b33f5887f0faef9 --- CMakeLists.txt | 8 +++- packaging/dumpsys.spec | 18 +++++++- tests/CMakeLists.txt | 2 +- tests/system/CMakeLists.txt | 24 +++++++++++ .../dumpsys_notfound/dumpsys_notfound.sh.template | 21 ++++++++++ .../dumpsys_session/dumpsys_session.sh.template | 41 ++++++++++++++++++ .../dumpsys_system/dumpsys_system.sh.template | 43 +++++++++++++++++++ tests/system/run.sh.template | 48 ++++++++++++++++++++++ tests/system/{util => utils}/CMakeLists.txt | 9 +++- tests/system/utils/dumpsys-utils.sh | 41 ++++++++++++++++++ tests/system/{util => utils}/test-app.c | 4 +- 11 files changed, 253 insertions(+), 6 deletions(-) create mode 100644 tests/system/CMakeLists.txt create mode 100644 tests/system/dumpsys_notfound/dumpsys_notfound.sh.template create mode 100644 tests/system/dumpsys_session/dumpsys_session.sh.template create mode 100644 tests/system/dumpsys_system/dumpsys_system.sh.template create mode 100644 tests/system/run.sh.template rename tests/system/{util => utils}/CMakeLists.txt (72%) create mode 100644 tests/system/utils/dumpsys-utils.sh rename tests/system/{util => utils}/test-app.c (97%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 665b768..0b3999a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,9 +5,15 @@ SET(PREFIX ${CMAKE_INSTALL_PREFIX}) ADD_DEFINITIONS(-D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE=1) +IF("${TESTS}" STREQUAL "ON") + ADD_DEFINITIONS(-DTESTS) +ENDIF() + # Sub modules ADD_SUBDIRECTORY(src/client-api) ADD_SUBDIRECTORY(src/dumpsys) ADD_SUBDIRECTORY(src/agent) ADD_SUBDIRECTORY(src/service) -ADD_SUBDIRECTORY(tests) +IF("${TESTS}" STREQUAL "ON") + ADD_SUBDIRECTORY(tests) +ENDIF() diff --git a/packaging/dumpsys.spec b/packaging/dumpsys.spec index 760b45a..eb38823 100644 --- a/packaging/dumpsys.spec +++ b/packaging/dumpsys.spec @@ -1,3 +1,8 @@ +%define on_off() %{expand:%%{?with_%{1}:ON}%%{!?with_%{1}:OFF}} + +%define _with_tests on +%bcond_with tests + Name: dumpsys Summary: Dumpsys is a framework to get logs from services and applications. Version: 0.0.3 @@ -42,9 +47,11 @@ Summary: libdumpsys-user development package. %description -n libdumpsys-user-devel This package provides library and header files. +%if %{with tests} %package tests Summary: Contains scripts for testing dumpsys %description tests +%endif %prep %setup -q @@ -55,6 +62,7 @@ export CFLAGS+=" -Werror -fvisibility=hidden" %cmake . \ -DVERSION=%{version} \ + -DTESTS=%{on_off tests} \ -DDUMPSYS_SYSTEM_TESTS_PATH=%{_libdir}/dumpsys_system_tests/ make %{?jobs:-j%jobs} @@ -116,6 +124,14 @@ ln -s ../dumpsys-session-agent.service %{buildroot}/%{_unitdir_user}/delayed.tar %{_libdir}/libdumpsys-user.so %{_libdir}/pkgconfig/dumpsys-user.pc +%if %{with tests} %files tests %manifest %{name}.manifest -%attr(0755,root,root) %{_libdir}/dumpsys_system_tests/utils/test-app +%defattr(0755,root,root) +%{_libdir}/dumpsys_system_tests/utils/dumpsys-utils.sh +%{_libdir}/dumpsys_system_tests/utils/test-app +%{_libdir}/dumpsys_system_tests/run.sh +%{_libdir}/dumpsys_system_tests/dumpsys_system/dumpsys_system.sh +%{_libdir}/dumpsys_system_tests/dumpsys_session/dumpsys_session.sh +%{_libdir}/dumpsys_system_tests/dumpsys_notfound/dumpsys_notfound.sh +%endif diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index a79f14e..53c0353 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -6,4 +6,4 @@ SET(PREFIX ${CMAKE_INSTALL_PREFIX}) ADD_DEFINITIONS(-D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE=1) # Sub modules -ADD_SUBDIRECTORY(system/util) +ADD_SUBDIRECTORY(system) diff --git a/tests/system/CMakeLists.txt b/tests/system/CMakeLists.txt new file mode 100644 index 0000000..868a82f --- /dev/null +++ b/tests/system/CMakeLists.txt @@ -0,0 +1,24 @@ +cmake_minimum_required(VERSION 2.6) + +ADD_SUBDIRECTORY(utils) + +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 ${DUMPSYS_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("dumpsys_system") +configure_test("dumpsys_session") +configure_test("dumpsys_notfound") + +configure_file("run.sh.template" "run.sh" @ONLY) +INSTALL(FILES run.sh DESTINATION ${DUMPSYS_SYSTEM_TESTS_PATH}) diff --git a/tests/system/dumpsys_notfound/dumpsys_notfound.sh.template b/tests/system/dumpsys_notfound/dumpsys_notfound.sh.template new file mode 100644 index 0000000..c092e52 --- /dev/null +++ b/tests/system/dumpsys_notfound/dumpsys_notfound.sh.template @@ -0,0 +1,21 @@ +#!/bin/bash + +# Check return code of dumpsys in case of a failure + +if [ -z "${DUMPSYS_SYSTEM_TESTS}" ]; then + DUMPSYS_SYSTEM_TESTS="@DUMPSYS_SYSTEM_TESTS_PATH@" +fi + +DUMPSYS_SYSTEM_TESTS=/usr/lib/dumpsys_system_tests + +. ${DUMPSYS_SYSTEM_TESTS}/utils/dumpsys-utils.sh + +APP_NAME=org.tizem.some_non_existent_app + +dumpsys ${APP_NAME} -- "${ARG1}" "${ARG2}" + +if [ $? -eq 1 ]; then + exit_ok +else + fail "return code not equal 1" +fi diff --git a/tests/system/dumpsys_session/dumpsys_session.sh.template b/tests/system/dumpsys_session/dumpsys_session.sh.template new file mode 100644 index 0000000..3612a6f --- /dev/null +++ b/tests/system/dumpsys_session/dumpsys_session.sh.template @@ -0,0 +1,41 @@ +#!/bin/bash + +# dumpsys session bus test + +if [ -z "${DUMPSYS_SYSTEM_TESTS}" ]; then + DUMPSYS_SYSTEM_TESTS="@DUMPSYS_SYSTEM_TESTS_PATH@" +fi + +. ${DUMPSYS_SYSTEM_TESTS}/utils/dumpsys-utils.sh + +APP_NAME=apka +MESSAGE="some message" +ARG1="-some arg 1" +ARG2="--some arg 2" +OUTPUT_FILE=/tmp/dumpsys_output +APP_PID=0 + +cleanup() +{ + if [[ ${APP_PID} != 0 ]]; then + kill ${APP_PID} + fi + + rm -f "${OUTPUT_FILE:?}" +} + +trap cleanup 0 + +su -c "${DUMPSYS_SYSTEM_TESTS}/utils/test-app \"${APP_NAME}\" \"${MESSAGE}\"" -l owner > ${OUTPUT_FILE} & +APP_PID=${!} +sleep 1 +RESULT=`dumpsys ${APP_NAME} -- "${ARG1}" "${ARG2}"` + +check "${MESSAGE}" + +RESULT=`cat ${OUTPUT_FILE}` + +check "${ARG1}" +check "${ARG2}" + +exit_ok diff --git a/tests/system/dumpsys_system/dumpsys_system.sh.template b/tests/system/dumpsys_system/dumpsys_system.sh.template new file mode 100644 index 0000000..2c83c47 --- /dev/null +++ b/tests/system/dumpsys_system/dumpsys_system.sh.template @@ -0,0 +1,43 @@ +#!/bin/bash + +# dumpsys system bus test + +if [ -z "${DUMPSYS_SYSTEM_TESTS}" ]; then + DUMPSYS_SYSTEM_TESTS="@DUMPSYS_SYSTEM_TESTS_PATH@" +fi + +DUMPSYS_SYSTEM_TESTS=/usr/lib/dumpsys_system_tests + +. ${DUMPSYS_SYSTEM_TESTS}/utils/dumpsys-utils.sh + +APP_NAME=apka +MESSAGE="some message" +ARG1="-some arg 1" +ARG2="--some arg 2" +OUTPUT_FILE=/tmp/dumpsys_output +APP_PID=0 + +cleanup() +{ + if [[ ${APP_PID} != 0 ]]; then + kill ${APP_PID} + fi + rm -f "${OUTPUT_FILE:?}" +} + +trap cleanup 0 + +${DUMPSYS_SYSTEM_TESTS}/utils/test-app "${APP_NAME}" "${MESSAGE}" > ${OUTPUT_FILE} & + +sleep 1 +RESULT=`dumpsys ${APP_NAME} -- "${ARG1}" "${ARG2}"` +APP_PID=${!} + +check "${MESSAGE}" + +RESULT=`cat ${OUTPUT_FILE}` + +check "${ARG1}" +check "${ARG2}" + +exit_ok diff --git a/tests/system/run.sh.template b/tests/system/run.sh.template new file mode 100644 index 0000000..71c9009 --- /dev/null +++ b/tests/system/run.sh.template @@ -0,0 +1,48 @@ +#!/bin/bash + +if [ -z "${DUMPSYS_SYSTEM_TESTS}" ]; then + DUMPSYS_SYSTEM_TESTS="@DUMPSYS_SYSTEM_TESTS_PATH@" +fi + +. ${DUMPSYS_SYSTEM_TESTS}/utils/dumpsys-utils.sh + +LOG_FILE=/tmp/dumpsys_system-tests.log + +function run_test { + RED="\033[0;31m" + GREEN="\033[0;32m" + YELLOW="\033[1;33m" + NORMAL="\033[0m" + TEST=${1} + echo -ne "${TEST}:\t" + echo "===================================" >> ${LOG_FILE} + echo "Test ${TEST}:" >> ${LOG_FILE} + echo "===================================" >> ${LOG_FILE} + ${DUMPSYS_SYSTEM_TESTS}/${TEST}/${TEST}.sh 1>> ${LOG_FILE} 2>&1 + case $? in + ${SUCCESS_CODE}) + echo -e "${GREEN}SUCCESS${NORMAL}" + echo "SUCCESS" >> ${LOG_FILE} + ;; + ${FAIL_CODE}) + echo -e "${RED}FAIL${NORMAL}" + echo "FAIL" >> ${LOG_FILE} + ;; + ${SKIP_CODE}) + echo -e "${YELLOW}SKIP${NORMAL}" + echo "SKIP" >> ${LOG_FILE} + ;; + esac +} + +if [ -f ${LOG_FILE} ]; then + rm ${LOG_FILE} +fi + +for DIR in ${DUMPSYS_SYSTEM_TESTS}/*/; do + test_name=`basename ${DIR}` + + if [ -x ${DUMPSYS_SYSTEM_TESTS}/${test_name}/${test_name}.sh ]; then + run_test ${test_name} + fi +done diff --git a/tests/system/util/CMakeLists.txt b/tests/system/utils/CMakeLists.txt similarity index 72% rename from tests/system/util/CMakeLists.txt rename to tests/system/utils/CMakeLists.txt index fd37c13..7165c59 100644 --- a/tests/system/util/CMakeLists.txt +++ b/tests/system/utils/CMakeLists.txt @@ -7,7 +7,8 @@ pkg_check_modules(dumpsys-system_pkgs REQUIRED capi-base-common dlog gio-2.0 - gio-unix-2.0) + gio-unix-2.0 + pkgmgr-info) INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/src/shared) INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/src/client-api) @@ -17,7 +18,10 @@ ENDFOREACH(flag) SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_CFLAGS} -fPIE -Wno-unused-function -Wno-unused-const-variable") -ADD_EXECUTABLE(test-app test-app.c ${CMAKE_SOURCE_DIR}/src/client-api/dumpsys-system.c) +ADD_EXECUTABLE(test-app + test-app.c + ${CMAKE_SOURCE_DIR}/src/client-api/dumpsys-system.c + ${CMAKE_SOURCE_DIR}/src/client-api/dumpsys-user.c) TARGET_LINK_LIBRARIES(test-app PUBLIC ${dumpsys-system_pkgs_LIBRARIES}) @@ -25,3 +29,4 @@ LINK_DIRECTORIES(${CMAKE_BINARY_DIR}) INSTALL(TARGETS test-app DESTINATION ${DUMPSYS_SYSTEM_TESTS_PATH}/utils PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE) +INSTALL(FILES dumpsys-utils.sh DESTINATION ${DUMPSYS_SYSTEM_TESTS_PATH}/utils) diff --git a/tests/system/utils/dumpsys-utils.sh b/tests/system/utils/dumpsys-utils.sh new file mode 100644 index 0000000..29833af --- /dev/null +++ b/tests/system/utils/dumpsys-utils.sh @@ -0,0 +1,41 @@ +#!/bin/bash + +SUCCESS_CODE=0 +FAIL_CODE=1 +SKIP_CODE=2 + +function exit_with_code { + echo ${1} + exit ${2} +} + +function exit_ok { + exit_with_code "SUCCESS" ${SUCCESS_CODE} +} + +function fail { + exit_with_code "FAIL: $1" ${FAIL_CODE} +} + +function skip { + exit_with_code "SKIP: $1" ${SKIP_CODE} +} + +function check { + if [[ ${RESULT} =~ ${1} ]]; then + return 0 + fi + fail "not found ${1} in ${RESULT}" +} + +function check_file_exists { + if [ ! -f ${1} ]; then + fail "file not exists $1" + fi +} + +function check_file_not_exists { + if [ -f ${1} ]; then + fail "file exists $1" + fi +} diff --git a/tests/system/util/test-app.c b/tests/system/utils/test-app.c similarity index 97% rename from tests/system/util/test-app.c rename to tests/system/utils/test-app.c index 57c246c..f507ae9 100644 --- a/tests/system/util/test-app.c +++ b/tests/system/utils/test-app.c @@ -107,7 +107,9 @@ int main(int argc, char *argv[]) } int *handler; - int ret = dumpsys_system_register_dump_cb(dump, name, (void**)&handler); + int ret; + ret = dumpsys_system_register_dump_cb(dump, name, (void**)&handler); + if (ret != TIZEN_ERROR_NONE) { printf("Some error :(. Check dlog.\n"); return EXIT_FAILURE; -- 2.7.4 From 2316062ce22dc7df26d749164bde6f198f629e22 Mon Sep 17 00:00:00 2001 From: Mateusz Moscicki Date: Tue, 25 Feb 2020 15:07:17 +0100 Subject: [PATCH 9/9] Remove session agent Change-Id: I26a6394635130eabad8aaedd5b0ecd9fa11ff6a2 --- packaging/dumpsys.spec | 9 ----- src/agent/CMakeLists.txt | 14 ++------ src/agent/dumpsys-agent.c | 40 --------------------- src/agent/dumpsys-session-agent.service | 6 ---- src/client-api/dumpsys-user.c | 8 +---- src/service/CMakeLists.txt | 2 +- tests/system/CMakeLists.txt | 1 - .../dumpsys_session/dumpsys_session.sh.template | 41 ---------------------- 8 files changed, 4 insertions(+), 117 deletions(-) delete mode 100644 src/agent/dumpsys-agent.c delete mode 100644 src/agent/dumpsys-session-agent.service delete mode 100644 tests/system/dumpsys_session/dumpsys_session.sh.template diff --git a/packaging/dumpsys.spec b/packaging/dumpsys.spec index eb38823..79f32a6 100644 --- a/packaging/dumpsys.spec +++ b/packaging/dumpsys.spec @@ -74,9 +74,6 @@ rm -rf %{buildroot} mkdir -p %{buildroot}/%{_unitdir}/basic.target.wants/ ln -s ../dumpsys-service.service %{buildroot}/%{_unitdir}/basic.target.wants/dumpsys-service.service -mkdir -p %{buildroot}/%{_unitdir_user}/delayed.target.wants/ -ln -s ../dumpsys-session-agent.service %{buildroot}/%{_unitdir_user}/delayed.target.wants/dumpsys-session-agent.service - %files %license LICENSE %manifest %{name}.manifest @@ -85,14 +82,10 @@ ln -s ../dumpsys-session-agent.service %{buildroot}/%{_unitdir_user}/delayed.tar %{_bindir}/dumpsys %{_libdir}/libdumpsys.so.* %{_bindir}/dumpsys-service -%{_bindir}/dumpsys-session-agent -%{_libdir}/libdumpsys-agent.so.* %defattr(0655,root,root) %{_unitdir}/basic.target.wants/dumpsys-service.service %{_unitdir}/dumpsys-service.service -%{_unitdir_user}/delayed.target.wants/dumpsys-session-agent.service -%{_unitdir_user}/dumpsys-session-agent.service %{_libdir}/tmpfiles.d/dumpsys-run.conf %{_sysconfdir}/dbus-1/system.d/dumpsys-service.conf %{_sysconfdir}/dbus-1/system.d/dumpsys-agent.conf @@ -101,7 +94,6 @@ ln -s ../dumpsys-session-agent.service %{buildroot}/%{_unitdir_user}/delayed.tar %manifest %{name}.manifest %{_includedir}/libdumpsys.h %{_libdir}/libdumpsys.so -%{_libdir}/libdumpsys-agent.so %{_libdir}/pkgconfig/dumpsys.pc %files -n libdumpsys-system @@ -132,6 +124,5 @@ ln -s ../dumpsys-session-agent.service %{buildroot}/%{_unitdir_user}/delayed.tar %{_libdir}/dumpsys_system_tests/utils/test-app %{_libdir}/dumpsys_system_tests/run.sh %{_libdir}/dumpsys_system_tests/dumpsys_system/dumpsys_system.sh -%{_libdir}/dumpsys_system_tests/dumpsys_session/dumpsys_session.sh %{_libdir}/dumpsys_system_tests/dumpsys_notfound/dumpsys_notfound.sh %endif diff --git a/src/agent/CMakeLists.txt b/src/agent/CMakeLists.txt index 54aa7a0..a510127 100644 --- a/src/agent/CMakeLists.txt +++ b/src/agent/CMakeLists.txt @@ -4,7 +4,6 @@ PROJECT(DUMPSYS_AGENT C) INCLUDE(GNUInstallDirs) INCLUDE(FindPkgConfig) -SET(AGENT_BIN "dumpsys-session-agent") SET(AGENT_LIB "libdumpsys-agent") find_package(PkgConfig REQUIRED) @@ -25,20 +24,11 @@ ENDFOREACH(flag) SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_CFLAGS} -Wall -Wextra -Werror -fPIE -Wno-unused-function -Wno-unused-const-variable") -ADD_LIBRARY(${AGENT_LIB} SHARED libdumpsys-agent.c) +ADD_LIBRARY(${AGENT_LIB} STATIC libdumpsys-agent.c) TARGET_LINK_LIBRARIES(${AGENT_LIB} PUBLIC ${libagent_pkgs_LIBRARIES}) SET_TARGET_PROPERTIES(${AGENT_LIB} PROPERTIES SOVERSION 1 OUTPUT_NAME dumpsys-agent) -LINK_DIRECTORIES(${CMAKE_BINARY_DIR}) -ADD_EXECUTABLE(${AGENT_BIN} dumpsys-agent.c) -TARGET_LINK_LIBRARIES(${AGENT_BIN} PUBLIC ${AGENT_LIB}) - -INSTALL (TARGETS ${AGENT_BIN} DESTINATION ${CMAKE_INSTALL_BINDIR} - PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE) -INSTALL (TARGETS ${AGENT_LIB} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}) INSTALL (FILES dumpsys-agent.conf DESTINATION /etc/dbus-1/system.d/ - PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE) -INSTALL (FILES dumpsys-session-agent.service DESTINATION /usr/lib/systemd/user/ - PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE) + PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE) diff --git a/src/agent/dumpsys-agent.c b/src/agent/dumpsys-agent.c deleted file mode 100644 index 07b27d9..0000000 --- a/src/agent/dumpsys-agent.c +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (c) 2019-2020 Samsung Electronics Co., Ltd. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the License); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include -#include -#include "libdumpsys-agent.h" - -int main() -{ - int result = EXIT_SUCCESS; - GMainLoop *loop = g_main_loop_new(NULL, false); - - assert(loop); - - if (!dumpsys_agent_run(loop, G_BUS_TYPE_SESSION)) { - result = EXIT_FAILURE; - goto out; - } - - g_main_loop_run(loop); -out: - dumpsys_agent_stop(); - g_main_loop_unref(loop); - - return result; -} diff --git a/src/agent/dumpsys-session-agent.service b/src/agent/dumpsys-session-agent.service deleted file mode 100644 index 4dff996..0000000 --- a/src/agent/dumpsys-session-agent.service +++ /dev/null @@ -1,6 +0,0 @@ -[Unit] -Description=Dumpsys agent -After=dbus.socket - -[Service] -ExecStart=/usr/bin/dumpsys-session-agent diff --git a/src/client-api/dumpsys-user.c b/src/client-api/dumpsys-user.c index 5fbf5e4..488f85f 100644 --- a/src/client-api/dumpsys-user.c +++ b/src/client-api/dumpsys-user.c @@ -273,11 +273,6 @@ static bool register_object(GDBusConnection *conn) return true; } -static GBusType get_bus_type() -{ - return (getuid() == 0) ? G_BUS_TYPE_SYSTEM : G_BUS_TYPE_SESSION; -} - static bool dbus_init() { introspection_data = g_dbus_node_info_new_for_xml(introspection_xml, NULL); @@ -288,8 +283,7 @@ static bool dbus_init() GError *error = NULL; - GBusType default_bus_type = get_bus_type(); - connection = g_bus_get_sync(default_bus_type, NULL, &error); + connection = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &error); if (!connection || error != NULL) { LOGE("connect to the bus error: %s", error ? error->message : "(none)"); diff --git a/src/service/CMakeLists.txt b/src/service/CMakeLists.txt index 6d443c1..78c4e2f 100644 --- a/src/service/CMakeLists.txt +++ b/src/service/CMakeLists.txt @@ -28,7 +28,7 @@ LINK_DIRECTORIES(${CMAKE_BINARY_DIR}) LINK_DIRECTORIES(${CMAKE_SOURCE_DIR}/src/agent) ADD_EXECUTABLE(dumpsys-service ${DUMPSYS_SERVICE}) ADD_DEPENDENCIES(dumpsys-service libdumpsys-agent) -TARGET_LINK_LIBRARIES(dumpsys-service PUBLIC ${dumpsys_service_pkgs_LIBRARIES} -ldumpsys-agent) +TARGET_LINK_LIBRARIES(dumpsys-service PUBLIC ${dumpsys_service_pkgs_LIBRARIES} libdumpsys-agent) INSTALL (TARGETS dumpsys-service DESTINATION ${CMAKE_INSTALL_BINDIR} PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE) diff --git a/tests/system/CMakeLists.txt b/tests/system/CMakeLists.txt index 868a82f..4e5a7d6 100644 --- a/tests/system/CMakeLists.txt +++ b/tests/system/CMakeLists.txt @@ -17,7 +17,6 @@ macro(CONFIGURE_TEST test_name) endmacro() configure_test("dumpsys_system") -configure_test("dumpsys_session") configure_test("dumpsys_notfound") configure_file("run.sh.template" "run.sh" @ONLY) diff --git a/tests/system/dumpsys_session/dumpsys_session.sh.template b/tests/system/dumpsys_session/dumpsys_session.sh.template deleted file mode 100644 index 3612a6f..0000000 --- a/tests/system/dumpsys_session/dumpsys_session.sh.template +++ /dev/null @@ -1,41 +0,0 @@ -#!/bin/bash - -# dumpsys session bus test - -if [ -z "${DUMPSYS_SYSTEM_TESTS}" ]; then - DUMPSYS_SYSTEM_TESTS="@DUMPSYS_SYSTEM_TESTS_PATH@" -fi - -. ${DUMPSYS_SYSTEM_TESTS}/utils/dumpsys-utils.sh - -APP_NAME=apka -MESSAGE="some message" -ARG1="-some arg 1" -ARG2="--some arg 2" -OUTPUT_FILE=/tmp/dumpsys_output -APP_PID=0 - -cleanup() -{ - if [[ ${APP_PID} != 0 ]]; then - kill ${APP_PID} - fi - - rm -f "${OUTPUT_FILE:?}" -} - -trap cleanup 0 - -su -c "${DUMPSYS_SYSTEM_TESTS}/utils/test-app \"${APP_NAME}\" \"${MESSAGE}\"" -l owner > ${OUTPUT_FILE} & -APP_PID=${!} -sleep 1 -RESULT=`dumpsys ${APP_NAME} -- "${ARG1}" "${ARG2}"` - -check "${MESSAGE}" - -RESULT=`cat ${OUTPUT_FILE}` - -check "${ARG1}" -check "${ARG2}" - -exit_ok -- 2.7.4