diff --git a/devicemodel/Makefile b/devicemodel/Makefile index bceaeddb3..44a3e59d9 100644 --- a/devicemodel/Makefile +++ b/devicemodel/Makefile @@ -153,6 +153,7 @@ SRCS += arch/x86/pm.c # log SRCS += log/log.c SRCS += log/kmsg_logger.c +SRCS += log/disk_logger.c OBJS := $(patsubst %.c,$(DM_OBJDIR)/%.o,$(SRCS)) diff --git a/devicemodel/log/disk_logger.c b/devicemodel/log/disk_logger.c new file mode 100644 index 000000000..50a059e8f --- /dev/null +++ b/devicemodel/log/disk_logger.c @@ -0,0 +1,202 @@ +/* + * Copyright (C) 2018 Intel Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dm.h" +#include "log.h" + +#define DISK_PREFIX "disk_log: " + +#define LOG_PATH_NODE "/var/log/acrn-dm/" +#define LOG_NAME_PREFIX "%s_log_" +#define LOG_NAME_FMT "%s%s_log_%d" /* %s-->vm1/vm2..., %d-->1/2/3/4... */ +#define LOG_DELIMITER "\n\n----------------new vm instance------------------\n\n\0" + +#define FILE_NAME_LENGTH 96 + +#define LOG_SIZE_LIMIT 0x200000 /* one log file size limit */ +#define LOG_FILES_COUNT 8 + +static int disk_fd = -1; +static uint32_t cur_log_size; +static uint16_t cur_file_index; + +static uint8_t disk_log_level = LOG_DEBUG; +static bool disk_log_enabled = false; + +#define DISK_LOG_MAX_LEN (MAX_ONE_LOG_SIZE + 32) +#define INDEX_AFTER(a, b) ((short int)b - (short int)a < 0) + +static bool is_disk_log_enabled(void) +{ + return disk_log_enabled; +} + +static uint8_t get_disk_log_level(void) +{ + return disk_log_level; +} + +static int probe_disk_log_file(void) +{ + char file_name[FILE_NAME_LENGTH]; + struct dirent *pdir; + struct stat st; + int length; + uint16_t index = 0, tmp; + bool is_first_file = true; + DIR *dir; + + if (stat(LOG_PATH_NODE, &st)) { + if (mkdir(LOG_PATH_NODE, 0644)) { + printf(DISK_PREFIX"create path: %s failed! Error: %s\n", + LOG_PATH_NODE, strerror(errno)); + return -1; + } + } + + dir = opendir(LOG_PATH_NODE); + if (!dir) { + printf(DISK_PREFIX" open %s failed! Error: %s\n", + LOG_PATH_NODE, strerror(errno)); + return -1; + } + + snprintf(file_name, FILE_NAME_LENGTH - 1, LOG_NAME_PREFIX, vmname); + length = strlen(file_name); + + while ((pdir = readdir(dir)) != NULL) { + if (!(pdir->d_type & DT_REG)) + continue; + + if (strncmp(pdir->d_name, file_name, length) != 0) + continue; + + tmp = (uint16_t)atoi(pdir->d_name + length); + if (is_first_file) { + is_first_file = false; + index = tmp; + } else if (INDEX_AFTER(tmp, index)) { + index = tmp; + } + } + + snprintf(file_name, FILE_NAME_LENGTH - 1, LOG_NAME_FMT, LOG_PATH_NODE, vmname, index); + disk_fd = open(file_name, O_RDWR | O_CREAT | O_APPEND, 0644); + if (disk_fd < 0) { + printf(DISK_PREFIX" open %s failed! Error: %s\n", file_name, strerror(errno)); + return -1; + } + + if (write(disk_fd, LOG_DELIMITER, strlen(LOG_DELIMITER)) < 0) { + printf(DISK_PREFIX" write %s failed! Error: %s\n", file_name, strerror(errno)); + return -1; + } + + fstat(disk_fd, &st); + cur_log_size = st.st_size; + cur_file_index = index; + + return 0; +} + +static int init_disk_logger(bool enable, uint8_t log_level) +{ + disk_log_enabled = enable; + disk_log_level = log_level; + + return 1; +} + +static void deinit_disk_logger(void) +{ + if (disk_fd > 0) { + disk_log_enabled = false; + + fsync(disk_fd); + close(disk_fd); + disk_fd = -1; + } +} + +static void write_to_disk(const char *fmt, va_list args) +{ + char buffer[DISK_LOG_MAX_LEN]; + char *file_name = buffer; + int len1, len2; + int write_cnt; + struct timespec times = {0, 0}; + + if ((disk_fd < 0) && disk_log_enabled) { + /** + * usually this probe just be called once in DM whole life; but we need use vmname in + * probe_disk_log_file, it can't be called in init_disk_logger for vmname not inited then, + * so call it here. + */ + if (probe_disk_log_file() < 0) { + disk_log_enabled = false; + return; + } + } + + clock_gettime(CLOCK_MONOTONIC, ×); + len1 = sprintf(buffer, "[%5lu.%06lu] ", times.tv_sec, times.tv_nsec / 1000); + len2 = vsnprintf(buffer + len1, MAX_ONE_LOG_SIZE, fmt, args); + + write_cnt = write(disk_fd, buffer, len1 + len2); + if (write_cnt < 0) { + perror(DISK_PREFIX"write disk failed"); + close(disk_fd); + disk_fd = -1; + return; + } + + cur_log_size += write_cnt; + if (cur_log_size > LOG_SIZE_LIMIT) { + + cur_file_index++; + + /* remove the first old log file, to add a new one */ + snprintf(file_name, FILE_NAME_LENGTH - 1, LOG_NAME_FMT, + LOG_PATH_NODE, vmname, (uint16_t)(cur_file_index - LOG_FILES_COUNT)); + remove(file_name); + + snprintf(file_name, FILE_NAME_LENGTH - 1, LOG_NAME_FMT, + LOG_PATH_NODE, vmname, cur_file_index); + + close(disk_fd); + disk_fd = open(file_name, O_RDWR | O_CREAT, 0644); + if (disk_fd < 0) { + printf(DISK_PREFIX" open %s failed! Error: %s\n", file_name, strerror(errno)); + return; + } + cur_log_size = 0; + } +} + +static struct logger_ops logger_disk = { + .name = "disk", + .is_enabled = is_disk_log_enabled, + .get_log_level = get_disk_log_level, + .init = init_disk_logger, + .deinit = deinit_disk_logger, + .output = write_to_disk, +}; + +DEFINE_LOGGER_DEVICE(logger_disk);