scsictl: Create files with binary/JSON or text format protobuf data (#1369)

This commit is contained in:
Uwe Seimet
2023-11-20 07:40:53 +01:00
committed by GitHub
parent 287b9d7623
commit abc5c4b9ac
4 changed files with 309 additions and 183 deletions
+263 -167
View File
@@ -1,11 +1,12 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator PiSCSI
// for Raspberry Pi
// SCSI Target Emulator PiSCSI
// for Raspberry Pi
//
// Powered by XM6 TypeG Technology.
// Copyright (C) 2016-2020 GIMONS
// Copyright (C) 2020-2023 Contributors to the PiSCSI project
// Powered by XM6 TypeG Technology.
// Copyright (C) 2016-2020 GIMONS
// Copyright (C) 2020-2023 Contributors to the PiSCSI project
// Copyright (C) 2021-2023 Uwe Seimet
//
//---------------------------------------------------------------------------
@@ -18,11 +19,16 @@
#include "scsictl/scsictl_parser.h"
#include "scsictl/scsictl_commands.h"
#include "scsictl/scsictl_core.h"
#include <google/protobuf/util/json_util.h>
#include <google/protobuf/text_format.h>
#include <unistd.h>
#include <clocale>
#include <iostream>
#include <fstream>
using namespace std;
using namespace google::protobuf;
using namespace google::protobuf::util;
using namespace piscsi_interface;
using namespace piscsi_util;
using namespace protobuf_util;
@@ -33,8 +39,8 @@ void ScsiCtl::Banner(const vector<char *>& args) const
cout << piscsi_util::Banner("(Controller App)")
<< "\nUsage: " << args[0] << " -i ID[:LUN] [-c CMD] [-C FILE] [-t TYPE] [-b BLOCK_SIZE] [-n NAME] [-f FILE|PARAM] "
<< "[-F IMAGE_FOLDER] [-L LOG_LEVEL] [-h HOST] [-p PORT] [-r RESERVED_IDS] "
<< "[-C FILENAME:FILESIZE] [-d FILENAME] [-w FILENAME] [-R CURRENT_NAME:NEW_NAME] "
<< "[-x CURRENT_NAME:NEW_NAME] [-z LOCALE] "
<< "[-C FILENAME:FILESIZE] [-d FILENAME] [-B FILENAME] [-J FILENAME] [-T FILENAME] [-R CURRENT_NAME:NEW_NAME] "
<< "[-x CURRENT_NAME:NEW_NAME] [-z LOCALE] "
<< "[-e] [-E FILENAME] [-D] [-I] [-l] [-m] [o] [-O] [-P] [-s] [-S] [-v] [-V] [-y] [-X]\n"
<< " where ID[:LUN] ID := {0-" << (ControllerManager::GetScsiIdMax() - 1) << "},"
<< " LUN := {0-" << (ControllerManager::GetScsiLunMax() - 1) << "}, default is 0\n"
@@ -66,7 +72,7 @@ int ScsiCtl::run(const vector<char *>& args) const
PbCommand command;
PbDeviceDefinition* device = command.add_devices();
device->set_id(-1);
const char *hostname = "localhost";
string hostname = "localhost";
int port = 6868;
string param;
string log_level;
@@ -74,7 +80,10 @@ int ScsiCtl::run(const vector<char *>& args) const
string reserved_ids;
string image_params;
string filename;
string token;
string filename_json;
string filename_binary;
string filename_text;
string token;
bool list = false;
string locale = GetLocale();
@@ -82,186 +91,228 @@ int ScsiCtl::run(const vector<char *>& args) const
opterr = 1;
int opt;
while ((opt = getopt(static_cast<int>(args.size()), args.data(),
"e::lmos::vDINOSTVXa:b:c:d:f:h:i:n:p:r:t:x:z:C:E:F:L:P::R:")) != -1) {
switch (opt) {
case 'i':
if (const string error = SetIdAndLun(*device, optarg); !error.empty()) {
cerr << "Error: " << error << endl;
exit(EXIT_FAILURE);
}
break;
"e::lmos::vDINOSTVXa:b:c:d:f:h:i:n:p:r:t:x:z:B:C:E:F:J:L:P::R:Z:")) != -1) {
switch (opt) {
case 'i':
if (const string error = SetIdAndLun(*device, optarg); !error.empty()) {
cerr << "Error: " << error << endl;
exit(EXIT_FAILURE);
}
break;
case 'C':
command.set_operation(CREATE_IMAGE);
image_params = optarg;
break;
case 'C':
command.set_operation(CREATE_IMAGE);
image_params = optarg;
break;
case 'b':
int block_size;
if (!GetAsUnsignedInt(optarg, block_size)) {
cerr << "Error: Invalid block size " << optarg << endl;
exit(EXIT_FAILURE);
}
device->set_block_size(block_size);
break;
case 'b':
int block_size;
if (!GetAsUnsignedInt(optarg, block_size)) {
cerr << "Error: Invalid block size " << optarg << endl;
exit(EXIT_FAILURE);
}
device->set_block_size(block_size);
break;
case 'c':
command.set_operation(parser.ParseOperation(optarg));
if (command.operation() == NO_OPERATION) {
cerr << "Error: Unknown operation '" << optarg << "'" << endl;
exit(EXIT_FAILURE);
}
break;
case 'c':
command.set_operation(parser.ParseOperation(optarg));
if (command.operation() == NO_OPERATION) {
cerr << "Error: Unknown operation '" << optarg << "'" << endl;
exit(EXIT_FAILURE);
}
break;
case 'D':
command.set_operation(DETACH_ALL);
break;
case 'D':
command.set_operation(DETACH_ALL);
break;
case 'd':
command.set_operation(DELETE_IMAGE);
image_params = optarg;
break;
case 'd':
command.set_operation(DELETE_IMAGE);
image_params = optarg;
break;
case 'E':
command.set_operation(IMAGE_FILE_INFO);
filename = optarg;
break;
case 'E':
filename = optarg;
if (filename.empty()) {
cerr << "Error: Missing filename" << endl;
exit(EXIT_FAILURE);
}
command.set_operation(IMAGE_FILE_INFO);
break;
case 'e':
command.set_operation(DEFAULT_IMAGE_FILES_INFO);
if (optarg) {
SetCommandParams(command, optarg);
case 'e':
command.set_operation(DEFAULT_IMAGE_FILES_INFO);
if (optarg) {
SetCommandParams(command, optarg);
}
break;
case 'F':
command.set_operation(DEFAULT_FOLDER);
default_folder = optarg;
break;
case 'f':
param = optarg;
break;
case 'h':
hostname = optarg;
if (hostname.empty()) {
cerr << "Error: Missing hostname" << endl;
exit(EXIT_FAILURE);
}
break;
case 'B':
filename_binary = optarg;
if (filename_binary.empty()) {
cerr << "Error: Missing filename" << endl;
exit(EXIT_FAILURE);
}
break;
case 'J':
filename_json = optarg;
if (filename_json.empty()) {
cerr << "Error: Missing filename" << endl;
exit(EXIT_FAILURE);
}
break;
case 'Z':
filename_text = optarg;
if (filename_text.empty()) {
cerr << "Error: Missing filename" << endl;
exit(EXIT_FAILURE);
}
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;
case 'N':
command.set_operation(NETWORK_INTERFACES_INFO);
break;
case 'O':
command.set_operation(LOG_LEVEL_INFO);
break;
case 'o':
command.set_operation(OPERATION_INFO);
break;
case 't':
device->set_type(parser.ParseType(optarg));
if (device->type() == UNDEFINED) {
cerr << "Error: Unknown device type '" << optarg << "'" << endl;
exit(EXIT_FAILURE);
}
break;
case 'r':
command.set_operation(RESERVE_IDS);
reserved_ids = optarg;
break;
case 'R':
command.set_operation(RENAME_IMAGE);
image_params = optarg;
break;
case 'n':
SetProductData(*device, optarg);
break;
case 'p':
if (!GetAsUnsignedInt(optarg, port) || port <= 0 || port > 65535) {
cerr << "Error: Invalid port " << optarg << ", port must be between 1 and 65535" << endl;
exit(EXIT_FAILURE);
}
break;
case 's':
command.set_operation(SERVER_INFO);
if (optarg) {
if (const string error = SetCommandParams(command, optarg); !error.empty()) {
cerr << "Error: " << error << endl;
exit(EXIT_FAILURE);
}
break;
}
break;
case 'F':
command.set_operation(DEFAULT_FOLDER);
default_folder = optarg;
break;
case 'S':
command.set_operation(STATISTICS_INFO);
break;
case 'f':
param = optarg;
break;
case 'v':
cout << "scsictl version: " << piscsi_get_version_string() << '\n';
exit(EXIT_SUCCESS);
break;
case 'h':
hostname = optarg;
break;
case 'P':
token = optarg ? optarg : getpass("Password: ");
break;
case 'I':
command.set_operation(RESERVED_IDS_INFO);
break;
case 'V':
command.set_operation(VERSION_INFO);
break;
case 'L':
command.set_operation(LOG_LEVEL);
log_level = optarg;
break;
case 'x':
command.set_operation(COPY_IMAGE);
image_params = optarg;
break;
case 'l':
list = true;
break;
case 'T':
command.set_operation(DEVICE_TYPES_INFO);
break;
case 'm':
command.set_operation(MAPPING_INFO);
break;
case 'X':
command.set_operation(SHUT_DOWN);
SetParam(command, "mode", "rascsi");
break;
case 'N':
command.set_operation(NETWORK_INTERFACES_INFO);
break;
case 'z':
locale = optarg;
break;
case 'O':
command.set_operation(LOG_LEVEL_INFO);
break;
case 'o':
command.set_operation(OPERATION_INFO);
break;
case 't':
device->set_type(parser.ParseType(optarg));
if (device->type() == UNDEFINED) {
cerr << "Error: Unknown device type '" << optarg << "'" << endl;
exit(EXIT_FAILURE);
}
break;
case 'r':
command.set_operation(RESERVE_IDS);
reserved_ids = optarg;
break;
case 'R':
command.set_operation(RENAME_IMAGE);
image_params = optarg;
break;
case 'n':
SetProductData(*device, optarg);
break;
case 'p':
if (!GetAsUnsignedInt(optarg, port) || port <= 0 || port > 65535) {
cerr << "Error: Invalid port " << optarg << ", port must be between 1 and 65535" << endl;
exit(EXIT_FAILURE);
}
break;
case 's':
command.set_operation(SERVER_INFO);
if (optarg) {
if (const string error = SetCommandParams(command, optarg); !error.empty()) {
cerr << "Error: " << error << endl;
exit(EXIT_FAILURE);
}
}
break;
case 'S':
command.set_operation(STATISTICS_INFO);
break;
case 'v':
cout << "scsictl version: " << piscsi_get_version_string() << '\n';
exit(EXIT_SUCCESS);
break;
case 'P':
token = optarg ? optarg : getpass("Password: ");
break;
case 'V':
command.set_operation(VERSION_INFO);
break;
case 'x':
command.set_operation(COPY_IMAGE);
image_params = optarg;
break;
case 'T':
command.set_operation(DEVICE_TYPES_INFO);
break;
case 'X':
command.set_operation(SHUT_DOWN);
SetParam(command, "mode", "rascsi");
break;
case 'z':
locale = optarg;
break;
default:
break;
}
}
default:
break;
}
}
// For macos only 'optind != argc' appears to work, but then non-argument options do not reject arguments
if (optopt) {
exit(EXIT_FAILURE);
}
SetParam(command, "token", token);
SetParam(command, "locale", locale);
if (!filename_json.empty()) {
return ExportAsJson(command, filename_json);
}
if (!filename_binary.empty()) {
return ExportAsBinary(command, filename_binary);
}
if (!filename_text.empty()) {
return ExportAsText(command, filename_text);
}
SetParam(command, "token", token);
SetParam(command, "locale", locale);
ScsictlCommands scsictl_commands(command, hostname, port);
@@ -277,7 +328,7 @@ int ScsiCtl::run(const vector<char *>& args) const
else {
ParseParameters(*device, param);
status = scsictl_commands.Execute(log_level, default_folder, reserved_ids, image_params, filename);
status = scsictl_commands.Execute(log_level, default_folder, reserved_ids, image_params, filename);
}
}
catch(const io_exception& e) {
@@ -290,3 +341,48 @@ int ScsiCtl::run(const vector<char *>& args) const
return status ? EXIT_SUCCESS : EXIT_FAILURE;
}
int ScsiCtl::ExportAsBinary(const PbCommand &command, const string &filename) const
{
const string binary = command.SerializeAsString();
ofstream out;
out.open(filename, ios::binary);
out << binary;
if (out.fail()) {
cerr << "Error: Can't create protobuf binary file '" << filename << "'" << endl;
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
int ScsiCtl::ExportAsJson(const PbCommand &command, const string &filename) const
{
string json;
MessageToJsonString(command, &json);
ofstream out(filename);
out << json;
if (out.fail()) {
cerr << "Error: Can't create protobuf JSON file '" << filename << "'" << endl;
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
int ScsiCtl::ExportAsText(const PbCommand &command, const string &filename) const
{
string text;
TextFormat::PrintToString(command, &text);
ofstream out(filename);
out << text;
if (out.fail()) {
cerr << "Error: Can't create protobuf text format file '" << filename << "'" << endl;
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
+5 -1
View File
@@ -3,7 +3,7 @@
// SCSI Target Emulator PiSCSI
// for Raspberry Pi
//
// Copyright (C) 2022 Uwe Seimet
// Copyright (C) 2022-2023 Uwe Seimet
//
//---------------------------------------------------------------------------
@@ -28,4 +28,8 @@ class ScsiCtl
private:
void Banner(const vector<char *>&) const;
int ExportAsBinary(const PbCommand&, const string&) const;
int ExportAsJson(const PbCommand&, const string&) const;
int ExportAsText(const PbCommand&, const string&) const;
};