From 58d9de2f1de06b58ed63b453ef6fbc5074b0b8f2 Mon Sep 17 00:00:00 2001 From: Mateusz Moscicki Date: Fri, 19 Mar 2021 17:53:49 +0100 Subject: [PATCH] Allow to get bugreports using the diagnostics API bugreport-service registers as a org.tizen.bugreport-service and allows to get bugreport and crash-info report by dumpsys. For example: to get all bugreports created in the last hour run: dumpsys org.tizen.bugreport-service -- --type bugreport --last 3600 Supported parameters: --type Get specified report --last Get reports created in the last --from Get reports created after a specified time --to Get reports created before a specified time Change-Id: I0d3e04c34b63f3a559c8bde9af703eeabeb4e7ae --- packaging/crash-worker.spec | 1 + src/bugreport-service/CMakeLists.txt | 5 + src/bugreport-service/bugreport-service.c | 8 +- src/bugreport-service/diagnostics/diagnostics.c | 22 ++ src/bugreport-service/diagnostics/diagnostics.h | 4 +- .../diagnostics/diagnostics_dump.c | 355 +++++++++++++++++++++ .../diagnostics/diagnostics_dump.h | 19 ++ ...s.providers.org.tizen.bugreport-service.service | 4 + 8 files changed, 415 insertions(+), 3 deletions(-) create mode 100644 src/bugreport-service/diagnostics/diagnostics_dump.c create mode 100644 src/bugreport-service/diagnostics/diagnostics_dump.h create mode 100644 src/bugreport-service/org.tizen.dumpsys.providers.org.tizen.bugreport-service.service diff --git a/packaging/crash-worker.spec b/packaging/crash-worker.spec index 52ef210..4db8056 100644 --- a/packaging/crash-worker.spec +++ b/packaging/crash-worker.spec @@ -344,6 +344,7 @@ fi %attr(-,root,root) %{_sysconfdir}/dbus-1/system.d/bugreport-service.conf %attr(-,root,root) %{_datadir}/dbus-1/system-services/org.tizen.system.crash.livedump.service %attr(-,root,root) %{_datadir}/dbus-1/system-services/org.tizen.system.diagnostics.service +%attr(-,root,root) %{_datadir}/dbus-1/system-services/org.tizen.dumpsys.providers.org.tizen.bugreport-service.service %attr(0644,root,root) %{_prefix}/lib/tmpfiles.d/diagnostics-run.conf %if %{with doc} diff --git a/src/bugreport-service/CMakeLists.txt b/src/bugreport-service/CMakeLists.txt index 3d493be..b289d70 100644 --- a/src/bugreport-service/CMakeLists.txt +++ b/src/bugreport-service/CMakeLists.txt @@ -7,6 +7,7 @@ SET(CRASH_SERVICE_SRCS bugreport-service.c systemdump.c diagnostics/diagnostics.c + diagnostics/diagnostics_dump.c ${CMAKE_SOURCE_DIR}/src/shared/util.c ${CMAKE_SOURCE_DIR}/src/shared/spawn.c ${CMAKE_SOURCE_DIR}/src/shared/config.c @@ -22,6 +23,7 @@ pkg_check_modules(bugreport-service_pkgs REQUIRED gio-unix-2.0 libzip iniparser + diagnostics ) FOREACH(flag ${bugreport-service_pkgs_CFLAGS}) @@ -47,6 +49,9 @@ INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/org.tizen.system.crash.livedump.servic INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/diagnostics/org.tizen.system.diagnostics.service DESTINATION /usr/share/dbus-1/system-services) +INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/org.tizen.dumpsys.providers.org.tizen.bugreport-service.service + DESTINATION /usr/share/dbus-1/system-services) + INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/bugreport-service.conf DESTINATION /etc/dbus-1/system.d) diff --git a/src/bugreport-service/bugreport-service.c b/src/bugreport-service/bugreport-service.c index 3bea418..002149b 100644 --- a/src/bugreport-service/bugreport-service.c +++ b/src/bugreport-service/bugreport-service.c @@ -27,6 +27,7 @@ #include #include #include +#include #include "bugreport-service.h" @@ -35,6 +36,7 @@ #include "shared/log.h" #include "shared/util.h" #include "diagnostics/diagnostics.h" +#include "diagnostics/diagnostics_dump.h" #include "systemdump.h" /* Dbus activation */ @@ -510,8 +512,10 @@ int main(void) g_mutex_init(&timeout_mutex); add_timeout(); - diagnostics_set_client_id("org.tizen.bugreport-service"); - g_main_loop_run(loop); + if (diagnostics_init()) + g_main_loop_run(loop); + else + _E("Diagnostics API init error."); if (introspection_data) g_dbus_node_info_unref(introspection_data); diff --git a/src/bugreport-service/diagnostics/diagnostics.c b/src/bugreport-service/diagnostics/diagnostics.c index e66394f..defee17 100644 --- a/src/bugreport-service/diagnostics/diagnostics.c +++ b/src/bugreport-service/diagnostics/diagnostics.c @@ -41,12 +41,16 @@ #define TMP_FILE_TEMPLATE "file_XXXXXX" #define JSON_FILE_EXT ".json" +#define INFO_FILE_EXT ".info" +#define LOG_FILE_EXT ".log" #define ZIP_FILE_EXT ".zip" enum diagnostics_type { BT_UNKNOWN, BT_FULL_REPORT, BT_INFO_JSON, + BT_INFO_FILE, + BT_LOG, BT_DIR }; @@ -56,6 +60,10 @@ static char *type_to_str(enum diagnostics_entry entry) { return "FULL_REPORT"; case INFO_JSON: return "INFO_JSON"; + case INFO_FILE: + return "INFO_FILE"; + case LOG_FILE: + return "LOG_FILE"; default: return "UKNOWN"; } @@ -113,6 +121,16 @@ static enum diagnostics_type check_report_type(const char *path) return BT_INFO_JSON; } + if (strstr(path, INFO_FILE_EXT) == &path[strlen(path) - strlen(INFO_FILE_EXT)]) { + _D("Report type of %s is INFO_FILE\n", path); + return BT_INFO_FILE; + } + + if (strstr(path, LOG_FILE_EXT) == &path[strlen(path) - strlen(LOG_FILE_EXT)]) { + _D("Report type of %s is LOG_FILE\n", path); + return BT_LOG; + } + if (strstr(path, ZIP_FILE_EXT) == &path[strlen(path) - strlen(ZIP_FILE_EXT)]) { _D("Report type of %s is FULL_REPORT\n", path); return BT_FULL_REPORT; @@ -154,6 +172,10 @@ static bool file_match(const char *file_name, enum diagnostics_entry entry) switch(entry) { case INFO_JSON: return ends_with(file_name, JSON_FILE_EXT); + case INFO_FILE: + return ends_with(file_name, INFO_FILE_EXT); + case LOG_FILE: + return ends_with(file_name, LOG_FILE_EXT); default: return false; } diff --git a/src/bugreport-service/diagnostics/diagnostics.h b/src/bugreport-service/diagnostics/diagnostics.h index 8b2bea9..376ac54 100644 --- a/src/bugreport-service/diagnostics/diagnostics.h +++ b/src/bugreport-service/diagnostics/diagnostics.h @@ -25,7 +25,9 @@ enum diagnostics_entry { FULL_REPORT = 0, - INFO_JSON + INFO_JSON, + INFO_FILE, + LOG_FILE, }; enum diagnostics_metadata { diff --git a/src/bugreport-service/diagnostics/diagnostics_dump.c b/src/bugreport-service/diagnostics/diagnostics_dump.c new file mode 100644 index 0000000..f1fb212 --- /dev/null +++ b/src/bugreport-service/diagnostics/diagnostics_dump.c @@ -0,0 +1,355 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "diagnostics/diagnostics.h" +#include "diagnostics/diagnostics_dump.h" +#include "shared/log.h" +#include "shared/util.h" + +enum BUGREPORT_REPORT_TYPE { BR_UNKNOWN, BR_BUGREPORT, BR_CRASHINFO }; + +struct report_file { + char *file_name; + char *file_path; + struct timespec ctime; +}; + +static bool get_reports_list(const char *path, struct report_file **list, size_t *list_count) +{ + assert(list); + assert(list_count); + + DIR *dp; + struct dirent *ep; + dp = opendir(path); + if (dp == NULL) { + _E("Open directory %s error: %m", path); + return false; + } + + while ((ep = readdir(dp))) { + if (ep->d_type != 8) + continue; + struct stat st; + char *file_path = NULL; + if (asprintf(&file_path, "%s/%s", path, ep->d_name) == -1) { + _E("Memory allocation error: %m"); + return false; + } + if (stat(file_path, &st) != 0) { + _E("Get stats about %s error: %m", path); + return false; + } + struct tm t; + if (localtime_r(&(st.st_ctim.tv_sec), &t) == NULL) { + _E("localtime_r error: %m"); + return false; + } + + *list = realloc(*list, sizeof(struct report_file)*(*list_count + 1)); + (*list)[*list_count].file_name = strdup(ep->d_name); + (*list)[*list_count].file_path = strdup(file_path); + (*list)[*list_count].ctime = st.st_ctim; + (*list_count)++; + } + closedir(dp); + return true; +} + +static void free_record(struct report_file *record) +{ + free(record->file_name); + free(record->file_path); +} + +static void filter_time(struct report_file *list, size_t *list_count, long time_from, long time_to) +{ + size_t dest = 0; + size_t n_list_count = *list_count; + + struct timespec current_ts; + clock_gettime(CLOCK_REALTIME, ¤t_ts); + + for (int i = 0; i < *list_count; i++) { + if (list[i].ctime.tv_sec >= time_from && list[i].ctime.tv_sec < time_to) { + if (dest != i) + list[dest] = list[i]; + dest++; + } else { + free_record(&list[i]); + n_list_count--; + } + } + *list_count = n_list_count; +} + +static int ctime_compare(const void *a, const void *b) +{ + return ((struct report_file*)a)->ctime.tv_sec - ((struct report_file*)b)->ctime.tv_sec; +} + +static struct report_file* get_reports(long time_from, long time_to, size_t *count) +{ + struct report_file *list = NULL; + *count = 0; + + const char *dirs[] = { + CRASH_ROOT_PATH CRASH_PATH_SUBDIR, + CRASH_ROOT_PATH LIVE_PATH_SUBDIR + }; + + for (int i = 0; i < ARRAY_SIZE(dirs); i++) { + if (file_exists(dirs[i]) && !get_reports_list(dirs[i], &list, count)) { + _E("Can not read reports from: %s", dirs[i]); + return NULL; + } + } + + filter_time(list, count, time_from, time_to); + + qsort(list, *count, sizeof(struct report_file), ctime_compare); + return list; +} + +static void write_bugreport_log_file(int out_fd, const char *report_path) +{ + int in_fd = diagnostics_report_get_file(report_path, LOG_FILE); + if (in_fd < 0) { + _D("No LOG_FILE report in %s file", report_path); + return; + } + + if (dprintf(out_fd, "Begin systemtate-log\n") == -1) { + _E("Write error: %m"); + goto out; + } + + if (copy_bytes(out_fd, in_fd, NULL) == -1) { + _E("Copy data error"); + goto out; + } + +out: + close(in_fd); + if (dprintf(out_fd, "End systemtate-log\n") == -1) + _E("Write error: %m"); +} + +static bool write_bugreport(int fd, long time_from, long time_to) +{ + size_t list_count = 0; + bool result = true; + + struct report_file *list = get_reports(time_from, time_to, &list_count); + if (list == NULL) { + dprintf(fd, "Internal error.\n"); + return false; + } + + for (int i = 0; i < list_count; i++) { + if (dprintf(fd, "%sBegin bugreport <%s>\n", (i > 0) ? "\n" : "", list[i].file_path) == -1) { + _E("Write error: %m"); + result = false; + goto out; + } + + write_bugreport_log_file(fd, list[i].file_path); + + if (dprintf(fd, "End bugreport <%s>\n", list[i].file_path) == -1) { + _E("Write error: %m"); + result = false; + goto out; + } + } + +out: + for (int i = 0; i < list_count; i++) + free_record(&list[i]); + free(list); + return result; +} + +static bool write_crash_info(int fd, long time_from, long time_to) +{ + bool result = true; + size_t list_count = 0; + + struct report_file *list = get_reports(time_from, time_to, &list_count); + if (list == NULL) { + dprintf(fd, "Internal error.\n"); + return false; + } + + for (int i = 0; i < list_count; i++) { + int nfd = diagnostics_report_get_file(list[i].file_path, INFO_FILE); + if (nfd <= 0) { + _I("No INFO_FILE in %s file", list[i].file_path); + dprintf(nfd, "No INFO_FILE in %s\n", list[i].file_path); + continue; + } + + if (dprintf(fd, "%sBegin crash-info <%s>\n", (i > 0) ? "\n" : "", list[i].file_path) == -1) { + _E("Write error: %m"); + result = false; + goto out; + } + + if (copy_bytes(fd, nfd, NULL) == -1) { + _E("Copy data error"); + close(nfd); + result = false; + goto out; + } + + close(nfd); + + if (dprintf(fd, "End crash-info <%s>\n", list[i].file_path) == -1) { + _E("Write error: %m"); + result = false; + goto out; + } + } + +out: + for (int i = 0; i < list_count; i++) + free_record(&list[i]); + free(list); + return result; +} + +struct diagnostics_call_options { + enum BUGREPORT_REPORT_TYPE report_type; + long from, to; + bool last_set, from_set, to_set; +}; + +static bool diagnostics_call_parse_options(int out_fd, char **params, int params_size, struct diagnostics_call_options *dco) +{ + struct timespec cur_time; + clock_gettime(CLOCK_REALTIME, &cur_time); + dco->report_type = BR_UNKNOWN; + dco->from = 0; + dco->to = cur_time.tv_sec; + dco->last_set = false; + dco->from_set = false; + dco->to_set = false; + + enum PARAM_NAME { PN_TYPE = 1, PN_LAST, PN_TO, PN_FROM}; + + struct option long_options[] = { + {"type", required_argument, NULL, PN_TYPE}, + {"last", required_argument, NULL, PN_LAST}, + {"to", required_argument, NULL, PN_TO}, + {"from", required_argument, NULL, PN_FROM}, + }; + + int opt; + optind = 0; + + char **nparams = alloca((params_size+1)*sizeof(char*)); + memcpy(&nparams[1], params, params_size*sizeof(char*)); + nparams[0] = "n"; + while ((opt = getopt_long_only(params_size+1, nparams, "", long_options, NULL)) != -1) { + switch(opt) { + case PN_TYPE: + if (strcmp(optarg, "bugreport") == 0) { + dco->report_type = BR_BUGREPORT; + } else if (strcmp(optarg, "crash-info") == 0) { + dco->report_type = BR_CRASHINFO; + } else { + _E("Incorrect report type: %s", optarg); + dprintf(out_fd, "Incorrect report type: %s\n", optarg); + return false; + } + break; + case PN_LAST: + dco->last_set = true; + dco->from = cur_time.tv_sec - strtol(optarg, NULL, 10); + break; + case PN_FROM: + dco->from_set = true; + dco->from = strtol(optarg, NULL, 10); + break; + case PN_TO: + dco->to_set = true; + dco->to = strtol(optarg, NULL, 10); + break; + default: + _E("Incorrect option: %s", (optind > 0 && optind + 1 < params_size) ? params[optind-1] : ""); + dprintf(out_fd, "Incorrect option: %s\n", (optind > 0 && optind + 1 < params_size) ? params[optind-1] : ""); + return false; + } + } + + return true; +} + +static void diagnostics_callback(diagnostics_data_h data, char **params, int params_size, diagnostics_ctx_h ctx, void *user_data) +{ + int fd; + int ret = diagnostics_data_get_fd(data, &fd); + if (ret < 0) { + _E("Get data file descriptor error: %d", ret); + return; + } + struct diagnostics_call_options dco; + + if (!diagnostics_call_parse_options(fd, params, params_size, &dco)) + return; + + if ((dco.last_set && (dco.from_set || dco.from_set)) || (dco.to_set && !dco.from_set)) { + _E("Incorrect parameters set"); + dprintf(fd, "Incorrect parameters set.\n"); + return; + } + + switch(dco.report_type) { + case BR_CRASHINFO: + write_crash_info(fd, dco.from, dco.to); + break; + case BR_BUGREPORT: + write_bugreport(fd, dco.from, dco.to); + break; + default: + _E("Unknown report type\n"); + } +} + +bool diagnostics_init() +{ + diagnostics_set_client_id("org.tizen.bugreport-service"); + if (diagnostics_set_data_request_cb(&diagnostics_callback, NULL) != DIAGNOSTICS_ERROR_NONE) { + _E("Unable to register diagnostics callback"); + return false; + } + + return true; +} + diff --git a/src/bugreport-service/diagnostics/diagnostics_dump.h b/src/bugreport-service/diagnostics/diagnostics_dump.h new file mode 100644 index 0000000..a277463 --- /dev/null +++ b/src/bugreport-service/diagnostics/diagnostics_dump.h @@ -0,0 +1,19 @@ +/* + * 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. + */ +#pragma once +#define LOG_TAG "BUGREPORT-SERVICE" + +bool diagnostics_init(); diff --git a/src/bugreport-service/org.tizen.dumpsys.providers.org.tizen.bugreport-service.service b/src/bugreport-service/org.tizen.dumpsys.providers.org.tizen.bugreport-service.service new file mode 100644 index 0000000..8317dea --- /dev/null +++ b/src/bugreport-service/org.tizen.dumpsys.providers.org.tizen.bugreport-service.service @@ -0,0 +1,4 @@ +[D-BUS Service] +Name=org.tizen.dumpsys.providers.org.tizen.bugreport-service +Exec=/bin/false +SystemdService=bugreport.service -- 2.7.4