blob: 8ab75fd2b49e2ab53bdc14fdff2d5fe61624f43b [file] [log] [blame]
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Copyright (C) 2023 MediaTek Incorporation. All Rights Reserved.
*
* Author: Alvin Kuo <Alvin.Kuo@mediatek.com>
*/
#include <stdbool.h>
#include <signal.h>
#include <limits.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include <time.h>
#include <poll.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "common.h"
#include "logger.h"
static struct logger_runtime_info runtime_infos[LOG_NUM_MAX] = {
[LOG_NUM_MGMT] = {
.relayfs_path = LOG_MGMT_RELAYFS_PATH,
.log_name = LOG_MGMT_NAME,
},
[LOG_NUM_OFFLOAD] = {
.relayfs_path = LOG_OFFLOAD_RELAYFS_PATH,
.log_name = LOG_OFFLOAD_NAME,
},
};
static bool sig_rcv;
static void __tops_logger_runtime_info_deinit(int log_num)
{
struct logger_runtime_info *ri;
for (; log_num >= 0; log_num--) {
ri = &runtime_infos[log_num];
free(ri->log_file_path);
close(ri->relayfs_fd);
globfree(&ri->relayfs_glob);
}
}
static void tops_logger_runtime_info_deinit(void)
{
__tops_logger_runtime_info_deinit(LOG_NUM_MAX - 1);
}
static int __tops_logger_runtime_info_init(struct logger_runtime_info *ri,
const char *log_file_dir,
char *log_time_str)
{
int ret;
ret = glob(ri->relayfs_path, 0, NULL, &ri->relayfs_glob);
if (ret != 0) {
fprintf(stderr,
LOG_FMT("glob(%s) fail(%d)\n"),
ri->relayfs_path, ret);
goto out;
} else if (ri->relayfs_glob.gl_pathc > 1) {
fprintf(stderr,
LOG_FMT("glob(%s) match %lu paths\n"),
ri->relayfs_path, ri->relayfs_glob.gl_pathc);
ret = -EINVAL;
goto free_glob;
}
ri->relayfs_fd = open(ri->relayfs_glob.gl_pathv[0], O_RDONLY);
if (ri->relayfs_fd < 0) {
fprintf(stderr,
LOG_FMT("open(%s) fail(%s)\n"),
ri->relayfs_glob.gl_pathv[0], strerror(errno));
ret = ri->relayfs_fd;
goto free_glob;
}
ri->log_file_path = malloc(strlen(log_file_dir) + 1 +
strlen(ri->log_name) + 1 +
strlen(log_time_str) + 1);
if (!ri->log_file_path) {
ret = -ENOMEM;
goto close_fd;
}
sprintf(ri->log_file_path, "%s/%s-%s", log_file_dir, ri->log_name, log_time_str);
out:
return ret;
close_fd:
close(ri->relayfs_fd);
free_glob:
globfree(&ri->relayfs_glob);
return ret;
}
static int tops_logger_runtime_info_init(const char *log_file_dir)
{
char log_time_str[32];
time_t log_time;
int log_num;
int ret;
time(&log_time);
ret = time_to_str(&log_time, log_time_str, sizeof(log_time_str));
if (ret < 0) {
fprintf(stderr,
LOG_FMT("time_to_str(%lu) fail(%d)\n"),
log_time, ret);
goto out;
}
for (log_num = 0; log_num < LOG_NUM_MAX; log_num++) {
ret = __tops_logger_runtime_info_init(
&runtime_infos[log_num],
log_file_dir,
log_time_str);
if (ret)
goto deinit_logger_runtime_info;
}
out:
return ret;
deinit_logger_runtime_info:
__tops_logger_runtime_info_deinit(log_num - 1);
return ret;
}
static int tops_logger_log_save(char *file, char *data, uint32_t data_len)
{
int ret = 0;
int fd;
fd = open(file, O_RDWR | O_APPEND | O_CREAT, 0664);
if (fd < 0) {
fprintf(stderr,
LOG_FMT("open(%s) fail(%s)\n"),
file, strerror(errno));
ret = -1;
goto out;
}
write(fd, data, data_len);
close(fd);
out:
return ret;
}
static int tops_logger_log_proc(struct pollfd *pfds)
{
struct logger_runtime_info *ri;
char buffer[BUFFER_LEN];
int log_num;
int ret = 0;
for (log_num = 0; log_num < LOG_NUM_MAX; log_num++) {
ri = &runtime_infos[log_num];
if (pfds[log_num].revents & (POLLIN | POLLHUP | POLLERR)) {
ret = read(pfds[log_num].fd, buffer, BUFFER_LEN);
if (ret < 0) {
fprintf(stderr,
LOG_FMT("read log from %s failed(%d)\n"),
ri->relayfs_glob.gl_pathv[0], ret);
if (errno == EINTR || errno == EAGAIN)
continue;
break;
} else if (ret == 0) {
continue;
}
ret = tops_logger_log_save(ri->log_file_path,
buffer, ret);
if (ret) {
fprintf(stderr,
LOG_FMT("save log to %s failed(%d)\n"),
ri->log_file_path, ret);
break;
}
}
}
return ret;
}
static void signal_handler(int sig)
{
(void)sig;
fprintf(stderr,
LOG_FMT("killall or Ctrl+C received, stop saving log\n"));
sig_rcv = true;
}
static int tops_logger_is_running(bool *running)
{
char result[4];
int ret = 0;
ssize_t n;
int fd;
fd = open(LOGGER_DEBUGFS_PATH, O_RDONLY);
if (fd < 0) {
fprintf(stderr,
LOG_FMT("open(%s) fail(%s)\n"),
LOGGER_DEBUGFS_PATH, strerror(errno));
ret = errno;
goto out;
}
n = read(fd, result, sizeof(result) - 1);
if (n == -1) {
fprintf(stderr,
LOG_FMT("read(%s) fail(%s)\n"),
LOGGER_DEBUGFS_PATH, strerror(errno));
ret = errno;
goto close_file;
}
result[n] = '\0';
if (strcmp(result, "ON") == 0) {
fprintf(stderr, LOG_FMT("logger is running\n"));
*running = true;
} else {
*running = false;
}
close_file:
close(fd);
out:
return ret;
}
int tops_tool_run_logger(int argc, char *argv[])
{
const char *log_file_dir = argv[2];
bool running = false;
struct stat st;
int ret;
if (!log_file_dir) {
ret = -EINVAL;
goto out;
}
/* reserve 256 bytes for log file name */
if (strlen(log_file_dir) > (PATH_MAX - 256)) {
fprintf(stderr,
LOG_FMT("log_file_dir(%s) length %zu > %u\n"),
log_file_dir, strlen(log_file_dir), PATH_MAX - 256);
ret = -EINVAL;
goto out;
}
ret = tops_logger_is_running(&running);
if (ret || running) {
ret = -EINVAL;
goto out;
}
if (stat(log_file_dir, &st)) {
if (errno == ENOENT) {
ret = mkdir_p(log_file_dir, 0775);
if (ret) {
fprintf(stderr,
LOG_FMT("mkdir_p(%s) failed(%d)\n"),
log_file_dir, ret);
goto out;
}
} else {
fprintf(stderr,
LOG_FMT("stat(%s) failed(%s)\n"),
log_file_dir, strerror(errno));
ret = -EINVAL;
goto out;
}
}
signal(SIGTERM, signal_handler);
signal(SIGINT, signal_handler);
signal(SIGQUIT, signal_handler);
ret = tops_logger_runtime_info_init(log_file_dir);
if (ret)
goto out;
system("echo ON > " LOGGER_DEBUGFS_PATH);
while (1) {
struct pollfd pfds[LOG_NUM_MAX] = {
[LOG_NUM_MGMT] = {
.fd = runtime_infos[LOG_NUM_MGMT].relayfs_fd,
.events = POLLIN | POLLHUP | POLLERR,
.revents = 0,
},
[LOG_NUM_OFFLOAD] = {
.fd = runtime_infos[LOG_NUM_OFFLOAD].relayfs_fd,
.events = POLLIN | POLLHUP | POLLERR,
.revents = 0,
},
};
if (sig_rcv)
break;
poll(pfds, LOG_NUM_MAX, -1);
if (sig_rcv)
break;
ret = tops_logger_log_proc(pfds);
if (ret)
break;
}
system("echo OFF > " LOGGER_DEBUGFS_PATH);
tops_logger_runtime_info_deinit();
out:
return ret;
}