Added SHUT_DOWN command, split rascsi.cpp (#334)

* Added TERMINATE command

* Help text update

* Created ras_util namespace

* Created protobuf_util namespace

* Extracted RascsiImage from rascsi for better modularity

* Fixed wrong filenames (class name differed)

* No need for response handler to be a singleton

* Class renaming

* Renaming

* Moved code

* Moved code

* Image folder handling optimizations

* Updated device factory handling

* DeviceFactory constructor should be private (singleton)

* Renamed TERMINATE to SHUT_DOWN

* Fixed capacity calculation for INQUIRY data

* ATTACH and DETACH return the resulting device list for convenience

* Interface comment update

* Moved code, check fd

* Use new command when returning result for ATTACH/DETACH

* Updated interface comments
This commit is contained in:
Uwe Seimet 2021-10-17 08:51:14 +02:00 committed by GitHub
parent 641b176f93
commit 62a104f75d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 617 additions and 494 deletions

View File

@ -14,6 +14,7 @@ rasctl \- Sends management commands to the rascsi process
\fB\-O\fR |
\fB\-T\fR |
\fB\-V\fR |
\fB\-X\fR |
[\fB\-C\fR \fIFILENAME:FILESIZE\fR]
[\fB\-E\fR \fIFILENAME\fR]
[\fB\-F\fR \fIIMAGE_FOLDER\fR]
@ -98,6 +99,9 @@ Display the rascsi server version.
.BR \-V\fI " " \fI
Display the rasctl version.
.TP
.BR \-X\fI " " \fI
Shut down the rascsi process.
.TP
.BR \-d\fI " "\fIFILENAME
Delete an image file in the default image folder.
.TP

View File

@ -6,8 +6,8 @@ NAME
rasctl - Sends management commands to the rascsi process
SYNOPSIS
rasctl -e | -l | -m | -s | -v | -D | -I | -L | -O | -T | -V | [-C FILE
NAME:FILESIZE] [-E FILENAME] [-F IMAGE_FOLDER] [-R CUR
rasctl -e | -l | -m | -s | -v | -D | -I | -L | -O | -T | -V | -X | [-C
FILENAME:FILESIZE] [-E FILENAME] [-F IMAGE_FOLDER] [-R CUR
RENT_NAME:NEW_NAME] [-c CMD] [-f FILE|PARAM] [-g LOG_LEVEL] [-h HOST]
[-i ID [-n NAME] [-p PORT] [-r RESERVED_IDS] [-t TYPE] [-u UNIT] [-x
CURRENT_NAME:NEW_NAME]
@ -77,6 +77,8 @@ OPTIONS
-V Display the rasctl version.
-X Shut down the rascsi process.
-d FILENAME
Delete an image file in the default image folder.

View File

@ -84,8 +84,9 @@ SRC_RASCSI = \
filepath.cpp \
fileio.cpp\
rascsi_version.cpp \
rascsi_image.cpp \
rascsi_response.cpp \
rasutil.cpp \
protobuf_response_helper.cpp \
protobuf_util.cpp
SRC_RASCSI += $(shell find ./controllers -name '*.cpp')
SRC_RASCSI += $(shell find ./devices -name '*.cpp')

View File

@ -26,11 +26,12 @@ class Device;
class DeviceFactory
{
public:
private:
DeviceFactory();
~DeviceFactory() {};
public:
static DeviceFactory& instance();
Device *CreateDevice(PbDeviceType, const string&);

View File

@ -45,14 +45,13 @@ void SCSIHD::FinalizeSetup(const Filepath &path, off_t size)
// For non-removable media drives set the default product name based on the drive capacity
if (!IsRemovable()) {
int capacity;
uint64_t capacity = GetBlockCount() * GetSectorSizeInBytes();
string unit;
if (GetBlockCount() >> 11 >= 1) {
capacity = GetBlockCount() >> 11;
if (capacity >= 1000000) {
capacity /= 1000000;
unit = "MB";
}
else {
capacity = GetBlockCount() >> 1;
unit = "KB";
}
stringstream product;

View File

@ -8,6 +8,8 @@
//---------------------------------------------------------------------------
#include <unistd.h>
#include "os.h"
#include "log.h"
#include "rascsi_interface.pb.h"
#include "exceptions.h"
#include "protobuf_util.h"
@ -15,20 +17,21 @@
using namespace std;
using namespace rascsi_interface;
#define FPRT(fp, ...) fprintf(fp, __VA_ARGS__ )
const string GetParam(const PbCommand& command, const string& key)
const string protobuf_util::GetParam(const PbCommand& command, const string& key)
{
auto map = command.params();
return map[key];
}
const string GetParam(const PbDeviceDefinition& device, const string& key)
const string protobuf_util::GetParam(const PbDeviceDefinition& device, const string& key)
{
auto map = device.params();
return map[key];
}
void AddParam(PbCommand& command, const string& key, const string& value)
void protobuf_util::AddParam(PbCommand& command, const string& key, const string& value)
{
if (!key.empty() && !value.empty()) {
auto& map = *command.mutable_params();
@ -36,7 +39,7 @@ void AddParam(PbCommand& command, const string& key, const string& value)
}
}
void AddParam(PbDevice& device, const string& key, const string& value)
void protobuf_util::AddParam(PbDevice& device, const string& key, const string& value)
{
if (!key.empty() && !value.empty()) {
auto& map = *device.mutable_params();
@ -44,7 +47,7 @@ void AddParam(PbDevice& device, const string& key, const string& value)
}
}
void AddParam(PbDeviceDefinition& device, const string& key, const string& value)
void protobuf_util::AddParam(PbDeviceDefinition& device, const string& key, const string& value)
{
if (!key.empty() && !value.empty()) {
auto& map = *device.mutable_params();
@ -59,7 +62,7 @@ void AddParam(PbDeviceDefinition& device, const string& key, const string& value
//
//---------------------------------------------------------------------------
void SerializeMessage(int fd, const google::protobuf::Message& message)
void protobuf_util::SerializeMessage(int fd, const google::protobuf::Message& message)
{
string data;
message.SerializeToString(&data);
@ -76,7 +79,7 @@ void SerializeMessage(int fd, const google::protobuf::Message& message)
}
}
void DeserializeMessage(int fd, google::protobuf::Message& message)
void protobuf_util::DeserializeMessage(int fd, google::protobuf::Message& message)
{
// Read the header with the size of the protobuf data
uint8_t header_buf[4];
@ -98,7 +101,7 @@ void DeserializeMessage(int fd, google::protobuf::Message& message)
message.ParseFromString(data);
}
int ReadNBytes(int fd, uint8_t *buf, int n)
int protobuf_util::ReadNBytes(int fd, uint8_t *buf, int n)
{
int offset = 0;
while (offset < n) {
@ -112,3 +115,38 @@ int ReadNBytes(int fd, uint8_t *buf, int n)
return offset;
}
bool protobuf_util::ReturnStatus(int fd, bool status, const string msg)
{
if (!status && !msg.empty()) {
LOGERROR("%s", msg.c_str());
}
if (fd == -1) {
if (!msg.empty()) {
if (status) {
FPRT(stderr, "Error: ");
FPRT(stderr, "%s", msg.c_str());
FPRT(stderr, "\n");
}
else {
FPRT(stdout, "%s", msg.c_str());
FPRT(stderr, "\n");
}
}
}
else {
PbResult result;
result.set_status(status);
result.set_msg(msg);
SerializeMessage(fd, result);
}
return status;
}
bool protobuf_util::ReturnStatus(int fd, bool status, const ostringstream& msg)
{
return ReturnStatus(fd, status, msg.str());
}

View File

@ -13,16 +13,22 @@
#include "google/protobuf/message.h"
#include "rascsi_interface.pb.h"
#include <sstream>
#include <string>
using namespace std;
using namespace rascsi_interface;
const string GetParam(const PbCommand&, const string&);
const string GetParam(const PbDeviceDefinition&, const string&);
void AddParam(PbCommand&, const string&, const string&);
void AddParam(PbDevice&, const string&, const string&);
void AddParam(PbDeviceDefinition&, const string&, const string&);
void SerializeMessage(int, const google::protobuf::Message&);
void DeserializeMessage(int, google::protobuf::Message&);
int ReadNBytes(int, uint8_t *, int);
namespace protobuf_util
{
const string GetParam(const PbCommand&, const string&);
const string GetParam(const PbDeviceDefinition&, const string&);
void AddParam(PbCommand&, const string&, const string&);
void AddParam(PbDevice&, const string&, const string&);
void AddParam(PbDeviceDefinition&, const string&, const string&);
void SerializeMessage(int, const google::protobuf::Message&);
void DeserializeMessage(int, google::protobuf::Message&);
int ReadNBytes(int, uint8_t *, int);
bool ReturnStatus(int, bool = true, const string = "");
bool ReturnStatus(int, bool, const ostringstream&);
}

View File

@ -23,15 +23,15 @@
#include "devices/file_support.h"
#include "gpiobus.h"
#include "exceptions.h"
#include "protobuf_response_helper.h"
#include "protobuf_util.h"
#include "rascsi_version.h"
#include "rascsi_response.h"
#include "rasutil.h"
#include "rascsi_image.h"
#include "rascsi_interface.pb.h"
#include "spdlog/spdlog.h"
#include "spdlog/sinks/stdout_color_sinks.h"
#include <spdlog/async.h>
#include <sys/sendfile.h>
#include <dirent.h>
#include <ifaddrs.h>
#include <string>
@ -45,6 +45,8 @@
using namespace std;
using namespace spdlog;
using namespace rascsi_interface;
using namespace ras_util;
using namespace protobuf_util;
//---------------------------------------------------------------------------
//
@ -70,10 +72,10 @@ pthread_t monthread; // Monitor Thread
pthread_mutex_t ctrl_mutex; // Semaphore for the ctrl array
static void *MonThread(void *param);
string current_log_level; // Some versions of spdlog do not support get_log_level()
string default_image_folder;
set<int> reserved_ids;
DeviceFactory& device_factory = DeviceFactory::instance();
ProtobufResponseHandler& response_helper = ProtobufResponseHandler::instance();
RascsiImage rascsi_image;
RascsiResponse rascsi_response(&device_factory, &rascsi_image);
//---------------------------------------------------------------------------
//
@ -417,40 +419,6 @@ string ValidateLunSetup(const PbCommand& command, const vector<Device *>& existi
return "";
}
bool ReturnStatus(int fd, bool status = true, const string msg = "")
{
if (!status && !msg.empty()) {
LOGERROR("%s", msg.c_str());
}
if (fd == -1) {
if (!msg.empty()) {
if (status) {
FPRT(stderr, "Error: ");
FPRT(stderr, "%s", msg.c_str());
FPRT(stderr, "\n");
}
else {
FPRT(stdout, "%s", msg.c_str());
FPRT(stderr, "\n");
}
}
}
else {
PbResult result;
result.set_status(status);
result.set_msg(msg);
SerializeMessage(fd, result);
}
return status;
}
bool ReturnStatus(int fd, bool status, const ostringstream& msg)
{
return ReturnStatus(fd, status, msg.str());
}
bool SetLogLevel(const string& log_level)
{
if (log_level == "trace") {
@ -493,44 +461,6 @@ void LogDevices(const string& devices)
}
}
string SetDefaultImageFolder(const string& f)
{
string folder = f;
// If a relative path is specified the path is assumed to be relative to the user's home directory
if (folder[0] != '/') {
int uid = getuid();
const char *sudo_user = getenv("SUDO_UID");
if (sudo_user) {
uid = stoi(sudo_user);
}
const passwd *passwd = getpwuid(uid);
if (passwd) {
folder = passwd->pw_dir;
folder += "/";
folder += f;
}
}
else {
if (folder.find("/home/") != 0) {
return "Default image folder must be located in '/home/'";
}
}
struct stat info;
stat(folder.c_str(), &info);
if (!S_ISDIR(info.st_mode) || access(folder.c_str(), F_OK) == -1) {
return string("Folder '" + f + "' does not exist or is not accessible");
}
default_image_folder = folder;
LOGINFO("Default image folder set to '%s'", default_image_folder.c_str());
return "";
}
string SetReservedIds(const string& ids)
{
list<string> ids_to_reserve;
@ -578,264 +508,6 @@ string SetReservedIds(const string& ids)
return "";
}
bool IsValidSrcFilename(const string& filename)
{
// Source file must exist and must be a regular file or a symlink
struct stat st;
return !stat(filename.c_str(), &st) && (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode));
}
bool IsValidDstFilename(const string& filename)
{
// Destination file must not yet exist
struct stat st;
return stat(filename.c_str(), &st);
}
bool CreateImage(int fd, const PbCommand& command)
{
string filename = GetParam(command, "file");
if (filename.empty()) {
return ReturnStatus(fd, false, "Can't create image file: Missing image filename");
}
if (filename.find('/') != string::npos) {
return ReturnStatus(fd, false, "Can't create image file '" + filename + "': Filename must not contain a path");
}
filename = default_image_folder + "/" + filename;
if (!IsValidDstFilename(filename)) {
return ReturnStatus(fd, false, "Can't create image file: '" + filename + "': File already exists");
}
const string size = GetParam(command, "size");
if (size.empty()) {
return ReturnStatus(fd, false, "Can't create image file '" + filename + "': Missing image size");
}
off_t len;
try {
len = stoul(size);
}
catch(const invalid_argument& e) {
return ReturnStatus(fd, false, "Can't create image file '" + filename + "': Invalid file size " + size);
}
catch(const out_of_range& e) {
return ReturnStatus(fd, false, "Can't create image file '" + filename + "': Invalid file size " + size);
}
if (len < 512 || (len & 0x1ff)) {
ostringstream error;
error << "Invalid image file size " << len;
return ReturnStatus(fd, false, error.str());
}
struct stat st;
if (!stat(filename.c_str(), &st)) {
return ReturnStatus(fd, false, "Can't create image file '" + filename + "': File already exists");
}
string permission = GetParam(command, "read_only");
// Since rascsi is running as root ensure that others can access the file
int permissions = !strcasecmp(permission.c_str(), "true") ?
S_IRUSR | S_IRGRP | S_IROTH : S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
int image_fd = open(filename.c_str(), O_CREAT|O_WRONLY, permissions);
if (image_fd == -1) {
return ReturnStatus(fd, false, "Can't create image file '" + filename + "': " + string(strerror(errno)));
}
if (fallocate(image_fd, 0, 0, len) == -1) {
close(image_fd);
return ReturnStatus(fd, false, "Can't allocate space for image file '" + filename + "': " + string(strerror(errno)));
}
close(image_fd);
ostringstream msg;
msg << "Created " << (permissions & S_IWUSR ? "": "read-only ") << "image file '" << filename + "' with a size of " << len << " bytes";
LOGINFO("%s", msg.str().c_str());
return ReturnStatus(fd);
}
bool DeleteImage(int fd, const PbCommand& command)
{
string filename = GetParam(command, "file");
if (filename.empty()) {
return ReturnStatus(fd, false, "Missing image filename");
}
if (!IsValidDstFilename(filename)) {
return ReturnStatus(fd, false, "Can't delete image file '" + filename + "': File already exists");
}
if (filename.find('/') != string::npos) {
return ReturnStatus(fd, false, "The image filename '" + filename + "' must not contain a path");
}
filename = default_image_folder + "/" + filename;
int id;
int unit;
Filepath filepath;
filepath.SetPath(filename.c_str());
if (FileSupport::GetIdsForReservedFile(filepath, id, unit)) {
ostringstream msg;
msg << "Can't delete image file '" << filename << "', it is used by device ID " << id << ", unit " << unit;
return ReturnStatus(fd, false, msg.str());
}
if (unlink(filename.c_str())) {
return ReturnStatus(fd, false, "Can't delete image file '" + filename + "': " + string(strerror(errno)));
}
LOGINFO("Deleted image file '%s'", filename.c_str());
return ReturnStatus(fd);
}
bool RenameImage(int fd, const PbCommand& command)
{
string from = GetParam(command, "from");
if (from.empty()) {
return ReturnStatus(fd, false, "Can't rename image file: Missing source filename");
}
if (from.find('/') != string::npos) {
return ReturnStatus(fd, false, "The source filename '" + from + "' must not contain a path");
}
from = default_image_folder + "/" + from;
if (!IsValidSrcFilename(from)) {
return ReturnStatus(fd, false, "Can't rename image file: '" + from + "': Invalid name or type");
}
string to = GetParam(command, "to");
if (to.empty()) {
return ReturnStatus(fd, false, "Can't rename image file '" + from + "': Missing destination filename");
}
if (to.find('/') != string::npos) {
return ReturnStatus(fd, false, "The destination filename '" + to + "' must not contain a path");
}
to = default_image_folder + "/" + to;
if (!IsValidDstFilename(to)) {
return ReturnStatus(fd, false, "Can't rename image file '" + from + "' to '" + to + "': File already exists");
}
if (rename(from.c_str(), to.c_str())) {
return ReturnStatus(fd, false, "Can't rename image file '" + from + "' to '" + to + "': " + string(strerror(errno)));
}
LOGINFO("Renamed image file '%s' to '%s'", from.c_str(), to.c_str());
return ReturnStatus(fd);
}
bool CopyImage(int fd, const PbCommand& command)
{
string from = GetParam(command, "from");
if (from.empty()) {
return ReturnStatus(fd, false, "Can't copy image file: Missing source filename");
}
if (from.find('/') != string::npos) {
return ReturnStatus(fd, false, "The source filename '" + from + "' must not contain a path");
}
from = default_image_folder + "/" + from;
if (!IsValidSrcFilename(from)) {
return ReturnStatus(fd, false, "Can't copy image file: '" + from + "': Invalid name or type");
}
string to = GetParam(command, "to");
if (to.empty()) {
return ReturnStatus(fd, false, "Can't copy image file '" + from + "': Missing destination filename");
}
if (to.find('/') != string::npos) {
return ReturnStatus(fd, false, "The destination filename '" + to + "' must not contain a path");
}
to = default_image_folder + "/" + to;
if (!IsValidDstFilename(to)) {
return ReturnStatus(fd, false, "Can't copy image file '" + from + "' to '" + to + "': File already exists");
}
struct stat st;
if (lstat(from.c_str(), &st)) {
return ReturnStatus(fd, false, "Can't access source image file '" + from + "': " + string(strerror(errno)));
}
// Symbolic links need a special handling
if ((st.st_mode & S_IFMT) == S_IFLNK) {
if (symlink(filesystem::read_symlink(from).c_str(), to.c_str())) {
return ReturnStatus(fd, false, "Can't copy symlink '" + from + "': " + string(strerror(errno)));
}
LOGINFO("Copied symlink '%s' to '%s'", from.c_str(), to.c_str());
return ReturnStatus(fd);
}
int fd_src = open(from.c_str(), O_RDONLY, 0);
if (fd_src == -1) {
return ReturnStatus(fd, false, "Can't open source image file '" + from + "': " + string(strerror(errno)));
}
string permission = GetParam(command, "read_only");
// Since rascsi is running as root ensure that others can access the file
int permissions = !strcasecmp(permission.c_str(), "true") ?
S_IRUSR | S_IRGRP | S_IROTH : S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
int fd_dst = open(to.c_str(), O_WRONLY | O_CREAT, permissions);
if (fd_dst == -1) {
close(fd_src);
return ReturnStatus(fd, false, "Can't open destination image file '" + to + "': " + string(strerror(errno)));
}
if (sendfile(fd_dst, fd_src, 0, st.st_size) == -1) {
close(fd_dst);
close(fd_src);
return ReturnStatus(fd, false, "Can't copy image file '" + from + "' to '" + to + "': " + string(strerror(errno)));
}
close(fd_dst);
close(fd_src);
LOGINFO("Copied image file '%s' to '%s'", from.c_str(), to.c_str());
return ReturnStatus(fd);
}
bool SetImagePermissions(int fd, const PbCommand& command)
{
string filename = GetParam(command, "file");
if (filename.empty()) {
return ReturnStatus(fd, false, "Missing image filename");
}
if (filename.find('/') != string::npos) {
return ReturnStatus(fd, false, "The image filename '" + filename + "' must not contain a path");
}
filename = default_image_folder + "/" + filename;
if (!IsValidSrcFilename(filename)) {
return ReturnStatus(fd, false, "Can't modify image file '" + filename + "': Invalid name or type");
}
bool protect = command.operation() == PROTECT_IMAGE;
int permissions = protect ? S_IRUSR | S_IRGRP | S_IROTH : S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
if (chmod(filename.c_str(), permissions) == -1) {
ostringstream error;
error << "Can't " << (protect ? "protect" : "unprotect") << " image file '" << filename << "': " << strerror(errno);
return ReturnStatus(fd, false, error.str());
}
if (protect) {
LOGINFO("Protected image file '%s'", filename.c_str());
}
else {
LOGINFO("Unprotected image file '%s'", filename.c_str());
}
return ReturnStatus(fd);
}
void DetachAll()
{
Device *map[devices.size()];
@ -951,7 +623,7 @@ bool Attach(int fd, const PbDeviceDefinition& pb_device, Device *map[], bool dry
}
catch(const file_not_found_exception&) {
// If the file does not exist search for it in the default image folder
filepath.SetPath(string(default_image_folder + "/" + filename).c_str());
filepath.SetPath(string(rascsi_image.GetDefaultImageFolder() + "/" + filename).c_str());
file_support->Open(filepath);
}
}
@ -1020,8 +692,7 @@ bool Attach(int fd, const PbDeviceDefinition& pb_device, Device *map[], bool dry
bool Detach(int fd, Device *device, Device *map[], bool dryRun)
{
if (!dryRun) {
for (size_t i = devices.size() - 1; i > 0; i--) {
Device *d = map[i];
for (auto const& d : devices) {
// Detach all LUNs equal to or higher than the LUN specified
if (d && d->GetId() == device->GetId() && d->GetLun() >= device->GetLun()) {
map[d->GetId() * UnitNum + d->GetLun()] = NULL;
@ -1033,10 +704,10 @@ bool Detach(int fd, Device *device, Device *map[], bool dryRun)
LOGINFO("Detached %s device with ID %d, unit %d", d->GetType().c_str(), d->GetId(), d->GetLun());
}
// Re-map the controller
MapController(map);
}
// Re-map the controller
MapController(map);
}
return true;
@ -1096,7 +767,7 @@ bool Insert(int fd, const PbDeviceDefinition& pb_device, Device *device, bool dr
}
catch(const file_not_found_exception&) {
// If the file does not exist search for it in the default image folder
filepath.SetPath(string(default_image_folder + "/" + filename).c_str());
filepath.SetPath((rascsi_image.GetDefaultImageFolder() + "/" + filename).c_str());
file_support->Open(filepath);
}
}
@ -1114,6 +785,13 @@ bool Insert(int fd, const PbDeviceDefinition& pb_device, Device *device, bool dr
return true;
}
void TerminationHandler(int signum)
{
DetachAll();
exit(signum);
}
//---------------------------------------------------------------------------
//
// Command Processing
@ -1306,20 +984,20 @@ bool ProcessCmd(const int fd, const PbCommand& command)
}
case CREATE_IMAGE:
return CreateImage(fd, command);
return rascsi_image.CreateImage(fd, command);
case DELETE_IMAGE:
return DeleteImage(fd, command);
return rascsi_image.DeleteImage(fd, command);
case RENAME_IMAGE:
return RenameImage(fd, command);
return rascsi_image.RenameImage(fd, command);
case COPY_IMAGE:
return CopyImage(fd, command);
return rascsi_image.CopyImage(fd, command);
case PROTECT_IMAGE:
case UNPROTECT_IMAGE:
return SetImagePermissions(fd, command);
return rascsi_image.SetImagePermissions(fd, command);
default:
// This is a device-specific command handled below
@ -1350,6 +1028,16 @@ bool ProcessCmd(const int fd, const PbCommand& command)
}
}
// ATTACH and DETACH return the device list
if (fd != -1 && (command.operation() == ATTACH || command.operation() == DETACH)) {
// A new command with an empty device list is required here in order to return data for all devices
PbCommand command;
PbResult result;
rascsi_response.GetDevicesInfo(result, command, devices, UnitNum);
SerializeMessage(fd, result);
return true;
}
return ReturnStatus(fd);
}
@ -1434,7 +1122,7 @@ bool ParseArgument(int argc, char* argv[], int& port)
}
case 'F': {
string result = SetDefaultImageFolder(optarg);
string result = rascsi_image.SetDefaultImageFolder(optarg);
if (!result.empty()) {
cerr << result << endl;
return false;
@ -1532,7 +1220,7 @@ bool ParseArgument(int argc, char* argv[], int& port)
// Display and log the device list
PbServerInfo server_info;
response_helper.GetDevices(server_info, devices, default_image_folder);
rascsi_response.GetDevices(server_info, devices);
const list<PbDevice>& devices = { server_info.devices_info().devices().begin(), server_info.devices_info().devices().end() };
const string device_list = ListDevices(devices);
LogDevices(device_list);
@ -1625,7 +1313,7 @@ static void *MonThread(void *param)
ReturnStatus(fd, false, "Can't set default image folder: Missing folder name");
}
string result = SetDefaultImageFolder(folder);
string result = rascsi_image.SetDefaultImageFolder(folder);
if (!result.empty()) {
ReturnStatus(fd, false, result);
}
@ -1639,7 +1327,7 @@ static void *MonThread(void *param)
LOGTRACE("Received %s command", PbOperation_Name(command.operation()).c_str());
PbResult result;
response_helper.GetDevicesInfo(result, command, devices, default_image_folder, UnitNum);
rascsi_response.GetDevicesInfo(result, command, devices, UnitNum);
SerializeMessage(fd, result);
// For backwards compatibility: Log device list if information on all devices was requested.
@ -1654,7 +1342,7 @@ static void *MonThread(void *param)
LOGTRACE("Received %s command", PbOperation_Name(command.operation()).c_str());
PbResult result;
result.set_allocated_device_types_info(response_helper.GetDeviceTypesInfo(result, command));
result.set_allocated_device_types_info(rascsi_response.GetDeviceTypesInfo(result, command));
SerializeMessage(fd, result);
break;
}
@ -1663,8 +1351,8 @@ static void *MonThread(void *param)
LOGTRACE("Received %s command", PbOperation_Name(command.operation()).c_str());
PbResult result;
result.set_allocated_server_info(response_helper.GetServerInfo(
result, devices, reserved_ids, default_image_folder, current_log_level));
result.set_allocated_server_info(rascsi_response.GetServerInfo(
result, devices, reserved_ids, current_log_level));
SerializeMessage(fd, result);
break;
}
@ -1673,7 +1361,7 @@ static void *MonThread(void *param)
LOGTRACE("Received %s command", PbOperation_Name(command.operation()).c_str());
PbResult result;
result.set_allocated_version_info(response_helper.GetVersionInfo(result));
result.set_allocated_version_info(rascsi_response.GetVersionInfo(result));
SerializeMessage(fd, result);
break;
}
@ -1682,7 +1370,7 @@ static void *MonThread(void *param)
LOGTRACE("Received %s command", PbOperation_Name(command.operation()).c_str());
PbResult result;
result.set_allocated_log_level_info(response_helper.GetLogLevelInfo(result, current_log_level));
result.set_allocated_log_level_info(rascsi_response.GetLogLevelInfo(result, current_log_level));
SerializeMessage(fd, result);
break;
}
@ -1691,7 +1379,7 @@ static void *MonThread(void *param)
LOGTRACE("Received %s command", PbOperation_Name(command.operation()).c_str());
PbResult result;
result.set_allocated_image_files_info(response_helper.GetAvailableImages(result, default_image_folder));
result.set_allocated_image_files_info(rascsi_response.GetAvailableImages(result));
SerializeMessage(fd, result);
break;
}
@ -1706,7 +1394,7 @@ static void *MonThread(void *param)
else {
PbResult result;
PbImageFile* image_file = new PbImageFile();
bool status = response_helper.GetImageFile(image_file, filename, default_image_folder);
bool status = rascsi_response.GetImageFile(image_file, filename);
if (status) {
result.set_status(true);
result.set_allocated_image_file_info(image_file);
@ -1723,7 +1411,7 @@ static void *MonThread(void *param)
LOGTRACE("Received %s command", PbOperation_Name(command.operation()).c_str());
PbResult result;
result.set_allocated_network_interfaces_info(response_helper.GetNetworkInterfacesInfo(result));
result.set_allocated_network_interfaces_info(rascsi_response.GetNetworkInterfacesInfo(result));
SerializeMessage(fd, result);
break;
}
@ -1732,7 +1420,7 @@ static void *MonThread(void *param)
LOGTRACE("Received %s command", PbOperation_Name(command.operation()).c_str());
PbResult result;
result.set_allocated_mapping_info(response_helper.GetMappingInfo(result));
result.set_allocated_mapping_info(rascsi_response.GetMappingInfo(result));
SerializeMessage(fd, result);
break;
}
@ -1741,11 +1429,22 @@ static void *MonThread(void *param)
LOGTRACE("Received %s command", PbOperation_Name(command.operation()).c_str());
PbResult result;
result.set_allocated_reserved_ids_info(response_helper.GetReservedIds(result, reserved_ids));
result.set_allocated_reserved_ids_info(rascsi_response.GetReservedIds(result, reserved_ids));
SerializeMessage(fd, result);
break;
}
case SHUT_DOWN: {
LOGTRACE("Received %s command", PbOperation_Name(command.operation()).c_str());
PbResult result;
result.set_status(true);
SerializeMessage(fd, result);
TerminationHandler(0);
break;
}
default: {
// Wait until we become idle
while (active) {
@ -1771,13 +1470,6 @@ static void *MonThread(void *param)
return NULL;
}
void TerminationHandler(int signum)
{
DetachAll();
exit(signum);
}
//---------------------------------------------------------------------------
//
// Main processing
@ -1810,21 +1502,6 @@ int main(int argc, char* argv[])
// Create a thread-safe stdout logger to process the log messages
auto logger = stdout_color_mt("rascsi stdout logger");
// ~/images is the default folder for device image files, for the root user it is /home/pi/images
int uid = getuid();
const char *sudo_user = getenv("SUDO_UID");
if (sudo_user) {
uid = stoi(sudo_user);
}
const passwd *passwd = getpwuid(uid);
if (uid && passwd) {
default_image_folder = passwd->pw_dir;
default_image_folder += "/images";
}
else {
default_image_folder = "/home/pi/images";
}
int port = 6868;
if (!InitBus()) {

View File

@ -0,0 +1,343 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI (*^..^*)
// for Raspberry Pi
//
// Copyright (C) 2021 Uwe Seimet
//
//---------------------------------------------------------------------------
#include <unistd.h>
#include <sys/sendfile.h>
#include "os.h"
#include "log.h"
#include "filepath.h"
#include "spdlog/spdlog.h"
#include "devices/file_support.h"
#include "protobuf_util.h"
#include "rascsi_image.h"
#include <sstream>
#include <string>
#include <filesystem>
using namespace std;
using namespace spdlog;
using namespace rascsi_interface;
using namespace protobuf_util;
#define FPRT(fp, ...) fprintf(fp, __VA_ARGS__ )
RascsiImage::RascsiImage()
{
// ~/images is the default folder for device image files, for the root user it is /home/pi/images
int uid = getuid();
const char *sudo_user = getenv("SUDO_UID");
if (sudo_user) {
uid = stoi(sudo_user);
}
const passwd *passwd = getpwuid(uid);
if (uid && passwd) {
default_image_folder = passwd->pw_dir;
default_image_folder += "/images";
}
else {
default_image_folder = "/home/pi/images";
}
}
string RascsiImage::SetDefaultImageFolder(const string& f)
{
string folder = f;
// If a relative path is specified the path is assumed to be relative to the user's home directory
if (folder[0] != '/') {
int uid = getuid();
const char *sudo_user = getenv("SUDO_UID");
if (sudo_user) {
uid = stoi(sudo_user);
}
const passwd *passwd = getpwuid(uid);
if (passwd) {
folder = passwd->pw_dir;
folder += "/";
folder += f;
}
}
else {
if (folder.find("/home/") != 0) {
return "Default image folder must be located in '/home/'";
}
}
struct stat info;
stat(folder.c_str(), &info);
if (!S_ISDIR(info.st_mode) || access(folder.c_str(), F_OK) == -1) {
return "Folder '" + f + "' does not exist or is not accessible";
}
default_image_folder = folder;
LOGINFO("Default image folder set to '%s'", default_image_folder.c_str());
return "";
}
bool RascsiImage::IsValidSrcFilename(const string& filename)
{
// Source file must exist and must be a regular file or a symlink
struct stat st;
return !stat(filename.c_str(), &st) && (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode));
}
bool RascsiImage::IsValidDstFilename(const string& filename)
{
// Destination file must not yet exist
struct stat st;
return stat(filename.c_str(), &st);
}
bool RascsiImage::CreateImage(int fd, const PbCommand& command)
{
string filename = GetParam(command, "file");
if (filename.empty()) {
return ReturnStatus(fd, false, "Can't create image file: Missing image filename");
}
if (filename.find('/') != string::npos) {
return ReturnStatus(fd, false, "Can't create image file '" + filename + "': Filename must not contain a path");
}
filename = default_image_folder + "/" + filename;
if (!IsValidDstFilename(filename)) {
return ReturnStatus(fd, false, "Can't create image file: '" + filename + "': File already exists");
}
const string size = GetParam(command, "size");
if (size.empty()) {
return ReturnStatus(fd, false, "Can't create image file '" + filename + "': Missing image size");
}
off_t len;
try {
len = stoul(size);
}
catch(const invalid_argument& e) {
return ReturnStatus(fd, false, "Can't create image file '" + filename + "': Invalid file size " + size);
}
catch(const out_of_range& e) {
return ReturnStatus(fd, false, "Can't create image file '" + filename + "': Invalid file size " + size);
}
if (len < 512 || (len & 0x1ff)) {
ostringstream error;
error << "Invalid image file size " << len;
return ReturnStatus(fd, false, error.str());
}
struct stat st;
if (!stat(filename.c_str(), &st)) {
return ReturnStatus(fd, false, "Can't create image file '" + filename + "': File already exists");
}
string permission = GetParam(command, "read_only");
// Since rascsi is running as root ensure that others can access the file
int permissions = !strcasecmp(permission.c_str(), "true") ?
S_IRUSR | S_IRGRP | S_IROTH : S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
int image_fd = open(filename.c_str(), O_CREAT|O_WRONLY, permissions);
if (image_fd == -1) {
return ReturnStatus(fd, false, "Can't create image file '" + filename + "': " + string(strerror(errno)));
}
if (fallocate(image_fd, 0, 0, len) == -1) {
close(image_fd);
return ReturnStatus(fd, false, "Can't allocate space for image file '" + filename + "': " + string(strerror(errno)));
}
close(image_fd);
ostringstream msg;
msg << "Created " << (permissions & S_IWUSR ? "": "read-only ") << "image file '" << filename + "' with a size of " << len << " bytes";
LOGINFO("%s", msg.str().c_str());
return ReturnStatus(fd);
}
bool RascsiImage::DeleteImage(int fd, const PbCommand& command)
{
string filename = GetParam(command, "file");
if (filename.empty()) {
return ReturnStatus(fd, false, "Missing image filename");
}
if (!IsValidDstFilename(filename)) {
return ReturnStatus(fd, false, "Can't delete image file '" + filename + "': File already exists");
}
if (filename.find('/') != string::npos) {
return ReturnStatus(fd, false, "The image filename '" + filename + "' must not contain a path");
}
filename = default_image_folder + "/" + filename;
int id;
int unit;
Filepath filepath;
filepath.SetPath(filename.c_str());
if (FileSupport::GetIdsForReservedFile(filepath, id, unit)) {
ostringstream msg;
msg << "Can't delete image file '" << filename << "', it is used by device ID " << id << ", unit " << unit;
return ReturnStatus(fd, false, msg.str());
}
if (unlink(filename.c_str())) {
return ReturnStatus(fd, false, "Can't delete image file '" + filename + "': " + string(strerror(errno)));
}
LOGINFO("Deleted image file '%s'", filename.c_str());
return ReturnStatus(fd);
}
bool RascsiImage::RenameImage(int fd, const PbCommand& command)
{
string from = GetParam(command, "from");
if (from.empty()) {
return ReturnStatus(fd, false, "Can't rename image file: Missing source filename");
}
if (from.find('/') != string::npos) {
return ReturnStatus(fd, false, "The source filename '" + from + "' must not contain a path");
}
from = default_image_folder + "/" + from;
if (!IsValidSrcFilename(from)) {
return ReturnStatus(fd, false, "Can't rename image file: '" + from + "': Invalid name or type");
}
string to = GetParam(command, "to");
if (to.empty()) {
return ReturnStatus(fd, false, "Can't rename image file '" + from + "': Missing destination filename");
}
if (to.find('/') != string::npos) {
return ReturnStatus(fd, false, "The destination filename '" + to + "' must not contain a path");
}
to = default_image_folder + "/" + to;
if (!IsValidDstFilename(to)) {
return ReturnStatus(fd, false, "Can't rename image file '" + from + "' to '" + to + "': File already exists");
}
if (rename(from.c_str(), to.c_str())) {
return ReturnStatus(fd, false, "Can't rename image file '" + from + "' to '" + to + "': " + string(strerror(errno)));
}
LOGINFO("Renamed image file '%s' to '%s'", from.c_str(), to.c_str());
return ReturnStatus(fd);
}
bool RascsiImage::CopyImage(int fd, const PbCommand& command)
{
string from = GetParam(command, "from");
if (from.empty()) {
return ReturnStatus(fd, false, "Can't copy image file: Missing source filename");
}
if (from.find('/') != string::npos) {
return ReturnStatus(fd, false, "The source filename '" + from + "' must not contain a path");
}
from = default_image_folder + "/" + from;
if (!IsValidSrcFilename(from)) {
return ReturnStatus(fd, false, "Can't copy image file: '" + from + "': Invalid name or type");
}
string to = GetParam(command, "to");
if (to.empty()) {
return ReturnStatus(fd, false, "Can't copy image file '" + from + "': Missing destination filename");
}
if (to.find('/') != string::npos) {
return ReturnStatus(fd, false, "The destination filename '" + to + "' must not contain a path");
}
to = default_image_folder + "/" + to;
if (!IsValidDstFilename(to)) {
return ReturnStatus(fd, false, "Can't copy image file '" + from + "' to '" + to + "': File already exists");
}
struct stat st;
if (lstat(from.c_str(), &st)) {
return ReturnStatus(fd, false, "Can't access source image file '" + from + "': " + string(strerror(errno)));
}
// Symbolic links need a special handling
if ((st.st_mode & S_IFMT) == S_IFLNK) {
if (symlink(filesystem::read_symlink(from).c_str(), to.c_str())) {
return ReturnStatus(fd, false, "Can't copy symlink '" + from + "': " + string(strerror(errno)));
}
LOGINFO("Copied symlink '%s' to '%s'", from.c_str(), to.c_str());
return ReturnStatus(fd);
}
int fd_src = open(from.c_str(), O_RDONLY, 0);
if (fd_src == -1) {
return ReturnStatus(fd, false, "Can't open source image file '" + from + "': " + string(strerror(errno)));
}
string permission = GetParam(command, "read_only");
// Since rascsi is running as root ensure that others can access the file
int permissions = !strcasecmp(permission.c_str(), "true") ?
S_IRUSR | S_IRGRP | S_IROTH : S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
int fd_dst = open(to.c_str(), O_WRONLY | O_CREAT, permissions);
if (fd_dst == -1) {
close(fd_src);
return ReturnStatus(fd, false, "Can't open destination image file '" + to + "': " + string(strerror(errno)));
}
if (sendfile(fd_dst, fd_src, 0, st.st_size) == -1) {
close(fd_dst);
close(fd_src);
return ReturnStatus(fd, false, "Can't copy image file '" + from + "' to '" + to + "': " + string(strerror(errno)));
}
close(fd_dst);
close(fd_src);
LOGINFO("Copied image file '%s' to '%s'", from.c_str(), to.c_str());
return ReturnStatus(fd);
}
bool RascsiImage::SetImagePermissions(int fd, const PbCommand& command)
{
string filename = GetParam(command, "file");
if (filename.empty()) {
return ReturnStatus(fd, false, "Missing image filename");
}
if (filename.find('/') != string::npos) {
return ReturnStatus(fd, false, "The image filename '" + filename + "' must not contain a path");
}
filename = default_image_folder + "/" + filename;
if (!IsValidSrcFilename(filename)) {
return ReturnStatus(fd, false, "Can't modify image file '" + filename + "': Invalid name or type");
}
bool protect = command.operation() == PROTECT_IMAGE;
int permissions = protect ? S_IRUSR | S_IRGRP | S_IROTH : S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
if (chmod(filename.c_str(), permissions) == -1) {
ostringstream error;
error << "Can't " << (protect ? "protect" : "unprotect") << " image file '" << filename << "': " << strerror(errno);
return ReturnStatus(fd, false, error.str());
}
if (protect) {
LOGINFO("Protected image file '%s'", filename.c_str());
}
else {
LOGINFO("Unprotected image file '%s'", filename.c_str());
}
return ReturnStatus(fd);
}

View File

@ -0,0 +1,38 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI (*^..^*)
// for Raspberry Pi
//
// Copyright (C) 2021 Uwe Seimet
//
//---------------------------------------------------------------------------
#pragma once
#include "rascsi_interface.pb.h"
#include <string>
using namespace std;
using namespace rascsi_interface;
class RascsiImage
{
public:
RascsiImage();
~RascsiImage() {};
string GetDefaultImageFolder() const { return default_image_folder; }
string SetDefaultImageFolder(const string&);
bool IsValidSrcFilename(const string&);
bool IsValidDstFilename(const string&);
bool CreateImage(int, const PbCommand&);
bool DeleteImage(int, const PbCommand&);
bool RenameImage(int, const PbCommand&);
bool CopyImage(int, const PbCommand&);
bool SetImagePermissions(int, const PbCommand&);
private:
string default_image_folder;
};

View File

@ -31,13 +31,14 @@ enum PbDeviceType {
enum PbOperation {
NONE = 0;
// Attach devices
// Attach devices and return the new device list (PbDevicesInfo)
// Parameters (mutually exclusive):
// "file": The filename relative to the default image folder. It must not contain a slash.
// "interfaces": A prioritized comma-separated list of interfaces to create a network bridge for.
ATTACH = 1;
// Detach a device. Detaches all LUNs of that device which are equal to or higher than the LUN specified.
// Detach a device and return the new device list (PbDevicesInfo)
// Detaches all LUNs of that device which are equal to or higher than the LUN specified.
DETACH = 2;
// Detach all devices, does not require a device list
@ -63,19 +64,20 @@ enum PbOperation {
// Make medium writable (not possible for read-only media)
UNPROTECT = 9;
// Gets the server information
// Gets the server information (PbServerInfo)
SERVER_INFO = 10;
// Get rascsi version information
// Get rascsi version information (PbVersionInfo)
VERSION_INFO = 11;
// Get information on attached devices. Returns data for all attached devices if the device list is empty.
// Get information on attached devices (PbDevicesInfo)
// Returns data for all attached devices if the device list is empty.
DEVICES_INFO = 12;
// Get device properties by device type
// Get device properties by device type (PbDeviceTypesInfo)
DEVICE_TYPES_INFO = 13;
// Get information on available image files in the default image folder.
// Get information on available image files in the default image folder (PbImageFilesInfo)
DEFAULT_IMAGE_FILES_INFO = 14;
// Get information on an image file (not necessarily in the default image folder) based on an absolute path.
@ -83,16 +85,17 @@ enum PbOperation {
// "file": The filename. Either an absolute path or a path relative to the default image folder.
IMAGE_FILE_INFO = 15;
// Get information on the available log levels and the current log level
// Get information on the available log levels and the current log level (PbLogLevelInfo)
LOG_LEVEL_INFO = 16;
// Get the names of the available network interfaces. Only lists interfaces that are up.
// Get the names of the available network interfaces (PbNetworkInterfacesInfo)
// Only lists interfaces that are up.
NETWORK_INTERFACES_INFO = 17;
// Get the mapping of extensions to device types
// Get the mapping of extensions to device types (PbMappingInfo)
MAPPING_INFO = 18;
// Get the list of reserved device IDs
// Get the list of reserved device IDs (PbReservedIdsInfo)
RESERVED_IDS_INFO = 19;
// Set the default folder for image files. This folder must be located in /home.
@ -109,25 +112,28 @@ enum PbOperation {
// Parameters:
// "ids": A comma-separated list of IDs to reserve, or an empty string in order not to reserve any ID.
RESERVE_IDS = 22;
// Shut down the rascsi process
SHUT_DOWN = 23;
// 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": Optional, "true" (case-insensitive) in order to create a read-only file
CREATE_IMAGE = 23;
CREATE_IMAGE = 24;
// Delete an image file.
// Parameters:
// "file": The filename, relative to the default image folder. It must not contain a slash.
DELETE_IMAGE = 24;
DELETE_IMAGE = 25;
// 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 = 25;
RENAME_IMAGE = 26;
// Copy an image file.
// Parameters:
@ -135,17 +141,17 @@ enum PbOperation {
// "to": The destination filename, relative to the default image folder. It must not contain a slash.
// "read_only": Optional, "true" (case-insensitive) in order to create a read-only file
// The destination filename must not yet exist.
COPY_IMAGE = 26;
COPY_IMAGE = 27;
// Write-protect an image file.
// Parameters:
// "file": The filename, relative to the default image folder. It must not contain a slash.
PROTECT_IMAGE = 27;
PROTECT_IMAGE = 28;
// Make an image file writable.
// Parameters:
// "file": The filename, relative to the default image folder. It must not contain a slash.
UNPROTECT_IMAGE = 28;
UNPROTECT_IMAGE = 29;
}
// The supported file extensions mapped to their respective device types
@ -305,7 +311,7 @@ message PbResult {
PbVersionInfo version_info = 4;
// The result of a LOG_LEVEL_INFO command
PbLogLevelInfo log_level_info = 5;
// The result of a DEVICES_INFO command
// The result of a DEVICES_INFO, ATTACH or DETACH command
PbDevicesInfo devices_info = 6;
// The result of a DEVICE_TYPES_INFO command
PbDeviceTypesInfo device_types_info = 7;

View File

@ -14,14 +14,17 @@
#include "protobuf_util.h"
#include "rascsi_version.h"
#include "rascsi_interface.pb.h"
#include "protobuf_response_helper.h"
#include "rascsi_image.h"
#include "rascsi_response.h"
#include <sstream>
using namespace rascsi_interface;
using namespace protobuf_util;
ProtobufResponseHandler::ProtobufResponseHandler()
RascsiResponse::RascsiResponse(DeviceFactory *device_factory, const RascsiImage *rascsi_image)
{
device_factory = DeviceFactory::instance();
this->device_factory = device_factory;
this->rascsi_image = rascsi_image;
log_levels.push_back("trace");
log_levels.push_back("debug");
@ -32,13 +35,7 @@ ProtobufResponseHandler::ProtobufResponseHandler()
log_levels.push_back("off");
}
ProtobufResponseHandler& ProtobufResponseHandler::instance()
{
static ProtobufResponseHandler instance;
return instance;
}
PbDeviceProperties *ProtobufResponseHandler::GetDeviceProperties(const Device *device)
PbDeviceProperties *RascsiResponse::GetDeviceProperties(const Device *device)
{
PbDeviceProperties *properties = new PbDeviceProperties();
@ -55,29 +52,29 @@ PbDeviceProperties *ProtobufResponseHandler::GetDeviceProperties(const Device *d
PbDeviceType_Parse(device->GetType(), &t);
if (device->SupportsParams()) {
for (const auto& param : device_factory.GetDefaultParams(t)) {
for (const auto& param : device_factory->GetDefaultParams(t)) {
auto& map = *properties->mutable_default_params();
map[param.first] = param.second;
}
}
for (const auto& block_size : device_factory.GetSectorSizes(t)) {
for (const auto& block_size : device_factory->GetSectorSizes(t)) {
properties->add_block_sizes(block_size);
}
return properties;
}
void ProtobufResponseHandler::GetDeviceTypeProperties(PbDeviceTypesInfo& device_types_info, PbDeviceType type)
void RascsiResponse::GetDeviceTypeProperties(PbDeviceTypesInfo& device_types_info, PbDeviceType type)
{
PbDeviceTypeProperties *type_properties = device_types_info.add_properties();
type_properties->set_type(type);
Device *device = device_factory.CreateDevice(type, "");
Device *device = device_factory->CreateDevice(type, "");
type_properties->set_allocated_properties(GetDeviceProperties(device));
delete device;
}
void ProtobufResponseHandler::GetAllDeviceTypeProperties(PbDeviceTypesInfo& device_types_info)
void RascsiResponse::GetAllDeviceTypeProperties(PbDeviceTypesInfo& device_types_info)
{
GetDeviceTypeProperties(device_types_info, SAHD);
GetDeviceTypeProperties(device_types_info, SCHD);
@ -88,7 +85,7 @@ void ProtobufResponseHandler::GetAllDeviceTypeProperties(PbDeviceTypesInfo& devi
GetDeviceTypeProperties(device_types_info, SCDP);
}
void ProtobufResponseHandler::GetDevice(const Device *device, PbDevice *pb_device, const string& image_folder)
void RascsiResponse::GetDevice(const Device *device, PbDevice *pb_device)
{
pb_device->set_id(device->GetId());
pb_device->set_unit(device->GetLun());
@ -126,18 +123,18 @@ void ProtobufResponseHandler::GetDevice(const Device *device, PbDevice *pb_devic
Filepath filepath;
file_support->GetPath(filepath);
PbImageFile *image_file = new PbImageFile();
GetImageFile(image_file, device->IsRemovable() && !device->IsReady() ? "" : filepath.GetPath(), image_folder);
GetImageFile(image_file, device->IsRemovable() && !device->IsReady() ? "" : filepath.GetPath());
pb_device->set_allocated_file(image_file);
}
}
bool ProtobufResponseHandler::GetImageFile(PbImageFile *image_file, const string& filename, const string& image_folder)
bool RascsiResponse::GetImageFile(PbImageFile *image_file, const string& filename)
{
if (!filename.empty()) {
image_file->set_name(filename);
image_file->set_type(device_factory.GetTypeForFile(filename));
image_file->set_type(device_factory->GetTypeForFile(filename));
string f = filename[0] == '/' ? filename : image_folder + "/" + filename;
string f = filename[0] == '/' ? filename : rascsi_image->GetDefaultImageFolder() + "/" + filename;
image_file->set_read_only(access(f.c_str(), W_OK));
@ -151,34 +148,35 @@ bool ProtobufResponseHandler::GetImageFile(PbImageFile *image_file, const string
return false;
}
PbImageFilesInfo *ProtobufResponseHandler::GetAvailableImages(PbResult& result, const string& image_folder)
PbImageFilesInfo *RascsiResponse::GetAvailableImages(PbResult& result)
{
PbImageFilesInfo *image_files_info = new PbImageFilesInfo();
image_files_info->set_default_image_folder(image_folder);
string default_image_folder = rascsi_image->GetDefaultImageFolder();
image_files_info->set_default_image_folder(default_image_folder);
// filesystem::directory_iterator cannot be used because libstdc++ 8.3.0 does not support big files
DIR *d = opendir(image_folder.c_str());
DIR *d = opendir(default_image_folder.c_str());
if (d) {
struct dirent *dir;
while ((dir = readdir(d))) {
if (dir->d_type == DT_REG || dir->d_type == DT_LNK || dir->d_type == DT_BLK) {
string filename = image_folder + "/" + dir->d_name;
string filename = default_image_folder + "/" + dir->d_name;
struct stat st;
if (dir->d_type == DT_REG && !stat(filename.c_str(), &st)) {
if (!st.st_size) {
LOGTRACE("File '%s' in image folder '%s' has a size of 0 bytes", dir->d_name, image_folder.c_str());
LOGTRACE("File '%s' in image folder '%s' has a size of 0 bytes", dir->d_name, default_image_folder.c_str());
continue;
}
} else if (dir->d_type == DT_LNK && stat(filename.c_str(), &st)) {
LOGTRACE("Symlink '%s' in image folder '%s' is broken", dir->d_name, image_folder.c_str());
LOGTRACE("Symlink '%s' in image folder '%s' is broken", dir->d_name, default_image_folder.c_str());
continue;
}
PbImageFile *image_file = new PbImageFile();
if (GetImageFile(image_file, dir->d_name, image_folder)) {
GetImageFile(image_files_info->add_image_files(), dir->d_name, image_folder);
if (GetImageFile(image_file, dir->d_name)) {
GetImageFile(image_files_info->add_image_files(), dir->d_name);
}
delete image_file;
}
@ -192,16 +190,16 @@ PbImageFilesInfo *ProtobufResponseHandler::GetAvailableImages(PbResult& result,
return image_files_info;
}
void ProtobufResponseHandler::GetAvailableImages(PbResult& result, PbServerInfo& server_info, const string& image_folder)
void RascsiResponse::GetAvailableImages(PbResult& result, PbServerInfo& server_info)
{
PbImageFilesInfo *image_files_info = GetAvailableImages(result, image_folder);
image_files_info->set_default_image_folder(image_folder);
PbImageFilesInfo *image_files_info = GetAvailableImages(result);
image_files_info->set_default_image_folder(rascsi_image->GetDefaultImageFolder());
server_info.set_allocated_image_files_info(image_files_info);
result.set_status(true);
}
PbReservedIdsInfo *ProtobufResponseHandler::GetReservedIds(PbResult& result, const set<int>& ids)
PbReservedIdsInfo *RascsiResponse::GetReservedIds(PbResult& result, const set<int>& ids)
{
PbReservedIdsInfo *reserved_ids_info = new PbReservedIdsInfo();
for (int id : ids) {
@ -213,19 +211,19 @@ PbReservedIdsInfo *ProtobufResponseHandler::GetReservedIds(PbResult& result, con
return reserved_ids_info;
}
void ProtobufResponseHandler::GetDevices(PbServerInfo& server_info, const vector<Device *>& devices, const string& image_folder)
void RascsiResponse::GetDevices(PbServerInfo& server_info, const vector<Device *>& devices)
{
for (const Device *device : devices) {
// Skip if unit does not exist or is not assigned
if (device) {
PbDevice *pb_device = server_info.mutable_devices_info()->add_devices();
GetDevice(device, pb_device, image_folder);
GetDevice(device, pb_device);
}
}
}
void ProtobufResponseHandler::GetDevicesInfo(PbResult& result, const PbCommand& command, const vector<Device *>& devices,
const string& image_folder, int unit_count)
void RascsiResponse::GetDevicesInfo(PbResult& result, const PbCommand& command, const vector<Device *>& devices,
int unit_count)
{
set<id_set> id_sets;
if (!command.devices_size()) {
@ -255,13 +253,13 @@ void ProtobufResponseHandler::GetDevicesInfo(PbResult& result, const PbCommand&
for (const auto& id_set : id_sets) {
const Device *device = devices[id_set.first * unit_count + id_set.second];
GetDevice(device, devices_info->add_devices(), image_folder);
GetDevice(device, devices_info->add_devices());
}
result.set_status(true);
}
PbDeviceTypesInfo *ProtobufResponseHandler::GetDeviceTypesInfo(PbResult& result, const PbCommand& command)
PbDeviceTypesInfo *RascsiResponse::GetDeviceTypesInfo(PbResult& result, const PbCommand& command)
{
PbDeviceTypesInfo *device_types_info = new PbDeviceTypesInfo();
@ -272,18 +270,18 @@ PbDeviceTypesInfo *ProtobufResponseHandler::GetDeviceTypesInfo(PbResult& result,
return device_types_info;
}
PbServerInfo *ProtobufResponseHandler::GetServerInfo(PbResult& result, const vector<Device *>& devices, const set<int>& reserved_ids,
const string& image_folder, const string& current_log_level)
PbServerInfo *RascsiResponse::GetServerInfo(PbResult& result, const vector<Device *>& devices, const set<int>& reserved_ids,
const string& current_log_level)
{
PbServerInfo *server_info = new PbServerInfo();
server_info->set_allocated_version_info(GetVersionInfo(result));
server_info->set_allocated_log_level_info(GetLogLevelInfo(result, current_log_level));
GetAllDeviceTypeProperties(*server_info->mutable_device_types_info());
GetAvailableImages(result, *server_info, image_folder);
GetAvailableImages(result, *server_info);
server_info->set_allocated_network_interfaces_info(GetNetworkInterfacesInfo(result));
server_info->set_allocated_mapping_info(GetMappingInfo(result));
GetDevices(*server_info, devices, image_folder);
GetDevices(*server_info, devices);
server_info->set_allocated_reserved_ids_info(GetReservedIds(result, reserved_ids));
result.set_status(true);
@ -291,7 +289,7 @@ PbServerInfo *ProtobufResponseHandler::GetServerInfo(PbResult& result, const vec
return server_info;
}
PbVersionInfo *ProtobufResponseHandler::GetVersionInfo(PbResult& result)
PbVersionInfo *RascsiResponse::GetVersionInfo(PbResult& result)
{
PbVersionInfo *version_info = new PbVersionInfo();
@ -304,7 +302,7 @@ PbVersionInfo *ProtobufResponseHandler::GetVersionInfo(PbResult& result)
return version_info;
}
PbLogLevelInfo *ProtobufResponseHandler::GetLogLevelInfo(PbResult& result, const string& current_log_level)
PbLogLevelInfo *RascsiResponse::GetLogLevelInfo(PbResult& result, const string& current_log_level)
{
PbLogLevelInfo *log_level_info = new PbLogLevelInfo();
@ -319,11 +317,11 @@ PbLogLevelInfo *ProtobufResponseHandler::GetLogLevelInfo(PbResult& result, const
return log_level_info;
}
PbNetworkInterfacesInfo *ProtobufResponseHandler::GetNetworkInterfacesInfo(PbResult& result)
PbNetworkInterfacesInfo *RascsiResponse::GetNetworkInterfacesInfo(PbResult& result)
{
PbNetworkInterfacesInfo *network_interfaces_info = new PbNetworkInterfacesInfo();
for (const auto& network_interface : device_factory.GetNetworkInterfaces()) {
for (const auto& network_interface : device_factory->GetNetworkInterfaces()) {
network_interfaces_info->add_name(network_interface);
}
@ -332,11 +330,11 @@ PbNetworkInterfacesInfo *ProtobufResponseHandler::GetNetworkInterfacesInfo(PbRes
return network_interfaces_info;
}
PbMappingInfo *ProtobufResponseHandler::GetMappingInfo(PbResult& result)
PbMappingInfo *RascsiResponse::GetMappingInfo(PbResult& result)
{
PbMappingInfo *mapping_info = new PbMappingInfo();
for (const auto& mapping : device_factory.GetExtensionMapping()) {
for (const auto& mapping : device_factory->GetExtensionMapping()) {
(*mapping_info->mutable_mapping())[mapping.first] = mapping.second;
}

View File

@ -5,8 +5,6 @@
//
// Copyright (C) 2021 Uwe Seimet
//
// A singleton that creates responses for protobuf interface requests
//
//---------------------------------------------------------------------------
#pragma once
@ -19,38 +17,39 @@
using namespace std;
using namespace rascsi_interface;
class DeviceFactory;
class RascsiImage;
class Device;
class ProtobufResponseHandler
class RascsiResponse
{
public:
ProtobufResponseHandler();
~ProtobufResponseHandler() {};
RascsiResponse(DeviceFactory *, const RascsiImage *);
~RascsiResponse() {};
static ProtobufResponseHandler& instance();
bool GetImageFile(PbImageFile *, const string&, const string&);
PbImageFilesInfo *GetAvailableImages(PbResult&, const string&);
bool GetImageFile(PbImageFile *, const string&);
PbImageFilesInfo *GetAvailableImages(PbResult&);
PbReservedIdsInfo *GetReservedIds(PbResult&, const set<int>&);
void GetDevices(PbServerInfo&, const vector<Device *>&, const string&);
void GetDevicesInfo(PbResult&, const PbCommand&, const vector<Device *>&, const string&, int);
void GetDevices(PbServerInfo&, const vector<Device *>&);
void GetDevicesInfo(PbResult&, const PbCommand&, const vector<Device *>&, int);
PbDeviceTypesInfo *GetDeviceTypesInfo(PbResult&, const PbCommand&);
PbVersionInfo *GetVersionInfo(PbResult&);
PbServerInfo *GetServerInfo(PbResult&, const vector<Device *>&, const set<int>&, const string&, const string&);
PbServerInfo *GetServerInfo(PbResult&, const vector<Device *>&, const set<int>&, const string&);
PbNetworkInterfacesInfo *GetNetworkInterfacesInfo(PbResult&);
PbMappingInfo *GetMappingInfo(PbResult&);
PbLogLevelInfo *GetLogLevelInfo(PbResult&, const string&);
private:
DeviceFactory device_factory;
DeviceFactory *device_factory;
const RascsiImage *rascsi_image;
vector<string> log_levels;
PbDeviceProperties *GetDeviceProperties(const Device *);
void GetDevice(const Device *, PbDevice *, const string&);
void GetDevice(const Device *, PbDevice *);
void GetAllDeviceTypeProperties(PbDeviceTypesInfo&);
void GetDeviceTypeProperties(PbDeviceTypesInfo&, PbDeviceType);
void GetAvailableImages(PbResult& result, PbServerInfo&, const string&);
void GetAvailableImages(PbResult& result, PbServerInfo&);
};

View File

@ -23,6 +23,8 @@
using namespace std;
using namespace rascsi_interface;
using namespace ras_util;
using namespace protobuf_util;
PbOperation ParseOperation(const char *optarg)
{
@ -99,7 +101,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] [-d FILENAME] [-w FILENAME] [-R CURRENT_NAME:NEW_NAME] [-x CURRENT_NAME:NEW_NAME] ";
cerr << "[-e] [-E FILENAME] [-D] [-I] [-l] [-L] [-m] [-O] [-s] [-v] [-V] [-y]" << endl;
cerr << "[-e] [-E FILENAME] [-D] [-I] [-l] [-L] [-m] [-O] [-s] [-v] [-V] [-y] [-X]" << endl;
cerr << " where ID := {0-7}" << endl;
cerr << " UNIT := {0-31}, default is 0" << endl;
cerr << " CMD := {attach|detach|insert|eject|protect|unprotect|show}" << endl;
@ -135,7 +137,7 @@ int main(int argc, char* argv[])
opterr = 1;
int opt;
while ((opt = getopt(argc, argv, "elmsvDINOTVa:b:c:d:f:h:i:n:p:r:t:u:x:C:E:F:L:R:")) != -1) {
while ((opt = getopt(argc, argv, "elmsvDINOTVXa:b:c:d:f:h:i:n:p:r:t:u:x:C:E:F:L:R:")) != -1) {
switch (opt) {
case 'i': {
int id;
@ -310,6 +312,10 @@ int main(int argc, char* argv[])
case 'T':
command.set_operation(DEVICE_TYPES_INFO);
break;
case 'X':
command.set_operation(SHUT_DOWN);
break;
}
}

View File

@ -23,6 +23,7 @@
using namespace std;
using namespace rascsi_interface;
using namespace protobuf_util;
RasctlCommands::RasctlCommands(PbCommand& command, const string& hostname, int port)
{

View File

@ -15,6 +15,7 @@
using namespace std;
using namespace rascsi_interface;
using namespace ras_util;
void RasctlDisplay::DisplayDevices(const PbDevicesInfo& devices_info)
{

View File

@ -15,7 +15,7 @@
using namespace std;
using namespace rascsi_interface;
bool GetAsInt(const string& value, int& result)
bool ras_util::GetAsInt(const string& value, int& result)
{
if (value.find_first_not_of("0123456789") != string::npos) {
return false;
@ -34,7 +34,7 @@ bool GetAsInt(const string& value, int& result)
return true;
}
string ListDevices(const list<PbDevice>& pb_devices)
string ras_util::ListDevices(const list<PbDevice>& pb_devices)
{
if (pb_devices.empty()) {
return "No images currently attached.";

View File

@ -15,5 +15,8 @@
#include <string>
#include "rascsi_interface.pb.h"
bool GetAsInt(const std::string&, int&);
std::string ListDevices(const std::list<rascsi_interface::PbDevice>&);
namespace ras_util
{
bool GetAsInt(const std::string&, int&);
std::string ListDevices(const std::list<rascsi_interface::PbDevice>&);
}