2021-09-24 06:48:48 +00:00
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
//
|
2022-08-26 01:01:39 +00:00
|
|
|
// SCSI Target Emulator RaSCSI Reloaded
|
2021-09-24 06:48:48 +00:00
|
|
|
// for Raspberry Pi
|
|
|
|
//
|
SASI code removal, error handling update, bug fixes, code cleanup (#806)
Summary ov most important changes triggered by the SASI code removal:
- Removed the SASI controller code
- New controller management. There is a new controller base class AbstractController and a class ControllerManager managing the controller lifecycle. The lifecycle management was removed from rasci.cpp and is covered by unit tests.
- New device management. The DeviceFactory manages the device lifecycle instead of rascsi.cpp. The new code is covered by unit tests.
- The lifecycle managment uses C++ collections with variable size instead of arrays with hard-coded sizes.
- The ScsiController method contains most of what was previously contained in scsidev_ctrl.cpp plus the code from sasidev_ctrl.cpp that was relevant for SCSI.
- scsi_command_util contains helper methods used for identical SCSI command implementations of more than one device
- Devices know their controllers, so that the controller instance does not need to be passed to each SCSI command. This change helps to decouple the devices from the controller. The phase_handler interface is also part of this decoupling.
- Use scsi_command_exception for propagating SCSI command execution errors, This resolves issues with the previous error handling, which was based on return values and often on magic numbers.
- Removed legacy SCSI error codes, all errors are now encoded by sense_key::, asc:: and status::.
- Fixed various warnings reported with -Wextra, -Weffc++ and -Wpedantic.
- Use constructor member initialization lists (recommended for ISO C++)
- Consistently use new/delete instead of malloc/free (recommended for ISO C++), resulting in better type safety and error handling
- Replaced variable sized arrays on the stack (violates ISO C++ and can cause a stack overflow)
- Replaced NULL by nullptr (recommended for C++), resulting in better type safety
- Use more const member functions in order to avoid side effects
- The format device page can now also be changed for hard disk drives (Fujitsu M2624S supports this, for instance), not just for MOs.
- Better encapsulation, updated access specifiers in many places
- Removed unused methods and method arguments
- Fixed a number of TODOs
- Added/updated unit tests for a lot of non-legacy classes
- Makefile support for creating HTML coverage reports with lcov/genhtml
2022-09-03 14:53:53 +00:00
|
|
|
// Copyright (C) 2021-2022 Uwe Seimet
|
2021-09-24 06:48:48 +00:00
|
|
|
//
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
2022-10-04 15:23:42 +00:00
|
|
|
#include "controllers/controller_manager.h"
|
2021-09-24 06:48:48 +00:00
|
|
|
#include "devices/disk.h"
|
|
|
|
#include "devices/device_factory.h"
|
2022-10-08 17:26:04 +00:00
|
|
|
#include "protobuf_util.h"
|
2021-09-24 06:48:48 +00:00
|
|
|
#include "rascsi_version.h"
|
|
|
|
#include "rascsi_interface.pb.h"
|
2021-10-17 06:51:14 +00:00
|
|
|
#include "rascsi_response.h"
|
2022-10-25 08:29:57 +00:00
|
|
|
#include <filesystem>
|
2021-09-24 06:48:48 +00:00
|
|
|
|
2022-10-25 08:29:57 +00:00
|
|
|
using namespace std;
|
|
|
|
using namespace filesystem;
|
2021-09-24 06:48:48 +00:00
|
|
|
using namespace rascsi_interface;
|
2022-10-08 17:26:04 +00:00
|
|
|
using namespace protobuf_util;
|
2021-09-24 06:48:48 +00:00
|
|
|
|
2022-10-04 15:23:42 +00:00
|
|
|
unique_ptr<PbDeviceProperties> RascsiResponse::GetDeviceProperties(const Device& device) const
|
2021-09-24 06:48:48 +00:00
|
|
|
{
|
2022-10-04 15:23:42 +00:00
|
|
|
auto properties = make_unique<PbDeviceProperties>();
|
2021-09-24 06:48:48 +00:00
|
|
|
|
2022-10-04 15:23:42 +00:00
|
|
|
properties->set_luns(max_luns);
|
|
|
|
properties->set_read_only(device.IsReadOnly());
|
|
|
|
properties->set_protectable(device.IsProtectable());
|
|
|
|
properties->set_stoppable(device.IsStoppable());
|
|
|
|
properties->set_removable(device.IsRemovable());
|
|
|
|
properties->set_lockable(device.IsLockable());
|
2022-10-08 17:26:04 +00:00
|
|
|
properties->set_supports_file(device.SupportsFile());
|
2022-10-04 15:23:42 +00:00
|
|
|
properties->set_supports_params(device.SupportsParams());
|
2021-09-24 06:48:48 +00:00
|
|
|
|
2022-10-04 15:23:42 +00:00
|
|
|
if (device.SupportsParams()) {
|
2022-10-23 19:51:39 +00:00
|
|
|
for (const auto& [key, value] : device_factory.GetDefaultParams(device.GetType())) {
|
2021-09-24 06:48:48 +00:00
|
|
|
auto& map = *properties->mutable_default_params();
|
2022-09-07 14:38:42 +00:00
|
|
|
map[key] = value;
|
2021-09-24 06:48:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-10-23 19:51:39 +00:00
|
|
|
for (const auto& block_size : device_factory.GetSectorSizes(device.GetType())) {
|
2021-09-24 06:48:48 +00:00
|
|
|
properties->add_block_sizes(block_size);
|
|
|
|
}
|
|
|
|
|
|
|
|
return properties;
|
|
|
|
}
|
|
|
|
|
2022-10-04 15:23:42 +00:00
|
|
|
void RascsiResponse::GetDeviceTypeProperties(PbDeviceTypesInfo& device_types_info, PbDeviceType type) const
|
2021-09-24 06:48:48 +00:00
|
|
|
{
|
2022-10-04 15:23:42 +00:00
|
|
|
auto type_properties = device_types_info.add_properties();
|
2021-09-24 06:48:48 +00:00
|
|
|
type_properties->set_type(type);
|
2022-10-08 17:26:04 +00:00
|
|
|
const auto device = device_factory.CreateDevice(controller_manager, type, 0, "");
|
2022-10-04 15:23:42 +00:00
|
|
|
type_properties->set_allocated_properties(GetDeviceProperties(*device).release());
|
|
|
|
} //NOSONAR The allocated memory is managed by protobuf
|
2021-09-24 06:48:48 +00:00
|
|
|
|
2022-10-04 15:23:42 +00:00
|
|
|
void RascsiResponse::GetAllDeviceTypeProperties(PbDeviceTypesInfo& device_types_info) const
|
2021-09-24 06:48:48 +00:00
|
|
|
{
|
SASI code removal, error handling update, bug fixes, code cleanup (#806)
Summary ov most important changes triggered by the SASI code removal:
- Removed the SASI controller code
- New controller management. There is a new controller base class AbstractController and a class ControllerManager managing the controller lifecycle. The lifecycle management was removed from rasci.cpp and is covered by unit tests.
- New device management. The DeviceFactory manages the device lifecycle instead of rascsi.cpp. The new code is covered by unit tests.
- The lifecycle managment uses C++ collections with variable size instead of arrays with hard-coded sizes.
- The ScsiController method contains most of what was previously contained in scsidev_ctrl.cpp plus the code from sasidev_ctrl.cpp that was relevant for SCSI.
- scsi_command_util contains helper methods used for identical SCSI command implementations of more than one device
- Devices know their controllers, so that the controller instance does not need to be passed to each SCSI command. This change helps to decouple the devices from the controller. The phase_handler interface is also part of this decoupling.
- Use scsi_command_exception for propagating SCSI command execution errors, This resolves issues with the previous error handling, which was based on return values and often on magic numbers.
- Removed legacy SCSI error codes, all errors are now encoded by sense_key::, asc:: and status::.
- Fixed various warnings reported with -Wextra, -Weffc++ and -Wpedantic.
- Use constructor member initialization lists (recommended for ISO C++)
- Consistently use new/delete instead of malloc/free (recommended for ISO C++), resulting in better type safety and error handling
- Replaced variable sized arrays on the stack (violates ISO C++ and can cause a stack overflow)
- Replaced NULL by nullptr (recommended for C++), resulting in better type safety
- Use more const member functions in order to avoid side effects
- The format device page can now also be changed for hard disk drives (Fujitsu M2624S supports this, for instance), not just for MOs.
- Better encapsulation, updated access specifiers in many places
- Removed unused methods and method arguments
- Fixed a number of TODOs
- Added/updated unit tests for a lot of non-legacy classes
- Makefile support for creating HTML coverage reports with lcov/genhtml
2022-09-03 14:53:53 +00:00
|
|
|
// Start with 2 instead of 1. 1 was the removed SASI drive type.
|
|
|
|
int ordinal = 2;
|
2022-02-10 18:54:48 +00:00
|
|
|
while (PbDeviceType_IsValid(ordinal)) {
|
|
|
|
PbDeviceType type = UNDEFINED;
|
2022-02-13 19:30:02 +00:00
|
|
|
PbDeviceType_Parse(PbDeviceType_Name((PbDeviceType)ordinal), &type);
|
2022-02-10 18:54:48 +00:00
|
|
|
GetDeviceTypeProperties(device_types_info, type);
|
|
|
|
ordinal++;
|
|
|
|
}
|
2021-09-24 06:48:48 +00:00
|
|
|
}
|
|
|
|
|
2022-10-04 15:23:42 +00:00
|
|
|
void RascsiResponse::GetDevice(const Device& device, PbDevice& pb_device, const string& default_folder) const
|
2021-09-24 06:48:48 +00:00
|
|
|
{
|
2022-10-04 15:23:42 +00:00
|
|
|
pb_device.set_id(device.GetId());
|
|
|
|
pb_device.set_unit(device.GetLun());
|
|
|
|
pb_device.set_vendor(device.GetVendor());
|
|
|
|
pb_device.set_product(device.GetProduct());
|
|
|
|
pb_device.set_revision(device.GetRevision());
|
2022-10-23 19:51:39 +00:00
|
|
|
pb_device.set_type(device.GetType());
|
2021-09-24 06:48:48 +00:00
|
|
|
|
2022-10-04 15:23:42 +00:00
|
|
|
pb_device.set_allocated_properties(GetDeviceProperties(device).release());
|
2021-09-24 06:48:48 +00:00
|
|
|
|
2022-10-06 14:15:19 +00:00
|
|
|
auto status = make_unique<PbDeviceStatus>().release(); //NOSONAR The allocated memory is managed by protobuf
|
2022-10-04 15:23:42 +00:00
|
|
|
pb_device.set_allocated_status(status);
|
|
|
|
status->set_protected_(device.IsProtected());
|
|
|
|
status->set_stopped(device.IsStopped());
|
|
|
|
status->set_removed(device.IsRemoved());
|
|
|
|
status->set_locked(device.IsLocked());
|
|
|
|
|
|
|
|
if (device.SupportsParams()) { //NOSONAR The allocated memory is managed by protobuf
|
|
|
|
for (const auto& [key, value] : device.GetParams()) {
|
2022-10-23 19:51:39 +00:00
|
|
|
SetParam(pb_device, key, value);
|
2021-09-24 06:48:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-10-04 15:23:42 +00:00
|
|
|
if (const auto disk = dynamic_cast<const Disk*>(&device); disk) {
|
|
|
|
pb_device.set_block_size(device.IsRemoved()? 0 : disk->GetSectorSizeInBytes());
|
|
|
|
pb_device.set_block_count(device.IsRemoved() ? 0: disk->GetBlockCount());
|
2021-09-24 06:48:48 +00:00
|
|
|
}
|
|
|
|
|
2022-10-23 19:51:39 +00:00
|
|
|
const auto storage_device = dynamic_cast<const StorageDevice *>(&device);
|
|
|
|
if (storage_device != nullptr) {
|
2022-09-07 14:38:42 +00:00
|
|
|
auto image_file = make_unique<PbImageFile>().release();
|
2022-10-23 19:51:39 +00:00
|
|
|
GetImageFile(*image_file, default_folder, device.IsReady() ? storage_device->GetFilename() : "");
|
2022-10-04 15:23:42 +00:00
|
|
|
pb_device.set_allocated_file(image_file);
|
2021-09-24 06:48:48 +00:00
|
|
|
}
|
2022-10-01 15:56:06 +00:00
|
|
|
} //NOSONAR The allocated memory is managed by protobuf
|
2021-09-24 06:48:48 +00:00
|
|
|
|
2022-10-04 15:23:42 +00:00
|
|
|
bool RascsiResponse::GetImageFile(PbImageFile& image_file, const string& default_folder, const string& filename) const
|
2021-09-24 06:48:48 +00:00
|
|
|
{
|
|
|
|
if (!filename.empty()) {
|
2022-10-04 15:23:42 +00:00
|
|
|
image_file.set_name(filename);
|
|
|
|
image_file.set_type(device_factory.GetTypeForFile(filename));
|
2021-09-24 06:48:48 +00:00
|
|
|
|
2022-10-08 17:26:04 +00:00
|
|
|
const string f = filename[0] == '/' ? filename : default_folder + "/" + filename;
|
2021-09-24 06:48:48 +00:00
|
|
|
|
2022-10-04 15:23:42 +00:00
|
|
|
image_file.set_read_only(access(f.c_str(), W_OK));
|
2021-09-24 06:48:48 +00:00
|
|
|
|
2022-10-25 08:29:57 +00:00
|
|
|
// filesystem::file_size cannot be used here because gcc < 10.3.0 cannot handle files of more than 2 GiB
|
2022-10-01 15:56:06 +00:00
|
|
|
if (struct stat st; !stat(f.c_str(), &st) && !S_ISDIR(st.st_mode)) {
|
2022-10-04 15:23:42 +00:00
|
|
|
image_file.set_size(st.st_size);
|
2021-09-30 17:22:57 +00:00
|
|
|
return true;
|
2021-09-24 06:48:48 +00:00
|
|
|
}
|
|
|
|
}
|
2021-09-30 17:22:57 +00:00
|
|
|
|
|
|
|
return false;
|
2021-09-24 06:48:48 +00:00
|
|
|
}
|
|
|
|
|
2022-10-04 15:23:42 +00:00
|
|
|
void RascsiResponse::GetAvailableImages(PbImageFilesInfo& image_files_info, const string& default_folder,
|
|
|
|
const string& folder, const string& folder_pattern, const string& file_pattern, int scan_depth) const
|
|
|
|
{
|
2022-10-01 15:56:06 +00:00
|
|
|
if (scan_depth-- < 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-12-22 08:25:05 +00:00
|
|
|
string folder_pattern_lower = folder_pattern;
|
|
|
|
transform(folder_pattern_lower.begin(), folder_pattern_lower.end(), folder_pattern_lower.begin(), ::tolower);
|
|
|
|
|
|
|
|
string file_pattern_lower = file_pattern;
|
|
|
|
transform(file_pattern_lower.begin(), file_pattern_lower.end(), file_pattern_lower.begin(), ::tolower);
|
2021-12-19 10:49:17 +00:00
|
|
|
|
2022-10-01 15:56:06 +00:00
|
|
|
DIR *d = opendir(folder.c_str());
|
|
|
|
if (d == nullptr) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-10-25 08:29:57 +00:00
|
|
|
// C++ filesystem cannot be used here because gcc < 10.3.0 cannot handle files of more than 2 GiB
|
2022-10-01 15:56:06 +00:00
|
|
|
const dirent *dir;
|
|
|
|
while ((dir = readdir(d))) {
|
2022-10-04 15:23:42 +00:00
|
|
|
string filename = GetNextImageFile(dir, folder);
|
|
|
|
if (filename.empty()) {
|
|
|
|
continue;
|
|
|
|
}
|
2022-10-01 15:56:06 +00:00
|
|
|
|
2022-10-04 15:23:42 +00:00
|
|
|
string name_lower = dir->d_name;
|
|
|
|
if (!file_pattern.empty()) {
|
|
|
|
transform(name_lower.begin(), name_lower.end(), name_lower.begin(), ::tolower);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dir->d_type == DT_DIR) {
|
|
|
|
if (folder_pattern_lower.empty() || name_lower.find(folder_pattern_lower) != string::npos) {
|
|
|
|
GetAvailableImages(image_files_info, default_folder, filename, folder_pattern,
|
|
|
|
file_pattern, scan_depth);
|
2021-09-24 06:48:48 +00:00
|
|
|
}
|
|
|
|
|
2022-10-04 15:23:42 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (file_pattern_lower.empty() || name_lower.find(file_pattern_lower) != string::npos) {
|
|
|
|
if (auto image_file = make_unique<PbImageFile>(); GetImageFile(*image_file.get(), default_folder, filename)) {
|
|
|
|
GetImageFile(*image_files_info.add_image_files(), default_folder,
|
|
|
|
filename.substr(default_folder.length() + 1));
|
2022-10-01 15:56:06 +00:00
|
|
|
}
|
2021-12-19 10:49:17 +00:00
|
|
|
}
|
2021-09-24 06:48:48 +00:00
|
|
|
}
|
2022-10-01 15:56:06 +00:00
|
|
|
|
|
|
|
closedir(d);
|
2021-12-19 10:49:17 +00:00
|
|
|
}
|
|
|
|
|
2022-10-04 15:23:42 +00:00
|
|
|
unique_ptr<PbImageFilesInfo> RascsiResponse::GetAvailableImages(PbResult& result, const string& default_folder,
|
|
|
|
const string& folder_pattern, const string& file_pattern, int scan_depth) const
|
2021-12-19 10:49:17 +00:00
|
|
|
{
|
2022-10-04 15:23:42 +00:00
|
|
|
auto image_files_info = make_unique<PbImageFilesInfo>();
|
2021-12-19 10:49:17 +00:00
|
|
|
|
2022-10-04 15:23:42 +00:00
|
|
|
image_files_info->set_default_image_folder(default_folder);
|
2021-12-22 08:25:05 +00:00
|
|
|
image_files_info->set_depth(scan_depth);
|
2021-12-19 10:49:17 +00:00
|
|
|
|
2022-10-04 15:23:42 +00:00
|
|
|
GetAvailableImages(*image_files_info, default_folder, default_folder, folder_pattern,
|
2021-12-22 08:25:05 +00:00
|
|
|
file_pattern, scan_depth);
|
2021-09-24 06:48:48 +00:00
|
|
|
|
|
|
|
result.set_status(true);
|
|
|
|
|
|
|
|
return image_files_info;
|
|
|
|
}
|
|
|
|
|
2022-10-04 15:23:42 +00:00
|
|
|
void RascsiResponse::GetAvailableImages(PbResult& result, PbServerInfo& server_info, const string& default_folder,
|
|
|
|
const string& folder_pattern, const string& file_pattern, int scan_depth) const
|
2021-09-24 06:48:48 +00:00
|
|
|
{
|
2022-10-04 15:23:42 +00:00
|
|
|
auto image_files_info = GetAvailableImages(result, default_folder, folder_pattern, file_pattern, scan_depth);
|
|
|
|
image_files_info->set_default_image_folder(default_folder);
|
|
|
|
server_info.set_allocated_image_files_info(image_files_info.release());
|
2021-09-24 06:48:48 +00:00
|
|
|
|
2022-10-01 15:56:06 +00:00
|
|
|
result.set_status(true); //NOSONAR The allocated memory is managed by protobuf
|
2021-09-24 06:48:48 +00:00
|
|
|
}
|
|
|
|
|
2022-10-04 15:23:42 +00:00
|
|
|
unique_ptr<PbReservedIdsInfo> RascsiResponse::GetReservedIds(PbResult& result, const unordered_set<int>& ids) const
|
2021-10-06 21:25:43 +00:00
|
|
|
{
|
2022-10-04 15:23:42 +00:00
|
|
|
auto reserved_ids_info = make_unique<PbReservedIdsInfo>();
|
2022-10-08 17:26:04 +00:00
|
|
|
for (const int id : ids) {
|
2021-10-06 21:25:43 +00:00
|
|
|
reserved_ids_info->add_ids(id);
|
|
|
|
}
|
|
|
|
|
|
|
|
result.set_status(true);
|
|
|
|
|
|
|
|
return reserved_ids_info;
|
|
|
|
}
|
|
|
|
|
2022-10-04 15:23:42 +00:00
|
|
|
void RascsiResponse::GetDevices(PbServerInfo& server_info, const string& default_folder) const
|
2021-09-24 06:48:48 +00:00
|
|
|
{
|
2022-10-04 15:23:42 +00:00
|
|
|
for (const auto& device : controller_manager.GetAllDevices()) {
|
SASI code removal, error handling update, bug fixes, code cleanup (#806)
Summary ov most important changes triggered by the SASI code removal:
- Removed the SASI controller code
- New controller management. There is a new controller base class AbstractController and a class ControllerManager managing the controller lifecycle. The lifecycle management was removed from rasci.cpp and is covered by unit tests.
- New device management. The DeviceFactory manages the device lifecycle instead of rascsi.cpp. The new code is covered by unit tests.
- The lifecycle managment uses C++ collections with variable size instead of arrays with hard-coded sizes.
- The ScsiController method contains most of what was previously contained in scsidev_ctrl.cpp plus the code from sasidev_ctrl.cpp that was relevant for SCSI.
- scsi_command_util contains helper methods used for identical SCSI command implementations of more than one device
- Devices know their controllers, so that the controller instance does not need to be passed to each SCSI command. This change helps to decouple the devices from the controller. The phase_handler interface is also part of this decoupling.
- Use scsi_command_exception for propagating SCSI command execution errors, This resolves issues with the previous error handling, which was based on return values and often on magic numbers.
- Removed legacy SCSI error codes, all errors are now encoded by sense_key::, asc:: and status::.
- Fixed various warnings reported with -Wextra, -Weffc++ and -Wpedantic.
- Use constructor member initialization lists (recommended for ISO C++)
- Consistently use new/delete instead of malloc/free (recommended for ISO C++), resulting in better type safety and error handling
- Replaced variable sized arrays on the stack (violates ISO C++ and can cause a stack overflow)
- Replaced NULL by nullptr (recommended for C++), resulting in better type safety
- Use more const member functions in order to avoid side effects
- The format device page can now also be changed for hard disk drives (Fujitsu M2624S supports this, for instance), not just for MOs.
- Better encapsulation, updated access specifiers in many places
- Removed unused methods and method arguments
- Fixed a number of TODOs
- Added/updated unit tests for a lot of non-legacy classes
- Makefile support for creating HTML coverage reports with lcov/genhtml
2022-09-03 14:53:53 +00:00
|
|
|
PbDevice *pb_device = server_info.mutable_devices_info()->add_devices();
|
2022-10-04 15:23:42 +00:00
|
|
|
GetDevice(*device, *pb_device, default_folder);
|
2021-09-24 06:48:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-10-04 15:23:42 +00:00
|
|
|
void RascsiResponse::GetDevicesInfo(PbResult& result, const PbCommand& command, const string& default_folder) const
|
2021-09-24 06:48:48 +00:00
|
|
|
{
|
|
|
|
set<id_set> id_sets;
|
2022-10-04 15:23:42 +00:00
|
|
|
|
|
|
|
const auto& devices = controller_manager.GetAllDevices();
|
|
|
|
|
|
|
|
// If no device list was provided in the command get information on all devices
|
2021-09-24 06:48:48 +00:00
|
|
|
if (!command.devices_size()) {
|
2022-10-04 15:23:42 +00:00
|
|
|
for (const auto& device : devices) {
|
SASI code removal, error handling update, bug fixes, code cleanup (#806)
Summary ov most important changes triggered by the SASI code removal:
- Removed the SASI controller code
- New controller management. There is a new controller base class AbstractController and a class ControllerManager managing the controller lifecycle. The lifecycle management was removed from rasci.cpp and is covered by unit tests.
- New device management. The DeviceFactory manages the device lifecycle instead of rascsi.cpp. The new code is covered by unit tests.
- The lifecycle managment uses C++ collections with variable size instead of arrays with hard-coded sizes.
- The ScsiController method contains most of what was previously contained in scsidev_ctrl.cpp plus the code from sasidev_ctrl.cpp that was relevant for SCSI.
- scsi_command_util contains helper methods used for identical SCSI command implementations of more than one device
- Devices know their controllers, so that the controller instance does not need to be passed to each SCSI command. This change helps to decouple the devices from the controller. The phase_handler interface is also part of this decoupling.
- Use scsi_command_exception for propagating SCSI command execution errors, This resolves issues with the previous error handling, which was based on return values and often on magic numbers.
- Removed legacy SCSI error codes, all errors are now encoded by sense_key::, asc:: and status::.
- Fixed various warnings reported with -Wextra, -Weffc++ and -Wpedantic.
- Use constructor member initialization lists (recommended for ISO C++)
- Consistently use new/delete instead of malloc/free (recommended for ISO C++), resulting in better type safety and error handling
- Replaced variable sized arrays on the stack (violates ISO C++ and can cause a stack overflow)
- Replaced NULL by nullptr (recommended for C++), resulting in better type safety
- Use more const member functions in order to avoid side effects
- The format device page can now also be changed for hard disk drives (Fujitsu M2624S supports this, for instance), not just for MOs.
- Better encapsulation, updated access specifiers in many places
- Removed unused methods and method arguments
- Fixed a number of TODOs
- Added/updated unit tests for a lot of non-legacy classes
- Makefile support for creating HTML coverage reports with lcov/genhtml
2022-09-03 14:53:53 +00:00
|
|
|
id_sets.insert(make_pair(device->GetId(), device->GetLun()));
|
2021-09-24 06:48:48 +00:00
|
|
|
}
|
|
|
|
}
|
2022-10-04 15:23:42 +00:00
|
|
|
// Otherwise get information on the devices provided in the command
|
2021-09-24 06:48:48 +00:00
|
|
|
else {
|
2022-10-04 15:23:42 +00:00
|
|
|
id_sets = MatchDevices(result, command);
|
|
|
|
if (id_sets.empty()) {
|
|
|
|
return;
|
2021-09-24 06:48:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-10-04 15:23:42 +00:00
|
|
|
auto devices_info = make_unique<PbDevicesInfo>();
|
2021-09-24 06:48:48 +00:00
|
|
|
|
2022-09-10 05:59:41 +00:00
|
|
|
for (const auto& [id, lun] : id_sets) {
|
2022-10-04 15:23:42 +00:00
|
|
|
for (const auto& d : devices) {
|
|
|
|
if (d->GetId() == id && d->GetLun() == lun) {
|
|
|
|
GetDevice(*d, *devices_info->add_devices(), default_folder);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2021-09-24 06:48:48 +00:00
|
|
|
}
|
|
|
|
|
2022-10-04 15:23:42 +00:00
|
|
|
result.set_allocated_devices_info(devices_info.release());
|
2021-09-24 06:48:48 +00:00
|
|
|
result.set_status(true);
|
|
|
|
}
|
|
|
|
|
2022-10-04 15:23:42 +00:00
|
|
|
unique_ptr<PbDeviceTypesInfo> RascsiResponse::GetDeviceTypesInfo(PbResult& result) const
|
2021-09-24 06:48:48 +00:00
|
|
|
{
|
2022-10-04 15:23:42 +00:00
|
|
|
auto device_types_info = make_unique<PbDeviceTypesInfo>();
|
2021-09-24 06:48:48 +00:00
|
|
|
|
|
|
|
GetAllDeviceTypeProperties(*device_types_info);
|
|
|
|
|
|
|
|
result.set_status(true);
|
|
|
|
|
|
|
|
return device_types_info;
|
|
|
|
}
|
|
|
|
|
2022-10-04 15:23:42 +00:00
|
|
|
unique_ptr<PbServerInfo> RascsiResponse::GetServerInfo(PbResult& result, const unordered_set<int>& reserved_ids,
|
|
|
|
const string& current_log_level, const string& default_folder, const string& folder_pattern,
|
|
|
|
const string& file_pattern, int scan_depth) const
|
2021-09-24 06:48:48 +00:00
|
|
|
{
|
2022-10-04 15:23:42 +00:00
|
|
|
auto server_info = make_unique<PbServerInfo>();
|
2021-09-24 06:48:48 +00:00
|
|
|
|
2022-10-04 15:23:42 +00:00
|
|
|
server_info->set_allocated_version_info(GetVersionInfo(result).release());
|
|
|
|
server_info->set_allocated_log_level_info(GetLogLevelInfo(result, current_log_level).release()); //NOSONAR The allocated memory is managed by protobuf
|
2022-10-01 15:56:06 +00:00
|
|
|
GetAllDeviceTypeProperties(*server_info->mutable_device_types_info()); //NOSONAR The allocated memory is managed by protobuf
|
2022-10-04 15:23:42 +00:00
|
|
|
GetAvailableImages(result, *server_info, default_folder, folder_pattern, file_pattern, scan_depth);
|
|
|
|
server_info->set_allocated_network_interfaces_info(GetNetworkInterfacesInfo(result).release());
|
|
|
|
server_info->set_allocated_mapping_info(GetMappingInfo(result).release()); //NOSONAR The allocated memory is managed by protobuf
|
|
|
|
GetDevices(*server_info, default_folder); //NOSONAR The allocated memory is managed by protobuf
|
|
|
|
server_info->set_allocated_reserved_ids_info(GetReservedIds(result, reserved_ids).release());
|
|
|
|
server_info->set_allocated_operation_info(GetOperationInfo(result, scan_depth).release()); //NOSONAR The allocated memory is managed by protobuf
|
2021-09-24 06:48:48 +00:00
|
|
|
|
|
|
|
result.set_status(true);
|
|
|
|
|
|
|
|
return server_info;
|
|
|
|
}
|
|
|
|
|
2022-10-04 15:23:42 +00:00
|
|
|
unique_ptr<PbVersionInfo> RascsiResponse::GetVersionInfo(PbResult& result) const
|
2021-10-06 21:25:43 +00:00
|
|
|
{
|
2022-10-04 15:23:42 +00:00
|
|
|
auto version_info = make_unique<PbVersionInfo>();
|
2021-10-06 21:25:43 +00:00
|
|
|
|
|
|
|
version_info->set_major_version(rascsi_major_version);
|
|
|
|
version_info->set_minor_version(rascsi_minor_version);
|
|
|
|
version_info->set_patch_version(rascsi_patch_version);
|
|
|
|
|
|
|
|
result.set_status(true);
|
|
|
|
|
|
|
|
return version_info;
|
|
|
|
}
|
|
|
|
|
2022-10-04 15:23:42 +00:00
|
|
|
unique_ptr<PbLogLevelInfo> RascsiResponse::GetLogLevelInfo(PbResult& result, const string& current_log_level) const
|
2021-09-24 06:48:48 +00:00
|
|
|
{
|
2022-10-04 15:23:42 +00:00
|
|
|
auto log_level_info = make_unique<PbLogLevelInfo>();
|
2021-10-06 21:25:43 +00:00
|
|
|
|
2021-09-24 06:48:48 +00:00
|
|
|
for (const auto& log_level : log_levels) {
|
2021-10-06 21:25:43 +00:00
|
|
|
log_level_info->add_log_levels(log_level);
|
2021-09-24 06:48:48 +00:00
|
|
|
}
|
2021-10-06 21:25:43 +00:00
|
|
|
|
|
|
|
log_level_info->set_current_log_level(current_log_level);
|
|
|
|
|
|
|
|
result.set_status(true);
|
|
|
|
|
|
|
|
return log_level_info;
|
2021-09-24 06:48:48 +00:00
|
|
|
}
|
|
|
|
|
2022-10-04 15:23:42 +00:00
|
|
|
unique_ptr<PbNetworkInterfacesInfo> RascsiResponse::GetNetworkInterfacesInfo(PbResult& result) const
|
2021-09-24 06:48:48 +00:00
|
|
|
{
|
2022-10-04 15:23:42 +00:00
|
|
|
auto network_interfaces_info = make_unique<PbNetworkInterfacesInfo>();
|
2021-09-24 06:48:48 +00:00
|
|
|
|
2022-10-04 15:23:42 +00:00
|
|
|
for (const auto& network_interface : device_factory.GetNetworkInterfaces()) {
|
2021-09-24 06:48:48 +00:00
|
|
|
network_interfaces_info->add_name(network_interface);
|
|
|
|
}
|
|
|
|
|
|
|
|
result.set_status(true);
|
|
|
|
|
|
|
|
return network_interfaces_info;
|
|
|
|
}
|
2021-09-27 23:39:50 +00:00
|
|
|
|
2022-10-04 15:23:42 +00:00
|
|
|
unique_ptr<PbMappingInfo> RascsiResponse::GetMappingInfo(PbResult& result) const
|
2021-09-27 23:39:50 +00:00
|
|
|
{
|
2022-10-04 15:23:42 +00:00
|
|
|
auto mapping_info = make_unique<PbMappingInfo>();
|
2021-09-27 23:39:50 +00:00
|
|
|
|
2022-10-04 15:23:42 +00:00
|
|
|
for (const auto& [name, type] : device_factory.GetExtensionMapping()) {
|
2022-09-10 05:59:41 +00:00
|
|
|
(*mapping_info->mutable_mapping())[name] = type;
|
2021-09-27 23:39:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
result.set_status(true);
|
|
|
|
|
|
|
|
return mapping_info;
|
|
|
|
}
|
2021-12-21 07:43:21 +00:00
|
|
|
|
2022-10-04 15:23:42 +00:00
|
|
|
unique_ptr<PbOperationInfo> RascsiResponse::GetOperationInfo(PbResult& result, int depth) const
|
2021-12-21 07:43:21 +00:00
|
|
|
{
|
2022-09-21 06:27:51 +00:00
|
|
|
auto operation_info = make_unique<PbOperationInfo>();
|
2021-12-21 07:43:21 +00:00
|
|
|
|
2022-09-21 06:27:51 +00:00
|
|
|
auto operation = CreateOperation(*operation_info, ATTACH, "Attach device, device-specific parameters are required");
|
2022-10-04 15:23:42 +00:00
|
|
|
AddOperationParameter(*operation, "name", "Image file name in case of a mass storage device").release();
|
|
|
|
AddOperationParameter(*operation, "interface", "Comma-separated prioritized network interface list").release();
|
|
|
|
AddOperationParameter(*operation, "inet", "IP address and netmask of the network bridge").release();
|
|
|
|
AddOperationParameter(*operation, "cmd", "Print command for the printer device").release();
|
|
|
|
operation.release();
|
2021-12-21 07:43:21 +00:00
|
|
|
|
2022-10-04 15:23:42 +00:00
|
|
|
CreateOperation(*operation_info, DETACH, "Detach device, device-specific parameters are required").release();
|
2021-12-21 07:43:21 +00:00
|
|
|
|
2022-10-04 15:23:42 +00:00
|
|
|
CreateOperation(*operation_info, DETACH_ALL, "Detach all devices").release();
|
2021-12-21 07:43:21 +00:00
|
|
|
|
2022-10-04 15:23:42 +00:00
|
|
|
CreateOperation(*operation_info, START, "Start device, device-specific parameters are required").release();
|
2021-12-21 07:43:21 +00:00
|
|
|
|
2022-10-04 15:23:42 +00:00
|
|
|
CreateOperation(*operation_info, STOP, "Stop device, device-specific parameters are required").release();
|
2021-12-21 07:43:21 +00:00
|
|
|
|
2022-09-21 06:27:51 +00:00
|
|
|
operation = CreateOperation(*operation_info, INSERT, "Insert medium, device-specific parameters are required");
|
2022-10-04 15:23:42 +00:00
|
|
|
AddOperationParameter(*operation, "file", "Image file name", "", true).release();
|
|
|
|
operation.release();
|
2021-12-21 07:43:21 +00:00
|
|
|
|
2022-10-04 15:23:42 +00:00
|
|
|
CreateOperation(*operation_info, EJECT, "Eject medium, device-specific parameters are required").release();
|
2021-12-21 07:43:21 +00:00
|
|
|
|
2022-10-04 15:23:42 +00:00
|
|
|
CreateOperation(*operation_info, PROTECT, "Protect medium, device-specific parameters are required").release();
|
2021-12-21 07:43:21 +00:00
|
|
|
|
2022-10-04 15:23:42 +00:00
|
|
|
CreateOperation(*operation_info, UNPROTECT, "Unprotect medium, device-specific parameters are required").release();
|
2021-12-21 07:43:21 +00:00
|
|
|
|
2022-09-21 06:27:51 +00:00
|
|
|
operation = CreateOperation(*operation_info, SERVER_INFO, "Get rascsi server information");
|
2021-12-22 08:25:05 +00:00
|
|
|
if (depth) {
|
2022-10-04 15:23:42 +00:00
|
|
|
AddOperationParameter(*operation, "folder_pattern", "Pattern for filtering image folder names").release();
|
2021-12-22 08:25:05 +00:00
|
|
|
}
|
2022-10-04 15:23:42 +00:00
|
|
|
AddOperationParameter(*operation, "file_pattern", "Pattern for filtering image file names").release();
|
|
|
|
operation.release();
|
2021-12-21 07:43:21 +00:00
|
|
|
|
2022-10-04 15:23:42 +00:00
|
|
|
CreateOperation(*operation_info, VERSION_INFO, "Get rascsi server version").release();
|
2021-12-21 07:43:21 +00:00
|
|
|
|
2022-10-04 15:23:42 +00:00
|
|
|
CreateOperation(*operation_info, DEVICES_INFO, "Get information on attached devices").release();
|
2021-12-21 07:43:21 +00:00
|
|
|
|
2022-10-04 15:23:42 +00:00
|
|
|
CreateOperation(*operation_info, DEVICE_TYPES_INFO, "Get device properties by device type").release();
|
2021-12-21 07:43:21 +00:00
|
|
|
|
2022-09-21 06:27:51 +00:00
|
|
|
operation = CreateOperation(*operation_info, DEFAULT_IMAGE_FILES_INFO, "Get information on available image files");
|
2021-12-22 08:25:05 +00:00
|
|
|
if (depth) {
|
2022-10-04 15:23:42 +00:00
|
|
|
AddOperationParameter(*operation, "folder_pattern", "Pattern for filtering image folder names").release();
|
2021-12-22 08:25:05 +00:00
|
|
|
}
|
2022-10-04 15:23:42 +00:00
|
|
|
AddOperationParameter(*operation, "file_pattern", "Pattern for filtering image file names").release();
|
|
|
|
operation.release();
|
2021-12-21 07:43:21 +00:00
|
|
|
|
2022-09-21 06:27:51 +00:00
|
|
|
operation = CreateOperation(*operation_info, IMAGE_FILE_INFO, "Get information on image file");
|
2022-10-04 15:23:42 +00:00
|
|
|
AddOperationParameter(*operation, "file", "Image file name", "", true).release();
|
|
|
|
operation.release();
|
2021-12-21 07:43:21 +00:00
|
|
|
|
2022-10-04 15:23:42 +00:00
|
|
|
CreateOperation(*operation_info, LOG_LEVEL_INFO, "Get log level information").release();
|
2021-12-21 07:43:21 +00:00
|
|
|
|
2022-10-04 15:23:42 +00:00
|
|
|
CreateOperation(*operation_info, NETWORK_INTERFACES_INFO, "Get the available network interfaces").release();
|
2021-12-21 07:43:21 +00:00
|
|
|
|
2022-10-04 15:23:42 +00:00
|
|
|
CreateOperation(*operation_info, MAPPING_INFO, "Get mapping of extensions to device types").release();
|
2021-12-21 07:43:21 +00:00
|
|
|
|
2022-10-04 15:23:42 +00:00
|
|
|
CreateOperation(*operation_info, RESERVED_IDS_INFO, "Get list of reserved device IDs").release();
|
2021-12-21 07:43:21 +00:00
|
|
|
|
2022-09-21 06:27:51 +00:00
|
|
|
operation = CreateOperation(*operation_info, DEFAULT_FOLDER, "Set default image file folder");
|
2022-10-04 15:23:42 +00:00
|
|
|
AddOperationParameter(*operation, "folder", "Default image file folder name", "", true).release();
|
|
|
|
operation.release();
|
2021-12-21 07:43:21 +00:00
|
|
|
|
2022-09-21 06:27:51 +00:00
|
|
|
operation = CreateOperation(*operation_info, LOG_LEVEL, "Set log level");
|
2022-10-04 15:23:42 +00:00
|
|
|
AddOperationParameter(*operation, "level", "New log level", "", true).release();
|
|
|
|
operation.release();
|
2021-12-21 07:43:21 +00:00
|
|
|
|
2022-09-21 06:27:51 +00:00
|
|
|
operation = CreateOperation(*operation_info, RESERVE_IDS, "Reserve device IDs");
|
2022-10-04 15:23:42 +00:00
|
|
|
AddOperationParameter(*operation, "ids", "Comma-separated device ID list", "", true).release();
|
|
|
|
operation.release();
|
2021-12-21 07:43:21 +00:00
|
|
|
|
2022-09-21 06:27:51 +00:00
|
|
|
operation = CreateOperation(*operation_info, SHUT_DOWN, "Shut down or reboot");
|
2022-10-04 15:23:42 +00:00
|
|
|
auto parameter = AddOperationParameter(*operation, "mode", "Shutdown mode", "", true).release();
|
2021-12-21 07:43:21 +00:00
|
|
|
parameter->add_permitted_values("rascsi");
|
|
|
|
// System shutdown/reboot requires root permissions
|
|
|
|
if (!getuid()) {
|
|
|
|
parameter->add_permitted_values("system");
|
|
|
|
parameter->add_permitted_values("reboot");
|
|
|
|
}
|
2022-10-04 15:23:42 +00:00
|
|
|
operation.release();
|
2021-12-21 07:43:21 +00:00
|
|
|
|
2022-09-21 06:27:51 +00:00
|
|
|
operation = CreateOperation(*operation_info, CREATE_IMAGE, "Create an image file");
|
2022-10-04 15:23:42 +00:00
|
|
|
AddOperationParameter(*operation, "file", "Image file name", "", true).release();
|
|
|
|
AddOperationParameter(*operation, "size", "Image file size in bytes", "", true).release();
|
|
|
|
parameter = AddOperationParameter(*operation, "read_only", "Read-only flag", "false").release();
|
2021-12-21 07:43:21 +00:00
|
|
|
parameter->add_permitted_values("true");
|
|
|
|
parameter->add_permitted_values("false");
|
2022-10-04 15:23:42 +00:00
|
|
|
operation.release();
|
2021-12-21 07:43:21 +00:00
|
|
|
|
2022-09-21 06:27:51 +00:00
|
|
|
operation = CreateOperation(*operation_info, DELETE_IMAGE, "Delete image file");
|
2022-10-04 15:23:42 +00:00
|
|
|
AddOperationParameter(*operation, "file", "Image file name", "", true).release();
|
|
|
|
operation.release();
|
2021-12-21 07:43:21 +00:00
|
|
|
|
2022-09-21 06:27:51 +00:00
|
|
|
operation = CreateOperation(*operation_info, RENAME_IMAGE, "Rename image file");
|
2022-10-04 15:23:42 +00:00
|
|
|
AddOperationParameter(*operation, "from", "Source image file name", "", true).release();
|
|
|
|
AddOperationParameter(*operation, "to", "Destination image file name", "", true).release();
|
|
|
|
operation.release();
|
2021-12-21 07:43:21 +00:00
|
|
|
|
2022-09-21 06:27:51 +00:00
|
|
|
operation = CreateOperation(*operation_info, COPY_IMAGE, "Copy image file");
|
2022-10-04 15:23:42 +00:00
|
|
|
AddOperationParameter(*operation, "from", "Source image file name", "", true).release();
|
|
|
|
AddOperationParameter(*operation, "to", "Destination image file name", "", true).release();
|
|
|
|
parameter = AddOperationParameter(*operation, "read_only", "Read-only flag", "false").release();
|
2021-12-21 07:43:21 +00:00
|
|
|
parameter->add_permitted_values("true");
|
|
|
|
parameter->add_permitted_values("false");
|
2022-10-04 15:23:42 +00:00
|
|
|
operation.release();
|
2021-12-21 07:43:21 +00:00
|
|
|
|
2022-09-21 06:27:51 +00:00
|
|
|
operation = CreateOperation(*operation_info, PROTECT_IMAGE, "Write-protect image file");
|
2022-10-04 15:23:42 +00:00
|
|
|
AddOperationParameter(*operation, "file", "Image file name", "", true).release();
|
|
|
|
operation.release();
|
2021-12-21 07:43:21 +00:00
|
|
|
|
2022-09-21 06:27:51 +00:00
|
|
|
operation = CreateOperation(*operation_info, UNPROTECT_IMAGE, "Make image file writable");
|
2022-10-04 15:23:42 +00:00
|
|
|
AddOperationParameter(*operation, "file", "Image file name", "", true).release();
|
|
|
|
operation.release();
|
2021-12-21 07:43:21 +00:00
|
|
|
|
2022-09-21 06:27:51 +00:00
|
|
|
operation = CreateOperation(*operation_info, CHECK_AUTHENTICATION, "Check whether an authentication token is valid");
|
2022-10-04 15:23:42 +00:00
|
|
|
AddOperationParameter(*operation, "token", "Authentication token to be checked", "", true).release();
|
|
|
|
operation.release();
|
2021-12-21 07:43:21 +00:00
|
|
|
|
2022-10-04 15:23:42 +00:00
|
|
|
CreateOperation(*operation_info, OPERATION_INFO, "Get operation meta data").release();
|
2021-12-21 07:43:21 +00:00
|
|
|
|
|
|
|
result.set_status(true);
|
|
|
|
|
2022-10-04 15:23:42 +00:00
|
|
|
return operation_info;
|
2021-12-21 07:43:21 +00:00
|
|
|
}
|
|
|
|
|
2022-10-04 15:23:42 +00:00
|
|
|
unique_ptr<PbOperationMetaData> RascsiResponse::CreateOperation(PbOperationInfo& operation_info, const PbOperation& operation,
|
2022-09-21 06:27:51 +00:00
|
|
|
const string& description) const
|
2021-12-21 07:43:21 +00:00
|
|
|
{
|
2022-10-04 15:23:42 +00:00
|
|
|
auto meta_data = make_unique<PbOperationMetaData>();
|
2021-12-21 07:43:21 +00:00
|
|
|
meta_data->set_server_side_name(PbOperation_Name(operation));
|
2021-12-22 08:25:05 +00:00
|
|
|
meta_data->set_description(description);
|
2021-12-21 07:43:21 +00:00
|
|
|
int ordinal = PbOperation_descriptor()->FindValueByName(PbOperation_Name(operation))->index();
|
2022-10-04 15:23:42 +00:00
|
|
|
(*operation_info.mutable_operations())[ordinal] = *meta_data.release();
|
|
|
|
return unique_ptr<PbOperationMetaData>(&(*operation_info.mutable_operations())[ordinal]);
|
2021-12-21 07:43:21 +00:00
|
|
|
}
|
|
|
|
|
2022-10-04 15:23:42 +00:00
|
|
|
unique_ptr<PbOperationParameter> RascsiResponse::AddOperationParameter(PbOperationMetaData& meta_data,
|
|
|
|
const string& name, const string& description, const string& default_value, bool is_mandatory) const
|
2021-12-21 07:43:21 +00:00
|
|
|
{
|
2022-09-21 06:27:51 +00:00
|
|
|
auto parameter = unique_ptr<PbOperationParameter>(meta_data.add_parameters());
|
2021-12-21 07:43:21 +00:00
|
|
|
parameter->set_name(name);
|
2021-12-22 08:25:05 +00:00
|
|
|
parameter->set_description(description);
|
2021-12-21 07:43:21 +00:00
|
|
|
parameter->set_default_value(default_value);
|
|
|
|
parameter->set_is_mandatory(is_mandatory);
|
|
|
|
|
2022-10-04 15:23:42 +00:00
|
|
|
return parameter;
|
|
|
|
}
|
|
|
|
|
|
|
|
set<id_set> RascsiResponse::MatchDevices(PbResult& result, const PbCommand& command) const
|
|
|
|
{
|
|
|
|
set<id_set> id_sets;
|
|
|
|
|
|
|
|
for (const auto& device : command.devices()) {
|
|
|
|
bool has_device = false;
|
|
|
|
for (const auto& d : controller_manager.GetAllDevices()) {
|
|
|
|
if (d->GetId() == device.id() && d->GetLun() == device.unit()) {
|
|
|
|
id_sets.insert(make_pair(device.id(), device.unit()));
|
|
|
|
has_device = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!has_device) {
|
|
|
|
id_sets.clear();
|
|
|
|
|
|
|
|
result.set_status(false);
|
|
|
|
result.set_msg("No device for ID " + to_string(device.id()) + ", unit " + to_string(device.unit()));
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return id_sets;
|
|
|
|
}
|
|
|
|
|
|
|
|
string RascsiResponse::GetNextImageFile(const dirent *dir, const string& folder)
|
|
|
|
{
|
|
|
|
// Ignore unknown folder types and folder names starting with '.'
|
|
|
|
if ((dir->d_type != DT_REG && dir->d_type != DT_DIR && dir->d_type != DT_LNK && dir->d_type != DT_BLK)
|
|
|
|
|| dir->d_name[0] == '.') {
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
|
2022-10-08 17:26:04 +00:00
|
|
|
const string filename = folder + "/" + dir->d_name;
|
2022-10-04 15:23:42 +00:00
|
|
|
|
2022-10-25 08:29:57 +00:00
|
|
|
const bool file_exists = exists(path(filename));
|
2022-10-04 15:23:42 +00:00
|
|
|
|
2022-10-25 08:29:57 +00:00
|
|
|
// filesystem::file_size cannot be used here because gcc < 10.3.0 cannot handle files of more than 2 GiB
|
|
|
|
struct stat st;
|
|
|
|
stat(filename.c_str(), &st);
|
2022-10-04 15:23:42 +00:00
|
|
|
if (dir->d_type == DT_REG && file_exists && !st.st_size) {
|
2022-10-08 17:26:04 +00:00
|
|
|
LOGWARN("File '%s' in image folder '%s' is empty", dir->d_name, folder.c_str())
|
2022-10-04 15:23:42 +00:00
|
|
|
return "";
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dir->d_type == DT_LNK && !file_exists) {
|
|
|
|
LOGWARN("Symlink '%s' in image folder '%s' is broken", dir->d_name, folder.c_str())
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
|
|
|
|
return filename;
|
2021-12-21 07:43:21 +00:00
|
|
|
}
|