mirror of
https://github.com/akuker/RASCSI.git
synced 2025-02-16 19:31:09 +00:00
Feature extended server information (#169)
* PbDevice returns information on whether a device is removable * Fixed log message * Added additional status data (ejectable, protctable, removable) * Assume ejectable == removable * Removed obsolete fields * Added locked/lockable to interface * Code review * Logging update * The user should be allowed to also write protect hard disk drives * Updated error handling * Testing * INSERT also has to search in the default file folder * Added TODO * Manpage update * Message updates * Moved some sanity checks to the server, so that they are run for any client * Updated error handling * Moved CR/LF handling to the client, the server should not format too much * rascsi should not mention "rasctl" in its messages, there may be other clients * Improved error messages * Improved error handling by returning an error string * Added TODO * Moved check for duplicate ID to ProcessCmd() * Removed debug output * Moved include * Removed obsolete includes, updated includes * Reverted ready check * Fixed error messages * Added convenience method * Simplified error handling * Socket/error handling review * "NO MEDIA" is added by the client, not by the server * Added missing include * Only check for duplicate ID when attaching a device * Added type/string converters for PbDeviceType * PbDevice indicates whether image files are supported * Write-protecing a read-only devices is not treated as an error anymore * Updated read-only check * Made difference between read-only and protected more explicit * Added comment * Comment update * Made hard disk drives write-protectable by the user * Resolved https://github.com/akuker/RASCSI/issues/166 * Addressing compiler error - type needs to be initialized Co-authored-by: akuker <akuker@gmail.com>
This commit is contained in:
parent
3d14950548
commit
0a5298b8ac
@ -54,6 +54,7 @@ Command is the operation being requested. options are:
|
||||
insert: insert media (Magneto-Optical and CD only)
|
||||
eject: eject media (Magneto-Optical and CD only)
|
||||
protect: Write protect the media (Magneto-Optical only)
|
||||
unprotect: Remove write protection from the media (Magneto-Optical only)
|
||||
.IP
|
||||
When the command is omitted, rasctl will default to the 'attach' command.
|
||||
.TP
|
||||
|
@ -6,8 +6,8 @@ NAME
|
||||
rasctl - Sends management commands to the rascsi process
|
||||
|
||||
SYNOPSIS
|
||||
rasctl -l | -s | [-g LOG_LEVEL] [-h HOST] [-p PORT] -i ID [-c CMD] [-f
|
||||
FILE] [-t TYPE] [-u UNIT]
|
||||
rasctl -l | -s | [-g LOG_LEVEL] [-h HOST] [-p PORT] [-v] -i ID [-c CMD]
|
||||
[-f FILE] [-t TYPE] [-u UNIT]
|
||||
|
||||
DESCRIPTION
|
||||
rasctl Sends commands to the rascsi process to make configuration ad‐
|
||||
@ -34,7 +34,9 @@ OPTIONS
|
||||
-p PORT
|
||||
The rascsi port to connect to, default is 6868.
|
||||
|
||||
-s Display server-side configuration settings.
|
||||
-s Display the server-side configuration settings.
|
||||
|
||||
-v Display the rascsi version.
|
||||
|
||||
-i ID ID is the SCSI ID that you want to control. (0-7)
|
||||
|
||||
@ -44,27 +46,29 @@ OPTIONS
|
||||
insert: insert media (Magneto-Optical and CD only)
|
||||
eject: eject media (Magneto-Optical and CD only)
|
||||
protect: Write protect the media (Magneto-Optical only)
|
||||
unprotect: Remove write protection from the media (Magneto-
|
||||
Optical only)
|
||||
|
||||
When the command is omitted, rasctl will default to the 'attach'
|
||||
command.
|
||||
|
||||
-f FILE
|
||||
Path to the disk image file. See the rascsi(1) man page for al‐
|
||||
Path to the disk image file. See the rascsi(1) man page for al‐
|
||||
lowable file types.
|
||||
|
||||
-t TYPE
|
||||
Specifies the type of disk. If this disagrees with the file ex‐
|
||||
tension of the specified image, the TYPE argument is ignored.
|
||||
Specifies the type of disk. If this disagrees with the file ex‐
|
||||
tension of the specified image, the TYPE argument is ignored.
|
||||
Available drive types are:
|
||||
hd: Hard disk (SCSI or SASI)
|
||||
mo: Magneto-Optical disk)
|
||||
cd: CD-ROM
|
||||
bridge: Bridge device (This is only applicable to the Sharp
|
||||
bridge: Bridge device (This is only applicable to the Sharp
|
||||
X68000)
|
||||
|
||||
-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
|
||||
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
|
||||
|
@ -247,7 +247,7 @@ BOOL CTapDriver::Init()
|
||||
}
|
||||
#endif // __NetBSD__
|
||||
|
||||
BOOL CTapDriver::OpenDump(const Filepath& path) {
|
||||
const char *CTapDriver::OpenDump(const Filepath& path) {
|
||||
if (m_pcap == NULL) {
|
||||
m_pcap = pcap_open_dead(DLT_EN10MB, 65535);
|
||||
}
|
||||
@ -257,10 +257,10 @@ BOOL CTapDriver::OpenDump(const Filepath& path) {
|
||||
m_pcap_dumper = pcap_dump_open(m_pcap, path.GetPath());
|
||||
if (m_pcap_dumper == NULL) {
|
||||
LOGERROR("Error: can't open pcap file: %s", pcap_geterr(m_pcap));
|
||||
return FALSE;
|
||||
return "Can't open pcap file";
|
||||
}
|
||||
LOGTRACE("%s Opened %s for dumping", __PRETTY_FUNCTION__, path.GetPath());
|
||||
return TRUE;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
@ -34,7 +34,7 @@ public:
|
||||
// Basic Functionality
|
||||
CTapDriver(); // Constructor
|
||||
BOOL Init(); // Initilization
|
||||
BOOL OpenDump(const Filepath& path);
|
||||
const char *OpenDump(const Filepath& path);
|
||||
// Capture packets
|
||||
void Cleanup(); // Cleanup
|
||||
void GetMacAddr(BYTE *mac); // Get Mac Address
|
||||
|
@ -711,12 +711,16 @@ Disk::Disk(std::string id)
|
||||
|
||||
// Work initialization
|
||||
disk.ready = FALSE;
|
||||
disk.writep = FALSE;
|
||||
disk.readonly = FALSE;
|
||||
disk.removable = FALSE;
|
||||
disk.lock = FALSE;
|
||||
disk.attn = FALSE;
|
||||
disk.reset = FALSE;
|
||||
disk.protectable = false;
|
||||
disk.writep = false;
|
||||
disk.readonly = false;
|
||||
disk.removable = false;
|
||||
disk.removed = false;
|
||||
disk.lockable = false;
|
||||
disk.locked = false;
|
||||
disk.supports_file = true;
|
||||
disk.attn = false;
|
||||
disk.reset = false;
|
||||
disk.size = 0;
|
||||
disk.blocks = 0;
|
||||
disk.lun = 0;
|
||||
@ -758,9 +762,9 @@ Disk::~Disk()
|
||||
void Disk::Reset()
|
||||
{
|
||||
// no lock, no attention, reset
|
||||
disk.lock = FALSE;
|
||||
disk.attn = FALSE;
|
||||
disk.reset = TRUE;
|
||||
disk.locked = false;
|
||||
disk.attn = false;
|
||||
disk.reset = false;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
@ -839,9 +843,10 @@ bool Disk::IsNuvolink() const
|
||||
//
|
||||
// Open
|
||||
// * Call as a post-process after successful opening in a derived class
|
||||
// TODO Use exceptions for error handling instead of returning a string
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
BOOL Disk::Open(const Filepath& path, BOOL /*attn*/)
|
||||
const char *Disk::Open(const Filepath& path, BOOL /*attn*/)
|
||||
{
|
||||
ASSERT((disk.size >= 8) && (disk.size <= 11));
|
||||
ASSERT(disk.blocks > 0);
|
||||
@ -851,54 +856,55 @@ BOOL Disk::Open(const Filepath& path, BOOL /*attn*/)
|
||||
|
||||
// Cache initialization
|
||||
ASSERT(!disk.dcache);
|
||||
disk.dcache =
|
||||
new DiskCache(path, disk.size, disk.blocks, disk.imgoffset);
|
||||
disk.dcache = new DiskCache(path, disk.size, disk.blocks, disk.imgoffset);
|
||||
|
||||
// Can read/write open
|
||||
Fileio fio;
|
||||
if (fio.Open(path, Fileio::ReadWrite)) {
|
||||
// Write permission, not read only
|
||||
disk.writep = FALSE;
|
||||
disk.readonly = FALSE;
|
||||
disk.writep = false;
|
||||
disk.readonly = false;
|
||||
fio.Close();
|
||||
} else {
|
||||
// Write protected, read only
|
||||
disk.writep = TRUE;
|
||||
disk.readonly = TRUE;
|
||||
disk.writep = true;
|
||||
disk.readonly = true;
|
||||
}
|
||||
|
||||
// Not locked
|
||||
disk.lock = FALSE;
|
||||
// Not locked, not removed
|
||||
disk.locked = false;
|
||||
disk.removed = false;
|
||||
|
||||
// Save path
|
||||
diskpath = path;
|
||||
|
||||
// Success
|
||||
return TRUE;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Eject
|
||||
// TODO This implemenation appears to be wrong: If a device is locked there
|
||||
// is no way to eject the medium without unlocking. In other words, there is
|
||||
// no "force" mode.
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
void Disk::Eject(BOOL force)
|
||||
bool Disk::Eject(bool force)
|
||||
{
|
||||
// Can only be ejected if it is removable
|
||||
if (!disk.removable) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
// If you're not ready, you don't need to eject
|
||||
if (!disk.ready) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Must be unlocked if there is no force flag
|
||||
if (!force) {
|
||||
if (disk.lock) {
|
||||
return;
|
||||
}
|
||||
if (!force && disk.locked) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Remove disk cache
|
||||
@ -908,9 +914,12 @@ void Disk::Eject(BOOL force)
|
||||
|
||||
// Not ready, no attention
|
||||
disk.ready = FALSE;
|
||||
disk.writep = FALSE;
|
||||
disk.readonly = FALSE;
|
||||
disk.attn = FALSE;
|
||||
disk.writep = false;
|
||||
disk.readonly = false;
|
||||
disk.removed = true;
|
||||
disk.attn = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
@ -918,21 +927,22 @@ void Disk::Eject(BOOL force)
|
||||
// Write Protected
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
void Disk::WriteP(BOOL writep)
|
||||
bool Disk::WriteP(bool writep)
|
||||
{
|
||||
// be ready
|
||||
if (!disk.ready) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read Only, protect only
|
||||
if (disk.readonly) {
|
||||
ASSERT(disk.writep);
|
||||
return;
|
||||
if (disk.readonly && !writep) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Write protect flag setting
|
||||
disk.writep = writep;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
@ -971,7 +981,7 @@ BOOL Disk::CheckReady()
|
||||
// Not ready if reset
|
||||
if (disk.reset) {
|
||||
disk.code = DISK_DEVRESET;
|
||||
disk.reset = FALSE;
|
||||
disk.reset = false;
|
||||
LOGTRACE("%s Disk in reset", __PRETTY_FUNCTION__);
|
||||
return FALSE;
|
||||
}
|
||||
@ -979,7 +989,7 @@ BOOL Disk::CheckReady()
|
||||
// Not ready if it needs attention
|
||||
if (disk.attn) {
|
||||
disk.code = DISK_ATTENTION;
|
||||
disk.attn = FALSE;
|
||||
disk.attn = false;
|
||||
LOGTRACE("%s Disk in needs attention", __PRETTY_FUNCTION__);
|
||||
return FALSE;
|
||||
}
|
||||
@ -1896,14 +1906,14 @@ BOOL Disk::StartStop(const DWORD *cdb)
|
||||
|
||||
// Look at the eject bit and eject if necessary
|
||||
if (cdb[4] & 0x02) {
|
||||
if (disk.lock) {
|
||||
if (disk.locked) {
|
||||
// Cannot be ejected because it is locked
|
||||
disk.code = DISK_PREVENT;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Eject
|
||||
Eject(FALSE);
|
||||
Eject(false);
|
||||
}
|
||||
|
||||
// OK
|
||||
@ -1955,9 +1965,9 @@ BOOL Disk::Removal(const DWORD *cdb)
|
||||
|
||||
// Set Lock flag
|
||||
if (cdb[4] & 0x01) {
|
||||
disk.lock = TRUE;
|
||||
disk.locked = true;
|
||||
} else {
|
||||
disk.lock = FALSE;
|
||||
disk.locked = false;
|
||||
}
|
||||
|
||||
// REMOVAL Success
|
||||
|
@ -167,12 +167,17 @@ public:
|
||||
typedef struct {
|
||||
std::string id; // Media ID
|
||||
BOOL ready; // Valid Disk
|
||||
BOOL writep; // Write protected
|
||||
BOOL readonly; // Read only
|
||||
BOOL removable; // Removable
|
||||
BOOL lock; // Locked
|
||||
BOOL attn; // Attention
|
||||
BOOL reset; // Reset
|
||||
bool readonly; // Read only
|
||||
bool protectable;
|
||||
bool writep; // Write protected
|
||||
bool removable; // Removable
|
||||
bool removed;
|
||||
bool lockable;
|
||||
bool locked; // Locked
|
||||
// TODO Non-disk devices must not inherit from Disk class
|
||||
bool supports_file;
|
||||
bool attn; // Attention
|
||||
bool reset; // Reset
|
||||
int size; // Sector Size
|
||||
DWORD blocks; // Total number of sectors
|
||||
DWORD lun; // LUN
|
||||
@ -198,15 +203,19 @@ public:
|
||||
bool IsNuvolink() const;
|
||||
|
||||
// Media Operations
|
||||
virtual BOOL Open(const Filepath& path, BOOL attn = TRUE); // Open
|
||||
virtual const char *Open(const Filepath& path, BOOL attn = TRUE); // Open
|
||||
void GetPath(Filepath& path) const; // Get the path
|
||||
void Eject(BOOL force); // Eject
|
||||
bool Eject(bool); // Eject
|
||||
bool IsReady() const { return disk.ready; } // Ready check
|
||||
void WriteP(BOOL flag); // Set Write Protect flag
|
||||
bool IsProtectable() const { return disk.protectable; }
|
||||
bool WriteP(bool); // Set Write Protect flag
|
||||
bool IsWriteP() const { return disk.writep; } // Get write protect flag
|
||||
bool IsReadOnly() const { return disk.readonly; } // Get read only flag
|
||||
bool IsReadOnly() const { return disk.readonly; } // Get read only flag
|
||||
bool IsRemovable() const { return disk.removable; } // Get is removable flag
|
||||
bool IsLocked() const { return disk.lock; } // Get locked status
|
||||
bool IsRemoved() const { return disk.removed; }
|
||||
bool IsLockable() const { return disk.lockable; }
|
||||
bool IsLocked() const { return disk.locked; } // Get locked status
|
||||
bool SupportsFile() const { return disk.supports_file; }
|
||||
bool IsAttn() const { return disk.attn; } // Get attention flag
|
||||
bool Flush(); // Flush the cache
|
||||
|
||||
|
@ -31,6 +31,7 @@
|
||||
//---------------------------------------------------------------------------
|
||||
SASIHD::SASIHD() : Disk("SAHD")
|
||||
{
|
||||
disk.protectable = true;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
@ -41,7 +42,7 @@ SASIHD::SASIHD() : Disk("SAHD")
|
||||
void SASIHD::Reset()
|
||||
{
|
||||
// Unlock, clear attention
|
||||
disk.lock = FALSE;
|
||||
disk.locked = FALSE;
|
||||
disk.attn = FALSE;
|
||||
|
||||
// Reset, clear the code
|
||||
@ -54,14 +55,14 @@ void SASIHD::Reset()
|
||||
// Open
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
BOOL SASIHD::Open(const Filepath& path, BOOL /*attn*/)
|
||||
const char *SASIHD::Open(const Filepath& path, BOOL /*attn*/)
|
||||
{
|
||||
ASSERT(!disk.ready);
|
||||
|
||||
// Open as read-only
|
||||
Fileio fio;
|
||||
if (!fio.Open(path, Fileio::ReadOnly)) {
|
||||
return FALSE;
|
||||
return "Can't open hard disk file read-only";
|
||||
}
|
||||
|
||||
// Get file size
|
||||
@ -84,7 +85,7 @@ BOOL SASIHD::Open(const Filepath& path, BOOL /*attn*/)
|
||||
#if defined(REMOVE_FIXED_SASIHD_SIZE)
|
||||
// Must be in 256-byte units
|
||||
if (size & 0xff) {
|
||||
return FALSE;
|
||||
return "File size must be a multiple of 512";
|
||||
}
|
||||
|
||||
// 10MB or more
|
||||
@ -94,7 +95,7 @@ BOOL SASIHD::Open(const Filepath& path, BOOL /*attn*/)
|
||||
|
||||
// Limit to about 512MB
|
||||
if (size > 512 * 1024 * 1024) {
|
||||
return FALSE;
|
||||
return "File size must not exceed 512 MB";
|
||||
}
|
||||
#else
|
||||
// 10MB, 20MB, 40MBのみ
|
||||
@ -113,7 +114,7 @@ BOOL SASIHD::Open(const Filepath& path, BOOL /*attn*/)
|
||||
|
||||
// Other (Not supported )
|
||||
default:
|
||||
return FALSE;
|
||||
return "Unsupported file size";
|
||||
}
|
||||
#endif // REMOVE_FIXED_SASIHD_SIZE
|
||||
|
||||
|
@ -30,8 +30,8 @@ public:
|
||||
// Basic Functions
|
||||
SASIHD(); // Constructor
|
||||
void Reset(); // Reset
|
||||
BOOL Open(const Filepath& path, BOOL attn = TRUE); // Open
|
||||
const char *Open(const Filepath& path, BOOL attn = TRUE); // Open
|
||||
|
||||
// commands
|
||||
int RequestSense(const DWORD *cdb, BYTE *buf); // REQUEST SENSE command
|
||||
};
|
||||
};
|
||||
|
@ -49,6 +49,8 @@ const BYTE SCSIDaynaPort::m_apple_talk_addr[6] = { 0x09, 0x00, 0x07, 0xff, 0xff,
|
||||
//---------------------------------------------------------------------------
|
||||
SCSIDaynaPort::SCSIDaynaPort() : Disk("SCDP")
|
||||
{
|
||||
disk.supports_file = false;
|
||||
|
||||
#ifdef __linux__
|
||||
// TAP Driver Generation
|
||||
m_tap = new CTapDriver();
|
||||
@ -102,7 +104,7 @@ SCSIDaynaPort::~SCSIDaynaPort()
|
||||
}
|
||||
}
|
||||
|
||||
BOOL SCSIDaynaPort::Open(const Filepath& path, BOOL attn)
|
||||
const char *SCSIDaynaPort::Open(const Filepath& path, BOOL attn)
|
||||
{
|
||||
LOGTRACE("SCSIDaynaPort Open");
|
||||
return m_tap->OpenDump(path);
|
||||
|
@ -46,7 +46,7 @@ public:
|
||||
// Constructor
|
||||
virtual ~SCSIDaynaPort();
|
||||
// Destructor
|
||||
BOOL Open(const Filepath& path, BOOL attn = TRUE);
|
||||
const char *Open(const Filepath& path, BOOL attn = TRUE);
|
||||
// Capture packets
|
||||
|
||||
// commands
|
||||
|
@ -34,6 +34,8 @@
|
||||
//---------------------------------------------------------------------------
|
||||
SCSIBR::SCSIBR() : Disk("SCBR")
|
||||
{
|
||||
disk.supports_file = false;
|
||||
|
||||
fsoptlen = 0;
|
||||
fsoutlen = 0;
|
||||
fsresult = 0;
|
||||
|
@ -251,9 +251,9 @@ CDDABuf::~CDDABuf()
|
||||
//---------------------------------------------------------------------------
|
||||
SCSICD::SCSICD() : Disk("SCCD")
|
||||
{
|
||||
// removable, write protected
|
||||
disk.removable = TRUE;
|
||||
disk.writep = TRUE;
|
||||
disk.removable = true;
|
||||
disk.lockable = true;
|
||||
disk.writep = true;
|
||||
|
||||
// NOT in raw format
|
||||
rawfile = FALSE;
|
||||
@ -286,7 +286,7 @@ SCSICD::~SCSICD()
|
||||
// Open
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
BOOL SCSICD::Open(const Filepath& path, BOOL attn)
|
||||
const char *SCSICD::Open(const Filepath& path, BOOL attn)
|
||||
{
|
||||
Fileio fio;
|
||||
off64_t size;
|
||||
@ -301,7 +301,7 @@ BOOL SCSICD::Open(const Filepath& path, BOOL attn)
|
||||
|
||||
// Open as read-only
|
||||
if (!fio.Open(path, Fileio::ReadOnly)) {
|
||||
return FALSE;
|
||||
return "Can't open CD-ROM file read-only";
|
||||
}
|
||||
|
||||
// Close and transfer for physical CD access
|
||||
@ -311,14 +311,14 @@ BOOL SCSICD::Open(const Filepath& path, BOOL attn)
|
||||
|
||||
// Open physical CD
|
||||
if (!OpenPhysical(path)) {
|
||||
return FALSE;
|
||||
return "Can't open physical CD";
|
||||
}
|
||||
} else {
|
||||
// Get file size
|
||||
size = fio.GetFileSize();
|
||||
if (size <= 4) {
|
||||
fio.Close();
|
||||
return FALSE;
|
||||
return "Invalid file size";
|
||||
}
|
||||
|
||||
// Judge whether it is a CUE sheet or an ISO file
|
||||
@ -330,12 +330,12 @@ BOOL SCSICD::Open(const Filepath& path, BOOL attn)
|
||||
if (xstrncasecmp(file, _T("FILE"), 4) == 0) {
|
||||
// Open as CUE
|
||||
if (!OpenCue(path)) {
|
||||
return FALSE;
|
||||
return "Can't open as CUE";
|
||||
}
|
||||
} else {
|
||||
// Open as ISO
|
||||
if (!OpenIso(path)) {
|
||||
return FALSE;
|
||||
return "Can't open as ISO";
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -359,7 +359,7 @@ BOOL SCSICD::Open(const Filepath& path, BOOL attn)
|
||||
disk.attn = TRUE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
@ -114,7 +114,7 @@ public:
|
||||
// Basic Functions
|
||||
SCSICD(); // Constructor
|
||||
virtual ~SCSICD(); // Destructor
|
||||
BOOL Open(const Filepath& path, BOOL attn = TRUE); // Open
|
||||
const char *Open(const Filepath& path, BOOL attn = TRUE); // Open
|
||||
|
||||
// commands
|
||||
int Inquiry(const DWORD *cdb, BYTE *buf, DWORD major, DWORD minor); // INQUIRY command
|
||||
|
@ -30,6 +30,7 @@
|
||||
//---------------------------------------------------------------------------
|
||||
SCSIHD::SCSIHD() : Disk("SCHD")
|
||||
{
|
||||
disk.protectable = true;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
@ -40,7 +41,7 @@ SCSIHD::SCSIHD() : Disk("SCHD")
|
||||
void SCSIHD::Reset()
|
||||
{
|
||||
// Unlock and release attention
|
||||
disk.lock = FALSE;
|
||||
disk.locked = FALSE;
|
||||
disk.attn = FALSE;
|
||||
|
||||
// No reset, clear code
|
||||
@ -53,14 +54,14 @@ void SCSIHD::Reset()
|
||||
// Open
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
BOOL SCSIHD::Open(const Filepath& path, BOOL /*attn*/)
|
||||
const char *SCSIHD::Open(const Filepath& path, BOOL /*attn*/)
|
||||
{
|
||||
ASSERT(!disk.ready);
|
||||
|
||||
// read open required
|
||||
Fileio fio;
|
||||
if (!fio.Open(path, Fileio::ReadOnly)) {
|
||||
return FALSE;
|
||||
return "Can't open hard disk file read-only";
|
||||
}
|
||||
|
||||
// Get file size
|
||||
@ -69,13 +70,14 @@ BOOL SCSIHD::Open(const Filepath& path, BOOL /*attn*/)
|
||||
|
||||
// Must be 512 bytes
|
||||
if (size & 0x1ff) {
|
||||
return FALSE;
|
||||
return "File size must be a multiple of 512 bytes";
|
||||
}
|
||||
|
||||
// 2TB according to xm6i
|
||||
// There is a similar one in wxw/wxw_cfg.cpp
|
||||
// Bigger files/drives require READ/WRITE(16) to be implemented
|
||||
if (size > 2LL * 1024 * 1024 * 1024 * 1024) {
|
||||
return FALSE;
|
||||
return "File size must not exceed 2 TB";
|
||||
}
|
||||
|
||||
// sector size and number of blocks
|
||||
|
@ -30,9 +30,9 @@ public:
|
||||
// Basic Functions
|
||||
SCSIHD(); // Constructor
|
||||
void Reset(); // Reset
|
||||
BOOL Open(const Filepath& path, BOOL attn = TRUE); // Open
|
||||
const char *Open(const Filepath& path, BOOL attn = TRUE); // Open
|
||||
|
||||
// commands
|
||||
int Inquiry(const DWORD *cdb, BYTE *buf, DWORD major, DWORD minor); // INQUIRY command
|
||||
BOOL ModeSelect(const DWORD *cdb, const BYTE *buf, int length); // MODE SELECT(6) command
|
||||
};
|
||||
};
|
||||
|
@ -65,7 +65,7 @@ static inline DWORD getDwordLE(const BYTE *b)
|
||||
// Open
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
BOOL SCSIHD_NEC::Open(const Filepath& path, BOOL /*attn*/)
|
||||
const char *SCSIHD_NEC::Open(const Filepath& path, BOOL /*attn*/)
|
||||
{
|
||||
Fileio fio;
|
||||
off64_t size;
|
||||
@ -76,7 +76,7 @@ BOOL SCSIHD_NEC::Open(const Filepath& path, BOOL /*attn*/)
|
||||
|
||||
// Open as read-only
|
||||
if (!fio.Open(path, Fileio::ReadOnly)) {
|
||||
return FALSE;
|
||||
return "Can't open hard disk file read-only";
|
||||
}
|
||||
|
||||
// Get file size
|
||||
@ -86,14 +86,14 @@ BOOL SCSIHD_NEC::Open(const Filepath& path, BOOL /*attn*/)
|
||||
if (size >= (off64_t)sizeof(hdr)) {
|
||||
if (!fio.Read(hdr, sizeof(hdr))) {
|
||||
fio.Close();
|
||||
return FALSE;
|
||||
return "Can't read hard disk file header";
|
||||
}
|
||||
}
|
||||
fio.Close();
|
||||
|
||||
// Must be in 512 byte units
|
||||
if (size & 0x1ff) {
|
||||
return FALSE;
|
||||
return "File size must be a multiple of 512";
|
||||
}
|
||||
|
||||
// 10MB or more
|
||||
@ -102,7 +102,7 @@ BOOL SCSIHD_NEC::Open(const Filepath& path, BOOL /*attn*/)
|
||||
}
|
||||
// 2TB according to xm6i
|
||||
if (size > 2LL * 1024 * 1024 * 1024 * 1024) {
|
||||
return FALSE;
|
||||
return "File size must not exceed 2 TB";
|
||||
}
|
||||
|
||||
// Determine parameters by extension
|
||||
@ -136,12 +136,12 @@ BOOL SCSIHD_NEC::Open(const Filepath& path, BOOL /*attn*/)
|
||||
|
||||
// Supports 256 or 512 sector sizes
|
||||
if (sectorsize != 256 && sectorsize != 512) {
|
||||
return FALSE;
|
||||
return "Sector size must be 256 or 512";
|
||||
}
|
||||
|
||||
// Image size consistency check
|
||||
if (imgoffset + imgsize > size || (imgsize % sectorsize != 0)) {
|
||||
return FALSE;
|
||||
return "Image size consistency check failed";
|
||||
}
|
||||
|
||||
// Sector size
|
||||
@ -150,7 +150,7 @@ BOOL SCSIHD_NEC::Open(const Filepath& path, BOOL /*attn*/)
|
||||
break;
|
||||
}
|
||||
if (disk.size <= 0 || disk.size > 16) {
|
||||
return FALSE;
|
||||
return "Invalid disk size";
|
||||
}
|
||||
|
||||
// Number of blocks
|
||||
|
@ -27,7 +27,7 @@ class SCSIHD_NEC : public SCSIHD
|
||||
public:
|
||||
// Basic Functions
|
||||
SCSIHD_NEC(); // Constructor
|
||||
BOOL Open(const Filepath& path, BOOL attn = TRUE); // Open
|
||||
const char *Open(const Filepath& path, BOOL attn = TRUE); // Open
|
||||
|
||||
// commands
|
||||
int Inquiry(const DWORD *cdb, BYTE *buf, DWORD major, DWORD minor); // INQUIRY command
|
||||
@ -44,4 +44,4 @@ private:
|
||||
int sectorsize; // Sector size
|
||||
off64_t imgoffset; // Image offset
|
||||
off64_t imgsize; // Image size
|
||||
};
|
||||
};
|
||||
|
@ -31,8 +31,9 @@
|
||||
//---------------------------------------------------------------------------
|
||||
SCSIMO::SCSIMO() : Disk("SCMO")
|
||||
{
|
||||
// Set as removable
|
||||
disk.removable = TRUE;
|
||||
disk.removable = true;
|
||||
disk.lockable = true;
|
||||
disk.protectable = true;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
@ -40,14 +41,14 @@ SCSIMO::SCSIMO() : Disk("SCMO")
|
||||
// Open
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
BOOL SCSIMO::Open(const Filepath& path, BOOL attn)
|
||||
const char *SCSIMO::Open(const Filepath& path, BOOL attn)
|
||||
{
|
||||
ASSERT(!disk.ready);
|
||||
|
||||
// Open as read-only
|
||||
Fileio fio;
|
||||
if (!fio.Open(path, Fileio::ReadOnly)) {
|
||||
return FALSE;
|
||||
return "Can't open MO file read-only";
|
||||
}
|
||||
|
||||
// Get file size
|
||||
@ -81,7 +82,7 @@ BOOL SCSIMO::Open(const Filepath& path, BOOL attn)
|
||||
|
||||
// Other (this is an error)
|
||||
default:
|
||||
return FALSE;
|
||||
return "Invalid MO size";
|
||||
}
|
||||
|
||||
// Call the base class
|
||||
@ -92,7 +93,7 @@ BOOL SCSIMO::Open(const Filepath& path, BOOL attn)
|
||||
disk.attn = TRUE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
@ -29,7 +29,7 @@ class SCSIMO : public Disk
|
||||
public:
|
||||
// Basic Functions
|
||||
SCSIMO(); // Constructor
|
||||
BOOL Open(const Filepath& path, BOOL attn = TRUE); // Open
|
||||
const char *Open(const Filepath& path, BOOL attn = TRUE); // Open
|
||||
|
||||
// commands
|
||||
int Inquiry(const DWORD *cdb, BYTE *buf, DWORD major, DWORD minor); // INQUIRY command
|
||||
|
@ -12,6 +12,8 @@
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include "os.h"
|
||||
#include "xm6.h"
|
||||
#include "gpiobus.h"
|
||||
|
@ -35,19 +35,16 @@
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <assert.h>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstddef>
|
||||
#include <cstdarg>
|
||||
#include <cstring>
|
||||
#include <csignal>
|
||||
#include <cassert>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <time.h>
|
||||
#include <utime.h>
|
||||
#include <fcntl.h>
|
||||
#include <signal.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <sched.h>
|
||||
#include <pthread.h>
|
||||
#include <iconv.h>
|
||||
@ -58,14 +55,13 @@
|
||||
|
||||
#include <poll.h>
|
||||
#include <dirent.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/epoll.h>
|
||||
#include <netinet/in.h>
|
||||
#include <linux/gpio.h>
|
||||
|
||||
#if defined(__linux__)
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/if.h>
|
||||
#include <linux/if_tun.h>
|
||||
#elif defined(__NetBSD__)
|
||||
|
@ -176,18 +176,24 @@ BOOL InitService(int port)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool InitBusAndDisks() {
|
||||
bool InitBus()
|
||||
{
|
||||
// GPIOBUS creation
|
||||
bus = new GPIOBUS();
|
||||
|
||||
// GPIO Initialization
|
||||
if (!bus->Init()) {
|
||||
return FALSE;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Bus Reset
|
||||
bus->Reset();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void InitDisks()
|
||||
{
|
||||
// Controller initialization
|
||||
for (int i = 0; i < CtrlMax; i++) {
|
||||
ctrl[i] = NULL;
|
||||
@ -197,8 +203,6 @@ bool InitBusAndDisks() {
|
||||
for (int i = 0; i < CtrlMax; i++) {
|
||||
disk[i] = NULL;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
@ -225,10 +229,12 @@ void Cleanup()
|
||||
}
|
||||
|
||||
// Cleanup the Bus
|
||||
bus->Cleanup();
|
||||
if (bus) {
|
||||
bus->Cleanup();
|
||||
|
||||
// Discard the GPIOBUS object
|
||||
delete bus;
|
||||
// Discard the GPIOBUS object
|
||||
delete bus;
|
||||
}
|
||||
|
||||
// Close the monitor socket
|
||||
if (monsocket >= 0) {
|
||||
@ -278,7 +284,7 @@ PbDevices GetDevices() {
|
||||
device->set_un(i % UnitNum);
|
||||
|
||||
// ID,UNIT,Type,Device Status
|
||||
device->set_type(pUnit->GetID());
|
||||
device->set_type(MapIdToType(pUnit->GetID(), pUnit->IsSASI()));
|
||||
|
||||
// mount status output
|
||||
if (pUnit->IsBridge()) {
|
||||
@ -288,10 +294,16 @@ PbDevices GetDevices() {
|
||||
} else {
|
||||
Filepath filepath;
|
||||
pUnit->GetPath(filepath);
|
||||
device->set_file(pUnit->IsRemovable() && !pUnit->IsReady() ? "NO MEDIA" : filepath.GetPath());
|
||||
device->set_file(pUnit->IsRemovable() && !pUnit->IsReady() ? "" : filepath.GetPath());
|
||||
}
|
||||
|
||||
device->set_protectable(pUnit->IsProtectable());
|
||||
device->set_protected_(pUnit->IsProtectable() && pUnit->IsWriteP());
|
||||
device->set_removable(pUnit->IsRemovable());
|
||||
device->set_removed(pUnit->IsRemoved());
|
||||
device->set_lockable(pUnit->IsLockable());
|
||||
device->set_locked(pUnit->IsLocked());
|
||||
device->set_supports_file(pUnit->SupportsFile());
|
||||
|
||||
// Write protection status
|
||||
if (pUnit->IsRemovable() && pUnit->IsReady() && pUnit->IsWriteP()) {
|
||||
@ -419,9 +431,11 @@ bool MapController(Disk **map)
|
||||
return status;
|
||||
}
|
||||
|
||||
bool ReturnStatus(int fd, bool status = true, const string msg = "") {
|
||||
bool ReturnStatus(int fd, bool status = true, const string msg = "")
|
||||
{
|
||||
if (fd == -1) {
|
||||
if (msg.length()) {
|
||||
FPRT(stderr, "Error: ");
|
||||
FPRT(stderr, msg.c_str());
|
||||
FPRT(stderr, "\n");
|
||||
}
|
||||
@ -429,14 +443,20 @@ bool ReturnStatus(int fd, bool status = true, const string msg = "") {
|
||||
else {
|
||||
PbResult result;
|
||||
result.set_status(status);
|
||||
result.set_msg(msg + "\n");
|
||||
result.set_msg(msg);
|
||||
SerializeMessage(fd, result);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
bool SetLogLevel(const string& log_level) {
|
||||
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") {
|
||||
set_level(level::trace);
|
||||
}
|
||||
@ -502,12 +522,12 @@ void GetAvailableImages(PbServerInfo& serverInfo)
|
||||
//---------------------------------------------------------------------------
|
||||
bool ProcessCmd(int fd, const PbCommand &command)
|
||||
{
|
||||
Disk *map[CtrlMax * UnitNum];
|
||||
Filepath filepath;
|
||||
Disk *pUnit;
|
||||
char type_str[5];
|
||||
ostringstream error;
|
||||
const char *result;
|
||||
|
||||
int id = command.id();
|
||||
int id = command.id();
|
||||
int un = command.un();
|
||||
PbOperation cmd = command.cmd();
|
||||
PbDeviceType type = command.type();
|
||||
@ -518,37 +538,53 @@ bool ProcessCmd(int fd, const PbCommand &command)
|
||||
LOGINFO("%s", s.str().c_str());
|
||||
|
||||
// Copy the Unit List
|
||||
Disk *map[CtrlMax * UnitNum];
|
||||
memcpy(map, disk, sizeof(disk));
|
||||
|
||||
// Check the Controller Number
|
||||
if (id < 0 || id >= CtrlMax) {
|
||||
return ReturnStatus(fd, false, "Error : Invalid ID");
|
||||
error << "Invalid ID " << id << " (0-" << CtrlMax - 1 << ")";
|
||||
return ReturnStatus(fd, false, error);
|
||||
}
|
||||
|
||||
// Check the Unit Number
|
||||
if (un < 0 || un >= UnitNum) {
|
||||
return ReturnStatus(fd, false, "Error : Invalid unit number");
|
||||
error << "Invalid unit " << un << " (0-" << UnitNum - 1 << ")";
|
||||
return ReturnStatus(fd, false, error);
|
||||
}
|
||||
|
||||
string ext;
|
||||
int len = params.length();
|
||||
if (len > 4 && params[len - 4] == '.') {
|
||||
ext = params.substr(len - 3);
|
||||
}
|
||||
|
||||
// Connect Command
|
||||
if (cmd == ATTACH) {
|
||||
string ext;
|
||||
if (map[id]) {
|
||||
error << "Duplicate ID " << id;
|
||||
return ReturnStatus(fd, false, error);
|
||||
}
|
||||
|
||||
// Distinguish between SASI and SCSI
|
||||
if (type == SASI_HD) {
|
||||
// Check the extension
|
||||
int len = params.length();
|
||||
if (len < 5 || params[len - 4] != '.') {
|
||||
return ReturnStatus(fd, false);
|
||||
// If no type was specified try to derive the file type from the extension
|
||||
if (type == UNDEFINED) {
|
||||
if (ext == "hdf") {
|
||||
type = SASI_HD;
|
||||
}
|
||||
|
||||
// If the extension is not SASI type, replace with SCSI
|
||||
ext = params.substr(len - 3);
|
||||
if (ext != "hdf") {
|
||||
else if (ext == "hds" || ext == "hdn" || ext == "hdi" || ext == "nhd" || ext == "hda") {
|
||||
type = SCSI_HD;
|
||||
} else if (ext == "mos") {
|
||||
type = MO;
|
||||
} else if (ext == "iso") {
|
||||
type = CD;
|
||||
}
|
||||
}
|
||||
|
||||
// File check (type is HD, for CD and MO the medium (=file) may be inserted later)
|
||||
if ((type == SASI_HD || type == SCSI_HD) && params.empty()) {
|
||||
return ReturnStatus(fd, false, "Missing filename");
|
||||
}
|
||||
|
||||
// Create a new drive, based upon type
|
||||
pUnit = NULL;
|
||||
switch (type) {
|
||||
@ -580,9 +616,8 @@ bool ProcessCmd(int fd, const PbCommand &command)
|
||||
pUnit = new SCSIDaynaPort();
|
||||
break;
|
||||
default:
|
||||
ostringstream error;
|
||||
error << "rasctl sent a command for an invalid drive type: " << PbDeviceType_Name(type);
|
||||
return ReturnStatus(fd, false, error.str());
|
||||
error << "Received a command for an invalid drive type: " << PbDeviceType_Name(type);
|
||||
return ReturnStatus(fd, false, error);
|
||||
}
|
||||
|
||||
// drive checks files
|
||||
@ -594,25 +629,24 @@ bool ProcessCmd(int fd, const PbCommand &command)
|
||||
filepath.SetPath(file.c_str());
|
||||
|
||||
// Open the file path
|
||||
if (!pUnit->Open(filepath)) {
|
||||
result = pUnit->Open(filepath);
|
||||
if (result) {
|
||||
// If the file does not exist search for it in the default image folder
|
||||
string default_file = default_image_folder + "/" + file;
|
||||
filepath.SetPath(default_file.c_str());
|
||||
if (!pUnit->Open(filepath)) {
|
||||
result = pUnit->Open(filepath);
|
||||
if (result) {
|
||||
delete pUnit;
|
||||
|
||||
LOGWARN("rasctl tried to open an invalid file %s", file.c_str());
|
||||
|
||||
ostringstream error;
|
||||
error << "File open error [" << file << "]";
|
||||
return ReturnStatus(fd, false, error.str());
|
||||
error << "Tried to open an invalid file '" << file << "': " << result;
|
||||
LOGWARN("%s", error.str().c_str());
|
||||
return ReturnStatus(fd, false, error);
|
||||
}
|
||||
}
|
||||
|
||||
if (files_in_use.find(filepath.GetPath()) != files_in_use.end()) {
|
||||
ostringstream error;
|
||||
error << "Image file '" << file << "' is already in use";
|
||||
return ReturnStatus(fd, false, error.str());
|
||||
return ReturnStatus(fd, false, error);
|
||||
}
|
||||
|
||||
files_in_use.insert(filepath.GetPath());
|
||||
@ -627,30 +661,30 @@ bool ProcessCmd(int fd, const PbCommand &command)
|
||||
// Re-map the controller
|
||||
bool status = MapController(map);
|
||||
if (status) {
|
||||
LOGINFO("rasctl added new %s device. ID: %d UN: %d", pUnit->GetID().c_str(), id, un);
|
||||
LOGINFO("Added new %s device. ID: %d UN: %d", pUnit->GetID().c_str(), id, un);
|
||||
}
|
||||
|
||||
return ReturnStatus(fd, status, status ? "" : "Error : SASI and SCSI can't be mixed\n");
|
||||
return ReturnStatus(fd, status, status ? "" : "SASI and SCSI can't be mixed");
|
||||
}
|
||||
|
||||
// Does the controller exist?
|
||||
if (ctrl[id] == NULL) {
|
||||
LOGWARN("rasctl sent a command for invalid controller %d", id);
|
||||
LOGWARN("Received a command for invalid controller %d", id);
|
||||
|
||||
return ReturnStatus(fd, false, "Error : No such device");
|
||||
return ReturnStatus(fd, false, "No such device");
|
||||
}
|
||||
|
||||
// Does the unit exist?
|
||||
pUnit = disk[id * UnitNum + un];
|
||||
if (pUnit == NULL) {
|
||||
LOGWARN("rasctl sent a command for invalid unit ID %d UN %d", id, un);
|
||||
LOGWARN("Received a command for invalid unit ID %d UN %d", id, un);
|
||||
|
||||
return ReturnStatus(fd, false, "Error : No such device");
|
||||
return ReturnStatus(fd, false, "No such device");
|
||||
}
|
||||
|
||||
// Disconnect Command
|
||||
if (cmd == DETACH) {
|
||||
LOGINFO("rasctl command disconnect %s at ID: %d UN: %d", pUnit->GetID().c_str(), id, un);
|
||||
LOGINFO("Disconnect %s at ID: %d UN: %d", pUnit->GetID().c_str(), id, un);
|
||||
|
||||
// Free the existing unit
|
||||
map[id * UnitNum + un] = NULL;
|
||||
@ -662,54 +696,69 @@ bool ProcessCmd(int fd, const PbCommand &command)
|
||||
// Re-map the controller
|
||||
bool status = MapController(map);
|
||||
|
||||
return ReturnStatus(fd, status, status ? "" : "Error : SASI and SCSI can't be mixed\n");
|
||||
return ReturnStatus(fd, status, status ? "" : "SASI and SCSI can't be mixed");
|
||||
}
|
||||
|
||||
// Valid only for MO or CD
|
||||
if (!pUnit->IsMo() && !pUnit->IsCdRom()) {
|
||||
LOGWARN("rasctl sent an Insert/Eject/Protect command (%d) for incompatible type %s", cmd, pUnit->GetID().c_str());
|
||||
// Only MOs or CDs may be inserted/ejected, only MOs, CDs or hard disks may be protected
|
||||
if ((cmd == INSERT || cmd == EJECT) && !pUnit->IsRemovable()) {
|
||||
LOGWARN("%s requested for incompatible type %s", PbOperation_Name(cmd).c_str(), pUnit->GetID().c_str());
|
||||
|
||||
ostringstream error;
|
||||
error << "Operation denied (Device type " << type_str << " isn't removable)";
|
||||
return ReturnStatus(fd, false, error.str());
|
||||
error << "Operation denied (Device type " << pUnit->GetID().c_str() << " isn't removable)";
|
||||
return ReturnStatus(fd, false, error);
|
||||
}
|
||||
|
||||
if ((cmd == PROTECT || cmd == UNPROTECT) && (!pUnit->IsProtectable() || pUnit->IsReadOnly())) {
|
||||
LOGWARN("%s requested for incompatible type %s", PbOperation_Name(cmd).c_str(), pUnit->GetID().c_str());
|
||||
|
||||
error << "Operation denied (Device type " << pUnit->GetID().c_str() << " isn't protectable)";
|
||||
return ReturnStatus(fd, false, error);
|
||||
}
|
||||
|
||||
switch (cmd) {
|
||||
case INSERT:
|
||||
if (params.empty()) {
|
||||
return ReturnStatus(fd, false, "Missing filename");
|
||||
}
|
||||
|
||||
filepath.SetPath(params.c_str());
|
||||
LOGINFO("rasctl commanded insert file %s into %s ID: %d UN: %d", params.c_str(), pUnit->GetID().c_str(), id, un);
|
||||
LOGINFO("Insert file '%s' requested into %s ID: %d UN: %d", params.c_str(), pUnit->GetID().c_str(), id, un);
|
||||
|
||||
if (!pUnit->Open(filepath)) {
|
||||
ostringstream error;
|
||||
error << "File open error [" << params << "]";
|
||||
|
||||
return ReturnStatus(fd, false, error.str());
|
||||
result = pUnit->Open(filepath);
|
||||
if (result) {
|
||||
// If the file does not exist search for it in the default image folder
|
||||
string default_file = default_image_folder + "/" + params;
|
||||
filepath.SetPath(default_file.c_str());
|
||||
result = pUnit->Open(filepath);
|
||||
if (result) {
|
||||
error << "Tried to open an invalid file '" << params << "': " << result;
|
||||
LOGWARN("%s", error.str().c_str());
|
||||
return ReturnStatus(fd, false, error);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case EJECT:
|
||||
LOGINFO("rasctl commanded eject for %s ID: %d UN: %d", pUnit->GetID().c_str(), id, un);
|
||||
pUnit->Eject(TRUE);
|
||||
LOGINFO("Eject requested for %s ID: %d UN: %d", pUnit->GetID().c_str(), id, un);
|
||||
pUnit->Eject(true);
|
||||
break;
|
||||
|
||||
case PROTECT:
|
||||
if (!pUnit->IsMo()) {
|
||||
LOGWARN("rasctl sent an invalid PROTECT command for %s ID: %d UN: %d", pUnit->GetID().c_str(), id, un);
|
||||
LOGINFO("Write protection requested for %s ID: %d UN: %d", pUnit->GetID().c_str(), id, un);
|
||||
pUnit->WriteP(true);
|
||||
break;
|
||||
|
||||
return ReturnStatus(fd, false, "Error : Operation denied (Device isn't MO)");
|
||||
}
|
||||
LOGINFO("rasctl is setting write protect to %d for %s ID: %d UN: %d",!pUnit->IsWriteP(), pUnit->GetID().c_str(), id, un);
|
||||
pUnit->WriteP(!pUnit->IsWriteP());
|
||||
case UNPROTECT:
|
||||
LOGINFO("Write unprotection requested for %s ID: %d UN: %d", pUnit->GetID().c_str(), id, un);
|
||||
pUnit->WriteP(false);
|
||||
break;
|
||||
|
||||
default:
|
||||
ostringstream error;
|
||||
error << "Received unknown command from rasctl: " << PbOperation_Name(cmd);
|
||||
error << "Received unknown command: " << PbOperation_Name(cmd);
|
||||
LOGWARN("%s", error.str().c_str());
|
||||
return ReturnStatus(fd, false, error.str());
|
||||
return ReturnStatus(fd, false, error);
|
||||
}
|
||||
|
||||
return ReturnStatus(fd, true);
|
||||
return ReturnStatus(fd);
|
||||
}
|
||||
|
||||
bool has_suffix(const string& filename, const string& suffix) {
|
||||
@ -788,32 +837,6 @@ bool ParseArgument(int argc, char* argv[], int& port)
|
||||
break;
|
||||
}
|
||||
|
||||
if (id < 0) {
|
||||
cerr << optarg << ": ID not specified" << endl;
|
||||
return false;
|
||||
} else if (disk[id]) {
|
||||
cerr << id << ": duplicate ID" << endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
string path = optarg;
|
||||
PbDeviceType type = SASI_HD;
|
||||
if (has_suffix(path, ".hdf") || has_suffix(path, ".hds") || has_suffix(path, ".hdn")
|
||||
|| has_suffix(path, ".hdi") || has_suffix(path, ".hda") || has_suffix(path, ".nhd")) {
|
||||
type = SASI_HD;
|
||||
} else if (has_suffix(path, ".mos")) {
|
||||
type = MO;
|
||||
} else if (has_suffix(path, ".iso")) {
|
||||
type = CD;
|
||||
} else if (path == "bridge") {
|
||||
type = BR;
|
||||
} else if (path == "daynaport") {
|
||||
type = DAYNAPORT;
|
||||
} else {
|
||||
cerr << path << ": unknown file extension or basename is missing" << endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
int un = 0;
|
||||
if (is_sasi) {
|
||||
un = id % UnitNum;
|
||||
@ -825,8 +848,7 @@ bool ParseArgument(int argc, char* argv[], int& port)
|
||||
command.set_id(id);
|
||||
command.set_un(un);
|
||||
command.set_cmd(ATTACH);
|
||||
command.set_type(type);
|
||||
command.set_params(path);
|
||||
command.set_params(optarg);
|
||||
if (!ProcessCmd(-1, command)) {
|
||||
return false;
|
||||
}
|
||||
@ -920,7 +942,7 @@ static void *MonThread(void *param)
|
||||
if (!status) {
|
||||
ostringstream error;
|
||||
error << "Invalid log level: " << command.params();
|
||||
ReturnStatus(fd, false, error.str());
|
||||
ReturnStatus(fd, false, error);
|
||||
}
|
||||
else {
|
||||
ReturnStatus(fd);
|
||||
@ -1003,16 +1025,18 @@ int main(int argc, char* argv[])
|
||||
int ret = 0;
|
||||
int port = 6868;
|
||||
|
||||
if (!InitBusAndDisks()) {
|
||||
ret = EPERM;
|
||||
goto init_exit;
|
||||
}
|
||||
InitDisks();
|
||||
|
||||
if (!ParseArgument(argc, argv, port)) {
|
||||
ret = EINVAL;
|
||||
goto err_exit;
|
||||
}
|
||||
|
||||
if (!InitBus()) {
|
||||
ret = EPERM;
|
||||
goto init_exit;
|
||||
}
|
||||
|
||||
if (!InitService(port)) {
|
||||
ret = EPERM;
|
||||
goto init_exit;
|
||||
|
@ -18,13 +18,14 @@ enum PbDeviceType {
|
||||
enum PbOperation {
|
||||
NONE = 0;
|
||||
SERVER_INFO = 1;
|
||||
LIST = 2;
|
||||
ATTACH = 3;
|
||||
DETACH = 4;
|
||||
INSERT = 5;
|
||||
EJECT = 6;
|
||||
PROTECT = 7;
|
||||
LOG_LEVEL = 8;
|
||||
LOG_LEVEL = 2;
|
||||
LIST = 3;
|
||||
ATTACH = 4;
|
||||
DETACH = 5;
|
||||
INSERT = 6;
|
||||
EJECT = 7;
|
||||
PROTECT = 8;
|
||||
UNPROTECT = 9;
|
||||
}
|
||||
|
||||
// Commands rascsi can execute
|
||||
@ -42,14 +43,23 @@ message PbResult {
|
||||
string msg = 2;
|
||||
}
|
||||
|
||||
|
||||
// The device meta data
|
||||
message PbDevice {
|
||||
int32 id = 1;
|
||||
int32 un = 2;
|
||||
string type = 3;
|
||||
PbDeviceType type = 3;
|
||||
string file = 4;
|
||||
bool removable = 5;
|
||||
bool read_only = 6;
|
||||
bool read_only = 5;
|
||||
// Note: Read-only media (e.g. CD-ROMs) are not protectable
|
||||
bool protectable = 6;
|
||||
// Note: Read-only media (e.g. CD-ROMs) are not protected by just read-only
|
||||
bool protected = 7;
|
||||
bool removable = 8;
|
||||
bool removed = 9;
|
||||
bool lockable = 10;
|
||||
bool locked = 11;
|
||||
bool supports_file = 12;
|
||||
}
|
||||
|
||||
message PbDevices {
|
||||
|
@ -51,9 +51,9 @@ int SendCommand(const string& hostname, int port, const PbCommand& command)
|
||||
memcpy(&server.sin_addr.s_addr, host->h_addr, host->h_length);
|
||||
|
||||
if (connect(fd, (struct sockaddr *)&server, sizeof(struct sockaddr_in)) < 0) {
|
||||
ostringstream s;
|
||||
s << "Can't connect to rascsi process on host '" << hostname << "', port " << port;
|
||||
throw ioexception(s.str());
|
||||
ostringstream error;
|
||||
error << "Can't connect to rascsi process on host '" << hostname << "', port " << port;
|
||||
throw ioexception(error.str());
|
||||
}
|
||||
|
||||
SerializeMessage(fd, command);
|
||||
@ -65,7 +65,7 @@ int SendCommand(const string& hostname, int port, const PbCommand& command)
|
||||
close(fd);
|
||||
}
|
||||
|
||||
return -1;
|
||||
exit(fd < 0 ? ENOTCONN : -1);
|
||||
}
|
||||
|
||||
return fd;
|
||||
@ -76,32 +76,26 @@ int SendCommand(const string& hostname, int port, const PbCommand& command)
|
||||
// Receive command result
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
bool ReceiveResult(int fd) {
|
||||
bool status = true;
|
||||
|
||||
bool ReceiveResult(int fd)
|
||||
{
|
||||
try {
|
||||
PbResult result;
|
||||
DeserializeMessage(fd, result);
|
||||
close(fd);
|
||||
|
||||
status = result.status();
|
||||
if (status) {
|
||||
cerr << result.msg();
|
||||
}
|
||||
else {
|
||||
cout << result.msg();
|
||||
if (!result.status()) {
|
||||
throw ioexception(result.msg());
|
||||
}
|
||||
|
||||
cout << result.msg() << endl;
|
||||
}
|
||||
catch(const ioexception& e) {
|
||||
cerr << "Error: " << e.getmsg() << endl;
|
||||
|
||||
// Fall through
|
||||
|
||||
status = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
close(fd);
|
||||
|
||||
return status;
|
||||
return true;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
@ -116,9 +110,6 @@ void CommandList(const string& hostname, int port)
|
||||
command.set_cmd(LIST);
|
||||
|
||||
int fd = SendCommand(hostname.c_str(), port, command);
|
||||
if (fd < 0) {
|
||||
exit(ENOTCONN);
|
||||
}
|
||||
|
||||
PbDevices devices;
|
||||
try {
|
||||
@ -144,12 +135,7 @@ void CommandLogLevel(const string& hostname, int port, const string& log_level)
|
||||
command.set_params(log_level);
|
||||
|
||||
int fd = SendCommand(hostname.c_str(), port, command);
|
||||
if (fd < 0) {
|
||||
exit(ENOTCONN);
|
||||
}
|
||||
|
||||
ReceiveResult(fd);
|
||||
|
||||
close(fd);
|
||||
}
|
||||
|
||||
@ -159,10 +145,6 @@ void CommandServerInfo(const string& hostname, int port)
|
||||
command.set_cmd(SERVER_INFO);
|
||||
|
||||
int fd = SendCommand(hostname.c_str(), port, command);
|
||||
if (fd < 0) {
|
||||
exit(ENOTCONN);
|
||||
}
|
||||
|
||||
|
||||
PbServerInfo serverInfo;
|
||||
try {
|
||||
@ -178,18 +160,18 @@ void CommandServerInfo(const string& hostname, int port)
|
||||
|
||||
close(fd);
|
||||
|
||||
cout << "rascsi version: " << serverInfo.rascsi_version() << endl;
|
||||
cout << "rascsi server version: " << serverInfo.rascsi_version() << endl;
|
||||
|
||||
if (!serverInfo.available_log_levels_size()) {
|
||||
cout << " No log level settings available" << endl;
|
||||
}
|
||||
else {
|
||||
cout << "Available log levels, sorted by severity:" << endl;
|
||||
cout << "Available rascsi log levels, sorted by severity:" << endl;
|
||||
for (int i = 0; i < serverInfo.available_log_levels_size(); i++) {
|
||||
cout << " " << serverInfo.available_log_levels(i) << endl;
|
||||
}
|
||||
|
||||
cout << "Current log level: " << serverInfo.current_log_level() << endl;
|
||||
cout << "Current rascsi log level: " << serverInfo.current_log_level() << endl;
|
||||
}
|
||||
|
||||
cout << "Default image file folder: " << serverInfo.default_image_folder() << endl;
|
||||
@ -226,7 +208,7 @@ int main(int argc, char* argv[])
|
||||
cerr << "Usage: " << argv[0] << " -i ID [-u UNIT] [-c CMD] [-t TYPE] [-f FILE] [-g LOG_LEVEL] [-h HOST] [-p PORT] [-v]" << endl;
|
||||
cerr << " where ID := {0|1|2|3|4|5|6|7}" << endl;
|
||||
cerr << " UNIT := {0|1} default setting is 0." << endl;
|
||||
cerr << " CMD := {attach|detach|insert|eject|protect}" << endl;
|
||||
cerr << " CMD := {attach|detach|insert|eject|protect|unprotect}" << endl;
|
||||
cerr << " TYPE := {hd|mo|cd|bridge|daynaport}" << endl;
|
||||
cerr << " FILE := image file path" << endl;
|
||||
cerr << " HOST := rascsi host to connect to, default is 'localhost'" << endl;
|
||||
@ -249,7 +231,7 @@ int main(int argc, char* argv[])
|
||||
int port = 6868;
|
||||
string params;
|
||||
opterr = 0;
|
||||
while ((opt = getopt(argc, argv, "i:u:c:t:f:h:p:g:lsv")) != -1) {
|
||||
while ((opt = getopt(argc, argv, "i:u:c:t:f:h:p:u:g:lsv")) != -1) {
|
||||
switch (opt) {
|
||||
case 'i':
|
||||
id = optarg[0] - '0';
|
||||
@ -280,6 +262,10 @@ int main(int argc, char* argv[])
|
||||
case 'p':
|
||||
cmd = PROTECT;
|
||||
break;
|
||||
|
||||
case 'u':
|
||||
cmd = UNPROTECT;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
@ -351,8 +337,6 @@ int main(int argc, char* argv[])
|
||||
}
|
||||
}
|
||||
|
||||
PbCommand command;
|
||||
|
||||
if (cmd == LOG_LEVEL) {
|
||||
CommandLogLevel(hostname, port, params);
|
||||
exit(0);
|
||||
@ -369,47 +353,8 @@ int main(int argc, char* argv[])
|
||||
exit(0);
|
||||
}
|
||||
|
||||
// Check the ID number
|
||||
if (id < 0 || id > 7) {
|
||||
cerr << __PRETTY_FUNCTION__ << " Error : Invalid ID " << id << endl;
|
||||
exit(EINVAL);
|
||||
}
|
||||
|
||||
// Check the unit number
|
||||
if (un < 0 || un > 1) {
|
||||
cerr << __PRETTY_FUNCTION__ << " Error : Invalid UNIT " << un << endl;
|
||||
exit(EINVAL);
|
||||
}
|
||||
|
||||
// Type Check
|
||||
if (cmd == ATTACH && type == UNDEFINED) {
|
||||
// Try to determine the file type from the extension
|
||||
int len = params.length();
|
||||
if (len > 4 && params[len - 4] == '.') {
|
||||
string ext = params.substr(len - 3);
|
||||
if (ext == "hdf" || ext == "hds" || ext == "hdn" || ext == "hdi" || ext == "nhd" || ext == "hda") {
|
||||
type = SASI_HD;
|
||||
} else if (ext == "mos") {
|
||||
type = MO;
|
||||
} else if (ext == "iso") {
|
||||
type = CD;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// File check (command is ATTACH and type is HD, for CD and MO the medium (=file) may be inserted later)
|
||||
if (cmd == ATTACH && (type == SASI_HD || type == SCSI_HD) && params.empty()) {
|
||||
cerr << "Error : Invalid file path" << endl;
|
||||
exit(EINVAL);
|
||||
}
|
||||
|
||||
// File check (command is INSERT)
|
||||
if (cmd == INSERT && params.empty()) {
|
||||
cerr << "Error : Invalid file path" << endl;
|
||||
exit(EINVAL);
|
||||
}
|
||||
|
||||
// Generate the command and send it
|
||||
PbCommand command;
|
||||
command.set_id(id);
|
||||
command.set_un(un);
|
||||
command.set_cmd(cmd);
|
||||
@ -419,10 +364,6 @@ int main(int argc, char* argv[])
|
||||
}
|
||||
|
||||
int fd = SendCommand(hostname, port, command);
|
||||
if (fd < 0) {
|
||||
exit(ENOTCONN);
|
||||
}
|
||||
|
||||
bool status = ReceiveResult(fd);
|
||||
close(fd);
|
||||
|
||||
|
@ -86,7 +86,8 @@ int ReadNBytes(int fd, uint8_t *buf, int n)
|
||||
// List devices
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
string ListDevices(const PbDevices& devices) {
|
||||
string ListDevices(const PbDevices& devices)
|
||||
{
|
||||
ostringstream s;
|
||||
|
||||
if (devices.devices_size()) {
|
||||
@ -102,11 +103,64 @@ string ListDevices(const PbDevices& devices) {
|
||||
for (int i = 0; i < devices.devices_size() ; i++) {
|
||||
PbDevice device = devices.devices(i);
|
||||
|
||||
s << "| " << device.id() << " | " << device.un() << " | " << device.type() << " | "
|
||||
<< device.file() << (device.read_only() ? " (WRITEPROTECT)" : "") << endl;
|
||||
s << "| " << device.id() << " | " << device.un() << " | " << MapTypeToId(device.type()) << " | "
|
||||
<< (device.file().empty() ? "NO MEDIA" : device.file())
|
||||
<< (device.read_only() ? " (WRITEPROTECT)" : "") << endl;
|
||||
}
|
||||
|
||||
s << "+----+----+------+-------------------------------------" << endl;
|
||||
|
||||
return s.str();
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Map the device ID to the PbDeviceType and vice versa
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
PbDeviceType MapIdToType(const string& id, bool is_sasi)
|
||||
{
|
||||
if (id == "SCHD") {
|
||||
return is_sasi ? SASI_HD : SCSI_HD;
|
||||
}
|
||||
else if (id == "SCMO") {
|
||||
return MO;
|
||||
}
|
||||
else if (id == "SCCD") {
|
||||
return CD;
|
||||
}
|
||||
else if (id == "SCBR") {
|
||||
return BR;
|
||||
}
|
||||
else if (id == "SCDP") {
|
||||
return DAYNAPORT;
|
||||
}
|
||||
else {
|
||||
return UNDEFINED;
|
||||
}
|
||||
}
|
||||
|
||||
string MapTypeToId(const PbDeviceType type)
|
||||
{
|
||||
switch (type) {
|
||||
case SASI_HD:
|
||||
case SCSI_HD:
|
||||
return "SCHD";
|
||||
|
||||
case MO:
|
||||
return "SCMO";
|
||||
|
||||
case CD:
|
||||
return "SCCD";
|
||||
|
||||
case BR:
|
||||
return "SCBR";
|
||||
|
||||
case DAYNAPORT:
|
||||
return "SCDP";
|
||||
|
||||
default:
|
||||
return "????";
|
||||
}
|
||||
}
|
||||
|
@ -19,5 +19,7 @@ void SerializeMessage(int, const google::protobuf::MessageLite&);
|
||||
void DeserializeMessage(int, google::protobuf::MessageLite&);
|
||||
int ReadNBytes(int, uint8_t *, int);
|
||||
string ListDevices(const rascsi_interface::PbDevices&);
|
||||
rascsi_interface::PbDeviceType MapIdToType(const string&, bool);
|
||||
string MapTypeToId(const rascsi_interface::PbDeviceType);
|
||||
|
||||
#endif
|
||||
|
Loading…
x
Reference in New Issue
Block a user