protobuf-based rasctl/rascsi command interface (#129)

* Initial protobuf definition

* protobuf result message draft

* Merge with develop branch

* Makefile generates protobuf-based source files

* Interface update

* Fixed typo

* Fixed typo

* Updated returning status

* Serialize return data

* Use correct descriptor

* Made interface fields required

* Deserialize result

* Serialization update

* Updated serialization

* Serialization update

* Updated deserialization

* status handling update

* Evaluate status

* Revert "Evaluate status"

This reverts commit 3d8f2c3252a10618dede5f5fd80c84115551fc7d.

* Completed sense_key enum

* Renaming

* Added protobuf Command

* Updated command evaluation

* Interface update

* Interface update

* Added DeviceType enum

* Improved type-safety

* Fixed typo

* Type-safety update

* Fixed typo

* Error handling update

* Updated list handling

* Error handling update

* Use more C++ strings

* protobuf enums can provide their names

* Fixed listing devices

* Updated logging

* Enum usage cleanup

* More enum cleanup

* Fixed command check

* Updated type check

* Updated enums

* Removed unused variable

* Removed goto, added exception

* Socket handling cleanup

* Code locality cleanup

* Added helper method

* Extracted code

* Updated socket/file handling

* Use C++ I/O

* Use tolower()

* Renaming

* Simplified has_suffix

* Fixed typo

* Use spdlog namespace

* Simplified formatting (endl) of error messages

* Added -s option for changing the runtime log level to rasctl

* Renaming

* Renaming

* Updated error reporting

* Fixed log string formatting

* String conversion cleanup

* Fixed typo

* Log mmap error (happens on 64 bit)

* Improved proto3 compatibility, updated error handling

* CHanges based on review

* Fixed comment

* Comment update

* Updated ListDevice to not directly write to the stream

* Use size_t

* Renaming

* Buffering update

* MapController should not use fp

* Use fd, not fp

* rasctl has to display *all* results

* rasctl has to display *all* results

* Error handling update

* Updated to proto3 protocol

* Optimization by using protobuf-lite

* RaspBian outdated protoc does not support _Name

* Added protobuf-compiler to easyinstall.sh

Co-authored-by: akuker <34318535+akuker@users.noreply.github.com>
This commit is contained in:
uweseimet 2021-07-19 00:15:13 +02:00 committed by GitHub
parent 6136b29515
commit af6e311e6e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 620 additions and 532 deletions

View File

@ -41,7 +41,7 @@ function initialChecks() {
} }
function installPackages() { function installPackages() {
sudo apt-get update && sudo apt install git libspdlog-dev libpcap-dev genisoimage python3 python3-venv nginx libpcap-dev -y sudo apt-get update && sudo apt install git libspdlog-dev libpcap-dev genisoimage python3 python3-venv nginx libpcap-dev protobuf-compiler -y
} }
# install all dependency packages for RaSCSI Service # install all dependency packages for RaSCSI Service

View File

@ -73,19 +73,27 @@ BINDIR := ./bin/$(shell echo $(CONNECT_TYPE) | tr '[:upper:]' '[:lower:]')
# for my specific use case. If you need them - add them back in! # for my specific use case. If you need them - add them back in!
BIN_ALL = $(BINDIR)/$(RASCSI) $(BINDIR)/$(RASCTL) $(BINDIR)/$(SCSIMON) BIN_ALL = $(BINDIR)/$(RASCSI) $(BINDIR)/$(RASCTL) $(BINDIR)/$(SCSIMON)
SRC_PROTOC = \
rasctl_interface.proto
SRC_PROTOBUF = \
rasctl_interface.pb.cpp
SRC_RASCSI = \ SRC_RASCSI = \
rascsi.cpp \ rascsi.cpp \
scsi.cpp \ scsi.cpp \
gpiobus.cpp \ gpiobus.cpp \
filepath.cpp \ filepath.cpp \
fileio.cpp\ fileio.cpp\
rascsi_version.cpp rascsi_version.cpp \
rasutil.cpp
# os.cpp # os.cpp
# rasctl_command.cpp # rasctl_command.cpp
# rascsi_mgr.cpp # rascsi_mgr.cpp
# command_thread.cpp # command_thread.cpp
SRC_RASCSI += $(shell find ./controllers -name '*.cpp') SRC_RASCSI += $(shell find ./controllers -name '*.cpp')
SRC_RASCSI += $(shell find ./devices -name '*.cpp') SRC_RASCSI += $(shell find ./devices -name '*.cpp')
SRC_RASCSI += $(SRC_PROTOBUF)
SRC_SCSIMON = \ SRC_SCSIMON = \
scsimon.cpp \ scsimon.cpp \
@ -97,8 +105,10 @@ SRC_SCSIMON = \
SRC_RASCTL = \ SRC_RASCTL = \
rasctl.cpp\ rasctl.cpp\
rascsi_version.cpp rascsi_version.cpp \
rasutil.cpp
# rasctl_command.cpp # rasctl_command.cpp
SRC_RASCTL += $(SRC_PROTOBUF)
SRC_RASDUMP = \ SRC_RASDUMP = \
rasdump.cpp \ rasdump.cpp \
@ -126,9 +136,9 @@ OBJ_RASCSI := $(addprefix $(OBJDIR)/,$(notdir $(SRC_RASCSI:%.cpp=%.o)))
OBJ_RASCTL := $(addprefix $(OBJDIR)/,$(notdir $(SRC_RASCTL:%.cpp=%.o))) OBJ_RASCTL := $(addprefix $(OBJDIR)/,$(notdir $(SRC_RASCTL:%.cpp=%.o)))
OBJ_RASDUMP := $(addprefix $(OBJDIR)/,$(notdir $(SRC_RASDUMP:%.cpp=%.o))) OBJ_RASDUMP := $(addprefix $(OBJDIR)/,$(notdir $(SRC_RASDUMP:%.cpp=%.o)))
OBJ_SASIDUMP := $(addprefix $(OBJDIR)/,$(notdir $(SRC_SASIDUMP:%.cpp=%.o))) OBJ_SASIDUMP := $(addprefix $(OBJDIR)/,$(notdir $(SRC_SASIDUMP:%.cpp=%.o)))
OBJ_SCSIMON := $(addprefix $(OBJDIR)/,$(notdir $(SRC_SCSIMON:%.cpp=%.o))) OBJ_SCSIMON := $(addprefix $(OBJDIR)/,$(notdir $(SRC_SCSIMON:%.cpp=%.o)))
#OBJ_ALL := $(OBJ_RASCSI) $(OBJ_RASCTL) $(OBJ_RASDUMP) $(OBJ_SASIDUMP) $(OBJ_SCSIMON)
OBJ_ALL := $(OBJ_RASCSI) $(OBJ_RASCTL) $(OBJ_RASDUMP) $(OBJ_SASIDUMP) $(OBJ_SCSIMON) GEN_PROTOBUF := $(SRC_PROTOBUF) rasctl_interface.pb.h
# The following will include all of the auto-generated dependency files (*.d) # The following will include all of the auto-generated dependency files (*.d)
@ -143,6 +153,11 @@ $(OBJDIR) $(BINDIR):
$(OBJDIR)/%.o: %.cpp | $(OBJDIR) $(OBJDIR)/%.o: %.cpp | $(OBJDIR)
$(CXX) $(CXXFLAGS) -c $< -o $@ $(CXX) $(CXXFLAGS) -c $< -o $@
$(SRC_PROTOBUF): $(SRC_PROTOC)
echo "-- Generating protobuf-based source files"
protoc --cpp_out=. $(SRC_PROTOC)
mv rasctl_interface.pb.cc $@
## Build Targets: ## Build Targets:
## all : Rebuild all of the executable files and re-generate ## all : Rebuild all of the executable files and re-generate
## the text versions of the manpages ## the text versions of the manpages
@ -155,10 +170,10 @@ ALL: all
docs: $(DOC_DIR)/rascsi_man_page.txt $(DOC_DIR)/rasctl_man_page.txt $(DOC_DIR)/scsimon_man_page.txt docs: $(DOC_DIR)/rascsi_man_page.txt $(DOC_DIR)/rasctl_man_page.txt $(DOC_DIR)/scsimon_man_page.txt
$(BINDIR)/$(RASCSI): $(OBJ_RASCSI) | $(BINDIR) $(BINDIR)/$(RASCSI): $(OBJ_RASCSI) | $(BINDIR)
$(CXX) $(CXXFLAGS) -o $@ $(OBJ_RASCSI) -lpthread -lz -lpcap $(CXX) $(CXXFLAGS) -o $@ $(OBJ_RASCSI) -lpthread -lz -lpcap -lprotobuf-lite
$(BINDIR)/$(RASCTL): $(OBJ_RASCTL) | $(BINDIR) $(BINDIR)/$(RASCTL): $(OBJ_RASCTL) | $(BINDIR)
$(CXX) $(CXXFLAGS) -o $@ $(OBJ_RASCTL) $(CXX) $(CXXFLAGS) -o $@ $(OBJ_RASCTL) -lprotobuf-lite
$(BINDIR)/$(RASDUMP): $(OBJ_RASDUMP) | $(BINDIR) $(BINDIR)/$(RASDUMP): $(OBJ_RASDUMP) | $(BINDIR)
$(CXX) $(CXXFLAGS) -o $@ $(OBJ_RASDUMP) $(CXX) $(CXXFLAGS) -o $@ $(OBJ_RASDUMP)
@ -173,7 +188,7 @@ $(BINDIR)/$(SCSIMON): $(OBJ_SCSIMON) | $(BINDIR)
## compiler files and executable files ## compiler files and executable files
.PHONY: clean .PHONY: clean
clean: clean:
rm -rf $(OBJDIR) $(BINDIR) rm -rf $(OBJDIR) $(BINDIR) $(GEN_PROTOBUF)
## run : Launches RaSCSI using some pre-defined drive ## run : Launches RaSCSI using some pre-defined drive
## images. Useful for debugging when you're building ## images. Useful for debugging when you're building

View File

@ -12,20 +12,34 @@
#if !defined(exceptions_h) #if !defined(exceptions_h)
#define exceptions_h #define exceptions_h
#include <exception>
class lunexception : public std::exception { class lunexception : public std::exception {
private: private:
DWORD lun; int lun;
public: public:
lunexception(DWORD lun) { lunexception(int _lun) : lun(_lun) { }
this->lun = lun;
}
~lunexception() { } ~lunexception() { }
DWORD getlun() const { int getlun() const {
return lun; return lun;
} }
}; };
class ioexception : public std::exception {
private:
const char *msg;
public:
ioexception(const char *_msg) : msg(_msg) { }
~ioexception() { }
const char *getmsg() const {
return msg;
}
};
#endif #endif

View File

@ -179,9 +179,9 @@ BOOL FASTCALL GPIOBUS::Init(mode_e mode)
} }
// Map peripheral region memory // Map peripheral region memory
map = mmap(NULL, 0x1000100, map = mmap(NULL, 0x1000100, PROT_READ | PROT_WRITE, MAP_SHARED, fd, baseaddr);
PROT_READ | PROT_WRITE, MAP_SHARED, fd, baseaddr);
if (map == MAP_FAILED) { if (map == MAP_FAILED) {
LOGERROR("Error: Unable to map memory");
close(fd); close(fd);
return FALSE; return FALSE;
} }

File diff suppressed because it is too large Load Diff

View File

@ -11,51 +11,62 @@
#include "os.h" #include "os.h"
#include "rascsi_version.h" #include "rascsi_version.h"
#include "rasctl.h" #include "exceptions.h"
#include "rasutil.h"
#include "rasctl_interface.pb.h"
using namespace std;
using namespace rasctl_interface;
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
// //
// Send Command // Send Command
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
BOOL SendCommand(char *buf) bool SendCommand(const Command& command)
{ {
int fd;
struct sockaddr_in server;
FILE *fp;
// Create a socket to send the command // Create a socket to send the command
fd = socket(PF_INET, SOCK_STREAM, 0); struct sockaddr_in server;
memset(&server, 0, sizeof(server)); memset(&server, 0, sizeof(server));
server.sin_family = PF_INET; server.sin_family = PF_INET;
server.sin_port = htons(6868); server.sin_port = htons(6868);
server.sin_addr.s_addr = htonl(INADDR_LOOPBACK); server.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
int fd = socket(PF_INET, SOCK_STREAM, 0);
// Connect if (connect(fd, (struct sockaddr *)&server, sizeof(struct sockaddr_in)) < 0) {
if (connect(fd, (struct sockaddr *)&server, cerr << "Error : Can't connect to rascsi process" << endl;
sizeof(struct sockaddr_in)) < 0) { return false;
fprintf(stderr, "Error : Can't connect to rascsi process\n");
return FALSE;
} }
// Send the command // Send the command
fp = fdopen(fd, "r+"); FILE *fp = fdopen(fd, "r+");
setvbuf(fp, NULL, _IONBF, 0);
fputs(buf, fp); string data;
command.SerializeToString(&data);
SerializeProtobufData(fp, data);
// Receive the message // Receive the message
while (1) { bool status = true;
if (fgets((char *)buf, BUFSIZ, fp) == NULL) { try {
break; Result result;
} result.ParseFromString(DeserializeProtobufData(fd));
printf("%s", buf);
} status = result.status();
if (!result.msg().empty()) {
cout << result.msg();
}
}
catch(const ioexception& e) {
cerr << "Error : " << e.getmsg() << endl;
// Fall through
}
// Close the socket when we're done
fclose(fp); fclose(fp);
close(fd); close(fd);
return TRUE; return status;
} }
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
@ -65,48 +76,34 @@ BOOL SendCommand(char *buf)
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
int main(int argc, char* argv[]) int main(int argc, char* argv[])
{ {
int opt;
int id;
int un;
int cmd;
int type;
char *file;
BOOL list;
int len;
char *ext;
char buf[BUFSIZ];
id = -1;
un = 0;
cmd = -1;
type = -1;
file = NULL;
list = FALSE;
// Display help // Display help
if (argc < 2) { if (argc < 2) {
fprintf(stderr, "SCSI Target Emulator RaSCSI Controller\n"); cerr << "SCSI Target Emulator RaSCSI Controller" << endl;
fprintf(stderr, "version %s (%s, %s)\n", cerr << "version " << rascsi_get_version_string() << " (" << __DATE__ << ", " << __TIME__ << ")" << endl;
rascsi_get_version_string(), cerr << "Usage: " << argv[0] << " -i ID [-u UNIT] [-c CMD] [-t TYPE] [-f FILE] [-s LOG_LEVEL]" << endl;
__DATE__, cerr << " where ID := {0|1|2|3|4|5|6|7}" << endl;
__TIME__); cerr << " UNIT := {0|1} default setting is 0." << endl;
fprintf(stderr, cerr << " CMD := {attach|detach|insert|eject|protect}" << endl;
"Usage: %s -i ID [-u UNIT] [-c CMD] [-t TYPE] [-f FILE]\n", cerr << " TYPE := {hd|mo|cd|bridge|daynaport}" << endl;
argv[0]); cerr << " FILE := image file path" << endl;
fprintf(stderr, " where ID := {0|1|2|3|4|5|6|7}\n"); cerr << " LOG_LEVEL := log level" << endl;
fprintf(stderr, " UNIT := {0|1} default setting is 0.\n"); cerr << " If CMD is 'attach' or 'insert' the FILE parameter is required." << endl;
fprintf(stderr, " CMD := {attach|detach|insert|eject|protect}\n"); cerr << "Usage: " << argv[0] << " -l" << endl;
fprintf(stderr, " TYPE := {hd|mo|cd|bridge|daynaport}\n"); cerr << " Print device list." << endl;
fprintf(stderr, " FILE := image file path\n");
fprintf(stderr, " CMD is 'attach' or 'insert' and FILE parameter is required.\n");
fprintf(stderr, "Usage: %s -l\n", argv[0]);
fprintf(stderr, " Print device list.\n");
exit(0); exit(0);
} }
// Parse the arguments // Parse the arguments
int opt;
int id = -1;
int un = 0;
Operation cmd = LIST;
DeviceType type = UNDEFINED;
string params;
opterr = 0; opterr = 0;
while ((opt = getopt(argc, argv, "i:u:c:t:f:l")) != -1) {
while ((opt = getopt(argc, argv, "i:u:c:t:f:s:l")) != -1) {
switch (opt) { switch (opt) {
case 'i': case 'i':
id = optarg[0] - '0'; id = optarg[0] - '0';
@ -117,158 +114,142 @@ int main(int argc, char* argv[])
break; break;
case 'c': case 'c':
switch (optarg[0]) { switch (tolower(optarg[0])) {
case 'a': // ATTACH case 'a':
case 'A': cmd = ATTACH;
cmd = 0;
break; break;
case 'd': // DETACH
case 'D': case 'd':
cmd = 1; cmd = DETACH;
break; break;
case 'i': // INSERT
case 'I': case 'i':
cmd = 2; cmd = INSERT;
break; break;
case 'e': // EJECT
case 'E': case 'e':
cmd = 3; cmd = EJECT;
break; break;
case 'p': // PROTECT
case 'P': case 'p':
cmd = 4; cmd = PROTECT;
break; break;
} }
break; break;
case 't': case 't':
switch (optarg[0]) { switch (tolower(optarg[0])) {
case 's': // HD(SASI) case 's':
case 'S': type = SASI_HD;
case 'h': // HD(SCSI)
case 'H':
// rascsi will figure out if this should be SASI or
// SCSI later in the process....
type = rasctl_dev_sasi_hd;
break; break;
case 'm': // MO
case 'M': case 'h':
type = rasctl_dev_mo; type = SCSI_HD;
break; break;
case 'c': // CD
case 'C': case 'm':
type = rasctl_dev_cd; type = MO;
break; break;
case 'b': // BRIDGE
case 'B': case 'c':
type = rasctl_dev_br; type = CD;
break; break;
// case 'n': // Nuvolink
// case 'N': case 'b':
// type = rasctl_dev_nuvolink; type = BR;
break;
// case 'n':
// type = NUVOLINK;
// break; // break;
case 'd': // DaynaPort
case 'D': case 'd':
type = rasctl_dev_daynaport; type = DAYNAPORT;
break; break;
} }
break; break;
case 'f': case 'f':
file = optarg; params = optarg;
break; break;
case 'l': case 'l':
list = TRUE; cmd = LIST;
break;
case 's':
cmd = LOG_LEVEL;
params = optarg;
break; break;
} }
} }
Command command;
if (cmd == LOG_LEVEL) {
command.set_cmd(LOG_LEVEL);
command.set_params(params);
SendCommand(command);
exit(0);
}
// List display only // List display only
if (id < 0 && cmd < 0 && type < 0 && file == NULL && list) { if (cmd == LIST || (id < 0 && type == UNDEFINED && params.empty())) {
sprintf(buf, "list\n"); command.set_cmd(LIST);
SendCommand(buf); SendCommand(command);
exit(0); exit(0);
} }
// Check the ID number // Check the ID number
if (id < 0 || id > 7) { if (id < 0 || id > 7) {
fprintf(stderr, "%s Error : Invalid ID %d \n", __PRETTY_FUNCTION__, id); cerr << __PRETTY_FUNCTION__ << " Error : Invalid ID " << id << endl;
exit(EINVAL); exit(EINVAL);
} }
// Check the unit number // Check the unit number
if (un < 0 || un > 1) { if (un < 0 || un > 1) {
fprintf(stderr, "%s Error : Invalid UNIT %d \n", __PRETTY_FUNCTION__, un); cerr << __PRETTY_FUNCTION__ << " Error : Invalid UNIT " << un << endl;
exit(EINVAL); exit(EINVAL);
} }
// Command check
if (cmd < 0) {
cmd = 0; // Default command is ATTATCH
}
// Type Check // Type Check
if (cmd == 0 && type < 0) { if (cmd == ATTACH && type == UNDEFINED) {
// Try to determine the file type from the extension // Try to determine the file type from the extension
len = file ? strlen(file) : 0; int len = params.length();
if (len > 4 && file[len - 4] == '.') { if (len > 4 && params[len - 4] == '.') {
ext = &file[len - 3]; string ext = params.substr(len - 3);
if (xstrcasecmp(ext, "hdf") == 0 || if (ext == "hdf" || ext == "hds" || ext == "hdn" || ext == "hdi" || ext == "nhd" || ext == "hda") {
xstrcasecmp(ext, "hds") == 0 || type = SASI_HD;
xstrcasecmp(ext, "hdn") == 0 || } else if (ext == "mos") {
xstrcasecmp(ext, "hdi") == 0 || type = MO;
xstrcasecmp(ext, "nhd") == 0 || } else if (ext == "iso") {
xstrcasecmp(ext, "hda") == 0) { type = CD;
// HD(SASI/SCSI)
type = 0;
} else if (xstrcasecmp(ext, "mos") == 0) {
// MO
type = 2;
} else if (xstrcasecmp(ext, "iso") == 0) {
// CD
type = 3;
} }
} }
if (type < 0) {
fprintf(stderr, "Error : Invalid type\n");
exit(EINVAL);
}
} }
// File check (command is ATTACH and type is HD) // File check (command is ATTACH and type is HD, for CD and MO the medium (=file) may be inserted later)
if (cmd == 0 && type >= 0 && type <= 1) { if (cmd == ATTACH && (type == SASI_HD || type == SCSI_HD) && params.empty()) {
if (!file) { cerr << "Error : Invalid file path" << endl;
fprintf(stderr, "Error : Invalid file path\n"); exit(EINVAL);
exit(EINVAL);
}
} }
// File check (command is INSERT) // File check (command is INSERT)
if (cmd == 2) { if (cmd == INSERT && params.empty()) {
if (!file) { cerr << "Error : Invalid file path" << endl;
fprintf(stderr, "Error : Invalid file path\n"); exit(EINVAL);
exit(EINVAL);
}
}
// Set unnecessary type to 0
if (type < 0) {
type = 0;
} }
// Generate the command and send it // Generate the command and send it
sprintf(buf, "%d %d %d %d %s\n", id, un, cmd, type, file ? file : "-"); command.set_id(id);
if (!SendCommand(buf)) { command.set_un(un);
exit(ENOTCONN); command.set_cmd(cmd);
command.set_type(type);
if (!params.empty()) {
command.set_params(params);
} }
if (!SendCommand(command)) {
// Display the list exit(ENOTCONN);
if (list) {
sprintf(buf, "list\n");
SendCommand(buf);
} }
// All done! // All done!

View File

@ -1,24 +0,0 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI (*^..^*)
// for Raspberry Pi
//
// Powered by XM6 TypeG Technology.
// Copyright (C) 2016-2020 GIMONS
// Copyright (C) akuker
//
// [ Send Control Command ]
//
//---------------------------------------------------------------------------
enum rasctl_dev_type : int {
rasctl_dev_invalid = -1,
rasctl_dev_sasi_hd = 0,
rasctl_dev_scsi_hd = 1,
rasctl_dev_mo = 2,
rasctl_dev_cd = 3,
rasctl_dev_br = 4,
rasctl_dev_nuvolink = 5,
rasctl_dev_daynaport = 6,
};

View File

@ -0,0 +1,39 @@
syntax = "proto3";
option optimize_for = LITE_RUNTIME;
package rasctl_interface;
enum Operation {
LIST = 0;
ATTACH = 1;
DETACH = 2;
INSERT = 3;
EJECT = 4;
PROTECT = 5;
LOG_LEVEL = 6;
}
enum DeviceType {
UNDEFINED = 0;
SASI_HD = 1;
SCSI_HD = 2;
MO = 3;
CD = 4;
BR = 5;
NUVOLINK = 6;
DAYNAPORT = 7;
}
message Command {
Operation cmd = 1;
int32 id = 2;
int32 un = 3;
DeviceType type = 4;
string params = 5;
}
message Result {
bool status = 1;
string msg = 2;
}

View File

@ -0,0 +1,65 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI (*^..^*)
// for Raspberry Pi
//
// Powered by XM6 TypeG Technology.
// Copyright (C) 2016-2020 GIMONS
// Copyright (C) 2020 akuker
//
//---------------------------------------------------------------------------
#include <cstring>
#include <cstdlib>
#include <unistd.h>
#include "exceptions.h"
#include "rasutil.h"
using namespace std;
//---------------------------------------------------------------------------
//
// Serialize/Deserialize protobuf data: Length followed by the actual data
//
//---------------------------------------------------------------------------
void SerializeProtobufData(FILE *fp, const string& data)
{
// Write the size of the protobuf data as a header
size_t size = data.length();
fwrite(&size, sizeof(size), 1, fp);
// Write the actual protobuf data
void *buf = malloc(size);
memcpy(buf, data.data(), size);
fwrite(buf, size, 1, fp);
fflush(fp);
free(buf);
}
string DeserializeProtobufData(int fd)
{
// First read the header with the size of the protobuf data
size_t size;
size_t res = read(fd, &size, sizeof(int));
if (res != sizeof(int)) {
// No more data
return "";
}
// Read the actual protobuf data
void *buf = malloc(size);
res = read(fd, buf, size);
if (res != size) {
free(buf);
throw ioexception("Missing protobuf data");
}
// Read protobuf data into a string, to be converted into a protobuf data structure by the caller
string data((const char *)buf, size);
free(buf);
return data;
}

21
src/raspberrypi/rasutil.h Normal file
View File

@ -0,0 +1,21 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI (*^..^*)
// for Raspberry Pi
//
// Powered by XM6 TypeG Technology.
// Copyright (C) 2016-2020 GIMONS
// Copyright (C) 2020 akuker
//
//---------------------------------------------------------------------------
#if !defined(rasutil_h)
#define rasutil_h
#include <cstdio>
#include <string>
void SerializeProtobufData(FILE *fp, const std::string& data);
std::string DeserializeProtobufData(int fd);
#endif

View File

@ -23,7 +23,20 @@ class ERROR_CODES
public: public:
enum sense_key : int { enum sense_key : int {
NO_SENSE = 0x00, NO_SENSE = 0x00,
ILLEGAL_REQUEST = 0x05 RECOVERED_ERROR = 0x01,
NOT_READY = 0x02,
MEDIUM_ERROR = 0x03,
HARDWARE_ERROR = 0x04,
ILLEGAL_REQUEST = 0x05,
UNIT_ATTENTION = 0x06,
DATA_PROTECT = 0x07,
BLANK_CHECK = 0x08,
VENDOR_SPECIFIC = 0x09,
COPY_ABORTED = 0x0a,
ABORTED_COMMAND = 0x0b,
VOLUME_OVERFLOW = 0x0d,
MISCOMPARE = 0x0e,
COMPLETED = 0x0f
}; };
enum asc : int { enum asc : int {