mirror of
https://github.com/akuker/RASCSI.git
synced 2024-12-30 05:29:47 +00:00
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:
parent
641b176f93
commit
62a104f75d
@ -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
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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')
|
||||
|
@ -26,11 +26,12 @@ class Device;
|
||||
|
||||
class DeviceFactory
|
||||
{
|
||||
public:
|
||||
|
||||
private:
|
||||
DeviceFactory();
|
||||
~DeviceFactory() {};
|
||||
|
||||
public:
|
||||
|
||||
static DeviceFactory& instance();
|
||||
|
||||
Device *CreateDevice(PbDeviceType, const string&);
|
||||
|
@ -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;
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -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&);
|
||||
}
|
||||
|
@ -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()) {
|
||||
|
343
src/raspberrypi/rascsi_image.cpp
Normal file
343
src/raspberrypi/rascsi_image.cpp
Normal 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);
|
||||
}
|
38
src/raspberrypi/rascsi_image.h
Normal file
38
src/raspberrypi/rascsi_image.h
Normal 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;
|
||||
};
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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&);
|
||||
};
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -23,6 +23,7 @@
|
||||
|
||||
using namespace std;
|
||||
using namespace rascsi_interface;
|
||||
using namespace protobuf_util;
|
||||
|
||||
RasctlCommands::RasctlCommands(PbCommand& command, const string& hostname, int port)
|
||||
{
|
||||
|
@ -15,6 +15,7 @@
|
||||
|
||||
using namespace std;
|
||||
using namespace rascsi_interface;
|
||||
using namespace ras_util;
|
||||
|
||||
void RasctlDisplay::DisplayDevices(const PbDevicesInfo& devices_info)
|
||||
{
|
||||
|
@ -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.";
|
||||
|
@ -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>&);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user