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 <stdio.h>
23 #include <malloc.h>
24 #include <errno.h>
25 #include <string.h>
26 #include <stdlib.h>
27 #include <limits.h>
28 #include <openssl/sha.h>
29 #if OPENSSL_VERSION_NUMBER >= 0x30000000L
30 #include <openssl/evp.h>
31 #endif
32 #include <time.h>
33 #include "property.h"
34 #include "fsutils.h"
35 #include "history.h"
36 #include "load_conf.h"
37 #include "log_sys.h"
38 #include "probeutils.h"
39 #include "strutils.h"
40 
41 #define CRASH_CURRENT_LOG       "currentcrashlog"
42 #define STATS_CURRENT_LOG       "currentstatslog"
43 #define VM_CURRENT_LOG		"currentvmlog"
44 
45 #define BOOTID_NODE		"/proc/sys/kernel/random/boot_id"
46 #define BOOTID_LOG		"currentbootid"
47 
get_uptime(void)48 unsigned long long get_uptime(void)
49 {
50 	long long time_ns;
51 	struct timespec ts;
52 	int res;
53 
54 	res = clock_gettime(CLOCK_BOOTTIME, &ts);
55 	if (res == -1)
56 		return res;
57 
58 	time_ns = (long long)ts.tv_sec * 1000000000LL +
59 		  (long long)ts.tv_nsec;
60 
61 	return time_ns;
62 }
63 
get_uptime_string(char * newuptime,int * hours)64 int get_uptime_string(char *newuptime, int *hours)
65 {
66 	long long tm;
67 	int seconds, minutes;
68 	int len;
69 
70 	tm = get_uptime();
71 	if (tm == -1)
72 		return -1;
73 
74 	/* seconds */
75 	*hours = (int)(tm / 1000000000LL);
76 	seconds = *hours % 60;
77 
78 	/* minutes */
79 	*hours /= 60;
80 	minutes = *hours % 60;
81 
82 	/* hours */
83 	*hours /= 60;
84 
85 	len = snprintf(newuptime, UPTIME_SIZE, "%04d:%02d:%02d", *hours,
86 		       minutes, seconds);
87 	if (s_not_expect(len, UPTIME_SIZE))
88 		return -1;
89 	return 0;
90 }
91 
get_current_time_long(char * buf)92 int get_current_time_long(char *buf)
93 {
94 	time_t t;
95 	struct tm *time_val;
96 
97 	time(&t);
98 	time_val = localtime((const time_t *)&t);
99 	if (!time_val)
100 		return -1;
101 
102 	return strftime(buf, LONG_TIME_SIZE, "%Y-%m-%d/%H:%M:%S  ", time_val);
103 }
104 
compute_key(char * key,size_t klen,const char * seed,const size_t slen)105 static int compute_key(char *key, size_t klen, const char *seed,
106 			const size_t slen)
107 {
108 	char buf[VERSION_SIZE];
109 	int len;
110 	long long time_ns;
111 	char *tmp_key = key;
112 	unsigned char results[SHA256_DIGEST_LENGTH];
113 	size_t i;
114 
115 	if (!key || !seed || !slen)
116 		return -1;
117 	if (klen > SHA256_DIGEST_LENGTH * 2 || !klen)
118 		return -1;
119 
120 
121 	time_ns = get_uptime();
122 	len = snprintf(buf, VERSION_SIZE, "%s%s%lld",
123 			gbuildversion, guuid, time_ns);
124 	if (s_not_expect(len , VERSION_SIZE))
125 		return -1;
126 
127 #if OPENSSL_VERSION_NUMBER >= 0x30000000L
128 	EVP_MD_CTX *mdctx = EVP_MD_CTX_new();
129 	EVP_DigestInit_ex(mdctx, EVP_sha256(), NULL);
130 	EVP_DigestUpdate(mdctx, (unsigned char *)buf, strnlen(buf, VERSION_SIZE));
131 	EVP_DigestUpdate(mdctx, (unsigned char *)seed, strnlen(seed, slen));
132 	EVP_DigestFinal_ex(mdctx, results, NULL);
133 	EVP_MD_CTX_free(mdctx);
134 #else
135 	SHA256_CTX sha;
136 	SHA256_Init(&sha);
137 	SHA256_Update(&sha, (unsigned char *)buf, strnlen(buf, VERSION_SIZE));
138 	SHA256_Update(&sha, (unsigned char *)seed, strnlen(seed, slen));
139 	SHA256_Final(results, &sha);
140 #endif
141 
142 	for (i = 0; i < klen / 2; i++) {
143 		len = snprintf(tmp_key, 3, "%02x", results[i]);
144 		if (s_not_expect(len, 3))
145 			return -1;
146 		tmp_key += 2;
147 	}
148 	*tmp_key = 0;
149 
150 	return 0;
151 }
152 
153 /**
154  * Generate an event id with specified type.
155  *
156  * @param seed1 Seed1.
157  * @param seed2 Seed2, this parameter will be ignored if the value is NULL.
158  * @param type The type of key. The length of generated id will be 20
159  *		characters if type is KEY_SHORT; 32 characters if type is
160  *		KEY_LONG.
161  *
162  * @return a pointer to result haskkey if successful, or NULL if not.
163  */
generate_event_id(const char * seed1,size_t slen1,const char * seed2,size_t slen2,enum key_type type)164 char *generate_event_id(const char *seed1, size_t slen1, const char *seed2,
165 			size_t slen2, enum key_type type)
166 {
167 	int ret;
168 	char *buf;
169 	char *key;
170 	size_t klen;
171 
172 	if (!seed1 || !slen1)
173 		return NULL;
174 
175 	if (type == KEY_SHORT)
176 		klen = SHORT_KEY_LENGTH;
177 	else if (type == KEY_LONG)
178 		klen = LONG_KEY_LENGTH;
179 	else
180 		return NULL;
181 
182 	key = (char *)malloc(klen + 1);
183 	if (!key) {
184 		LOGE("failed to generate event id, out of memory\n");
185 		return NULL;
186 	}
187 
188 	if (seed2) {
189 		if (asprintf(&buf, "%s%s", seed1, seed2) == -1) {
190 			LOGE("failed to generate event id, out of memory\n");
191 			free(key);
192 			return NULL;
193 		}
194 		ret = compute_key(key, klen, (const char *)buf, slen1 + slen2);
195 		free(buf);
196 	} else {
197 		ret = compute_key(key, klen, seed1, slen1);
198 	}
199 
200 	if (ret < 0) {
201 		LOGE("compute_key error\n");
202 		free(key);
203 		key = NULL;
204 	}
205 
206 	return key;
207 }
208 
209 /**
210  * Reserve a dir for log storage.
211  *
212  * @param mode Mode for log storage.
213  * @param[out] dir Prefix of dir path reserved.
214  * @param[out] index of dir reserved.
215  *
216  * @return 0 if successful, or -1 if not.
217  */
reserve_log_folder(enum e_dir_mode mode,char * dir,unsigned int * current)218 static int reserve_log_folder(enum e_dir_mode mode, char *dir,
219 				unsigned int *current)
220 {
221 	char path[PATH_MAX];
222 	int res;
223 	int plen;
224 	int dlen;
225 	struct sender_t *crashlog;
226 	const char *outdir;
227 	int maxdirs;
228 
229 	crashlog = get_sender_by_name("crashlog");
230 	if (!crashlog)
231 		return -1;
232 
233 	outdir = crashlog->outdir;
234 
235 	switch (mode) {
236 	case MODE_CRASH:
237 		plen = snprintf(path, PATH_MAX, "%s/%s", outdir,
238 				CRASH_CURRENT_LOG);
239 		dlen = snprintf(dir, PATH_MAX, "%s/%s", outdir, "crashlog");
240 		break;
241 	case MODE_STATS:
242 		plen = snprintf(path, PATH_MAX, "%s/%s", outdir,
243 			STATS_CURRENT_LOG);
244 		dlen = snprintf(dir, PATH_MAX, "%s/%s", outdir, "stats");
245 		break;
246 	case MODE_VMEVENT:
247 		plen = snprintf(path, PATH_MAX, "%s/%s", outdir,
248 				VM_CURRENT_LOG);
249 		dlen = snprintf(dir, PATH_MAX, "%s/%s", outdir, "vmevent");
250 		break;
251 	default:
252 		LOGW("Invalid mode %d\n", mode);
253 		return -1;
254 	}
255 
256 	if (s_not_expect(plen, PATH_MAX) || s_not_expect(dlen, PATH_MAX)) {
257 		LOGE("the length of path/dir is too long\n");
258 		return -1;
259 	}
260 	/* Read current value in file */
261 	res = file_read_int(path, current);
262 	if (res < 0)
263 		return res;
264 
265 	if (cfg_atoi(crashlog->maxcrashdirs, crashlog->maxcrashdirs_len,
266 		     &maxdirs) == -1)
267 		return -1;
268 	if (maxdirs <= 0) {
269 		LOGE("failed to reserve dir, maxdirs must be greater than 0\n");
270 		return -1;
271 	}
272 	/* Open file in read/write mode to update the new current */
273 	res = file_update_int(path, *current, (unsigned int)maxdirs);
274 	if (res < 0)
275 		return res;
276 
277 
278 	return 0;
279 }
280 
cf_line(char * dest,const char * k,size_t klen,const char * v,size_t vlen)281 static char *cf_line(char *dest, const char *k, size_t klen, const char *v,
282 			size_t vlen)
283 {
284 	char *t;
285 
286 	t = mempcpy(dest, k, klen);
287 	t = mempcpy(t, v, vlen);
288 	return mempcpy(t, "\n", 1);
289 }
290 
291 /**
292  * Create a crashfile with given params.
293  *
294  * @param dir Where to generate crashfile.
295  * @param event Event name.
296  * @param hashkey Event id.
297  * @param type Subtype of this event.
298  * @param data* String obtained by get_data.
299  */
generate_crashfile(const char * dir,const char * event,size_t elen,const char * hashkey,size_t hlen,const char * type,size_t tlen,const char * data0,size_t d0len,const char * data1,size_t d1len,const char * data2,size_t d2len)300 void generate_crashfile(const char *dir,
301 			const char *event, size_t elen,
302 			const char *hashkey, size_t hlen,
303 			const char *type, size_t tlen,
304 			const char *data0, size_t d0len,
305 			const char *data1, size_t d1len,
306 			const char *data2, size_t d2len)
307 {
308 	char *buf;
309 	char *path;
310 	char *tail;
311 	char datetime[LONG_TIME_SIZE];
312 	char uptime[UPTIME_SIZE];
313 	int hours;
314 	const int fmtsize = 128;
315 	size_t ltlen;
316 	int n;
317 	int filesize;
318 
319 	if (!dir || !event || !elen || !hashkey || !hlen ||
320 	    !type || !tlen)
321 		return;
322 	if (d0len > 0 && !data0)
323 		return;
324 	if (d1len > 0 && !data1)
325 		return;
326 	if (d2len > 0 && !data2)
327 		return;
328 
329 	ltlen = get_current_time_long(datetime);
330 	if (!ltlen)
331 		return;
332 	n = get_uptime_string(uptime, &hours);
333 	if (n < 0)
334 		return;
335 
336 	filesize = fmtsize + ltlen + n + elen + hlen + tlen + d0len + d1len +
337 		   d2len + strnlen(guuid, UUID_SIZE) +
338 		   strnlen(gbuildversion, BUILD_VERSION_SIZE);
339 
340 	buf = malloc(filesize);
341 	if (buf == NULL) {
342 		LOGE("out of memory\n");
343 		return;
344 	}
345 
346 	tail = cf_line(buf, "EVENT=", 6, event, elen);
347 	tail = cf_line(tail, "ID=", 3, hashkey, hlen);
348 	tail = cf_line(tail, "DEVICEID=", 9, guuid, strnlen(guuid, UUID_SIZE));
349 	tail = cf_line(tail, "DATE=", 5, datetime, ltlen);
350 	tail = cf_line(tail, "UPTIME=", 7, uptime, n);
351 	tail = cf_line(tail, "BUILD=", 6, gbuildversion,
352 		       strnlen(gbuildversion, BUILD_VERSION_SIZE));
353 	tail = cf_line(tail, "TYPE=", 5, type, tlen);
354 
355 	if (d0len)
356 		tail = cf_line(tail, "DATA0=", 6, data0, d0len);
357 	if (d1len)
358 		tail = cf_line(tail, "DATA1=", 6, data1, d1len);
359 	if (d2len)
360 		tail = cf_line(tail, "DATA2=", 6, data2, d2len);
361 	tail = mempcpy(tail, "_END\n", 5);
362 	*tail = '\0';
363 
364 	if (asprintf(&path, "%s/crashfile", dir) == -1) {
365 		LOGE("out of memory\n");
366 		free(buf);
367 		return;
368 	}
369 
370 	if (overwrite_file(path, buf) != 0)
371 		LOGE("failed to new crashfile (%s), error (%s)\n", path,
372 		     strerror(errno));
373 
374 	free(buf);
375 	free(path);
376 }
377 
378 /**
379  * Create a dir for log storage.
380  *
381  * @param mode Mode for log storage.
382  * @param hashkey Event id.
383  * @param[out] dir_len Length of generated dir.
384  *
385  * @return a pointer to generated path if successful, or NULL if not.
386  */
generate_log_dir(enum e_dir_mode mode,char * hashkey,size_t * dir_len)387 char *generate_log_dir(enum e_dir_mode mode, char *hashkey, size_t *dir_len)
388 {
389 	char *path;
390 	char dir[PATH_MAX];
391 	unsigned int current;
392 	int len;
393 
394 	if (reserve_log_folder(mode, dir, &current))
395 		return NULL;
396 
397 	len = asprintf(&path, "%s%d_%s", dir, current, hashkey);
398 	if (len == -1) {
399 		LOGE("construct log path failed, out of memory\n");
400 		hist_raise_infoerror("DIR CREATE", 10);
401 		return NULL;
402 	}
403 
404 	if (mkdir(path, 0777) == -1) {
405 		LOGE("Cannot create dir %s\n", path);
406 		hist_raise_infoerror("DIR CREATE", 10);
407 		free(path);
408 		return NULL;
409 	}
410 
411 	if (dir_len)
412 		*dir_len = (size_t)len;
413 	return path;
414 }
415 
is_boot_id_changed(void)416 int is_boot_id_changed(void)
417 {
418 	void *boot_id;
419 	void *logged_boot_id;
420 	char logged_boot_id_path[PATH_MAX];
421 	unsigned long size;
422 	struct sender_t *crashlog;
423 	int res;
424 	int result = 1; /* returns changed by default */
425 
426 	crashlog = get_sender_by_name("crashlog");
427 	if (!crashlog)
428 		return result;
429 
430 	res = read_file(BOOTID_NODE, &size, &boot_id);
431 	if (res == -1 || !size)
432 		return result;
433 
434 	res = snprintf(logged_boot_id_path, sizeof(logged_boot_id_path),
435 		       "%s/%s", crashlog->outdir, BOOTID_LOG);
436 	if (s_not_expect(res, sizeof(logged_boot_id_path)))
437 		goto out;
438 
439 	if (file_exists(logged_boot_id_path)) {
440 		res = read_file(logged_boot_id_path, &size, &logged_boot_id);
441 		if (res == -1 || !size)
442 			goto out;
443 
444 		if (!strcmp((char *)logged_boot_id, (char *)boot_id))
445 			result = 0;
446 
447 		free(logged_boot_id);
448 	}
449 
450 	if (result)
451 		overwrite_file(logged_boot_id_path, boot_id);
452 out:
453 	free(boot_id);
454 	return result;
455 }
456