2 * Copyright (c) 2021 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the License);
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
18 #include <diagnostics.h>
27 #include <sys/types.h>
31 #include "diagnostics/diagnostics.h"
32 #include "diagnostics/diagnostics_dump.h"
33 #include "shared/log.h"
34 #include "shared/util.h"
36 enum BUGREPORT_REPORT_TYPE { BR_UNKNOWN, BR_BUGREPORT, BR_CRASHINFO, BR_CRASHINFO_JSON, BR_COREDUMP_TAR, BR_FULLREPORT, BR_PID };
41 struct timespec ctime;
44 static bool get_reports_list(const char *path, struct report_file **list, size_t *list_count)
53 _E("Open directory %s error: %m", path);
57 char file_path[PATH_MAX];
60 while ((ep = readdir(dp))) {
61 if (ep->d_type != DT_REG)
64 int r = snprintf(file_path, sizeof file_path, "%s/%s", path, ep->d_name);
65 if (r == -1 || r >= sizeof file_path) {
66 _E("Error while preparing report path: %m");
69 if (stat(file_path, &st) != 0) {
70 _E("Get stats about %s error: %m", path);
74 if (localtime_r(&(st.st_ctim.tv_sec), &t) == NULL) {
75 _E("localtime_r error: %m");
79 struct report_file *tmp_list = realloc(*list, sizeof(struct report_file)*(*list_count + 1));
80 char *fname = strdup(ep->d_name);
81 char *fpath = strdup(file_path);
82 if (tmp_list == NULL || fname == NULL || fpath == NULL) {
91 (*list)[*list_count].file_name = fname;
92 (*list)[*list_count].file_path = fpath;
93 (*list)[*list_count].ctime = st.st_ctim;
103 static void free_record(struct report_file *record)
105 free(record->file_name);
106 free(record->file_path);
109 static void filter_time(struct report_file *list, size_t *list_count, long time_from, long time_to)
112 size_t n_list_count = *list_count;
114 struct timespec current_ts;
115 clock_gettime(CLOCK_REALTIME, ¤t_ts);
117 for (int i = 0; i < *list_count; i++) {
118 if (list[i].ctime.tv_sec >= time_from && list[i].ctime.tv_sec < time_to) {
120 list[dest] = list[i];
123 free_record(&list[i]);
127 *list_count = n_list_count;
130 static int ctime_compare(const void *a, const void *b)
132 return ((struct report_file*)a)->ctime.tv_sec - ((struct report_file*)b)->ctime.tv_sec;
135 static struct report_file* get_reports(size_t *count)
139 struct report_file *list = NULL;
142 const char *dirs[] = {
143 CRASH_ROOT_PATH CRASH_PATH_SUBDIR,
144 CRASH_ROOT_PATH LIVE_PATH_SUBDIR
147 for (int i = 0; i < ARRAY_SIZE(dirs); i++) {
148 if (file_exists(dirs[i]) && !get_reports_list(dirs[i], &list, count)) {
149 _E("Can not read reports from: %s", dirs[i]);
154 qsort(list, *count, sizeof(struct report_file), ctime_compare);
158 static struct report_file* get_reports_from_range(long time_from, long time_to, size_t *count)
160 struct report_file *list = get_reports(count);
162 filter_time(list, count, time_from, time_to);
166 static void write_bugreport_log_file(int out_fd, const char *report_path)
168 int in_fd = diagnostics_report_get_file(report_path, LOG_FILE);
170 _D("No LOG_FILE report in %s file", report_path);
174 if (dprintf(out_fd, "Begin systemtate-log\n") == -1) {
175 _E("Write error: %m");
179 if (copy_bytes(out_fd, in_fd, NULL) == -1) {
180 _E("Copy data error");
186 if (dprintf(out_fd, "End systemtate-log\n") == -1)
187 _E("Write error: %m");
190 static bool write_bugreport(int fd, long time_from, long time_to)
192 size_t list_count = 0;
195 struct report_file *list = get_reports_from_range(time_from, time_to, &list_count);
199 for (int i = 0; i < list_count; i++) {
200 if (dprintf(fd, "%sBegin bugreport <%s>\n", (i > 0) ? "\n" : "", list[i].file_path) == -1) {
201 _E("Write error: %m");
206 write_bugreport_log_file(fd, list[i].file_path);
208 if (dprintf(fd, "End bugreport <%s>\n", list[i].file_path) == -1) {
209 _E("Write error: %m");
216 for (int i = 0; i < list_count; i++)
217 free_record(&list[i]);
222 static bool write_crash_info(int fd, long time_from, long time_to, bool as_json)
225 size_t list_count = 0;
227 struct report_file *list = get_reports_from_range(time_from, time_to, &list_count);
230 dprintf(fd, "{\"error\": \"Internal error.\"}");
232 dprintf(fd, "Internal error\n");
237 const enum diagnostics_entry entry_type = as_json ? INFO_JSON : INFO_FILE;
238 for (int i = 0, p = 0; i < list_count; i++) {
239 int nfd = diagnostics_report_get_file(list[i].file_path, entry_type);
241 _I("No INFO_FILE in %s file", list[i].file_path);
242 dprintf(nfd, "No INFO_FILE in %s\n", list[i].file_path);
248 res = dprintf(fd, "%c\n", (p == 0) ? '[' : ',');
250 res = dprintf(fd, "%sBegin crash-info <%s>\n", (i > 0) ? "\n" : "", list[i].file_path);
253 _E("Write error: %m");
258 if (copy_bytes(fd, nfd, NULL) == -1) {
259 _E("Copy data error");
269 if (i == list_count-1)
270 res = dprintf(fd, "]\n");
272 res = dprintf(fd, "End crash-info <%s>\n", list[i].file_path);
275 _E("Write error: %m");
282 for (int i = 0; i < list_count; i++)
283 free_record(&list[i]);
288 static struct report_file *find_report_file(const char *ident, struct report_file *list, size_t list_count)
293 for (size_t i = 0; i < list_count; i++) {
294 char *dot = rindex(list[i].file_name, '.');
297 if (strncmp(ident, list[i].file_name, dot - list[i].file_name) == 0)
303 static bool parse_pid_from_report_file(int report_fd, char *str)
309 fp = fdopen(report_fd, "r");
311 while (fgets(buff, sizeof(buff), fp)) {
312 if (sscanf(buff, "PID = %d", &pid) == 1)
318 sprintf(str, "%d", pid);
323 static bool write_pid(int fd, int in_fd)
327 if (!parse_pid_from_report_file(in_fd, pid)) {
328 dprintf(fd, "Internal error\n");
332 if (write(fd, pid, strlen(pid)) < 0) {
333 dprintf(fd, "Internal error: %m\n");
340 static bool write_blank(int fd)
342 if (write(fd, " ", 1) < 0) {
343 dprintf(fd, "Internal error: %m\n");
350 static bool write_pids(int fd, long time_from, long time_to)
353 size_t list_count = 0;
354 struct report_file *list = get_reports_from_range(time_from, time_to, &list_count);
356 dprintf(fd, "Internal error\n");
360 for (int i = 0; i < list_count; i++) {
361 int nfd = diagnostics_report_get_file(list[i].file_path, INFO_FILE);
363 _I("No INFO_FILE in %s file", list[i].file_path);
364 dprintf(nfd, "No INFO_FILE in %s\n", list[i].file_path);
368 if (!write_pid(fd, nfd)) {
369 _E("write_pid error");
377 if (!write_blank(fd)) {
378 _E("write_blank error");
384 for (int i = 0; i < list_count; i++)
385 free_record(&list[i]);
391 static bool write_single_file(int fd, enum BUGREPORT_REPORT_TYPE report_type, const char *ident)
397 size_t list_count = 0;
398 struct report_file *list = get_reports(&list_count);
400 if (report_type == BR_CRASHINFO_JSON)
401 dprintf(fd, "{\"error\": \"Internal error.\"}");
403 dprintf(fd, "Internal error\n");
407 struct report_file *report = find_report_file(ident, list, list_count);
408 if (report == NULL) {
413 enum diagnostics_entry report_entry = INFO_FILE;
414 switch (report_type) {
416 report_entry = LOG_FILE;
419 report_entry = INFO_FILE;
421 case BR_CRASHINFO_JSON:
422 report_entry = INFO_JSON;
424 case BR_COREDUMP_TAR:
425 report_entry = COREDUMP_TAR;
428 report_entry = FULL_REPORT;
431 report_entry = INFO_PID;
434 _E("Unsupported report type: %d", report_type);
439 int in_fd = diagnostics_report_get_file(report->file_path, report_entry);
441 _E("Can not get report type: %d from: %s (report ident: %s)", report_type, report->file_path, ident);
446 if (report_entry == INFO_PID)
447 write_pid(fd, in_fd);
448 else if (copy_bytes(fd, in_fd, NULL) == -1) {
449 _E("Copy data error");
454 for (size_t i = 0; i < list_count; i++)
455 free_record(&list[i]);
460 struct diagnostics_call_options {
461 enum BUGREPORT_REPORT_TYPE report_type;
463 bool last_set, from_set, to_set;
467 static void diagnostics_print_help(int out_fd)
469 dprintf(out_fd, "Usage:\n\n"
470 " dumpsys org.tizen.bugreport-service [--type=<report_type>] [[--last <seconds> |--from <timestamp> [--to <timestamp>]]|[report_ident]]\n\n"
471 " --type <report_type> Specify the type of report. Available types: bugreport, crash-info, crash-info-json. Additional types are available\n"
472 " if single report is specified by <report_ident>: fullreport, coredumptar (default: bugreport).\n"
473 " --last <seconds> Get reports generated in the last <seconds>.\n"
474 " --from <timestamp> Get reports generated after specified time.\n"
475 " --to <timestamp> Get reports generated before specified time (default: current time).\n"
476 " report_ident Get a specified report.\n\n"
477 " If no time range or report_ident is specified, all reports will be returned.\n\n"
481 static bool get_file_from_ctx(diagnostics_ctx_h ctx, char **report_file)
489 char *event_name = NULL;
490 bundle *event_data = NULL;
492 int ret = diagnostics_get_event_name(ctx, &event_name);
493 if (ret != DIAGNOSTICS_ERROR_NONE) {
494 _E("Get event name error: %d", ret);
497 if (strcmp(event_name, "BugreportCreated") != 0) {
498 _E("Unknown context: %s", event_name);
502 ret = diagnostics_get_event_data(ctx, &event_data);
503 if (ret != DIAGNOSTICS_ERROR_NONE) {
504 _E("Get event data error: %d", ret);
508 char *tmp_report_file;
509 ret = bundle_get_str(event_data, "report_file", &tmp_report_file);
510 if (ret != BUNDLE_ERROR_NONE) {
511 _E("Can not get report_file from event data: %d", ret);
515 *report_file = strdup(tmp_report_file);
516 if (*report_file == NULL) {
517 _E("Out of memory: %m");
520 _D("Report file from event data: %s", *report_file);
524 if (event_name != NULL)
526 if (event_data != NULL)
527 bundle_free(event_data);
532 static bool diagnostics_call_parse_options(int out_fd, char **params, int params_size, diagnostics_ctx_h ctx, struct diagnostics_call_options *dco)
534 struct timespec cur_time;
535 clock_gettime(CLOCK_REALTIME, &cur_time);
536 dco->report_type = BR_BUGREPORT;
538 dco->to = cur_time.tv_sec;
539 dco->last_set = false;
540 dco->from_set = false;
544 enum PARAM_NAME { PN_TYPE = 1, PN_LAST, PN_TO, PN_FROM, PN_HELP };
546 struct option long_options[] = {
547 {"type", required_argument, NULL, PN_TYPE},
548 {"last", required_argument, NULL, PN_LAST},
549 {"to", required_argument, NULL, PN_TO},
550 {"from", required_argument, NULL, PN_FROM},
551 {"help", no_argument, NULL, PN_HELP},
558 char **nparams = alloca((params_size+1)*sizeof(char*));
559 memcpy(&nparams[1], params, params_size*sizeof(char*));
561 while ((opt = getopt_long_only(params_size+1, nparams, "", long_options, NULL)) != -1) {
564 if (strcmp(optarg, "bugreport") == 0) {
565 dco->report_type = BR_BUGREPORT;
566 } else if (strcmp(optarg, "crash-info") == 0) {
567 dco->report_type = BR_CRASHINFO;
568 } else if (strcmp(optarg, "crash-info-json") == 0) {
569 dco->report_type = BR_CRASHINFO_JSON;
570 } else if (strcmp(optarg, "coredumptar") == 0) {
571 dco->report_type = BR_COREDUMP_TAR;
572 } else if (strcmp(optarg, "fullreport") == 0) {
573 dco->report_type = BR_FULLREPORT;
574 } else if (strcmp(optarg, "pid") == 0) {
575 dco->report_type = BR_PID;
577 _E("Incorrect report type: %s", optarg);
578 dprintf(out_fd, "Incorrect report type: %s\n", optarg);
583 dco->last_set = true;
584 dco->from = cur_time.tv_sec - strtol(optarg, NULL, 10);
587 dco->from_set = true;
588 dco->from = strtol(optarg, NULL, 10);
592 dco->to = strtol(optarg, NULL, 10);
595 diagnostics_print_help(out_fd);
598 _E("Incorrect option: %s", (optind > 0 && optind - 1 < params_size + 1) ? nparams[optind-1] : "<unknown>");
599 dprintf(out_fd, "Incorrect option: %s\n", (optind > 0 && optind - 1 < params_size + 1) ? nparams[optind-1] : "<unknown>");
604 if (optind < params_size+1) {
605 dco->arg = strdup(nparams[optind]);
606 if (dco->arg == NULL) {
607 _E("Out of memory: %m");
611 get_file_from_ctx(ctx, &dco->arg);
617 static void diagnostics_callback(diagnostics_data_h data, char **params, int params_size, diagnostics_ctx_h ctx, void *user_data)
620 int ret = diagnostics_data_get_fd(data, &fd);
622 _E("Get data file descriptor error: %d", ret);
625 struct diagnostics_call_options dco;
627 if (!diagnostics_call_parse_options(fd, params, params_size, ctx, &dco))
630 if ((dco.last_set && (dco.from_set || dco.to_set)) || (dco.to_set && !dco.from_set)) {
631 _E("Incorrect parameters set");
632 dprintf(fd, "Incorrect parameters set.\n");
636 if (dco.arg != NULL) {
637 if (dco.last_set || dco.from_set || dco.to_set) {
638 _E("Incorrect parameters set.");
639 dprintf(fd, "Incorrect parameters set.\n");
642 write_single_file(fd, dco.report_type, dco.arg);
646 switch(dco.report_type) {
648 write_crash_info(fd, dco.from, dco.to, false);
650 case BR_CRASHINFO_JSON:
651 write_crash_info(fd, dco.from, dco.to, true);
654 write_bugreport(fd, dco.from, dco.to);
656 case BR_COREDUMP_TAR:
657 dprintf(fd, "Report type \"coredumptar\" requires a specific report with report_ident argument.\n");
660 dprintf(fd, "Report type \"fullreport\" requires a specific report with report_ident argument.\n");
663 write_pids(fd, dco.from, dco.to);
666 _E("Unknown report type\n");
673 bool diagnostics_init()
675 diagnostics_set_client_id("org.tizen.bugreport-service");
676 if (diagnostics_set_data_request_cb(&diagnostics_callback, NULL) != DIAGNOSTICS_ERROR_NONE) {
677 _E("Unable to register diagnostics callback");