1 /*
2  * Copyright (C) 2018-2022 Intel Corporation.
3  * SPDX-License-Identifier: BSD-3-Clause
4  */
5 
6 /*
7  * Copyright (C) 2018-2022 Intel Corporation.
8  *
9  * Licensed under the Apache License, Version 2.0 (the "License");
10  * you may not use this file except in compliance with the License.
11  * You may obtain a copy of the License at
12  *
13  *      http://www.apache.org/licenses/LICENSE-2.0
14  *
15  * Unless required by applicable law or agreed to in writing, software
16  * distributed under the License is distributed on an "AS IS" BASIS,
17  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18  * See the License for the specific language governing permissions and
19  * limitations under the License.
20  */
21 
22 #include <string.h>
23 #include <stdio.h>
24 #include <malloc.h>
25 #include <stdlib.h>
26 #include <sys/stat.h>
27 #include <sys/types.h>
28 #include <fcntl.h>
29 #include <unistd.h>
30 #include "fsutils.h"
31 #include "load_conf.h"
32 #include "history.h"
33 #include "log_sys.h"
34 #include "probeutils.h"
35 #include "strutils.h"
36 
37 #define HISTORY_FIRST_LINE_FMT \
38 		"#V1.0 CURRENTUPTIME   %-24s\n"
39 #define HISTORY_BLANK_LINE2 \
40 		"#EVENT  ID                    DATE                 TYPE\n"
41 
42 struct history_entry {
43 	const char *event;
44 	const char *type;
45 	const char *log;
46 	const char *lastuptime; /* for uptime */
47 	const char *key;
48 	const char *eventtime;
49 };
50 
51 char *history_file;
52 static int current_lines;
53 
54 #define EVENT_COUNT_FILE_NAME "all_events"
55 
56 static char *all_events_cnt;
57 static size_t all_events_size;
58 
event_count_file_path(char * path,size_t size)59 static int event_count_file_path(char *path, size_t size)
60 {
61 	struct sender_t *crashlog = get_sender_by_name("crashlog");
62 	int res;
63 
64 	if (!crashlog || !path || !size)
65 		return -1;
66 
67 	res = snprintf(path, size, "%s/%s", crashlog->outdir,
68 		       EVENT_COUNT_FILE_NAME);
69 	if (s_not_expect(res, size))
70 		return -1;
71 
72 	return 0;
73 }
74 
update_event_count_file(struct history_entry * entry)75 static void update_event_count_file(struct history_entry *entry)
76 {
77 	char path[PATH_MAX];
78 	char line[MAXLINESIZE];
79 	char *update_line;
80 	char *all_events_new;
81 	int len;
82 
83 	if (!entry->event)
84 		return;
85 
86 	if (entry->type)
87 		len = snprintf(line, sizeof(line), "%s-%s: ", entry->event,
88 			       entry->type);
89 	else
90 		len = snprintf(line, sizeof(line), "%s: ", entry->event);
91 
92 	if (s_not_expect(len, sizeof(line)))
93 		return;
94 
95 	update_line = strstr(all_events_cnt, line);
96 	if (!update_line) {
97 		*(char *)(mempcpy(line + len, "1\n", 2)) = '\0';
98 		len += 2;
99 		all_events_new = realloc(all_events_cnt, all_events_size +
100 					 len + 1);
101 		if (!all_events_new)
102 			return;
103 
104 		*(char *)(mempcpy(all_events_new + all_events_size,
105 				  line, len)) = '\0';
106 		all_events_cnt = all_events_new;
107 		all_events_size += len;
108 	} else {
109 		char *s = strstr(update_line, ": ");
110 		char *e = strchr(update_line, '\n');
111 		const char *fmt = "%*[: ]%[[0-9]*]";
112 		char num_str[16];
113 		int num;
114 		char *ne;
115 		char *replace;
116 
117 		if (!s || !e)
118 			return;
119 
120 		if (str_split_ere(s, e - s, fmt, strlen(fmt), num_str,
121 				  sizeof(num_str)) != 1)
122 			return;
123 
124 		if (cfg_atoi(num_str, strnlen(num_str, sizeof(num_str)),
125 			     &num) == -1)
126 			return;
127 
128 		if (strspn(num_str, "9") == strnlen(num_str, sizeof(num_str))) {
129 			all_events_new = realloc(all_events_cnt,
130 						 all_events_size + 1 + 1);
131 			if (!all_events_new)
132 				return;
133 
134 			ne = all_events_new + (e - all_events_cnt);
135 			memmove(ne + 1, ne,
136 				all_events_cnt + all_events_size - e + 1);
137 			replace = all_events_new + (s - all_events_cnt) + 2;
138 
139 			all_events_cnt = all_events_new;
140 			all_events_size++;
141 		} else {
142 			replace = s + 2;
143 		}
144 
145 		len = snprintf(num_str, sizeof(num_str), "%u", num + 1);
146 		if (s_not_expect(len, sizeof(num_str)))
147 			return;
148 
149 		memcpy(replace, num_str, len);
150 	}
151 
152 	if (event_count_file_path(path, sizeof(path)) == -1)
153 		return;
154 
155 	if (overwrite_file(path, all_events_cnt)) {
156 		LOGE("failed to write %s, %s\n", path,
157 		     strerror(errno));
158 		return;
159 	}
160 	return;
161 }
162 
init_event_count_file(void)163 static int init_event_count_file(void)
164 {
165 	char path[PATH_MAX];
166 
167 	if (event_count_file_path(path, sizeof(path)) == -1)
168 		return -1;
169 
170 	if (!file_exists(path)) {
171 		if (overwrite_file(path, "Total:\n")) {
172 			LOGE("failed to prepare %s, %s\n", path,
173 			     strerror(errno));
174 			return -1;
175 		}
176 	}
177 
178 	if (read_file(path, &all_events_size,
179 		      (void *)&all_events_cnt) == -1) {
180 		LOGE("failed to read %s, %s\n", path,
181 		     strerror(errno));
182 		return -1;
183 	}
184 	return 0;
185 }
186 
entry_to_history_line(struct history_entry * entry,char * newline,size_t size)187 static int entry_to_history_line(struct history_entry *entry,
188 				char *newline, size_t size)
189 {
190 	const char *general_event_with_msg = "%-8s%-22s%-20s%-16s %s\n";
191 	const char *general_event_without_msg = "%-8s%-22s%-20s%-16s\n";
192 	const char *simple_event = "%-8s%-22s%-20s%s\n";
193 	int len;
194 
195 	if (!entry || !entry->event || !entry->key || !entry->eventtime)
196 		return -1;
197 
198 	if (entry->type) {
199 		const char *fmt;
200 		const char *msg;
201 
202 		if (entry->log || entry->lastuptime) {
203 			fmt = general_event_with_msg;
204 			msg = entry->log ? entry->log : entry->lastuptime;
205 			len = snprintf(newline, size, fmt,
206 				       entry->event, entry->key,
207 				       entry->eventtime, entry->type, msg);
208 		} else {
209 			fmt = general_event_without_msg;
210 			len = snprintf(newline, size, fmt,
211 				       entry->event, entry->key,
212 				       entry->eventtime, entry->type);
213 		}
214 	} else if (entry->lastuptime) {
215 		len = snprintf(newline, size, simple_event,
216 			       entry->event, entry->key,
217 			       entry->eventtime, entry->lastuptime);
218 	} else
219 		return -1;
220 
221 	if (s_not_expect(len, size))
222 		return -1;
223 	return 0;
224 }
225 
backup_history(void)226 static void backup_history(void)
227 {
228 	int ret;
229 	char *des;
230 
231 	ret = asprintf(&des, "%s.bak", history_file);
232 	if (ret < 0) {
233 		LOGE("compute string failed, out of memory\n");
234 		return;
235 	}
236 
237 	ret = do_mv(history_file, des);
238 	if (ret < 0) {
239 		LOGE("backup %s failed, error (%s)\n", history_file,
240 		     strerror(errno));
241 		goto free;
242 	}
243 
244 	ret = prepare_history();
245 	if (ret < 0) {
246 		LOGE("Prepare new history_file failed, exit\n");
247 		exit(EXIT_FAILURE);
248 	}
249 
250 free:
251 	free(des);
252 }
253 
hist_raise_event(const char * event,const char * type,const char * log,const char * lastuptime,const char * key)254 void hist_raise_event(const char *event, const char *type, const char *log,
255 			const char *lastuptime, const char *key)
256 {
257 	char line[MAXLINESIZE];
258 	char eventtime[LONG_TIME_SIZE];
259 	struct sender_t *crashlog;
260 	int maxlines;
261 	struct history_entry entry = {
262 		.event = event,
263 		.type = type,
264 		.log = log,
265 		.lastuptime = lastuptime,
266 		.key = key
267 	};
268 
269 	/* here means user have configured the crashlog sender */
270 	crashlog = get_sender_by_name("crashlog");
271 	if (!crashlog)
272 		return;
273 
274 	update_event_count_file(&entry);
275 
276 	if (cfg_atoi(crashlog->maxlines, crashlog->maxlines_len,
277 		     &maxlines) == -1)
278 		return;
279 
280 	if (++current_lines >= maxlines) {
281 		LOGW("lines of (%s) meet quota %d, backup... Pls clean!\n",
282 		     history_file, maxlines);
283 		backup_history();
284 	}
285 
286 	if (get_current_time_long(eventtime) <= 0)
287 		return;
288 
289 	entry.eventtime = eventtime;
290 	if (entry_to_history_line(&entry, line, sizeof(line)) == -1) {
291 		LOGE("failed to generate new line\n");
292 		return;
293 	}
294 	if (append_file(history_file, line, strnlen(line, MAXLINESIZE)) <= 0) {
295 		LOGE("failed to append (%s) to (%s)\n", line, history_file);
296 		return;
297 	}
298 }
299 
hist_raise_uptime(char * lastuptime)300 void hist_raise_uptime(char *lastuptime)
301 {
302 	char boot_time[UPTIME_SIZE];
303 	char firstline[MAXLINESIZE];
304 	int hours;
305 	int ret;
306 	char *key;
307 	static int loop_uptime_event = 1;
308 	struct sender_t *crashlog;
309 	struct uptime_t *uptime;
310 	static int uptime_hours;
311 
312 	/* user have configured the crashlog sender */
313 	crashlog = get_sender_by_name("crashlog");
314 	if (!crashlog)
315 		return;
316 
317 	uptime = crashlog->uptime;
318 	if (cfg_atoi(uptime->eventhours, uptime->eventhours_len,
319 		     &uptime_hours) == -1)
320 		return;
321 
322 	if (lastuptime)
323 		hist_raise_event(uptime->name, NULL, NULL, lastuptime,
324 			    "00000000000000000000");
325 	else {
326 		ret = get_uptime_string(boot_time, &hours);
327 		if (ret < 0) {
328 			LOGE("cannot get uptime - %s\n", strerror(-ret));
329 			return;
330 		}
331 
332 		ret = snprintf(firstline, sizeof(firstline),
333 			       HISTORY_FIRST_LINE_FMT, boot_time);
334 		if (s_not_expect(ret, sizeof(firstline))) {
335 			LOGE("failed to construct the firstline\n");
336 			return;
337 		}
338 		replace_file_head(history_file, firstline);
339 
340 		if (hours / uptime_hours >= loop_uptime_event) {
341 			loop_uptime_event = (hours / uptime_hours) + 1;
342 
343 			key = generate_event_id((const char *)uptime->name,
344 						uptime->name_len,
345 						NULL, 0, KEY_SHORT);
346 			if (key == NULL) {
347 				LOGE("generate event id failed, error (%s)\n",
348 				     strerror(errno));
349 				return;
350 			}
351 
352 			hist_raise_event(uptime->name, NULL, NULL,
353 					 boot_time, key);
354 			free(key);
355 		}
356 	}
357 }
358 
hist_raise_infoerror(const char * type,size_t tlen)359 void hist_raise_infoerror(const char *type, size_t tlen)
360 {
361 	char *key;
362 
363 	key = generate_event_id("ERROR", 5, type, tlen, KEY_SHORT);
364 	if (key == NULL) {
365 		LOGE("generate event id failed, error (%s)\n",
366 		     strerror(errno));
367 		return;
368 	}
369 
370 	hist_raise_event("ERROR", type, NULL, NULL, key);
371 	free(key);
372 }
373 
get_time_from_firstline(char * buffer,size_t size)374 static int get_time_from_firstline(char *buffer, size_t size)
375 {
376 	char lasttime[MAXLINESIZE];
377 	const char *prefix = "#V1.0 CURRENTUPTIME   ";
378 	int len;
379 
380 	len = file_read_key_value(lasttime, MAXLINESIZE, history_file, prefix,
381 				  strlen(prefix));
382 	if (len <= 0) {
383 		LOGW("failed to read value from %s, error %s\n",
384 		      history_file, strerror(-len));
385 		return -1;
386 	}
387 	if ((size_t)len >= size)
388 		return -1;
389 
390 	*(char *)mempcpy(buffer, lasttime, len) = '\0';
391 
392 	return 0;
393 }
394 
prepare_history(void)395 int prepare_history(void)
396 {
397 	int ret;
398 	int llen;
399 	struct sender_t *crashlog;
400 	char linebuf[MAXLINESIZE];
401 
402 	crashlog = get_sender_by_name("crashlog");
403 	if (!crashlog)
404 		return 0;
405 
406 	if (init_event_count_file() == -1)
407 		return -1;
408 
409 	if (!history_file) {
410 		ret = asprintf(&history_file, "%s/%s", crashlog->outdir,
411 			       HISTORY_NAME);
412 		if (ret < 0) {
413 			LOGE("compute string failed, out of memory\n");
414 			return -ENOMEM;
415 		}
416 	}
417 
418 	ret = get_time_from_firstline(linebuf, MAXLINESIZE);
419 	if (ret == 0) {
420 		current_lines = count_lines_in_file(history_file);
421 		hist_raise_uptime(linebuf);
422 	} else {
423 		/* new history */
424 		LOGW("new history\n");
425 		llen = snprintf(linebuf, sizeof(linebuf),
426 				HISTORY_FIRST_LINE_FMT, "0000:00:00");
427 		if (s_not_expect(llen, sizeof(linebuf))) {
428 			LOGE("failed to construct the fristline\n");
429 			return -EINVAL;
430 		}
431 		ret = overwrite_file(history_file, linebuf);
432 		if (ret < 0) {
433 			LOGE("Write (%s, %s) failed, error (%s)\n",
434 			     history_file, linebuf,
435 			     strerror(errno));
436 			return ret;
437 		}
438 		ret = append_file(history_file, HISTORY_BLANK_LINE2,
439 				  sizeof(HISTORY_BLANK_LINE2) - 1);
440 		if (ret < 0) {
441 			LOGE("Write (%s, %s) failed, error (%s)\n",
442 			     history_file, HISTORY_BLANK_LINE2,
443 			     strerror(-ret));
444 			return ret;
445 		}
446 		current_lines = count_lines_in_file(history_file);
447 	}
448 
449 	return 0;
450 }
451