From af6e311e6eab40255572d2e55b20ab6edb6871ea Mon Sep 17 00:00:00 2001 From: uweseimet <48174652+uweseimet@users.noreply.github.com> Date: Mon, 19 Jul 2021 00:15:13 +0200 Subject: [PATCH] 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> --- easyinstall.sh | 2 +- src/raspberrypi/Makefile | 31 +- src/raspberrypi/exceptions.h | 24 +- src/raspberrypi/gpiobus.cpp | 4 +- src/raspberrypi/rascsi.cpp | 630 ++++++++++++------------- src/raspberrypi/rasctl.cpp | 297 ++++++------ src/raspberrypi/rasctl.h | 24 - src/raspberrypi/rasctl_interface.proto | 39 ++ src/raspberrypi/rasutil.cpp | 65 +++ src/raspberrypi/rasutil.h | 21 + src/raspberrypi/scsi.h | 15 +- 11 files changed, 620 insertions(+), 532 deletions(-) delete mode 100644 src/raspberrypi/rasctl.h create mode 100644 src/raspberrypi/rasctl_interface.proto create mode 100644 src/raspberrypi/rasutil.cpp create mode 100644 src/raspberrypi/rasutil.h diff --git a/easyinstall.sh b/easyinstall.sh index 9f2f27c3..0167cdc2 100755 --- a/easyinstall.sh +++ b/easyinstall.sh @@ -41,7 +41,7 @@ function initialChecks() { } 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 diff --git a/src/raspberrypi/Makefile b/src/raspberrypi/Makefile index 9a2e742e..60cea44d 100644 --- a/src/raspberrypi/Makefile +++ b/src/raspberrypi/Makefile @@ -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! BIN_ALL = $(BINDIR)/$(RASCSI) $(BINDIR)/$(RASCTL) $(BINDIR)/$(SCSIMON) +SRC_PROTOC = \ + rasctl_interface.proto + +SRC_PROTOBUF = \ + rasctl_interface.pb.cpp + SRC_RASCSI = \ rascsi.cpp \ scsi.cpp \ gpiobus.cpp \ filepath.cpp \ fileio.cpp\ - rascsi_version.cpp + rascsi_version.cpp \ + rasutil.cpp # os.cpp # rasctl_command.cpp # rascsi_mgr.cpp # command_thread.cpp SRC_RASCSI += $(shell find ./controllers -name '*.cpp') SRC_RASCSI += $(shell find ./devices -name '*.cpp') +SRC_RASCSI += $(SRC_PROTOBUF) SRC_SCSIMON = \ scsimon.cpp \ @@ -97,8 +105,10 @@ SRC_SCSIMON = \ SRC_RASCTL = \ rasctl.cpp\ - rascsi_version.cpp + rascsi_version.cpp \ + rasutil.cpp # rasctl_command.cpp +SRC_RASCTL += $(SRC_PROTOBUF) SRC_RASDUMP = \ 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_RASDUMP := $(addprefix $(OBJDIR)/,$(notdir $(SRC_RASDUMP:%.cpp=%.o))) OBJ_SASIDUMP := $(addprefix $(OBJDIR)/,$(notdir $(SRC_SASIDUMP:%.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) +OBJ_SCSIMON := $(addprefix $(OBJDIR)/,$(notdir $(SRC_SCSIMON:%.cpp=%.o))) + +GEN_PROTOBUF := $(SRC_PROTOBUF) rasctl_interface.pb.h # The following will include all of the auto-generated dependency files (*.d) @@ -143,6 +153,11 @@ $(OBJDIR) $(BINDIR): $(OBJDIR)/%.o: %.cpp | $(OBJDIR) $(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: ## all : Rebuild all of the executable files and re-generate ## 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 $(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) - $(CXX) $(CXXFLAGS) -o $@ $(OBJ_RASCTL) + $(CXX) $(CXXFLAGS) -o $@ $(OBJ_RASCTL) -lprotobuf-lite $(BINDIR)/$(RASDUMP): $(OBJ_RASDUMP) | $(BINDIR) $(CXX) $(CXXFLAGS) -o $@ $(OBJ_RASDUMP) @@ -173,7 +188,7 @@ $(BINDIR)/$(SCSIMON): $(OBJ_SCSIMON) | $(BINDIR) ## compiler files and executable files .PHONY: clean clean: - rm -rf $(OBJDIR) $(BINDIR) + rm -rf $(OBJDIR) $(BINDIR) $(GEN_PROTOBUF) ## run : Launches RaSCSI using some pre-defined drive ## images. Useful for debugging when you're building diff --git a/src/raspberrypi/exceptions.h b/src/raspberrypi/exceptions.h index 7d2c6d28..b50760a7 100644 --- a/src/raspberrypi/exceptions.h +++ b/src/raspberrypi/exceptions.h @@ -12,20 +12,34 @@ #if !defined(exceptions_h) #define exceptions_h +#include + class lunexception : public std::exception { private: - DWORD lun; + int lun; public: - lunexception(DWORD lun) { - this->lun = lun; - } + lunexception(int _lun) : lun(_lun) { } ~lunexception() { } - DWORD getlun() const { + int getlun() const { 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 diff --git a/src/raspberrypi/gpiobus.cpp b/src/raspberrypi/gpiobus.cpp index 0295eef1..54e4da7a 100644 --- a/src/raspberrypi/gpiobus.cpp +++ b/src/raspberrypi/gpiobus.cpp @@ -179,9 +179,9 @@ BOOL FASTCALL GPIOBUS::Init(mode_e mode) } // Map peripheral region memory - map = mmap(NULL, 0x1000100, - PROT_READ | PROT_WRITE, MAP_SHARED, fd, baseaddr); + map = mmap(NULL, 0x1000100, PROT_READ | PROT_WRITE, MAP_SHARED, fd, baseaddr); if (map == MAP_FAILED) { + LOGERROR("Error: Unable to map memory"); close(fd); return FALSE; } diff --git a/src/raspberrypi/rascsi.cpp b/src/raspberrypi/rascsi.cpp index 9e627f01..d44f606c 100644 --- a/src/raspberrypi/rascsi.cpp +++ b/src/raspberrypi/rascsi.cpp @@ -25,15 +25,20 @@ #include "controllers/scsidev_ctrl.h" #include "controllers/sasidev_ctrl.h" #include "gpiobus.h" +#include "exceptions.h" #include "rascsi_version.h" -#include "rasctl.h" +#include "rasutil.h" +#include "rasctl_interface.pb.h" #include "spdlog/spdlog.h" #include "spdlog/sinks/stdout_color_sinks.h" #include #include +#include #include using namespace std; +using namespace spdlog; +using namespace rasctl_interface; //--------------------------------------------------------------------------- // @@ -128,15 +133,13 @@ void Banner(int argc, char* argv[]) //--------------------------------------------------------------------------- BOOL Init() { - int i; - #ifndef BAREMETAL struct sockaddr_in server; int yes, result; result = pthread_mutex_init(&ctrl_mutex,NULL); if(result != EXIT_SUCCESS){ - LOGERROR("Unable to create a mutex. Err code: %d",result); + LOGERROR("Unable to create a mutex. Err code: %d", result); return FALSE; } @@ -188,12 +191,12 @@ BOOL Init() bus->Reset(); // Controller initialization - for (i = 0; i < CtrlMax; i++) { + for (int i = 0; i < CtrlMax; i++) { ctrl[i] = NULL; } // Disk Initialization - for (i = 0; i < CtrlMax; i++) { + for (int i = 0; i < CtrlMax; i++) { disk[i] = NULL; } @@ -211,10 +214,8 @@ BOOL Init() //--------------------------------------------------------------------------- void Cleanup() { - int i; - // Delete the disks - for (i = 0; i < CtrlMax * UnitNum; i++) { + for (int i = 0; i < CtrlMax * UnitNum; i++) { if (disk[i]) { delete disk[i]; disk[i] = NULL; @@ -222,7 +223,7 @@ void Cleanup() } // Delete the Controllers - for (i = 0; i < CtrlMax; i++) { + for (int i = 0; i < CtrlMax; i++) { if (ctrl[i]) { delete ctrl[i]; ctrl[i] = NULL; @@ -252,10 +253,8 @@ void Cleanup() //--------------------------------------------------------------------------- void Reset() { - int i; - // Reset all of the controllers - for (i = 0; i < CtrlMax; i++) { + for (int i = 0; i < CtrlMax; i++) { if (ctrl[i]) { ctrl[i]->Reset(); } @@ -270,25 +269,20 @@ void Reset() // List Devices // //--------------------------------------------------------------------------- -void ListDevice(FILE *fp) -{ - int i; - int id; - int un; - Disk *pUnit; - Filepath filepath; - BOOL find; +string ListDevice() { char type[5]; char dev_status[_MAX_FNAME+26]; + ostringstream s; - find = FALSE; + bool has_header = false; type[4] = 0; - for (i = 0; i < CtrlMax * UnitNum; i++) { + + for (int i = 0; i < CtrlMax * UnitNum; i++) { strncpy(dev_status,"",sizeof(dev_status)); // Initialize ID and unit number - id = i / UnitNum; - un = i % UnitNum; - pUnit = disk[i]; + int id = i / UnitNum; + int un = i % UnitNum; + Disk *pUnit = disk[i]; // skip if unit does not exist or null disk if (pUnit == NULL || pUnit->IsNULL()) { @@ -296,14 +290,15 @@ void ListDevice(FILE *fp) } // Output the header - if (!find) { - FPRT(fp, "\n"); - FPRT(fp, "+----+----+------+-------------------------------------\n"); + if (!has_header) { + s << endl + << "+----+----+------+-------------------------------------" << endl + << "| ID | UN | TYPE | DEVICE STATUS" << endl + << "+----+----+------+-------------------------------------" << endl; LOGINFO( "+----+----+------+-------------------------------------"); - FPRT(fp, "| ID | UN | TYPE | DEVICE STATUS\n"); - LOGINFO( "| ID | UN | TYPE | DEVICE STATUS\n"); - FPRT(fp, "+----+----+------+-------------------------------------\n"); - find = TRUE; + LOGINFO( "| ID | UN | TYPE | DEVICE STATUS"); + LOGINFO( "+----+----+------+-------------------------------------\n"); + has_header = true; } // ID,UNIT,Type,Device Status @@ -318,29 +313,31 @@ void ListDevice(FILE *fp) } else if (pUnit->GetID() == MAKEID('S', 'C', 'D', 'P')) { strncpy(dev_status,"DaynaPort SCSI/Link",sizeof(dev_status)); } else { + Filepath filepath; pUnit->GetPath(filepath); - snprintf(dev_status, sizeof(dev_status), "%s", + snprintf(dev_status, sizeof(dev_status), "%s", (pUnit->IsRemovable() && !pUnit->IsReady()) ? "NO MEDIA" : filepath.GetPath()); } // Write protection status if (pUnit->IsRemovable() && pUnit->IsReady() && pUnit->IsWriteP()) { - strcat(dev_status, "(WRITEPROTECT)"); + strcat(dev_status, " (WRITEPROTECT)"); } - FPRT(fp, "| %d | %d | %s | %s\n", id, un, type, dev_status); + s << "| " << id << " | " << un << " | " << type << " | " << dev_status << endl; LOGINFO( "| %d | %d | %s | %s", id, un, type, dev_status); } // If there is no controller, find will be null - if (!find) { - FPRT(fp, "No images currently attached.\n"); - return; + if (!has_header) { + return "No images currently attached.\n"; } - FPRT(fp, "+----+----+------+-------------------------------------\n"); + s << "+----+----+------+-------------------------------------" << endl; LOGINFO( "+----+----+------+-------------------------------------"); + + return s.str(); } //--------------------------------------------------------------------------- @@ -348,21 +345,17 @@ void ListDevice(FILE *fp) // Controller Mapping // //--------------------------------------------------------------------------- -void MapControler(FILE *fp, Disk **map) +bool MapController(Disk **map) { - int i; - int j; - int unitno; - int sasi_num; - int scsi_num; + bool status = true; // Take ownership of the ctrl data structure pthread_mutex_lock(&ctrl_mutex); // Replace the changed unit - for (i = 0; i < CtrlMax; i++) { - for (j = 0; j < UnitNum; j++) { - unitno = i * UnitNum + j; + for (int i = 0; i < CtrlMax; i++) { + for (int j = 0; j < UnitNum; j++) { + int unitno = i * UnitNum + j; if (disk[unitno] != map[unitno]) { // Check if the original unit exists if (disk[unitno]) { @@ -382,12 +375,12 @@ void MapControler(FILE *fp, Disk **map) } // Reconfigure all of the controllers - for (i = 0; i < CtrlMax; i++) { + for (int i = 0; i < CtrlMax; i++) { // Examine the unit configuration - sasi_num = 0; - scsi_num = 0; - for (j = 0; j < UnitNum; j++) { - unitno = i * UnitNum + j; + int sasi_num = 0; + int scsi_num = 0; + for (int j = 0; j < UnitNum; j++) { + int unitno = i * UnitNum + j; // branch by unit type if (disk[unitno]) { if (disk[unitno]->IsSASI()) { @@ -416,7 +409,7 @@ void MapControler(FILE *fp, Disk **map) // Mixture of SCSI and SASI if (sasi_num > 0 && scsi_num > 0) { - FPRT(fp, "Error : SASI and SCSI can't be mixed\n"); + status = false; continue; } @@ -451,8 +444,8 @@ void MapControler(FILE *fp, Disk **map) } // connect all units - for (j = 0; j < UnitNum; j++) { - unitno = i * UnitNum + j; + for (int j = 0; j < UnitNum; j++) { + int unitno = i * UnitNum + j; if (disk[unitno]) { // Add the unit connection ctrl[i]->SetUnit(j, disk[unitno]); @@ -460,6 +453,55 @@ void MapControler(FILE *fp, Disk **map) } } pthread_mutex_unlock(&ctrl_mutex); + + return status; +} + +bool ReturnStatus(FILE *fp, bool status = true, const string msg = "") { +#ifdef BAREMETAL + if (msg.length()) { + FPRT(fp, msg.c_str()); + FPRT(fp, "\n"); + } +#else + Result result; + result.set_msg(msg + "\n"); + result.set_status(status); + + string data; + result.SerializeToString(&data); + SerializeProtobufData(fp, data); +#endif + + return status; +} + +void SetLogLevel(const string& log_level) { + if (log_level == "trace") { + set_level(level::trace); + } + else if (log_level == "debug") { + set_level(level::debug); + } + else if (log_level == "info") { + set_level(level::info); + } + else if (log_level == "warn") { + set_level(level::warn); + } + else if (log_level == "err") { + set_level(level::err); + } + else if (log_level == "critical") { + set_level(level::critical); + } + else if (log_level == "off") { + set_level(level::off); + } + else { + LOGWARN("Invalid log level '%s', falling back to 'trace'", log_level.c_str()); + set_level(level::trace); + } } //--------------------------------------------------------------------------- @@ -467,114 +509,108 @@ void MapControler(FILE *fp, Disk **map) // Command Processing // //--------------------------------------------------------------------------- -BOOL ProcessCmd(FILE *fp, int id, int un, int cmd, int type, char *file) +bool ProcessCmd(FILE *fp, const Command &command) { Disk *map[CtrlMax * UnitNum]; - int len; - char *ext; Filepath filepath; Disk *pUnit; - char type_str[5]; + char type_str[5]; + + int id = command.id(); + int un = command.un(); + Operation cmd = command.cmd(); + DeviceType type = command.type(); + string params = command.params().c_str(); + + ostringstream s; + s << "Processing: cmd=" << cmd << ", id=" << id << ", un=" << un << ", type=" << type << ", params=" << params << endl; + LOGINFO("%s", s.str().c_str()); // Copy the Unit List memcpy(map, disk, sizeof(disk)); // Check the Controller Number if (id < 0 || id >= CtrlMax) { - FPRT(fp, "Error : Invalid ID\n"); - return FALSE; + return ReturnStatus(fp, false, "Error : Invalid ID"); } // Check the Unit Number if (un < 0 || un >= UnitNum) { - FPRT(fp, "Error : Invalid unit number\n"); - return FALSE; + return ReturnStatus(fp, false, "Error : Invalid unit number"); } // Connect Command - if (cmd == 0) { // ATTACH + if (cmd == ATTACH) { + string ext; + // Distinguish between SASI and SCSI - ext = NULL; - pUnit = NULL; - if (type == rasctl_dev_sasi_hd) { - // Passed the check - if (!file) { - return FALSE; - } - - // Check that command is at least 5 characters long - len = strlen(file); - if (len < 5) { - return FALSE; - } - + if (type == SASI_HD) { // Check the extension - if (file[len - 4] != '.') { - return FALSE; + int len = params.length(); + if (len < 5 || params[len - 4] != '.') { + return ReturnStatus(fp, false); } // If the extension is not SASI type, replace with SCSI - ext = &file[len - 3]; - if (xstrcasecmp(ext, "hdf") != 0) { - type = rasctl_dev_scsi_hd; + ext = params.substr(len - 3); + if (ext != "hdf") { + type = SCSI_HD; } } // Create a new drive, based upon type + pUnit = NULL; switch (type) { - case rasctl_dev_sasi_hd: // HDF + case SASI_HD: // HDF pUnit = new SASIHD(); break; - case rasctl_dev_scsi_hd: // HDS/HDN/HDI/NHD/HDA - if (ext == NULL) { - break; - } - if (xstrcasecmp(ext, "hdn") == 0 || - xstrcasecmp(ext, "hdi") == 0 || xstrcasecmp(ext, "nhd") == 0) { + case SCSI_HD: // HDS/HDN/HDI/NHD/HDA + if (ext == "hdn" || ext == "hdi" || ext == "nhd") { pUnit = new SCSIHD_NEC(); - } else if (xstrcasecmp(ext, "hda") == 0) { + } else if (ext == "hda") { pUnit = new SCSIHD_APPLE(); } else { pUnit = new SCSIHD(); } break; - case rasctl_dev_mo: // MO + case MO: pUnit = new SCSIMO(); break; - case rasctl_dev_cd: // CD + case CD: pUnit = new SCSICD(); break; - case rasctl_dev_br: // BRIDGE + case BR: pUnit = new SCSIBR(); break; - // case rasctl_dev_nuvolink: // Nuvolink + // case NUVOLINK: // pUnit = new SCSINuvolink(); // break; - case rasctl_dev_daynaport: // DaynaPort SCSI Link + case DAYNAPORT: pUnit = new SCSIDaynaPort(); - LOGTRACE("Done creating SCSIDaynaPort"); break; default: - FPRT(fp, "Error : Invalid device type\n"); - LOGWARN("rasctl sent a command for an invalid drive type: %d", type); - return FALSE; + ostringstream msg; + msg << "rasctl sent a command for an invalid drive type: " << type; + return ReturnStatus(fp, false, msg.str()); } // drive checks files - if (type <= rasctl_dev_scsi_hd || ((type <= rasctl_dev_cd || type == rasctl_dev_daynaport) && xstrcasecmp(file, "-") != 0)) { + if (type != BR && type != DAYNAPORT && !command.params().empty()) { // Strip the image file extension from device file names, so that device files can be used as drive images - string f = file; - string effective_file = f.find("/dev/") ? f : f.substr(0, f.length() - 4); + string file = params.find("/dev/") ? params : params.substr(0, params.length() - 4); // Set the Path - filepath.SetPath(effective_file.c_str()); + filepath.SetPath(file.c_str()); // Open the file path if (!pUnit->Open(filepath)) { - FPRT(fp, "Error : File open error [%s]\n", file); - LOGWARN("rasctl tried to open an invalid file %s", file); delete pUnit; - return FALSE; + + LOGWARN("rasctl tried to open an invalid file %s", file.c_str()); + + ostringstream msg; + msg << "Error : File open error [" << file << "]"; + return ReturnStatus(fp, false, msg.str()); } } @@ -585,109 +621,103 @@ BOOL ProcessCmd(FILE *fp, int id, int un, int cmd, int type, char *file) map[id * UnitNum + un] = pUnit; // Re-map the controller - MapControler(fp, map); - type_str[0] = (char)(pUnit->GetID() >> 24); - type_str[1] = (char)(pUnit->GetID() >> 16); - type_str[2] = (char)(pUnit->GetID() >> 8); - type_str[3] = (char)(pUnit->GetID()); - type_str[4] = '\0'; - LOGINFO("rasctl added new %s device. ID: %d UN: %d", type_str, id, un); - return TRUE; - } + bool status = MapController(map); + if (status) { + type_str[0] = (char)(pUnit->GetID() >> 24); + type_str[1] = (char)(pUnit->GetID() >> 16); + type_str[2] = (char)(pUnit->GetID() >> 8); + type_str[3] = (char)(pUnit->GetID()); + type_str[4] = '\0'; - // Is this a valid command? - if (cmd > 4) { - FPRT(fp, "Error : Invalid command\n"); - LOGINFO("rasctl sent an invalid command: %d",cmd); - return FALSE; + LOGINFO("rasctl added new %s device. ID: %d UN: %d", type_str, id, un); + } + + return ReturnStatus(fp, status, status ? "" : "Error : SASI and SCSI can't be mixed\n"); } // Does the controller exist? if (ctrl[id] == NULL) { - FPRT(fp, "Error : No such device\n"); LOGWARN("rasctl sent a command for invalid controller %d", id); - return FALSE; + + return ReturnStatus(fp, false, "Error : No such device"); } // Does the unit exist? pUnit = disk[id * UnitNum + un]; if (pUnit == NULL) { - FPRT(fp, "Error : No such device\n"); LOGWARN("rasctl sent a command for invalid unit ID %d UN %d", id, un); - return FALSE; - } - type_str[0] = (char)(map[id * UnitNum + un]->GetID() >> 24); - type_str[1] = (char)(map[id * UnitNum + un]->GetID() >> 16); - type_str[2] = (char)(map[id * UnitNum + un]->GetID() >> 8); - type_str[3] = (char)(map[id * UnitNum + un]->GetID()); - type_str[4] = '\0'; + return ReturnStatus(fp, false, "Error : No such device"); + } + + type_str[0] = (char)(pUnit->GetID() >> 24); + type_str[1] = (char)(pUnit->GetID() >> 16); + type_str[2] = (char)(pUnit->GetID() >> 8); + type_str[3] = (char)(pUnit->GetID()); + type_str[4] = '\0'; // Disconnect Command - if (cmd == 1) { // DETACH - type_str[0] = (char)(map[id * UnitNum + un]->GetID() >> 24); - type_str[1] = (char)(map[id * UnitNum + un]->GetID() >> 16); - type_str[2] = (char)(map[id * UnitNum + un]->GetID() >> 8); - type_str[3] = (char)(map[id * UnitNum + un]->GetID()); - type_str[4] = '\0'; + if (cmd == DETACH) { LOGINFO("rasctl command disconnect %s at ID: %d UN: %d", type_str, id, un); + // Free the existing unit map[id * UnitNum + un] = NULL; // Re-map the controller - MapControler(fp, map); - return TRUE; + bool status = MapController(map); + return ReturnStatus(fp, status, status ? "" : "Error : SASI and SCSI can't be mixed\n"); } // Valid only for MO or CD if (pUnit->GetID() != MAKEID('S', 'C', 'M', 'O') && pUnit->GetID() != MAKEID('S', 'C', 'C', 'D')) { - FPRT(fp, "Error : Operation denied (Device type %s isn't removable)\n", type_str); LOGWARN("rasctl sent an Insert/Eject/Protect command (%d) for incompatible type %s", cmd, type_str); - return FALSE; + + ostringstream msg; + msg << "Error : Operation denied (Device type " << type_str << " isn't removable)"; + return ReturnStatus(fp, false, msg.str()); } switch (cmd) { - case 2: // INSERT - // Set the file path - filepath.SetPath(file); - LOGINFO("rasctl commanded insert file %s into %s ID: %d UN: %d", file, type_str, id, un); + case INSERT: + filepath.SetPath(params.c_str()); + LOGINFO("rasctl commanded insert file %s into %s ID: %d UN: %d", params.c_str(), type_str, id, un); - // Open the file if (!pUnit->Open(filepath)) { - FPRT(fp, "Error : File open error [%s]\n", file); - return FALSE; + ostringstream msg; + msg << "Error : File open error [" << params << "]"; + + return ReturnStatus(fp, false, msg.str()); } break; - case 3: // EJECT + case EJECT: LOGINFO("rasctl commands eject %s ID: %d UN: %d", type_str, id, un); pUnit->Eject(TRUE); break; - case 4: // PROTECT + case PROTECT: if (pUnit->GetID() != MAKEID('S', 'C', 'M', 'O')) { - FPRT(fp, "Error : Operation denied(Deveice isn't MO)\n"); LOGWARN("rasctl sent an invalid PROTECT command for %s ID: %d UN: %d", type_str, id, un); - return FALSE; + + return ReturnStatus(fp, false, "Error : Operation denied (Device isn't MO)"); } LOGINFO("rasctl is setting write protect to %d for %s ID: %d UN: %d",!pUnit->IsWriteP(), type_str, id, un); pUnit->WriteP(!pUnit->IsWriteP()); break; + default: - LOGWARN("Received unknown command from rasctl: %d", cmd); - ASSERT(FALSE); - return FALSE; + ostringstream msg; + msg << "Received unknown command from rasctl: " << cmd; + LOGWARN("%s", msg.str().c_str()); + return ReturnStatus(fp, false, msg.str()); } - return TRUE; + return ReturnStatus(fp, true); } -bool has_suffix(const char* string, const char* suffix) { - int string_len = strlen(string); - int suffix_len = strlen(suffix); - return (string_len > suffix_len) - && (xstrcasecmp(string + (string_len - suffix_len), suffix) == 0); +bool has_suffix(const string& filename, const string& suffix) { + return filename.size() >= suffix.size() && !filename.compare(filename.size() - suffix.size(), suffix.size(), suffix); } //--------------------------------------------------------------------------- @@ -698,19 +728,18 @@ bool has_suffix(const char* string, const char* suffix) { #ifdef BAREMETAL BOOL ParseConfig(int argc, char* argv[]) { - FRESULT fr; FIL fp; char line[512]; int id; int un; - int type; + DeviceType type; char *argID; char *argPath; int len; char *ext; // Mount the SD card - fr = f_mount(&fatfs, "", 1); + FRESULT fr = f_mount(&fatfs, "", 1); if (fr != FR_OK) { FPRT(stderr, "Error : SD card mount failed.\n"); return FALSE; @@ -807,12 +836,9 @@ BOOL ParseConfig(int argc, char* argv[]) continue; } - // Initialize device type - type = -1; - // Check ethernet and host bridge - if (xstrcasecmp(argPath, "bridge") == 0) { - type = 4; + if (!strcasecmp(argPath, "bridge")) { + type = BR; } else { // Check the path length len = strlen(argPath); @@ -832,29 +858,24 @@ BOOL ParseConfig(int argc, char* argv[]) // Figure out what the type is ext = &argPath[len - 3]; - if (xstrcasecmp(ext, "hdf") == 0 || - xstrcasecmp(ext, "hds") == 0 || - xstrcasecmp(ext, "hdn") == 0 || - xstrcasecmp(ext, "hdi") == 0 || xstrcasecmp(ext, "nhd") == 0 || - xstrcasecmp(ext, "hda") == 0) { - // HD(SASI/SCSI) - type = 0; - } else if (strcasecmp(ext, "mos") == 0) { - // MO - type = 2; - } else if (strcasecmp(ext, "iso") == 0) { - // CD - type = 3; - } else { - // Cannot determine the file type - FPRT(stderr, - "Error : Invalid argument(file type) [%s]\n", ext); - goto parse_error; + if (!strcasecmp(ext, "hdf") || !strcasecmp(ext, "hds") || !strcasecmp(ext, "hdn") || + !strcasecmp(ext, "hdi") || !strcasecmp(ext, "nhd") || !strcasecmp(ext, "hda")) { + type = SASI_HD; + } else if (!strcasecmp(ext, "mos")) { + type = MO; + } else if (!strcasecmp(ext, "iso")) { + type = CD; } } // Execute the command - if (!ProcessCmd(stderr, id, un, 0, type, argPath)) { + Command command; + command.set_id(id); + command.set_un(un); + command.set_cmd(0); + command.set_type(type); + command.set_file(argPath); + if (!ProcessCmd(stderr, command)) { goto parse_error; } } @@ -863,7 +884,7 @@ BOOL ParseConfig(int argc, char* argv[]) f_close(&fp); // Display the device list - ListDevice(stdout); + fprintf(stdout, "%s", ListDevice().c_str()); return TRUE; @@ -884,33 +905,28 @@ bool ParseArgument(int argc, char* argv[]) int opt; while ((opt = getopt(argc, argv, "-IiHhL:l:D:d:")) != -1) { - switch (opt) { - case 'I': + switch (tolower(opt)) { case 'i': is_sasi = false; max_id = 7; id = -1; continue; - case 'H': case 'h': is_sasi = true; max_id = 15; id = -1; continue; - case 'L': case 'l': log_level = optarg; continue; - case 'D': case 'd': { char* end; id = strtol(optarg, &end, 10); - if (*end || (id < 0) || (max_id < id)) { - fprintf(stderr, "%s: invalid %s (0-%d)\n", - optarg, is_sasi ? "HD" : "ID", max_id); + if (*end || id < 0 || max_id < id) { + cerr << optarg << ": invalid " << (is_sasi ? "HD" : "ID") << " (0-" << max_id << ")" << endl; return false; } continue; @@ -924,35 +940,29 @@ bool ParseArgument(int argc, char* argv[]) } if (id < 0) { - fprintf(stderr, "%s: ID not specified\n", optarg); + cerr << optarg << ": ID not specified" << endl; return false; } else if (disk[id] && !disk[id]->IsNULL()) { - fprintf(stderr, "%d: duplicate ID\n", id); + cerr << id << ": duplicate ID" << endl; return false; } - char* path = optarg; - int type = -1; - if (has_suffix(path, ".hdf") - || has_suffix(path, ".hds") - || has_suffix(path, ".hdn") - || has_suffix(path, ".hdi") - || has_suffix(path, ".hda") - || has_suffix(path, ".nhd")) { - type = rasctl_dev_sasi_hd; + string path = optarg; + DeviceType type = SASI_HD; + if (has_suffix(path, ".hdf") || has_suffix(path, ".hds") || has_suffix(path, ".hdn") + || has_suffix(path, ".hdi") || has_suffix(path, ".hda") || has_suffix(path, ".nhd")) { + type = SASI_HD; } else if (has_suffix(path, ".mos")) { - type = rasctl_dev_mo; + type = MO; } else if (has_suffix(path, ".iso")) { - type = rasctl_dev_cd; - } else if (xstrcasecmp(path, "bridge") == 0) { - type = rasctl_dev_br; - } else if (xstrcasecmp(path, "daynaport") == 0) { - type = rasctl_dev_daynaport; + type = CD; + } else if (path == "bridge") { + type = BR; + } else if (path == "daynaport") { + type = DAYNAPORT; } else { - // Cannot determine the file type or the basename is missing - fprintf(stderr, - "%s: unknown file extension or basename is missing\n", path); - return false; + cerr << path << ": unknown file extension or basename is missing" << endl; + return false; } int un = 0; @@ -962,40 +972,22 @@ bool ParseArgument(int argc, char* argv[]) } // Execute the command - if (!ProcessCmd(stderr, id, un, 0, type, path)) { + Command command; + command.set_id(id); + command.set_un(un); + command.set_cmd(ATTACH); + command.set_type(type); + command.set_params(path); + if (!ProcessCmd(stderr, command)) { return false; } id = -1; } - // Evaluate log level - if (log_level == "trace") { - spdlog::set_level(spdlog::level::trace); - } - else if (log_level == "debug") { - spdlog::set_level(spdlog::level::debug); - } - else if (log_level == "info") { - spdlog::set_level(spdlog::level::info); - } - else if (log_level == "warn") { - spdlog::set_level(spdlog::level::warn); - } - else if (log_level == "err") { - spdlog::set_level(spdlog::level::err); - } - else if (log_level == "critical") { - spdlog::set_level(spdlog::level::critical); - } - else if (log_level == "off") { - spdlog::set_level(spdlog::level::off); - } - else { - cerr << "Invalid log level '" << log_level << "', falling back to 'trace'" << endl; - } + SetLogLevel(log_level); // Display the device list - ListDevice(stdout); + fprintf(stdout, "%s", ListDevice().c_str()); return true; } #endif // BAREMETAL @@ -1008,13 +1000,11 @@ bool ParseArgument(int argc, char* argv[]) //--------------------------------------------------------------------------- void FixCpu(int cpu) { - cpu_set_t cpuset; - int cpus; - // Get the number of CPUs + cpu_set_t cpuset; CPU_ZERO(&cpuset); sched_getaffinity(0, sizeof(cpu_set_t), &cpuset); - cpus = CPU_COUNT(&cpuset); + int cpus = CPU_COUNT(&cpuset); // Set the thread affinity if (cpu < cpus) { @@ -1031,22 +1021,11 @@ void FixCpu(int cpu) //--------------------------------------------------------------------------- static void *MonThread(void *param) { - struct sched_param schedparam; - struct sockaddr_in client; - socklen_t len; int fd; - FILE *fp; - char buf[BUFSIZ]; - char *p; - int i; - char *argv[5]; - int id; - int un; - int cmd; - int type; - char *file; + FILE *fp; - // Scheduler Settings + // Scheduler Settings + struct sched_param schedparam; schedparam.sched_priority = 0; sched_setscheduler(0, SCHED_IDLE, &schedparam); @@ -1058,81 +1037,67 @@ static void *MonThread(void *param) usleep(1); } - // Setup the monitor socket to receive commands + // Set up the monitor socket to receive commands listen(monsocket, 1); - while (1) { - // Wait for connection - memset(&client, 0, sizeof(client)); - len = sizeof(client); - fd = accept(monsocket, (struct sockaddr*)&client, &len); - if (fd < 0) { - break; - } - - // Fetch the command - fp = fdopen(fd, "r+"); - p = fgets(buf, BUFSIZ, fp); - - // Failed to get the command - if (!p) { - goto next; - } - - // Remove the newline character - p[strlen(p) - 1] = 0; - - // List all of the devices - if (xstrncasecmp(p, "list", 4) == 0) { - ListDevice(fp); - goto next; - } - - // Parameter separation - argv[0] = p; - for (i = 1; i < 5; i++) { - // Skip parameter values - while (*p && (*p != ' ')) { - p++; + try { + while (true) { + // Wait for connection + struct sockaddr_in client; + socklen_t socklen = sizeof(client); + memset(&client, 0, socklen); + fd = accept(monsocket, (struct sockaddr*)&client, &socklen); + if (fd < 0) { + throw ioexception("accept() failed"); } - // Replace spaces with null characters - while (*p && (*p == ' ')) { - *p++ = 0; + // Fetch the command + fp = fdopen(fd, "r+"); + if (!fp) { + throw ioexception("fdopen() failed"); } - // The parameters were lost - if (!*p) { - break; + Command command; + command.ParseFromString(DeserializeProtobufData(fd)); + + // List all of the devices + if (command.cmd() == LIST) { + Result result; + result.set_msg(ListDevice() + "\n"); + result.set_status(true); + + string data; + result.SerializeToString(&data); + SerializeProtobufData(fp, data); + } + else if (command.cmd() == LOG_LEVEL) { + SetLogLevel(command.params()); + } + else { + // Wait until we become idle + while (active) { + usleep(500 * 1000); + } + + ProcessCmd(fp, command); } - // Recognized as a parameter - argv[i] = p; + fclose(fp); + fp = NULL; + close(fd); + fd = -1; } + } + catch(const ioexception& e) { + LOGERROR("%s", e.getmsg()); - // Failed to get all parameters - if (i < 5) { - goto next; - } + // Fall through + } - // ID, unit, command, type, file - id = atoi(argv[0]); - un = atoi(argv[1]); - cmd = atoi(argv[2]); - type = atoi(argv[3]); - file = argv[4]; - - // Wait until we becom idle - while (active) { - usleep(500 * 1000); - } - - // Execute the command - ProcessCmd(fp, id, un, cmd, type, file); - -next: - // Release the connection - fclose(fp); + if (fp) { + fclose(fp); + } + if (fd >= 0) { close(fd); } @@ -1156,7 +1121,6 @@ int main(int argc, char* argv[]) { #endif // BAREMETAL int i; - int ret; int actid; DWORD now; BUS::phase_t phase; @@ -1167,15 +1131,15 @@ int main(int argc, char* argv[]) struct sched_param schparam; #endif // BAREMETAL - spdlog::set_level(spdlog::level::trace); + set_level(level::trace); // Create a thread-safe stdout logger to process the log messages - auto logger = spdlog::stdout_color_mt("rascsi stdout logger"); + auto logger = stdout_color_mt("rascsi stdout logger"); // Output the Banner Banner(argc, argv); // Initialize - ret = 0; + int ret = 0; if (!Init()) { ret = EPERM; goto init_exit; diff --git a/src/raspberrypi/rasctl.cpp b/src/raspberrypi/rasctl.cpp index 406b814f..ff7976a5 100644 --- a/src/raspberrypi/rasctl.cpp +++ b/src/raspberrypi/rasctl.cpp @@ -11,51 +11,62 @@ #include "os.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 // //--------------------------------------------------------------------------- -BOOL SendCommand(char *buf) +bool SendCommand(const Command& command) { - int fd; - struct sockaddr_in server; - FILE *fp; - // Create a socket to send the command - fd = socket(PF_INET, SOCK_STREAM, 0); + struct sockaddr_in server; memset(&server, 0, sizeof(server)); server.sin_family = PF_INET; 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) { - fprintf(stderr, "Error : Can't connect to rascsi process\n"); - return FALSE; + if (connect(fd, (struct sockaddr *)&server, sizeof(struct sockaddr_in)) < 0) { + cerr << "Error : Can't connect to rascsi process" << endl; + return false; } // Send the command - fp = fdopen(fd, "r+"); - setvbuf(fp, NULL, _IONBF, 0); - fputs(buf, fp); + FILE *fp = fdopen(fd, "r+"); + + string data; + command.SerializeToString(&data); + SerializeProtobufData(fp, data); // Receive the message - while (1) { - if (fgets((char *)buf, BUFSIZ, fp) == NULL) { - break; - } - printf("%s", buf); - } + bool status = true; + try { + Result result; + result.ParseFromString(DeserializeProtobufData(fd)); + + 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); close(fd); - return TRUE; + return status; } //--------------------------------------------------------------------------- @@ -65,48 +76,34 @@ BOOL SendCommand(char *buf) //--------------------------------------------------------------------------- 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 if (argc < 2) { - fprintf(stderr, "SCSI Target Emulator RaSCSI Controller\n"); - fprintf(stderr, "version %s (%s, %s)\n", - rascsi_get_version_string(), - __DATE__, - __TIME__); - fprintf(stderr, - "Usage: %s -i ID [-u UNIT] [-c CMD] [-t TYPE] [-f FILE]\n", - argv[0]); - fprintf(stderr, " where ID := {0|1|2|3|4|5|6|7}\n"); - fprintf(stderr, " UNIT := {0|1} default setting is 0.\n"); - fprintf(stderr, " CMD := {attach|detach|insert|eject|protect}\n"); - fprintf(stderr, " TYPE := {hd|mo|cd|bridge|daynaport}\n"); - 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"); + cerr << "SCSI Target Emulator RaSCSI Controller" << endl; + cerr << "version " << rascsi_get_version_string() << " (" << __DATE__ << ", " << __TIME__ << ")" << endl; + cerr << "Usage: " << argv[0] << " -i ID [-u UNIT] [-c CMD] [-t TYPE] [-f FILE] [-s LOG_LEVEL]" << endl; + cerr << " where ID := {0|1|2|3|4|5|6|7}" << endl; + cerr << " UNIT := {0|1} default setting is 0." << endl; + cerr << " CMD := {attach|detach|insert|eject|protect}" << endl; + cerr << " TYPE := {hd|mo|cd|bridge|daynaport}" << endl; + cerr << " FILE := image file path" << endl; + cerr << " LOG_LEVEL := log level" << endl; + cerr << " If CMD is 'attach' or 'insert' the FILE parameter is required." << endl; + cerr << "Usage: " << argv[0] << " -l" << endl; + cerr << " Print device list." << endl; + exit(0); } // Parse the arguments + int opt; + int id = -1; + int un = 0; + Operation cmd = LIST; + DeviceType type = UNDEFINED; + string params; 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) { case 'i': id = optarg[0] - '0'; @@ -117,158 +114,142 @@ int main(int argc, char* argv[]) break; case 'c': - switch (optarg[0]) { - case 'a': // ATTACH - case 'A': - cmd = 0; + switch (tolower(optarg[0])) { + case 'a': + cmd = ATTACH; break; - case 'd': // DETACH - case 'D': - cmd = 1; + + case 'd': + cmd = DETACH; break; - case 'i': // INSERT - case 'I': - cmd = 2; + + case 'i': + cmd = INSERT; break; - case 'e': // EJECT - case 'E': - cmd = 3; + + case 'e': + cmd = EJECT; break; - case 'p': // PROTECT - case 'P': - cmd = 4; + + case 'p': + cmd = PROTECT; break; } break; case 't': - switch (optarg[0]) { - case 's': // HD(SASI) - case 'S': - 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; + switch (tolower(optarg[0])) { + case 's': + type = SASI_HD; break; - case 'm': // MO - case 'M': - type = rasctl_dev_mo; + + case 'h': + type = SCSI_HD; break; - case 'c': // CD - case 'C': - type = rasctl_dev_cd; + + case 'm': + type = MO; break; - case 'b': // BRIDGE - case 'B': - type = rasctl_dev_br; + + case 'c': + type = CD; break; - // case 'n': // Nuvolink - // case 'N': - // type = rasctl_dev_nuvolink; + + case 'b': + type = BR; + break; + + // case 'n': + // type = NUVOLINK; // break; - case 'd': // DaynaPort - case 'D': - type = rasctl_dev_daynaport; + + case 'd': + type = DAYNAPORT; break; } break; case 'f': - file = optarg; + params = optarg; break; case 'l': - list = TRUE; + cmd = LIST; + break; + + case 's': + cmd = LOG_LEVEL; + params = optarg; break; } } + Command command; + + if (cmd == LOG_LEVEL) { + command.set_cmd(LOG_LEVEL); + command.set_params(params); + SendCommand(command); + exit(0); + } + // List display only - if (id < 0 && cmd < 0 && type < 0 && file == NULL && list) { - sprintf(buf, "list\n"); - SendCommand(buf); + if (cmd == LIST || (id < 0 && type == UNDEFINED && params.empty())) { + command.set_cmd(LIST); + SendCommand(command); exit(0); } // Check the ID number 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); } // Check the unit number 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); } - // Command check - if (cmd < 0) { - cmd = 0; // Default command is ATTATCH - } - // Type Check - if (cmd == 0 && type < 0) { - + if (cmd == ATTACH && type == UNDEFINED) { // Try to determine the file type from the extension - len = file ? strlen(file) : 0; - if (len > 4 && file[len - 4] == '.') { - ext = &file[len - 3]; - if (xstrcasecmp(ext, "hdf") == 0 || - xstrcasecmp(ext, "hds") == 0 || - xstrcasecmp(ext, "hdn") == 0 || - xstrcasecmp(ext, "hdi") == 0 || - xstrcasecmp(ext, "nhd") == 0 || - xstrcasecmp(ext, "hda") == 0) { - // HD(SASI/SCSI) - type = 0; - } else if (xstrcasecmp(ext, "mos") == 0) { - // MO - type = 2; - } else if (xstrcasecmp(ext, "iso") == 0) { - // CD - type = 3; + int len = params.length(); + if (len > 4 && params[len - 4] == '.') { + string ext = params.substr(len - 3); + if (ext == "hdf" || ext == "hds" || ext == "hdn" || ext == "hdi" || ext == "nhd" || ext == "hda") { + type = SASI_HD; + } else if (ext == "mos") { + type = MO; + } else if (ext == "iso") { + type = CD; } } - - if (type < 0) { - fprintf(stderr, "Error : Invalid type\n"); - exit(EINVAL); - } } - // File check (command is ATTACH and type is HD) - if (cmd == 0 && type >= 0 && type <= 1) { - if (!file) { - fprintf(stderr, "Error : Invalid file path\n"); - exit(EINVAL); - } + // File check (command is ATTACH and type is HD, for CD and MO the medium (=file) may be inserted later) + if (cmd == ATTACH && (type == SASI_HD || type == SCSI_HD) && params.empty()) { + cerr << "Error : Invalid file path" << endl; + exit(EINVAL); } // File check (command is INSERT) - if (cmd == 2) { - if (!file) { - fprintf(stderr, "Error : Invalid file path\n"); - exit(EINVAL); - } - } - - // Set unnecessary type to 0 - if (type < 0) { - type = 0; + if (cmd == INSERT && params.empty()) { + cerr << "Error : Invalid file path" << endl; + exit(EINVAL); } // Generate the command and send it - sprintf(buf, "%d %d %d %d %s\n", id, un, cmd, type, file ? file : "-"); - if (!SendCommand(buf)) { - exit(ENOTCONN); + command.set_id(id); + command.set_un(un); + command.set_cmd(cmd); + command.set_type(type); + if (!params.empty()) { + command.set_params(params); } - - // Display the list - if (list) { - sprintf(buf, "list\n"); - SendCommand(buf); + if (!SendCommand(command)) { + exit(ENOTCONN); } // All done! diff --git a/src/raspberrypi/rasctl.h b/src/raspberrypi/rasctl.h deleted file mode 100644 index a56e95dd..00000000 --- a/src/raspberrypi/rasctl.h +++ /dev/null @@ -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, -}; diff --git a/src/raspberrypi/rasctl_interface.proto b/src/raspberrypi/rasctl_interface.proto new file mode 100644 index 00000000..f53adc90 --- /dev/null +++ b/src/raspberrypi/rasctl_interface.proto @@ -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; +} \ No newline at end of file diff --git a/src/raspberrypi/rasutil.cpp b/src/raspberrypi/rasutil.cpp new file mode 100644 index 00000000..1cadec9a --- /dev/null +++ b/src/raspberrypi/rasutil.cpp @@ -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 +#include +#include +#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; +} diff --git a/src/raspberrypi/rasutil.h b/src/raspberrypi/rasutil.h new file mode 100644 index 00000000..5eed9205 --- /dev/null +++ b/src/raspberrypi/rasutil.h @@ -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 +#include + +void SerializeProtobufData(FILE *fp, const std::string& data); +std::string DeserializeProtobufData(int fd); + +#endif diff --git a/src/raspberrypi/scsi.h b/src/raspberrypi/scsi.h index c0dfde14..c97e73f8 100644 --- a/src/raspberrypi/scsi.h +++ b/src/raspberrypi/scsi.h @@ -23,7 +23,20 @@ class ERROR_CODES public: enum sense_key : int { 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 {