/* * Copyright (C) 2018 Intel Corporation * SPDX-License-Identifier: BSD-3-Clause */ /* * Copyright (C) 2018 Intel Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include #include "property.h" #include "fsutils.h" #include "history.h" #include "load_conf.h" #include "log_sys.h" #include "probeutils.h" #define CRASH_CURRENT_LOG "currentcrashlog" #define STATS_CURRENT_LOG "currentstatslog" #define VM_CURRENT_LOG "currentvmlog" #define BOOTID_NODE "/proc/sys/kernel/random/boot_id" #define BOOTID_LOG "currentbootid" unsigned long long get_uptime(void) { long long time_ns; struct timespec ts; int res; res = clock_gettime(CLOCK_BOOTTIME, &ts); if (res == -1) return res; time_ns = (long long)ts.tv_sec * 1000000000LL + (long long)ts.tv_nsec; return time_ns; } int get_uptime_string(char *newuptime, int *hours) { long long tm; int seconds, minutes; tm = get_uptime(); if (tm == -1) return -1; /* seconds */ *hours = (int)(tm / 1000000000LL); seconds = *hours % 60; /* minutes */ *hours /= 60; minutes = *hours % 60; /* hours */ *hours /= 60; return snprintf(newuptime, UPTIME_SIZE, "%04d:%02d:%02d", *hours, minutes, seconds); } int get_current_time_long(char *buf) { time_t t; struct tm *time_val; time(&t); time_val = localtime((const time_t *)&t); if (!time_val) return -1; return strftime(buf, LONG_TIME_SIZE, "%Y-%m-%d/%H:%M:%S ", time_val); } /** * Compute a key with 20 characters * * @param[out] key The result key. * @param seed1 Seed. * @param seed2 Seed. * * @return 0 if successful, or -1 if not. */ static int compute_key(char *key, char *seed1, char *seed2) { static SHA_CTX *sha; char buf[VERSION_SIZE] = {'\0',}; long long time_ns = 0; char *tmp_key = key; unsigned char results[SHA_DIGEST_LENGTH]; int i; int ret; if (sha == NULL) { sha = (SHA_CTX *)malloc(sizeof(SHA_CTX)); if (sha == NULL) { LOGE("cannot create SHA_CTX memory...\n"); return -1; } ret = SHA1_Init(sha); if (ret != 1) { LOGE("SHA1_Init failed, error (%s)\n", strerror(errno)); free(sha); sha = NULL; return -1; } } if (!key || !seed1 || !seed2) return -1; time_ns = get_uptime(); snprintf(buf, VERSION_SIZE, "%s%s%s%s%lld", gbuildversion, guuid, seed1, seed2, time_ns); ret = SHA1_Update(sha, (unsigned char *)buf, strlen(buf)); if (ret != 1) { LOGE("SHA1_Update failed, error (%s)\n", strerror(errno)); return -1; } ret = SHA1_Final(results, sha); if (ret != 1) { LOGE("SHA1_Final failed, error (%s)\n", strerror(errno)); return -1; } for (i = 0; i < SHA_DIGEST_LENGTH / 2; i++) { sprintf(tmp_key, "%02x", results[i]); tmp_key += 2; } *tmp_key = 0; return 0; } /** * Compute a key with 32 characters * * @param[out] key The result key. * @param seed Seed. * * @return 0 if successful, or -1 if not. */ static int compute_key256(char *key, char *seed) { static SHA256_CTX *sha; char buf[VERSION_SIZE] = {'\0',}; long long time_ns = 0; char *tmp_key = key; unsigned char results[SHA256_DIGEST_LENGTH]; int i; int ret; if (sha == NULL) { sha = (SHA256_CTX *)malloc(sizeof(SHA256_CTX)); if (sha == NULL) { LOGE("cannot create SHA256_CTX memory...\n"); return -1; } ret = SHA256_Init(sha); if (ret != 1) { LOGE("SHA256_Init failed, error (%s)\n", strerror(errno)); free(sha); sha = NULL; return -1; } } if (!key || !seed) return -1; time_ns = get_uptime(); snprintf(buf, VERSION_SIZE, "%s%s%s%lld", gbuildversion, guuid, seed, time_ns); ret = SHA256_Update(sha, (unsigned char *)buf, strlen(buf)); if (ret != 1) { LOGE("SHA256_Update failed, error (%s)\n", strerror(errno)); return -1; } ret = SHA256_Final(results, sha); if (ret != 1) { LOGE("SHA256_Final failed, error (%s)\n", strerror(errno)); return -1; } for (i = 0; i < SHA256_DIGEST_LENGTH / 2; i++) { sprintf(tmp_key, "%02x", results[i]); tmp_key += 2; } *tmp_key = 0; return 0; } /** * Generate a event id with 20 characters * * @param seed1 Seed. * @param seed2 Seed. * * @return a pointer to result haskkey if successful, or NULL if not. */ char *generate_event_id(char *seed1, char *seed2) { int ret; char *key = (char *)malloc(SHA_DIGEST_LENGTH + 1); if (!key) return NULL; ret = compute_key(key, seed1, seed2); if (ret < 0) { LOGE("compute_key error\n"); free(key); key = NULL; } return key; } /** * Generate a event id with 32 characters * * @param seed Seed. * * @return a pointer to result haskkey if successful, or NULL if not. */ char *generate_eventid256(char *seed) { int ret; char *key = (char *)malloc(SHA256_DIGEST_LENGTH + 1); if (!key) return NULL; ret = compute_key256(key, seed); if (ret < 0) { LOGE("compute_key256 error\n"); free(key); key = NULL; } return key; } /** * Reserve a dir for log storage. * * @param mode Mode for log storage. * @param[out] dir Prefix of dir path reserved. * @param[out] index of dir reserved. * * @return 0 if successful, or -1 if not. */ static int reserve_log_folder(enum e_dir_mode mode, char *dir, unsigned int *current) { char path[512]; int res; struct sender_t *crashlog; char *outdir; unsigned int maxdirs; crashlog = get_sender_by_name("crashlog"); if (!crashlog) return -1; outdir = crashlog->outdir; switch (mode) { case MODE_CRASH: sprintf(path, "%s/%s", outdir, CRASH_CURRENT_LOG); sprintf(dir, "%s/%s", outdir, "crashlog"); break; case MODE_STATS: sprintf(path, "%s/%s", outdir, STATS_CURRENT_LOG); sprintf(dir, "%s/%s", outdir, "stats"); break; case MODE_VMEVENT: sprintf(path, "%s/%s", outdir, VM_CURRENT_LOG); sprintf(dir, "%s/%s", outdir, "vmevent"); break; default: LOGW("Invalid mode %d\n", mode); return -1; } /* Read current value in file */ res = file_read_int(path, current); if (res < 0) return res; maxdirs = atoi(crashlog->maxcrashdirs); /* Open file in read/write mode to update the new current */ res = file_update_int(path, *current, maxdirs); if (res < 0) return res; return 0; } #define strcat_fmt(buf, fmt, ...) \ (__extension__ \ ({ \ char __buf[1024] = {'\0',}; \ snprintf(__buf, sizeof(__buf), fmt, ##__VA_ARGS__); \ strcat(buf, __buf); \ }) \ ) /** * Create a crashfile with given params. * * @param dir Where to generate crashfile. * @param event Event name. * @param hashkey Event id. * @param type Subtype of this event. * @param data* String obtained by get_data. */ void generate_crashfile(char *dir, char *event, char *hashkey, char *type, char *data0, char *data1, char *data2) { char *buf; char *path; char datetime[LONG_TIME_SIZE]; char uptime[UPTIME_SIZE]; int hours; int ret; const int fmtsize = 128; int filesize; datetime[0] = 0; ret = get_current_time_long(datetime); if (ret <= 0) return; uptime[0] = 0; get_uptime_string(uptime, &hours); filesize = fmtsize + strlen(event) + strlen(hashkey) + strlen(guuid) + strlen(datetime) + strlen(uptime) + strlen(gbuildversion) + strlen(type); if (data0) filesize += strlen(data0); if (data1) filesize += strlen(data1); if (data2) filesize += strlen(data2); buf = malloc(filesize); if (buf == NULL) { LOGE("compute string failed, out of memory\n"); return; } memset(buf, 0, filesize); strcat_fmt(buf, "EVENT=%s\n", event); strcat_fmt(buf, "ID=%s\n", hashkey); strcat_fmt(buf, "DEVICEID=%s\n", guuid); strcat_fmt(buf, "DATE=%s\n", datetime); strcat_fmt(buf, "UPTIME=%s\n", uptime); strcat_fmt(buf, "BUILD=%s\n", gbuildversion); strcat_fmt(buf, "TYPE=%s\n", type); if (data0) strcat_fmt(buf, "DATA0=%s\n", data0); if (data1) strcat_fmt(buf, "DATA1=%s\n", data1); if (data2) strcat_fmt(buf, "DATA2=%s\n", data2); strcat(buf, "_END\n"); ret = asprintf(&path, "%s/%s", dir, "crashfile"); if (ret < 0) { LOGE("compute string failed, out of memory\n"); free(buf); return; } ret = overwrite_file(path, buf); if (ret) LOGE("new crashfile (%s) fail, error (%s)\n", path, strerror(errno)); free(buf); free(path); } /** * Create a dir for log storage. * * @param mode Mode for log storage. * @param hashkey Event id. * * @return a pointer to generated path if successful, or NULL if not. */ char *generate_log_dir(enum e_dir_mode mode, char *hashkey) { char *path; char dir[PATH_MAX]; unsigned int current; int ret; ret = reserve_log_folder(mode, dir, ¤t); if (ret) return NULL; ret = asprintf(&path, "%s%d_%s", dir, current, hashkey); if (ret == -1) { LOGE("construct log path failed, out of memory\n"); hist_raise_infoerror("DIR CREATE"); return NULL; } ret = mkdir(path, 0777); if (ret == -1) { LOGE("Cannot create dir %s\n", path); hist_raise_infoerror("DIR CREATE"); free(path); return NULL; } return path; } int is_boot_id_changed(void) { void *boot_id; void *logged_boot_id; char logged_boot_id_path[PATH_MAX]; unsigned long size; struct sender_t *crashlog; int res; int result = 1; /* returns changed by default */ crashlog = get_sender_by_name("crashlog"); if (!crashlog) return result; res = read_file(BOOTID_NODE, &size, &boot_id); if (res == -1 || !size) return result; snprintf(logged_boot_id_path, sizeof(logged_boot_id_path), "%s/%s", crashlog->outdir, BOOTID_LOG); if (file_exists(logged_boot_id_path)) { res = read_file(logged_boot_id_path, &size, &logged_boot_id); if (res == -1 || !size) goto out; if (!strcmp((char *)logged_boot_id, (char *)boot_id)) result = 0; free(logged_boot_id); } if (result) overwrite_file(logged_boot_id_path, boot_id); out: free(boot_id); return result; }