//--------------------------------------------------------------------------- // // SCSI Target Emulator PiSCSI // for Raspberry Pi // // Copyright (C) 2021-2022 Uwe Seimet // //--------------------------------------------------------------------------- #include "shared/protobuf_util.h" #include "generated/piscsi_interface.pb.h" #include "scsictl_display.h" #include #include #include using namespace std; using namespace piscsi_interface; using namespace protobuf_util; string ScsictlDisplay::DisplayDevicesInfo(const PbDevicesInfo& devices_info) const { ostringstream s; const list& devices = { devices_info.devices().begin(), devices_info.devices().end() }; s << ListDevices(devices); return s.str(); } string ScsictlDisplay::DisplayDeviceInfo(const PbDevice& pb_device) const { ostringstream s; s << " " << 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()) { s << " " << pb_device.block_size() << " bytes per sector"; if (pb_device.block_count()) { s << " " << pb_device.block_size() * pb_device.block_count() << " bytes capacity"; } } if (pb_device.properties().supports_file() && !pb_device.file().name().empty()) { s << " " << pb_device.file().name(); } s << " "; bool hasProperty = false; if (pb_device.properties().read_only()) { s << "read-only"; hasProperty = true; } if (pb_device.properties().protectable() && pb_device.status().protected_()) { if (hasProperty) { s << ", "; } s << "protected"; hasProperty = true; } if (pb_device.properties().stoppable() && pb_device.status().stopped()) { if (hasProperty) { s << ", "; } s << "stopped"; hasProperty = true; } if (pb_device.properties().removable() && pb_device.status().removed()) { if (hasProperty) { s << ", "; } s << "removed"; hasProperty = true; } if (pb_device.properties().lockable() && pb_device.status().locked()) { if (hasProperty) { s << ", "; } s << "locked"; } if (hasProperty) { s << " "; } DisplayParams(s, pb_device); s << '\n'; return s.str(); } string ScsictlDisplay::DisplayVersionInfo(const PbVersionInfo& version_info) const { ostringstream s; s << "piscsi server version: " << setw(2) << setfill('0') << version_info.major_version() << "." << setw(2) << setfill('0') << version_info.minor_version(); if (version_info.patch_version() > 0) { s << "." << version_info.patch_version(); } else if (version_info.patch_version() < 0) { s << " (development version)"; } s << '\n'; return s.str(); } string ScsictlDisplay::DisplayLogLevelInfo(const PbLogLevelInfo& log_level_info) const { ostringstream s; if (!log_level_info.log_levels_size()) { s << " No log level settings available\n"; } else { s << "piscsi log levels, sorted by severity:\n"; for (const auto& log_level : log_level_info.log_levels()) { s << " " << log_level << '\n'; } } s << "Current piscsi log level: " << log_level_info.current_log_level() << '\n'; return s.str(); } string ScsictlDisplay::DisplayDeviceTypesInfo(const PbDeviceTypesInfo& device_types_info) const { ostringstream s; s << "Supported device types and their properties:"; for (const auto& device_type_info : device_types_info.properties()) { s << "\n " << PbDeviceType_Name(device_type_info.type()) << " "; const PbDeviceProperties& properties = device_type_info.properties(); DisplayAttributes(s, properties); if (properties.supports_file()) { s << "Image file support\n "; } if (properties.supports_params()) { s << "Parameter support\n "; } DisplayDefaultParameters(s, properties); DisplayBlockSizes(s, properties); } s << '\n'; return s.str(); } string ScsictlDisplay::DisplayReservedIdsInfo(const PbReservedIdsInfo& reserved_ids_info) const { ostringstream s; if (reserved_ids_info.ids_size()) { s << "Reserved device IDs: "; for (int i = 0; i < reserved_ids_info.ids_size(); i++) { if(i) { s << ", "; } s << reserved_ids_info.ids(i); } s << '\n'; } else { s << "No reserved device IDs\n"; } return s.str(); } string ScsictlDisplay::DisplayImageFile(const PbImageFile& image_file_info) const { ostringstream s; s << image_file_info.name() << " " << image_file_info.size() << " bytes"; if (image_file_info.read_only()) { s << " read-only"; } if (image_file_info.type() != UNDEFINED) { s << " " << PbDeviceType_Name(image_file_info.type()); } s << '\n'; return s.str(); } string ScsictlDisplay::DisplayImageFilesInfo(const PbImageFilesInfo& image_files_info) const { ostringstream s; s << "Default image file folder: " << image_files_info.default_image_folder() << '\n'; s << "Supported folder depth: " << image_files_info.depth() << '\n'; if (image_files_info.image_files().empty()) { s << " No image files available\n"; } else { list image_files = { image_files_info.image_files().begin(), image_files_info.image_files().end() }; image_files.sort([](const auto& a, const auto& b) { return a.name() < b.name(); }); s << "Available image files:\n"; for (const auto& image_file : image_files) { s << " "; s << DisplayImageFile(image_file); } } return s.str(); } string ScsictlDisplay::DisplayNetworkInterfaces(const PbNetworkInterfacesInfo& network_interfaces_info) const { ostringstream s; s << "Available (up) network interfaces:\n"; const list sorted_interfaces = { network_interfaces_info.name().begin(), network_interfaces_info.name().end() }; bool isFirst = true; for (const auto& interface : sorted_interfaces) { if (!isFirst) { s << ", "; } else { s << " "; } isFirst = false; s << interface; } s << '\n'; return s.str(); } string ScsictlDisplay::DisplayMappingInfo(const PbMappingInfo& mapping_info) const { ostringstream s; s << "Supported image file extension to device type mappings:\n"; const map> sorted_mappings = { mapping_info.mapping().begin(), mapping_info.mapping().end() }; for (const auto& [extension, type] : sorted_mappings) { s << " " << extension << "->" << PbDeviceType_Name(type) << '\n'; } return s.str(); } string ScsictlDisplay::DisplayOperationInfo(const PbOperationInfo& operation_info) const { ostringstream s; const map> operations = { operation_info.operations().begin(), operation_info.operations().end() }; // Copies result into a map sorted by operation name auto unknown_operation = make_unique(); map> sorted_operations; for (const auto& [ordinal, meta_data] : operations) { if (PbOperation_IsValid(static_cast(ordinal))) { sorted_operations[PbOperation_Name(static_cast(ordinal))] = meta_data; } else { // If the server-side operation is unknown for the client use the server-provided operation name // No further operation information is available in this case sorted_operations[meta_data.server_side_name()] = *unknown_operation; } } s << "Operations supported by piscsi server and their parameters:\n"; for (const auto& [name, meta_data] : sorted_operations) { if (!meta_data.server_side_name().empty()) { s << " " << name; if (!meta_data.description().empty()) { s << " (" << meta_data.description() << ")"; } s << '\n'; DisplayParameters(s, meta_data); } else { s << " " << name << " (Unknown server-side operation)\n"; } } return s.str(); } void ScsictlDisplay::DisplayParams(ostringstream& s, const PbDevice& pb_device) const { const map> sorted_params = { pb_device.params().begin(), pb_device.params().end() }; bool isFirst = true; for (const auto& [key, value] : sorted_params) { if (!isFirst) { s << ":"; } isFirst = false; s << key << "=" << value; } } void ScsictlDisplay::DisplayAttributes(ostringstream& s, const PbDeviceProperties& properties) const { if (properties.read_only() || properties.protectable() || properties.stoppable() || properties.lockable()) { s << "Properties: "; bool has_property = false; if (properties.read_only()) { s << "read-only"; has_property = true; } if (properties.protectable()) { s << (has_property ? ", " : "") << "protectable"; has_property = true; } if (properties.stoppable()) { s << (has_property ? ", " : "") << "stoppable"; has_property = true; } if (properties.removable()) { s << (has_property ? ", " : "") << "removable"; has_property = true; } if (properties.lockable()) { s << (has_property ? ", " : "") << "lockable"; } s << "\n "; } } void ScsictlDisplay::DisplayDefaultParameters(ostringstream& s, const PbDeviceProperties& properties) const { if (properties.supports_params() && properties.default_params_size()) { s << "Default parameters: "; const map> sorted_params = { properties.default_params().begin(), properties.default_params().end() }; bool isFirst = true; for (const auto& [key, value] : sorted_params) { if (!isFirst) { s << "\n "; } s << key << "=" << value; isFirst = false; } } } void ScsictlDisplay::DisplayBlockSizes(ostringstream& s, const PbDeviceProperties& properties) const { if (properties.block_sizes_size()) { s << "Configurable block sizes in bytes: "; const set sorted_sizes = { properties.block_sizes().begin(), properties.block_sizes().end() }; bool isFirst = true; for (const auto& size : sorted_sizes) { if (!isFirst) { s << ", "; } s << size; isFirst = false; } } } void ScsictlDisplay::DisplayParameters(ostringstream& s, const PbOperationMetaData& meta_data) const { list sorted_parameters = { meta_data.parameters().begin(), meta_data.parameters().end() }; sorted_parameters.sort([](const auto& a, const auto& b) { return a.name() < b.name(); }); for (const auto& parameter : sorted_parameters) { s << " " << parameter.name() << ": " << (parameter.is_mandatory() ? "mandatory" : "optional"); if (!parameter.description().empty()) { s << " (" << parameter.description() << ")"; } s << '\n'; DisplayPermittedValues(s, parameter); if (!parameter.default_value().empty()) { s << " Default value: " << parameter.default_value() << '\n'; } } } void ScsictlDisplay::DisplayPermittedValues(ostringstream& s, const PbOperationParameter& parameter) const { if (parameter.permitted_values_size()) { s << " Permitted values: "; bool isFirst = true; for (const auto& permitted_value : parameter.permitted_values()) { if (!isFirst) { s << ", "; } isFirst = false; s << permitted_value; } s << '\n'; } }