Modify dumpsys architecture 67/222967/8
authorMateusz Moscicki <m.moscicki2@partner.samsung.com>
Tue, 21 Jan 2020 15:12:48 +0000 (16:12 +0100)
committerMateusz Moscicki <m.moscicki2@partner.samsung.com>
Wed, 4 Mar 2020 12:58:54 +0000 (13:58 +0100)
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

25 files changed:
CMakeLists.txt
packaging/dumpsys-system_tests.spec [deleted file]
packaging/dumpsys.manifest [new file with mode: 0644]
packaging/dumpsys.spec
src/agent/CMakeLists.txt [new file with mode: 0644]
src/agent/dumpsys-agent.c [new file with mode: 0644]
src/agent/dumpsys-agent.conf [new file with mode: 0644]
src/agent/dumpsys-session-agent.service [new file with mode: 0644]
src/agent/libdumpsys-agent.c [new file with mode: 0644]
src/agent/libdumpsys-agent.h [new file with mode: 0644]
src/client-api/dumpsys-system.c
src/client-api/dumpsys-system.h
src/dumpsys/CMakeLists.txt
src/dumpsys/dumpsys.c
src/dumpsys/dumpsys.conf [deleted file]
src/dumpsys/dumpsys.pc.in [new file with mode: 0644]
src/dumpsys/libdumpsys.c [new file with mode: 0644]
src/dumpsys/libdumpsys.h [new file with mode: 0644]
src/service/CMakeLists.txt [new file with mode: 0644]
src/service/dumpsys-service.c [new file with mode: 0644]
src/service/dumpsys-service.conf [new file with mode: 0644]
src/service/dumpsys-service.service [new file with mode: 0644]
src/shared/common.h
tests/system/util/CMakeLists.txt
tests/system/util/test-app.c

index 086094f..665b768 100644 (file)
@@ -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 (file)
index 3084f8a..0000000
+++ /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 (file)
index 0000000..75b0fa5
--- /dev/null
@@ -0,0 +1,5 @@
+<manifest>
+    <request>
+        <domain name="_"/>
+    </request>
+</manifest>
index a300d2c..e32522d 100644 (file)
@@ -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 (file)
index 0000000..54aa7a0
--- /dev/null
@@ -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 (file)
index 0000000..07b27d9
--- /dev/null
@@ -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 <assert.h>
+#include <gio/gio.h>
+#include <stdlib.h>
+#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 (file)
index 0000000..3dbe693
--- /dev/null
@@ -0,0 +1,12 @@
+<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
+ "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
+<busconfig>
+    <policy user="log">
+        <allow own="org.tizen.dumpsys.agent"/>
+        <allow send_destination="org.tizen.dumpsys.agent"/>
+    </policy>
+    <policy context="default">
+        <deny own="org.tizen.dumpsys.agent"/>
+        <deny send_destination="org.tizen.dumpsys.agent"/>
+    </policy>
+</busconfig>
diff --git a/src/agent/dumpsys-session-agent.service b/src/agent/dumpsys-session-agent.service
new file mode 100644 (file)
index 0000000..4dff996
--- /dev/null
@@ -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 (file)
index 0000000..6714ff6
--- /dev/null
@@ -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 <assert.h>
+#include <ctype.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <gio/gio.h>
+#include <gio/gunixfdlist.h>
+#include <linux/limits.h>
+#include <pthread.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#ifndef LOG_TAG
+#define LOG_TAG "LIBDUMPSYS_AGENT"
+#endif
+
+#include <dlog.h>
+
+#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[] =
+
+"<node>"
+        "<interface name='org.tizen.dumpsys.agent'>"
+                "<method name='Dump'>"
+                        "<arg type='as' name='args' direction='in'/>"
+                        "<arg type='s' name='name' direction='in'/>"
+                        "<arg type='i' name='result' direction='out'/>"
+                "</method>"
+        "</interface>"
+"</node>";
+
+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 (file)
index 0000000..14796ca
--- /dev/null
@@ -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 <stdbool.h>
+#include <gio/gio.h>
+
+bool dumpsys_agent_run(GMainLoop *loop, GBusType bus_type);
+void dumpsys_agent_stop();
+
+#endif  // __LIBDUMPSYS_AGENT_H__
index ffb4f24..a3c7fb2 100644 (file)
@@ -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.
 #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[] =
 "<node>"
        "<interface name='org.tizen.dumpsys'>"
@@ -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;
        }
 
index 579bb3f..1abadad 100644 (file)
@@ -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.
index 19af459..44f5dfc 100644 (file)
@@ -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)
index 819a7b5..c4049ae 100644 (file)
@@ -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.
  * limitations under the License.
  */
 
-#include <stdio.h>
-#include <stdlib.h>
-#include <stdbool.h>
-#include <unistd.h>
+#include <ctype.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
 #include <gio/gio.h>
 #include <gio/gunixfdlist.h>
 #include <linux/limits.h>
+#include <pthread.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.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 <sys/types.h>
+#include <unistd.h>
+#include <libdumpsys.h>
+#include <tizen_error.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)
+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(&copy_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 (file)
index de9b8ee..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-<!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/dumpsys/dumpsys.pc.in b/src/dumpsys/dumpsys.pc.in
new file mode 100644 (file)
index 0000000..cba8db6
--- /dev/null
@@ -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 (file)
index 0000000..5875b03
--- /dev/null
@@ -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 <assert.h>
+#include <ctype.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <gio/gio.h>
+#include <gio/gunixfdlist.h>
+#include <linux/limits.h>
+#include <pthread.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <tizen_error.h>
+#include <unistd.h>
+
+#ifndef LOG_TAG
+#define LOG_TAG "LIBDUMPSYS"
+#endif
+
+#include <dlog.h>
+
+#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 (file)
index 0000000..9cf80f2
--- /dev/null
@@ -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 (file)
index 0000000..6d443c1
--- /dev/null
@@ -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 (file)
index 0000000..7e7c54d
--- /dev/null
@@ -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 <assert.h>
+#include <ctype.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <gio/gio.h>
+#include <gio/gunixfdlist.h>
+#include <linux/limits.h>
+#include <pthread.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "libdumpsys-agent.h"
+
+#ifndef LOG_TAG
+#define LOG_TAG "DUMPSYS_SERVICE"
+#endif
+
+#include <dlog.h>
+
+#include "common.h"
+
+#define FIFO_BASE_DIR "/run/dumpsys/priv/fifo"
+
+static GDBusNodeInfo *introspection_data;
+static const gchar introspection_xml[] =
+"<node>"
+        "<interface name='org.tizen.dumpsys.service'>"
+                "<method name='Dump'>"
+                        "<arg type='as' name='args' direction='in'/>"
+                        "<arg type='s' name='name' direction='in'/>"
+                        "<arg type='i' name='result' direction='out'/>"
+                "</method>"
+                "<method name='Register'>"
+                        "<arg type='b' name='result' direction='out'/>"
+                "</method>"
+        "</interface>"
+"</node>";
+
+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 (file)
index 0000000..f5851f8
--- /dev/null
@@ -0,0 +1,19 @@
+<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
+ "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
+<busconfig>
+    <policy user="log">
+        <allow own="org.tizen.dumpsys.service"/>
+        <allow send_destination_prefix="org.tizen.dumpsys"/>
+    </policy>
+    <policy user="root">
+        <allow own_prefix="org.tizen.dumpsys"/>
+        <allow send_destination="org.tizen.dumpsys.service"/>
+    </policy>
+    <policy context="default">
+        <deny own_prefix="org.tizen.dumpsys"/>
+        <deny own="org.tizen.dumpsys.service"/>
+        <deny send_destination="org.tizen.dumpsys.service"/>
+        <deny send_destination_prefix="org.tizen.dumpsys"/>
+    </policy>
+</busconfig>
+
diff --git a/src/service/dumpsys-service.service b/src/service/dumpsys-service.service
new file mode 100644 (file)
index 0000000..b6ea95a
--- /dev/null
@@ -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
index d622012..4384377 100644 (file)
@@ -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.
 #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
index a7444ae..fd37c13 100644 (file)
@@ -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})
 
index a9189f3..57c246c 100644 (file)
@@ -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.
  * limitations under the License.
  */
 
-#include <stdio.h>
-#include <stdbool.h>
+#include <errno.h>
+#include <fcntl.h>
 #include <gio/gio.h>
-#include <unistd.h>
-#include <string.h>
+#include <stdbool.h>
+#include <stdio.h>
 #include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
 #include <tizen_error.h>
-#include "common.h"
-
+#include <unistd.h>
 #include <dumpsys-system.h>
 
+#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 <name> <message>\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 <name> <message>\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;
        }