diff --git a/doc/rasctl.1 b/doc/rasctl.1 index a7630404..a096bf6a 100644 --- a/doc/rasctl.1 +++ b/doc/rasctl.1 @@ -9,6 +9,7 @@ rasctl \- Sends management commands to the rascsi process \fB\-m\fR | \fB\-s\fR | \fB\-T\fR | +[\fB\-E\fR \fIFILENAME\fR] [\fB\-F\fR \fIIMAGE_FOLDER\fR] [\fB\-C\fR \fIFILENAME:FILESIZE\fR] [\fB\-x\fR \fICURRENT_NAME:NEW_NAME\fR] @@ -39,6 +40,9 @@ Note: The command and type arguments are case insensitive. Only the first letter .BR \-C\fI " "\fIFILENAME:FILESIZE Create an image file in the default image folder with the specified name and size in bytes. .TP +.BR \-E\fI " " \fIFILENAME +Display information on an image file. +.TP .BR \-F\fI " "\fIIMAGE_FOLDER Set the default image folder. .TP diff --git a/doc/rasctl_man_page.txt b/doc/rasctl_man_page.txt index d8233291..0a47e907 100644 --- a/doc/rasctl_man_page.txt +++ b/doc/rasctl_man_page.txt @@ -6,20 +6,21 @@ NAME rasctl - Sends management commands to the rascsi process SYNOPSIS - 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] + 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 + UNIT] DESCRIPTION - rasctl Sends commands to the rascsi process to make configuration ad‐ + rasctl Sends commands to the rascsi process to make configuration ad‐ justments at runtime or to check the status of the devices. Either the -i or -l option should be specified at one time. Not both. You do NOT need root privileges to use rasctl. - Note: The command and type arguments are case insensitive. Only the + Note: The command and type arguments are case insensitive. Only the first letter of the command/type is evaluated by the tool. OPTIONS @@ -27,11 +28,14 @@ OPTIONS Create an image file in the default image folder with the speci‐ fied name and size in bytes. + -E FILENAME + Display information on an image file. + -F IMAGE_FOLDER Set the default image folder. -L LOG_LEVEL - Set the rascsi log level (trace, debug, info, warn, err, criti‐ + Set the rascsi log level (trace, debug, info, warn, err, criti‐ cal, off). -h HOST @@ -39,13 +43,13 @@ OPTIONS -e List all images files in the default image folder. - -N Lists all available network interfaces provided that they are + -N Lists all available network interfaces provided that they are up. - -l List all of the devices that are currently being emulated by + -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 @@ -57,7 +61,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. @@ -77,7 +81,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) @@ -87,18 +91,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 @@ -108,16 +112,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 diff --git a/src/raspberrypi/protobuf_response_helper.cpp b/src/raspberrypi/protobuf_response_helper.cpp index b5123aeb..2c7b6b00 100644 --- a/src/raspberrypi/protobuf_response_helper.cpp +++ b/src/raspberrypi/protobuf_response_helper.cpp @@ -131,10 +131,10 @@ void ProtobufResponseHandler::GetDevice(const Device *device, PbDevice *pb_devic } } -void ProtobufResponseHandler::GetImageFile(PbImageFile *image_file, const string& filename, const string& image_folder) +bool ProtobufResponseHandler::GetImageFile(PbImageFile *image_file, const string& filename, const string& image_folder) { - image_file->set_name(filename); if (!filename.empty()) { + image_file->set_name(filename); image_file->set_type(device_factory.GetTypeForFile(filename)); string f = filename[0] == '/' ? filename : image_folder + "/" + filename; @@ -144,8 +144,11 @@ void ProtobufResponseHandler::GetImageFile(PbImageFile *image_file, const string struct stat st; if (!stat(f.c_str(), &st)) { image_file->set_size(st.st_size); + return true; } } + + return false; } PbImageFilesInfo *ProtobufResponseHandler::GetAvailableImages(PbResult& result, const string& image_folder) diff --git a/src/raspberrypi/protobuf_response_helper.h b/src/raspberrypi/protobuf_response_helper.h index 3260f971..2b4049f9 100644 --- a/src/raspberrypi/protobuf_response_helper.h +++ b/src/raspberrypi/protobuf_response_helper.h @@ -30,6 +30,7 @@ public: static ProtobufResponseHandler& instance(); + bool GetImageFile(PbImageFile *, const string&, const string&); PbImageFilesInfo *GetAvailableImages(PbResult&, const string&); void GetDevices(PbServerInfo&, const vector&, const string&); void GetDevicesInfo(PbResult&, const PbCommand&, const vector&, const string&, int); @@ -49,6 +50,5 @@ private: void GetAllDeviceTypeProperties(PbDeviceTypesInfo&); void GetDeviceTypeProperties(PbDeviceTypesInfo&, PbDeviceType); void GetAvailableImages(PbResult& result, PbServerInfo&, const string&); - void GetImageFile(PbImageFile *, const string&, const string&); void GetLogLevels(PbServerInfo&); }; diff --git a/src/raspberrypi/rascsi.cpp b/src/raspberrypi/rascsi.cpp index c337c512..05adb10c 100644 --- a/src/raspberrypi/rascsi.cpp +++ b/src/raspberrypi/rascsi.cpp @@ -839,11 +839,6 @@ bool Attach(int fd, const PbDeviceDefinition& pb_device, Device *map[], bool dry device->SetId(id); device->SetLun(unit); - // Only non read-only devices support protect/unprotect - if (!device->IsReadOnly()) { - device->SetProtected(pb_device.protected_()); - } - try { if (!pb_device.vendor().empty()) { device->SetVendor(pb_device.vendor()); @@ -859,19 +854,6 @@ bool Attach(int fd, const PbDeviceDefinition& pb_device, Device *map[], bool dry return ReturnStatus(fd, false, e.getmsg()); } - if (pb_device.block_size()) { - Disk *disk = dynamic_cast(device); - if (disk && disk->IsSectorSizeConfigurable()) { - if (!disk->SetConfiguredSectorSize(pb_device.block_size())) { - error << "Invalid block size " << pb_device.block_size() << " bytes"; - return ReturnStatus(fd, false, error); - } - } - else { - return ReturnStatus(fd, false, "Block size is not configurable for device type " + PbDeviceType_Name(type)); - } - } - // File check (type is HD, for removable media drives, CD and MO the medium (=file) may be inserted later) if (file_support && !device->IsRemovable() && filename.empty()) { delete device; @@ -912,6 +894,26 @@ bool Attach(int fd, const PbDeviceDefinition& pb_device, Device *map[], bool dry file_support->ReserveFile(filepath, device->GetId(), device->GetLun()); } + // The operations below must not be executed before Open() because Open() overrides some settings + + if (pb_device.block_size()) { + Disk *disk = dynamic_cast(device); + if (disk && disk->IsSectorSizeConfigurable()) { + if (!disk->SetConfiguredSectorSize(pb_device.block_size())) { + error << "Invalid block size " << pb_device.block_size() << " bytes"; + return ReturnStatus(fd, false, error); + } + } + else { + return ReturnStatus(fd, false, "Block size is not configurable for device type " + PbDeviceType_Name(type)); + } + } + + // Only non read-only devices support protect/unprotect + if (device->IsProtectable() && !device->IsReadOnly()) { + device->SetProtected(pb_device.protected_()); + } + // Stop the dry run here, before permanently modifying something if (dryRun) { delete device; @@ -1027,7 +1029,6 @@ bool Insert(int fd, const PbDeviceDefinition& pb_device, Device *device, bool dr } FileSupport *file_support = dynamic_cast(device); - try { try { file_support->Open(filepath); @@ -1041,9 +1042,14 @@ bool Insert(int fd, const PbDeviceDefinition& pb_device, Device *device, bool dr catch(const io_exception& e) { return ReturnStatus(fd, false, "Tried to open an invalid file '" + initial_filename + "': " + e.getmsg()); } - file_support->ReserveFile(filepath, device->GetId(), device->GetLun()); + // Only non read-only devices support protect/unprotect. + // This operation must not be executed before Open() because Open() overrides some settings. + if (device->IsProtectable() && !device->IsReadOnly()) { + device->SetProtected(pb_device.protected_()); + } + return true; } @@ -1574,7 +1580,7 @@ static void *MonThread(void *param) break; } - case IMAGE_FILES_INFO: { + case DEFAULT_IMAGE_FILES_INFO: { LOGTRACE("Received %s command", PbOperation_Name(command.operation()).c_str()); PbResult result; @@ -1583,6 +1589,29 @@ static void *MonThread(void *param) break; } + case IMAGE_FILE_INFO: { + LOGTRACE("Received %s command", PbOperation_Name(command.operation()).c_str()); + + string filename = GetParam(command, "file"); + if (filename.empty()) { + ReturnStatus(fd, false, "Can't get image file info: Missing filename"); + } + else { + PbResult result; + PbImageFile* image_file = new PbImageFile(); + bool status = response_helper.GetImageFile(image_file, filename, default_image_folder); + if (status) { + result.set_status(true); + result.set_allocated_image_file_info(image_file); + SerializeMessage(fd, result); + } + else { + ReturnStatus(fd, false, "Can't get image file info for '" + filename + "'"); + } + } + break; + } + case NETWORK_INTERFACES_INFO: { LOGTRACE("Received %s command", PbOperation_Name(command.operation()).c_str()); diff --git a/src/raspberrypi/rascsi_interface.proto b/src/raspberrypi/rascsi_interface.proto index 8363f750..05c49abc 100644 --- a/src/raspberrypi/rascsi_interface.proto +++ b/src/raspberrypi/rascsi_interface.proto @@ -72,65 +72,70 @@ enum PbOperation { // Device properties by device type DEVICE_TYPES_INFO = 12; - // Gets information on available image files. A lightweight alternative to getting the complete server info. - IMAGE_FILES_INFO = 13; + // Gets information on available image files in the default image folder. + DEFAULT_IMAGE_FILES_INFO = 13; + // Gets 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; + // Gets the names of the available network interfaces. Only lists interfaces that are up. - NETWORK_INTERFACES_INFO = 14; + NETWORK_INTERFACES_INFO = 15; // Gets the mapping of extensions to device types - MAPPING_INFO = 15; + MAPPING_INFO = 16; // Set the default folder for image files. // Parameters: // "folder": The default folder name. - DEFAULT_FOLDER = 16; + DEFAULT_FOLDER = 17; // Set server log level. // Parameters: // "level": The new log level - LOG_LEVEL = 17; + LOG_LEVEL = 18; // 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 = 18; + RESERVE = 19; // 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 = 19; + CREATE_IMAGE = 20; // Delete an image file. // Parameters: // "file": The filename, relative to the default image folder. It must not contain a slash. - DELETE_IMAGE = 20; + DELETE_IMAGE = 21; // 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 = 21; + RENAME_IMAGE = 22; // 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 = 22; + COPY_IMAGE = 23; // Write-protect an image file. // Parameters: // "file": The filename, relative to the default image folder. It must not contain a slash. - PROTECT_IMAGE = 23; + PROTECT_IMAGE = 24; // Make an image file writable. // Parameters: // "file": The filename, relative to the default image folder. It must not contain a slash. - UNPROTECT_IMAGE = 24; + UNPROTECT_IMAGE = 25; } // The supported file extensions mapped to their respective device types @@ -189,9 +194,9 @@ message PbImageFile { string name = 1; // The assumed device type, based on the filename extension PbDeviceType type = 2; - bool read_only = 3; // The file size in bytes, 0 for block devices - int64 size = 4; + uint64 size = 3; + bool read_only = 4; } // The default image folder and the image files it contains @@ -269,12 +274,14 @@ message PbResult { PbDevices device_info = 4; // The result of a DEVICE_TYPES_INFO command PbDeviceTypesInfo device_types_info = 5; - // The result of an IMAGE_FILES_INFO command + // The result of a DEFAULT_IMAGE_FILES_INFO command PbImageFilesInfo image_files_info = 6; + // The result of an IMAGE_FILE_INFO command + PbImageFile image_file_info = 7; // The result of a NETWORK_INTERFACES_INFO command - PbNetworkInterfacesInfo network_interfaces_info = 7; + PbNetworkInterfacesInfo network_interfaces_info = 8; // The result of an MAPPING_INFO command - PbMappingInfo mapping_info = 8; + PbMappingInfo mapping_info = 9; } } diff --git a/src/raspberrypi/rasctl.cpp b/src/raspberrypi/rasctl.cpp index 2410114e..c65f5a60 100644 --- a/src/raspberrypi/rasctl.cpp +++ b/src/raspberrypi/rasctl.cpp @@ -234,6 +234,19 @@ void DisplayDeviceTypesInfo(const PbDeviceTypesInfo& device_types_info) } } +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() }; @@ -249,14 +262,8 @@ void DisplayImageFiles(const PbImageFilesInfo& image_files_info) cout << "Available image files:" << endl; for (const auto& file : files) { - cout << " " << file.name() << " " << file.size() << " bytes"; - if (file.read_only()) { - cout << " read-only"; - } - if (file.type() != UNDEFINED) { - cout << " " << PbDeviceType_Name(file.type()); - } - cout << endl; + cout << " "; + DisplayImageFile(file); } } } @@ -469,7 +476,7 @@ void CommandServerInfo(PbCommand& command, const string& hostname, int port) } } -void CommandImageFilesInfo(const PbCommand& command, const string& hostname, int port) +void CommandDefaultImageFilesInfo(const PbCommand& command, const string& hostname, int port) { PbResult result; SendCommand(hostname.c_str(), port, command, result); @@ -477,6 +484,16 @@ void CommandImageFilesInfo(const PbCommand& command, const string& hostname, int 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; @@ -573,7 +590,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] [-l] [-m] [-s] [-v] [-y]" << endl; + cerr << "[-e] [-E FILENAME] [-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; @@ -605,11 +622,12 @@ int main(int argc, char* argv[]) string default_folder; string reserved_ids; string image_params; + string filename; bool list = false; 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:F:L:")) != -1) { + 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) { switch (opt) { case 'i': device->set_id(optarg[0] - '0'); @@ -641,13 +659,18 @@ int main(int argc, char* argv[]) } break; - case 'F': - command.set_operation(DEFAULT_FOLDER); - default_folder = optarg; + case 'E': + command.set_operation(IMAGE_FILE_INFO); + filename = optarg; break; case 'e': - command.set_operation(IMAGE_FILES_INFO); + command.set_operation(DEFAULT_IMAGE_FILES_INFO); + break; + + case 'F': + command.set_operation(DEFAULT_FOLDER); + default_folder = optarg; break; case 'f': @@ -799,8 +822,12 @@ int main(int argc, char* argv[]) CommandServerInfo(command, hostname, port); exit(EXIT_SUCCESS); - case IMAGE_FILES_INFO: - CommandImageFilesInfo(command, hostname, port); + 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: diff --git a/src/web/file_cmds.py b/src/web/file_cmds.py index f9cb528c..5db0cdc5 100644 --- a/src/web/file_cmds.py +++ b/src/web/file_cmds.py @@ -13,7 +13,7 @@ import rascsi_interface_pb2 as proto def list_files(): command = proto.PbCommand() - command.operation = proto.PbOperation.IMAGE_FILES_INFO + command.operation = proto.PbOperation.DEFAULT_IMAGE_FILES_INFO data = send_pb_command(command.SerializeToString()) result = proto.PbResult()