From a638fec8a895ec95db5be063bfe908c0fdfc1cf3 Mon Sep 17 00:00:00 2001 From: Uwe Seimet <48174652+uweseimet@users.noreply.github.com> Date: Wed, 6 Oct 2021 23:25:43 +0200 Subject: [PATCH] Feature interface granularity (#304) * Added VERSION_INFO * Added LOG_LEVEL_INFO * Added RESERVED_IDS_INFO * Renaming * Renaming * Split rasctl * Code cleanup * Removed unused method * Include cleanup * More cleanup * More cleanup * Simplified sending commands * Fully set up command * Further simplified sending commands * Moved code * Include cleanup * Reject reserving an ID that is in use * Update rascsi-web for the changes in protobuf interface Co-authored-by: Daniel Markstedt --- doc/rasctl.1 | 20 +- doc/rasctl_man_page.txt | 43 +- src/raspberrypi/Makefile | 2 + src/raspberrypi/protobuf_response_helper.cpp | 60 +- src/raspberrypi/protobuf_response_helper.h | 4 +- src/raspberrypi/protobuf_util.h | 2 + src/raspberrypi/rascsi.cpp | 60 +- src/raspberrypi/rascsi_interface.proto | 140 ++-- src/raspberrypi/rasctl.cpp | 699 +++---------------- src/raspberrypi/rasctl_commands.cpp | 270 +++++++ src/raspberrypi/rasctl_commands.h | 55 ++ src/raspberrypi/rasctl_display.cpp | 268 +++++++ src/raspberrypi/rasctl_display.h | 33 + src/web/ractl_cmds.py | 38 +- 14 files changed, 983 insertions(+), 711 deletions(-) create mode 100644 src/raspberrypi/rasctl_commands.cpp create mode 100644 src/raspberrypi/rasctl_commands.h create mode 100644 src/raspberrypi/rasctl_display.cpp create mode 100644 src/raspberrypi/rasctl_display.h diff --git a/doc/rasctl.1 b/doc/rasctl.1 index 8aac071c..d07af057 100644 --- a/doc/rasctl.1 +++ b/doc/rasctl.1 @@ -3,12 +3,16 @@ rasctl \- Sends management commands to the rascsi process .SH SYNOPSIS .B rasctl -\fB\-L\fR | \fB\-e\fR | \fB\-l\fR | \fB\-m\fR | \fB\-s\fR | +\fB\-v\fR | +\fB\-I\fR | +\fB\-L\fR | +\fB\-O\fR | \fB\-T\fR | +\fB\-V\fR | [\fB\-E\fR \fIFILENAME\fR] [\fB\-F\fR \fIIMAGE_FOLDER\fR] [\fB\-C\fR \fIFILENAME:FILESIZE\fR] @@ -18,8 +22,7 @@ rasctl \- Sends management commands to the rascsi process [\fB\-h\fR \fIHOST\fR] [\fB\-p\fR \fIPORT\fR] [\fB\-r\fR \fIRESERVED_IDS\fR] -[\fB\-v\fR] -\fB\-i\fR \fIID\fR +[\fB\-i\fR \fIID\fR [\fB\-c\fR \fICMD\fR] [\fB\-f\fR \fIFILE|PARAM\fR] [\fB\-n\fR \fINAME\fR] @@ -46,6 +49,9 @@ Display information on an image file. .BR \-F\fI " "\fIIMAGE_FOLDER Set the default image folder. .TP +.BR \-I\fI +Gets the list of reserved device IDs. +.TP .BR \-L\fI " "\fILOG_LEVEL Set the rascsi log level (trace, debug, info, warn, err, critical, off). .TP @@ -58,6 +64,9 @@ List all images files in the default image folder. .BR \-N\fI Lists all available network interfaces provided that they are up. .TP +.BR \-O\fI +Display the available rascsi server log levels and the current log level. +.TP .BR \-l\fI List all of the devices that are currently being emulated by RaSCSI, as well as their current status. .TP @@ -80,7 +89,10 @@ Display server-side settings like available images or supported device types. Display all device types and their properties. .TP .BR \-v\fI " " \fI -Display the rascsi version. +Display the rascsi server version. +.TP +.BR \-V\fI " " \fI +Display the rasctl version. .TP .BR \-D\fI " "\fIFILENAME Delete an image file in the default image folder. diff --git a/doc/rasctl_man_page.txt b/doc/rasctl_man_page.txt index 0a47e907..4eecfbb8 100644 --- a/doc/rasctl_man_page.txt +++ b/doc/rasctl_man_page.txt @@ -6,10 +6,10 @@ NAME rasctl - Sends management commands to the rascsi process SYNOPSIS - rasctl -L | -e | -l | -m | -s | -T | [-E FILENAME] [-F IMAGE_FOLDER] - [-C FILENAME:FILESIZE] [-x CURRENT_NAME:NEW_NAME] [-R CUR‐ - RENT_NAME:NEW_NAME] [-g LOG_LEVEL] [-h HOST] [-p PORT] [-r RE‐ - SERVED_IDS] [-v] -i ID [-c CMD] [-f FILE|PARAM] [-n NAME] [-t TYPE] [-u + rasctl -e | -l | -m | -s | -v | -I | -L | -O | -T | -V | [-E FILENAME] + [-F IMAGE_FOLDER] [-C FILENAME:FILESIZE] [-x CURRENT_NAME:NEW_NAME] [-R + CURRENT_NAME:NEW_NAME] [-g LOG_LEVEL] [-h HOST] [-p PORT] [-r RE‐ + SERVED_IDS] [-i ID [-c CMD] [-f FILE|PARAM] [-n NAME] [-t TYPE] [-u UNIT] DESCRIPTION @@ -34,6 +34,8 @@ OPTIONS -F IMAGE_FOLDER Set the default image folder. + -I Gets the list of reserved device IDs. + -L LOG_LEVEL Set the rascsi log level (trace, debug, info, warn, err, criti‐ cal, off). @@ -46,10 +48,13 @@ OPTIONS -N Lists all available network interfaces provided that they are up. - -l List all of the devices that are currently being emulated by + -O Display the available rascsi server log levels and the current + log level. + + -l List all of the devices that are currently being emulated by RaSCSI, as well as their current status. - -m List all file extensions recognized by RaSCSI and the device + -m List all file extensions recognized by RaSCSI and the device types they map to. -R CURRENT_NAME:NEW_NAME @@ -61,12 +66,14 @@ OPTIONS -r RESERVED_IDS Comma-separated list of IDs to reserve. - -s Display server-side settings like available images or supported + -s Display server-side settings like available images or supported device types. -T Display all device types and their properties. - -v Display the rascsi version. + -v Display the rascsi server version. + + -V Display the rasctl version. -D FILENAME Delete an image file in the default image folder. @@ -81,7 +88,7 @@ OPTIONS d(etach): Detach disk i(nsert): Insert media (removable media devices only) e(ject): Eject media (removable media devices only) - p(rotect): Write protect the medium (not for CD-ROMs, which + p(rotect): Write protect the medium (not for CD-ROMs, which are always read-only) u(nprotect): Remove write protection from the medium (not for CD-ROMs, which are always read-only) @@ -91,18 +98,18 @@ OPTIONS -b BLOCK_SIZE The optional block size. For SCSI drives 512, 1024, 2048 or 4096 - bytes, default size is 512 bytes. For SASI drives 256 or 1024 + bytes, default size is 512 bytes. For SASI drives 256 or 1024 bytes, default is 256 bytes. -f FILE|PARAM Device-specific: Either a path to a disk image file, or a param‐ - eter for a non-disk device. See the rascsi(1) man page for per‐ + eter for a non-disk device. See the rascsi(1) man page for per‐ mitted file types. -t TYPE - Specifies the device type. This type overrides the type derived + Specifies the device type. This type overrides the type derived from the file extension of the specified image. See the - rascsi(1) man page for the available device types. For some + rascsi(1) man page for the available device types. For some types there are shortcuts (only the first letter is required): hd: SCSI hard disk drive rm: SCSI removable media drive @@ -112,16 +119,16 @@ OPTIONS daynaport: DaynaPORT network adapter -n VENDOR:PRODUCT:REVISION - The vendor, product and revision for the device, to be returned + The vendor, product and revision for the device, to be returned with the INQUIRY data. A complete set of name components must be provided. VENDOR may have up to 8, PRODUCT up to 16, REVISION up to 4 characters. Padding with blanks to the maxium length is au‐ - tomatically applied. Once set the name of a device cannot be + tomatically applied. Once set the name of a device cannot be changed. -u UNIT - Unit number (0 or 1). This will default to 0. This option is - only used when there are multiple SCSI devices on a shared SCSI + Unit number (0 or 1). This will default to 0. This option is + only used when there are multiple SCSI devices on a shared SCSI controller. (This is not common) EXAMPLES @@ -140,7 +147,7 @@ EXAMPLES rasctl -i 0 -f HDIIMAGE0.HDS SEE ALSO - rascsi(1) scsimon(1) + rascsi(1), scsimon(1), rasdump(1), sasidump(1) Full documentation is available at: diff --git a/src/raspberrypi/Makefile b/src/raspberrypi/Makefile index 9bd1f7e6..423c7a07 100644 --- a/src/raspberrypi/Makefile +++ b/src/raspberrypi/Makefile @@ -101,6 +101,8 @@ SRC_SCSIMON = \ SRC_RASCTL = \ rasctl.cpp\ + rasctl_commands.cpp \ + rasctl_display.cpp \ rascsi_version.cpp \ rasutil.cpp \ protobuf_util.cpp diff --git a/src/raspberrypi/protobuf_response_helper.cpp b/src/raspberrypi/protobuf_response_helper.cpp index de727b00..7043b71d 100644 --- a/src/raspberrypi/protobuf_response_helper.cpp +++ b/src/raspberrypi/protobuf_response_helper.cpp @@ -206,12 +206,24 @@ void ProtobufResponseHandler::GetAvailableImages(PbResult& result, PbServerInfo& result.set_status(true); } -void ProtobufResponseHandler::GetDevices(PbServerInfo& serverInfo, const vector& devices, const string& image_folder) +PbReservedIdsInfo *ProtobufResponseHandler::GetReservedIds(PbResult& result, const set& ids) +{ + PbReservedIdsInfo *reserved_ids_info = new PbReservedIdsInfo(); + for (int id : ids) { + reserved_ids_info->add_ids(id); + } + + result.set_status(true); + + return reserved_ids_info; +} + +void ProtobufResponseHandler::GetDevices(PbServerInfo& server_info, const vector& devices, const string& image_folder) { for (const Device *device : devices) { // Skip if unit does not exist or is not assigned if (device) { - PbDevice *pb_device = serverInfo.mutable_devices()->add_devices(); + PbDevice *pb_device = server_info.mutable_devices_info()->add_devices(); GetDevice(device, pb_device, image_folder); } } @@ -243,12 +255,12 @@ void ProtobufResponseHandler::GetDevicesInfo(PbResult& result, const PbCommand& } } - PbDevices *pb_devices = new PbDevices(); - result.set_allocated_device_info(pb_devices); + PbDevicesInfo *devices_info = new PbDevicesInfo(); + result.set_allocated_devices_info(devices_info); for (const auto& id_set : id_sets) { const Device *device = devices[id_set.first * unit_count + id_set.second]; - GetDevice(device, pb_devices->add_devices(), image_folder); + GetDevice(device, devices_info->add_devices(), image_folder); } result.set_status(true); @@ -266,34 +278,50 @@ PbDeviceTypesInfo *ProtobufResponseHandler::GetDeviceTypesInfo(PbResult& result, } PbServerInfo *ProtobufResponseHandler::GetServerInfo(PbResult& result, const vector& devices, const set& reserved_ids, - const string& image_folder, const string& log_level) + const string& image_folder, const string& current_log_level) { PbServerInfo *server_info = new PbServerInfo(); - server_info->set_major_version(rascsi_major_version); - server_info->set_minor_version(rascsi_minor_version); - server_info->set_patch_version(rascsi_patch_version); - GetLogLevels(*server_info); - server_info->set_current_log_level(log_level); + server_info->set_allocated_version_info(GetVersionInfo(result)); + server_info->set_allocated_log_level_info(GetLogLevelInfo(result, current_log_level)); GetAllDeviceTypeProperties(*server_info->mutable_device_types_info()); GetAvailableImages(result, *server_info, image_folder); server_info->set_allocated_network_interfaces_info(GetNetworkInterfacesInfo(result)); server_info->set_allocated_mapping_info(GetMappingInfo(result)); GetDevices(*server_info, devices, image_folder); - for (int id : reserved_ids) { - server_info->add_reserved_ids(id); - } + server_info->set_allocated_reserved_ids_info(GetReservedIds(result, reserved_ids)); result.set_status(true); return server_info; } -void ProtobufResponseHandler::GetLogLevels(PbServerInfo& server_info) +PbVersionInfo *ProtobufResponseHandler::GetVersionInfo(PbResult& result) { + PbVersionInfo *version_info = new PbVersionInfo(); + + version_info->set_major_version(rascsi_major_version); + version_info->set_minor_version(rascsi_minor_version); + version_info->set_patch_version(rascsi_patch_version); + + result.set_status(true); + + return version_info; +} + +PbLogLevelInfo *ProtobufResponseHandler::GetLogLevelInfo(PbResult& result, const string& current_log_level) +{ + PbLogLevelInfo *log_level_info = new PbLogLevelInfo(); + for (const auto& log_level : log_levels) { - server_info.add_log_levels(log_level); + log_level_info->add_log_levels(log_level); } + + log_level_info->set_current_log_level(current_log_level); + + result.set_status(true); + + return log_level_info; } PbNetworkInterfacesInfo *ProtobufResponseHandler::GetNetworkInterfacesInfo(PbResult& result) diff --git a/src/raspberrypi/protobuf_response_helper.h b/src/raspberrypi/protobuf_response_helper.h index 2b4049f9..42fccf47 100644 --- a/src/raspberrypi/protobuf_response_helper.h +++ b/src/raspberrypi/protobuf_response_helper.h @@ -32,12 +32,15 @@ public: bool GetImageFile(PbImageFile *, const string&, const string&); PbImageFilesInfo *GetAvailableImages(PbResult&, const string&); + PbReservedIdsInfo *GetReservedIds(PbResult&, const set&); void GetDevices(PbServerInfo&, const vector&, const string&); void GetDevicesInfo(PbResult&, const PbCommand&, const vector&, const string&, int); PbDeviceTypesInfo *GetDeviceTypesInfo(PbResult&, const PbCommand&); + PbVersionInfo *GetVersionInfo(PbResult&); PbServerInfo *GetServerInfo(PbResult&, const vector&, const set&, const string&, const string&); PbNetworkInterfacesInfo *GetNetworkInterfacesInfo(PbResult&); PbMappingInfo *GetMappingInfo(PbResult&); + PbLogLevelInfo *GetLogLevelInfo(PbResult&, const string&); private: @@ -50,5 +53,4 @@ private: void GetAllDeviceTypeProperties(PbDeviceTypesInfo&); void GetDeviceTypeProperties(PbDeviceTypesInfo&, PbDeviceType); void GetAvailableImages(PbResult& result, PbServerInfo&, const string&); - void GetLogLevels(PbServerInfo&); }; diff --git a/src/raspberrypi/protobuf_util.h b/src/raspberrypi/protobuf_util.h index 18d07dd2..d327932f 100644 --- a/src/raspberrypi/protobuf_util.h +++ b/src/raspberrypi/protobuf_util.h @@ -13,7 +13,9 @@ #include "google/protobuf/message.h" #include "rascsi_interface.pb.h" +#include +using namespace std; using namespace rascsi_interface; const string GetParam(const PbCommand&, const string&); diff --git a/src/raspberrypi/rascsi.cpp b/src/raspberrypi/rascsi.cpp index ea13d33e..2bf364bb 100644 --- a/src/raspberrypi/rascsi.cpp +++ b/src/raspberrypi/rascsi.cpp @@ -498,8 +498,12 @@ string SetReservedIds(const string& ids) set reserved; for (string id_to_reserve : ids_to_reserve) { int id; - if (!GetAsInt(id_to_reserve, id)) { - return id_to_reserve; + if (!GetAsInt(id_to_reserve, id) || id > 7) { + return "Invalid ID " + id_to_reserve; + } + + if (devices[id * UnitNum]) { + return "ID " + id_to_reserve + " is currently in use"; } reserved.insert(id); @@ -508,16 +512,14 @@ string SetReservedIds(const string& ids) reserved_ids = reserved; if (!reserved_ids.empty()) { - list ids = { reserved_ids.begin(), reserved_ids.end() }; - ids.sort([](const auto& a, const auto& b) { return a < b; }); ostringstream s; bool isFirst = true; - for (auto const& id : ids) { + for (auto const& reserved_id : reserved_ids) { if (!isFirst) { s << ", "; } isFirst = false; - s << id; + s << reserved_id; } LOGINFO("Reserved ID(s) set to %s", s.str().c_str()); @@ -1236,11 +1238,11 @@ bool ProcessCmd(const int fd, const PbCommand& command) DetachAll(); return ReturnStatus(fd); - case RESERVE: { + case RESERVE_IDS: { const string ids = GetParam(command, "ids"); - string invalid_id = SetReservedIds(ids); - if (!invalid_id.empty()) { - return ReturnStatus(fd, false, "Invalid ID " + invalid_id + " for " + PbOperation_Name(RESERVE)); + string error = SetReservedIds(ids); + if (!error.empty()) { + return ReturnStatus(fd, false, error); } return ReturnStatus(fd); @@ -1365,9 +1367,9 @@ bool ParseArgument(int argc, char* argv[], int& port) continue; case 'r': { - string invalid_id = SetReservedIds(optarg); - if (!invalid_id.empty()) { - cerr << "Invalid ID " << invalid_id << " for " << PbOperation_Name(RESERVE); + string error = SetReservedIds(optarg); + if (!error.empty()) { + cerr << error << endl; return false; } } @@ -1446,7 +1448,7 @@ bool ParseArgument(int argc, char* argv[], int& port) // Display and log the device list PbServerInfo server_info; response_helper.GetDevices(server_info, devices, default_image_folder); - const list& devices = { server_info.devices().devices().begin(), server_info.devices().devices().end() }; + const list& devices = { server_info.devices_info().devices().begin(), server_info.devices_info().devices().end() }; const string device_list = ListDevices(devices); LogDevices(device_list); cout << device_list << endl; @@ -1556,7 +1558,7 @@ static void *MonThread(void *param) // For backwards compatibility: Log device list if information on all devices was requested. if (!command.devices_size()) { - const list& devices = { result.device_info().devices().begin(), result.device_info().devices().end() }; + const list& devices = { result.devices_info().devices().begin(), result.devices_info().devices().end() }; LogDevices(ListDevices(devices)); } break; @@ -1571,7 +1573,6 @@ static void *MonThread(void *param) break; } - case SERVER_INFO: { LOGTRACE("Received %s command", PbOperation_Name(command.operation()).c_str()); @@ -1582,6 +1583,24 @@ static void *MonThread(void *param) break; } + case VERSION_INFO: { + LOGTRACE("Received %s command", PbOperation_Name(command.operation()).c_str()); + + PbResult result; + result.set_allocated_version_info(response_helper.GetVersionInfo(result)); + SerializeMessage(fd, result); + break; + } + + case LOG_LEVEL_INFO: { + LOGTRACE("Received %s command", PbOperation_Name(command.operation()).c_str()); + + PbResult result; + result.set_allocated_log_level_info(response_helper.GetLogLevelInfo(result, current_log_level)); + SerializeMessage(fd, result); + break; + } + case DEFAULT_IMAGE_FILES_INFO: { LOGTRACE("Received %s command", PbOperation_Name(command.operation()).c_str()); @@ -1632,6 +1651,15 @@ static void *MonThread(void *param) break; } + case RESERVED_IDS_INFO: { + LOGTRACE("Received %s command", PbOperation_Name(command.operation()).c_str()); + + PbResult result; + result.set_allocated_reserved_ids_info(response_helper.GetReservedIds(result, reserved_ids)); + SerializeMessage(fd, result); + break; + } + default: { // Wait until we become idle while (active) { diff --git a/src/raspberrypi/rascsi_interface.proto b/src/raspberrypi/rascsi_interface.proto index 05c49abc..a74fd903 100644 --- a/src/raspberrypi/rascsi_interface.proto +++ b/src/raspberrypi/rascsi_interface.proto @@ -66,76 +66,85 @@ enum PbOperation { // Gets the server information SERVER_INFO = 10; - // Gets information on attached devices. Returns data for all attached devices if empty. - DEVICES_INFO = 11; - - // Device properties by device type - DEVICE_TYPES_INFO = 12; + // Get rascsi version information + VERSION_INFO = 11; - // Gets information on available image files in the default image folder. - DEFAULT_IMAGE_FILES_INFO = 13; + // Get information on attached devices. Returns data for all attached devices if the device list is empty. + DEVICES_INFO = 12; + + // Get device properties by device type + DEVICE_TYPES_INFO = 13; + + // Get information on available image files in the default image folder. + DEFAULT_IMAGE_FILES_INFO = 14; - // Gets information on an image file (not necessarily in the default image folder) based on an absolute path. + // Get information on an image file (not necessarily in the default image folder) based on an absolute path. // Parameters: // "file": The filename. Either an absolute path or a path relative to the default image folder. - IMAGE_FILE_INFO = 14; + IMAGE_FILE_INFO = 15; - // Gets the names of the available network interfaces. Only lists interfaces that are up. - NETWORK_INTERFACES_INFO = 15; + // Get information on the available log levels and the current log level + LOG_LEVEL_INFO = 16; + + // Get the names of the available network interfaces. Only lists interfaces that are up. + NETWORK_INTERFACES_INFO = 17; - // Gets the mapping of extensions to device types - MAPPING_INFO = 16; + // Get the mapping of extensions to device types + MAPPING_INFO = 18; + + // Get the list of reserved device IDs + RESERVED_IDS_INFO = 19; // Set the default folder for image files. // Parameters: // "folder": The default folder name. - DEFAULT_FOLDER = 17; + DEFAULT_FOLDER = 20; - // Set server log level. + // Set a new log level. // Parameters: // "level": The new log level - LOG_LEVEL = 18; + LOG_LEVEL = 21; // Block IDs from being used, usually the IDs of the initiators (computers) in the SCSI chain. // Parameters: // "ids": A comma-separated list of IDs to reserve, or an empty string in order not to reserve any ID. - RESERVE = 19; - + RESERVE_IDS = 22; + // Create an image file. The image file must not yet exist. // Parameters: // "file": The filename, relative to the default image folder. It must not contain a slash. // "size": The file size in bytes, must be a multiple of 512 // "read_only": "true" (case-insensitive) in order to create a read-only file, otherwise "false" - CREATE_IMAGE = 20; + CREATE_IMAGE = 23; // Delete an image file. // Parameters: // "file": The filename, relative to the default image folder. It must not contain a slash. - DELETE_IMAGE = 21; + DELETE_IMAGE = 24; // Rename an image file. // Parameters: // "from": The old filename, relative to the default image folder. It must not contain a slash. // "to": The new filename, relative to the default image folder. It must not contain a slash. // The new filename must not yet exist. - RENAME_IMAGE = 22; + RENAME_IMAGE = 25; // Copy an image file. // Parameters: // "from": The source filename, relative to the default image folder. It must not contain a slash. // "to": The destination filename, relative to the default image folder. It must not contain a slash. // The destination filename must not yet exist. - COPY_IMAGE = 23; + COPY_IMAGE = 26; // Write-protect an image file. // Parameters: // "file": The filename, relative to the default image folder. It must not contain a slash. - PROTECT_IMAGE = 24; + PROTECT_IMAGE = 27; // Make an image file writable. // Parameters: // "file": The filename, relative to the default image folder. It must not contain a slash. - UNPROTECT_IMAGE = 25; + UNPROTECT_IMAGE = 28; } // The supported file extensions mapped to their respective device types @@ -162,7 +171,7 @@ message PbDeviceProperties { // List of default parameters, if any (requires supports_params to be true) map default_params = 8; // Number of supported LUNs, at least 1 (for LUN 0) - uint32 luns = 9; + int32 luns = 9; // Unordered list of permitted block sizes in bytes, empty if the block size is not configurable repeated uint32 block_sizes = 10; } @@ -205,6 +214,13 @@ message PbImageFilesInfo { repeated PbImageFile image_files = 2; } +// Log level information +message PbLogLevelInfo { + // List of available log levels, ordered by increasing by severity + repeated string log_levels = 2; + string current_log_level = 3; +} + // The network interfaces information message PbNetworkInterfacesInfo { repeated string name = 1; @@ -244,13 +260,27 @@ message PbDevice { // Block size in bytes int32 block_size = 11; // Number of blocks - int64 block_count = 12; + uint64 block_count = 12; } -message PbDevices { +message PbDevicesInfo { repeated PbDevice devices = 1; } +// The reserved device IDs +message PbReservedIdsInfo { + repeated int32 ids = 1; +} + +// Rascsi server version information +message PbVersionInfo { + // The rascsi version + int32 major_version = 1; + int32 minor_version = 2; + // < 0 for a development version, = 0 if there is no patch yet + int32 patch_version = 3; +} + // Commands rascsi can execute and their parameters message PbCommand { PbOperation operation = 1; @@ -270,41 +300,43 @@ message PbResult { oneof result { // The result of a SERVER_INFO command PbServerInfo server_info = 3; - // The result of a DEVICE_INFO command - PbDevices device_info = 4; + // The result of a VERSION_INFO command + PbVersionInfo version_info = 4; + // The result of a LOG_LEVEL_INFO command + PbLogLevelInfo log_level_info = 5; + // The result of a DEVICES_INFO command + PbDevicesInfo devices_info = 6; // The result of a DEVICE_TYPES_INFO command - PbDeviceTypesInfo device_types_info = 5; + PbDeviceTypesInfo device_types_info = 7; // The result of a DEFAULT_IMAGE_FILES_INFO command - PbImageFilesInfo image_files_info = 6; + PbImageFilesInfo image_files_info = 8; // The result of an IMAGE_FILE_INFO command - PbImageFile image_file_info = 7; + PbImageFile image_file_info = 9; // The result of a NETWORK_INTERFACES_INFO command - PbNetworkInterfacesInfo network_interfaces_info = 8; + PbNetworkInterfacesInfo network_interfaces_info = 10; // The result of an MAPPING_INFO command - PbMappingInfo mapping_info = 9; + PbMappingInfo mapping_info = 11; + // The result of a RESERVED_IDS_INFO command + PbReservedIdsInfo reserved_ids_info = 12; } } -// The rascsi server information +// The rascsi server information. All data can also be requested with individual commands. message PbServerInfo { - // The rascsi server version - uint32 major_version = 1; - uint32 minor_version = 2; - // < 0 for a development version, = 0 if there is no patch yet - int32 patch_version = 3; - // List of available log levels, ordered by increasing by severity - repeated string log_levels = 4; - string current_log_level = 5; - // Supported device types and their properties, also available separately - PbDeviceTypesInfo device_types_info = 6; - // The image files in the default folder, also available separately - PbImageFilesInfo image_files_info = 7; - // The available (up) network interfaces, also available separately - PbNetworkInterfacesInfo network_interfaces_info = 8; + // The rascsi server version data + PbVersionInfo version_info = 1; + // The available log levels and the current log level + PbLogLevelInfo log_level_info = 2; + // Supported device types and their properties + PbDeviceTypesInfo device_types_info = 3; + // The image files in the default folder + PbImageFilesInfo image_files_info = 4; + // The available (up) network interfaces + PbNetworkInterfacesInfo network_interfaces_info = 5; // The extensions to device types mapping - PbMappingInfo mapping_info = 9; - // The attached devices, also available separately - PbDevices devices = 10; - // The unsorted list of reserved IDs - repeated uint32 reserved_ids = 11; + PbMappingInfo mapping_info = 6; + // The reserved device IDs + PbReservedIdsInfo reserved_ids_info = 7; + // The attached devices + PbDevicesInfo devices_info = 8; } diff --git a/src/raspberrypi/rasctl.cpp b/src/raspberrypi/rasctl.cpp index c65f5a60..4bb2a722 100644 --- a/src/raspberrypi/rasctl.cpp +++ b/src/raspberrypi/rasctl.cpp @@ -9,14 +9,12 @@ // //--------------------------------------------------------------------------- -#include #include "os.h" #include "rascsi_version.h" -#include "exceptions.h" #include "protobuf_util.h" #include "rasutil.h" +#include "rasctl_commands.h" #include "rascsi_interface.pb.h" -#include #include #include @@ -26,490 +24,6 @@ using namespace std; using namespace rascsi_interface; -void SendCommand(const string& hostname, int port, const PbCommand& command, PbResult& result) -{ - // Send command - int fd = -1; - try { - struct hostent *host = gethostbyname(hostname.c_str()); - if (!host) { - throw io_exception("Can't resolve hostname '" + hostname + "'"); - } - - fd = socket(AF_INET, SOCK_STREAM, 0); - if (fd < 0) { - throw io_exception("Can't create socket"); - } - - 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); - memcpy(&server.sin_addr.s_addr, host->h_addr, host->h_length); - - if (connect(fd, (struct sockaddr *)&server, sizeof(struct sockaddr_in)) < 0) { - ostringstream error; - error << "Can't connect to rascsi process on host '" << hostname << "', port " << port; - throw io_exception(error.str()); - } - - SerializeMessage(fd, command); - } - catch(const io_exception& e) { - cerr << "Error: " << e.getmsg() << endl; - - if (fd >= 0) { - close(fd); - } - - exit(fd < 0 ? ENOTCONN : EXIT_FAILURE); - } - - // Receive result - try { - DeserializeMessage(fd, result); - - if (!result.status()) { - throw io_exception(result.msg()); - } - } - catch(const io_exception& e) { - close(fd); - - cerr << "Error: " << e.getmsg() << endl; - - exit(EXIT_FAILURE); - } - - close(fd); - - if (!result.msg().empty()) { - cout << result.msg() << endl; - } -} - -void DisplayDeviceInfo(const PbDevice& pb_device) -{ - cout << " " << pb_device.id() << ":" << pb_device.unit() << " " << PbDeviceType_Name(pb_device.type()) - << " " << pb_device.vendor() << ":" << pb_device.product() << ":" << pb_device.revision(); - - if (pb_device.block_size()) { - cout << " " << pb_device.block_size() << " bytes per sector"; - if (pb_device.block_count()) { - cout << " " << pb_device.block_size() * pb_device.block_count() << " bytes capacity"; - } - } - - if (pb_device.properties().supports_file() && !pb_device.file().name().empty()) { - cout << " " << pb_device.file().name(); - } - - cout << " "; - bool hasProperty = false; - if (pb_device.properties().read_only()) { - cout << "read-only"; - hasProperty = true; - } - if (pb_device.properties().protectable() && pb_device.status().protected_()) { - if (hasProperty) { - cout << ", "; - } - cout << "protected"; - hasProperty = true; - } - if (pb_device.properties().stoppable() && pb_device.status().stopped()) { - if (hasProperty) { - cout << ", "; - } - cout << "stopped"; - hasProperty = true; - } - if (pb_device.properties().removable() && pb_device.status().removed()) { - if (hasProperty) { - cout << ", "; - } - cout << "removed"; - hasProperty = true; - } - if (pb_device.properties().lockable() && pb_device.status().locked()) { - if (hasProperty) { - cout << ", "; - } - cout << "locked"; - } - if (hasProperty) { - cout << " "; - } - - bool isFirst = true; - for (const auto& param : pb_device.params()) { - if (!isFirst) { - cout << " "; - } - isFirst = false; - cout << param.first << "=" << param.second; - } - - cout << endl; -} - -void DisplayDeviceTypesInfo(const PbDeviceTypesInfo& device_types_info) -{ - cout << "Supported device types and their properties:" << endl; - for (auto it = device_types_info.properties().begin(); it != device_types_info.properties().end(); ++it) { - cout << " " << PbDeviceType_Name(it->type()); - - const PbDeviceProperties& properties = it->properties(); - - cout << " Supported LUNs: " << properties.luns() << endl; - - if (properties.read_only() || properties.protectable() || properties.stoppable() || properties.read_only() - || properties.lockable()) { - cout << " Properties: "; - bool has_property = false; - if (properties.read_only()) { - cout << "read-only"; - has_property = true; - } - if (properties.protectable()) { - cout << (has_property ? ", " : "") << "protectable"; - has_property = true; - } - if (properties.stoppable()) { - cout << (has_property ? ", " : "") << "stoppable"; - has_property = true; - } - if (properties.removable()) { - cout << (has_property ? ", " : "") << "removable"; - has_property = true; - } - if (properties.lockable()) { - cout << (has_property ? ", " : "") << "lockable"; - } - cout << endl; - } - - if (properties.supports_file()) { - cout << " Image file support" << endl; - } - else if (properties.supports_params()) { - cout << " Parameter support" << endl; - } - - if (properties.supports_params() && properties.default_params_size()) { - map params = { properties.default_params().begin(), properties.default_params().end() }; - - cout << " Default parameters: "; - - bool isFirst = true; - for (const auto& param : params) { - if (!isFirst) { - cout << ", "; - } - cout << param.first << "=" << param.second; - - isFirst = false; - } - cout << endl; - } - - if (properties.block_sizes_size()) { - list block_sizes = { properties.block_sizes().begin(), properties.block_sizes().end() }; - block_sizes.sort([](const auto& a, const auto& b) { return a < b; }); - - cout << " Configurable block sizes in bytes: "; - - bool isFirst = true; - for (const auto& block_size : block_sizes) { - if (!isFirst) { - cout << ", "; - } - cout << block_size; - - isFirst = false; - } - cout << endl; - } - } -} - -void DisplayImageFile(const PbImageFile& image_file_info) -{ - cout << image_file_info.name() << " " << image_file_info.size() << " bytes"; - if (image_file_info.read_only()) { - cout << " read-only"; - } - if (image_file_info.type() != UNDEFINED) { - cout << " " << PbDeviceType_Name(image_file_info.type()); - } - cout << endl; - -} - -void DisplayImageFiles(const PbImageFilesInfo& image_files_info) -{ - const list image_files = { image_files_info.image_files().begin(), image_files_info.image_files().end() }; - - cout << "Default image file folder: " << image_files_info.default_image_folder() << endl; - - if (image_files.empty()) { - cout << " No image files available" << endl; - } - else { - list files = { image_files.begin(), image_files.end() }; - files.sort([](const auto& a, const auto& b) { return a.name() < b.name(); }); - - cout << "Available image files:" << endl; - for (const auto& file : files) { - cout << " "; - DisplayImageFile(file); - } - } -} - -void DisplayNetworkInterfaces(const PbNetworkInterfacesInfo& network_interfaces_info) -{ - const list interfaces = { network_interfaces_info.name().begin(), network_interfaces_info.name().end() }; - - cout << "Available (up) network interfaces:" << endl; - bool isFirst = true; - for (const auto& interface : interfaces) { - if (!isFirst) { - cout << ", "; - } - isFirst = false; - cout << interface; - } - cout << endl; -} - -void DisplayMappingInfo(const PbMappingInfo& mapping_info) -{ - const map mappings = { mapping_info.mapping().begin(), mapping_info.mapping().end() }; - - cout << "Supported image file extension to device type mappings:" << endl; - for (const auto& mapping : mappings) { - cout << " " << mapping.first << "->" << PbDeviceType_Name(mapping.second) << endl; - } -} - -//--------------------------------------------------------------------------- -// -// Command implementations -// -//--------------------------------------------------------------------------- - -void CommandList(const string& hostname, int port) -{ - PbCommand command; - command.set_operation(DEVICES_INFO); - - PbResult result; - SendCommand(hostname.c_str(), port, command, result); - - const list& devices = { result.device_info().devices().begin(), result.device_info().devices().end() }; - cout << ListDevices(devices) << endl; -} - -const PbServerInfo GetServerInfo(const PbCommand& command, const string& hostname, int port) -{ - PbResult result; - SendCommand(hostname.c_str(), port, command, result); - - return result.server_info(); -} - -void CommandLogLevel(PbCommand& command, const string& hostname, int port, const string& log_level) -{ - AddParam(command, "level", log_level); - - PbResult result; - SendCommand(hostname.c_str(), port, command, result); -} - -void CommandReserve(PbCommand& command, const string&hostname, int port, const string& reserved_ids) -{ - AddParam(command, "ids", reserved_ids); - - PbResult result; - SendCommand(hostname.c_str(), port, command, result); -} - -void CommandCreateImage(PbCommand& command, const string&hostname, int port, const string& image_params) -{ - size_t separatorPos = image_params.find(COMPONENT_SEPARATOR); - if (separatorPos != string::npos) { - AddParam(command, "file", image_params.substr(0, separatorPos)); - AddParam(command, "size", image_params.substr(separatorPos + 1)); - } - else { - cerr << "Error: Invalid file descriptor '" << image_params << "', format is NAME:SIZE" << endl; - exit(EXIT_FAILURE); - } - - AddParam(command, "read_only", "false"); - - PbResult result; - SendCommand(hostname.c_str(), port, command, result); -} - -void CommandDeleteImage(PbCommand& command, const string&hostname, int port, const string& filename) -{ - AddParam(command, "file", filename); - - PbResult result; - SendCommand(hostname.c_str(), port, command, result); -} - -void CommandRenameImage(PbCommand& command, const string&hostname, int port, const string& image_params) -{ - size_t separatorPos = image_params.find(COMPONENT_SEPARATOR); - if (separatorPos != string::npos) { - AddParam(command, "from", image_params.substr(0, separatorPos)); - AddParam(command, "to", image_params.substr(separatorPos + 1)); - } - else { - cerr << "Error: Invalid file descriptor '" << image_params << "', format is CURRENT_NAME:NEW_NAME" << endl; - exit(EXIT_FAILURE); - } - - PbResult result; - SendCommand(hostname.c_str(), port, command, result); -} - -void CommandCopyImage(PbCommand& command, const string&hostname, int port, const string& image_params) -{ - size_t separatorPos = image_params.find(COMPONENT_SEPARATOR); - if (separatorPos != string::npos) { - AddParam(command, "from", image_params.substr(0, separatorPos)); - AddParam(command, "to", image_params.substr(separatorPos + 1)); - } - else { - cerr << "Error: Invalid file descriptor '" << image_params << "', format is CURRENT_NAME:NEW_NAME" << endl; - exit(EXIT_FAILURE); - } - - PbResult result; - SendCommand(hostname.c_str(), port, command, result); -} - -void CommandDefaultImageFolder(PbCommand& command, const string& hostname, int port, const string& folder) -{ - AddParam(command, "folder", folder); - - PbResult result; - SendCommand(hostname.c_str(), port, command, result); -} - -void CommandDeviceInfo(const PbCommand& command, const string& hostname, int port) -{ - PbResult result; - SendCommand(hostname.c_str(), port, command, result); - - for (const auto& pb_device : result.device_info().devices()) { - DisplayDeviceInfo(pb_device); - } -} - -void CommandDeviceTypesInfo(const PbCommand& command, const string& hostname, int port) -{ - PbResult result; - SendCommand(hostname.c_str(), port, command, result); - - DisplayDeviceTypesInfo(result.device_types_info()); -} - -void CommandServerInfo(PbCommand& command, const string& hostname, int port) -{ - PbResult result; - SendCommand(hostname.c_str(), port, command, result); - - PbServerInfo server_info = result.server_info(); - - cout << "rascsi server version: " << server_info.major_version() << "." << server_info.minor_version(); - if (server_info.patch_version() > 0) { - cout << "." << server_info.patch_version(); - } - else if (server_info.patch_version() < 0) { - cout << " (development version)"; - } - cout << endl; - - if (!server_info.log_levels_size()) { - cout << " No log level settings available" << endl; - } - else { - cout << "rascsi log levels, sorted by severity:" << endl; - for (const auto& log_level : server_info.log_levels()) { - cout << " " << log_level << endl; - } - - cout << "Current rascsi log level: " << server_info.current_log_level() << endl; - } - - DisplayImageFiles(server_info.image_files_info()); - DisplayMappingInfo(server_info.mapping_info()); - DisplayNetworkInterfaces(server_info.network_interfaces_info()); - DisplayDeviceTypesInfo(server_info.device_types_info()); - - if (server_info.reserved_ids_size()) { - cout << "Reserved device IDs: "; - for (int i = 0; i < server_info.reserved_ids_size(); i++) { - if(i) { - cout << ", "; - } - cout << server_info.reserved_ids(i); - } - cout < sorted_devices = { server_info.devices().devices().begin(), server_info.devices().devices().end() }; - sorted_devices.sort([](const auto& a, const auto& b) { return a.id() < b.id(); }); - - cout << "Attached devices:" << endl; - - for (const auto& device : sorted_devices) { - DisplayDeviceInfo(device); - } - } -} - -void CommandDefaultImageFilesInfo(const PbCommand& command, const string& hostname, int port) -{ - PbResult result; - SendCommand(hostname.c_str(), port, command, result); - - DisplayImageFiles(result.image_files_info()); -} - -void CommandImageFileInfo(PbCommand& command, const string& hostname, int port, const string& filename) -{ - AddParam(command, "file", filename); - - PbResult result; - SendCommand(hostname.c_str(), port, command, result); - - DisplayImageFile(result.image_file_info()); -} - -void CommandNetworkInterfacesInfo(const PbCommand& command, const string&hostname, int port) -{ - PbResult result; - SendCommand(hostname.c_str(), port, command, result); - - DisplayNetworkInterfaces(result.network_interfaces_info()); -} - -void CommandMappingInfo(const PbCommand& command, const string&hostname, int port) -{ - PbResult result; - SendCommand(hostname.c_str(), port, command, result); - - DisplayMappingInfo(result.mapping_info()); -} - PbOperation ParseOperation(const char *optarg) { switch (tolower(optarg[0])) { @@ -549,7 +63,7 @@ PbDeviceType ParseType(const char *optarg) return type; } else { - // Parse convenience types (shortcuts) + // Parse convenience device types (shortcuts) switch (tolower(optarg[0])) { case 'c': return SCCD; @@ -574,11 +88,6 @@ PbDeviceType ParseType(const char *optarg) return UNDEFINED; } -//--------------------------------------------------------------------------- -// -// Main processing -// -//--------------------------------------------------------------------------- int main(int argc, char* argv[]) { GOOGLE_PROTOBUF_VERIFY_VERSION; @@ -590,7 +99,7 @@ int main(int argc, char* argv[]) cerr << "Usage: " << argv[0] << " -i ID [-u UNIT] [-c CMD] [-C FILE] [-t TYPE] [-b BLOCK_SIZE] [-n NAME] [-f FILE|PARAM] "; cerr << "[-F IMAGE_FOLDER] [-L LOG_LEVEL] [-h HOST] [-p PORT] [-r RESERVED_IDS] "; cerr << "[-C FILENAME:FILESIZE] [-w FILENAME] [-R CURRENT_NAME:NEW_NAME] [-x CURRENT_NAME:NEW_NAME] "; - cerr << "[-e] [-E FILENAME] [-l] [-L] [-m] [-s] [-v] [-y]" << endl; + cerr << "[-e] [-E FILENAME] [-I] [-l] [-L] [-m] [-O] [-s] [-v] [-V] [-y]" << endl; cerr << " where ID := {0-7}" << endl; cerr << " UNIT := {0|1}, default is 0" << endl; cerr << " CMD := {attach|detach|insert|eject|protect|unprotect|show}" << endl; @@ -610,7 +119,6 @@ int main(int argc, char* argv[]) exit(EXIT_SUCCESS); } - // Parse the arguments PbCommand command; list devices; PbDeviceDefinition* device = command.add_devices(); @@ -627,7 +135,7 @@ int main(int argc, char* argv[]) opterr = 1; int opt; - while ((opt = getopt(argc, argv, "elmsvNTD:L:R:a:b:c:f:h:i:n:p:r:t:u:x:C:E:F:L:")) != -1) { + while ((opt = getopt(argc, argv, "elmsvINOTVa:b:c:f:h:i:n:p:r:t:u:x:C:D:E:F:L:R:")) != -1) { switch (opt) { case 'i': device->set_id(optarg[0] - '0'); @@ -677,6 +185,23 @@ int main(int argc, char* argv[]) param = optarg; break; + case 'h': + hostname = optarg; + break; + + case 'I': + command.set_operation(RESERVED_IDS_INFO); + break; + + case 'L': + command.set_operation(LOG_LEVEL); + log_level = optarg; + break; + + case 'l': + list = true; + break; + case 'm': command.set_operation(MAPPING_INFO); break; @@ -685,6 +210,10 @@ int main(int argc, char* argv[]) command.set_operation(NETWORK_INTERFACES_INFO); break; + case 'O': + command.set_operation(LOG_LEVEL_INFO); + break; + case 't': device->set_type(ParseType(optarg)); if (device->type() == UNDEFINED) { @@ -693,17 +222,9 @@ int main(int argc, char* argv[]) } break; - case 'L': - command.set_operation(LOG_LEVEL); - log_level = optarg; - break; - - case 'h': - hostname = optarg; - break; - - case 'l': - list = true; + case 'r': + command.set_operation(RESERVE_IDS); + reserved_ids = optarg; break; case 'R': @@ -747,20 +268,19 @@ int main(int argc, char* argv[]) } break; - case 'r': - command.set_operation(RESERVE); - reserved_ids = optarg; - break; - case 's': command.set_operation(SERVER_INFO); break; case 'v': - cout << rascsi_get_version_string() << endl; + cout << "rasctl version: " << rascsi_get_version_string() << endl; exit(EXIT_SUCCESS); break; + case 'V': + command.set_operation(VERSION_INFO); + break; + case 'x': command.set_operation(COPY_IMAGE); image_params = optarg; @@ -781,83 +301,96 @@ int main(int argc, char* argv[]) exit(EXIT_FAILURE); } - switch(command.operation()) { - case LOG_LEVEL: - CommandLogLevel(command, hostname, port, log_level); - exit(EXIT_SUCCESS); - - case DEFAULT_FOLDER: - CommandDefaultImageFolder(command, hostname, port, default_folder); - exit(EXIT_SUCCESS); - - case RESERVE: - CommandReserve(command, hostname, port, reserved_ids); - exit(EXIT_SUCCESS); - - case CREATE_IMAGE: - CommandCreateImage(command, hostname, port, image_params); - exit(EXIT_SUCCESS); - - case DELETE_IMAGE: - CommandDeleteImage(command, hostname, port, image_params); - exit(EXIT_SUCCESS); - - case RENAME_IMAGE: - CommandRenameImage(command, hostname, port, image_params); - exit(EXIT_SUCCESS); - - case COPY_IMAGE: - CommandCopyImage(command, hostname, port, image_params); - exit(EXIT_SUCCESS); - - case DEVICES_INFO: - CommandDeviceInfo(command, hostname, port); - exit(EXIT_SUCCESS); - - case DEVICE_TYPES_INFO: - CommandDeviceTypesInfo(command, hostname, port); - exit(EXIT_SUCCESS); - - case SERVER_INFO: - CommandServerInfo(command, hostname, port); - exit(EXIT_SUCCESS); - - case DEFAULT_IMAGE_FILES_INFO: - CommandDefaultImageFilesInfo(command, hostname, port); - exit(EXIT_SUCCESS); - - case IMAGE_FILE_INFO: - CommandImageFileInfo(command, hostname, port, filename); - exit(EXIT_SUCCESS); - - case NETWORK_INTERFACES_INFO: - CommandNetworkInterfacesInfo(command, hostname, port); - exit(EXIT_SUCCESS); - - case MAPPING_INFO: - CommandMappingInfo(command, hostname, port); - exit(EXIT_SUCCESS); - - default: - break; - } - + // Listing devices is a special case (rasctl backwards compatibility) if (list) { - CommandList(hostname, port); + PbCommand command_list; + command_list.set_operation(DEVICES_INFO); + RasctlCommands rasctl_commands(command_list, hostname, port); + rasctl_commands.CommandDevicesInfo(); exit(EXIT_SUCCESS); } if (!param.empty()) { - if (device->type() == SCBR || device->type() == SCDP) { - AddParam(*device, "interfaces", param); - } - else { - AddParam(*device, "file", param); - } + // Only one of these parameters will be used, depending on the device type + AddParam(*device, "interfaces", param); + AddParam(*device, "file", param); } - PbResult result; - SendCommand(hostname, port, command, result); + RasctlCommands rasctl_commands(command, hostname, port); + + switch(command.operation()) { + case LOG_LEVEL: + rasctl_commands.CommandLogLevel(log_level); + break; + + case DEFAULT_FOLDER: + rasctl_commands.CommandDefaultImageFolder(default_folder); + break; + + case RESERVE_IDS: + rasctl_commands.CommandReserveIds(reserved_ids); + break; + + case CREATE_IMAGE: + rasctl_commands.CommandCreateImage(image_params); + break; + + case DELETE_IMAGE: + rasctl_commands.CommandDeleteImage(image_params); + break; + + case RENAME_IMAGE: + rasctl_commands.CommandRenameImage(image_params); + break; + + case COPY_IMAGE: + rasctl_commands.CommandCopyImage(image_params); + break; + + case DEVICES_INFO: + rasctl_commands.CommandDeviceInfo(); + break; + + case DEVICE_TYPES_INFO: + rasctl_commands.CommandDeviceTypesInfo(); + break; + + case VERSION_INFO: + rasctl_commands.CommandVersionInfo(); + break; + + case SERVER_INFO: + rasctl_commands.CommandServerInfo(); + break; + + case DEFAULT_IMAGE_FILES_INFO: + rasctl_commands.CommandDefaultImageFilesInfo(); + break; + + case IMAGE_FILE_INFO: + rasctl_commands.CommandImageFileInfo(hostname); + break; + + case NETWORK_INTERFACES_INFO: + rasctl_commands.CommandNetworkInterfacesInfo(); + break; + + case LOG_LEVEL_INFO: + rasctl_commands.CommandLogLevelInfo(); + break; + + case RESERVED_IDS_INFO: + rasctl_commands.CommandReservedIdsInfo(); + break; + + case MAPPING_INFO: + rasctl_commands.CommandMappingInfo(); + break; + + default: + rasctl_commands.SendCommand(); + break; + } exit(EXIT_SUCCESS); } diff --git a/src/raspberrypi/rasctl_commands.cpp b/src/raspberrypi/rasctl_commands.cpp new file mode 100644 index 00000000..6b3e197e --- /dev/null +++ b/src/raspberrypi/rasctl_commands.cpp @@ -0,0 +1,270 @@ +//--------------------------------------------------------------------------- +// +// SCSI Target Emulator RaSCSI (*^..^*) +// for Raspberry Pi +// +// Copyright (C) 2021 Uwe Seimet +// +//--------------------------------------------------------------------------- + +#include +#include "os.h" +#include "exceptions.h" +#include "protobuf_util.h" +#include "rasutil.h" +#include "rasctl_commands.h" +#include "rascsi_interface.pb.h" +#include +#include +#include + +// Separator for the INQUIRY name components +#define COMPONENT_SEPARATOR ':' + +using namespace std; +using namespace rascsi_interface; + +RasctlCommands::RasctlCommands(PbCommand& command, const string& hostname, int port) +{ + this->command = command; + this->hostname = hostname; + this->port = port; +} + +void RasctlCommands::SendCommand() +{ + // Send command + int fd = -1; + try { + struct hostent *host = gethostbyname(hostname.c_str()); + if (!host) { + throw io_exception("Can't resolve hostname '" + hostname + "'"); + } + + fd = socket(AF_INET, SOCK_STREAM, 0); + if (fd < 0) { + throw io_exception("Can't create socket"); + } + + 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); + memcpy(&server.sin_addr.s_addr, host->h_addr, host->h_length); + + if (connect(fd, (struct sockaddr *)&server, sizeof(struct sockaddr_in)) < 0) { + ostringstream error; + error << "Can't connect to rascsi process on host '" << hostname << "', port " << port; + throw io_exception(error.str()); + } + + SerializeMessage(fd, command); + } + catch(const io_exception& e) { + cerr << "Error: " << e.getmsg() << endl; + + if (fd >= 0) { + close(fd); + } + + exit(fd < 0 ? ENOTCONN : EXIT_FAILURE); + } + + // Receive result + try { + DeserializeMessage(fd, result); + + if (!result.status()) { + throw io_exception(result.msg()); + } + } + catch(const io_exception& e) { + close(fd); + + cerr << "Error: " << e.getmsg() << endl; + + exit(EXIT_FAILURE); + } + + close(fd); + + if (!result.msg().empty()) { + cout << result.msg() << endl; + } +} + +void RasctlCommands::CommandDevicesInfo() +{ + SendCommand(); + + rasctl_display.DisplayDevices(result.devices_info()); +} + +void RasctlCommands::CommandLogLevel(const string& log_level) +{ + AddParam(command, "level", log_level); + + SendCommand(); +} + +void RasctlCommands::CommandReserveIds(const string& reserved_ids) +{ + AddParam(command, "ids", reserved_ids); + + SendCommand(); +} + +void RasctlCommands::CommandCreateImage(const string& image_params) +{ + size_t separatorPos = image_params.find(COMPONENT_SEPARATOR); + if (separatorPos != string::npos) { + AddParam(command, "file", image_params.substr(0, separatorPos)); + AddParam(command, "size", image_params.substr(separatorPos + 1)); + } + else { + cerr << "Error: Invalid file descriptor '" << image_params << "', format is NAME:SIZE" << endl; + exit(EXIT_FAILURE); + } + + AddParam(command, "read_only", "false"); + + SendCommand(); +} + +void RasctlCommands::CommandDeleteImage(const string& filename) +{ + AddParam(command, "file", filename); + + SendCommand(); +} + +void RasctlCommands::CommandRenameImage(const string& image_params) +{ + size_t separatorPos = image_params.find(COMPONENT_SEPARATOR); + if (separatorPos != string::npos) { + AddParam(command, "from", image_params.substr(0, separatorPos)); + AddParam(command, "to", image_params.substr(separatorPos + 1)); + } + else { + cerr << "Error: Invalid file descriptor '" << image_params << "', format is CURRENT_NAME:NEW_NAME" << endl; + exit(EXIT_FAILURE); + } + + SendCommand(); +} + +void RasctlCommands::CommandCopyImage(const string& image_params) +{ + size_t separatorPos = image_params.find(COMPONENT_SEPARATOR); + if (separatorPos != string::npos) { + AddParam(command, "from", image_params.substr(0, separatorPos)); + AddParam(command, "to", image_params.substr(separatorPos + 1)); + } + else { + cerr << "Error: Invalid file descriptor '" << image_params << "', format is CURRENT_NAME:NEW_NAME" << endl; + exit(EXIT_FAILURE); + } + + SendCommand(); +} + +void RasctlCommands::CommandDefaultImageFolder(const string& folder) +{ + AddParam(command, "folder", folder); + + SendCommand(); +} + +void RasctlCommands::CommandDeviceInfo() +{ + SendCommand(); + + for (const auto& device : result.devices_info().devices()) { + rasctl_display.DisplayDeviceInfo(device); + } +} + +void RasctlCommands::CommandDeviceTypesInfo() +{ + SendCommand(); + + rasctl_display.DisplayDeviceTypesInfo(result.device_types_info()); +} + +void RasctlCommands::CommandVersionInfo() +{ + SendCommand(); + + rasctl_display.DisplayVersionInfo(result.version_info()); +} + +void RasctlCommands::CommandServerInfo() +{ + SendCommand(); + + PbServerInfo server_info = result.server_info(); + + rasctl_display.DisplayVersionInfo(server_info.version_info()); + rasctl_display.DisplayLogLevelInfo(server_info.log_level_info()); + rasctl_display.DisplayImageFiles(server_info.image_files_info()); + rasctl_display.DisplayMappingInfo(server_info.mapping_info()); + rasctl_display.DisplayNetworkInterfaces(server_info.network_interfaces_info()); + rasctl_display.DisplayDeviceTypesInfo(server_info.device_types_info()); + rasctl_display.DisplayReservedIdsInfo(server_info.reserved_ids_info()); + + if (server_info.devices_info().devices_size()) { + list sorted_devices = { server_info.devices_info().devices().begin(), server_info.devices_info().devices().end() }; + sorted_devices.sort([](const auto& a, const auto& b) { return a.id() < b.id(); }); + + cout << "Attached devices:" << endl; + + for (const auto& device : sorted_devices) { + rasctl_display.DisplayDeviceInfo(device); + } + } +} + +void RasctlCommands::CommandDefaultImageFilesInfo() +{ + SendCommand(); + + rasctl_display.DisplayImageFiles(result.image_files_info()); +} + +void RasctlCommands::CommandImageFileInfo(const string& filename) +{ + AddParam(command, "file", filename); + + SendCommand(); + + rasctl_display.DisplayImageFile(result.image_file_info()); +} + +void RasctlCommands::CommandNetworkInterfacesInfo() +{ + SendCommand(); + + rasctl_display.DisplayNetworkInterfaces(result.network_interfaces_info()); +} + +void RasctlCommands::CommandLogLevelInfo() +{ + SendCommand(); + + rasctl_display.DisplayLogLevelInfo(result.log_level_info()); +} + +void RasctlCommands::CommandReservedIdsInfo() +{ + SendCommand(); + + rasctl_display.DisplayReservedIdsInfo(result.reserved_ids_info()); +} + +void RasctlCommands::CommandMappingInfo() +{ + SendCommand(); + + rasctl_display.DisplayMappingInfo(result.mapping_info()); +} diff --git a/src/raspberrypi/rasctl_commands.h b/src/raspberrypi/rasctl_commands.h new file mode 100644 index 00000000..0704191c --- /dev/null +++ b/src/raspberrypi/rasctl_commands.h @@ -0,0 +1,55 @@ +//--------------------------------------------------------------------------- +// +// SCSI Target Emulator RaSCSI (*^..^*) +// for Raspberry Pi +// +// Copyright (C) 2021 Uwe Seimet +// +//--------------------------------------------------------------------------- + +#pragma once + +#include "rascsi_interface.pb.h" +#include "rasctl_display.h" +#include + +using namespace std; +using namespace rascsi_interface; + +class RasctlCommands +{ +public: + + RasctlCommands(PbCommand&, const string&, int); + ~RasctlCommands() {}; + + void SendCommand(); + void CommandDevicesInfo(); + void CommandLogLevel(const string&); + void CommandReserveIds(const string&); + void CommandCreateImage(const string&); + void CommandDeleteImage(const string&); + void CommandRenameImage(const string&); + void CommandCopyImage(const string&); + void CommandDefaultImageFolder(const string&); + void CommandDeviceInfo(); + void CommandDeviceTypesInfo(); + void CommandVersionInfo(); + void CommandServerInfo(); + void CommandDefaultImageFilesInfo(); + void CommandImageFileInfo(const string&); + void CommandNetworkInterfacesInfo(); + void CommandLogLevelInfo(); + void CommandReservedIdsInfo(); + void CommandMappingInfo(); + +private: + + PbCommand command; + string hostname; + int port; + + PbResult result; + + RasctlDisplay rasctl_display; +}; diff --git a/src/raspberrypi/rasctl_display.cpp b/src/raspberrypi/rasctl_display.cpp new file mode 100644 index 00000000..41fc81e1 --- /dev/null +++ b/src/raspberrypi/rasctl_display.cpp @@ -0,0 +1,268 @@ +//--------------------------------------------------------------------------- +// +// SCSI Target Emulator RaSCSI (*^..^*) +// for Raspberry Pi +// +// Copyright (C) 2021 Uwe Seimet +// +//--------------------------------------------------------------------------- + +#include "rascsi_interface.pb.h" +#include "rasutil.h" +#include "rasctl_display.h" +#include +#include + +using namespace std; +using namespace rascsi_interface; + +void RasctlDisplay::DisplayDevices(const PbDevicesInfo& devices_info) +{ + const list& devices = { devices_info.devices().begin(), devices_info.devices().end() }; + cout << ListDevices(devices) << endl; +} + +void RasctlDisplay::DisplayDeviceInfo(const PbDevice& pb_device) +{ + cout << " " << pb_device.id() << ":" << pb_device.unit() << " " << PbDeviceType_Name(pb_device.type()) + << " " << pb_device.vendor() << ":" << pb_device.product() << ":" << pb_device.revision(); + + if (pb_device.block_size()) { + cout << " " << pb_device.block_size() << " bytes per sector"; + if (pb_device.block_count()) { + cout << " " << pb_device.block_size() * pb_device.block_count() << " bytes capacity"; + } + } + + if (pb_device.properties().supports_file() && !pb_device.file().name().empty()) { + cout << " " << pb_device.file().name(); + } + + cout << " "; + bool hasProperty = false; + if (pb_device.properties().read_only()) { + cout << "read-only"; + hasProperty = true; + } + if (pb_device.properties().protectable() && pb_device.status().protected_()) { + if (hasProperty) { + cout << ", "; + } + cout << "protected"; + hasProperty = true; + } + if (pb_device.properties().stoppable() && pb_device.status().stopped()) { + if (hasProperty) { + cout << ", "; + } + cout << "stopped"; + hasProperty = true; + } + if (pb_device.properties().removable() && pb_device.status().removed()) { + if (hasProperty) { + cout << ", "; + } + cout << "removed"; + hasProperty = true; + } + if (pb_device.properties().lockable() && pb_device.status().locked()) { + if (hasProperty) { + cout << ", "; + } + cout << "locked"; + } + if (hasProperty) { + cout << " "; + } + + bool isFirst = true; + for (const auto& param : pb_device.params()) { + if (!isFirst) { + cout << " "; + } + isFirst = false; + cout << param.first << "=" << param.second; + } + + cout << endl; +} + +void RasctlDisplay::DisplayVersionInfo(const PbVersionInfo& version_info) +{ + cout << "rascsi server version: " << version_info.major_version() << "." << version_info.minor_version(); + if (version_info.patch_version() > 0) { + cout << "." << version_info.patch_version(); + } + else if (version_info.patch_version() < 0) { + cout << " (development version)"; + } + cout << endl; +} + +void RasctlDisplay::DisplayLogLevelInfo(const PbLogLevelInfo& log_level_info) +{ + if (!log_level_info.log_levels_size()) { + cout << " No log level settings available" << endl; + } + else { + cout << "rascsi log levels, sorted by severity:" << endl; + for (const auto& log_level : log_level_info.log_levels()) { + cout << " " << log_level << endl; + } + } + + cout << "Current rascsi log level: " << log_level_info.current_log_level() << endl; +} + +void RasctlDisplay::DisplayDeviceTypesInfo(const PbDeviceTypesInfo& device_types_info) +{ + cout << "Supported device types and their properties:" << endl; + for (auto it = device_types_info.properties().begin(); it != device_types_info.properties().end(); ++it) { + cout << " " << PbDeviceType_Name(it->type()); + + const PbDeviceProperties& properties = it->properties(); + + cout << " Supported LUNs: " << properties.luns() << endl; + + if (properties.read_only() || properties.protectable() || properties.stoppable() || properties.read_only() + || properties.lockable()) { + cout << " Properties: "; + bool has_property = false; + if (properties.read_only()) { + cout << "read-only"; + has_property = true; + } + if (properties.protectable()) { + cout << (has_property ? ", " : "") << "protectable"; + has_property = true; + } + if (properties.stoppable()) { + cout << (has_property ? ", " : "") << "stoppable"; + has_property = true; + } + if (properties.removable()) { + cout << (has_property ? ", " : "") << "removable"; + has_property = true; + } + if (properties.lockable()) { + cout << (has_property ? ", " : "") << "lockable"; + } + cout << endl; + } + + if (properties.supports_file()) { + cout << " Image file support" << endl; + } + else if (properties.supports_params()) { + cout << " Parameter support" << endl; + } + + if (properties.supports_params() && properties.default_params_size()) { + map params = { properties.default_params().begin(), properties.default_params().end() }; + + cout << " Default parameters: "; + + bool isFirst = true; + for (const auto& param : params) { + if (!isFirst) { + cout << ", "; + } + cout << param.first << "=" << param.second; + + isFirst = false; + } + cout << endl; + } + + if (properties.block_sizes_size()) { + set block_sizes = { properties.block_sizes().begin(), properties.block_sizes().end() }; + + cout << " Configurable block sizes in bytes: "; + + bool isFirst = true; + for (const auto& block_size : block_sizes) { + if (!isFirst) { + cout << ", "; + } + cout << block_size; + + isFirst = false; + } + cout << endl; + } + } +} + +void RasctlDisplay::DisplayReservedIdsInfo(const PbReservedIdsInfo& reserved_ids_info) +{ + if (reserved_ids_info.ids_size()) { + cout << "Reserved device IDs: "; + for (int i = 0; i < reserved_ids_info.ids_size(); i++) { + if(i) { + cout << ", "; + } + cout << reserved_ids_info.ids(i); + } + cout < image_files = { image_files_info.image_files().begin(), image_files_info.image_files().end() }; + + cout << "Default image file folder: " << image_files_info.default_image_folder() << endl; + + if (image_files.empty()) { + cout << " No image files available" << endl; + } + else { + list files = { image_files.begin(), image_files.end() }; + files.sort([](const auto& a, const auto& b) { return a.name() < b.name(); }); + + cout << "Available image files:" << endl; + for (const auto& file : files) { + cout << " "; + DisplayImageFile(file); + } + } +} + +void RasctlDisplay::DisplayNetworkInterfaces(const PbNetworkInterfacesInfo& network_interfaces_info) +{ + const list interfaces = { network_interfaces_info.name().begin(), network_interfaces_info.name().end() }; + + cout << "Available (up) network interfaces:" << endl; + bool isFirst = true; + for (const auto& interface : interfaces) { + if (!isFirst) { + cout << ", "; + } + isFirst = false; + cout << interface; + } + cout << endl; +} + +void RasctlDisplay::DisplayMappingInfo(const PbMappingInfo& mapping_info) +{ + const map mappings = { mapping_info.mapping().begin(), mapping_info.mapping().end() }; + + cout << "Supported image file extension to device type mappings:" << endl; + for (const auto& mapping : mappings) { + cout << " " << mapping.first << "->" << PbDeviceType_Name(mapping.second) << endl; + } +} diff --git a/src/raspberrypi/rasctl_display.h b/src/raspberrypi/rasctl_display.h new file mode 100644 index 00000000..9957e5fa --- /dev/null +++ b/src/raspberrypi/rasctl_display.h @@ -0,0 +1,33 @@ +//--------------------------------------------------------------------------- +// +// SCSI Target Emulator RaSCSI (*^..^*) +// for Raspberry Pi +// +// Copyright (C) 2021 Uwe Seimet +// +//--------------------------------------------------------------------------- + +#pragma once + +#include "rascsi_interface.pb.h" + +using namespace rascsi_interface; + +class RasctlDisplay +{ +public: + + RasctlDisplay() {}; + ~RasctlDisplay() {}; + + void DisplayDevices(const PbDevicesInfo&); + void DisplayDeviceInfo(const PbDevice&); + void DisplayVersionInfo(const PbVersionInfo&); + void DisplayLogLevelInfo(const PbLogLevelInfo&); + void DisplayDeviceTypesInfo(const PbDeviceTypesInfo&); + void DisplayReservedIdsInfo(const PbReservedIdsInfo&); + void DisplayImageFile(const PbImageFile&); + void DisplayImageFiles(const PbImageFilesInfo&); + void DisplayNetworkInterfaces(const PbNetworkInterfacesInfo&); + void DisplayMappingInfo(const PbMappingInfo&); +}; diff --git a/src/web/ractl_cmds.py b/src/web/ractl_cmds.py index 487a6673..91fa5525 100644 --- a/src/web/ractl_cmds.py +++ b/src/web/ractl_cmds.py @@ -21,12 +21,12 @@ def get_server_info(): data = send_pb_command(command.SerializeToString()) result = proto.PbResult() result.ParseFromString(data) - version = str(result.server_info.major_version) + "." +\ - str(result.server_info.minor_version) + "." +\ - str(result.server_info.patch_version) - log_levels = result.server_info.log_levels - current_log_level = result.server_info.current_log_level - reserved_ids = list(result.server_info.reserved_ids) + version = str(result.server_info.version_info.major_version) + "." +\ + str(result.server_info.version_info.minor_version) + "." +\ + str(result.server_info.version_info.patch_version) + log_levels = result.server_info.log_level_info.log_levels + current_log_level = result.server_info.log_level_info.current_log_level + reserved_ids = list(result.server_info.reserved_ids_info.ids) # Creates lists of file endings recognized by RaSCSI mappings = result.server_info.mapping_info.mapping @@ -280,15 +280,15 @@ def list_devices(scsi_id=None): n = 0 # Return an empty list if no devices are attached - if len(result.device_info.devices) == 0: + if len(result.devices_info.devices) == 0: return {"status": False, "device_list": []} - while n < len(result.device_info.devices): - did = result.device_info.devices[n].id - dun = result.device_info.devices[n].unit - dtype = proto.PbDeviceType.Name(result.device_info.devices[n].type) - dstat = result.device_info.devices[n].status - dprop = result.device_info.devices[n].properties + while n < len(result.devices_info.devices): + did = result.devices_info.devices[n].id + dun = result.devices_info.devices[n].unit + dtype = proto.PbDeviceType.Name(result.devices_info.devices[n].type) + dstat = result.devices_info.devices[n].status + dprop = result.devices_info.devices[n].properties # Building the status string # TODO: This formatting should probably be moved elsewhere @@ -302,13 +302,13 @@ def list_devices(scsi_id=None): if dstat.locked == True and dprop.lockable == True: dstat_msg.append("Locked") - dpath = result.device_info.devices[n].file.name + dpath = result.devices_info.devices[n].file.name dfile = path.basename(dpath) - dparam = result.device_info.devices[n].params - dven = result.device_info.devices[n].vendor - dprod = result.device_info.devices[n].product - drev = result.device_info.devices[n].revision - dblock = result.device_info.devices[n].block_size + dparam = result.devices_info.devices[n].params + dven = result.devices_info.devices[n].vendor + dprod = result.devices_info.devices[n].product + drev = result.devices_info.devices[n].revision + dblock = result.devices_info.devices[n].block_size device_list.append({"id": did, "un": dun, "device_type": dtype, \ "status": ", ".join(dstat_msg), "image": dpath, "file": dfile, "params": dparam,\