# Sub modules
ADD_SUBDIRECTORY(src/client-api)
ADD_SUBDIRECTORY(src/dumpsys)
+ADD_SUBDIRECTORY(src/agent)
+ADD_SUBDIRECTORY(src/service)
+ADD_SUBDIRECTORY(tests)
+++ /dev/null
-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
--- /dev/null
+<manifest>
+ <request>
+ <domain name="_"/>
+ </request>
+</manifest>
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
%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}
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
--- /dev/null
+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)
--- /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 <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;
+}
--- /dev/null
+<!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>
--- /dev/null
+[Unit]
+Description=Dumpsys agent
+After=dbus.socket
+
+[Service]
+ExecStart=/usr/bin/dumpsys-session-agent
--- /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 <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();
+}
--- /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 __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__
/*
- * 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'>"
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)
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,
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);
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);
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,
GDBusMethodInvocation *invocation,
gpointer user_data)
{
- if (g_strcmp0(method_name, "Dump") == 0)
+ if (g_strcmp0(method_name, METHOD_DUMP) == 0)
dump_handler(invocation);
}
&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;
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;
}
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;
}
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;
}
/*
- * 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.
* @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.
* @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.
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)
/*
- * 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));
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(©_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)
{
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) {
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;
}
+++ /dev/null
-<!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>
-
--- /dev/null
+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
+
--- /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 <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);
+}
--- /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 __LIBDUMPSYS_H__
+#define __LIBDUMPSYS_H__
+
+int dumpsys_dump(char *service_name, int argc, char **argv, int *out_fd);
+
+#endif // __LIBDUMPSYS_H__
--- /dev/null
+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)
--- /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 <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;
+}
--- /dev/null
+<!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>
+
--- /dev/null
+[Unit]
+Description=Dumpsys service
+DefaultDependencies=false
+After=dbus.socket systemd-tmpfiles-setup.service
+
+[Service]
+Type=simple
+User=log
+ExecStart=/usr/bin/dumpsys-service
/*
- * 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
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})
/*
- * 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;
}