//--------------------------------------------------------------------------- // // SCSI Target Emulator RaSCSI Reloaded // for Raspberry Pi // // Copyright (C) 2021-2022 Uwe Seimet // //--------------------------------------------------------------------------- #include "rascsi_exceptions.h" #include "protobuf_serializer.h" #include "command_util.h" #include "rasutil.h" #include "rasctl_commands.h" #include "rascsi_interface.pb.h" #include #include #include #include using namespace std; using namespace rascsi_interface; using namespace command_util; // Separator for the INQUIRY name components static const char COMPONENT_SEPARATOR = ':'; RasctlCommands::RasctlCommands(const PbCommand& command, const string& hostname, int port, const string& token, const string& locale) : command(command), hostname(hostname), port(port), token(token), locale(locale) { } void RasctlCommands::Execute(const string& log_level, const string& default_folder, const string& reserved_ids, const string& image_params, const string& filename) { switch(command.operation()) { case LOG_LEVEL: CommandLogLevel(log_level); break; case DEFAULT_FOLDER: CommandDefaultImageFolder(default_folder); break; case RESERVE_IDS: CommandReserveIds(reserved_ids); break; case CREATE_IMAGE: CommandCreateImage(image_params); break; case DELETE_IMAGE: CommandDeleteImage(image_params); break; case RENAME_IMAGE: CommandRenameImage(image_params); break; case COPY_IMAGE: CommandCopyImage(image_params); break; case DEVICES_INFO: CommandDeviceInfo(); break; case DEVICE_TYPES_INFO: CommandDeviceTypesInfo(); break; case VERSION_INFO: CommandVersionInfo(); break; case SERVER_INFO: CommandServerInfo(); break; case DEFAULT_IMAGE_FILES_INFO: CommandDefaultImageFilesInfo(); break; case IMAGE_FILE_INFO: CommandImageFileInfo(filename); break; case NETWORK_INTERFACES_INFO: CommandNetworkInterfacesInfo(); break; case LOG_LEVEL_INFO: CommandLogLevelInfo(); break; case RESERVED_IDS_INFO: CommandReservedIdsInfo(); break; case MAPPING_INFO: CommandMappingInfo(); break; case OPERATION_INFO: CommandOperationInfo(); break; default: SendCommand(); break; } } void RasctlCommands::SendCommand() { if (!token.empty()) { AddParam(command, "token", token); } if (!locale.empty()) { AddParam(command, "locale", locale); } // Send command int fd = -1; try { const 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"); } sockaddr_in server = {}; server.sin_family = AF_INET; server.sin_port = htons((uint16_t)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) { throw io_exception("Can't connect to rascsi process on host '" + hostname + "', port " + to_string(port)); } if (write(fd, "RASCSI", 6) != 6) { throw io_exception("Can't write magic"); } serializer.SerializeMessage(fd, command); } catch(const io_exception& e) { cerr << "Error: " << e.get_msg() << endl; if (fd >= 0) { close(fd); } exit(fd < 0 ? ENOTCONN : EXIT_FAILURE); } // Receive result try { serializer.DeserializeMessage(fd, result); if (!result.status()) { throw io_exception(result.msg()); } } catch(const io_exception& e) { close(fd); cerr << "Error: " << e.get_msg() << 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) { if (size_t separator_pos = image_params.find(COMPONENT_SEPARATOR); separator_pos != string::npos) { AddParam(command, "file", string_view(image_params).substr(0, separator_pos)); AddParam(command, "size", string_view(image_params).substr(separator_pos + 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) { if (size_t separator_pos = image_params.find(COMPONENT_SEPARATOR); separator_pos != string::npos) { AddParam(command, "from", string_view(image_params).substr(0, separator_pos)); AddParam(command, "to", string_view(image_params).substr(separator_pos + 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) { if (size_t separator_pos = image_params.find(COMPONENT_SEPARATOR); separator_pos != string::npos) { AddParam(command, "from", string_view(image_params).substr(0, separator_pos)); AddParam(command, "to", string_view(image_params).substr(separator_pos + 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()); rasctl_display.DisplayOperationInfo(server_info.operation_info()); if (server_info.devices_info().devices_size()) { list sorted_devices = { server_info.devices_info().devices().begin(), server_info.devices_info().devices().end() }; sorted_devices.sort([](const auto& a, const auto& b) { return a.id() < b.id() || a.unit() < b.unit(); }); 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()); } void RasctlCommands::CommandOperationInfo() { SendCommand(); rasctl_display.DisplayOperationInfo(result.operation_info()); }