Add library providing API for user app services. 85/226285/7
authorMateusz Moscicki <m.moscicki2@partner.samsung.com>
Thu, 27 Feb 2020 16:58:01 +0000 (17:58 +0100)
committerMateusz Moscicki <m.moscicki2@partner.samsung.com>
Mon, 9 Mar 2020 12:01:12 +0000 (13:01 +0100)
Change-Id: Idd1c4b763a6377912d62fa38a0d79a83a09b5122

packaging/dumpsys.spec
src/client-api/CMakeLists.txt
src/client-api/dumpsys-user.c [new file with mode: 0644]
src/client-api/dumpsys-user.h [new file with mode: 0644]
src/client-api/dumpsys-user.pc.in [new file with mode: 0644]
src/service/dumpsys-service.conf

index e32522d..760b45a 100644 (file)
@@ -30,6 +30,18 @@ Summary: libdumpsys-system development package.
 %description -n libdumpsys-system-devel
 This package provides library and header files.
 
+%package -n libdumpsys-user
+Summary: Package with dumpsys API library for services.
+BuildRequires: pkgconfig(pkgmgr-info)
+%description -n libdumpsys-user
+This package provides dumpsys API library for services.
+
+%package -n libdumpsys-user-devel
+Requires: libdumpsys-user
+Summary: libdumpsys-user development package.
+%description -n libdumpsys-user-devel
+This package provides library and header files.
+
 %package tests
 Summary: Contains scripts for testing dumpsys
 %description tests
@@ -94,6 +106,16 @@ ln -s ../dumpsys-session-agent.service %{buildroot}/%{_unitdir_user}/delayed.tar
 %{_libdir}/libdumpsys-system.so
 %{_libdir}/pkgconfig/dumpsys-system.pc
 
+%files -n libdumpsys-user
+%manifest %{name}.manifest
+%{_libdir}/libdumpsys-user.so.*
+
+%files -n libdumpsys-user-devel
+%manifest %{name}.manifest
+%{_includedir}/dumpsys-user.h
+%{_libdir}/libdumpsys-user.so
+%{_libdir}/pkgconfig/dumpsys-user.pc
+
 %files tests
 %manifest %{name}.manifest
 %attr(0755,root,root) %{_libdir}/dumpsys_system_tests/utils/test-app
index b07609c..14484db 100644 (file)
@@ -6,7 +6,8 @@ INCLUDE(FindPkgConfig)
 pkg_check_modules(dumpsys-system_pkgs REQUIRED
        dlog
        gio-2.0
-       gio-unix-2.0)
+       gio-unix-2.0
+       pkgmgr-info)
 INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/src/shared)
 FOREACH(flag ${dumpsys-system_pkgs_CFLAGS})
        SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} ${flag}")
@@ -23,11 +24,25 @@ SET_TARGET_PROPERTIES(libdumpsys-system PROPERTIES
        OUTPUT_NAME dumpsys-system)
 TARGET_LINK_LIBRARIES(libdumpsys-system PUBLIC ${dumpsys-system_pkgs_LIBRARIES})
 
+
+ADD_LIBRARY(libdumpsys-user SHARED dumpsys-user.c)
+SET_TARGET_PROPERTIES(libdumpsys-user PROPERTIES
+       SOVERSION 1
+       PUBLIC_HEADER dumpsys-user.h
+       OUTPUT_NAME dumpsys-user)
+TARGET_LINK_LIBRARIES(libdumpsys-user PUBLIC ${dumpsys-system_pkgs_LIBRARIES})
+
 LINK_DIRECTORIES(${CMAKE_BINARY_DIR})
 
 CONFIGURE_FILE(dumpsys-system.pc.in dumpsys-system.pc @ONLY)
+CONFIGURE_FILE(dumpsys-user.pc.in dumpsys-user.pc @ONLY)
 
 INSTALL(TARGETS libdumpsys-system LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
        PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
 INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/dumpsys-system.pc
        DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
+
+INSTALL(TARGETS libdumpsys-user LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+       PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
+INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/dumpsys-user.pc
+       DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
diff --git a/src/client-api/dumpsys-user.c b/src/client-api/dumpsys-user.c
new file mode 100644 (file)
index 0000000..5fbf5e4
--- /dev/null
@@ -0,0 +1,427 @@
+/*
+ * Copyright (c) 2019-2020 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <assert.h>
+#include <gio/gio.h>
+#include <gio/gunixfdlist.h>
+#include <libgen.h>
+#include <pkgmgr-info.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 <sys/stat.h>
+#include <fcntl.h>
+
+#include "dumpsys-user.h"
+#include "common.h"
+
+#ifndef LOG_TAG
+#define LOG_TAG "DUMPSYS_USER"
+#endif
+
+#include <dlog.h>
+
+#define APPID_MAX 128
+#define DBUS_BUS_NAME_MAX_LEN 255
+
+#define DCA_ERROR 1
+#define DCA_ERROR_UNSPECIFIED_ERROR 1
+#define DCA_ERROR_GETEXEPATH 2
+#define DCA_ERROR_GETAPPID 3
+#define DCA_ERROR_UNIXFDLISTEMPTY 4
+
+static dumpsys_dump_cb dump_cb;
+static GDBusNodeInfo *introspection_data;
+static GDBusConnection *connection;
+static guint reg_id, own_id;
+static char *service_name;
+
+static const gchar introspection_xml[] =
+"<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 dumpsys_dump_data {
+       int fd;
+       int argc;
+       char **argv;
+};
+
+typedef struct dumpsys_dump_data* dumpsys_dump_h;
+
+static int appinfo_get_appid_func(pkgmgrinfo_appinfo_h handle, void *user_data)
+{
+       char *str = NULL;
+
+       int ret = pkgmgrinfo_appinfo_get_appid(handle, &str);
+       if (ret == PMINFO_R_OK && str)
+               (*(char **)user_data) = strdup(str);
+
+       return ret;
+}
+
+static bool get_exepath(char *exepath, int len)
+{
+       int fd = open("/proc/self/cmdline", O_RDONLY);
+
+       if (fd == -1) {
+               LOGE("open() cmdline error: %m\n");
+               return false;
+       }
+
+       ssize_t res;
+
+       if ((res = read(fd, exepath, len)) == -1) {
+               LOGE("read() cmdline error: %m\n");
+               close(fd);
+               return false;
+       }
+
+       close(fd);
+
+       exepath[res] = '\0';
+       return true;
+}
+
+static bool get_appid(char *exepath, char *appid, int len)
+{
+       pkgmgrinfo_appinfo_filter_h handle = NULL;
+       int count, ret;
+       char *aid = NULL;
+       bool result = false;
+
+       ret = pkgmgrinfo_appinfo_filter_create(&handle);
+       if (ret != PMINFO_R_OK) {
+               LOGE("pkgmgrinfo_appinfo_filter_create() error: %d\n", ret);
+               goto out;
+       }
+
+       ret = pkgmgrinfo_appinfo_filter_add_string(handle, PMINFO_APPINFO_PROP_APP_EXEC, exepath);
+       if (ret != PMINFO_R_OK) {
+               LOGE("pkgmgrinfo_appinfo_filter_add_string() error: %d\n", ret);
+               goto out_free;
+       }
+
+       ret = pkgmgrinfo_appinfo_filter_count(handle, &count);
+       if (ret != PMINFO_R_OK) {
+               LOGE("pkgmgrinfo_appinfo_filter_count() error: %d\n", ret);
+               goto out_free;
+       }
+
+       if (count < 1) {
+               LOGI("appid not found for %s\n", exepath);
+               goto out_free;
+       }
+
+       ret = pkgmgrinfo_appinfo_filter_foreach_appinfo(handle, appinfo_get_appid_func, &aid);
+       if (ret != PMINFO_R_OK) {
+               LOGE("pkgmgrinfo_appinfo_filter_foreach_appinfo() error: %d\n", ret);
+               goto out_free;
+       }
+
+       if (aid) {
+               snprintf(appid, len, "%s", aid);
+               result = true;
+               free(aid);
+       }
+
+out_free:
+       pkgmgrinfo_appinfo_filter_destroy(handle);
+out:
+       return result;
+}
+
+static bool get_name(char **name)
+{
+       assert(name);
+
+       char app_id[APPID_MAX];
+       char exe_path[PATH_MAX];
+
+       if (!get_exepath(exe_path, sizeof(exe_path)))
+               return false;
+
+       if (get_appid(exe_path, app_id, sizeof(app_id))) {
+               *name = strdup(app_id);
+               return true;
+       }
+
+       return false;
+}
+
+static int get_args(GVariant *vargs, gchar ***argv)
+{
+       assert(vargs);
+       assert(argv);
+
+       gsize count = -1;
+
+       GVariant *array = g_variant_get_child_value(vargs, 0);
+       *argv = g_variant_dup_strv(array, &count);
+       return count;
+}
+
+static void dump_handler(GDBusMethodInvocation *invocation)
+{
+       assert(invocation);
+       assert(dump_cb);
+
+       GDBusMessage *msg = g_dbus_method_invocation_get_message(invocation);
+       if (msg == NULL) {
+               LOGE("g_dbus_method_invocation_get_message() error");
+               return;
+       }
+
+       GUnixFDList *fd_list = g_dbus_message_get_unix_fd_list(msg);
+       if (fd_list == NULL) {
+               LOGE("No file descriptor provided");
+               g_dbus_method_invocation_return_error(invocation,
+                                                     DCA_ERROR,
+                                                     DCA_ERROR_UNIXFDLISTEMPTY,
+                                                     "No file descriptor provided");
+               return;
+       }
+
+       GError *error = NULL;
+
+       int fd = g_unix_fd_list_get(fd_list, 0, &error);
+       if (fd == -1) {
+               LOGE("g_unix_fd_list_get() error: %s", error ? error->message : "(none)");
+               g_dbus_method_invocation_return_gerror(invocation, error);
+               if (error)
+                       g_error_free(error);
+               return;
+       }
+
+       GVariant *body = g_dbus_message_get_body(msg);
+
+       struct dumpsys_dump_data *dump_data = malloc(sizeof(*dump_data));
+       if (dump_data == NULL) {
+               LOGE("malloc error: %m");
+               return;
+       }
+       dump_data->fd = fd;
+       dump_data->argc = get_args(body, &dump_data->argv);
+
+       GTask *task = g_task_new(NULL, NULL, NULL, NULL);
+       g_task_set_task_data(task, dump_data, NULL);
+
+       g_object_unref(task);
+       g_dbus_method_invocation_return_value(invocation,
+                                             g_variant_new("(i)", DUMP_SUCCESS));
+       dump_cb(dump_data);
+       close(dump_data->fd);
+       free(dump_data);
+}
+
+static void method_call_handler(GDBusConnection *conn,
+                                const gchar *sender,
+                                const gchar *object_path,
+                                const gchar *iface_name,
+                                const gchar *method_name,
+                                GVariant *parameters,
+                                GDBusMethodInvocation *invocation,
+                                gpointer user_data)
+{
+       if (g_strcmp0(method_name, METHOD_DUMP) == 0)
+               dump_handler(invocation);
+}
+
+static const GDBusInterfaceVTable interface_vtable = {
+       method_call_handler, NULL, NULL
+};
+
+static bool register_object(GDBusConnection *conn)
+{
+       GError *error = NULL;
+       reg_id = g_dbus_connection_register_object(conn,
+                                                   DUMPSYS_PATH,
+                                                   introspection_data->interfaces[0],
+                                                   &interface_vtable,
+                                                   NULL,
+                                                   NULL,
+                                                   &error);
+
+       if (!reg_id) {
+               LOGE("g_dbus_connection_register_object() error: %s", error ? error->message : "(none)");
+               if (error)
+                       g_error_free(error);
+               return false;
+       }
+       return true;
+}
+
+static GBusType get_bus_type()
+{
+       return (getuid() == 0) ? G_BUS_TYPE_SYSTEM : G_BUS_TYPE_SESSION;
+}
+
+static bool dbus_init()
+{
+       introspection_data = g_dbus_node_info_new_for_xml(introspection_xml, NULL);
+       if (introspection_data == NULL) {
+               LOGE("g_dbus_node_info_new_for_xml() error");
+               return false;
+       }
+
+       GError *error = NULL;
+
+       GBusType default_bus_type = get_bus_type();
+       connection = g_bus_get_sync(default_bus_type, NULL, &error);
+
+       if (!connection || error != NULL) {
+               LOGE("connect to the bus error: %s", error ? error->message : "(none)");
+               if (error)
+                       g_error_free(error);
+               return false;
+       }
+
+       char name_with_prefix[DBUS_BUS_NAME_MAX_LEN];
+       int ret = snprintf(name_with_prefix, sizeof(name_with_prefix), "%s.%s", DUMPSYS_SERVICE_NAME_PREFIX, service_name);
+       if (ret < 0) {
+               LOGE("snprintf error: %m");
+               return false;
+       }
+
+       own_id = g_bus_own_name_on_connection(connection,
+                                             name_with_prefix,
+                                             G_BUS_NAME_OWNER_FLAGS_NONE,
+                                             NULL,
+                                             NULL,
+                                             NULL,
+                                             NULL);
+
+       return register_object(connection);
+}
+
+static bool dbus_close()
+{
+       if (!connection)
+               return false;
+
+       bool res = g_dbus_connection_unregister_object(connection, reg_id);
+       g_bus_unown_name(own_id);
+       g_object_unref(connection);
+       g_dbus_node_info_unref(introspection_data);
+       connection = NULL;
+       reg_id = 0;
+       own_id = 0;
+       return res;
+}
+
+static int register_callback(dumpsys_dump_cb callback, const char *name)
+{
+       assert(callback);
+
+       if (name) {
+               service_name = strdup(name);
+               if (!service_name) {
+                       LOGE("strdup error: %m\n");
+                       return TIZEN_ERROR_INVALID_OPERATION;
+               }
+       } else if (!get_name(&service_name)) {
+               LOGE("get_name error\n");
+               return TIZEN_ERROR_INVALID_OPERATION;
+       }
+
+       bool result = dbus_init();
+       if (result)
+               dump_cb = callback;
+       return result ? TIZEN_ERROR_NONE : TIZEN_ERROR_IO_ERROR;
+
+}
+
+static int dumpsys_register_dump_name_cb(dumpsys_dump_cb callback, const char *name, void **handler)
+{
+       if (dump_cb != NULL) {
+               LOGE("register_dump_cb(): already registered\n");
+               return TIZEN_ERROR_ALREADY_IN_PROGRESS;
+       }
+
+       if (callback == NULL || handler == NULL) {
+               LOGE("register_me(): invalid parameter\n");
+               return TIZEN_ERROR_INVALID_PARAMETER;
+       }
+
+       int res = register_callback(callback, name);
+       if (res == TIZEN_ERROR_NONE)
+               *handler = (void*)callback;
+       return res;
+}
+
+int API_FUNCTION dumpsys_register_dump_cb(dumpsys_dump_cb callback, void **handler)
+{
+       return dumpsys_register_dump_name_cb(callback, NULL, handler);
+}
+
+int API_FUNCTION dumpsys_unregister_dump_cb(void *handler)
+{
+       if (handler == NULL) {
+               LOGE("dumpsys_unregister_dump_cb(): parameter is NULL\n");
+               return TIZEN_ERROR_INVALID_PARAMETER;
+       }
+
+       if (handler != (void*)dump_cb) {
+               LOGE("dumpsys_unregister_dump_cb(): handler not registered\n");
+               return TIZEN_ERROR_ALREADY_IN_PROGRESS;
+       }
+
+       dump_cb = NULL;
+       free(service_name);
+       return dbus_close() ? TIZEN_ERROR_NONE : TIZEN_ERROR_IO_ERROR;
+}
+
+int API_FUNCTION dumpsys_get_args_count(dumpsys_dump_h dump_handler, int *args_count)
+{
+       if (dump_handler == NULL || args_count == NULL) {
+               LOGE("dumpsys_get_args_count(): invalid parameter\n");
+               return TIZEN_ERROR_INVALID_PARAMETER;
+       }
+
+       *args_count = dump_handler->argc;
+
+       return TIZEN_ERROR_NONE;
+}
+
+int API_FUNCTION dumpsys_get_args_array(dumpsys_dump_h dump_handler, char ***args_array)
+{
+       if (dump_handler == NULL || args_array == NULL) {
+               LOGE("dumpsys_get_args_array(): invalid parameter\n");
+               return TIZEN_ERROR_INVALID_PARAMETER;
+       }
+
+       *args_array = dump_handler->argv;
+
+       return TIZEN_ERROR_NONE;
+}
+
+int API_FUNCTION dumpsys_write(struct dumpsys_dump_data *dump_handler, const void *buff, size_t count)
+{
+       if (dump_handler == NULL || buff == NULL) {
+               LOGE("dumpsys_write(): invalid parameter\n");
+               return TIZEN_ERROR_INVALID_PARAMETER;
+       }
+       return write(dump_handler->fd, buff, count) >= 0 ? TIZEN_ERROR_NONE : TIZEN_ERROR_IO_ERROR;
+}
diff --git a/src/client-api/dumpsys-user.h b/src/client-api/dumpsys-user.h
new file mode 100644 (file)
index 0000000..7d22993
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2019-2020 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __DUMPSYS_USER_H__
+#define __DUMPSYS_USER_H__
+
+#include <unistd.h>
+
+typedef struct dumpsys_dump_data* dumpsys_dump_h;
+/**
+ * @brief Callback function.
+ * @param[in] dump_context It is freed automatically after returning.
+ * @retval The result of the callback (currently not used).
+ */
+typedef int (*dumpsys_dump_cb)(dumpsys_dump_h dump_context);
+
+/**
+ * @brief Register the callback.
+ * @param[in] callback A callback for data retrieval.
+ * @param[out] context Value used to unregister specific callback.
+ * @retval TIZEN_ERROR_NONE Success.
+ * @retval TIZEN_ERROR_ALREADY_IN_PROGRESS Callback already registered.
+ * @retval TIZEN_ERROR_INVALID_PARAMETER Provided parameter is invalid.
+ * @retval TIZEN_ERROR_INVALID_OPERATION Internal error.
+ * @retval TIZEN_ERROR_IO_ERROR Error during DBus registration.
+ */
+extern int dumpsys_register_dump_cb(dumpsys_dump_cb callback, void **context);
+
+/**
+ * @brief Unregister the callback.
+ * @param[in] context Context returned by dumpsys_register_dump_cb().
+ * @retval TIZEN_ERROR_NONE Success.
+ * @retval TIZEN_ERROR_INVALID_PARAMETER Invalid context.
+ * @retval TIZEN_ERROR_ALREADY_IN_PROGRESS Context is not registered.
+ * @retval TIZEN_ERROR_IO_ERROR Error during DBus deregistration.
+ */
+extern int dumpsys_unregister_dump_cb(void *context);
+
+/**
+ * @brief Send data to the dump caller.
+ * @param[in] dump_context Dump context provided as callback argument.
+ * @param[in] buf A buffer containing data to write.
+ * @param[in] count Buffer size.
+ * @retval TIZEN_ERROR_NONE Success.
+ * @retval TIZEN_ERROR_INVALID_PARAMETER Invalid parameter value.
+ * @retval TIZEN_ERROR_IO_ERROR Data write error.
+ */
+extern int dumpsys_write(dumpsys_dump_h dump_context, const void *buf, size_t count);
+
+/**
+ * @brief Get arguments count
+ * @param[in] dump_context A value provided as callback argument
+ * @param[out] args_count Number of arguments
+ * @retval TIZEN_ERROR_NONE Success.
+ * @retval TIZEN_ERROR_INVALID_PARAMETER Invalid parameter value.
+ */
+extern int dumpsys_get_args_count(dumpsys_dump_h dump_context, int *args_count);
+
+/**
+ * @brief Get arguments array
+ * @param[in] dump_handler A handler provided as callback argument
+ * @param[out] args_array Array of null-terminated strings
+ * @retval TIZEN_ERROR_NONE Success.
+ * @retval TIZEN_ERROR_INVALID_PARAMETER Invalid parameter value.
+ */
+extern int dumpsys_get_args_array(dumpsys_dump_h dump_context, char ***args_array);
+#endif  // __DUMPSYS_USER_H__
diff --git a/src/client-api/dumpsys-user.pc.in b/src/client-api/dumpsys-user.pc.in
new file mode 100644 (file)
index 0000000..068be52
--- /dev/null
@@ -0,0 +1,11 @@
+prefix=/usr
+exec_prefix=${prefix}
+includedir=${prefix}/include
+libdir=${exec_prefix}/lib
+
+Name: dumpsys-user
+Description: The crash-manager library
+Version: @VERSION@
+Cflags: -I${includedir}
+Libs: -L${libdir} -ldumpsys-user
+
index f5851f8..cf7382a 100644 (file)
@@ -14,6 +14,8 @@
         <deny own="org.tizen.dumpsys.service"/>
         <deny send_destination="org.tizen.dumpsys.service"/>
         <deny send_destination_prefix="org.tizen.dumpsys"/>
+        <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>