Release 6.5.15: bugreport-service fixes
[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                 struct report_file *tmp_list = realloc(*list, sizeof(struct report_file)*(*list_count + 1));
77                 if (tmp_list == NULL) {
78                         _E("Out of memory");
79                         return false;
80                 }
81                 *list = tmp_list;
82                 (*list)[*list_count].file_name = strdup(ep->d_name);
83                 (*list)[*list_count].file_path = strdup(file_path);
84                 (*list)[*list_count].ctime = st.st_ctim;
85                 (*list_count)++;
86         }
87         closedir(dp);
88         return true;
89 }
90
91 static void free_record(struct report_file *record)
92 {
93         free(record->file_name);
94         free(record->file_path);
95 }
96
97 static void filter_time(struct report_file *list, size_t *list_count, long time_from, long time_to)
98 {
99         size_t dest = 0;
100         size_t n_list_count = *list_count;
101
102         struct timespec current_ts;
103         clock_gettime(CLOCK_REALTIME, &current_ts);
104
105         for (int i = 0; i < *list_count; i++) {
106                 if (list[i].ctime.tv_sec >= time_from && list[i].ctime.tv_sec < time_to) {
107                         if (dest != i)
108                                 list[dest] = list[i];
109                         dest++;
110                 } else {
111                         free_record(&list[i]);
112                         n_list_count--;
113                 }
114         }
115         *list_count = n_list_count;
116 }
117
118 static int ctime_compare(const void *a, const void *b)
119 {
120         return ((struct report_file*)a)->ctime.tv_sec - ((struct report_file*)b)->ctime.tv_sec;
121 }
122
123 static struct report_file* get_reports(long time_from, long time_to, size_t *count)
124 {
125         struct report_file *list = NULL;
126         *count = 0;
127
128         const char *dirs[] = {
129                 CRASH_ROOT_PATH CRASH_PATH_SUBDIR,
130                 CRASH_ROOT_PATH LIVE_PATH_SUBDIR
131         };
132
133         for (int i = 0; i < ARRAY_SIZE(dirs); i++) {
134                 if (file_exists(dirs[i]) && !get_reports_list(dirs[i], &list, count)) {
135                         _E("Can not read reports from: %s", dirs[i]);
136                         return NULL;
137                 }
138         }
139
140         filter_time(list, count, time_from, time_to);
141
142         qsort(list, *count, sizeof(struct report_file), ctime_compare);
143         return list;
144 }
145
146 static void write_bugreport_log_file(int out_fd, const char *report_path)
147 {
148         int in_fd = diagnostics_report_get_file(report_path, LOG_FILE);
149         if (in_fd < 0) {
150                 _D("No LOG_FILE report in %s file", report_path);
151                 return;
152         }
153
154         if (dprintf(out_fd, "Begin systemtate-log\n") == -1) {
155                 _E("Write error: %m");
156                 goto out;
157         }
158
159         if (copy_bytes(out_fd, in_fd, NULL) == -1) {
160                 _E("Copy data error");
161                 goto out;
162         }
163
164 out:
165         close(in_fd);
166         if (dprintf(out_fd, "End systemtate-log\n") == -1)
167                 _E("Write error: %m");
168 }
169
170 static bool write_bugreport(int fd, long time_from, long time_to)
171 {
172         size_t list_count = 0;
173         bool result = true;
174
175         struct report_file *list = get_reports(time_from, time_to, &list_count);
176         if (list == NULL) {
177                 dprintf(fd, "Internal error.\n");
178                 return false;
179         }
180
181         for (int i = 0; i < list_count; i++) {
182                 if (dprintf(fd, "%sBegin bugreport <%s>\n", (i > 0) ? "\n" : "", list[i].file_path) == -1) {
183                         _E("Write error: %m");
184                         result = false;
185                         goto out;
186                 }
187
188                 write_bugreport_log_file(fd, list[i].file_path);
189
190                 if (dprintf(fd, "End bugreport <%s>\n", list[i].file_path) == -1) {
191                         _E("Write error: %m");
192                         result = false;
193                         goto out;
194                 }
195         }
196
197 out:
198         for (int i = 0; i < list_count; i++)
199                 free_record(&list[i]);
200         free(list);
201         return result;
202 }
203
204 static bool write_crash_info(int fd, long time_from, long time_to)
205 {
206         bool result = true;
207         size_t list_count = 0;
208
209         struct report_file *list = get_reports(time_from, time_to, &list_count);
210         if (list == NULL) {
211                 dprintf(fd, "Internal error.\n");
212                 return false;
213         }
214
215         for (int i = 0; i < list_count; i++) {
216                 int nfd = diagnostics_report_get_file(list[i].file_path, INFO_FILE);
217                 if (nfd <= 0) {
218                         _I("No INFO_FILE in %s file", list[i].file_path);
219                         dprintf(nfd, "No INFO_FILE in %s\n",  list[i].file_path);
220                         continue;
221                 }
222
223                 if (dprintf(fd, "%sBegin crash-info <%s>\n", (i > 0) ? "\n" : "", list[i].file_path) == -1) {
224                         _E("Write error: %m");
225                         result = false;
226                         goto out;
227                 }
228
229                 if (copy_bytes(fd, nfd, NULL) == -1) {
230                         _E("Copy data error");
231                         close(nfd);
232                         result = false;
233                         goto out;
234                 }
235
236                 close(nfd);
237
238                 if (dprintf(fd, "End crash-info <%s>\n", list[i].file_path) == -1) {
239                         _E("Write error: %m");
240                         result = false;
241                         goto out;
242                 }
243         }
244
245 out:
246         for (int i = 0; i < list_count; i++)
247                 free_record(&list[i]);
248         free(list);
249         return result;
250 }
251
252 struct diagnostics_call_options {
253         enum BUGREPORT_REPORT_TYPE report_type;
254         long from, to;
255         bool last_set, from_set, to_set;
256 };
257
258 static bool diagnostics_call_parse_options(int out_fd, char **params, int params_size, struct diagnostics_call_options *dco)
259 {
260         struct timespec cur_time;
261         clock_gettime(CLOCK_REALTIME, &cur_time);
262         dco->report_type = BR_BUGREPORT;
263         dco->from = 0;
264         dco->to = cur_time.tv_sec;
265         dco->last_set = false;
266         dco->from_set = false;
267         dco->to_set = false;
268
269         enum PARAM_NAME { PN_TYPE = 1, PN_LAST, PN_TO, PN_FROM};
270
271         struct option long_options[] = {
272                 {"type", required_argument, NULL, PN_TYPE},
273                 {"last", required_argument, NULL, PN_LAST},
274                 {"to", required_argument, NULL, PN_TO},
275                 {"from", required_argument, NULL, PN_FROM},
276                 {0, 0, 0, 0},
277         };
278
279         int opt;
280         optind = 0;
281
282         char **nparams = alloca((params_size+1)*sizeof(char*));
283         memcpy(&nparams[1], params, params_size*sizeof(char*));
284         nparams[0] = "n";
285         while ((opt = getopt_long_only(params_size+1, nparams, "", long_options, NULL)) != -1) {
286                 switch(opt) {
287                 case PN_TYPE:
288                         if (strcmp(optarg, "bugreport") == 0) {
289                                 dco->report_type = BR_BUGREPORT;
290                         } else if (strcmp(optarg, "crash-info") == 0) {
291                                 dco->report_type = BR_CRASHINFO;
292                         } else {
293                                 _E("Incorrect report type: %s", optarg);
294                                 dprintf(out_fd, "Incorrect report type: %s\n", optarg);
295                                 return false;
296                         }
297                         break;
298                 case PN_LAST:
299                         dco->last_set = true;
300                         dco->from = cur_time.tv_sec - strtol(optarg, NULL, 10);
301                         break;
302                 case PN_FROM:
303                         dco->from_set = true;
304                         dco->from = strtol(optarg, NULL, 10);
305                         break;
306                 case PN_TO:
307                         dco->to_set = true;
308                         dco->to = strtol(optarg, NULL, 10);
309                         break;
310                 default:
311                         _E("Incorrect option: %s", (optind > 0 && optind + 1 < params_size) ? params[optind-1] : "<unknown>");
312                         dprintf(out_fd, "Incorrect option: %s\n", (optind > 0 && optind + 1 < params_size) ? params[optind-1] : "<unknown>");
313                         return false;
314                 }
315         }
316
317         return true;
318 }
319
320 static void diagnostics_callback(diagnostics_data_h data, char **params, int params_size, diagnostics_ctx_h ctx, void *user_data)
321 {
322         int fd;
323         int ret = diagnostics_data_get_fd(data, &fd);
324         if (ret < 0) {
325                 _E("Get data file descriptor error: %d", ret);
326                 return;
327         }
328         struct diagnostics_call_options dco;
329
330         if (!diagnostics_call_parse_options(fd, params, params_size, &dco))
331                 return;
332
333         if ((dco.last_set && (dco.from_set || dco.to_set)) || (dco.to_set && !dco.from_set)) {
334                 _E("Incorrect parameters set");
335                 dprintf(fd, "Incorrect parameters set.\n");
336                 return;
337         }
338
339         switch(dco.report_type) {
340         case BR_CRASHINFO:
341                 write_crash_info(fd, dco.from, dco.to);
342                 break;
343         case BR_BUGREPORT:
344                 write_bugreport(fd, dco.from, dco.to);
345                 break;
346         default:
347                 _E("Unknown report type\n");
348         }
349 }
350
351 bool diagnostics_init()
352 {
353         diagnostics_set_client_id("org.tizen.bugreport-service");
354         if (diagnostics_set_data_request_cb(&diagnostics_callback, NULL) != DIAGNOSTICS_ERROR_NONE) {
355                 _E("Unable to register diagnostics callback");
356                 return false;
357         }
358
359         return true;
360 }
361