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, ¤t))
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