Feature device list (#146)

* Removed all BAREMETAL ifdefs

* Cleaned up subfolders

* Fixed regression

* Re-added underscores

* Fixed merge conflicts

* Assume RASCSI is always defined

* Fixed mege issue

* Re-added result message

* Added Devices message to protobuf interface

* Fixed typo

* Fixed include file names

* Updated files to be ignored by git

* Merge with develop branch

* Synchronized output with develop branch

* Fixed missing dependency, causing issues when running "make -j1"

* Fixed handling of connection errors

* Improved protobuf interface upwards compatibility

* Use -g instead of -s, this has less conflict potential with future options

* Made log level options consistent (rascsi and rasctl used a different option)

* Serialization improvements (#142)

* Simplified serialization, extracted sending commands

* Simplified serialization

* Fixed warning

* Signature update

* Use Pb prefix for protobuf interface messages

* Moved ListDevices to rasutil

* Log device list

* Split initialization

* Moved code
This commit is contained in:
Uwe Seimet 2021-07-24 02:13:05 +02:00 committed by GitHub
parent 845d249d9b
commit 863feed611
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 203 additions and 203 deletions

View File

@ -13,8 +13,11 @@
#define exceptions_h
#include <exception>
#include <string>
class lunexception : public std::exception {
using namespace std;
class lunexception final : public exception {
private:
int lun;
@ -28,16 +31,16 @@ public:
}
};
class ioexception : public std::exception {
class ioexception : public exception {
private:
const char *msg;
string msg;
public:
ioexception(const char *_msg) : msg(_msg) { }
ioexception(const string& _msg) : msg(_msg) { }
~ioexception() { }
const char *getmsg() const {
const string& getmsg() const {
return msg;
}
};

View File

@ -119,7 +119,8 @@ void Banner(int argc, char* argv[])
// Initialization
//
//---------------------------------------------------------------------------
BOOL Init(int port)
BOOL InitService(int port)
{
struct sockaddr_in server;
int yes, result;
@ -165,6 +166,13 @@ BOOL Init(int port)
return FALSE;
}
running = FALSE;
active = FALSE;
return true;
}
bool InitBusAndDisks() {
// GPIOBUS creation
bus = new GPIOBUS();
@ -186,10 +194,6 @@ BOOL Init(int port)
disk[i] = NULL;
}
// Other
running = FALSE;
active = FALSE;
return TRUE;
}
@ -253,8 +257,8 @@ void Reset()
// Get the list of attached devices
//
//---------------------------------------------------------------------------
Devices GetDevices() {
Devices devices;
PbDevices GetDevices() {
PbDevices devices;
for (int i = 0; i < CtrlMax * UnitNum; i++) {
// skip if unit does not exist or null disk
@ -263,7 +267,7 @@ Devices GetDevices() {
continue;
}
Device *device = devices.add_devices();
PbDevice *device = devices.add_devices();
// Initialize ID and unit number
device->set_id(i / UnitNum);
@ -298,45 +302,6 @@ Devices GetDevices() {
return devices;
}
//---------------------------------------------------------------------------
//
// List and log devices
//
//---------------------------------------------------------------------------
string ListDevices(Devices devices) {
ostringstream s;
if (devices.devices_size()) {
s << endl
<< "+----+----+------+-------------------------------------" << endl
<< "| ID | UN | TYPE | DEVICE STATUS" << endl
<< "+----+----+------+-------------------------------------" << endl;
LOGINFO( "+----+----+------+-------------------------------------");
LOGINFO( "| ID | UN | TYPE | DEVICE STATUS");
LOGINFO( "+----+----+------+-------------------------------------\n");
}
else {
return "No images currently attached.\n";
}
for (int i = 0; i < devices.devices_size() ; i++) {
Device device = devices.devices(i);
s << "| " << device.id() << " | " << device.un() << " | " << device.type() << " | "
<< device.file() << (device.read_only() ? " (WRITEPROTECT)" : "") << endl;
LOGINFO( "| %d | %d | %s | %s%s", device.id(), device.un(), device.type().c_str(),
device.file().c_str(), device.read_only() ? " (WRITEPROTECT)" : "");
}
s << "+----+----+------+-------------------------------------" << endl;
LOGINFO( "+----+----+------+-------------------------------------");
return s.str();
}
//---------------------------------------------------------------------------
//
// Controller Mapping
@ -462,13 +427,10 @@ bool ReturnStatus(int fd, bool status = true, const string msg = "") {
}
}
else {
Result result;
PbResult result;
result.set_status(status);
result.set_msg(msg + "\n");
string data;
result.SerializeToString(&data);
SerializeProtobufData(fd, data);
SerializeMessage(fd, result);
}
return status;
@ -502,12 +464,22 @@ void SetLogLevel(const string& log_level) {
}
}
void LogDeviceList(const string& device_list)
{
stringstream ss(device_list);
string line;
while (getline(ss, line, '\n')) {
LOGINFO("%s", line.c_str());
}
}
//---------------------------------------------------------------------------
//
// Command Processing
//
//---------------------------------------------------------------------------
bool ProcessCmd(int fd, const Command &command)
bool ProcessCmd(int fd, const PbCommand &command)
{
Disk *map[CtrlMax * UnitNum];
Filepath filepath;
@ -516,8 +488,8 @@ bool ProcessCmd(int fd, const Command &command)
int id = command.id();
int un = command.un();
Operation cmd = command.cmd();
DeviceType type = command.type();
PbOperation cmd = command.cmd();
PbDeviceType type = command.type();
string params = command.params().c_str();
ostringstream s;
@ -783,7 +755,7 @@ bool ParseArgument(int argc, char* argv[], int& port)
}
string path = optarg;
DeviceType type = SASI_HD;
PbDeviceType 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;
@ -807,7 +779,7 @@ bool ParseArgument(int argc, char* argv[], int& port)
}
// Execute the command
Command command;
PbCommand command;
command.set_id(id);
command.set_un(un);
command.set_cmd(ATTACH);
@ -821,9 +793,11 @@ bool ParseArgument(int argc, char* argv[], int& port)
SetLogLevel(log_level);
// Display the device list
Devices devices = GetDevices();
cout << ListDevices(devices) << endl;
// Display and log the device list
const PbDevices devices = GetDevices();
const string device_list = ListDevices(devices);
cout << device_list << endl;
LogDeviceList(device_list);
return true;
}
@ -886,17 +860,14 @@ static void *MonThread(void *param)
}
// Fetch the command
Command command;
command.ParseFromString(DeserializeProtobufData(fd));
PbCommand command;
DeserializeMessage(fd, command);
// List all of the devices
// List and log all of the devices
if (command.cmd() == LIST) {
Devices devices = GetDevices();
ListDevices(devices);
string data;
devices.SerializeToString(&data);
SerializeProtobufData(fd, data);
const PbDevices devices = GetDevices();
SerializeMessage(fd, devices);
LogDeviceList(ListDevices(devices));
}
else if (command.cmd() == LOG_LEVEL) {
SetLogLevel(command.params());
@ -915,7 +886,7 @@ static void *MonThread(void *param)
}
}
catch(const ioexception& e) {
LOGERROR("%s", e.getmsg());
LOGERROR("%s", e.getmsg().c_str());
// Fall through
}
@ -952,16 +923,20 @@ int main(int argc, char* argv[])
// Output the Banner
Banner(argc, argv);
// Argument parsing
int ret = 0;
int port = 6868;
if (!InitBusAndDisks()) {
ret = EPERM;
goto init_exit;
}
if (!ParseArgument(argc, argv, port)) {
ret = EINVAL;
goto err_exit;
}
// Initialize
if (!Init(port)) {
if (!InitService(port)) {
ret = EPERM;
goto init_exit;
}

View File

@ -5,7 +5,7 @@ option optimize_for = LITE_RUNTIME;
package rascsi_interface;
// The supported device types
enum DeviceType {
enum PbDeviceType {
UNDEFINED = 0;
SASI_HD = 1;
SCSI_HD = 2;
@ -17,7 +17,7 @@ enum DeviceType {
}
// rascsi remote operations
enum Operation {
enum PbOperation {
NONE = 0;
LIST = 1;
ATTACH = 2;
@ -29,22 +29,22 @@ enum Operation {
}
// Commands rascsi can execute
message Command {
Operation cmd = 1;
message PbCommand {
PbOperation cmd = 1;
int32 id = 2;
int32 un = 3;
DeviceType type = 4;
PbDeviceType type = 4;
string params = 5;
}
// The result of a command
message Result {
message PbResult {
bool status = 1;
string msg = 2;
}
// The device meta data
message Device {
message PbDevice {
int32 id = 1;
int32 un = 2;
string type = 3;
@ -52,6 +52,6 @@ message Device {
bool read_only = 5;
}
message Devices {
repeated Device devices = 1;
message PbDevices {
repeated PbDevice devices = 1;
}

View File

@ -10,6 +10,7 @@
//---------------------------------------------------------------------------
#include <netdb.h>
#include "google/protobuf/message_lite.h"
#include "os.h"
#include "rascsi_version.h"
#include "exceptions.h"
@ -26,44 +27,45 @@ using namespace rascsi_interface;
// Send Command
//
//---------------------------------------------------------------------------
int SendCommand(const char *hostname, int port, const Command& command)
int SendCommand(const string& hostname, const PbCommand& 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 = AF_INET;
server.sin_port = htons(port);
server.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
int fd;
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);
try {
struct hostent *host = gethostbyname(hostname.c_str());
if (!host) {
throw ioexception("Can't resolve hostname '" + hostname + "'");
}
// Connect
if (connect(fd, (struct sockaddr *)&server, sizeof(struct sockaddr_in)) < 0) {
cerr << "Error: Can't connect to rascsi process on '" << hostname << ":" << port << "'" << endl;
return -1;
}
fd = socket(AF_INET, SOCK_STREAM, 0);
if (fd < 0) {
throw ioexception("Can't create socket");
}
string data;
command.SerializeToString(&data);
struct sockaddr_in server;
memset(&server, 0, sizeof(server));
server.sin_family = AF_INET;
server.sin_port = htons(6868);
server.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
memcpy(&server.sin_addr.s_addr, host->h_addr, host->h_length);
try {
SerializeProtobufData(fd, data);
if (connect(fd, (struct sockaddr *)&server, sizeof(struct sockaddr_in)) < 0) {
throw ioexception("Can't connect to rascsi process on host '" + hostname + "'");
}
SerializeMessage(fd, command);
}
catch(const ioexception& e) {
cerr << "Error : " << e.getmsg() << endl;
cerr << "Error: " << e.getmsg() << endl;
close(fd);
if (fd >= 0) {
close(fd);
}
return -1;
return -1;
}
return fd;
return fd;
}
//---------------------------------------------------------------------------
@ -73,11 +75,12 @@ int SendCommand(const char *hostname, int port, const Command& command)
//---------------------------------------------------------------------------
bool ReceiveResult(int fd) {
bool status = true;
try {
Result result;
result.ParseFromString(DeserializeProtobufData(fd));
status = result.status();
try {
PbResult result;
DeserializeMessage(fd, result);
status = result.status();
if (status) {
cerr << result.msg();
}
@ -98,29 +101,53 @@ bool ReceiveResult(int fd) {
return status;
}
string ListDevices(const Devices& devices) {
ostringstream s;
//---------------------------------------------------------------------------
//
// Command implementations
//
//---------------------------------------------------------------------------
if (devices.devices_size()) {
s << endl
<< "+----+----+------+-------------------------------------" << endl
<< "| ID | UN | TYPE | DEVICE STATUS" << endl
<< "+----+----+------+-------------------------------------" << endl;
}
else {
return "No images currently attached.\n";
void CommandList(const string& hostname)
{
PbCommand command;
command.set_cmd(LIST);
int fd = SendCommand(hostname.c_str(), command);
if (fd < 0) {
exit(ENOTCONN);
}
for (int i = 0; i < devices.devices_size() ; i++) {
Device device = devices.devices(i);
PbDevices devices;
try {
DeserializeMessage(fd, devices);
}
catch(const ioexception& e) {
cerr << "Error: " << e.getmsg() << endl;
s << "| " << device.id() << " | " << device.un() << " | " << device.type() << " | "
<< device.file() << (device.read_only() ? " (WRITEPROTECT)" : "") << endl;
close(fd);
exit(-1);
}
s << "+----+----+------+-------------------------------------" << endl;
close (fd);
return s.str();
cout << ListDevices(devices) << endl;
}
void CommandLogLevel(const string& hostname, const string& log_level)
{
PbCommand command;
command.set_cmd(LOG_LEVEL);
command.set_params(log_level);
int fd = SendCommand(hostname.c_str(), command);
if (fd < 0) {
exit(ENOTCONN);
}
ReceiveResult(fd);
close(fd);
}
//---------------------------------------------------------------------------
@ -136,14 +163,13 @@ int main(int argc, char* argv[])
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] [-h HOSTNAME] [-p PORT] [-g LOG_LEVEL]" << endl;
cerr << "Usage: " << argv[0] << " -i ID [-u UNIT] [-c CMD] [-t TYPE] [-f FILE] [-h HOSTNAME] [-g 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 << " HOSTNAME := rascsi host to connect to, default is 'localhost'" << endl;
cerr << " PORT := rascsi port to connect to, default is 6868" << 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;
@ -156,13 +182,12 @@ int main(int argc, char* argv[])
int opt;
int id = -1;
int un = 0;
Operation cmd = LIST;
DeviceType type = UNDEFINED;
PbOperation cmd = LIST;
PbDeviceType type = UNDEFINED;
const char *hostname = "localhost";
int port = 6868;
string params;
opterr = 0;
while ((opt = getopt(argc, argv, "i:u:c:t:f:h:p:g:l")) != -1) {
while ((opt = getopt(argc, argv, "i:u:c:t:f:h:g:l")) != -1) {
switch (opt) {
case 'i':
id = optarg[0] - '0';
@ -240,14 +265,6 @@ int main(int argc, char* argv[])
hostname = optarg;
break;
case 'p':
port = atoi(optarg);
if (port <= 0 || port > 65535) {
cerr << "Invalid port " << optarg << ", port must be between 1 and 65535" << endl;
exit(-1);
}
break;
case 'g':
cmd = LOG_LEVEL;
params = optarg;
@ -255,44 +272,16 @@ int main(int argc, char* argv[])
}
}
Command command;
PbCommand command;
if (cmd == LOG_LEVEL) {
command.set_cmd(LOG_LEVEL);
command.set_params(params);
int fd = SendCommand(hostname, port, command);
if (fd < 0) {
exit(ENOTCONN);
}
ReceiveResult(fd);
CommandLogLevel(hostname, params);
exit(0);
}
// List display only
if (cmd == LIST || (id < 0 && type == UNDEFINED && params.empty())) {
command.set_cmd(LIST);
int fd = SendCommand(hostname, port, command);
if (fd < 0) {
exit(ENOTCONN);
}
Devices devices;
try {
devices.ParseFromString(DeserializeProtobufData(fd));
}
catch(const ioexception& e) {
cerr << "Error : " << e.getmsg() << endl;
close(fd);
exit(-1);
}
close (fd);
cout << ListDevices(devices) << endl;
CommandList(hostname);
exit(0);
}
@ -345,8 +334,8 @@ int main(int argc, char* argv[])
command.set_params(params);
}
int fd = SendCommand(hostname, port, command);
if (fd == -1) {
int fd = SendCommand(hostname, command);
if (fd < 0) {
exit(ENOTCONN);
}

View File

@ -9,25 +9,30 @@
//
//---------------------------------------------------------------------------
#include <cstring>
#include <unistd.h>
#include <sstream>
#include "rascsi_interface.pb.h"
#include "exceptions.h"
#include "rasutil.h"
using namespace std;
using namespace rascsi_interface;
//---------------------------------------------------------------------------
//
// Serialize/Deserialize protobuf data: Length followed by the actual data
// Serialize/Deserialize protobuf message: Length followed by the actual data
//
//---------------------------------------------------------------------------
void SerializeProtobufData(int fd, const string& data)
void SerializeMessage(int fd, const google::protobuf::MessageLite& message)
{
string data;
message.SerializeToString(&data);
// Write the size of the protobuf data as a header
int32_t size = data.length();
if (write(fd, &size, sizeof(size)) != sizeof(size)) {
throw ioexception("Cannot write protobuf header");
throw ioexception("Can't write protobuf header");
}
// Write the actual protobuf data
@ -36,32 +41,59 @@ void SerializeProtobufData(int fd, const string& data)
if (write(fd, buf, size) != size) {
free(buf);
throw ioexception("Cannot write protobuf data");
throw ioexception("Can't write protobuf data");
}
free(buf);
}
string DeserializeProtobufData(int fd)
void DeserializeMessage(int fd, google::protobuf::MessageLite& message)
{
// First read the header with the size of the protobuf data
int32_t size;
if (read(fd, &size, sizeof(size)) != sizeof(size)) {
// No more data
return "";
}
if (read(fd, &size, sizeof(size)) == sizeof(size)) {
// Read the actual protobuf data
void *buf = malloc(size);
if (read(fd, buf, size) != (ssize_t)size) {
free(buf);
// Read the actual protobuf data
void *buf = malloc(size);
if (read(fd, buf, size) != (ssize_t)size) {
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);
throw ioexception("Missing protobuf data");
message.ParseFromString(data);
}
}
//---------------------------------------------------------------------------
//
// List devices
//
//---------------------------------------------------------------------------
string ListDevices(const PbDevices& devices) {
ostringstream s;
if (devices.devices_size()) {
s << endl
<< "+----+----+------+-------------------------------------" << endl
<< "| ID | UN | TYPE | DEVICE STATUS" << endl
<< "+----+----+------+-------------------------------------" << endl;
}
else {
return "No images currently attached.\n";
}
// 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);
for (int i = 0; i < devices.devices_size() ; i++) {
PbDevice device = devices.devices(i);
return data;
s << "| " << device.id() << " | " << device.un() << " | " << device.type() << " | "
<< device.file() << (device.read_only() ? " (WRITEPROTECT)" : "") << endl;
}
s << "+----+----+------+-------------------------------------" << endl;
return s.str();
}

View File

@ -12,10 +12,11 @@
#if !defined(rasutil_h)
#define rasutil_h
#include <cstdio>
#include <string>
#include "google/protobuf/message_lite.h"
#include "rascsi_interface.pb.h"
void SerializeProtobufData(int fd, const std::string& data);
std::string DeserializeProtobufData(int fd);
void SerializeMessage(int fd, const google::protobuf::MessageLite&);
void DeserializeMessage(int fd, google::protobuf::MessageLite&);
string ListDevices(const rascsi_interface::PbDevices&);
#endif