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 <akuker@gmail.com>

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.
This commit is contained in:
uweseimet 2021-07-20 01:41:00 +02:00 committed by GitHub
parent 6698b8b90a
commit 0032ce5010
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 94 additions and 79 deletions

View File

@ -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;

View File

@ -9,6 +9,7 @@
//
//---------------------------------------------------------------------------
#include <netdb.h>
#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);
}

View File

@ -10,7 +10,6 @@
//---------------------------------------------------------------------------
#include <cstring>
#include <cstdlib>
#include <unistd.h>
#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");

View File

@ -15,7 +15,7 @@
#include <cstdio>
#include <string>
void SerializeProtobufData(FILE *fp, const std::string& data);
void SerializeProtobufData(int fd, const std::string& data);
std::string DeserializeProtobufData(int fd);
#endif