mirror of
https://github.com/akuker/RASCSI.git
synced 2024-11-29 16:49:55 +00:00
7bbcf59c76
* scsictl accepts generic key/value pairs for options that take parameters
243 lines
6.0 KiB
C++
243 lines
6.0 KiB
C++
//---------------------------------------------------------------------------
|
|
//
|
|
// SCSI Target Emulator PiSCSI
|
|
// for Raspberry Pi
|
|
//
|
|
// Copyright (C) 2021-2023 Uwe Seimet
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
|
|
#include "shared/piscsi_exceptions.h"
|
|
#include "piscsi_util.h"
|
|
#include "protobuf_util.h"
|
|
#include <unistd.h>
|
|
#include <sstream>
|
|
#include <array>
|
|
#include <vector>
|
|
#include <iomanip>
|
|
|
|
using namespace std;
|
|
using namespace piscsi_util;
|
|
using namespace piscsi_interface;
|
|
|
|
#define FPRT(fp, ...) fprintf(fp, __VA_ARGS__ )
|
|
|
|
void protobuf_util::ParseParameters(PbDeviceDefinition& device, const string& params)
|
|
{
|
|
if (params.empty()) {
|
|
return;
|
|
}
|
|
|
|
// Old style parameter (filename), for backwards compatibility only
|
|
if (params.find(KEY_VALUE_SEPARATOR) == string::npos) {
|
|
SetParam(device, "file", params);
|
|
|
|
return;
|
|
}
|
|
|
|
for (const auto& p : Split(params, COMPONENT_SEPARATOR)) {
|
|
if (const auto& param = Split(p, KEY_VALUE_SEPARATOR, 2); param.size() == 2) {
|
|
SetParam(device, param[0], param[1]);
|
|
}
|
|
}
|
|
}
|
|
|
|
string protobuf_util::SetCommandParams(PbCommand& command, const string& params)
|
|
{
|
|
if (params.find(KEY_VALUE_SEPARATOR) != string::npos) {
|
|
return SetFromGenericParams(command, params);
|
|
}
|
|
|
|
string folder_pattern;
|
|
string file_pattern;
|
|
string operations;
|
|
|
|
switch (const auto& components = Split(params, COMPONENT_SEPARATOR, 3); components.size()) {
|
|
case 3:
|
|
operations = components[2];
|
|
[[fallthrough]];
|
|
|
|
case 2:
|
|
folder_pattern = components[0];
|
|
file_pattern = components[1];
|
|
break;
|
|
|
|
case 1:
|
|
file_pattern = components[0];
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
SetParam(command, "folder_pattern", folder_pattern);
|
|
SetParam(command, "file_pattern", file_pattern);
|
|
SetParam(command, "operations", operations);
|
|
|
|
return "";
|
|
}
|
|
|
|
string protobuf_util::SetFromGenericParams(PbCommand& command, const string& params)
|
|
{
|
|
for (const string& key_value : Split(params, COMPONENT_SEPARATOR)) {
|
|
const auto& param = Split(key_value, KEY_VALUE_SEPARATOR, 2);
|
|
if (param.size() > 1 && !param[0].empty()) {
|
|
SetParam(command, param[0], param[1]);
|
|
}
|
|
else {
|
|
return "Parameter '" + key_value + "' has to be a key/value pair";
|
|
}
|
|
}
|
|
|
|
return "";
|
|
}
|
|
|
|
void protobuf_util::SetProductData(PbDeviceDefinition& device, const string& data)
|
|
{
|
|
const auto& components = Split(data, COMPONENT_SEPARATOR, 3);
|
|
switch (components.size()) {
|
|
case 3:
|
|
device.set_revision(components[2]);
|
|
[[fallthrough]];
|
|
|
|
case 2:
|
|
device.set_product(components[1]);
|
|
[[fallthrough]];
|
|
|
|
case 1:
|
|
device.set_vendor(components[0]);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
string protobuf_util::SetIdAndLun(PbDeviceDefinition& device, const string& value)
|
|
{
|
|
int id;
|
|
int lun;
|
|
if (const string error = ProcessId(value, id, lun); !error.empty()) {
|
|
return error;
|
|
}
|
|
|
|
device.set_id(id);
|
|
device.set_unit(lun != -1 ? lun : 0);
|
|
|
|
return "";
|
|
}
|
|
|
|
string protobuf_util::ListDevices(const vector<PbDevice>& pb_devices)
|
|
{
|
|
if (pb_devices.empty()) {
|
|
return "No devices currently attached.\n";
|
|
}
|
|
|
|
ostringstream s;
|
|
s << "+----+-----+------+-------------------------------------\n"
|
|
<< "| ID | LUN | TYPE | IMAGE FILE\n"
|
|
<< "+----+-----+------+-------------------------------------\n";
|
|
|
|
vector<PbDevice> devices = pb_devices;
|
|
ranges::sort(devices, [](const auto& a, const auto& b) { return a.id() < b.id() || a.unit() < b.unit(); });
|
|
|
|
for (const auto& device : devices) {
|
|
string filename;
|
|
switch (device.type()) {
|
|
case SCBR:
|
|
filename = "X68000 HOST BRIDGE";
|
|
break;
|
|
|
|
case SCDP:
|
|
filename = "DaynaPort SCSI/Link";
|
|
break;
|
|
|
|
case SCHS:
|
|
filename = "Host Services";
|
|
break;
|
|
|
|
case SCLP:
|
|
filename = "SCSI Printer";
|
|
break;
|
|
|
|
default:
|
|
filename = device.file().name();
|
|
break;
|
|
}
|
|
|
|
s << "| " << device.id() << " | " << setw(3) << device.unit() << " | " << PbDeviceType_Name(device.type()) << " | "
|
|
<< (filename.empty() ? "NO MEDIUM" : filename)
|
|
<< (!device.status().removed() && (device.properties().read_only() || device.status().protected_()) ? " (READ-ONLY)" : "")
|
|
<< '\n';
|
|
}
|
|
|
|
s << "+----+-----+------+-------------------------------------\n";
|
|
|
|
return s.str();
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// Serialize/Deserialize protobuf message: Length followed by the actual data.
|
|
// A little endian platform is assumed.
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
|
|
void protobuf_util::SerializeMessage(int fd, const google::protobuf::Message& message)
|
|
{
|
|
const string data = message.SerializeAsString();
|
|
|
|
// Write the size of the protobuf data as a header
|
|
const auto size = static_cast<int32_t>(data.length());
|
|
if (write(fd, &size, sizeof(size)) != sizeof(size)) {
|
|
throw io_exception("Can't write protobuf message size");
|
|
}
|
|
|
|
// Write the actual protobuf data
|
|
if (write(fd, data.data(), size) != size) {
|
|
throw io_exception("Can't write protobuf message data");
|
|
}
|
|
}
|
|
|
|
void protobuf_util::DeserializeMessage(int fd, google::protobuf::Message& message)
|
|
{
|
|
// Read the header with the size of the protobuf data
|
|
array<byte, sizeof(int32_t)> header_buf;
|
|
if (ReadBytes(fd, header_buf) < header_buf.size()) {
|
|
throw io_exception("Can't read protobuf message size");
|
|
}
|
|
|
|
const int size = (static_cast<int>(header_buf[3]) << 24) + (static_cast<int>(header_buf[2]) << 16)
|
|
+ (static_cast<int>(header_buf[1]) << 8) + static_cast<int>(header_buf[0]);
|
|
if (size < 0) {
|
|
throw io_exception("Invalid protobuf message size");
|
|
}
|
|
|
|
// Read the binary protobuf data
|
|
vector<byte> data_buf(size);
|
|
if (ReadBytes(fd, data_buf) != data_buf.size()) {
|
|
throw io_exception("Invalid protobuf message data");
|
|
}
|
|
|
|
message.ParseFromArray(data_buf.data(), size);
|
|
}
|
|
|
|
size_t protobuf_util::ReadBytes(int fd, span<byte> buf)
|
|
{
|
|
size_t offset = 0;
|
|
while (offset < buf.size()) {
|
|
const auto len = read(fd, &buf.data()[offset], buf.size() - offset);
|
|
if (len == -1) {
|
|
throw io_exception("Read error: " + string(strerror(errno)));
|
|
}
|
|
|
|
if (!len) {
|
|
break;
|
|
}
|
|
|
|
offset += len;
|
|
}
|
|
|
|
return offset;
|
|
}
|