--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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__