#include "shared/log.h"
#include "shared/util.h"
-enum BUGREPORT_REPORT_TYPE { BR_UNKNOWN, BR_BUGREPORT, BR_CRASHINFO };
+enum BUGREPORT_REPORT_TYPE { BR_UNKNOWN, BR_BUGREPORT, BR_CRASHINFO, BR_CRASHINFO_JSON, BR_COREDUMP_TAR, BR_FULLREPORT, BR_PID };
struct report_file {
char *file_name;
return false;
}
+ char file_path[PATH_MAX];
+ bool ret = false;
+
while ((ep = readdir(dp))) {
- if (ep->d_type != 8)
+ if (ep->d_type != DT_REG)
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;
+ int r = snprintf(file_path, sizeof file_path, "%s/%s", path, ep->d_name);
+ if (r == -1 || r >= sizeof file_path) {
+ _E("Error while preparing report path: %m");
+ goto out;
}
if (stat(file_path, &st) != 0) {
_E("Get stats about %s error: %m", path);
- return false;
+ goto out;
}
struct tm t;
if (localtime_r(&(st.st_ctim.tv_sec), &t) == NULL) {
_E("localtime_r error: %m");
- return false;
+ goto out;
+ }
+
+ struct report_file *tmp_list = realloc(*list, sizeof(struct report_file)*(*list_count + 1));
+ char *fname = strdup(ep->d_name);
+ char *fpath = strdup(file_path);
+ if (tmp_list == NULL || fname == NULL || fpath == NULL) {
+ _E("Out of memory");
+ free(fname);
+ free(fpath);
+ free(tmp_list);
+ goto out;
}
- *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 = tmp_list;
+ (*list)[*list_count].file_name = fname;
+ (*list)[*list_count].file_path = fpath;
(*list)[*list_count].ctime = st.st_ctim;
(*list_count)++;
}
+
+ ret = true;
+out:
closedir(dp);
- return true;
+ return ret;
}
static void free_record(struct report_file *record)
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)
+static struct report_file* get_reports(size_t *count)
{
+ assert(count);
+
struct report_file *list = NULL;
*count = 0;
}
}
- filter_time(list, count, time_from, time_to);
-
qsort(list, *count, sizeof(struct report_file), ctime_compare);
return list;
}
+static struct report_file* get_reports_from_range(long time_from, long time_to, size_t *count)
+{
+ struct report_file *list = get_reports(count);
+
+ filter_time(list, count, time_from, time_to);
+ 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);
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");
+ struct report_file *list = get_reports_from_range(time_from, time_to, &list_count);
+ if (list == NULL)
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) {
return result;
}
-static bool write_crash_info(int fd, long time_from, long time_to)
+static bool write_crash_info(int fd, long time_from, long time_to, bool as_json)
{
bool result = true;
size_t list_count = 0;
- struct report_file *list = get_reports(time_from, time_to, &list_count);
+ struct report_file *list = get_reports_from_range(time_from, time_to, &list_count);
if (list == NULL) {
- dprintf(fd, "Internal error.\n");
+ if (as_json)
+ dprintf(fd, "{\"error\": \"Internal error.\"}");
+ else
+ 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);
+
+ const enum diagnostics_entry entry_type = as_json ? INFO_JSON : INFO_FILE;
+ for (int i = 0, p = 0; i < list_count; i++) {
+ int nfd = diagnostics_report_get_file(list[i].file_path, entry_type);
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) {
+ int res = 0;
+ if (as_json)
+ res = dprintf(fd, "%c\n", (p == 0) ? '[' : ',');
+ else
+ res = dprintf(fd, "%sBegin crash-info <%s>\n", (i > 0) ? "\n" : "", list[i].file_path);
+
+ if (res == -1) {
_E("Write error: %m");
result = false;
goto out;
result = false;
goto out;
}
+ p++;
close(nfd);
- if (dprintf(fd, "End crash-info <%s>\n", list[i].file_path) == -1) {
+ if (as_json) {
+ if (i == list_count-1)
+ res = dprintf(fd, "]\n");
+ } else {
+ res = dprintf(fd, "End crash-info <%s>\n", list[i].file_path);
+ }
+ if (res == -1) {
_E("Write error: %m");
result = false;
goto out;
return result;
}
+static struct report_file *find_report_file(const char *ident, struct report_file *list, size_t list_count)
+{
+ assert(ident);
+ assert(list);
+
+ for (size_t i = 0; i < list_count; i++) {
+ char *dot = rindex(list[i].file_name, '.');
+ if (dot == NULL)
+ continue;
+ if (strncmp(ident, list[i].file_name, dot - list[i].file_name) == 0)
+ return &list[i];
+ }
+ return NULL;
+}
+
+static bool parse_pid_from_report_file(int report_fd, char *str)
+{
+ FILE *fp;
+ char buff[1024];
+ int pid = -1;
+
+ fp = fdopen(report_fd, "r");
+
+ while (fgets(buff, sizeof(buff), fp)) {
+ if (sscanf(buff, "PID = %d", &pid) == 1)
+ break;
+ }
+ if (pid < 0)
+ return false;
+
+ sprintf(str, "%d", pid);
+
+ return true;
+}
+
+static bool write_pid(int fd, int in_fd)
+{
+ char pid[10];
+
+ if (!parse_pid_from_report_file(in_fd, pid)) {
+ dprintf(fd, "Internal error\n");
+ return false;
+ }
+
+ if (write(fd, pid, strlen(pid)) < 0) {
+ dprintf(fd, "Internal error: %m\n");
+ return false;
+ }
+
+ return true;
+}
+
+static bool write_blank(int fd)
+{
+ if (write(fd, " ", 1) < 0) {
+ dprintf(fd, "Internal error: %m\n");
+ return false;
+ }
+
+ return true;
+}
+
+static bool write_pids(int fd, long time_from, long time_to)
+{
+ bool result = true;
+ size_t list_count = 0;
+ struct report_file *list = get_reports_from_range(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 (!write_pid(fd, nfd)) {
+ _E("write_pid error");
+ close(nfd);
+ result = false;
+ break;
+ }
+
+ close(nfd);
+
+ if (!write_blank(fd)) {
+ _E("write_blank error");
+ result = false;
+ break;
+ }
+ }
+
+ for (int i = 0; i < list_count; i++)
+ free_record(&list[i]);
+ free(list);
+
+ return result;
+}
+
+static bool write_single_file(int fd, enum BUGREPORT_REPORT_TYPE report_type, const char *ident)
+{
+ assert(fd >= 0);
+ assert(ident);
+
+ bool result = true;
+ size_t list_count = 0;
+ struct report_file *list = get_reports(&list_count);
+ if (list == NULL) {
+ if (report_type == BR_CRASHINFO_JSON)
+ dprintf(fd, "{\"error\": \"Internal error.\"}");
+ else
+ dprintf(fd, "Internal error\n");
+ return false;
+ }
+
+ struct report_file *report = find_report_file(ident, list, list_count);
+ if (report == NULL) {
+ result = false;
+ goto out;
+ }
+
+ enum diagnostics_entry report_entry = INFO_FILE;
+ switch (report_type) {
+ case BR_BUGREPORT:
+ report_entry = LOG_FILE;
+ break;
+ case BR_CRASHINFO:
+ report_entry = INFO_FILE;
+ break;
+ case BR_CRASHINFO_JSON:
+ report_entry = INFO_JSON;
+ break;
+ case BR_COREDUMP_TAR:
+ report_entry = COREDUMP_TAR;
+ break;
+ case BR_FULLREPORT:
+ report_entry = FULL_REPORT;
+ break;
+ case BR_PID:
+ report_entry = INFO_PID;
+ break;
+ default:
+ _E("Unsupported report type: %d", report_type);
+ result = false;
+ goto out;
+ }
+
+ int in_fd = diagnostics_report_get_file(report->file_path, report_entry);
+ if (in_fd < 0) {
+ _E("Can not get report type: %d from: %s (report ident: %s)", report_type, report->file_path, ident);
+ result = false;
+ goto out;
+ }
+
+ if (report_entry == INFO_PID)
+ write_pid(fd, in_fd);
+ else if (copy_bytes(fd, in_fd, NULL) == -1) {
+ _E("Copy data error");
+ result = false;
+ goto out;
+ }
+out:
+ for (size_t 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;
+ char *arg;
};
-static bool diagnostics_call_parse_options(int out_fd, char **params, int params_size, struct diagnostics_call_options *dco)
+static void diagnostics_print_help(int out_fd)
+{
+ dprintf(out_fd, "Usage:\n\n"
+ " dumpsys org.tizen.bugreport-service [--type=<report_type>] [[--last <seconds> |--from <timestamp> [--to <timestamp>]]|[report_ident]]\n\n"
+ " --type <report_type> Specify the type of report. Available types: bugreport, crash-info, crash-info-json. Additional types are available\n"
+ " if single report is specified by <report_ident>: fullreport, coredumptar (default: bugreport).\n"
+ " --last <seconds> Get reports generated in the last <seconds>.\n"
+ " --from <timestamp> Get reports generated after specified time.\n"
+ " --to <timestamp> Get reports generated before specified time (default: current time).\n"
+ " report_ident Get a specified report.\n\n"
+ " If no time range or report_ident is specified, all reports will be returned.\n\n"
+ );
+}
+
+static bool get_file_from_ctx(diagnostics_ctx_h ctx, char **report_file)
+{
+ assert(report_file);
+
+ if (ctx == NULL)
+ return false;
+
+ bool result = false;
+ char *event_name = NULL;
+ bundle *event_data = NULL;
+
+ int ret = diagnostics_get_event_name(ctx, &event_name);
+ if (ret != DIAGNOSTICS_ERROR_NONE) {
+ _E("Get event name error: %d", ret);
+ goto out;
+ }
+ if (strcmp(event_name, "BugreportCreated") != 0) {
+ _E("Unknown context: %s", event_name);
+ goto out;
+ }
+
+ ret = diagnostics_get_event_data(ctx, &event_data);
+ if (ret != DIAGNOSTICS_ERROR_NONE) {
+ _E("Get event data error: %d", ret);
+ goto out;
+ }
+
+ char *tmp_report_file;
+ ret = bundle_get_str(event_data, "report_file", &tmp_report_file);
+ if (ret != BUNDLE_ERROR_NONE) {
+ _E("Can not get report_file from event data: %d", ret);
+ goto out;
+ }
+
+ *report_file = strdup(tmp_report_file);
+ if (*report_file == NULL) {
+ _E("Out of memory: %m");
+ goto out;
+ }
+ _D("Report file from event data: %s", *report_file);
+
+ result = true;
+out:
+ if (event_name != NULL)
+ free(event_name);
+ if (event_data != NULL)
+ bundle_free(event_data);
+
+ return result;
+}
+
+static bool diagnostics_call_parse_options(int out_fd, char **params, int params_size, diagnostics_ctx_h ctx, struct diagnostics_call_options *dco)
{
struct timespec cur_time;
clock_gettime(CLOCK_REALTIME, &cur_time);
- dco->report_type = BR_UNKNOWN;
+ dco->report_type = BR_BUGREPORT;
dco->from = 0;
dco->to = cur_time.tv_sec;
dco->last_set = false;
dco->from_set = false;
dco->to_set = false;
+ dco->arg = NULL;
- enum PARAM_NAME { PN_TYPE = 1, PN_LAST, PN_TO, PN_FROM};
+ enum PARAM_NAME { PN_TYPE = 1, PN_LAST, PN_TO, PN_FROM, PN_HELP };
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},
+ {"help", no_argument, NULL, PN_HELP},
+ {0, 0, 0, 0},
};
int opt;
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) {
+ 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 {
+ } else if (strcmp(optarg, "crash-info-json") == 0) {
+ dco->report_type = BR_CRASHINFO_JSON;
+ } else if (strcmp(optarg, "coredumptar") == 0) {
+ dco->report_type = BR_COREDUMP_TAR;
+ } else if (strcmp(optarg, "fullreport") == 0) {
+ dco->report_type = BR_FULLREPORT;
+ } else if (strcmp(optarg, "pid") == 0) {
+ dco->report_type = BR_PID;
+ }else {
_E("Incorrect report type: %s", optarg);
dprintf(out_fd, "Incorrect report type: %s\n", optarg);
return false;
dco->to_set = true;
dco->to = strtol(optarg, NULL, 10);
break;
+ case PN_HELP:
+ diagnostics_print_help(out_fd);
+ return false;
default:
- _E("Incorrect option: %s", (optind > 0 && optind + 1 < params_size) ? params[optind-1] : "<unknown>");
- dprintf(out_fd, "Incorrect option: %s\n", (optind > 0 && optind + 1 < params_size) ? params[optind-1] : "<unknown>");
+ _E("Incorrect option: %s", (optind > 0 && optind - 1 < params_size + 1) ? nparams[optind-1] : "<unknown>");
+ dprintf(out_fd, "Incorrect option: %s\n", (optind > 0 && optind - 1 < params_size + 1) ? nparams[optind-1] : "<unknown>");
return false;
}
}
+ if (optind < params_size+1) {
+ dco->arg = strdup(nparams[optind]);
+ if (dco->arg == NULL) {
+ _E("Out of memory: %m");
+ return false;
+ }
+ } else {
+ get_file_from_ctx(ctx, &dco->arg);
+ }
+
return true;
}
}
struct diagnostics_call_options dco;
- if (!diagnostics_call_parse_options(fd, params, params_size, &dco))
+ if (!diagnostics_call_parse_options(fd, params, params_size, ctx, &dco))
return;
- if ((dco.last_set && (dco.from_set || dco.from_set)) || (dco.to_set && !dco.from_set)) {
+ if ((dco.last_set && (dco.from_set || dco.to_set)) || (dco.to_set && !dco.from_set)) {
_E("Incorrect parameters set");
dprintf(fd, "Incorrect parameters set.\n");
- return;
+ goto out;
+ }
+
+ if (dco.arg != NULL) {
+ if (dco.last_set || dco.from_set || dco.to_set) {
+ _E("Incorrect parameters set.");
+ dprintf(fd, "Incorrect parameters set.\n");
+ goto out;
+ }
+ write_single_file(fd, dco.report_type, dco.arg);
+ goto out;
}
switch(dco.report_type) {
case BR_CRASHINFO:
- write_crash_info(fd, dco.from, dco.to);
+ write_crash_info(fd, dco.from, dco.to, false);
+ break;
+ case BR_CRASHINFO_JSON:
+ write_crash_info(fd, dco.from, dco.to, true);
break;
case BR_BUGREPORT:
write_bugreport(fd, dco.from, dco.to);
break;
+ case BR_COREDUMP_TAR:
+ dprintf(fd, "Report type \"coredumptar\" requires a specific report with report_ident argument.\n");
+ break;
+ case BR_FULLREPORT:
+ dprintf(fd, "Report type \"fullreport\" requires a specific report with report_ident argument.\n");
+ break;
+ case BR_PID:
+ write_pids(fd, dco.from, dco.to);
+ break;
default:
_E("Unknown report type\n");
}
+out:
+ if (dco.arg != NULL)
+ free(dco.arg);
}
bool diagnostics_init()