Add PID information as a new type of bugreport
[platform/core/system/crash-worker.git] / src / bugreport-service / diagnostics / diagnostics_dump.c
index 2b91ac7..ff35141 100644 (file)
@@ -33,7 +33,7 @@
 #include "shared/log.h"
 #include "shared/util.h"
 
-enum BUGREPORT_REPORT_TYPE { BR_UNKNOWN, BR_BUGREPORT, BR_CRASHINFO, BR_CRASHINFO_JSON };
+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;
@@ -132,8 +132,10 @@ 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)
+static struct report_file* get_reports(size_t *count)
 {
+       assert(count);
+
        struct report_file *list = NULL;
        *count = 0;
 
@@ -149,12 +151,18 @@ static struct report_file* get_reports(long time_from, long time_to, size_t *cou
                }
        }
 
-       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);
@@ -184,11 +192,9 @@ 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");
+       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) {
@@ -218,9 +224,12 @@ 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;
        }
 
@@ -276,13 +285,251 @@ 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);
@@ -292,14 +539,16 @@ static bool diagnostics_call_parse_options(int out_fd, char **params, int params
        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},
        };
 
@@ -310,7 +559,7 @@ static bool diagnostics_call_parse_options(int out_fd, char **params, int params
        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;
@@ -318,7 +567,13 @@ static bool diagnostics_call_parse_options(int out_fd, char **params, int params
                                dco->report_type = BR_CRASHINFO;
                        } else if (strcmp(optarg, "crash-info-json") == 0) {
                                dco->report_type = BR_CRASHINFO_JSON;
-                       } else {
+                       } 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;
@@ -336,13 +591,26 @@ static bool diagnostics_call_parse_options(int out_fd, char **params, int params
                        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;
 }
 
@@ -356,13 +624,23 @@ static void diagnostics_callback(diagnostics_data_h data, char **params, int par
        }
        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.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) {
@@ -375,9 +653,21 @@ static void diagnostics_callback(diagnostics_data_h data, char **params, int par
        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()