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