%define _with_tests on
%define _with_livedumper on
%define _with_bugreportservice on
+%define _with_dumpsystemstateservice on
%bcond_with doc
%bcond_with tests
%bcond_with livedumper
%bcond_with bugreportservice
+%bcond_with dumpsystemstateservice
%if %{?_with_bugreportservice} == on
%define _with_livedumper on
BuildRequires: libcap-devel
BuildRequires: pkgconfig(json-c)
BuildRequires: pkgconfig(openssl1.1)
+%if %{with dumpsystemstateservice}
+BuildRequires: pkgconfig(dumpsys-system)
+%endif
%if %{with doc}
BuildRequires: doxygen
%if %{with bugreportservice}
Requires: %{name}-livedumper = %{version}-%{release}
%endif
+
%description -n crash-manager-core
%package -n libcrash-manager-devel
%description -n libbugreport-devel
%endif
+%if %{with dumpsystemstateservice}
+%package -n dumpsystemstate-service
+Summary: service provides dumpsys provider to system state information
+Requires: %{name}-dumpsystemstate-config = %{version}-%{release}
+%description -n dumpsystemstate-service
+%endif
+
%if %{with doc}
%package doc
Summary: Documentation package for crash-worker
-DCRASH_SYSTEM_TESTS_PATH=%{_libexecdir}/crash-worker/system-tests \
-DLIVEDUMPER=%{on_off livedumper} \
-DCRASH_SERVICE=%{on_off bugreportservice} \
+ -DDUMP_SYSTEMSTATE_SERVICE=%{on_off dumpsystemstateservice} \
-DUPGRADE_SCRIPT_PATH=%{upgrade_script_path} \
-DLOGGER=dlog \
-DDLOG_LOG_LEVEL=DLOG_INFO \
%{_libdir}/pkgconfig/bugreport.pc
%endif
+%if %{with dumpsystemstateservice}
+%files -n dumpsystemstate-service
+%license LICENSE
+%manifest crash-worker.manifest
+%attr(0750,crash_worker,crash_worker) %{_bindir}/dump_systemstate-service
+%attr(-,root,root) %{_unitdir}/dump_systemstate.service
+%attr(-,root,root) %{_datadir}/dbus-1/system-services/org.tizen.dumpsys.providers.org.tizen.systemstate.service
+%endif
+
%if %{with doc}
%files doc
%{_datadir}/doc/crash-worker
--- /dev/null
+/*
+ * dump_systemstate
+ *
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ *
+ * 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 <dumpsys-system.h>
+#include <gio/gio.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include "dump_systemstate/dump_systemstate.h"
+#include "dump_systemstate/extras.h"
+#include "shared/log.h"
+#include "shared/spawn.h"
+#include "shared/util.h"
+
+#define DUMP_SYSTEMSTATE_BUS_NAME "org.tizen.systemstate"
+#define DUMP_SYSTEMSTATE_OBJECT_PATH "/Org/Tizen/Systemstate"
+#define DUMP_SYSTEMSTATE_INTERFACE_NAME DUMP_SYSTEMSTATE_BUS_NAME
+#define TIMEOUT_INTERVAL_SEC 30
+
+static GMainLoop *loop = NULL;
+static GMutex timeout_mutex;
+static guint timeout_id;
+static GHashTable *commands = NULL;
+struct extra_items_vector commands_list = { .size = 0, .data = NULL };
+
+static int timeout_cb(gpointer data)
+{
+ assert(data);
+ _I("Time out!");
+ g_main_loop_quit((GMainLoop *)data);
+
+ return 0;
+}
+
+static void add_timeout(void)
+{
+ g_mutex_lock(&timeout_mutex);
+
+ if (timeout_id)
+ g_source_remove(timeout_id);
+ timeout_id = g_timeout_add_seconds(TIMEOUT_INTERVAL_SEC, timeout_cb, loop);
+
+ g_mutex_unlock(&timeout_mutex);
+ _D("Add loop timeout (%d)", TIMEOUT_INTERVAL_SEC);
+}
+
+static void remove_timeout(void)
+{
+ g_mutex_lock(&timeout_mutex);
+
+ if (timeout_id) {
+ g_source_remove(timeout_id);
+ timeout_id = 0;
+ }
+
+ g_mutex_unlock(&timeout_mutex);
+ _D("Remove loop timeout");
+}
+
+static char* join(int argc, char **argv, int offset)
+{
+ assert(argv);
+
+ if (offset <= argc)
+ return strdup("");
+
+ ssize_t length = 0;
+ for (int i = offset; i < argc; i++)
+ length += strlen(argv[i]) + 1;
+
+ char *result = malloc(length + 1);
+ if (result == NULL) {
+ _E("Out of memory");
+ return NULL;
+ }
+ char *pointer = result;
+
+ for (int i = offset; i < argc; i++) {
+ int printed = snprintf(pointer, strlen(argv[i]) + 2, "%s ", argv[i]);
+ if (printed < 0) {
+ free(result);
+ _E("Cannot concatenate strings: %m");
+ return NULL;
+ }
+
+ pointer += printed;
+ }
+ result[length-1] = '\0';
+ return result;
+}
+
+char** split(const char *str, size_t *size)
+{
+ assert(str);
+
+ char **result = NULL;
+ char *str_tmp = strdup(str);
+ if (str_tmp == NULL) {
+ _E("Out of memory");
+ return NULL;
+ }
+
+ char *chunk = strtok(str_tmp, " ");
+ *size = 0;
+
+ while (chunk != NULL) {
+ char **result_tmp = realloc(result, (*size + 1) * sizeof(char *));
+ char *chunk_copy = strdup(chunk);
+
+ if (result_tmp == NULL || chunk_copy == NULL) {
+ _E("Out of memory");
+ if (result_tmp != NULL)
+ free(result_tmp);
+ if (chunk_copy != NULL)
+ free(chunk_copy);
+ free(result);
+ result = NULL;
+ goto out;
+ }
+
+ result = result_tmp;
+ result[(*size)++] = chunk_copy;
+ chunk = strtok(NULL, " ");
+ }
+
+ char **result_tmp = realloc(result, (*size + 1) * sizeof(char *));
+ if (result_tmp == NULL) {
+ _E("Out of memory");
+ free(result);
+ result = NULL;
+ goto out;
+ }
+
+ result = result_tmp;
+ result[*size] = NULL;
+
+out:
+ free(str_tmp);
+ return result;
+}
+
+static void free_array(char **array)
+{
+ if (array != NULL) {
+ for (size_t i = 0; array[i] != NULL; i++)
+ free(array[i]);
+ free(array);
+ }
+}
+
+static char** build_argv_array(const char *path, int argc, char **argv)
+{
+ char **av = malloc((argc + 2) * sizeof(char *));
+ if (av == NULL) {
+ _E("Out of memory");
+ return NULL;
+ }
+
+ av[0] = strdup(path);
+ if (av[0] == NULL) {
+ _E("Out of memory");
+ free(av);
+ return NULL;
+ }
+
+ for (int i = 0; i < argc; i++) {
+ av[i + 1] = strdup(argv[i]);
+ if (av[i + 1] == NULL) {
+ _E("Out of memory");
+ free_array(av);
+ return NULL;
+ }
+ }
+
+ av[argc + 1] = NULL;
+
+ return av;
+}
+
+static char** prepare_dumpsys_args(const char *path, int dumpsys_argc, char **dumpsys_argv)
+{
+ return build_argv_array(path, dumpsys_argc - 1, dumpsys_argv+1);
+}
+
+static char** prepare_config_args(const char *path, char *args)
+{
+ size_t argc = 0;
+ char **argv = split(args, &argc);
+ return build_argv_array(path, argc, argv);
+}
+
+static char** prepare_args(struct extra_dump_item *item, const int dumpsys_argc, char **dumpsys_argv)
+{
+ bool allow_dumpsys_args = false;
+ if (item->fields[INI_FIELD_ALLOWDUMPSYSARGS] != NULL)
+ allow_dumpsys_args = strncasecmp(item->fields[INI_FIELD_ALLOWDUMPSYSARGS], "yes", 4) == 0;
+
+ if (allow_dumpsys_args && dumpsys_argc > 1)
+ return prepare_dumpsys_args(item->fields[INI_FIELD_PATH], dumpsys_argc, dumpsys_argv);
+ else
+ return prepare_config_args(item->fields[INI_FIELD_PATH], item->fields[INI_FIELD_ARGS]);
+}
+
+static char** prepare_env(const char *config_env)
+{
+ char **ev = NULL;
+
+ if (config_env != NULL) {
+ size_t size = 0;
+ ev = split(config_env, &size);
+ }
+
+ return ev;
+}
+
+static int dump_item(int fd, struct extra_dump_item *item, const int dumpsys_argc, char **dumpsys_argv)
+{
+ assert(item);
+
+ int result = TIZEN_ERROR_NONE;
+
+ char **av = prepare_args(item, dumpsys_argc, dumpsys_argv);
+
+ if (av == NULL)
+ goto out;
+
+ char **ev = prepare_env(item->fields[INI_FIELD_ENV]);
+ int err;
+ spawn_param_s param_stdout = { .fn = spawn_setstdout, .u.int_val = fd };
+ spawn_param_s param_stderr = { .fn = spawn_setstderr, .u.int_val = fd, .next = ¶m_stdout };
+
+ if (!spawn_wait(av, ev, ¶m_stderr, DEFAULT_COMMAND_TIMEOUT_MS, &err))
+ result = TIZEN_ERROR_INVALID_OPERATION;
+
+ free_array(ev);
+out:
+ free_array(av);
+
+ return result;
+}
+
+static bool dumpsys_enabled(struct extra_dump_item *item)
+{
+ assert(item);
+
+ return item->fields[INI_FIELD_DUMPSYSCMD] != NULL && *(item->fields[INI_FIELD_DUMPSYSCMD]) != '\0';
+}
+
+static void print_header(int fd, struct extra_dump_item *item)
+{
+ assert(item);
+ dprintf(fd,
+ "\n==== %s (%s %s)\n",
+ item->fields[INI_FIELD_TITLE] ?: "",
+ item->fields[INI_FIELD_PATH] ?: "",
+ item->fields[INI_FIELD_ARGS] ?: "");
+}
+
+static int dump_all_items(int fd)
+{
+ int result = TIZEN_ERROR_NONE;
+
+ for (size_t i = 0; i < commands_list.size; i++) {
+ if (dumpsys_enabled(&commands_list.data[i])) {
+ print_header(fd, &commands_list.data[i]);
+ result |= dump_item(fd, &commands_list.data[i], 0, NULL);
+ }
+ }
+
+ return result;
+}
+
+static int dumpsys_cb(const int fd, const int argc, char **argv)
+{
+ assert(fd >= 0);
+ assert(argv);
+
+ if (commands == NULL) {
+ _E("Commands not initialized");
+ return TIZEN_ERROR_INVALID_OPERATION;
+ }
+
+ int result = TIZEN_ERROR_NONE;
+ if (argc == 0) {
+ result = dump_all_items(fd);
+ } else {
+ struct extra_dump_item *item = g_hash_table_lookup(commands, argv[0]);
+ if (item == NULL) {
+ _E("Command not found: %s", argv[0]);
+ return TIZEN_ERROR_INVALID_PARAMETER;
+ }
+ result = dump_item(fd, item, argc, argv);
+ }
+
+ return result;
+}
+
+static void free_item(struct extra_dump_item *item)
+{
+ assert(item);
+
+ for (size_t i = 0; i < ARRAY_SIZE(item->fields); i++)
+ free(item->fields[i]);
+}
+
+static int fill_commands()
+{
+ int result = read_ini_files(STDOUT_FILENO, DUMP_SYSTEMSTATE_CONFIG_DIR_PROGRAMS_PATH, &commands_list);
+
+ if (result != EXIT_OK)
+ return result;
+
+ commands = g_hash_table_new(g_str_hash, g_str_equal);
+ if (commands == NULL) {
+ _E("Out of memory");
+ return EXIT_ERR;
+ }
+
+ struct extra_dump_item *item = commands_list.data;
+
+ for (size_t i = 0; i < commands_list.size; i++, item++) {
+ char *dumpcmd = item->fields[INI_FIELD_DUMPSYSCMD];
+
+ if (dumpcmd != NULL && strlen(dumpcmd) > 0) {
+ if (g_hash_table_lookup(commands, dumpcmd) != NULL) {
+ _I("Duplicated command: %s", dumpcmd);
+ free_item(item);
+ } else {
+ _D("New dumpsys command: %s", dumpcmd);
+ g_hash_table_insert(commands, dumpcmd, item);
+ }
+ }
+ }
+
+ return result;
+}
+
+int main()
+{
+ void *handler = NULL;
+ int exit_code = EXIT_SUCCESS;
+
+ if (fill_commands() != EXIT_OK) {
+ exit_code = EXIT_FAILURE;
+ goto out;
+ }
+
+ loop = g_main_loop_new(NULL, false);
+
+ if (dumpsys_system_register_dump_cb(dumpsys_cb, DUMP_SYSTEMSTATE_BUS_NAME, &handler) != TIZEN_ERROR_NONE) {
+ _E("Callback registration error");
+ exit_code = EXIT_FAILURE;
+ goto out;
+ }
+
+ g_mutex_init(&timeout_mutex);
+ add_timeout();
+ g_main_loop_run(loop);
+
+ g_mutex_clear(&timeout_mutex);
+
+out:
+ if (loop != NULL)
+ g_main_loop_unref(loop);
+
+ if (handler != NULL)
+ dumpsys_system_unregister_dump_cb(handler);
+
+ if (commands != NULL)
+ g_hash_table_destroy(commands);
+
+ for (size_t i = 0; i < commands_list.size; i++)
+ free_item(&commands_list.data[i]);
+
+ return exit_code;
+}