From 0032ce5010eb53a264e066a866e09ca9a9e22171 Mon Sep 17 00:00:00 2001 From: uweseimet <48174652+uweseimet@users.noreply.github.com> Date: Tue, 20 Jul 2021 01:41:00 +0200 Subject: [PATCH] Allow rasctl to connect from a different host (#134) * Set hostname * Added option for hostname * Support connecting from a different host * Squashed commit of the following: commit 6698b8b90a0760102ce9fb30a5ee0656dd46491c Author: akuker <34318535+akuker@users.noreply.github.com> Date: Mon Jul 19 01:19:41 2021 -0500 Remove extraneous carriage return (#135) Co-authored-by: Tony Kuker commit af6e311e6eab40255572d2e55b20ab6edb6871ea Author: uweseimet <48174652+uweseimet@users.noreply.github.com> Date: Mon Jul 19 00:15:13 2021 +0200 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> * Resolved merge conflicts * Updated help message * Re-added CR/LF * Updated error handling * Use fd instead of fp * Removed CR/LF * Comment update * Fixed data types * Data type update * Updated help message * Added new option to usage info, rascsi: use -s instead of -l for consistency * Display name of (remote) host in error message * Fixed buffer overflow, cfilesystem.cpp:1185 assumes size of 11 bytes * Revert "Fixed buffer overflow, cfilesystem.cpp:1185 assumes size of 11 bytes" This reverts commit 126592d411121e0a807c287b867895d9786025e0. --- src/raspberrypi/rascsi.cpp | 96 ++++++++++++++++++------------------- src/raspberrypi/rasctl.cpp | 51 ++++++++++++-------- src/raspberrypi/rasutil.cpp | 24 +++++----- src/raspberrypi/rasutil.h | 2 +- 4 files changed, 94 insertions(+), 79 deletions(-) diff --git a/src/raspberrypi/rascsi.cpp b/src/raspberrypi/rascsi.cpp index 2c2d61c2..b73f87f9 100644 --- a/src/raspberrypi/rascsi.cpp +++ b/src/raspberrypi/rascsi.cpp @@ -104,12 +104,14 @@ void Banner(int argc, char* argv[]) if ((argc > 1 && strcmp(argv[1], "-h") == 0) || (argc > 1 && strcmp(argv[1], "--help") == 0)){ FPRT(stdout,"\n"); - FPRT(stdout,"Usage: %s [-IDn FILE] ...\n\n", argv[0]); + FPRT(stdout,"Usage: %s [-IDn FILE] [-s LOG_LEVEL] ...\n\n", argv[0]); FPRT(stdout," n is SCSI identification number(0-7).\n"); - FPRT(stdout," FILE is disk image file.\n\n"); - FPRT(stdout,"Usage: %s [-HDn FILE] ...\n\n", argv[0]); + FPRT(stdout," FILE is disk image file.\n"); + FPRT(stdout," LOG_LEVEL is {trace|debug|info|warn|err|critical|off}, default is 'trace'\n\n"); + FPRT(stdout,"Usage: %s [-HDn FILE] [-s LOG_LEVEL] ...\n\n", argv[0]); FPRT(stdout," n is X68000 SASI HD number(0-15).\n"); - FPRT(stdout," FILE is disk image file, \"daynaport\", or \"bridge\".\n\n"); + FPRT(stdout," FILE is disk image file, \"daynaport\", or \"bridge\".\n"); + FPRT(stdout," LOG_LEVEL is {trace|debug|info|warn|err|critical|off}, default is 'trace'\n\n"); FPRT(stdout," Image type is detected based on file extension.\n"); FPRT(stdout," hdf : SASI HD image(XM6 SASI HD image)\n"); FPRT(stdout," hds : SCSI HD image(XM6 SCSI HD image)\n"); @@ -144,9 +146,9 @@ BOOL Init() } // Create socket for monitor - monsocket = socket(PF_INET, SOCK_STREAM, 0); + monsocket = socket(AF_INET, SOCK_STREAM, 0); memset(&server, 0, sizeof(server)); - server.sin_family = PF_INET; + server.sin_family = AF_INET; server.sin_port = htons(6868); server.sin_addr.s_addr = htonl(INADDR_ANY); @@ -457,20 +459,27 @@ bool MapController(Disk **map) return status; } -bool ReturnStatus(FILE *fp, bool status = true, const string msg = "") { +bool ReturnStatus(int fd, bool status = true, const string msg = "") { #ifdef BAREMETAL if (msg.length()) { - FPRT(fp, msg.c_str()); - FPRT(fp, "\n"); + FPRT(stderr, msg.c_str()); + FPRT(stderr, "\n"); } #else - Result result; - result.set_msg(msg); - result.set_status(status); + if (fd == -1) { + if (msg.length()) { + FPRT(stderr, msg.c_str()); + FPRT(stderr, "\n"); + } + } + else { + Result result; + result.set_status(status); - string data; - result.SerializeToString(&data); - SerializeProtobufData(fp, data); + string data; + result.SerializeToString(&data); + SerializeProtobufData(fd, data); + } #endif return status; @@ -506,10 +515,10 @@ void SetLogLevel(const string& log_level) { //--------------------------------------------------------------------------- // -// Command Processing +// Command Processing. If fd is -1 error messages are displayed on the console. // //--------------------------------------------------------------------------- -bool ProcessCmd(FILE *fp, const Command &command) +bool ProcessCmd(int fd, const Command &command) { Disk *map[CtrlMax * UnitNum]; Filepath filepath; @@ -531,12 +540,12 @@ bool ProcessCmd(FILE *fp, const Command &command) // Check the Controller Number if (id < 0 || id >= CtrlMax) { - return ReturnStatus(fp, false, "Error : Invalid ID"); + return ReturnStatus(fd, false, "Error : Invalid ID"); } // Check the Unit Number if (un < 0 || un >= UnitNum) { - return ReturnStatus(fp, false, "Error : Invalid unit number"); + return ReturnStatus(fd, false, "Error : Invalid unit number"); } // Connect Command @@ -548,7 +557,7 @@ bool ProcessCmd(FILE *fp, const Command &command) // Check the extension int len = params.length(); if (len < 5 || params[len - 4] != '.') { - return ReturnStatus(fp, false); + return ReturnStatus(fd, false); } // If the extension is not SASI type, replace with SCSI @@ -591,7 +600,7 @@ bool ProcessCmd(FILE *fp, const Command &command) default: ostringstream msg; msg << "rasctl sent a command for an invalid drive type: " << type; - return ReturnStatus(fp, false, msg.str()); + return ReturnStatus(fd, false, msg.str()); } // drive checks files @@ -610,7 +619,7 @@ bool ProcessCmd(FILE *fp, const Command &command) ostringstream msg; msg << "Error : File open error [" << file << "]"; - return ReturnStatus(fp, false, msg.str()); + return ReturnStatus(fd, false, msg.str()); } } @@ -632,14 +641,14 @@ bool ProcessCmd(FILE *fp, const Command &command) 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"); + return ReturnStatus(fd, status, status ? "" : "Error : SASI and SCSI can't be mixed\n"); } // Does the controller exist? if (ctrl[id] == NULL) { LOGWARN("rasctl sent a command for invalid controller %d", id); - return ReturnStatus(fp, false, "Error : No such device"); + return ReturnStatus(fd, false, "Error : No such device"); } // Does the unit exist? @@ -647,7 +656,7 @@ bool ProcessCmd(FILE *fp, const Command &command) if (pUnit == NULL) { LOGWARN("rasctl sent a command for invalid unit ID %d UN %d", id, un); - return ReturnStatus(fp, false, "Error : No such device"); + return ReturnStatus(fd, false, "Error : No such device"); } type_str[0] = (char)(pUnit->GetID() >> 24); @@ -665,7 +674,7 @@ bool ProcessCmd(FILE *fp, const Command &command) // Re-map the controller bool status = MapController(map); - return ReturnStatus(fp, status, status ? "" : "Error : SASI and SCSI can't be mixed\n"); + return ReturnStatus(fd, status, status ? "" : "Error : SASI and SCSI can't be mixed\n"); } // Valid only for MO or CD @@ -675,7 +684,7 @@ bool ProcessCmd(FILE *fp, const Command &command) ostringstream msg; msg << "Error : Operation denied (Device type " << type_str << " isn't removable)"; - return ReturnStatus(fp, false, msg.str()); + return ReturnStatus(fd, false, msg.str()); } switch (cmd) { @@ -687,7 +696,7 @@ bool ProcessCmd(FILE *fp, const Command &command) ostringstream msg; msg << "Error : File open error [" << params << "]"; - return ReturnStatus(fp, false, msg.str()); + return ReturnStatus(fd, false, msg.str()); } break; @@ -700,7 +709,7 @@ bool ProcessCmd(FILE *fp, const Command &command) if (pUnit->GetID() != MAKEID('S', 'C', 'M', 'O')) { LOGWARN("rasctl sent an invalid PROTECT command for %s ID: %d UN: %d", type_str, id, un); - return ReturnStatus(fp, false, "Error : Operation denied (Device isn't MO)"); + return ReturnStatus(fd, 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()); @@ -710,10 +719,10 @@ bool ProcessCmd(FILE *fp, const Command &command) ostringstream msg; msg << "Received unknown command from rasctl: " << cmd; LOGWARN("%s", msg.str().c_str()); - return ReturnStatus(fp, false, msg.str()); + return ReturnStatus(fd, false, msg.str()); } - return ReturnStatus(fp, true); + return ReturnStatus(fd, true); } bool has_suffix(const string& filename, const string& suffix) { @@ -875,7 +884,7 @@ BOOL ParseConfig(int argc, char* argv[]) command.set_cmd(0); command.set_type(type); command.set_file(argPath); - if (!ProcessCmd(stderr, command)) { + if (!ProcessCmd(-1, command)) { goto parse_error; } } @@ -904,7 +913,7 @@ bool ParseArgument(int argc, char* argv[]) string log_level = "trace"; int opt; - while ((opt = getopt(argc, argv, "-IiHhL:l:D:d:")) != -1) { + while ((opt = getopt(argc, argv, "-IiHhL:s:D:d:")) != -1) { switch (tolower(opt)) { case 'i': is_sasi = false; @@ -918,7 +927,7 @@ bool ParseArgument(int argc, char* argv[]) id = -1; continue; - case 'l': + case 's': log_level = optarg; continue; @@ -978,7 +987,7 @@ bool ParseArgument(int argc, char* argv[]) command.set_cmd(ATTACH); command.set_type(type); command.set_params(path); - if (!ProcessCmd(stderr, command)) { + if (!ProcessCmd(-1, command)) { return false; } id = -1; @@ -1022,7 +1031,6 @@ void FixCpu(int cpu) static void *MonThread(void *param) { int fd; - FILE *fp; // Scheduler Settings struct sched_param schedparam; @@ -1052,11 +1060,6 @@ static void *MonThread(void *param) } // Fetch the command - fp = fdopen(fd, "r+"); - if (!fp) { - throw ioexception("fdopen() failed"); - } - Command command; command.ParseFromString(DeserializeProtobufData(fd)); @@ -1068,7 +1071,7 @@ static void *MonThread(void *param) string data; result.SerializeToString(&data); - SerializeProtobufData(fp, data); + SerializeProtobufData(fd, data); } else if (command.cmd() == LOG_LEVEL) { SetLogLevel(command.params()); @@ -1079,11 +1082,9 @@ static void *MonThread(void *param) usleep(500 * 1000); } - ProcessCmd(fp, command); + ProcessCmd(fd, command); } - fclose(fp); - fp = NULL; close(fd); fd = -1; } @@ -1094,9 +1095,6 @@ static void *MonThread(void *param) // Fall through } - if (fp) { - fclose(fp); - } if (fd >= 0) { close(fd); } @@ -1120,6 +1118,8 @@ int startrascsi(void) int main(int argc, char* argv[]) { #endif // BAREMETAL + GOOGLE_PROTOBUF_VERIFY_VERSION; + int i; int actid; DWORD now; diff --git a/src/raspberrypi/rasctl.cpp b/src/raspberrypi/rasctl.cpp index ff7976a5..fb0a03ef 100644 --- a/src/raspberrypi/rasctl.cpp +++ b/src/raspberrypi/rasctl.cpp @@ -9,6 +9,7 @@ // //--------------------------------------------------------------------------- +#include #include "os.h" #include "rascsi_version.h" #include "exceptions.h" @@ -23,32 +24,38 @@ using namespace rasctl_interface; // Send Command // //--------------------------------------------------------------------------- -bool SendCommand(const Command& command) +BOOL SendCommand(const char *hostname, const Command& command) { // Create a socket to send the command + int fd = socket(AF_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_family = AF_INET; + server.sin_port = htons(6868); server.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - int fd = socket(PF_INET, SOCK_STREAM, 0); + struct hostent *host = gethostbyname(hostname); + if(!host) { + fprintf(stderr, "Error : Can't resolve hostname '%s'\n", hostname); + return false; + } + memcpy((char *)&server.sin_addr.s_addr, (char *)host->h_addr, host->h_length); + + // Connect if (connect(fd, (struct sockaddr *)&server, sizeof(struct sockaddr_in)) < 0) { - cerr << "Error : Can't connect to rascsi process" << endl; + fprintf(stderr, "Error : Can't connect to rascsi process on host '%s'\n", hostname); return false; } - // Send the command - FILE *fp = fdopen(fd, "r+"); - string data; command.SerializeToString(&data); - SerializeProtobufData(fp, data); // Receive the message bool status = true; try { - Result result; + SerializeProtobufData(fd, data); + + Result result; result.ParseFromString(DeserializeProtobufData(fd)); status = result.status(); @@ -63,8 +70,7 @@ bool SendCommand(const Command& command) // Fall through } - fclose(fp); - close(fd); + close(fd); return status; } @@ -76,17 +82,20 @@ bool SendCommand(const Command& command) //--------------------------------------------------------------------------- int main(int argc, char* argv[]) { + GOOGLE_PROTOBUF_VERIFY_VERSION; + // Display help if (argc < 2) { 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 << "Usage: " << argv[0] << " -i ID [-u UNIT] [-c CMD] [-t TYPE] [-f FILE] [-h HOSTNAME] [-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 << " HOSTNAME := rascsi host to connect to, default is 'localhost'" << endl; + cerr << " LOG_LEVEL := log level {trace|debug|info|warn|err|critical|off}, default is 'trace'" << 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; @@ -100,10 +109,10 @@ int main(int argc, char* argv[]) int un = 0; Operation cmd = LIST; DeviceType type = UNDEFINED; + const char *hostname = "localhost"; string params; opterr = 0; - - while ((opt = getopt(argc, argv, "i:u:c:t:f:s:l")) != -1) { + while ((opt = getopt(argc, argv, "i:u:c:t:f:h:s:l")) != -1) { switch (opt) { case 'i': id = optarg[0] - '0'; @@ -177,6 +186,10 @@ int main(int argc, char* argv[]) cmd = LIST; break; + case 'h': + hostname = optarg; + break; + case 's': cmd = LOG_LEVEL; params = optarg; @@ -189,14 +202,14 @@ int main(int argc, char* argv[]) if (cmd == LOG_LEVEL) { command.set_cmd(LOG_LEVEL); command.set_params(params); - SendCommand(command); + SendCommand(hostname, command); exit(0); } // List display only if (cmd == LIST || (id < 0 && type == UNDEFINED && params.empty())) { command.set_cmd(LIST); - SendCommand(command); + SendCommand(hostname, command); exit(0); } @@ -248,7 +261,7 @@ int main(int argc, char* argv[]) if (!params.empty()) { command.set_params(params); } - if (!SendCommand(command)) { + if (!SendCommand(hostname, command)) { exit(ENOTCONN); } diff --git a/src/raspberrypi/rasutil.cpp b/src/raspberrypi/rasutil.cpp index 1cadec9a..654d5567 100644 --- a/src/raspberrypi/rasutil.cpp +++ b/src/raspberrypi/rasutil.cpp @@ -10,7 +10,6 @@ //--------------------------------------------------------------------------- #include -#include #include #include "exceptions.h" #include "rasutil.h" @@ -23,17 +22,22 @@ using namespace std; // //--------------------------------------------------------------------------- -void SerializeProtobufData(FILE *fp, const string& data) +void SerializeProtobufData(int fd, const string& data) { // Write the size of the protobuf data as a header - size_t size = data.length(); - fwrite(&size, sizeof(size), 1, fp); + int32_t size = data.length(); + if (write(fd, &size, sizeof(size)) != sizeof(size)) { + throw ioexception("Cannot write protobuf header"); + } // Write the actual protobuf data void *buf = malloc(size); memcpy(buf, data.data(), size); - fwrite(buf, size, 1, fp); - fflush(fp); + if (write(fd, buf, size) != size) { + free(buf); + + throw ioexception("Cannot write protobuf data"); + } free(buf); } @@ -41,17 +45,15 @@ void SerializeProtobufData(FILE *fp, const string& data) 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)) { + int32_t size; + if (read(fd, &size, sizeof(size)) != sizeof(size)) { // No more data return ""; } // Read the actual protobuf data void *buf = malloc(size); - res = read(fd, buf, size); - if (res != size) { + if (read(fd, buf, size) != (ssize_t)size) { free(buf); throw ioexception("Missing protobuf data"); diff --git a/src/raspberrypi/rasutil.h b/src/raspberrypi/rasutil.h index 5eed9205..42dd70cd 100644 --- a/src/raspberrypi/rasutil.h +++ b/src/raspberrypi/rasutil.h @@ -15,7 +15,7 @@ #include #include -void SerializeProtobufData(FILE *fp, const std::string& data); +void SerializeProtobufData(int fd, const std::string& data); std::string DeserializeProtobufData(int fd); #endif