mirror of
https://github.com/akuker/RASCSI.git
synced 2025-01-11 09:29:53 +00:00
Merge pull request #240 from akuker/feature_image_creation
rascsi support for creating/deleting/renaming image files in the default image folder
This commit is contained in:
commit
5161a83999
24
doc/rasctl.1
24
doc/rasctl.1
@ -5,6 +5,7 @@ rasctl \- Sends management commands to the rascsi process
|
|||||||
.B rasctl
|
.B rasctl
|
||||||
\fB\-l\fR |
|
\fB\-l\fR |
|
||||||
\fB\-s\fR |
|
\fB\-s\fR |
|
||||||
|
[\fB\-d\fR \fIIMAGE_FOLDER\fR]
|
||||||
[\fB\-g\fR \fILOG_LEVEL\fR]
|
[\fB\-g\fR \fILOG_LEVEL\fR]
|
||||||
[\fB\-h\fR \fIHOST\fR]
|
[\fB\-h\fR \fIHOST\fR]
|
||||||
[\fB\-p\fR \fIPORT\fR]
|
[\fB\-p\fR \fIPORT\fR]
|
||||||
@ -12,7 +13,7 @@ rasctl \- Sends management commands to the rascsi process
|
|||||||
[\fB\-v\fR]
|
[\fB\-v\fR]
|
||||||
\fB\-i\fR \fIID\fR
|
\fB\-i\fR \fIID\fR
|
||||||
[\fB\-c\fR \fICMD\fR]
|
[\fB\-c\fR \fICMD\fR]
|
||||||
[\fB\-f\fR \fIFILE\fR]
|
[\fB\-f\fR \fIFILE|PARAM\fR]
|
||||||
[\fB\-n\fR \fINAME\fR]
|
[\fB\-n\fR \fINAME\fR]
|
||||||
[\fB\-t\fR \fITYPE\fR]
|
[\fB\-t\fR \fITYPE\fR]
|
||||||
[\fB\-u\fR \fIUNIT\fR]
|
[\fB\-u\fR \fIUNIT\fR]
|
||||||
@ -28,8 +29,14 @@ Note: The command and type arguments are case insensitive. Only the first letter
|
|||||||
|
|
||||||
.SH OPTIONS
|
.SH OPTIONS
|
||||||
.TP
|
.TP
|
||||||
|
.BR \-a\fI " "\fIFILENAME:FILESIZE
|
||||||
|
Create an image file in the default image folder with the specified name and size in bytes.
|
||||||
|
.TP
|
||||||
|
.BR \-g\fI " "\fIIMAGE_FOLDER
|
||||||
|
Set the default image folder.
|
||||||
|
.TP
|
||||||
.BR \-g\fI " "\fILOG_LEVEL
|
.BR \-g\fI " "\fILOG_LEVEL
|
||||||
The rascsi log level to set (trace, debug, info, warn, err, critical, off)
|
Set the rascsi log level (trace, debug, info, warn, err, critical, off).
|
||||||
.TP
|
.TP
|
||||||
.BR \-h\fI " " \fIHOST
|
.BR \-h\fI " " \fIHOST
|
||||||
The rascsi host to connect to, default is 'localhost'.
|
The rascsi host to connect to, default is 'localhost'.
|
||||||
@ -37,6 +44,9 @@ The rascsi host to connect to, default is 'localhost'.
|
|||||||
.BR \-l\fI
|
.BR \-l\fI
|
||||||
List all of the devices that are currently being emulated by RaSCSI, as well as their current status.
|
List all of the devices that are currently being emulated by RaSCSI, as well as their current status.
|
||||||
.TP
|
.TP
|
||||||
|
.BR \-m\fI " "\fICURRENT_NAME:NEW_NAME
|
||||||
|
Rename an image file in the default image folder.
|
||||||
|
.TP
|
||||||
.BR \-p\fI " " \fIPORT
|
.BR \-p\fI " " \fIPORT
|
||||||
The rascsi port to connect to, default is 6868.
|
The rascsi port to connect to, default is 6868.
|
||||||
.TP
|
.TP
|
||||||
@ -49,6 +59,12 @@ Display server-side settings like available images or supported device types.
|
|||||||
.BR \-v\fI " " \fI
|
.BR \-v\fI " " \fI
|
||||||
Display the rascsi version.
|
Display the rascsi version.
|
||||||
.TP
|
.TP
|
||||||
|
.BR \-w\fI " "\fIFILENAME
|
||||||
|
Delete an image file in the default image folder.
|
||||||
|
.TP
|
||||||
|
.BR \-x\fI " "\fICURRENT_NAME:NEW_NAME
|
||||||
|
Copy an image file in the default image folder.
|
||||||
|
.TP
|
||||||
.BR \-i\fI " " \fIID
|
.BR \-i\fI " " \fIID
|
||||||
ID is the SCSI ID that you want to control. (0-7)
|
ID is the SCSI ID that you want to control. (0-7)
|
||||||
.TP
|
.TP
|
||||||
@ -67,8 +83,8 @@ eject, protect and unprotect are idempotent.
|
|||||||
.BR \-b\fI " " \fIBLOCK_SIZE
|
.BR \-b\fI " " \fIBLOCK_SIZE
|
||||||
The optional block size. For SCSI drives 512, 1024, 2048 or 4096 bytes, default size is 512 bytes. For SASI drives 256 or 1024 bytes, default is 256 bytes.
|
The optional block size. For SCSI drives 512, 1024, 2048 or 4096 bytes, default size is 512 bytes. For SASI drives 256 or 1024 bytes, default is 256 bytes.
|
||||||
.TP
|
.TP
|
||||||
.BR \-f\fI " " \fIFILE
|
.BR \-f\fI " " \fIFILE|PARAM
|
||||||
Path to the disk image file. See the rascsi(1) man page for allowable file types.
|
Device-specific: Either a path to a disk image file, or a parameter for a non-disk device. See the rascsi(1) man page for permitted file types.
|
||||||
.TP
|
.TP
|
||||||
.BR \-t\fI " " \fITYPE
|
.BR \-t\fI " " \fITYPE
|
||||||
Specifies the device type. This type overrides the type derived from the file extension of the specified image. See the rascsi(1) man page for the available device types. For some types there are shortcuts (only the first letter is required):
|
Specifies the device type. This type overrides the type derived from the file extension of the specified image. See the rascsi(1) man page for the available device types. For some types there are shortcuts (only the first letter is required):
|
||||||
|
@ -1,32 +1,46 @@
|
|||||||
!! ------ THIS FILE IS AUTO_GENERATED! DO NOT MANUALLY UPDATE!!!
|
!! ------ THIS FILE IS AUTO_GENERATED! DO NOT MANUALLY UPDATE!!!
|
||||||
!! ------ The native file is rasctl.1. Re-run 'make docs' after updating
|
!! ------ The native file is rasctl.1. Re-run 'make docs' after updating\n\n
|
||||||
|
rascsi(1) General Commands Manual rascsi(1)
|
||||||
|
|
||||||
rascsi(1) General Commands Manual rascsi(1)
|
|
||||||
|
|
||||||
NAME
|
NAME
|
||||||
rasctl - Sends management commands to the rascsi process
|
rasctl - Sends management commands to the rascsi process
|
||||||
|
|
||||||
SYNOPSIS
|
SYNOPSIS
|
||||||
rasctl -l | -s | [-g LOG_LEVEL] [-h HOST] [-p PORT] [-r RESERVED_IDS] [-v] -i ID [-c CMD] [-f FILE] [-n NAME] [-t TYPE] [-u UNIT]
|
rasctl -l | -s | [-d IMAGE_FOLDER] [-g LOG_LEVEL] [-h HOST] [-p PORT]
|
||||||
|
[-r RESERVED_IDS] [-v] -i ID [-c CMD] [-f FILE|PARAM] [-n NAME] [-t
|
||||||
|
TYPE] [-u UNIT]
|
||||||
|
|
||||||
DESCRIPTION
|
DESCRIPTION
|
||||||
rasctl Sends commands to the rascsi process to make configuration adjustments at runtime or to check the status of the devices.
|
rasctl Sends commands to the rascsi process to make configuration ad‐
|
||||||
|
justments at runtime or to check the status of the devices.
|
||||||
|
|
||||||
Either the -i or -l option should be specified at one time. Not both.
|
Either the -i or -l option should be specified at one time. Not both.
|
||||||
|
|
||||||
You do NOT need root privileges to use rasctl.
|
You do NOT need root privileges to use rasctl.
|
||||||
|
|
||||||
Note: The command and type arguments are case insensitive. Only the first letter of the command/type is evaluated by the tool.
|
Note: The command and type arguments are case insensitive. Only the
|
||||||
|
first letter of the command/type is evaluated by the tool.
|
||||||
|
|
||||||
OPTIONS
|
OPTIONS
|
||||||
|
-a FILENAME:FILESIZE
|
||||||
|
Create an image file in the default image folder with the speci‐
|
||||||
|
fied name and size in bytes.
|
||||||
|
|
||||||
|
-g IMAGE_FOLDER
|
||||||
|
Set the default image folder.
|
||||||
|
|
||||||
-g LOG_LEVEL
|
-g LOG_LEVEL
|
||||||
The rascsi log level to set (trace, debug, info, warn, err, critical, off)
|
Set the rascsi log level (trace, debug, info, warn, err, criti‐
|
||||||
|
cal, off).
|
||||||
|
|
||||||
-h HOST
|
-h HOST
|
||||||
The rascsi host to connect to, default is 'localhost'.
|
The rascsi host to connect to, default is 'localhost'.
|
||||||
|
|
||||||
-l List all of the devices that are currently being emulated by RaSCSI, as well as their current status.
|
-l List all of the devices that are currently being emulated by
|
||||||
|
RaSCSI, as well as their current status.
|
||||||
|
|
||||||
|
-m CURRENT_NAME:NEW_NAME
|
||||||
|
Rename an image file in the default image folder.
|
||||||
|
|
||||||
-p PORT
|
-p PORT
|
||||||
The rascsi port to connect to, default is 6868.
|
The rascsi port to connect to, default is 6868.
|
||||||
@ -34,10 +48,17 @@ OPTIONS
|
|||||||
-r RESERVED_IDS
|
-r RESERVED_IDS
|
||||||
Comma-separated list of IDs to reserve.
|
Comma-separated list of IDs to reserve.
|
||||||
|
|
||||||
-s Display server-side settings like available images or supported device types.
|
-s Display server-side settings like available images or supported
|
||||||
|
device types.
|
||||||
|
|
||||||
-v Display the rascsi version.
|
-v Display the rascsi version.
|
||||||
|
|
||||||
|
-w FILENAME
|
||||||
|
Delete an image file in the default image folder.
|
||||||
|
|
||||||
|
-x CURRENT_NAME:NEW_NAME
|
||||||
|
Copy an image file in the default image folder.
|
||||||
|
|
||||||
-i ID ID is the SCSI ID that you want to control. (0-7)
|
-i ID ID is the SCSI ID that you want to control. (0-7)
|
||||||
|
|
||||||
-c CMD Command is the operation being requested. Options are:
|
-c CMD Command is the operation being requested. Options are:
|
||||||
@ -45,21 +66,29 @@ OPTIONS
|
|||||||
d(etach): Detach disk
|
d(etach): Detach disk
|
||||||
i(nsert): Insert media (removable media devices only)
|
i(nsert): Insert media (removable media devices only)
|
||||||
e(ject): Eject media (removable media devices only)
|
e(ject): Eject media (removable media devices only)
|
||||||
p(rotect): Write protect the medium (not for CD-ROMs, which are always read-only)
|
p(rotect): Write protect the medium (not for CD-ROMs, which
|
||||||
u(nprotect): Remove write protection from the medium (not for CD-ROMs, which are always read-only)
|
are always read-only)
|
||||||
|
u(nprotect): Remove write protection from the medium (not for
|
||||||
|
CD-ROMs, which are always read-only)
|
||||||
s(how): Display device information
|
s(how): Display device information
|
||||||
|
|
||||||
eject, protect and unprotect are idempotent.
|
eject, protect and unprotect are idempotent.
|
||||||
|
|
||||||
-b BLOCK_SIZE
|
-b BLOCK_SIZE
|
||||||
The optional block size. For SCSI drives 512, 1024, 2048 or 4096 bytes, default size is 512 bytes. For SASI drives 256 or 1024 bytes, default is 256 bytes.
|
The optional block size. For SCSI drives 512, 1024, 2048 or 4096
|
||||||
|
bytes, default size is 512 bytes. For SASI drives 256 or 1024
|
||||||
|
bytes, default is 256 bytes.
|
||||||
|
|
||||||
-f FILE
|
-f FILE|PARAM
|
||||||
Path to the disk image file. See the rascsi(1) man page for allowable file types.
|
Device-specific: Either a path to a disk image file, or a param‐
|
||||||
|
eter for a non-disk device. See the rascsi(1) man page for per‐
|
||||||
|
mitted file types.
|
||||||
|
|
||||||
-t TYPE
|
-t TYPE
|
||||||
Specifies the device type. This type overrides the type derived from the file extension of the specified image. See the rascsi(1) man page for the available device types.
|
Specifies the device type. This type overrides the type derived
|
||||||
For some types there are shortcuts (only the first letter is required):
|
from the file extension of the specified image. See the
|
||||||
|
rascsi(1) man page for the available device types. For some
|
||||||
|
types there are shortcuts (only the first letter is required):
|
||||||
hd: SCSI hard disk drive
|
hd: SCSI hard disk drive
|
||||||
rm: SCSI removable media drive
|
rm: SCSI removable media drive
|
||||||
cd: CD-ROM
|
cd: CD-ROM
|
||||||
@ -68,11 +97,17 @@ OPTIONS
|
|||||||
daynaport: DaynaPORT network adapter
|
daynaport: DaynaPORT network adapter
|
||||||
|
|
||||||
-n VENDOR:PRODUCT:REVISION
|
-n VENDOR:PRODUCT:REVISION
|
||||||
The vendor, product and revision for the device, to be returned with the INQUIRY data. A complete set of name components must be provided. VENDOR may have up to 8, PRODUCT
|
The vendor, product and revision for the device, to be returned
|
||||||
up to 16, REVISION up to 4 characters. Padding with blanks to the maxium length is automatically applied. Once set the name of a device cannot be changed.
|
with the INQUIRY data. A complete set of name components must be
|
||||||
|
provided. VENDOR may have up to 8, PRODUCT up to 16, REVISION up
|
||||||
|
to 4 characters. Padding with blanks to the maxium length is au‐
|
||||||
|
tomatically applied. Once set the name of a device cannot be
|
||||||
|
changed.
|
||||||
|
|
||||||
-u UNIT
|
-u UNIT
|
||||||
Unit number (0 or 1). This will default to 0. This option is only used when there are multiple SCSI devices on a shared SCSI controller. (This is not common)
|
Unit number (0 or 1). This will default to 0. This option is
|
||||||
|
only used when there are multiple SCSI devices on a shared SCSI
|
||||||
|
controller. (This is not common)
|
||||||
|
|
||||||
EXAMPLES
|
EXAMPLES
|
||||||
Show a listing of all of the SCSI devices and their current status.
|
Show a listing of all of the SCSI devices and their current status.
|
||||||
@ -85,12 +120,14 @@ EXAMPLES
|
|||||||
| 0 | 1 | SCHD | /home/pi/harddisk.hda
|
| 0 | 1 | SCHD | /home/pi/harddisk.hda
|
||||||
+----+----+------+-------------------------------------
|
+----+----+------+-------------------------------------
|
||||||
|
|
||||||
Request the RaSCSI process to attach a disk (assumed) to SCSI ID 0 with the contents of the file system image "HDIIMAGE0.HDS".
|
Request the RaSCSI process to attach a disk (assumed) to SCSI ID 0 with
|
||||||
|
the contents of the file system image "HDIIMAGE0.HDS".
|
||||||
rasctl -i 0 -f HDIIMAGE0.HDS
|
rasctl -i 0 -f HDIIMAGE0.HDS
|
||||||
|
|
||||||
SEE ALSO
|
SEE ALSO
|
||||||
rascsi(1) scsimon(1)
|
rascsi(1) scsimon(1)
|
||||||
|
|
||||||
Full documentation is available at: <https://www.github.com/akuker/RASCSI/wiki/>
|
Full documentation is available at:
|
||||||
|
<https://www.github.com/akuker/RASCSI/wiki/>
|
||||||
|
|
||||||
rascsi(1)
|
rascsi(1)
|
||||||
|
@ -108,6 +108,11 @@ const string Device::GetPaddedName() const
|
|||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const string Device::GetParam(const string& key)
|
||||||
|
{
|
||||||
|
return params.find(key) != params.end() ? params[key] : "";
|
||||||
|
}
|
||||||
|
|
||||||
void Device::SetStatusCode(int status_code)
|
void Device::SetStatusCode(int status_code)
|
||||||
{
|
{
|
||||||
if (status_code) {
|
if (status_code) {
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <list>
|
#include <map>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
@ -96,10 +96,10 @@ private:
|
|||||||
string revision;
|
string revision;
|
||||||
|
|
||||||
// The parameters the device was created with
|
// The parameters the device was created with
|
||||||
list<string> params;
|
map<string, string> params;
|
||||||
|
|
||||||
// The default parameters
|
// The default parameters
|
||||||
list<string> default_params;
|
map<string, string> default_params;
|
||||||
|
|
||||||
// Sense Key, ASC and ASCQ
|
// Sense Key, ASC and ASCQ
|
||||||
int status_code;
|
int status_code;
|
||||||
@ -110,7 +110,7 @@ public:
|
|||||||
virtual ~Device() {};
|
virtual ~Device() {};
|
||||||
|
|
||||||
// Override for device specific initializations, to be called after all device properties have been set
|
// Override for device specific initializations, to be called after all device properties have been set
|
||||||
virtual bool Init(const list<string>&) { return true; };
|
virtual bool Init(const map<string, string>&) { return true; };
|
||||||
|
|
||||||
virtual bool Dispatch(SCSIDEV *) = 0;
|
virtual bool Dispatch(SCSIDEV *) = 0;
|
||||||
|
|
||||||
@ -163,13 +163,14 @@ public:
|
|||||||
|
|
||||||
bool SupportsParams() const { return supports_params; }
|
bool SupportsParams() const { return supports_params; }
|
||||||
void SupportsParams(bool supports_paams) { this->supports_params = supports_paams; }
|
void SupportsParams(bool supports_paams) { this->supports_params = supports_paams; }
|
||||||
const list<string> GetParams() const { return params; }
|
const map<string, string> GetParams() const { return params; }
|
||||||
void SetParams(const list<string>& params) { this->params = params; }
|
const string GetParam(const string&);
|
||||||
const list<string> GetDefaultParams() const { return default_params; }
|
void SetParams(const map<string, string>& params) { this->params = params; }
|
||||||
void SetDefaultParams(const list<string>& default_params) { this->default_params = default_params; }
|
const map<string, string> GetDefaultParams() const { return default_params; }
|
||||||
|
void SetDefaultParams(const map<string, string>& default_params) { this->default_params = default_params; }
|
||||||
|
|
||||||
int GetStatusCode() const { return status_code; }
|
int GetStatusCode() const { return status_code; }
|
||||||
void SetStatusCode(int status_code);
|
void SetStatusCode(int);
|
||||||
|
|
||||||
bool Start();
|
bool Start();
|
||||||
void Stop();
|
void Stop();
|
||||||
|
@ -53,8 +53,8 @@ DeviceFactory::DeviceFactory()
|
|||||||
default_params[SCRM] = {};
|
default_params[SCRM] = {};
|
||||||
default_params[SCMO] = {};
|
default_params[SCMO] = {};
|
||||||
default_params[SCCD] = {};
|
default_params[SCCD] = {};
|
||||||
default_params[SCBR] = { "eth0,wlan0" };
|
default_params[SCBR]["interfaces"] = "eth0,wlan0";
|
||||||
default_params[SCDP] = { "eth0,wlan0" };
|
default_params[SCDP]["interfaces"] = "eth0,wlan0";
|
||||||
}
|
}
|
||||||
|
|
||||||
DeviceFactory& DeviceFactory::instance()
|
DeviceFactory& DeviceFactory::instance()
|
||||||
|
@ -36,7 +36,7 @@ public:
|
|||||||
const set<uint32_t>& GetSectorSizes(PbDeviceType type) { return sector_sizes[type]; }
|
const set<uint32_t>& GetSectorSizes(PbDeviceType type) { return sector_sizes[type]; }
|
||||||
const set<uint32_t>& GetSectorSizes(const string&);
|
const set<uint32_t>& GetSectorSizes(const string&);
|
||||||
const set<uint64_t> GetCapacities(PbDeviceType);
|
const set<uint64_t> GetCapacities(PbDeviceType);
|
||||||
const list<string>& GetDefaultParams(PbDeviceType type) { return default_params[type]; }
|
const map<string, string>& GetDefaultParams(PbDeviceType type) { return default_params[type]; }
|
||||||
|
|
||||||
Device *CreateDevice(PbDeviceType type, const string& filename, const string& ext);
|
Device *CreateDevice(PbDeviceType type, const string& filename, const string& ext);
|
||||||
|
|
||||||
@ -46,5 +46,5 @@ private:
|
|||||||
|
|
||||||
map<PbDeviceType, map<uint64_t, Geometry>> geometries;
|
map<PbDeviceType, map<uint64_t, Geometry>> geometries;
|
||||||
|
|
||||||
map<PbDeviceType, list<string>> default_params;
|
map<PbDeviceType, map<string, string>> default_params;
|
||||||
};
|
};
|
||||||
|
@ -25,7 +25,7 @@ void FileSupport::UnreserveFile()
|
|||||||
reserved_files.erase(diskpath.GetPath());
|
reserved_files.erase(diskpath.GetPath());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FileSupport::GetIdsForReservedFile(const Filepath& path, int& id, int& unit) const
|
bool FileSupport::GetIdsForReservedFile(const Filepath& path, int& id, int& unit)
|
||||||
{
|
{
|
||||||
if (reserved_files.find(path.GetPath()) != reserved_files.end()) {
|
if (reserved_files.find(path.GetPath()) != reserved_files.end()) {
|
||||||
const id_set ids = reserved_files[path.GetPath()];
|
const id_set ids = reserved_files[path.GetPath()];
|
||||||
|
@ -38,8 +38,8 @@ public:
|
|||||||
static void SetReservedFiles(const map<string, id_set>& files_in_use) { FileSupport::reserved_files = files_in_use; }
|
static void SetReservedFiles(const map<string, id_set>& files_in_use) { FileSupport::reserved_files = files_in_use; }
|
||||||
void ReserveFile(const Filepath&, int, int);
|
void ReserveFile(const Filepath&, int, int);
|
||||||
void UnreserveFile();
|
void UnreserveFile();
|
||||||
bool GetIdsForReservedFile(const Filepath&, int&, int&) const;
|
|
||||||
|
|
||||||
|
static bool GetIdsForReservedFile(const Filepath&, int&, int&);
|
||||||
static void UnreserveAll();
|
static void UnreserveAll();
|
||||||
|
|
||||||
virtual void Open(const Filepath&) = 0;
|
virtual void Open(const Filepath&) = 0;
|
||||||
|
@ -85,13 +85,13 @@ bool SCSIDaynaPort::Dispatch(SCSIDEV *controller)
|
|||||||
return Disk::Dispatch(controller);
|
return Disk::Dispatch(controller);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SCSIDaynaPort::Init(const list<string>& params)
|
bool SCSIDaynaPort::Init(const map<string, string>& params)
|
||||||
{
|
{
|
||||||
SetParams(params.empty() ? GetDefaultParams() : params);
|
SetParams(params.empty() ? GetDefaultParams() : params);
|
||||||
|
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
// TAP Driver Generation
|
// TAP Driver Generation
|
||||||
m_tap = new CTapDriver(GetParams().front());
|
m_tap = new CTapDriver(GetParam("interfaces"));
|
||||||
m_bTapEnable = m_tap->Init();
|
m_bTapEnable = m_tap->Init();
|
||||||
if(!m_bTapEnable){
|
if(!m_bTapEnable){
|
||||||
LOGERROR("Unable to open the TAP interface");
|
LOGERROR("Unable to open the TAP interface");
|
||||||
|
@ -60,7 +60,7 @@ public:
|
|||||||
SCSIDaynaPort();
|
SCSIDaynaPort();
|
||||||
~SCSIDaynaPort();
|
~SCSIDaynaPort();
|
||||||
|
|
||||||
bool Init(const list<string>&) override;
|
bool Init(const map<string, string>&) override;
|
||||||
void Open(const Filepath& path) override;
|
void Open(const Filepath& path) override;
|
||||||
|
|
||||||
// Commands
|
// Commands
|
||||||
|
@ -63,14 +63,14 @@ SCSIBR::~SCSIBR()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SCSIBR::Init(const list<string>& params)
|
bool SCSIBR::Init(const map<string, string>& params)
|
||||||
{
|
{
|
||||||
// Use default parameters if no parameters were provided
|
// Use default parameters if no parameters were provided
|
||||||
SetParams(params.empty() ? GetDefaultParams() : params);
|
SetParams(params.empty() ? GetDefaultParams() : params);
|
||||||
|
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
// TAP Driver Generation
|
// TAP Driver Generation
|
||||||
tap = new CTapDriver(GetParams().front());
|
tap = new CTapDriver(GetParam("interfaces"));
|
||||||
m_bTapEnable = tap->Init();
|
m_bTapEnable = tap->Init();
|
||||||
|
|
||||||
// Generate MAC Address
|
// Generate MAC Address
|
||||||
|
@ -50,7 +50,7 @@ public:
|
|||||||
SCSIBR();
|
SCSIBR();
|
||||||
~SCSIBR();
|
~SCSIBR();
|
||||||
|
|
||||||
bool Init(const list<string>&) override;
|
bool Init(const map<string, string>&) override;
|
||||||
bool Dispatch(SCSIDEV *) override;
|
bool Dispatch(SCSIDEV *) override;
|
||||||
|
|
||||||
// Commands
|
// Commands
|
||||||
|
@ -8,7 +8,6 @@
|
|||||||
//---------------------------------------------------------------------------
|
//---------------------------------------------------------------------------
|
||||||
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <sstream>
|
|
||||||
#include "rascsi_interface.pb.h"
|
#include "rascsi_interface.pb.h"
|
||||||
#include "exceptions.h"
|
#include "exceptions.h"
|
||||||
#include "protobuf_util.h"
|
#include "protobuf_util.h"
|
||||||
@ -16,6 +15,43 @@
|
|||||||
using namespace std;
|
using namespace std;
|
||||||
using namespace rascsi_interface;
|
using namespace rascsi_interface;
|
||||||
|
|
||||||
|
|
||||||
|
const string GetParam(const PbCommand& command, const string& key)
|
||||||
|
{
|
||||||
|
auto map = command.params();
|
||||||
|
return map[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
const string GetParam(const PbDeviceDefinition& device, const string& key)
|
||||||
|
{
|
||||||
|
auto map = device.params();
|
||||||
|
return map[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddParam(PbCommand& command, const string& key, const string& value)
|
||||||
|
{
|
||||||
|
if (!key.empty() && !value.empty()) {
|
||||||
|
auto& map = *command.mutable_params();
|
||||||
|
map[key] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddParam(PbDevice& device, const string& key, const string& value)
|
||||||
|
{
|
||||||
|
if (!key.empty() && !value.empty()) {
|
||||||
|
auto& map = *device.mutable_params();
|
||||||
|
map[key] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddParam(PbDeviceDefinition& device, const string& key, const string& value)
|
||||||
|
{
|
||||||
|
if (!key.empty() && !value.empty()) {
|
||||||
|
auto& map = *device.mutable_params();
|
||||||
|
map[key] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//---------------------------------------------------------------------------
|
//---------------------------------------------------------------------------
|
||||||
//
|
//
|
||||||
// Serialize/Deserialize protobuf message: Length followed by the actual data.
|
// Serialize/Deserialize protobuf message: Length followed by the actual data.
|
||||||
|
@ -14,6 +14,13 @@
|
|||||||
#include "google/protobuf/message.h"
|
#include "google/protobuf/message.h"
|
||||||
#include "rascsi_interface.pb.h"
|
#include "rascsi_interface.pb.h"
|
||||||
|
|
||||||
|
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 SerializeMessage(int, const google::protobuf::Message&);
|
||||||
void DeserializeMessage(int, google::protobuf::Message&);
|
void DeserializeMessage(int, google::protobuf::Message&);
|
||||||
int ReadNBytes(int, uint8_t *, int);
|
int ReadNBytes(int, uint8_t *, int);
|
||||||
|
@ -31,11 +31,13 @@
|
|||||||
#include "spdlog/spdlog.h"
|
#include "spdlog/spdlog.h"
|
||||||
#include "spdlog/sinks/stdout_color_sinks.h"
|
#include "spdlog/sinks/stdout_color_sinks.h"
|
||||||
#include <spdlog/async.h>
|
#include <spdlog/async.h>
|
||||||
|
#include <sys/sendfile.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <list>
|
#include <list>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <map>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
@ -132,7 +134,7 @@ bool InitService(int port)
|
|||||||
{
|
{
|
||||||
int result = pthread_mutex_init(&ctrl_mutex,NULL);
|
int result = pthread_mutex_init(&ctrl_mutex,NULL);
|
||||||
if (result != EXIT_SUCCESS){
|
if (result != EXIT_SUCCESS){
|
||||||
LOGERROR("Unable to create a mutex. Err code: %d", result);
|
LOGERROR("Unable to create a mutex. Error code: %d", result);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -155,7 +157,7 @@ bool InitService(int port)
|
|||||||
// Bind
|
// Bind
|
||||||
if (bind(monsocket, (struct sockaddr *)&server,
|
if (bind(monsocket, (struct sockaddr *)&server,
|
||||||
sizeof(struct sockaddr_in)) < 0) {
|
sizeof(struct sockaddr_in)) < 0) {
|
||||||
FPRT(stderr, "Error : Already running?\n");
|
FPRT(stderr, "Error: Already running?\n");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -488,7 +490,8 @@ PbDeviceProperties *GetDeviceProperties(const Device *device)
|
|||||||
|
|
||||||
if (device->SupportsParams()) {
|
if (device->SupportsParams()) {
|
||||||
for (const auto& param : device_factory.GetDefaultParams(t)) {
|
for (const auto& param : device_factory.GetDefaultParams(t)) {
|
||||||
properties->add_default_params(param);
|
auto& map = *properties->mutable_default_params();
|
||||||
|
map[param.first] = param.second;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -526,8 +529,8 @@ void GetAllDeviceTypeProperties(PbServerInfo& server_info)
|
|||||||
void GetAvailableImages(PbServerInfo& server_info)
|
void GetAvailableImages(PbServerInfo& server_info)
|
||||||
{
|
{
|
||||||
if (!access(default_image_folder.c_str(), F_OK)) {
|
if (!access(default_image_folder.c_str(), F_OK)) {
|
||||||
for (const auto& entry : filesystem::directory_iterator(default_image_folder)) {
|
for (const auto& entry : filesystem::directory_iterator(default_image_folder, filesystem::directory_options::skip_permission_denied)) {
|
||||||
if (entry.is_regular_file()) {
|
if (entry.is_regular_file() && entry.file_size() && !(entry.file_size() & 0x1ff)) {
|
||||||
GetImageFile(server_info.add_image_files(), entry.path().filename());
|
GetImageFile(server_info.add_image_files(), entry.path().filename());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -556,8 +559,8 @@ void GetDevice(const Device *device, PbDevice *pb_device)
|
|||||||
status->set_locked(device->IsLocked());
|
status->set_locked(device->IsLocked());
|
||||||
|
|
||||||
if (device->SupportsParams()) {
|
if (device->SupportsParams()) {
|
||||||
for (const string& param : device->GetParams()) {
|
for (const auto& param : device->GetParams()) {
|
||||||
pb_device->add_params(param);
|
AddParam(*pb_device, param.first, param.second);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -669,9 +672,16 @@ bool SetDefaultImageFolder(const string& f)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
string SetReservedIds(const list<string>& ids_to_reserve)
|
string SetReservedIds(const string& ids)
|
||||||
{
|
{
|
||||||
set<int> reserved;
|
list<string> ids_to_reserve;
|
||||||
|
stringstream ss(ids);
|
||||||
|
string id;
|
||||||
|
while (getline(ss, id, ',')) {
|
||||||
|
ids_to_reserve.push_back(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
set<int> reserved;
|
||||||
for (string id_to_reserve : ids_to_reserve) {
|
for (string id_to_reserve : ids_to_reserve) {
|
||||||
int id;
|
int id;
|
||||||
if (!GetAsInt(id_to_reserve, id)) {
|
if (!GetAsInt(id_to_reserve, id)) {
|
||||||
@ -692,8 +702,8 @@ string SetReservedIds(const list<string>& ids_to_reserve)
|
|||||||
if (!isFirst) {
|
if (!isFirst) {
|
||||||
s << ", ";
|
s << ", ";
|
||||||
}
|
}
|
||||||
s << id;
|
|
||||||
isFirst = false;
|
isFirst = false;
|
||||||
|
s << id;
|
||||||
}
|
}
|
||||||
|
|
||||||
LOGINFO("Reserved IDs set to: %s", s.str().c_str());
|
LOGINFO("Reserved IDs set to: %s", s.str().c_str());
|
||||||
@ -705,6 +715,272 @@ string SetReservedIds(const list<string>& ids_to_reserve)
|
|||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IsValidFilename(const string& filename)
|
||||||
|
{
|
||||||
|
struct stat st;
|
||||||
|
return stat(filename.c_str(), &st) || !S_ISREG(st.st_mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 (!IsValidFilename(filename)) {
|
||||||
|
return ReturnStatus(fd, false, "Can't create image file: '" + filename + "': Invalid filename");
|
||||||
|
}
|
||||||
|
|
||||||
|
string size = GetParam(command, "size");
|
||||||
|
if (size.empty()) {
|
||||||
|
return ReturnStatus(fd, false, "Can't create image file '" + filename + "': Missing image size");
|
||||||
|
}
|
||||||
|
|
||||||
|
string permission = GetParam(command, "read_only");
|
||||||
|
if (permission.empty()) {
|
||||||
|
return ReturnStatus(fd, false, "Can't create image file'" + filename + "': Missing read-only flag");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcasecmp(permission.c_str(), "true") && strcasecmp(permission.c_str(), "false")) {
|
||||||
|
return ReturnStatus(fd, false, "Can't create image file '" + filename + "': Invalid read-only flag '" + permission + "'");
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Since rascsi is running as root ensure that others can access the file
|
||||||
|
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 (!IsValidFilename(filename)) {
|
||||||
|
return ReturnStatus(fd, false, "Can't delete image file '" + filename + "': Invalid filename");
|
||||||
|
}
|
||||||
|
|
||||||
|
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("%s", string("Deleted image file '" + 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");
|
||||||
|
}
|
||||||
|
|
||||||
|
string to = GetParam(command, "to");
|
||||||
|
if (to.empty()) {
|
||||||
|
return ReturnStatus(fd, false, "Can't rename image file '" + from + "': Missing destination filename");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!IsValidFilename(from)) {
|
||||||
|
return ReturnStatus(fd, false, "Can't rename image file: '" + from + "': Invalid filename");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!IsValidFilename(to)) {
|
||||||
|
return ReturnStatus(fd, false, "Can't rename image file '" + from + "' to '" + to + "': Invalid filename");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (from.find('/') != string::npos) {
|
||||||
|
return ReturnStatus(fd, false, "The source filename '" + from + "' must not contain a path");
|
||||||
|
}
|
||||||
|
if (to.find('/') != string::npos) {
|
||||||
|
return ReturnStatus(fd, false, "The destination filename '" + to + "' must not contain a path");
|
||||||
|
}
|
||||||
|
|
||||||
|
from = default_image_folder + "/" + from;
|
||||||
|
to = default_image_folder + "/" + to;
|
||||||
|
|
||||||
|
struct stat st;
|
||||||
|
if (!stat(to.c_str(), &st)) {
|
||||||
|
return ReturnStatus(fd, false, "Image file '" + to + "' 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("%s", string("Renamed image file '" + from + "' to '" + 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");
|
||||||
|
}
|
||||||
|
|
||||||
|
string to = GetParam(command, "to");
|
||||||
|
if (to.empty()) {
|
||||||
|
return ReturnStatus(fd, false, "Can't copy image file '" + from + "': Missing destination filename");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!IsValidFilename(from)) {
|
||||||
|
return ReturnStatus(fd, false, "Can't copy image file: '" + from + "': Invalid filename");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!IsValidFilename(to)) {
|
||||||
|
return ReturnStatus(fd, false, "Can't copy image file '" + from + "' to '" + to + "': Invalid filename");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (from.find('/') != string::npos) {
|
||||||
|
return ReturnStatus(fd, false, "The source filename '" + from + "' must not contain a path");
|
||||||
|
}
|
||||||
|
if (to.find('/') != string::npos) {
|
||||||
|
return ReturnStatus(fd, false, "The destination filename '" + to + "' must not contain a path");
|
||||||
|
}
|
||||||
|
|
||||||
|
from = default_image_folder + "/" + from;
|
||||||
|
to = default_image_folder + "/" + to;
|
||||||
|
|
||||||
|
struct stat st;
|
||||||
|
if (!stat(to.c_str(), &st)) {
|
||||||
|
return ReturnStatus(fd, false, "Image file '" + to + "' already exists");
|
||||||
|
}
|
||||||
|
|
||||||
|
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)));
|
||||||
|
}
|
||||||
|
|
||||||
|
struct stat st_src;
|
||||||
|
if (fstat(fd_src, &st_src) == -1) {
|
||||||
|
return ReturnStatus(fd, false, "Can't read source image file '" + from + "': " + string(strerror(errno)));
|
||||||
|
}
|
||||||
|
|
||||||
|
int fd_dst = open(to.c_str(), O_WRONLY | O_CREAT, st_src.st_mode);
|
||||||
|
if (fd_dst == -1) {
|
||||||
|
close (fd_dst);
|
||||||
|
|
||||||
|
return ReturnStatus(fd, false, "Can't open destination image file '" + to + "': " + string(strerror(errno)));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sendfile(fd_dst, fd_src, 0, st_src.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("%s", string("Copied image file '" + from + "' to '" + 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 (!IsValidFilename(filename)) {
|
||||||
|
return ReturnStatus(fd, false, "Can't modify image file '" + filename + "': Invalid filename");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filename.find('/') != string::npos) {
|
||||||
|
return ReturnStatus(fd, false, "The image filename '" + filename + "' must not contain a path");
|
||||||
|
}
|
||||||
|
|
||||||
|
filename = default_image_folder + "/" + filename;
|
||||||
|
|
||||||
|
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("%s", string("Protected image file '" + filename + "'").c_str());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
LOGINFO("%s", string("Unprotected image file '" + filename + "'").c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
return ReturnStatus(fd);
|
||||||
|
}
|
||||||
|
|
||||||
void DetachAll()
|
void DetachAll()
|
||||||
{
|
{
|
||||||
Device *map[devices.size()];
|
Device *map[devices.size()];
|
||||||
@ -731,7 +1007,7 @@ bool Attach(int fd, const PbDeviceDefinition& pb_device, Device *map[], bool dry
|
|||||||
return ReturnStatus(fd, false, error);
|
return ReturnStatus(fd, false, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
string filename = pb_device.params_size() > 0 ? pb_device.params().Get(0) : "";
|
string filename = GetParam(pb_device, "file");
|
||||||
string ext;
|
string ext;
|
||||||
size_t separator = filename.rfind('.');
|
size_t separator = filename.rfind('.');
|
||||||
if (separator != string::npos) {
|
if (separator != string::npos) {
|
||||||
@ -824,7 +1100,7 @@ bool Attach(int fd, const PbDeviceDefinition& pb_device, Device *map[], bool dry
|
|||||||
|
|
||||||
int id;
|
int id;
|
||||||
int unit;
|
int unit;
|
||||||
if (file_support->GetIdsForReservedFile(filepath, id, unit)) {
|
if (FileSupport::GetIdsForReservedFile(filepath, id, unit)) {
|
||||||
delete device;
|
delete device;
|
||||||
|
|
||||||
error << "Image file '" << filename << "' is already used by ID " << id << ", unit " << unit;
|
error << "Image file '" << filename << "' is already used by ID " << id << ", unit " << unit;
|
||||||
@ -841,7 +1117,7 @@ bool Attach(int fd, const PbDeviceDefinition& pb_device, Device *map[], bool dry
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const list<string> params = { pb_device.params().begin(), pb_device.params().end() };
|
std::map<string, string> params = { pb_device.params().begin(), pb_device.params().end() };
|
||||||
if (!device->Init(params)) {
|
if (!device->Init(params)) {
|
||||||
error << "Initialization of " << device->GetType() << " device, ID " << id << ", unit " << unit << " failed";
|
error << "Initialization of " << device->GetType() << " device, ID " << id << ", unit " << unit << " failed";
|
||||||
|
|
||||||
@ -911,7 +1187,7 @@ bool Insert(int fd, const PbDeviceDefinition& pb_device, Device *device, bool dr
|
|||||||
return ReturnStatus(fd, false, "Once set the device name cannot be changed anymore");
|
return ReturnStatus(fd, false, "Once set the device name cannot be changed anymore");
|
||||||
}
|
}
|
||||||
|
|
||||||
string filename = pb_device.params_size() > 0 ? pb_device.params().Get(0): "";
|
string filename = GetParam(pb_device, "file");
|
||||||
if (filename.empty()) {
|
if (filename.empty()) {
|
||||||
return ReturnStatus(fd, false, "Missing filename for " + PbOperation_Name(INSERT));
|
return ReturnStatus(fd, false, "Missing filename for " + PbOperation_Name(INSERT));
|
||||||
}
|
}
|
||||||
@ -928,8 +1204,7 @@ bool Insert(int fd, const PbDeviceDefinition& pb_device, Device *device, bool dr
|
|||||||
Filepath filepath;
|
Filepath filepath;
|
||||||
filepath.SetPath(filename.c_str());
|
filepath.SetPath(filename.c_str());
|
||||||
string initial_filename = filepath.GetPath();
|
string initial_filename = filepath.GetPath();
|
||||||
FileSupport *file_support = dynamic_cast<FileSupport *>(device);
|
if (FileSupport::GetIdsForReservedFile(filepath, id, unit)) {
|
||||||
if (file_support->GetIdsForReservedFile(filepath, id, unit)) {
|
|
||||||
ostringstream error;
|
ostringstream error;
|
||||||
error << "Image file '" << filename << "' is already used by ID " << id << ", unit " << unit;
|
error << "Image file '" << filename << "' is already used by ID " << id << ", unit " << unit;
|
||||||
return ReturnStatus(fd, false, error);
|
return ReturnStatus(fd, false, error);
|
||||||
@ -949,6 +1224,8 @@ bool Insert(int fd, const PbDeviceDefinition& pb_device, Device *device, bool dr
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FileSupport *file_support = dynamic_cast<FileSupport *>(device);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
try {
|
try {
|
||||||
file_support->Open(filepath);
|
file_support->Open(filepath);
|
||||||
@ -974,13 +1251,15 @@ bool Insert(int fd, const PbDeviceDefinition& pb_device, Device *device, bool dr
|
|||||||
//
|
//
|
||||||
//---------------------------------------------------------------------------
|
//---------------------------------------------------------------------------
|
||||||
|
|
||||||
bool ProcessCmd(int fd, const PbDeviceDefinition& pb_device, const PbOperation operation, const vector<string>& params, bool dryRun)
|
bool ProcessCmd(int fd, const PbDeviceDefinition& pb_device, const PbCommand& command, bool dryRun)
|
||||||
{
|
{
|
||||||
ostringstream error;
|
ostringstream error;
|
||||||
|
|
||||||
const int id = pb_device.id();
|
const int id = pb_device.id();
|
||||||
const int unit = pb_device.unit();
|
const int unit = pb_device.unit();
|
||||||
const PbDeviceType type = pb_device.type();
|
const PbDeviceType type = pb_device.type();
|
||||||
|
const PbOperation operation = command.operation();
|
||||||
|
const map<string, string> params = { command.params().begin(), command.params().end() };
|
||||||
|
|
||||||
ostringstream s;
|
ostringstream s;
|
||||||
s << (dryRun ? "Validating: " : "Executing: ");
|
s << (dryRun ? "Validating: " : "Executing: ");
|
||||||
@ -988,11 +1267,13 @@ bool ProcessCmd(int fd, const PbDeviceDefinition& pb_device, const PbOperation o
|
|||||||
|
|
||||||
if (!params.empty()) {
|
if (!params.empty()) {
|
||||||
s << ", command params=";
|
s << ", command params=";
|
||||||
for (size_t i = 0; i < params.size(); i++) {
|
bool isFirst = true;
|
||||||
if (i) {
|
for (const auto& param: params) {
|
||||||
|
if (!isFirst) {
|
||||||
s << ", ";
|
s << ", ";
|
||||||
}
|
}
|
||||||
s << "'" << params[i] << "'";
|
isFirst = false;
|
||||||
|
s << "'" << param.first << "=" << param.second << "'";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1000,11 +1281,13 @@ bool ProcessCmd(int fd, const PbDeviceDefinition& pb_device, const PbOperation o
|
|||||||
|
|
||||||
if (pb_device.params_size()) {
|
if (pb_device.params_size()) {
|
||||||
s << ", device params=";
|
s << ", device params=";
|
||||||
for (int i = 0; i < pb_device.params_size(); i++) {
|
bool isFirst = true;
|
||||||
if (i) {
|
for (const auto& param: pb_device.params()) {
|
||||||
|
if (!isFirst) {
|
||||||
s << ", ";
|
s << ", ";
|
||||||
}
|
}
|
||||||
s << "'" << pb_device.params().Get(i) << "'";
|
isFirst = false;
|
||||||
|
s << "'" << param.first << "=" << param.second << "'";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1138,26 +1421,46 @@ bool ProcessCmd(int fd, const PbDeviceDefinition& pb_device, const PbOperation o
|
|||||||
|
|
||||||
bool ProcessCmd(const int fd, const PbCommand& command)
|
bool ProcessCmd(const int fd, const PbCommand& command)
|
||||||
{
|
{
|
||||||
if (command.operation() == DETACH_ALL) {
|
switch (command.operation()) {
|
||||||
DetachAll();
|
case DETACH_ALL:
|
||||||
return ReturnStatus(fd);
|
DetachAll();
|
||||||
}
|
return ReturnStatus(fd);
|
||||||
else if (command.operation() == RESERVE) {
|
|
||||||
const list<string> ids = { command.params().begin(), command.params().end() };
|
case RESERVE: {
|
||||||
string invalid_id = SetReservedIds(ids);
|
const string ids = GetParam(command, "ids");
|
||||||
if (!invalid_id.empty()) {
|
string invalid_id = SetReservedIds(ids);
|
||||||
return ReturnStatus(fd, false,"Invalid ID " + invalid_id + " for " + PbOperation_Name(RESERVE));
|
if (!invalid_id.empty()) {
|
||||||
|
return ReturnStatus(fd, false, "Invalid ID " + invalid_id + " for " + PbOperation_Name(RESERVE));
|
||||||
|
}
|
||||||
|
|
||||||
|
return ReturnStatus(fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ReturnStatus(fd);
|
case CREATE_IMAGE:
|
||||||
}
|
return CreateImage(fd, command);
|
||||||
|
|
||||||
const vector<string> params = { command.params().begin(), command.params().end() };
|
case DELETE_IMAGE:
|
||||||
|
return DeleteImage(fd, command);
|
||||||
|
|
||||||
|
case RENAME_IMAGE:
|
||||||
|
return RenameImage(fd, command);
|
||||||
|
|
||||||
|
case COPY_IMAGE:
|
||||||
|
return CopyImage(fd, command);
|
||||||
|
|
||||||
|
case PROTECT_IMAGE:
|
||||||
|
case UNPROTECT_IMAGE:
|
||||||
|
return SetImagePermissions(fd, command);
|
||||||
|
|
||||||
|
default:
|
||||||
|
// This is a device-specific command handled below
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
// Remember the list of reserved files, than run the dry run
|
// Remember the list of reserved files, than run the dry run
|
||||||
const auto reserved_files = FileSupport::GetReservedFiles();
|
const auto reserved_files = FileSupport::GetReservedFiles();
|
||||||
for (const auto& device : command.devices()) {
|
for (const auto& device : command.devices()) {
|
||||||
if (!ProcessCmd(fd, device, command.operation(), params, true)) {
|
if (!ProcessCmd(fd, device, command, true)) {
|
||||||
// Dry run failed, restore the file list
|
// Dry run failed, restore the file list
|
||||||
FileSupport::SetReservedFiles(reserved_files);
|
FileSupport::SetReservedFiles(reserved_files);
|
||||||
return false;
|
return false;
|
||||||
@ -1167,7 +1470,7 @@ bool ProcessCmd(const int fd, const PbCommand& command)
|
|||||||
// Restore list of reserved files, then execute the command
|
// Restore list of reserved files, then execute the command
|
||||||
FileSupport::SetReservedFiles(reserved_files);
|
FileSupport::SetReservedFiles(reserved_files);
|
||||||
for (const auto& device : command.devices()) {
|
for (const auto& device : command.devices()) {
|
||||||
if (!ProcessCmd(fd, device, command.operation(), params, false)) {
|
if (!ProcessCmd(fd, device, command, false)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1248,15 +1551,7 @@ bool ParseArgument(int argc, char* argv[], int& port)
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
case 'r': {
|
case 'r': {
|
||||||
stringstream ss(optarg);
|
string invalid_id = SetReservedIds(optarg);
|
||||||
string id;
|
|
||||||
|
|
||||||
list<string> ids;
|
|
||||||
while (getline(ss, id, ',')) {
|
|
||||||
ids.push_back(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
string invalid_id = SetReservedIds(ids);
|
|
||||||
if (!invalid_id.empty()) {
|
if (!invalid_id.empty()) {
|
||||||
cerr << "Invalid ID " << invalid_id << " for " << PbOperation_Name(RESERVE);
|
cerr << "Invalid ID " << invalid_id << " for " << PbOperation_Name(RESERVE);
|
||||||
return false;
|
return false;
|
||||||
@ -1298,9 +1593,7 @@ bool ParseArgument(int argc, char* argv[], int& port)
|
|||||||
device->set_unit(unit);
|
device->set_unit(unit);
|
||||||
device->set_type(type);
|
device->set_type(type);
|
||||||
device->set_block_size(block_size);
|
device->set_block_size(block_size);
|
||||||
if (strlen(optarg)) {
|
AddParam(*device, "file", optarg);
|
||||||
device->add_params(optarg);
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t separatorPos = name.find(':');
|
size_t separatorPos = name.find(':');
|
||||||
if (separatorPos != string::npos) {
|
if (separatorPos != string::npos) {
|
||||||
@ -1412,7 +1705,7 @@ static void *MonThread(void *param)
|
|||||||
case LOG_LEVEL: {
|
case LOG_LEVEL: {
|
||||||
LOGTRACE(string("Received " + PbOperation_Name(LOG_LEVEL) + " command").c_str());
|
LOGTRACE(string("Received " + PbOperation_Name(LOG_LEVEL) + " command").c_str());
|
||||||
|
|
||||||
string log_level = command.params_size() > 0 ? command.params().Get(0) : "";
|
string log_level = GetParam(command, "level");
|
||||||
bool status = SetLogLevel(log_level);
|
bool status = SetLogLevel(log_level);
|
||||||
if (!status) {
|
if (!status) {
|
||||||
ReturnStatus(fd, false, "Invalid log level: " + log_level);
|
ReturnStatus(fd, false, "Invalid log level: " + log_level);
|
||||||
@ -1426,7 +1719,7 @@ static void *MonThread(void *param)
|
|||||||
case DEFAULT_FOLDER: {
|
case DEFAULT_FOLDER: {
|
||||||
LOGTRACE(string("Received " + PbOperation_Name(DEFAULT_FOLDER) + " command").c_str());
|
LOGTRACE(string("Received " + PbOperation_Name(DEFAULT_FOLDER) + " command").c_str());
|
||||||
|
|
||||||
string folder = command.params_size() > 0 ? command.params().Get(0) : "";
|
string folder = GetParam(command, "folder");
|
||||||
if (folder.empty()) {
|
if (folder.empty()) {
|
||||||
ReturnStatus(fd, false, "Can't set default image folder: Missing folder name");
|
ReturnStatus(fd, false, "Can't set default image folder: Missing folder name");
|
||||||
}
|
}
|
||||||
|
@ -27,41 +27,101 @@ enum PbDeviceType {
|
|||||||
SCDP = 7;
|
SCDP = 7;
|
||||||
}
|
}
|
||||||
|
|
||||||
// rascsi remote operations, returns PbResult
|
// rascsi remote operations, returning PbResult
|
||||||
enum PbOperation {
|
enum PbOperation {
|
||||||
NONE = 0;
|
NONE = 0;
|
||||||
// Gets the server information
|
|
||||||
SERVER_INFO = 1;
|
|
||||||
// Gets information for a list of attached devices. Returns data for all attached devices if empty.
|
|
||||||
DEVICE_INFO = 2;
|
|
||||||
// Set the default folder for image files. PbCommand.params contains the folder name.
|
|
||||||
DEFAULT_FOLDER = 3;
|
|
||||||
// Set server log level. PbCommand.params contains the log level.
|
|
||||||
LOG_LEVEL = 4;
|
|
||||||
// Attach devices
|
// Attach devices
|
||||||
ATTACH = 5;
|
// 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 devices
|
// Detach devices
|
||||||
DETACH = 6;
|
DETACH = 2;
|
||||||
|
|
||||||
// Detach all devices, does not require a device list
|
// Detach all devices, does not require a device list
|
||||||
DETACH_ALL = 7;
|
DETACH_ALL = 3;
|
||||||
|
|
||||||
// Start device
|
// Start device
|
||||||
START = 8;
|
START = 4;
|
||||||
|
|
||||||
// Stop device, e.g. park drive
|
// Stop device, e.g. park drive
|
||||||
STOP = 9;
|
STOP = 5;
|
||||||
|
|
||||||
// Insert medium
|
// Insert medium
|
||||||
INSERT = 10;
|
// Parameters:
|
||||||
|
// "file": The filename, relative to the default image folder. It must not contain a slash.
|
||||||
|
INSERT = 6;
|
||||||
|
|
||||||
// Eject medium
|
// Eject medium
|
||||||
EJECT = 11;
|
EJECT = 7;
|
||||||
|
|
||||||
// Write-protect medium (not possible for read-only media)
|
// Write-protect medium (not possible for read-only media)
|
||||||
PROTECT = 12;
|
PROTECT = 8;
|
||||||
|
|
||||||
// Make medium writable (not possible for read-only media)
|
// Make medium writable (not possible for read-only media)
|
||||||
UNPROTECT = 13;
|
UNPROTECT = 9;
|
||||||
// IDs blocked from being used, usually the IDs of the initiators (computers) in the SCSI chain.
|
|
||||||
// PbCommand.params contains the list of IDs to reserve, or is empty in order not to reserve any ID.
|
// Gets the server information
|
||||||
|
SERVER_INFO = 10;
|
||||||
|
|
||||||
|
// Gets information for a list of attached devices. Returns data for all attached devices if empty.
|
||||||
|
DEVICE_INFO = 11;
|
||||||
|
|
||||||
|
// Set the default folder for image files.
|
||||||
|
// Parameters:
|
||||||
|
// "folder": The default folder name.
|
||||||
|
DEFAULT_FOLDER = 12;
|
||||||
|
|
||||||
|
// Set server log level.
|
||||||
|
// Parameters:
|
||||||
|
// "level": The new log level
|
||||||
|
LOG_LEVEL = 13;
|
||||||
|
|
||||||
|
// Block IDs from being used, usually the IDs of the initiators (computers) in the SCSI chain.
|
||||||
|
// Parameters:
|
||||||
|
// "ids": A comma-separated list of IDs to reserve, or an empty string in order not to reserve any ID.
|
||||||
RESERVE = 14;
|
RESERVE = 14;
|
||||||
|
|
||||||
|
// 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": "true" (case-insensitive) in order to create a read-only file, otherwise "false"
|
||||||
|
CREATE_IMAGE = 15;
|
||||||
|
|
||||||
|
// Delete an image file.
|
||||||
|
// Parameters:
|
||||||
|
// "file": The filename, relative to the default image folder. It must not contain a slash.
|
||||||
|
DELETE_IMAGE = 16;
|
||||||
|
|
||||||
|
// 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 = 17;
|
||||||
|
|
||||||
|
// Copy an image file.
|
||||||
|
// Parameters:
|
||||||
|
// "from": The source filename, relative to the default image folder. It must not contain a slash.
|
||||||
|
// "to": The destination filename, relative to the default image folder. It must not contain a slash.
|
||||||
|
// The destination filename must not yet exist.
|
||||||
|
COPY_IMAGE = 18;
|
||||||
|
|
||||||
|
// Write-protect an image file.
|
||||||
|
// Parameters:
|
||||||
|
// "file": The filename, relative to the default image folder. It must not contain a slash.
|
||||||
|
PROTECT_IMAGE = 19;
|
||||||
|
|
||||||
|
// Make an image file writable.
|
||||||
|
// Parameters:
|
||||||
|
// "file": The filename, relative to the default image folder. It must not contain a slash.
|
||||||
|
UNPROTECT_IMAGE = 20;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The properties supported by a device, helping clients to offer a good user experience
|
// The properties supported by a device
|
||||||
message PbDeviceProperties {
|
message PbDeviceProperties {
|
||||||
// Read-only media (e.g. CD-ROMs) are not protectable but permanently read-only
|
// Read-only media (e.g. CD-ROMs) are not protectable but permanently read-only
|
||||||
bool read_only = 1;
|
bool read_only = 1;
|
||||||
@ -77,8 +137,8 @@ message PbDeviceProperties {
|
|||||||
bool supports_file = 6;
|
bool supports_file = 6;
|
||||||
// Device supports parameters other than a filename
|
// Device supports parameters other than a filename
|
||||||
bool supports_params = 7;
|
bool supports_params = 7;
|
||||||
// Ordered list of default parameters, if any (requires supports_params to be true)
|
// List of default parameters, if any (requires supports_params to be true)
|
||||||
repeated string default_params = 8;
|
map<string, string> default_params = 8;
|
||||||
// Number of supported LUNs, at least 1 (for LUN 0)
|
// Number of supported LUNs, at least 1 (for LUN 0)
|
||||||
uint32 luns = 9;
|
uint32 luns = 9;
|
||||||
// Unordered list of permitted block sizes in bytes, empty if the block size is not configurable
|
// Unordered list of permitted block sizes in bytes, empty if the block size is not configurable
|
||||||
@ -117,8 +177,8 @@ message PbDeviceDefinition {
|
|||||||
int32 id = 1;
|
int32 id = 1;
|
||||||
int32 unit = 2;
|
int32 unit = 2;
|
||||||
PbDeviceType type = 3;
|
PbDeviceType type = 3;
|
||||||
// Optional device specific parameters, e.g. the name of an image file
|
// Device specific named parameters, e.g. the name of an image file
|
||||||
repeated string params = 4;
|
map<string, string> params = 4;
|
||||||
// The optional block size in bytes per sector, must be one of the supported block sizes for SASI/SCSI
|
// The optional block size in bytes per sector, must be one of the supported block sizes for SASI/SCSI
|
||||||
int32 block_size = 5;
|
int32 block_size = 5;
|
||||||
// The device name components
|
// The device name components
|
||||||
@ -138,8 +198,8 @@ message PbDevice {
|
|||||||
PbDeviceStatus status = 5;
|
PbDeviceStatus status = 5;
|
||||||
// Image file information, if the device supports image files
|
// Image file information, if the device supports image files
|
||||||
PbImageFile file = 6;
|
PbImageFile file = 6;
|
||||||
// Ordered list of effective parameters the device was created with
|
// Effective parameters the device was created with
|
||||||
repeated string params = 7;
|
map<string, string> params = 7;
|
||||||
string vendor = 8;
|
string vendor = 8;
|
||||||
string product = 9;
|
string product = 9;
|
||||||
string revision = 10;
|
string revision = 10;
|
||||||
@ -158,8 +218,8 @@ message PbCommand {
|
|||||||
PbOperation operation = 1;
|
PbOperation operation = 1;
|
||||||
// The non-empty list of devices for this command
|
// The non-empty list of devices for this command
|
||||||
repeated PbDeviceDefinition devices = 2;
|
repeated PbDeviceDefinition devices = 2;
|
||||||
// The optional parameters depending on the operation, e.g. a filename, or a network interface list
|
// The named parameters for the operation, e.g. a filename, or a network interface list
|
||||||
repeated string params = 3;
|
map<string, string> params = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The result of a command
|
// The result of a command
|
||||||
|
@ -142,10 +142,13 @@ void DisplayDeviceInfo(const PbDevice& pb_device)
|
|||||||
cout << " ";
|
cout << " ";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pb_device.params_size()) {
|
bool isFirst = true;
|
||||||
for (const string param : pb_device.params()) {
|
for (const auto& param : pb_device.params()) {
|
||||||
cout << param << " ";
|
if (!isFirst) {
|
||||||
|
cout << " ";
|
||||||
}
|
}
|
||||||
|
isFirst = false;
|
||||||
|
cout << param.first << "=" << param.second;
|
||||||
}
|
}
|
||||||
|
|
||||||
cout << endl;
|
cout << endl;
|
||||||
@ -184,7 +187,7 @@ void CommandLogLevel(const string& hostname, int port, const string& log_level)
|
|||||||
{
|
{
|
||||||
PbCommand command;
|
PbCommand command;
|
||||||
command.set_operation(LOG_LEVEL);
|
command.set_operation(LOG_LEVEL);
|
||||||
command.add_params(log_level);
|
AddParam(command, "level", log_level);
|
||||||
|
|
||||||
PbResult result;
|
PbResult result;
|
||||||
SendCommand(hostname.c_str(), port, command, result);
|
SendCommand(hostname.c_str(), port, command, result);
|
||||||
@ -194,12 +197,75 @@ void CommandReserve(const string&hostname, int port, const string& reserved_ids)
|
|||||||
{
|
{
|
||||||
PbCommand command;
|
PbCommand command;
|
||||||
command.set_operation(RESERVE);
|
command.set_operation(RESERVE);
|
||||||
|
AddParam(command, "ids", reserved_ids);
|
||||||
|
|
||||||
stringstream ss(reserved_ids);
|
PbResult result;
|
||||||
string reserved_id;
|
SendCommand(hostname.c_str(), port, command, result);
|
||||||
|
}
|
||||||
|
|
||||||
while (getline(ss, reserved_id, ',')) {
|
void CommandCreateImage(const string&hostname, int port, const string& image_params)
|
||||||
command.add_params(reserved_id);
|
{
|
||||||
|
PbCommand command;
|
||||||
|
command.set_operation(CREATE_IMAGE);
|
||||||
|
|
||||||
|
size_t separatorPos = image_params.find(COMPONENT_SEPARATOR);
|
||||||
|
if (separatorPos != string::npos) {
|
||||||
|
AddParam(command, "file", image_params.substr(0, separatorPos));
|
||||||
|
AddParam(command, "size", image_params.substr(separatorPos + 1));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
cerr << "Error: Invalid file descriptor '" << image_params << "', format is NAME:SIZE" << endl;
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
AddParam(command, "read_only", "false");
|
||||||
|
|
||||||
|
PbResult result;
|
||||||
|
SendCommand(hostname.c_str(), port, command, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CommandDeleteImage(const string&hostname, int port, const string& filename)
|
||||||
|
{
|
||||||
|
PbCommand command;
|
||||||
|
command.set_operation(DELETE_IMAGE);
|
||||||
|
AddParam(command, "file", filename);
|
||||||
|
|
||||||
|
PbResult result;
|
||||||
|
SendCommand(hostname.c_str(), port, command, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CommandRenameImage(const string&hostname, int port, const string& image_params)
|
||||||
|
{
|
||||||
|
PbCommand command;
|
||||||
|
command.set_operation(RENAME_IMAGE);
|
||||||
|
|
||||||
|
size_t separatorPos = image_params.find(COMPONENT_SEPARATOR);
|
||||||
|
if (separatorPos != string::npos) {
|
||||||
|
AddParam(command, "from", image_params.substr(0, separatorPos));
|
||||||
|
AddParam(command, "to", image_params.substr(separatorPos + 1));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
cerr << "Error: Invalid file descriptor '" << image_params << "', format is CURRENT_NAME:NEW_NAME" << endl;
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
PbResult result;
|
||||||
|
SendCommand(hostname.c_str(), port, command, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CommandCopyImage(const string&hostname, int port, const string& image_params)
|
||||||
|
{
|
||||||
|
PbCommand command;
|
||||||
|
command.set_operation(COPY_IMAGE);
|
||||||
|
|
||||||
|
size_t separatorPos = image_params.find(COMPONENT_SEPARATOR);
|
||||||
|
if (separatorPos != string::npos) {
|
||||||
|
AddParam(command, "from", image_params.substr(0, separatorPos));
|
||||||
|
AddParam(command, "to", image_params.substr(separatorPos + 1));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
cerr << "Error: Invalid file descriptor '" << image_params << "', format is CURRENT_NAME:NEW_NAME" << endl;
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
PbResult result;
|
PbResult result;
|
||||||
@ -210,7 +276,7 @@ void CommandDefaultImageFolder(const string& hostname, int port, const string& f
|
|||||||
{
|
{
|
||||||
PbCommand command;
|
PbCommand command;
|
||||||
command.set_operation(DEFAULT_FOLDER);
|
command.set_operation(DEFAULT_FOLDER);
|
||||||
command.add_params(folder);
|
AddParam(command, "folder", folder);
|
||||||
|
|
||||||
PbResult result;
|
PbResult result;
|
||||||
SendCommand(hostname.c_str(), port, command, result);
|
SendCommand(hostname.c_str(), port, command, result);
|
||||||
@ -317,8 +383,7 @@ void CommandServerInfo(const string& hostname, int port)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (properties.supports_params() && properties.default_params_size()) {
|
if (properties.supports_params() && properties.default_params_size()) {
|
||||||
list<string> params = { properties.default_params().begin(), properties.default_params().end() };
|
map<string, string> params = { properties.default_params().begin(), properties.default_params().end() };
|
||||||
params.sort([](const auto& a, const auto& b) { return a < b; });
|
|
||||||
|
|
||||||
cout << " Default parameters: ";
|
cout << " Default parameters: ";
|
||||||
|
|
||||||
@ -327,7 +392,7 @@ void CommandServerInfo(const string& hostname, int port)
|
|||||||
if (!isFirst) {
|
if (!isFirst) {
|
||||||
cout << ", ";
|
cout << ", ";
|
||||||
}
|
}
|
||||||
cout << param;
|
cout << param.first << "=" << param.second;
|
||||||
|
|
||||||
isFirst = false;
|
isFirst = false;
|
||||||
}
|
}
|
||||||
@ -471,20 +536,22 @@ int main(int argc, char* argv[])
|
|||||||
if (argc < 2) {
|
if (argc < 2) {
|
||||||
cerr << "SCSI Target Emulator RaSCSI Controller" << endl;
|
cerr << "SCSI Target Emulator RaSCSI Controller" << endl;
|
||||||
cerr << "version " << rascsi_get_version_string() << " (" << __DATE__ << ", " << __TIME__ << ")" << endl;
|
cerr << "version " << rascsi_get_version_string() << " (" << __DATE__ << ", " << __TIME__ << ")" << endl;
|
||||||
cerr << "Usage: " << argv[0] << " -i ID [-u UNIT] [-c CMD] [-t TYPE] [-b BLOCK_SIZE] [-n NAME] [-f FILE] ";
|
cerr << "Usage: " << argv[0] << " -i ID [-u UNIT] [-c CMD] [-t TYPE] [-b BLOCK_SIZE] [-n NAME] [-f FILE|PARAM] ";
|
||||||
cerr << "[-d DEFAULT_IMAGE_FOLDER] [-g LOG_LEVEL] [-h HOST] [-p PORT] [-r RESERVED_IDS] [-l] [-v]" << endl;
|
cerr << "[-d IMAGE_FOLDER] [-g LOG_LEVEL] [-h HOST] [-p PORT] [-r RESERVED_IDS] ";
|
||||||
cerr << " where ID := {0|1|2|3|4|5|6|7}" << endl;
|
cerr << "[-a FILENAME:FILESIZE] [-w FILENAME] [-m CURRENT_NAME:NEW_NAME] [-x CURRENT_NAME:NEW_NAME] ";
|
||||||
cerr << " UNIT := {0|1}, default setting is 0." << endl;
|
cerr << "[-l] [-v]" << endl;
|
||||||
|
cerr << " where ID := {0-7}" << endl;
|
||||||
|
cerr << " UNIT := {0|1}, default is 0" << endl;
|
||||||
cerr << " CMD := {attach|detach|insert|eject|protect|unprotect|show}" << endl;
|
cerr << " CMD := {attach|detach|insert|eject|protect|unprotect|show}" << endl;
|
||||||
cerr << " TYPE := {sahd|schd|scrm|sccd|scmo|scbr|scdp} or convenience type {hd|rm|mo|cd|bridge|daynaport}" << endl;
|
cerr << " TYPE := {sahd|schd|scrm|sccd|scmo|scbr|scdp} or convenience type {hd|rm|mo|cd|bridge|daynaport}" << endl;
|
||||||
cerr << " BLOCK_SIZE := {256|512|1024|2048|4096) bytes per hard disk drive block" << endl;
|
cerr << " BLOCK_SIZE := {256|512|1024|2048|4096) bytes per hard disk drive block" << endl;
|
||||||
cerr << " NAME := name of device to attach (VENDOR:PRODUCT:REVISION)" << endl;
|
cerr << " NAME := name of device to attach (VENDOR:PRODUCT:REVISION)" << endl;
|
||||||
cerr << " FILE := image file path" << endl;
|
cerr << " FILE|PARAM := image file path or device-specific parameter" << endl;
|
||||||
cerr << " DEFAULT_IMAGE_FOLDER := default location for image files, default is '~/images'" << endl;
|
cerr << " IMAGE_FOLDER := default location for image files, default is '~/images'" << endl;
|
||||||
cerr << " HOST := rascsi host to connect to, default is 'localhost'" << endl;
|
cerr << " HOST := rascsi host to connect to, default is 'localhost'" << endl;
|
||||||
cerr << " PORT := rascsi port to connect to, default is 6868" << endl;
|
cerr << " PORT := rascsi port to connect to, default is 6868" << endl;
|
||||||
cerr << " RESERVED_IDS := comma-separated list of IDs to reserve" << endl;
|
cerr << " RESERVED_IDS := comma-separated list of IDs to reserve" << endl;
|
||||||
cerr << " LOG_LEVEL := log level {trace|debug|info|warn|err|critical|off}, default is 'trace'" << endl;
|
cerr << " LOG_LEVEL := log level {trace|debug|info|warn|err|critical|off}, default is 'info'" << endl;
|
||||||
cerr << " If CMD is 'attach' or 'insert' the FILE parameter is required." << endl;
|
cerr << " If CMD is 'attach' or 'insert' the FILE parameter is required." << endl;
|
||||||
cerr << "Usage: " << argv[0] << " -l" << endl;
|
cerr << "Usage: " << argv[0] << " -l" << endl;
|
||||||
cerr << " Print device list." << endl;
|
cerr << " Print device list." << endl;
|
||||||
@ -499,14 +566,16 @@ int main(int argc, char* argv[])
|
|||||||
device->set_id(-1);
|
device->set_id(-1);
|
||||||
const char *hostname = "localhost";
|
const char *hostname = "localhost";
|
||||||
int port = 6868;
|
int port = 6868;
|
||||||
|
string param;
|
||||||
string log_level;
|
string log_level;
|
||||||
string default_folder;
|
string default_folder;
|
||||||
string reserved_ids;
|
string reserved_ids;
|
||||||
|
string image_params;
|
||||||
bool list = false;
|
bool list = false;
|
||||||
|
|
||||||
opterr = 1;
|
opterr = 1;
|
||||||
int opt;
|
int opt;
|
||||||
while ((opt = getopt(argc, argv, "b:c:d:f:g:h:i:n:p:r:t:u:lsv")) != -1) {
|
while ((opt = getopt(argc, argv, "a:b:c:d:f:g:h:i:m:n:p:r:t:u:x:w:lsv")) != -1) {
|
||||||
switch (opt) {
|
switch (opt) {
|
||||||
case 'i':
|
case 'i':
|
||||||
device->set_id(optarg[0] - '0');
|
device->set_id(optarg[0] - '0');
|
||||||
@ -516,6 +585,11 @@ int main(int argc, char* argv[])
|
|||||||
device->set_unit(optarg[0] - '0');
|
device->set_unit(optarg[0] - '0');
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'a':
|
||||||
|
command.set_operation(CREATE_IMAGE);
|
||||||
|
image_params = optarg;
|
||||||
|
break;
|
||||||
|
|
||||||
case 'b':
|
case 'b':
|
||||||
int block_size;
|
int block_size;
|
||||||
if (!GetAsInt(optarg, block_size)) {
|
if (!GetAsInt(optarg, block_size)) {
|
||||||
@ -539,7 +613,7 @@ int main(int argc, char* argv[])
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case 'f':
|
case 'f':
|
||||||
device->add_params(optarg);
|
param = optarg;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 't':
|
case 't':
|
||||||
@ -563,6 +637,11 @@ int main(int argc, char* argv[])
|
|||||||
list = true;
|
list = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'm':
|
||||||
|
command.set_operation(RENAME_IMAGE);
|
||||||
|
image_params = optarg;
|
||||||
|
break;
|
||||||
|
|
||||||
case 'n': {
|
case 'n': {
|
||||||
string vendor;
|
string vendor;
|
||||||
string product;
|
string product;
|
||||||
@ -612,6 +691,16 @@ int main(int argc, char* argv[])
|
|||||||
cout << rascsi_get_version_string() << endl;
|
cout << rascsi_get_version_string() << endl;
|
||||||
exit(EXIT_SUCCESS);
|
exit(EXIT_SUCCESS);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'x':
|
||||||
|
command.set_operation(COPY_IMAGE);
|
||||||
|
image_params = optarg;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'w':
|
||||||
|
command.set_operation(DELETE_IMAGE);
|
||||||
|
image_params = optarg;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -632,6 +721,22 @@ int main(int argc, char* argv[])
|
|||||||
CommandReserve(hostname, port, reserved_ids);
|
CommandReserve(hostname, port, reserved_ids);
|
||||||
exit(EXIT_SUCCESS);
|
exit(EXIT_SUCCESS);
|
||||||
|
|
||||||
|
case CREATE_IMAGE:
|
||||||
|
CommandCreateImage(hostname, port, image_params);
|
||||||
|
exit(EXIT_SUCCESS);
|
||||||
|
|
||||||
|
case DELETE_IMAGE:
|
||||||
|
CommandDeleteImage(hostname, port, image_params);
|
||||||
|
exit(EXIT_SUCCESS);
|
||||||
|
|
||||||
|
case RENAME_IMAGE:
|
||||||
|
CommandRenameImage(hostname, port, image_params);
|
||||||
|
exit(EXIT_SUCCESS);
|
||||||
|
|
||||||
|
case COPY_IMAGE:
|
||||||
|
CommandCopyImage(hostname, port, image_params);
|
||||||
|
exit(EXIT_SUCCESS);
|
||||||
|
|
||||||
case DEVICE_INFO:
|
case DEVICE_INFO:
|
||||||
CommandDeviceInfo(hostname, port, command);
|
CommandDeviceInfo(hostname, port, command);
|
||||||
exit(EXIT_SUCCESS);
|
exit(EXIT_SUCCESS);
|
||||||
@ -649,6 +754,15 @@ int main(int argc, char* argv[])
|
|||||||
exit(EXIT_SUCCESS);
|
exit(EXIT_SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!param.empty()) {
|
||||||
|
if (device->type() == SCBR || device->type() == SCDP) {
|
||||||
|
AddParam(*device, "interfaces", param);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
AddParam(*device, "file", param);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
PbResult result;
|
PbResult result;
|
||||||
SendCommand(hostname, port, command, result);
|
SendCommand(hostname, port, command, result);
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user