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