Allow to get bugreports using the diagnostics API
[platform/core/system/crash-worker.git] / src / bugreport-service / diagnostics / diagnostics_dump.c
1 /*
2  * Copyright (c) 2021 Samsung Electronics Co., Ltd.
3  *
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
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16 #include <alloca.h>
17 #include <assert.h>
18 #include <diagnostics.h>
19 #include <dirent.h>
20 #include <getopt.h>
21 #include <stdbool.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <sys/mman.h>
26 #include <sys/stat.h>
27 #include <sys/types.h>
28 #include <time.h>
29 #include <unistd.h>
30
31 #include "diagnostics/diagnostics.h"
32 #include "diagnostics/diagnostics_dump.h"
33 #include "shared/log.h"
34 #include "shared/util.h"
35
36 enum BUGREPORT_REPORT_TYPE { BR_UNKNOWN, BR_BUGREPORT, BR_CRASHINFO };
37
38 struct report_file {
39         char *file_name;
40         char *file_path;
41         struct timespec ctime;
42 };
43
44 static bool get_reports_list(const char *path, struct report_file **list, size_t *list_count)
45 {
46         assert(list);
47         assert(list_count);
48
49         DIR *dp;
50         struct dirent *ep;
51         dp = opendir(path);
52         if (dp == NULL) {
53                 _E("Open directory %s error: %m", path);
54                 return false;
55         }
56
57         while ((ep = readdir(dp))) {
58                 if (ep->d_type != 8)
59                         continue;
60                 struct stat st;
61                 char *file_path = NULL;
62                 if (asprintf(&file_path, "%s/%s", path, ep->d_name) == -1) {
63                         _E("Memory allocation error: %m");
64                         return false;
65                 }
66                 if (stat(file_path, &st) != 0) {
67                         _E("Get stats about %s error: %m", path);
68                         return false;
69                 }
70                 struct tm t;
71                 if (localtime_r(&(st.st_ctim.tv_sec), &t) == NULL) {
72                         _E("localtime_r error: %m");
73                         return false;
74                 }
75
76                 *list = realloc(*list, sizeof(struct report_file)*(*list_count + 1));
77                 (*list)[*list_count].file_name = strdup(ep->d_name);
78                 (*list)[*list_count].file_path = strdup(file_path);
79                 (*list)[*list_count].ctime = st.st_ctim;
80                 (*list_count)++;
81         }
82         closedir(dp);
83         return true;
84 }
85
86 static void free_record(struct report_file *record)
87 {
88         free(record->file_name);
89         free(record->file_path);
90 }
91
92 static void filter_time(struct report_file *list, size_t *list_count, long time_from, long time_to)
93 {
94         size_t dest = 0;
95         size_t n_list_count = *list_count;
96
97         struct timespec current_ts;
98         clock_gettime(CLOCK_REALTIME, &current_ts);
99
100         for (int i = 0; i < *list_count; i++) {
101                 if (list[i].ctime.tv_sec >= time_from && list[i].ctime.tv_sec < time_to) {
102                         if (dest != i)
103                                 list[dest] = list[i];
104                         dest++;
105                 } else {
106                         free_record(&list[i]);
107                         n_list_count--;
108                 }
109         }
110         *list_count = n_list_count;
111 }
112
113 static int ctime_compare(const void *a, const void *b)
114 {
115         return ((struct report_file*)a)->ctime.tv_sec - ((struct report_file*)b)->ctime.tv_sec;
116 }
117
118 static struct report_file* get_reports(long time_from, long time_to, size_t *count)
119 {
120         struct report_file *list = NULL;
121         *count = 0;
122
123         const char *dirs[] = {
124                 CRASH_ROOT_PATH CRASH_PATH_SUBDIR,
125                 CRASH_ROOT_PATH LIVE_PATH_SUBDIR
126         };
127
128         for (int i = 0; i < ARRAY_SIZE(dirs); i++) {
129                 if (file_exists(dirs[i]) && !get_reports_list(dirs[i], &list, count)) {
130                         _E("Can not read reports from: %s", dirs[i]);
131                         return NULL;
132                 }
133         }
134
135         filter_time(list, count, time_from, time_to);
136
137         qsort(list, *count, sizeof(struct report_file), ctime_compare);
138         return list;
139 }
140
141 static void write_bugreport_log_file(int out_fd, const char *report_path)
142 {
143         int in_fd = diagnostics_report_get_file(report_path, LOG_FILE);
144         if (in_fd < 0) {
145                 _D("No LOG_FILE report in %s file", report_path);
146                 return;
147         }
148
149         if (dprintf(out_fd, "Begin systemtate-log\n") == -1) {
150                 _E("Write error: %m");
151                 goto out;
152         }
153
154         if (copy_bytes(out_fd, in_fd, NULL) == -1) {
155                 _E("Copy data error");
156                 goto out;
157         }
158
159 out:
160         close(in_fd);
161         if (dprintf(out_fd, "End systemtate-log\n") == -1)
162                 _E("Write error: %m");
163 }
164
165 static bool write_bugreport(int fd, long time_from, long time_to)
166 {
167         size_t list_count = 0;
168         bool result = true;
169
170         struct report_file *list = get_reports(time_from, time_to, &list_count);
171         if (list == NULL) {
172                 dprintf(fd, "Internal error.\n");
173                 return false;
174         }
175
176         for (int i = 0; i < list_count; i++) {
177                 if (dprintf(fd, "%sBegin bugreport <%s>\n", (i > 0) ? "\n" : "", list[i].file_path) == -1) {
178                         _E("Write error: %m");
179                         result = false;
180                         goto out;
181                 }
182
183                 write_bugreport_log_file(fd, list[i].file_path);
184
185                 if (dprintf(fd, "End bugreport <%s>\n", list[i].file_path) == -1) {
186                         _E("Write error: %m");
187                         result = false;
188                         goto out;
189                 }
190         }
191
192 out:
193         for (int i = 0; i < list_count; i++)
194                 free_record(&list[i]);
195         free(list);
196         return result;
197 }
198
199 static bool write_crash_info(int fd, long time_from, long time_to)
200 {
201         bool result = true;
202         size_t list_count = 0;
203
204         struct report_file *list = get_reports(time_from, time_to, &list_count);
205         if (list == NULL) {
206                 dprintf(fd, "Internal error.\n");
207                 return false;
208         }
209
210         for (int i = 0; i < list_count; i++) {
211                 int nfd = diagnostics_report_get_file(list[i].file_path, INFO_FILE);
212                 if (nfd <= 0) {
213                         _I("No INFO_FILE in %s file", list[i].file_path);
214                         dprintf(nfd, "No INFO_FILE in %s\n",  list[i].file_path);
215                         continue;
216                 }
217
218                 if (dprintf(fd, "%sBegin crash-info <%s>\n", (i > 0) ? "\n" : "", list[i].file_path) == -1) {
219                         _E("Write error: %m");
220                         result = false;
221                         goto out;
222                 }
223
224                 if (copy_bytes(fd, nfd, NULL) == -1) {
225                         _E("Copy data error");
226                         close(nfd);
227                         result = false;
228                         goto out;
229                 }
230
231                 close(nfd);
232
233                 if (dprintf(fd, "End crash-info <%s>\n", list[i].file_path) == -1) {
234                         _E("Write error: %m");
235                         result = false;
236                         goto out;
237                 }
238         }
239
240 out:
241         for (int i = 0; i < list_count; i++)
242                 free_record(&list[i]);
243         free(list);
244         return result;
245 }
246
247 struct diagnostics_call_options {
248         enum BUGREPORT_REPORT_TYPE report_type;
249         long from, to;
250         bool last_set, from_set, to_set;
251 };
252
253 static bool diagnostics_call_parse_options(int out_fd, char **params, int params_size, struct diagnostics_call_options *dco)
254 {
255         struct timespec cur_time;
256         clock_gettime(CLOCK_REALTIME, &cur_time);
257         dco->report_type = BR_UNKNOWN;
258         dco->from = 0;
259         dco->to = cur_time.tv_sec;
260         dco->last_set = false;
261         dco->from_set = false;
262         dco->to_set = false;
263
264         enum PARAM_NAME { PN_TYPE = 1, PN_LAST, PN_TO, PN_FROM};
265
266         struct option long_options[] = {
267                 {"type", required_argument, NULL, PN_TYPE},
268                 {"last", required_argument, NULL, PN_LAST},
269                 {"to", required_argument, NULL, PN_TO},
270                 {"from", required_argument, NULL, PN_FROM},
271         };
272
273         int opt;
274         optind = 0;
275
276         char **nparams = alloca((params_size+1)*sizeof(char*));
277         memcpy(&nparams[1], params, params_size*sizeof(char*));
278         nparams[0] = "n";
279         while ((opt = getopt_long_only(params_size+1, nparams, "", long_options, NULL)) != -1) {
280                 switch(opt) {
281                 case PN_TYPE:
282                         if (strcmp(optarg, "bugreport") == 0) {
283                                 dco->report_type = BR_BUGREPORT;
284                         } else if (strcmp(optarg, "crash-info") == 0) {
285                                 dco->report_type = BR_CRASHINFO;
286                         } else {
287                                 _E("Incorrect report type: %s", optarg);
288                                 dprintf(out_fd, "Incorrect report type: %s\n", optarg);
289                                 return false;
290                         }
291                         break;
292                 case PN_LAST:
293                         dco->last_set = true;
294                         dco->from = cur_time.tv_sec - strtol(optarg, NULL, 10);
295                         break;
296                 case PN_FROM:
297                         dco->from_set = true;
298                         dco->from = strtol(optarg, NULL, 10);
299                         break;
300                 case PN_TO:
301                         dco->to_set = true;
302                         dco->to = strtol(optarg, NULL, 10);
303                         break;
304                 default:
305                         _E("Incorrect option: %s", (optind > 0 && optind + 1 < params_size) ? params[optind-1] : "<unknown>");
306                         dprintf(out_fd, "Incorrect option: %s\n", (optind > 0 && optind + 1 < params_size) ? params[optind-1] : "<unknown>");
307                         return false;
308                 }
309         }
310
311         return true;
312 }
313
314 static void diagnostics_callback(diagnostics_data_h data, char **params, int params_size, diagnostics_ctx_h ctx, void *user_data)
315 {
316         int fd;
317         int ret = diagnostics_data_get_fd(data, &fd);
318         if (ret < 0) {
319                 _E("Get data file descriptor error: %d", ret);
320                 return;
321         }
322         struct diagnostics_call_options dco;
323
324         if (!diagnostics_call_parse_options(fd, params, params_size, &dco))
325                 return;
326
327         if ((dco.last_set && (dco.from_set || dco.from_set)) || (dco.to_set && !dco.from_set)) {
328                 _E("Incorrect parameters set");
329                 dprintf(fd, "Incorrect parameters set.\n");
330                 return;
331         }
332
333         switch(dco.report_type) {
334         case BR_CRASHINFO:
335                 write_crash_info(fd, dco.from, dco.to);
336                 break;
337         case BR_BUGREPORT:
338                 write_bugreport(fd, dco.from, dco.to);
339                 break;
340         default:
341                 _E("Unknown report type\n");
342         }
343 }
344
345 bool diagnostics_init()
346 {
347         diagnostics_set_client_id("org.tizen.bugreport-service");
348         if (diagnostics_set_data_request_cb(&diagnostics_callback, NULL) != DIAGNOSTICS_ERROR_NONE) {
349                 _E("Unable to register diagnostics callback");
350                 return false;
351         }
352
353         return true;
354 }
355