* Copyright (c) 2025 Huawei Technologies Co., Ltd.
* This program is free software, you can redistribute it and/or modify it under the terms and conditions of
* CANN Open Software License Agreement Version 2.0 (the "License").
* Please refer to the License for details. You may not use this file except in compliance with the License.
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
* See LICENSE in the root of the software repository for the full text of the License.
*/
#include <string>
#include <cstdlib>
#include <cstring>
#include <clocale>
#include <cctype>
#include <syscall.h>
#include <mutex>
#include <sstream>
#include <iostream>
#include <iomanip>
#include <algorithm>
#include <sys/stat.h>
#include <pwd.h>
#include <fcntl.h>
#include <unistd.h>
#include <dirent.h>
#include <sys/statvfs.h>
#include <securec.h>
#include "log/log_core_sip.h"
#include "utils/env.h"
#include "mki/utils/strings/match.h"
#include "mki/utils/strings/str_checker.h"
#include "mki/utils/strings/str_split.h"
#include "log/log_sink_file_sip.h"
namespace AsdSip {
constexpr size_t MAX_LOG_FILE_COUNT = 50;
constexpr size_t MAX_FILE_NAME_LEN = 128;
constexpr uint64_t MAX_FILE_SIZE_THRESHOLD = 20 * 1024 * 1024;
constexpr uint64_t DISK_AVAILABEL_LIMIT = 1 * 1024 * 1024 * 1024;
static bool isValidChar(unsigned char c)
{
return (c >= 'a' && c <= 'z') ||
(c >= 'A' && c <= 'Z') ||
(c >= '0' && c <= '9') ||
(c == '.') || (c == '_') ||
(c == '-') || (c == '/');
}
std::mutex localeMutex;
static bool IsValidFileName(const char *name)
{
std::lock_guard<std::mutex> lock(localeMutex);
size_t len = strlen(name);
if (len == 0 || len > MAX_FILE_NAME_LEN) {
return false;
}
std::string original_locale = std::setlocale(LC_CTYPE, nullptr);
std::setlocale(LC_CTYPE, "en_US.UTF-8");
for (size_t i = 0; i < len; ++i) {
char c = name[i];
if (!isValidChar(static_cast<unsigned char>(c))) {
std::setlocale(LC_CTYPE, original_locale.c_str());
return false;
}
}
std::setlocale(LC_CTYPE, original_locale.c_str());
return true;
}
LogSinkFileSip::LogSinkFileSip() { Init(); }
LogSinkFileSip::~LogSinkFileSip() { CloseFile(); }
void LogSinkFileSip::LogSip(const char *log, uint64_t logLen)
{
std::lock_guard<std::mutex> guard(mutex_);
if (currentFileSize_ + logLen >= MAX_FILE_SIZE_THRESHOLD) {
CloseFile();
}
if (currentFd_ < 0) {
OpenFile();
}
if (currentFd_ < 0) {
return;
}
ssize_t writeSize = write(currentFd_, log, logLen);
if (writeSize != static_cast<ssize_t>(logLen)) {
std::cout << "asdsip_log write file fail, want to write size: " << logLen
<< ", success write size:" << writeSize;
CloseFile();
return;
}
currentFileSize_ += writeSize;
}
static bool IsDirectory(const std::string& path)
{
struct stat statbuf;
if (stat(path.c_str(), &statbuf) != 0) {
return false;
}
return S_ISDIR(statbuf.st_mode);
}
static bool IsWritable(const std::string& path)
{
return access(path.c_str(), W_OK) == 0;
}
static void ReplaceAll(std::string& str, const std::string& from, const std::string& to)
{
if (from.empty()) {
return;
}
size_t start_pos = 0;
while ((start_pos = str.find(from, start_pos)) != std::string::npos) {
str.replace(start_pos, from.length(), to);
start_pos += to.length();
}
}
static std::string SafeNormalizePath(const std::string& inputPath)
{
if (inputPath.empty()) {
return "";
}
std::string path;
size_t i = 0;
while (i < inputPath.size()) {
if (inputPath[i] == '%' && i + 2 < inputPath.size()) {
char hex[3] = { inputPath[i + 1], inputPath[i + 2], '\0' };
char* end;
long val = strtol(hex, &end, 16);
if (end == hex + 2) {
path += static_cast<char>(val);
i += 3;
continue;
}
}
path += inputPath[i];
++i;
}
ReplaceAll(path, ".", ".");
ReplaceAll(path, "﹒", ".");
ReplaceAll(path, "。", ".");
ReplaceAll(path, "/", "/");
size_t null_pos = path.find('\0');
if (null_pos != std::string::npos) {
path = path.substr(0, null_pos);
}
std::replace(path.begin(), path.end(), '\\', '/');
std::vector<std::string> parts;
std::string current;
bool isAbsolute = (path[0] == '/');
for (char c : path) {
if (c == '/') {
if (!current.empty()) {
if (current == ".") {
} else if (current == "..") {
if (!parts.empty() && parts.back() != "..") {
parts.pop_back();
} else if (!isAbsolute) {
parts.push_back(current);
}
} else {
parts.push_back(current);
}
current.clear();
}
} else {
current += c;
}
}
if (!current.empty()) {
if (current == ".") {
} else if (current == "..") {
if (!parts.empty() && parts.back() != "..") {
parts.pop_back();
} else if (!isAbsolute) {
parts.push_back(current);
}
} else {
parts.push_back(current);
}
}
std::string normalized;
for (const auto &part : parts) {
if (!normalized.empty() || isAbsolute) {
normalized += "/";
}
normalized += part;
}
if (normalized.empty()) {
normalized = isAbsolute ? "/" : ".";
}
return normalized;
}
static std::string ResolveAndValidatePath(const std::string& inputPath)
{
std::string normalized = SafeNormalizePath(inputPath);
char resolved[PATH_MAX] = {0};
if (realpath(normalized.c_str(), resolved) == nullptr) {
if (mkdir(normalized.c_str(), 0755) != 0 && errno != EEXIST) {
return "";
}
if (realpath(normalized.c_str(), resolved) == nullptr) {
return "";
}
}
std::string realPath = resolved;
if (!IsDirectory(realPath)) {
return "";
}
if (!IsWritable(realPath)) {
return "";
}
const std::vector<std::string> forbiddenPaths = {
"/", "/bin", "/sbin", "/usr", "/etc", "/root", "/var", "/sys", "/proc"
};
for (const auto& forbidden : forbiddenPaths) {
if (realPath == forbidden || realPath.find(forbidden + "/") == 0) {
return "";
}
}
std::string current = realPath;
while (!current.empty()) {
char linkPath[PATH_MAX] = {0};
ssize_t len = readlink(current.c_str(), linkPath, sizeof(linkPath) - 1);
if (len > 0) {
linkPath[len] = '\0';
std::string linkTarget = SafeNormalizePath(linkPath);
if (!linkTarget.empty() && linkTarget[0] != '/') {
size_t pos = current.find_last_of('/');
if (pos != std::string::npos) {
linkTarget = current.substr(0, pos) + "/" + linkTarget;
}
}
for (const auto& forbidden : forbiddenPaths) {
if (linkTarget.find(forbidden) == 0) {
return "";
}
}
size_t pos = current.find_last_of('/');
if (pos == 0) {
break;
}
current = current.substr(0, pos);
} else {
break;
}
}
return realPath;
}
void LogSinkFileSip::Init()
{
const char *env = "asdsip";
boostType_ = env && strlen(env) <= MAX_ENV_STRING_LEN && IsValidFileName(env) ? std::string(env) : "asdsip";
env = std::getenv("ASCEND_PROCESS_LOG_PATH") ? std::getenv("ASCEND_PROCESS_LOG_PATH") : "asdsip";
std::string logRootDir =
env && strlen(env) <= MAX_ENV_STRING_LEN && IsValidFileName(env) ? std::string(env) : GetHomeDir();
logRootDir = ResolveAndValidatePath(logRootDir);
if (logRootDir.empty()) {
std::cerr << "Warning: User-specified log path is invalid: " << env;
std::string defaultPath = GetHomeDir();
logRootDir = ResolveAndValidatePath(defaultPath);
}
logDir_ = logRootDir + "/log" + "/" + boostType_;
env = "1";
isFlush_ = env && strlen(env) <= MAX_ENV_STRING_LEN ? std::string(env) == "1" : false;
}
bool LogSinkFileSip::IsFileNameMatched(const std::string &fileName, std::string &createTime)
{
std::string prefix = boostType_ + '_';
bool match = Mki::StartsWith(fileName, prefix);
if (!match) {
return false;
}
match = Mki::EndsWith(fileName, ".log");
if (!match) {
return false;
}
size_t subStrLen = fileName.length() - prefix.length() - 4;
std::string subStr = fileName.substr(prefix.length(), subStrLen);
std::vector<std::string> splitResult;
Mki::StrSplit(subStr, '_', splitResult);
if (splitResult.size() != 2) {
return false;
}
for (auto &str : splitResult) {
if (str.empty() || !std::all_of(str.begin(), str.end(), ::isdigit)) {
return false;
}
}
createTime = splitResult.at(1);
return true;
}
void LogSinkFileSip::DeleteOldestFile()
{
std::vector<std::pair<std::string, std::string>> logFiles;
DIR *dir = opendir(logDir_.c_str());
if (!dir) {
return;
}
struct dirent *ptr = nullptr;
while ((ptr = readdir(dir)) != nullptr) {
if (ptr->d_name[0] != '.') {
std::string fileName = ptr->d_name;
std::string createTime;
if (IsFileNameMatched(fileName, createTime)) {
logFiles.emplace_back(logDir_ + "/" + fileName, createTime);
}
}
}
closedir(dir);
std::sort(logFiles.begin(), logFiles.end(),
[](std::pair<std::string, std::string> &a, std::pair<std::string, std::string> &b) {
return a.second < b.second;
});
if (logFiles.size() >= MAX_LOG_FILE_COUNT) {
size_t deleteCount = logFiles.size() - MAX_LOG_FILE_COUNT + 1;
for (size_t i = 0; i < deleteCount; ++i) {
std::cout << "asdsip_log delete old file:" << logFiles[i].first << std::endl;
remove(logFiles[i].first.c_str());
}
}
}
void LogSinkFileSip::OpenFile()
{
MakeLogDir();
DeleteOldestFile();
if (!IsDiskAvailable()) {
return;
}
std::string logFilePath = GetNewLogFilePath();
if (currentFd_ >= 0) {
CloseFile();
}
currentFd_ = open(logFilePath.c_str(), O_RDWR | O_CREAT | O_APPEND, S_IRUSR | S_IWUSR | S_IRGRP);
if (currentFd_ < 0) {
std::cout << "asdsip_log open " << logFilePath << " fail" << std::endl;
if (remove(logFilePath.c_str()) == 0) {
std::cerr << "Successfully removed residual file: " << logFilePath << std::endl;
} else {
std::cerr << "Failed to remove residual file: " << logFilePath << " - " << strerror(errno) << std::endl;
}
}
}
std::string LogSinkFileSip::GetNewLogFilePath()
{
std::time_t tmpTime = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
struct tm *nowTime = std::localtime(&tmpTime);
std::stringstream filePath;
filePath << logDir_ << "/" << boostType_ << "_" << std::to_string(syscall(SYS_getpid)) << "_"
<< std::put_time(nowTime, "%Y%m%d%H%M%S") << ".log";
return filePath.str();
}
bool LogSinkFileSip::IsDiskAvailable()
{
struct statvfs vfs;
if (statvfs(logDir_.c_str(), &vfs) == -1) {
std::cout << "asdsip_log get current disk stats fail" << std::endl;
return false;
}
uint64_t availableSize = vfs.f_bsize * vfs.f_bfree;
if (availableSize <= DISK_AVAILABEL_LIMIT) {
std::cout << "asdsip_log disk available space it too low, available size:" << availableSize
<< ", limit size:" << DISK_AVAILABEL_LIMIT << std::endl;
return false;
}
return true;
}
void LogSinkFileSip::MakeLogDir()
{
struct stat st;
if (stat(logDir_.c_str(), &st) == 0) {
return;
}
mode_t mode = S_IRWXU;
uint32_t offset = 0;
uint32_t pathLen = logDir_.size();
do {
const char *str = strchr(logDir_.c_str() + offset, '/');
offset = (str == nullptr) ? pathLen : str - logDir_.c_str() + 1;
std::string childDir = logDir_.substr(0, offset);
if (stat(childDir.c_str(), &st) < 0) {
std::cout << "asdsip_log mkdir " << childDir << std::endl;
if (mkdir(childDir.c_str(), mode) < 0) {
std::cerr << "Failed to create directory: " << childDir << ", error: " << strerror(errno) << std::endl;
return;
}
}
} while (offset != pathLen);
}
void LogSinkFileSip::CloseFile()
{
if (currentFd_ > 0) {
(void)fchmod(currentFd_, S_IRUSR | S_IRGRP);
close(currentFd_);
currentFd_ = -1;
currentFileSize_ = 0;
}
}
std::string LogSinkFileSip::GetHomeDir()
{
int bufsize;
if ((bufsize = sysconf(_SC_GETPW_R_SIZE_MAX)) == -1) {
return "";
}
char buffer[bufsize] = {0};
struct passwd pwd;
struct passwd *result = nullptr;
if (getpwuid_r(getuid(), &pwd, buffer, bufsize, &result) != 0 && !result) {
return "";
}
return std::string(pwd.pw_dir);
}
}