RASCSI/cpp/shared/piscsi_util.cpp
2024-04-23 08:00:58 +09:00

153 lines
3.3 KiB
C++

//---------------------------------------------------------------------------
//
// SCSI Target Emulator PiSCSI
// for Raspberry Pi
//
// Copyright (C) 2021-2023 Uwe Seimet
//
//---------------------------------------------------------------------------
#include "controllers/controller_manager.h"
#include "piscsi_version.h"
#include "piscsi_util.h"
#include <spdlog/spdlog.h>
#include <cassert>
#include <cstring>
#include <sstream>
#include <filesystem>
#include <algorithm>
using namespace std;
using namespace filesystem;
vector<string> piscsi_util::Split(const string& s, char separator, int limit)
{
assert(limit >= 0);
string component;
vector<string> result;
stringstream str(s);
while (--limit > 0 && getline(str, component, separator)) {
result.push_back(component);
}
if (!str.eof()) {
getline(str, component);
result.push_back(component);
}
return result;
}
string piscsi_util::GetLocale()
{
const char *locale = setlocale(LC_MESSAGES, "");
if (locale == nullptr || !strcmp(locale, "C")) {
locale = "en";
}
return locale;
}
bool piscsi_util::GetAsUnsignedInt(const string& value, int& result)
{
if (value.find_first_not_of("0123456789") != string::npos) {
return false;
}
try {
auto v = stoul(value);
result = (int)v;
}
catch(const invalid_argument&) {
return false;
}
catch(const out_of_range&) {
return false;
}
return true;
}
string piscsi_util::ProcessId(const string& id_spec, int& id, int& lun)
{
id = -1;
lun = -1;
if (id_spec.empty()) {
return "Missing device ID";
}
const int id_max = ControllerManager::GetScsiIdMax();
const int lun_max = ControllerManager::GetScsiLunMax();
if (const auto& components = Split(id_spec, COMPONENT_SEPARATOR, 2); !components.empty()) {
if (components.size() == 1) {
if (!GetAsUnsignedInt(components[0], id) || id >= id_max) {
id = -1;
return "Invalid device ID (0-" + to_string(ControllerManager::GetScsiIdMax() - 1) + ")";
}
return "";
}
if (!GetAsUnsignedInt(components[0], id) || id >= id_max || !GetAsUnsignedInt(components[1], lun) || lun >= lun_max) {
id = -1;
lun = -1;
return "Invalid LUN (0-" + to_string(lun_max - 1) + ")";
}
}
return "";
}
string piscsi_util::Banner(string_view app)
{
ostringstream s;
s << "SCSI Target Emulator PiSCSI " << app << "\n";
s << "Version " << piscsi_get_version_string() << " (" << __DATE__ << ' ' << __TIME__ << ")\n";
s << "Powered by XM6 TypeG Technology / ";
s << "Copyright (C) 2016-2020 GIMONS\n";
s << "Copyright (C) 2020-2024 Contributors to the PiSCSI project\n";
return s.str();
}
string piscsi_util::GetExtensionLowerCase(string_view filename)
{
string ext;
ranges::transform(path(filename).extension().string(), back_inserter(ext), ::tolower);
// Remove the leading dot
return ext.empty() ? "" : ext.substr(1);
}
void piscsi_util::LogErrno(const string& msg)
{
spdlog::error(errno ? msg + ": " + string(strerror(errno)) : msg);
}
// Pin the thread to a specific CPU
// TODO Check whether just using a single CPU really makes sense
void piscsi_util::FixCpu(int cpu)
{
#ifdef __linux__
// Get the number of CPUs
cpu_set_t mask;
CPU_ZERO(&mask);
sched_getaffinity(0, sizeof(cpu_set_t), &mask);
const int cpus = CPU_COUNT(&mask);
// Set the thread affinity
if (cpu < cpus) {
CPU_ZERO(&mask);
CPU_SET(cpu, &mask);
sched_setaffinity(0, sizeof(cpu_set_t), &mask);
}
#endif
}