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 <markstedt@gmail.com>
This commit is contained in:
Uwe Seimet 2021-10-06 23:25:43 +02:00 committed by GitHub
parent 95607cd603
commit a638fec8a8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 983 additions and 711 deletions

View File

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

View File

@ -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:
<https://www.github.com/akuker/RASCSI/wiki/>

View File

@ -101,6 +101,8 @@ SRC_SCSIMON = \
SRC_RASCTL = \
rasctl.cpp\
rasctl_commands.cpp \
rasctl_display.cpp \
rascsi_version.cpp \
rasutil.cpp \
protobuf_util.cpp

View File

@ -206,12 +206,24 @@ void ProtobufResponseHandler::GetAvailableImages(PbResult& result, PbServerInfo&
result.set_status(true);
}
void ProtobufResponseHandler::GetDevices(PbServerInfo& serverInfo, const vector<Device *>& devices, const string& image_folder)
PbReservedIdsInfo *ProtobufResponseHandler::GetReservedIds(PbResult& result, const set<int>& 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<Device *>& 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<Device *>& devices, const set<int>& 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)

View File

@ -32,12 +32,15 @@ public:
bool GetImageFile(PbImageFile *, const string&, const string&);
PbImageFilesInfo *GetAvailableImages(PbResult&, const string&);
PbReservedIdsInfo *GetReservedIds(PbResult&, const set<int>&);
void GetDevices(PbServerInfo&, const vector<Device *>&, const string&);
void GetDevicesInfo(PbResult&, const PbCommand&, const vector<Device *>&, const string&, int);
PbDeviceTypesInfo *GetDeviceTypesInfo(PbResult&, const PbCommand&);
PbVersionInfo *GetVersionInfo(PbResult&);
PbServerInfo *GetServerInfo(PbResult&, const vector<Device *>&, const set<int>&, 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&);
};

View File

@ -13,7 +13,9 @@
#include "google/protobuf/message.h"
#include "rascsi_interface.pb.h"
#include <string>
using namespace std;
using namespace rascsi_interface;
const string GetParam(const PbCommand&, const string&);

View File

@ -498,8 +498,12 @@ string SetReservedIds(const string& ids)
set<int> 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<int> 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<PbDevice>& devices = { server_info.devices().devices().begin(), server_info.devices().devices().end() };
const list<PbDevice>& 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<PbDevice>& devices = { result.device_info().devices().begin(), result.device_info().devices().end() };
const list<PbDevice>& 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) {

View File

@ -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<string, string> 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;
}

View File

@ -9,14 +9,12 @@
//
//---------------------------------------------------------------------------
#include <netdb.h>
#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 <sstream>
#include <iostream>
#include <list>
@ -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<string, string> 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<uint32_t> 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<PbImageFile> 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<PbImageFile> 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<string> 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<string, PbDeviceType> 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<PbDevice>& 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 <<endl;
}
if (server_info.devices().devices_size()) {
list<PbDevice> 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<PbDeviceDefinition> 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);
}

View File

@ -0,0 +1,270 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI (*^..^*)
// for Raspberry Pi
//
// Copyright (C) 2021 Uwe Seimet
//
//---------------------------------------------------------------------------
#include <netdb.h>
#include "os.h"
#include "exceptions.h"
#include "protobuf_util.h"
#include "rasutil.h"
#include "rasctl_commands.h"
#include "rascsi_interface.pb.h"
#include <sstream>
#include <iostream>
#include <list>
// 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<PbDevice> 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());
}

View File

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

View File

@ -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 <iostream>
#include <list>
using namespace std;
using namespace rascsi_interface;
void RasctlDisplay::DisplayDevices(const PbDevicesInfo& devices_info)
{
const list<PbDevice>& 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<string, string> 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<uint32_t> 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 <<endl;
}
}
void RasctlDisplay::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 RasctlDisplay::DisplayImageFiles(const PbImageFilesInfo& image_files_info)
{
const list<PbImageFile> 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<PbImageFile> 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<string> 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<string, PbDeviceType> 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;
}
}

View File

@ -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&);
};

View File

@ -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,\