Add dumpsys API library accepted/tizen/5.5/unified/20200124.120351 accepted/tizen/unified/20200123.005120 submit/tizen/20200121.121857 submit/tizen_5.5/20200123.115131
authorMateusz Moscicki <m.moscicki2@partner.samsung.com>
Thu, 31 Oct 2019 12:24:09 +0000 (13:24 +0100)
committerKarol Lewandowski <k.lewandowsk@samsung.com>
Thu, 16 Jan 2020 12:15:23 +0000 (13:15 +0100)
Signed-off-by: Mateusz Moscicki <m.moscicki2@partner.samsung.com>
Change-Id: I4f27610c7facfc48bc532bb5a943beb699a41dfd

16 files changed:
CMakeLists.txt [new file with mode: 0644]
LICENSE [new file with mode: 0644]
packaging/dumpsys-system_tests.spec [new file with mode: 0644]
packaging/dumpsys.spec [new file with mode: 0644]
src/client-api/CMakeLists.txt [new file with mode: 0644]
src/client-api/dumpsys-system.c [new file with mode: 0644]
src/client-api/dumpsys-system.h [new file with mode: 0644]
src/client-api/dumpsys-system.pc.in [new file with mode: 0644]
src/dumpsys/CMakeLists.txt [new file with mode: 0644]
src/dumpsys/dumpsys-run.conf [new file with mode: 0644]
src/dumpsys/dumpsys.c [new file with mode: 0644]
src/dumpsys/dumpsys.conf [new file with mode: 0644]
src/shared/common.h [new file with mode: 0644]
tests/CMakeLists.txt [new file with mode: 0644]
tests/system/util/CMakeLists.txt [new file with mode: 0644]
tests/system/util/test-app.c [new file with mode: 0644]

diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644 (file)
index 0000000..086094f
--- /dev/null
@@ -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 (file)
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 (file)
index 0000000..3084f8a
--- /dev/null
@@ -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 (file)
index 0000000..a300d2c
--- /dev/null
@@ -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 (file)
index 0000000..b07609c
--- /dev/null
@@ -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 (file)
index 0000000..ffb4f24
--- /dev/null
@@ -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 <assert.h>
+#include <gio/gio.h>
+#include <gio/gunixfdlist.h>
+#include <libgen.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <tizen_error.h>
+#include <unistd.h>
+
+#include "dumpsys-system.h"
+#include "common.h"
+
+#ifndef LOG_TAG
+#define LOG_TAG "DUMPSYS_SYSTEM"
+#endif
+
+#include <dlog.h>
+
+#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[] =
+"<node>"
+       "<interface name='org.tizen.dumpsys'>"
+               "<method name='Dump'>"
+                       "<arg type='as' name='args' direction='in'/>"
+                       "<arg type='i' name='result' direction='out'/>"
+               "</method>"
+       "</interface>"
+"</node>";
+
+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 (file)
index 0000000..579bb3f
--- /dev/null
@@ -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 (file)
index 0000000..b6c20fb
--- /dev/null
@@ -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 (file)
index 0000000..19af459
--- /dev/null
@@ -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 (file)
index 0000000..9cf3269
--- /dev/null
@@ -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 (file)
index 0000000..7d4a746
--- /dev/null
@@ -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 <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <gio/gio.h>
+#include <gio/gunixfdlist.h>
+#include <linux/limits.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <errno.h>
+#include <dirent.h>
+#include <ctype.h>
+
+#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(&copy_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_name> -- [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 (file)
index 0000000..de9b8ee
--- /dev/null
@@ -0,0 +1,9 @@
+<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
+ "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
+<busconfig>
+    <policy context="default">
+        <check own_prefix="org.tizen.dumpsys" privilege="http://tizen.org/privilege/internal/default/platform"/>
+        <check send_destination_prefix="org.tizen.dumpsys" privilege="http://tizen.org/privilege/internal/default/platform"/>
+    </policy>
+</busconfig>
+
diff --git a/src/shared/common.h b/src/shared/common.h
new file mode 100644 (file)
index 0000000..d622012
--- /dev/null
@@ -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 (file)
index 0000000..a79f14e
--- /dev/null
@@ -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 (file)
index 0000000..a7444ae
--- /dev/null
@@ -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 (file)
index 0000000..a9189f3
--- /dev/null
@@ -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 <stdio.h>
+#include <stdbool.h>
+#include <gio/gio.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <tizen_error.h>
+#include "common.h"
+
+#include <dumpsys-system.h>
+
+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 <name> <message>\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;
+}