1 /*
2 * Copyright (C) 2018-2022 Intel Corporation.
3 * SPDX-License-Identifier: BSD-3-Clause
4 */
5
6 #include <string.h>
7 #include <stdio.h>
8 #include <malloc.h>
9 #include <sys/mman.h>
10 #include <sys/types.h>
11 #include <sys/stat.h>
12 #include <fcntl.h>
13 #include <unistd.h>
14 #include <errno.h>
15 #include "load_conf.h"
16 #include "fsutils.h"
17 #include "strutils.h"
18 #include "log_sys.h"
19 #include "crash_reclassify.h"
20
21 /**
22 * Check if file contains content or not.
23 * This function couldn't use for binary file.
24 *
25 * @param file Starting address of file cache.
26 * @param content String to be searched.
27 *
28 * @return 1 if find the same string, or 0 if not.
29 */
has_content(const char * file,const char * content)30 static int has_content(const char *file, const char *content)
31 {
32 if (content && strstr(file, content))
33 return 1;
34
35 return 0;
36 }
37
38 /**
39 * Check if file contains all configured contents or not.
40 * This function couldn't use for binary file.
41 *
42 * @param crash Crash need checking.
43 * @param file Starting address of file cache.
44 *
45 * @return 1 if all configured strings were found, or 0 if not.
46 */
crash_has_all_contents(const struct crash_t * crash,const char * file)47 static int crash_has_all_contents(const struct crash_t *crash,
48 const char *file)
49 {
50 int id;
51 int ret = 1;
52 const char *content;
53
54 for_each_content_crash(id, content, crash) {
55 if (!content)
56 continue;
57
58 if (!has_content(file, content)) {
59 ret = 0;
60 break;
61 }
62 }
63
64 return ret;
65 }
66
67 /**
68 * Might content is a 2-D array, write as mc[exp][cnt]
69 * This function implements the following algorithm:
70 *
71 * r_mc[exp] = has_content(mc[exp][0]) || has_content(mc[exp][1]) || ...
72 * result = r_mc[0] && r_mc[1] && ...
73 *
74 * This function couldn't use for binary file.
75 *
76 * @param crash Crash need checking.
77 * @param file Starting address of file cache.
78 *
79 * @return 1 if result is true, or 0 if false.
80 */
crash_has_mightcontents(const struct crash_t * crash,const char * file)81 static int crash_has_mightcontents(const struct crash_t *crash,
82 const char *file)
83 {
84 int ret = 1;
85 int ret_exp;
86 int expid, cntid;
87 const char * const *exp;
88 const char *content;
89
90 for_each_expression_crash(expid, exp, crash) {
91 if (!exp || !exp_valid(exp))
92 continue;
93
94 ret_exp = 0;
95 for_each_content_expression(cntid, content, exp) {
96 if (!content)
97 continue;
98
99 if (has_content(file, content)) {
100 ret_exp = 1;
101 break;
102 }
103 }
104 if (ret_exp == 0) {
105 ret = 0;
106 break;
107 }
108 }
109
110 return ret;
111 }
112
113 /**
114 * Judge the type of crash, according to configured content/mightcontent.
115 * This function couldn't use for binary file.
116 *
117 * @param crash Crash need checking.
118 * @param file Starting address of file cache.
119 *
120 * @return 1 if file matches these strings configured in crash, or 0 if not.
121 */
crash_match_content(const struct crash_t * crash,const char * file)122 static int crash_match_content(const struct crash_t *crash, const char *file)
123 {
124 return crash_has_all_contents(crash, file) &&
125 crash_has_mightcontents(crash, file);
126 }
127
_get_data(const char * file,const struct crash_t * crash,char ** data,size_t * dsize,const int index)128 static int _get_data(const char *file, const struct crash_t *crash,
129 char **data, size_t *dsize, const int index)
130 {
131 const char *search_key;
132 char *value;
133 char *end;
134 char *data_new;
135 ssize_t size;
136 const size_t max_size = 255;
137
138 search_key = crash->data[index];
139 if (!search_key)
140 goto empty;
141
142 value = strrstr(file, search_key);
143 if (!value)
144 goto empty;
145
146 end = strchr(value, '\n');
147 if (!end)
148 goto empty;
149
150 size = MIN(max_size, (size_t)(end - value));
151 if (!size)
152 goto empty;
153
154 data_new = realloc(*data, *dsize + size + 1);
155 if (!data_new) {
156 LOGE("failed to realloc\n");
157 return -1;
158 }
159
160 strncpy(data_new + *dsize, value, size);
161 *(data_new + *dsize + size) = 0;
162
163 *data = data_new;
164 *dsize += size;
165 return 0;
166 empty:
167 data_new = realloc(*data, *dsize + 1);
168 if (!data_new) {
169 LOGE("failed to realloc\n");
170 return -1;
171 }
172
173 *(data_new + *dsize) = 0;
174
175 *data = data_new;
176 *dsize += 1;
177 return 0;
178 }
179
180 /**
181 * Get segment from file, according to 'data' configuread in crash.
182 * This function couldn't use for binary file.
183 *
184 * @param file Starting address of file cache.
185 * @param crash Crash need checking.
186 * @param[out] data Searched result, according to 'data' configuread in crash.
187 *
188 * @return 0 if successful, or -1 if not.
189 */
get_data(const char * file,const struct crash_t * crash,char ** r_data,size_t * r_dsize)190 static int get_data(const char *file, const struct crash_t *crash,
191 char **r_data, size_t *r_dsize)
192 {
193 char *data = NULL;
194 size_t dsize = 0;
195 int i;
196
197 /* to find strings which match conf words */
198 for (i = 0; i < DATA_MAX; i++) {
199 if (_get_data(file, crash, &data, &dsize, i) == -1)
200 goto fail;
201 }
202
203 *r_data = data;
204 *r_dsize = dsize;
205 return 0;
206 fail:
207 if (data)
208 free(data);
209 return -1;
210 }
211
crash_match_file(const struct crash_t * crash,const char * filename)212 static int crash_match_file(const struct crash_t *crash, const char *filename)
213 {
214 size_t size;
215 void *cnt;
216
217 if (read_file(filename, &size, &cnt) == -1) {
218 LOGE("read %s failed, error (%s)\n", filename, strerror(errno));
219 return 0;
220 }
221 if (!size)
222 return 0;
223 if (crash_match_content(crash, cnt)) {
224 free(cnt);
225 return 1;
226 }
227 free(cnt);
228 return 0;
229 }
230
crash_match_filefmt(const struct crash_t * crash,const char * filefmt)231 int crash_match_filefmt(const struct crash_t *crash, const char *filefmt)
232 {
233 int count;
234 int i;
235 int ret = 0;
236 char **files;
237
238 count = config_fmt_to_files(filefmt, &files);
239 if (count <= 0)
240 return ret;
241 for (i = 0; i < count; i++) {
242 if (crash_match_file(crash, files[i])) {
243 ret = 1;
244 break;
245 }
246 }
247 for (i = 0; i < count; i++)
248 free(files[i]);
249 free(files);
250 return ret;
251 }
252
crash_find_matched_child(const struct crash_t * crash,const char * rtrfmt)253 static struct crash_t *crash_find_matched_child(const struct crash_t *crash,
254 const char *rtrfmt)
255 {
256 struct crash_t *child;
257 struct crash_t *matched_child = NULL;
258 const char *trfile_fmt;
259
260 if (!crash)
261 return NULL;
262
263 for_crash_children(child, crash) {
264 if (!child->trigger)
265 continue;
266
267 if (!strcmp(child->trigger->type, "dir"))
268 trfile_fmt = rtrfmt;
269 else
270 trfile_fmt = child->trigger->path;
271
272 if (crash_match_filefmt(child, trfile_fmt)) {
273 matched_child = child;
274 break;
275 }
276 }
277
278 /* It returns the first matched crash */
279 return matched_child;
280 }
281
282 /**
283 * Judge the crash type. We only got a root crash from channel, sometimes,
284 * we need to calculate a more specific type.
285 * This function reclassify the crash type by searching trigger file's content.
286 * This function couldn't use for binary file.
287 *
288 * @param rcrash Root crash obtained from channel.
289 * @param rtrfile_fmt Path fmt of trigger file of root crash.
290 * @param[out] data Searched result, according to 'data' configuread in crash.
291 *
292 * @return a pointer to the calculated crash structure if successful,
293 * or NULL if not.
294 */
crash_reclassify_by_content(const struct crash_t * rcrash,const char * rtrfile_fmt,char ** data,size_t * dsize)295 static struct crash_t *crash_reclassify_by_content(const struct crash_t *rcrash,
296 const char *rtrfile_fmt,
297 char **data, size_t *dsize)
298 {
299 int count;
300 const struct crash_t *crash;
301 const struct crash_t *ret_crash = rcrash;
302 const char *trfile_fmt;
303 char **trfiles;
304 void *content;
305 unsigned long size;
306 int i;
307
308 if (!rcrash || !data || !dsize)
309 return NULL;
310
311 crash = rcrash;
312
313 while (1) {
314 crash = crash_find_matched_child(crash, rtrfile_fmt);
315 if (!crash)
316 break;
317
318 ret_crash = crash;
319 }
320
321 if (!strcmp(ret_crash->trigger->type, "dir"))
322 trfile_fmt = rtrfile_fmt;
323 else
324 trfile_fmt = ret_crash->trigger->path;
325
326 /* trfile may not be specified */
327 if (!trfile_fmt)
328 return (struct crash_t *)ret_crash;
329
330 count = config_fmt_to_files(trfile_fmt, &trfiles);
331 if (count <= 0)
332 return (struct crash_t *)ret_crash;
333
334 /* get data from last file */
335 if (read_file(trfiles[count - 1], &size, &content) == -1) {
336 LOGE("failed to read %s, error (%s)\n",
337 trfiles[count - 1], strerror(errno));
338 goto free_files;
339 }
340 if (!size)
341 goto free_files;
342
343 if (get_data(content, ret_crash, data, dsize) == -1)
344 LOGE("failed to get data\n");
345
346 free(content);
347
348 free_files:
349 for (i = 0; i < count; i++)
350 free(trfiles[i]);
351 free(trfiles);
352
353 return (struct crash_t *)ret_crash;
354 }
355
356 /**
357 * Initailize crash reclassify, we only got a root crash from channel,
358 * sometimes, we need to get a more specific type.
359 */
init_crash_reclassify(void)360 void init_crash_reclassify(void)
361 {
362 int id;
363 struct crash_t *crash;
364
365 for_each_crash(id, crash, conf) {
366 if (!crash)
367 continue;
368
369 crash->reclassify = crash_reclassify_by_content;
370 }
371 }
372