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