RASCSI/src/raspberrypi/devices/device_factory.cpp
Uwe Seimet 05db0e4688
Fix simple SonarCloud issues (#834)
* Fixing SonarCloud issues, first round

* Fixing SonarCLoud issues, next round

* Fixing SonarQube issues, next round

* Fixed warning

* Replaced empty constructors/destructors with = default;

* Fixed warning

* Replaced new

* Use constants instead of macros

* Use structured binding declarations

* Use init statements for if

* Use string views

* Use enum class, use using instead of typedef

* Fixed more SonarCloud warnings

* Replaced redundant/duplicate types with auto

* Devlared methods const

* Memory management update

* Fixed warning

* Added missing const

* Improved RaScsiResponse memory management

* Improved memory management

* Improved memory management

* Replaced macros by constants, removed unused constants

* Made member private

* Fixed warning

* Added comment

* Fixed shadowing warnings

* Cleanup

* Cleanup

* Cleanup

* Fixed shadowing warning

* Removed unused code

* Fixed more warnings

* Removed obsolete casts

* Fixed warnings

* Removed unused field

* Removed library not needed by rasctl

* Include cleanup

* Updated platform check for better compatibility

* Improved check for invalid option. This prevents rasctl to break on macos.

* Updated option check

* Fixed typo

* Added TODO

* Removed macro

* Scope update

* Replaced macro

* Added TODO, update memory management

* Fixed typo

* Replaced NULL by nullptr

* Use more structured bindings

* Added TODO

* Use calloc instead of mallco to not need memset

* Fixed warnings

* Fixed SonarQube initialization issues

* Fixed warning

* Cleaned up override/virtual/final

* Fixed warnings

* Constructor update

* Fixed tests

* Improved memory management

* Added missing const

* Added const

* Fixed two bugs reported by SonarCloud

* Fix SonarCloud hotspot

* Fixed memory management

* Memory management update

* Addressing hotspot by using strncpy

* Fixed SonarCloud issues

* Fixed SonarQube issues

* Added missing const

* Added const

* Added const

* Suppress false positive

* Added SonarQube suppressions for false positives

* Added suppresoin

* Fixed code smells

* Reverted changes that is a SonarQube issue, but caused problems with -O3

* Removed TODO based on review
2022-09-07 09:38:42 -05:00

304 lines
7.4 KiB
C++

//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI Reloaded
// for Raspberry Pi
//
// Copyright (C) 2021-2022 Uwe Seimet
//
//---------------------------------------------------------------------------
#include "scsihd.h"
#include "scsihd_nec.h"
#include "scsimo.h"
#include "scsicd.h"
#include "scsi_printer.h"
#include "scsi_host_bridge.h"
#include "scsi_daynaport.h"
#include "rascsi_exceptions.h"
#include "device_factory.h"
#include <ifaddrs.h>
#include "host_services.h"
using namespace std;
using namespace rascsi_interface;
multimap<int, Device *> DeviceFactory::devices;
DeviceFactory::DeviceFactory() : sector_sizes({}), geometries({}), default_params({}), extension_mapping({})
{
sector_sizes[SCHD] = { 512, 1024, 2048, 4096 };
sector_sizes[SCRM] = { 512, 1024, 2048, 4096 };
sector_sizes[SCMO] = { 512, 1024, 2048, 4096 };
sector_sizes[SCCD] = { 512, 2048};
// 128 MB, 512 bytes per sector, 248826 sectors
geometries[SCMO][0x797f400] = make_pair(512, 248826);
// 230 MB, 512 bytes per block, 446325 sectors
geometries[SCMO][0xd9eea00] = make_pair(512, 446325);
// 540 MB, 512 bytes per sector, 1041500 sectors
geometries[SCMO][0x1fc8b800] = make_pair(512, 1041500);
// 640 MB, 20248 bytes per sector, 310352 sectors
geometries[SCMO][0x25e28000] = make_pair(2048, 310352);
string network_interfaces;
for (const auto& network_interface : GetNetworkInterfaces()) {
if (!network_interfaces.empty()) {
network_interfaces += ",";
}
network_interfaces += network_interface;
}
default_params[SCBR]["interface"] = network_interfaces;
default_params[SCBR]["inet"] = "10.10.20.1/24";
default_params[SCDP]["interface"] = network_interfaces;
default_params[SCDP]["inet"] = "10.10.20.1/24";
default_params[SCLP]["cmd"] = "lp -oraw %f";
default_params[SCLP]["timeout"] = "30";
extension_mapping["hds"] = SCHD;
extension_mapping["hda"] = SCHD;
extension_mapping["hdn"] = SCHD;
extension_mapping["hdi"] = SCHD;
extension_mapping["nhd"] = SCHD;
extension_mapping["hdr"] = SCRM;
extension_mapping["mos"] = SCMO;
extension_mapping["iso"] = SCCD;
}
DeviceFactory::~DeviceFactory()
{
DeleteAllDevices();
}
DeviceFactory& DeviceFactory::instance()
{
static DeviceFactory instance;
return instance;
}
void DeviceFactory::DeleteDevice(const Device *device) const
{
auto iterpair = devices.equal_range(device->GetId());
for (auto it = iterpair.first; it != iterpair.second; ++it) {
if (it->second->GetLun() == device->GetLun()) {
devices.erase(it);
delete device;
break;
}
}
}
void DeviceFactory::DeleteAllDevices() const
{
for (const auto& [id, device] : devices) {
delete device;
}
devices.clear();
}
const Device * DeviceFactory::GetDeviceByIdAndLun(int i, int lun) const
{
for (const auto& [id, device] : devices) {
if (device->GetId() == i && device->GetLun() == lun) {
return device;
}
}
return nullptr;
}
list<Device *> DeviceFactory::GetAllDevices() const
{
list<Device *> result;
for (const auto& [id, device] : devices) {
result.push_back(device);
}
return result;
}
string DeviceFactory::GetExtension(const string& filename) const
{
string ext;
size_t separator = filename.rfind('.');
if (separator != string::npos) {
ext = filename.substr(separator + 1);
}
std::transform(ext.begin(), ext.end(), ext.begin(), [](unsigned char c){ return std::tolower(c); });
return ext;
}
PbDeviceType DeviceFactory::GetTypeForFile(const string& filename) const
{
const auto& it = extension_mapping.find(GetExtension(filename));
if (it != extension_mapping.end()) {
return it->second;
}
else if (filename == "bridge") {
return SCBR;
}
else if (filename == "daynaport") {
return SCDP;
}
else if (filename == "printer") {
return SCLP;
}
else if (filename == "services") {
return SCHS;
}
return UNDEFINED;
}
// ID -1 is used by rascsi to create a temporary device
Device *DeviceFactory::CreateDevice(PbDeviceType type, const string& filename, int id)
{
// If no type was specified try to derive the device type from the filename
if (type == UNDEFINED) {
type = GetTypeForFile(filename);
if (type == UNDEFINED) {
return nullptr;
}
}
unique_ptr<Device> device;
switch (type) {
case SCHD: {
if (string ext = GetExtension(filename); ext == "hdn" || ext == "hdi" || ext == "nhd") {
device = make_unique<SCSIHD_NEC>();
} else {
device = make_unique<SCSIHD>(sector_sizes[SCHD], false);
// Some Apple tools require a particular drive identification
if (ext == "hda") {
device->SetVendor("QUANTUM");
device->SetProduct("FIREBALL");
}
}
device->SetProtectable(true);
device->SetStoppable(true);
break;
}
case SCRM:
device = make_unique<SCSIHD>(sector_sizes[SCRM], true);
device->SetProtectable(true);
device->SetStoppable(true);
device->SetRemovable(true);
device->SetLockable(true);
device->SetProduct("SCSI HD (REM.)");
break;
case SCMO:
device = make_unique<SCSIMO>(sector_sizes[SCMO], geometries[SCMO]);
device->SetProtectable(true);
device->SetStoppable(true);
device->SetRemovable(true);
device->SetLockable(true);
device->SetProduct("SCSI MO");
break;
case SCCD:
device = make_unique<SCSICD>(sector_sizes[SCCD]);
device->SetReadOnly(true);
device->SetStoppable(true);
device->SetRemovable(true);
device->SetLockable(true);
device->SetProduct("SCSI CD-ROM");
break;
case SCBR:
device = make_unique<SCSIBR>();
device->SetProduct("SCSI HOST BRIDGE");
device->SupportsParams(true);
device->SetDefaultParams(default_params[SCBR]);
break;
case SCDP:
device = make_unique<SCSIDaynaPort>();
// Since this is an emulation for a specific device the full INQUIRY data have to be set accordingly
device->SetVendor("Dayna");
device->SetProduct("SCSI/Link");
device->SetRevision("1.4a");
device->SupportsParams(true);
device->SetDefaultParams(default_params[SCDP]);
break;
case SCHS:
device = make_unique<HostServices>();
// Since this is an emulation for a specific device the full INQUIRY data have to be set accordingly
device->SetVendor("RaSCSI");
device->SetProduct("Host Services");
break;
case SCLP:
device = make_unique<SCSIPrinter>();
device->SetProduct("SCSI PRINTER");
device->SupportsParams(true);
device->SetDefaultParams(default_params[SCLP]);
break;
default:
break;
}
assert(device != nullptr);
Device *d = device.release();
if (d != nullptr) {
d->SetId(id);
devices.emplace(id, d);
}
return d;
}
const unordered_set<uint32_t>& DeviceFactory::GetSectorSizes(const string& type) const
{
PbDeviceType t = UNDEFINED;
PbDeviceType_Parse(type, &t);
const auto it = sector_sizes.find(t);
assert (it != sector_sizes.end());
return it->second;
}
list<string> DeviceFactory::GetNetworkInterfaces() const
{
list<string> network_interfaces;
struct ifaddrs *addrs;
getifaddrs(&addrs);
struct ifaddrs *tmp = addrs;
while (tmp) {
if (tmp->ifa_addr && tmp->ifa_addr->sa_family == AF_PACKET &&
strcmp(tmp->ifa_name, "lo") && strcmp(tmp->ifa_name, "rascsi_bridge")) {
int fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
ifreq ifr = {};
strcpy(ifr.ifr_name, tmp->ifa_name);
// Only list interfaces that are up
if (!ioctl(fd, SIOCGIFFLAGS, &ifr) && ifr.ifr_flags & IFF_UP) {
network_interfaces.push_back(tmp->ifa_name);
}
close(fd);
}
tmp = tmp->ifa_next;
}
freeifaddrs(addrs);
return network_interfaces;
}