MAPPING_INFO command, returning the mapping of extensions to types (#275)

* Added MAPPING_INFO command, returning the mapping of extensions to types

* Updated help message

* Set explicit vendor/product/revision for .hda extension

* Added leading blanks to vendor and product for .hda

* Product string update

* Fixed typo in help string

* Manpage update

* Spoof hda as a Quantum Fireball

Co-authored-by: Daniel Markstedt <markstedt@gmail.com>
This commit is contained in:
Uwe Seimet 2021-09-28 01:39:50 +02:00 committed by GitHub
parent c98dec8f44
commit 97c2a11329
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 130 additions and 61 deletions

View File

@ -5,8 +5,8 @@ rasctl \- Sends management commands to the rascsi process
.B rasctl
\fB\-L\fR |
\fB\-e\fR |
\fB\-k\fR |
\fB\-l\fR |
\fB\-m\fR |
\fB\-s\fR |
\fB\-T\fR |
[\fB\-F\fR \fIIMAGE_FOLDER\fR]
@ -57,6 +57,9 @@ Lists all available network interfaces provided that they are up.
.BR \-l\fI
List all of the devices that are currently being emulated by RaSCSI, as well as their current status.
.TP
.BR \-m\fI
List all file extensions recognized by RaSCSI and the device types they map to.
.TP
.BR \-R\fI " "\fICURRENT_NAME:NEW_NAME
Rename an image file in the default image folder.
.TP

View File

@ -6,7 +6,7 @@ NAME
rasctl - Sends management commands to the rascsi process
SYNOPSIS
rasctl -L | -e | -k | -l | -s | -T | [-F IMAGE_FOLDER] [-C FILE
rasctl -L | -e | -l | -m | -s | -T | [-F IMAGE_FOLDER] [-C FILE
NAME:FILESIZE] [-x CURRENT_NAME:NEW_NAME] [-R CURRENT_NAME:NEW_NAME]
[-g LOG_LEVEL] [-h HOST] [-p PORT] [-r RESERVED_IDS] [-v] -i ID [-c
CMD] [-f FILE|PARAM] [-n NAME] [-t TYPE] [-u UNIT]
@ -45,6 +45,9 @@ OPTIONS
-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
types they map to.
-R CURRENT_NAME:NEW_NAME
Rename an image file in the default image folder.
@ -54,7 +57,7 @@ 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.
@ -74,7 +77,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)
@ -84,18 +87,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
@ -105,16 +108,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

View File

@ -64,6 +64,16 @@ DeviceFactory::DeviceFactory()
default_params[SCCD] = {};
default_params[SCBR]["interfaces"] = network_interfaces;
default_params[SCDP]["interfaces"] = network_interfaces;
extension_mapping["hdf"] = SAHD;
extension_mapping["hds"] = SCHD;
extension_mapping["hda"] = SCHD;
extension_mapping["hdn"] = SCHD;
extension_mapping["hdi"] = SCHD;
extension_mapping["nhd"] = SCHD;
extension_mapping["hdr"] = SCRM;
extension_mapping["mos"] = SCMO;
extension_mapping["iso"] = SCCD;
}
DeviceFactory& DeviceFactory::instance()
@ -72,7 +82,7 @@ DeviceFactory& DeviceFactory::instance()
return instance;
}
string DeviceFactory::GetExtension(const string& filename)
string DeviceFactory::GetExtension(const string& filename) const
{
string ext;
size_t separator = filename.rfind('.');
@ -87,18 +97,9 @@ string DeviceFactory::GetExtension(const string& filename)
PbDeviceType DeviceFactory::GetTypeForFile(const string& filename)
{
string ext = GetExtension(filename);
if (ext == "hdf") {
return SAHD;
}
else if (ext == "hds" || ext == "hdn" || ext == "hdi" || ext == "nhd" || ext == "hda") {
return SCHD;
}
else if (ext == "hdr") {
return SCRM;
} else if (ext == "mos") {
return SCMO;
} else if (ext == "iso") {
return SCCD;
if (extension_mapping.find(ext) != extension_mapping.end()) {
return extension_mapping[ext];
}
else if (filename == "bridge") {
return SCBR;
@ -137,6 +138,12 @@ Device *DeviceFactory::CreateDevice(PbDeviceType type, const string& filename)
} else {
device = new SCSIHD(false);
((Disk *)device)->SetSectorSizes(sector_sizes[SCHD]);
// Some Apple tools require a particular drive identification
if (ext == "hda") {
device->SetVendor("QUANTUM");
device->SetProduct("FIREBALL");
}
}
device->SetSupportedLuns(1);
device->SetProtectable(true);

View File

@ -33,15 +33,14 @@ public:
static DeviceFactory& instance();
static PbDeviceType GetTypeForFile(const string&);
Device *CreateDevice(PbDeviceType, const string&);
PbDeviceType GetTypeForFile(const string&);
const set<uint32_t>& GetSectorSizes(PbDeviceType type) { return sector_sizes[type]; }
const set<uint32_t>& GetSectorSizes(const string&);
const set<uint64_t> GetCapacities(PbDeviceType);
const map<string, string>& GetDefaultParams(PbDeviceType type) { return default_params[type]; }
const list<string> GetNetworkInterfaces() const;
const map<string, PbDeviceType> GetExtensionMapping() const { return extension_mapping; }
private:
@ -51,5 +50,7 @@ private:
map<PbDeviceType, map<string, string>> default_params;
static string GetExtension(const string&);
map<string, PbDeviceType> extension_mapping;
string GetExtension(const string&) const;
};

View File

@ -139,7 +139,7 @@ void ProtobufResponseHandler::GetImageFile(PbImageFile *image_file, const string
{
image_file->set_name(filename);
if (!filename.empty()) {
image_file->set_type(DeviceFactory::GetTypeForFile(filename));
image_file->set_type(device_factory.GetTypeForFile(filename));
string f = filename[0] == '/' ? filename : image_folder + "/" + filename;
@ -275,6 +275,7 @@ PbServerInfo *ProtobufResponseHandler::GetServerInfo(PbResult& result, const vec
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);
@ -304,3 +305,16 @@ PbNetworkInterfacesInfo *ProtobufResponseHandler::GetNetworkInterfacesInfo(PbRes
return network_interfaces_info;
}
PbMappingInfo *ProtobufResponseHandler::GetMappingInfo(PbResult& result)
{
PbMappingInfo *mapping_info = new PbMappingInfo();
for (const auto& mapping : device_factory.GetExtensionMapping()) {
(*mapping_info->mutable_mapping())[mapping.first] = mapping.second;
}
result.set_status(true);
return mapping_info;
}

View File

@ -36,6 +36,7 @@ public:
PbDeviceTypesInfo *GetDeviceTypesInfo(PbResult&, const PbCommand&);
PbServerInfo *GetServerInfo(PbResult&, const vector<Device *>&, const set<int>&, const string&, const string&);
PbNetworkInterfacesInfo *GetNetworkInterfacesInfo(PbResult&);
PbMappingInfo *GetMappingInfo(PbResult&);
private:

View File

@ -1592,6 +1592,15 @@ static void *MonThread(void *param)
break;
}
case MAPPING_INFO: {
LOGTRACE("Received %s command", PbOperation_Name(command.operation()).c_str());
PbResult result;
result.set_allocated_mapping_info(response_helper.GetMappingInfo(result));
SerializeMessage(fd, result);
break;
}
default: {
// Wait until we become idle
while (active) {

View File

@ -77,57 +77,65 @@ enum PbOperation {
// Gets the names of the available network interfaces. Only lists interfaces that are up.
NETWORK_INTERFACES_INFO = 14;
// Gets the mapping of extensions to device types
MAPPING_INFO = 15;
// Set the default folder for image files.
// Parameters:
// "folder": The default folder name.
DEFAULT_FOLDER = 15;
DEFAULT_FOLDER = 16;
// Set server log level.
// Parameters:
// "level": The new log level
LOG_LEVEL = 16;
LOG_LEVEL = 17;
// 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 = 17;
RESERVE = 18;
// 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 = 18;
CREATE_IMAGE = 19;
// Delete an image file.
// Parameters:
// "file": The filename, relative to the default image folder. It must not contain a slash.
DELETE_IMAGE = 19;
DELETE_IMAGE = 20;
// 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 = 20;
RENAME_IMAGE = 21;
// 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 = 21;
COPY_IMAGE = 22;
// Write-protect an image file.
// Parameters:
// "file": The filename, relative to the default image folder. It must not contain a slash.
PROTECT_IMAGE = 22;
PROTECT_IMAGE = 23;
// Make an image file writable.
// Parameters:
// "file": The filename, relative to the default image folder. It must not contain a slash.
UNPROTECT_IMAGE = 23;
UNPROTECT_IMAGE = 24;
}
// The supported file extensions mapped to their respective device types
message PbMappingInfo {
map<string, PbDeviceType> mapping = 1;
}
// The properties supported by a device
@ -267,6 +275,8 @@ message PbResult {
PbImageFilesInfo image_files_info = 6;
// The result of a NETWORK_INTERFACES_INFO command
PbNetworkInterfacesInfo network_interfaces_info = 7;
// The result of an MAPPING_INFO command
PbMappingInfo mapping_info = 8;
}
}
@ -286,8 +296,10 @@ message PbServerInfo {
PbImageFilesInfo image_files_info = 7;
// The available (up) network interfaces, also available separately
PbNetworkInterfacesInfo network_interfaces_info = 8;
// The extensions to device types mapping
PbMappingInfo mapping_info = 9;
// The attached devices, also available separately
PbDevices devices = 9;
PbDevices devices = 10;
// The unsorted list of reserved IDs
repeated uint32 reserved_ids = 10;
repeated uint32 reserved_ids = 11;
}

View File

@ -252,9 +252,11 @@ void DisplayDeviceTypesInfo(const PbDeviceTypesInfo& device_types_info)
}
}
void DisplayImageFiles(const list<PbImageFile> image_files, const string& default_image_folder)
void DisplayImageFiles(const PbImageFilesInfo& image_files_info)
{
cout << "Default image file folder: " << default_image_folder << endl;
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;
@ -277,9 +279,9 @@ void DisplayImageFiles(const list<PbImageFile> image_files, const string& defaul
}
}
void DisplayNetworkInterfaces(list<string> interfaces)
void DisplayNetworkInterfaces(const PbNetworkInterfacesInfo& network_interfaces_info)
{
interfaces.sort([](const auto& a, const auto& b) { return a < b; });
const list<string> interfaces = { network_interfaces_info.name().begin(), network_interfaces_info.name().end() };
cout << "Available (up) network interfaces:" << endl;
bool isFirst = true;
@ -293,6 +295,16 @@ void DisplayNetworkInterfaces(list<string> interfaces)
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
@ -447,14 +459,9 @@ void CommandServerInfo(PbCommand& command, const string& hostname, int port)
cout << "Current rascsi log level: " << server_info.current_log_level() << endl;
}
const list<PbImageFile> image_files =
{ server_info.image_files_info().image_files().begin(), server_info.image_files_info().image_files().end() };
DisplayImageFiles(image_files, server_info.image_files_info().default_image_folder());
const list<string> network_interfaces =
{ server_info.network_interfaces_info().name().begin(), server_info.network_interfaces_info().name().end() };
DisplayNetworkInterfaces(network_interfaces);
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()) {
@ -485,9 +492,7 @@ void CommandImageFilesInfo(const PbCommand& command, const string& hostname, int
PbResult result;
SendCommand(hostname.c_str(), port, command, result);
const list<PbImageFile> image_files =
{ result.image_files_info().image_files().begin(),result.image_files_info().image_files().end() };
DisplayImageFiles(image_files, result.image_files_info().default_image_folder());
DisplayImageFiles(result.image_files_info());
}
void CommandNetworkInterfacesInfo(const PbCommand& command, const string&hostname, int port)
@ -495,9 +500,15 @@ void CommandNetworkInterfacesInfo(const PbCommand& command, const string&hostnam
PbResult result;
SendCommand(hostname.c_str(), port, command, result);
list<string> network_interfaces =
{ result.network_interfaces_info().name().begin(), result.network_interfaces_info().name().end() };
DisplayNetworkInterfaces(network_interfaces);
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)
@ -580,7 +591,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 << "[-L] [-k] [-l] [-v] [-y]" << endl;
cerr << "[-L] [-l] [-m] [-s] [-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;
@ -616,7 +627,7 @@ int main(int argc, char* argv[])
opterr = 1;
int opt;
while ((opt = getopt(argc, argv, "elsvNTD:L:R:a:b:c:f:h:i:n:p:r:t:u:x:C:F:L:")) != -1) {
while ((opt = getopt(argc, argv, "elmsvNTD:L:R:a:b:c:f:h:i:n:p:r:t:u:x:C:F:L:")) != -1) {
switch (opt) {
case 'i':
device->set_id(optarg[0] - '0');
@ -661,6 +672,10 @@ int main(int argc, char* argv[])
param = optarg;
break;
case 'm':
command.set_operation(MAPPING_INFO);
break;
case 'N':
command.set_operation(NETWORK_INTERFACES_INFO);
break;
@ -810,6 +825,10 @@ int main(int argc, char* argv[])
CommandNetworkInterfacesInfo(command, hostname, port);
exit(EXIT_SUCCESS);
case MAPPING_INFO:
CommandMappingInfo(command, hostname, port);
exit(EXIT_SUCCESS);
default:
break;
}