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:
Uwe Seimet 2021-08-08 17:08:58 +02:00 committed by GitHub
parent 3d14950548
commit 0a5298b8ac
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 382 additions and 321 deletions

View File

@ -54,6 +54,7 @@ Command is the operation being requested. options are:
insert: insert media (Magneto-Optical and CD only) insert: insert media (Magneto-Optical and CD only)
eject: eject media (Magneto-Optical and CD only) eject: eject media (Magneto-Optical and CD only)
protect: Write protect the media (Magneto-Optical only) protect: Write protect the media (Magneto-Optical only)
unprotect: Remove write protection from the media (Magneto-Optical only)
.IP .IP
When the command is omitted, rasctl will default to the 'attach' command. When the command is omitted, rasctl will default to the 'attach' command.
.TP .TP

View File

@ -6,8 +6,8 @@ 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] -i ID [-c CMD] [-f rasctl -l | -s | [-g LOG_LEVEL] [-h HOST] [-p PORT] [-v] -i ID [-c CMD]
FILE] [-t TYPE] [-u UNIT] [-f FILE] [-t TYPE] [-u UNIT]
DESCRIPTION DESCRIPTION
rasctl Sends commands to the rascsi process to make configuration ad rasctl Sends commands to the rascsi process to make configuration ad
@ -34,7 +34,9 @@ OPTIONS
-p PORT -p PORT
The rascsi port to connect to, default is 6868. 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) -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) insert: insert media (Magneto-Optical and CD only)
eject: eject media (Magneto-Optical and CD only) eject: eject media (Magneto-Optical and CD only)
protect: Write protect the media (Magneto-Optical 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' When the command is omitted, rasctl will default to the 'attach'
command. command.
-f FILE -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. lowable file types.
-t TYPE -t TYPE
Specifies the type of disk. If this disagrees with the file ex Specifies the type of disk. If this disagrees with the file ex
tension of the specified image, the TYPE argument is ignored. tension of the specified image, the TYPE argument is ignored.
Available drive types are: Available drive types are:
hd: Hard disk (SCSI or SASI) hd: Hard disk (SCSI or SASI)
mo: Magneto-Optical disk) mo: Magneto-Optical disk)
cd: CD-ROM cd: CD-ROM
bridge: Bridge device (This is only applicable to the Sharp bridge: Bridge device (This is only applicable to the Sharp
X68000) X68000)
-u UNIT -u UNIT
Unit number (0 or 1). This will default to 0. This option is 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 only used when there are multiple SCSI devices on a shared SCSI
controller. (This is not common) controller. (This is not common)
EXAMPLES EXAMPLES

View File

@ -247,7 +247,7 @@ BOOL CTapDriver::Init()
} }
#endif // __NetBSD__ #endif // __NetBSD__
BOOL CTapDriver::OpenDump(const Filepath& path) { const char *CTapDriver::OpenDump(const Filepath& path) {
if (m_pcap == NULL) { if (m_pcap == NULL) {
m_pcap = pcap_open_dead(DLT_EN10MB, 65535); 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()); m_pcap_dumper = pcap_dump_open(m_pcap, path.GetPath());
if (m_pcap_dumper == NULL) { if (m_pcap_dumper == NULL) {
LOGERROR("Error: can't open pcap file: %s", pcap_geterr(m_pcap)); 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()); LOGTRACE("%s Opened %s for dumping", __PRETTY_FUNCTION__, path.GetPath());
return TRUE; return NULL;
} }
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------

View File

@ -34,7 +34,7 @@ public:
// Basic Functionality // Basic Functionality
CTapDriver(); // Constructor CTapDriver(); // Constructor
BOOL Init(); // Initilization BOOL Init(); // Initilization
BOOL OpenDump(const Filepath& path); const char *OpenDump(const Filepath& path);
// Capture packets // Capture packets
void Cleanup(); // Cleanup void Cleanup(); // Cleanup
void GetMacAddr(BYTE *mac); // Get Mac Address void GetMacAddr(BYTE *mac); // Get Mac Address

View File

@ -711,12 +711,16 @@ Disk::Disk(std::string id)
// Work initialization // Work initialization
disk.ready = FALSE; disk.ready = FALSE;
disk.writep = FALSE; disk.protectable = false;
disk.readonly = FALSE; disk.writep = false;
disk.removable = FALSE; disk.readonly = false;
disk.lock = FALSE; disk.removable = false;
disk.attn = FALSE; disk.removed = false;
disk.reset = FALSE; disk.lockable = false;
disk.locked = false;
disk.supports_file = true;
disk.attn = false;
disk.reset = false;
disk.size = 0; disk.size = 0;
disk.blocks = 0; disk.blocks = 0;
disk.lun = 0; disk.lun = 0;
@ -758,9 +762,9 @@ Disk::~Disk()
void Disk::Reset() void Disk::Reset()
{ {
// no lock, no attention, reset // no lock, no attention, reset
disk.lock = FALSE; disk.locked = false;
disk.attn = FALSE; disk.attn = false;
disk.reset = TRUE; disk.reset = false;
} }
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
@ -839,9 +843,10 @@ bool Disk::IsNuvolink() const
// //
// Open // Open
// * Call as a post-process after successful opening in a derived class // * 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.size >= 8) && (disk.size <= 11));
ASSERT(disk.blocks > 0); ASSERT(disk.blocks > 0);
@ -851,54 +856,55 @@ BOOL Disk::Open(const Filepath& path, BOOL /*attn*/)
// Cache initialization // Cache initialization
ASSERT(!disk.dcache); ASSERT(!disk.dcache);
disk.dcache = disk.dcache = new DiskCache(path, disk.size, disk.blocks, disk.imgoffset);
new DiskCache(path, disk.size, disk.blocks, disk.imgoffset);
// Can read/write open // Can read/write open
Fileio fio; Fileio fio;
if (fio.Open(path, Fileio::ReadWrite)) { if (fio.Open(path, Fileio::ReadWrite)) {
// Write permission, not read only // Write permission, not read only
disk.writep = FALSE; disk.writep = false;
disk.readonly = FALSE; disk.readonly = false;
fio.Close(); fio.Close();
} else { } else {
// Write protected, read only // Write protected, read only
disk.writep = TRUE; disk.writep = true;
disk.readonly = TRUE; disk.readonly = true;
} }
// Not locked // Not locked, not removed
disk.lock = FALSE; disk.locked = false;
disk.removed = false;
// Save path // Save path
diskpath = path; diskpath = path;
// Success // Success
return TRUE; return NULL;
} }
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
// //
// Eject // 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 // Can only be ejected if it is removable
if (!disk.removable) { if (!disk.removable) {
return; return false;
} }
// If you're not ready, you don't need to eject // If you're not ready, you don't need to eject
if (!disk.ready) { if (!disk.ready) {
return; return false;
} }
// Must be unlocked if there is no force flag // Must be unlocked if there is no force flag
if (!force) { if (!force && disk.locked) {
if (disk.lock) { return false;
return;
}
} }
// Remove disk cache // Remove disk cache
@ -908,9 +914,12 @@ void Disk::Eject(BOOL force)
// Not ready, no attention // Not ready, no attention
disk.ready = FALSE; disk.ready = FALSE;
disk.writep = FALSE; disk.writep = false;
disk.readonly = FALSE; disk.readonly = false;
disk.attn = FALSE; disk.removed = true;
disk.attn = false;
return true;
} }
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
@ -918,21 +927,22 @@ void Disk::Eject(BOOL force)
// Write Protected // Write Protected
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
void Disk::WriteP(BOOL writep) bool Disk::WriteP(bool writep)
{ {
// be ready // be ready
if (!disk.ready) { if (!disk.ready) {
return; return false;
} }
// Read Only, protect only // Read Only, protect only
if (disk.readonly) { if (disk.readonly && !writep) {
ASSERT(disk.writep); return false;
return;
} }
// Write protect flag setting // Write protect flag setting
disk.writep = writep; disk.writep = writep;
return true;
} }
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
@ -971,7 +981,7 @@ BOOL Disk::CheckReady()
// Not ready if reset // Not ready if reset
if (disk.reset) { if (disk.reset) {
disk.code = DISK_DEVRESET; disk.code = DISK_DEVRESET;
disk.reset = FALSE; disk.reset = false;
LOGTRACE("%s Disk in reset", __PRETTY_FUNCTION__); LOGTRACE("%s Disk in reset", __PRETTY_FUNCTION__);
return FALSE; return FALSE;
} }
@ -979,7 +989,7 @@ BOOL Disk::CheckReady()
// Not ready if it needs attention // Not ready if it needs attention
if (disk.attn) { if (disk.attn) {
disk.code = DISK_ATTENTION; disk.code = DISK_ATTENTION;
disk.attn = FALSE; disk.attn = false;
LOGTRACE("%s Disk in needs attention", __PRETTY_FUNCTION__); LOGTRACE("%s Disk in needs attention", __PRETTY_FUNCTION__);
return FALSE; return FALSE;
} }
@ -1896,14 +1906,14 @@ BOOL Disk::StartStop(const DWORD *cdb)
// Look at the eject bit and eject if necessary // Look at the eject bit and eject if necessary
if (cdb[4] & 0x02) { if (cdb[4] & 0x02) {
if (disk.lock) { if (disk.locked) {
// Cannot be ejected because it is locked // Cannot be ejected because it is locked
disk.code = DISK_PREVENT; disk.code = DISK_PREVENT;
return FALSE; return FALSE;
} }
// Eject // Eject
Eject(FALSE); Eject(false);
} }
// OK // OK
@ -1955,9 +1965,9 @@ BOOL Disk::Removal(const DWORD *cdb)
// Set Lock flag // Set Lock flag
if (cdb[4] & 0x01) { if (cdb[4] & 0x01) {
disk.lock = TRUE; disk.locked = true;
} else { } else {
disk.lock = FALSE; disk.locked = false;
} }
// REMOVAL Success // REMOVAL Success

View File

@ -167,12 +167,17 @@ public:
typedef struct { typedef struct {
std::string id; // Media ID std::string id; // Media ID
BOOL ready; // Valid Disk BOOL ready; // Valid Disk
BOOL writep; // Write protected bool readonly; // Read only
BOOL readonly; // Read only bool protectable;
BOOL removable; // Removable bool writep; // Write protected
BOOL lock; // Locked bool removable; // Removable
BOOL attn; // Attention bool removed;
BOOL reset; // Reset 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 int size; // Sector Size
DWORD blocks; // Total number of sectors DWORD blocks; // Total number of sectors
DWORD lun; // LUN DWORD lun; // LUN
@ -198,15 +203,19 @@ public:
bool IsNuvolink() const; bool IsNuvolink() const;
// Media Operations // 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 GetPath(Filepath& path) const; // Get the path
void Eject(BOOL force); // Eject bool Eject(bool); // Eject
bool IsReady() const { return disk.ready; } // Ready check 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 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 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 IsAttn() const { return disk.attn; } // Get attention flag
bool Flush(); // Flush the cache bool Flush(); // Flush the cache

View File

@ -31,6 +31,7 @@
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
SASIHD::SASIHD() : Disk("SAHD") SASIHD::SASIHD() : Disk("SAHD")
{ {
disk.protectable = true;
} }
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
@ -41,7 +42,7 @@ SASIHD::SASIHD() : Disk("SAHD")
void SASIHD::Reset() void SASIHD::Reset()
{ {
// Unlock, clear attention // Unlock, clear attention
disk.lock = FALSE; disk.locked = FALSE;
disk.attn = FALSE; disk.attn = FALSE;
// Reset, clear the code // Reset, clear the code
@ -54,14 +55,14 @@ void SASIHD::Reset()
// Open // Open
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
BOOL SASIHD::Open(const Filepath& path, BOOL /*attn*/) const char *SASIHD::Open(const Filepath& path, BOOL /*attn*/)
{ {
ASSERT(!disk.ready); ASSERT(!disk.ready);
// Open as read-only // Open as read-only
Fileio fio; Fileio fio;
if (!fio.Open(path, Fileio::ReadOnly)) { if (!fio.Open(path, Fileio::ReadOnly)) {
return FALSE; return "Can't open hard disk file read-only";
} }
// Get file size // Get file size
@ -84,7 +85,7 @@ BOOL SASIHD::Open(const Filepath& path, BOOL /*attn*/)
#if defined(REMOVE_FIXED_SASIHD_SIZE) #if defined(REMOVE_FIXED_SASIHD_SIZE)
// Must be in 256-byte units // Must be in 256-byte units
if (size & 0xff) { if (size & 0xff) {
return FALSE; return "File size must be a multiple of 512";
} }
// 10MB or more // 10MB or more
@ -94,7 +95,7 @@ BOOL SASIHD::Open(const Filepath& path, BOOL /*attn*/)
// Limit to about 512MB // Limit to about 512MB
if (size > 512 * 1024 * 1024) { if (size > 512 * 1024 * 1024) {
return FALSE; return "File size must not exceed 512 MB";
} }
#else #else
// 10MB, 20MB, 40MBのみ // 10MB, 20MB, 40MBのみ
@ -113,7 +114,7 @@ BOOL SASIHD::Open(const Filepath& path, BOOL /*attn*/)
// Other (Not supported ) // Other (Not supported )
default: default:
return FALSE; return "Unsupported file size";
} }
#endif // REMOVE_FIXED_SASIHD_SIZE #endif // REMOVE_FIXED_SASIHD_SIZE

View File

@ -30,8 +30,8 @@ public:
// Basic Functions // Basic Functions
SASIHD(); // Constructor SASIHD(); // Constructor
void Reset(); // Reset void Reset(); // Reset
BOOL Open(const Filepath& path, BOOL attn = TRUE); // Open const char *Open(const Filepath& path, BOOL attn = TRUE); // Open
// commands // commands
int RequestSense(const DWORD *cdb, BYTE *buf); // REQUEST SENSE command int RequestSense(const DWORD *cdb, BYTE *buf); // REQUEST SENSE command
}; };

View File

@ -49,6 +49,8 @@ const BYTE SCSIDaynaPort::m_apple_talk_addr[6] = { 0x09, 0x00, 0x07, 0xff, 0xff,
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
SCSIDaynaPort::SCSIDaynaPort() : Disk("SCDP") SCSIDaynaPort::SCSIDaynaPort() : Disk("SCDP")
{ {
disk.supports_file = false;
#ifdef __linux__ #ifdef __linux__
// TAP Driver Generation // TAP Driver Generation
m_tap = new CTapDriver(); 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"); LOGTRACE("SCSIDaynaPort Open");
return m_tap->OpenDump(path); return m_tap->OpenDump(path);

View File

@ -46,7 +46,7 @@ public:
// Constructor // Constructor
virtual ~SCSIDaynaPort(); virtual ~SCSIDaynaPort();
// Destructor // Destructor
BOOL Open(const Filepath& path, BOOL attn = TRUE); const char *Open(const Filepath& path, BOOL attn = TRUE);
// Capture packets // Capture packets
// commands // commands

View File

@ -34,6 +34,8 @@
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
SCSIBR::SCSIBR() : Disk("SCBR") SCSIBR::SCSIBR() : Disk("SCBR")
{ {
disk.supports_file = false;
fsoptlen = 0; fsoptlen = 0;
fsoutlen = 0; fsoutlen = 0;
fsresult = 0; fsresult = 0;

View File

@ -251,9 +251,9 @@ CDDABuf::~CDDABuf()
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
SCSICD::SCSICD() : Disk("SCCD") SCSICD::SCSICD() : Disk("SCCD")
{ {
// removable, write protected disk.removable = true;
disk.removable = TRUE; disk.lockable = true;
disk.writep = TRUE; disk.writep = true;
// NOT in raw format // NOT in raw format
rawfile = FALSE; rawfile = FALSE;
@ -286,7 +286,7 @@ SCSICD::~SCSICD()
// Open // Open
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
BOOL SCSICD::Open(const Filepath& path, BOOL attn) const char *SCSICD::Open(const Filepath& path, BOOL attn)
{ {
Fileio fio; Fileio fio;
off64_t size; off64_t size;
@ -301,7 +301,7 @@ BOOL SCSICD::Open(const Filepath& path, BOOL attn)
// Open as read-only // Open as read-only
if (!fio.Open(path, Fileio::ReadOnly)) { if (!fio.Open(path, Fileio::ReadOnly)) {
return FALSE; return "Can't open CD-ROM file read-only";
} }
// Close and transfer for physical CD access // Close and transfer for physical CD access
@ -311,14 +311,14 @@ BOOL SCSICD::Open(const Filepath& path, BOOL attn)
// Open physical CD // Open physical CD
if (!OpenPhysical(path)) { if (!OpenPhysical(path)) {
return FALSE; return "Can't open physical CD";
} }
} else { } else {
// Get file size // Get file size
size = fio.GetFileSize(); size = fio.GetFileSize();
if (size <= 4) { if (size <= 4) {
fio.Close(); fio.Close();
return FALSE; return "Invalid file size";
} }
// Judge whether it is a CUE sheet or an ISO file // 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) { if (xstrncasecmp(file, _T("FILE"), 4) == 0) {
// Open as CUE // Open as CUE
if (!OpenCue(path)) { if (!OpenCue(path)) {
return FALSE; return "Can't open as CUE";
} }
} else { } else {
// Open as ISO // Open as ISO
if (!OpenIso(path)) { 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; disk.attn = TRUE;
} }
return TRUE; return NULL;
} }
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------

View File

@ -114,7 +114,7 @@ public:
// Basic Functions // Basic Functions
SCSICD(); // Constructor SCSICD(); // Constructor
virtual ~SCSICD(); // Destructor virtual ~SCSICD(); // Destructor
BOOL Open(const Filepath& path, BOOL attn = TRUE); // Open const char *Open(const Filepath& path, BOOL attn = TRUE); // Open
// commands // commands
int Inquiry(const DWORD *cdb, BYTE *buf, DWORD major, DWORD minor); // INQUIRY command int Inquiry(const DWORD *cdb, BYTE *buf, DWORD major, DWORD minor); // INQUIRY command

View File

@ -30,6 +30,7 @@
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
SCSIHD::SCSIHD() : Disk("SCHD") SCSIHD::SCSIHD() : Disk("SCHD")
{ {
disk.protectable = true;
} }
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
@ -40,7 +41,7 @@ SCSIHD::SCSIHD() : Disk("SCHD")
void SCSIHD::Reset() void SCSIHD::Reset()
{ {
// Unlock and release attention // Unlock and release attention
disk.lock = FALSE; disk.locked = FALSE;
disk.attn = FALSE; disk.attn = FALSE;
// No reset, clear code // No reset, clear code
@ -53,14 +54,14 @@ void SCSIHD::Reset()
// Open // Open
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
BOOL SCSIHD::Open(const Filepath& path, BOOL /*attn*/) const char *SCSIHD::Open(const Filepath& path, BOOL /*attn*/)
{ {
ASSERT(!disk.ready); ASSERT(!disk.ready);
// read open required // read open required
Fileio fio; Fileio fio;
if (!fio.Open(path, Fileio::ReadOnly)) { if (!fio.Open(path, Fileio::ReadOnly)) {
return FALSE; return "Can't open hard disk file read-only";
} }
// Get file size // Get file size
@ -69,13 +70,14 @@ BOOL SCSIHD::Open(const Filepath& path, BOOL /*attn*/)
// Must be 512 bytes // Must be 512 bytes
if (size & 0x1ff) { if (size & 0x1ff) {
return FALSE; return "File size must be a multiple of 512 bytes";
} }
// 2TB according to xm6i // 2TB according to xm6i
// There is a similar one in wxw/wxw_cfg.cpp // 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) { if (size > 2LL * 1024 * 1024 * 1024 * 1024) {
return FALSE; return "File size must not exceed 2 TB";
} }
// sector size and number of blocks // sector size and number of blocks

View File

@ -30,9 +30,9 @@ public:
// Basic Functions // Basic Functions
SCSIHD(); // Constructor SCSIHD(); // Constructor
void Reset(); // Reset void Reset(); // Reset
BOOL Open(const Filepath& path, BOOL attn = TRUE); // Open const char *Open(const Filepath& path, BOOL attn = TRUE); // Open
// commands // commands
int Inquiry(const DWORD *cdb, BYTE *buf, DWORD major, DWORD minor); // INQUIRY command 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 BOOL ModeSelect(const DWORD *cdb, const BYTE *buf, int length); // MODE SELECT(6) command
}; };

View File

@ -65,7 +65,7 @@ static inline DWORD getDwordLE(const BYTE *b)
// Open // Open
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
BOOL SCSIHD_NEC::Open(const Filepath& path, BOOL /*attn*/) const char *SCSIHD_NEC::Open(const Filepath& path, BOOL /*attn*/)
{ {
Fileio fio; Fileio fio;
off64_t size; off64_t size;
@ -76,7 +76,7 @@ BOOL SCSIHD_NEC::Open(const Filepath& path, BOOL /*attn*/)
// Open as read-only // Open as read-only
if (!fio.Open(path, Fileio::ReadOnly)) { if (!fio.Open(path, Fileio::ReadOnly)) {
return FALSE; return "Can't open hard disk file read-only";
} }
// Get file size // Get file size
@ -86,14 +86,14 @@ BOOL SCSIHD_NEC::Open(const Filepath& path, BOOL /*attn*/)
if (size >= (off64_t)sizeof(hdr)) { if (size >= (off64_t)sizeof(hdr)) {
if (!fio.Read(hdr, sizeof(hdr))) { if (!fio.Read(hdr, sizeof(hdr))) {
fio.Close(); fio.Close();
return FALSE; return "Can't read hard disk file header";
} }
} }
fio.Close(); fio.Close();
// Must be in 512 byte units // Must be in 512 byte units
if (size & 0x1ff) { if (size & 0x1ff) {
return FALSE; return "File size must be a multiple of 512";
} }
// 10MB or more // 10MB or more
@ -102,7 +102,7 @@ BOOL SCSIHD_NEC::Open(const Filepath& path, BOOL /*attn*/)
} }
// 2TB according to xm6i // 2TB according to xm6i
if (size > 2LL * 1024 * 1024 * 1024 * 1024) { if (size > 2LL * 1024 * 1024 * 1024 * 1024) {
return FALSE; return "File size must not exceed 2 TB";
} }
// Determine parameters by extension // Determine parameters by extension
@ -136,12 +136,12 @@ BOOL SCSIHD_NEC::Open(const Filepath& path, BOOL /*attn*/)
// Supports 256 or 512 sector sizes // Supports 256 or 512 sector sizes
if (sectorsize != 256 && sectorsize != 512) { if (sectorsize != 256 && sectorsize != 512) {
return FALSE; return "Sector size must be 256 or 512";
} }
// Image size consistency check // Image size consistency check
if (imgoffset + imgsize > size || (imgsize % sectorsize != 0)) { if (imgoffset + imgsize > size || (imgsize % sectorsize != 0)) {
return FALSE; return "Image size consistency check failed";
} }
// Sector size // Sector size
@ -150,7 +150,7 @@ BOOL SCSIHD_NEC::Open(const Filepath& path, BOOL /*attn*/)
break; break;
} }
if (disk.size <= 0 || disk.size > 16) { if (disk.size <= 0 || disk.size > 16) {
return FALSE; return "Invalid disk size";
} }
// Number of blocks // Number of blocks

View File

@ -27,7 +27,7 @@ class SCSIHD_NEC : public SCSIHD
public: public:
// Basic Functions // Basic Functions
SCSIHD_NEC(); // Constructor SCSIHD_NEC(); // Constructor
BOOL Open(const Filepath& path, BOOL attn = TRUE); // Open const char *Open(const Filepath& path, BOOL attn = TRUE); // Open
// commands // commands
int Inquiry(const DWORD *cdb, BYTE *buf, DWORD major, DWORD minor); // INQUIRY command int Inquiry(const DWORD *cdb, BYTE *buf, DWORD major, DWORD minor); // INQUIRY command
@ -44,4 +44,4 @@ private:
int sectorsize; // Sector size int sectorsize; // Sector size
off64_t imgoffset; // Image offset off64_t imgoffset; // Image offset
off64_t imgsize; // Image size off64_t imgsize; // Image size
}; };

View File

@ -31,8 +31,9 @@
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
SCSIMO::SCSIMO() : Disk("SCMO") 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 // Open
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
BOOL SCSIMO::Open(const Filepath& path, BOOL attn) const char *SCSIMO::Open(const Filepath& path, BOOL attn)
{ {
ASSERT(!disk.ready); ASSERT(!disk.ready);
// Open as read-only // Open as read-only
Fileio fio; Fileio fio;
if (!fio.Open(path, Fileio::ReadOnly)) { if (!fio.Open(path, Fileio::ReadOnly)) {
return FALSE; return "Can't open MO file read-only";
} }
// Get file size // Get file size
@ -81,7 +82,7 @@ BOOL SCSIMO::Open(const Filepath& path, BOOL attn)
// Other (this is an error) // Other (this is an error)
default: default:
return FALSE; return "Invalid MO size";
} }
// Call the base class // Call the base class
@ -92,7 +93,7 @@ BOOL SCSIMO::Open(const Filepath& path, BOOL attn)
disk.attn = TRUE; disk.attn = TRUE;
} }
return TRUE; return NULL;
} }
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------

View File

@ -29,7 +29,7 @@ class SCSIMO : public Disk
public: public:
// Basic Functions // Basic Functions
SCSIMO(); // Constructor SCSIMO(); // Constructor
BOOL Open(const Filepath& path, BOOL attn = TRUE); // Open const char *Open(const Filepath& path, BOOL attn = TRUE); // Open
// commands // commands
int Inquiry(const DWORD *cdb, BYTE *buf, DWORD major, DWORD minor); // INQUIRY command int Inquiry(const DWORD *cdb, BYTE *buf, DWORD major, DWORD minor); // INQUIRY command

View File

@ -12,6 +12,8 @@
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
#include <sys/mman.h>
#include "os.h" #include "os.h"
#include "xm6.h" #include "xm6.h"
#include "gpiobus.h" #include "gpiobus.h"

View File

@ -35,19 +35,16 @@
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
#include <stdio.h> #include <cstdio>
#include <stdlib.h> #include <cstdlib>
#include <stdint.h> #include <cstddef>
#include <stddef.h> #include <cstdarg>
#include <assert.h> #include <cstring>
#include <csignal>
#include <cassert>
#include <unistd.h> #include <unistd.h>
#include <errno.h>
#include <time.h>
#include <utime.h> #include <utime.h>
#include <fcntl.h> #include <fcntl.h>
#include <signal.h>
#include <stdarg.h>
#include <string.h>
#include <sched.h> #include <sched.h>
#include <pthread.h> #include <pthread.h>
#include <iconv.h> #include <iconv.h>
@ -58,14 +55,13 @@
#include <poll.h> #include <poll.h>
#include <dirent.h> #include <dirent.h>
#include <sys/mman.h>
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <sys/epoll.h> #include <sys/epoll.h>
#include <netinet/in.h> #include <netinet/in.h>
#include <linux/gpio.h>
#if defined(__linux__) #if defined(__linux__)
#include <linux/gpio.h>
#include <linux/if.h> #include <linux/if.h>
#include <linux/if_tun.h> #include <linux/if_tun.h>
#elif defined(__NetBSD__) #elif defined(__NetBSD__)

View File

@ -176,18 +176,24 @@ BOOL InitService(int port)
return true; return true;
} }
bool InitBusAndDisks() { bool InitBus()
{
// GPIOBUS creation // GPIOBUS creation
bus = new GPIOBUS(); bus = new GPIOBUS();
// GPIO Initialization // GPIO Initialization
if (!bus->Init()) { if (!bus->Init()) {
return FALSE; return false;
} }
// Bus Reset // Bus Reset
bus->Reset(); bus->Reset();
return true;
}
void InitDisks()
{
// Controller initialization // Controller initialization
for (int i = 0; i < CtrlMax; i++) { for (int i = 0; i < CtrlMax; i++) {
ctrl[i] = NULL; ctrl[i] = NULL;
@ -197,8 +203,6 @@ bool InitBusAndDisks() {
for (int i = 0; i < CtrlMax; i++) { for (int i = 0; i < CtrlMax; i++) {
disk[i] = NULL; disk[i] = NULL;
} }
return TRUE;
} }
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
@ -225,10 +229,12 @@ void Cleanup()
} }
// Cleanup the Bus // Cleanup the Bus
bus->Cleanup(); if (bus) {
bus->Cleanup();
// Discard the GPIOBUS object // Discard the GPIOBUS object
delete bus; delete bus;
}
// Close the monitor socket // Close the monitor socket
if (monsocket >= 0) { if (monsocket >= 0) {
@ -278,7 +284,7 @@ PbDevices GetDevices() {
device->set_un(i % UnitNum); device->set_un(i % UnitNum);
// ID,UNIT,Type,Device Status // ID,UNIT,Type,Device Status
device->set_type(pUnit->GetID()); device->set_type(MapIdToType(pUnit->GetID(), pUnit->IsSASI()));
// mount status output // mount status output
if (pUnit->IsBridge()) { if (pUnit->IsBridge()) {
@ -288,10 +294,16 @@ PbDevices GetDevices() {
} else { } else {
Filepath filepath; Filepath filepath;
pUnit->GetPath(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_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 // Write protection status
if (pUnit->IsRemovable() && pUnit->IsReady() && pUnit->IsWriteP()) { if (pUnit->IsRemovable() && pUnit->IsReady() && pUnit->IsWriteP()) {
@ -419,9 +431,11 @@ bool MapController(Disk **map)
return status; 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 (fd == -1) {
if (msg.length()) { if (msg.length()) {
FPRT(stderr, "Error: ");
FPRT(stderr, msg.c_str()); FPRT(stderr, msg.c_str());
FPRT(stderr, "\n"); FPRT(stderr, "\n");
} }
@ -429,14 +443,20 @@ bool ReturnStatus(int fd, bool status = true, const string msg = "") {
else { else {
PbResult result; PbResult result;
result.set_status(status); result.set_status(status);
result.set_msg(msg + "\n"); result.set_msg(msg);
SerializeMessage(fd, result); SerializeMessage(fd, result);
} }
return status; 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") { if (log_level == "trace") {
set_level(level::trace); set_level(level::trace);
} }
@ -502,12 +522,12 @@ void GetAvailableImages(PbServerInfo& serverInfo)
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
bool ProcessCmd(int fd, const PbCommand &command) bool ProcessCmd(int fd, const PbCommand &command)
{ {
Disk *map[CtrlMax * UnitNum];
Filepath filepath; Filepath filepath;
Disk *pUnit; Disk *pUnit;
char type_str[5]; ostringstream error;
const char *result;
int id = command.id(); int id = command.id();
int un = command.un(); int un = command.un();
PbOperation cmd = command.cmd(); PbOperation cmd = command.cmd();
PbDeviceType type = command.type(); PbDeviceType type = command.type();
@ -518,37 +538,53 @@ bool ProcessCmd(int fd, const PbCommand &command)
LOGINFO("%s", s.str().c_str()); LOGINFO("%s", s.str().c_str());
// Copy the Unit List // Copy the Unit List
Disk *map[CtrlMax * UnitNum];
memcpy(map, disk, sizeof(disk)); memcpy(map, disk, sizeof(disk));
// Check the Controller Number // Check the Controller Number
if (id < 0 || id >= CtrlMax) { 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 // Check the Unit Number
if (un < 0 || un >= UnitNum) { 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 // Connect Command
if (cmd == ATTACH) { if (cmd == ATTACH) {
string ext; if (map[id]) {
error << "Duplicate ID " << id;
return ReturnStatus(fd, false, error);
}
// Distinguish between SASI and SCSI // If no type was specified try to derive the file type from the extension
if (type == SASI_HD) { if (type == UNDEFINED) {
// Check the extension if (ext == "hdf") {
int len = params.length(); type = SASI_HD;
if (len < 5 || params[len - 4] != '.') {
return ReturnStatus(fd, false);
} }
else if (ext == "hds" || ext == "hdn" || ext == "hdi" || ext == "nhd" || ext == "hda") {
// If the extension is not SASI type, replace with SCSI
ext = params.substr(len - 3);
if (ext != "hdf") {
type = SCSI_HD; 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 // Create a new drive, based upon type
pUnit = NULL; pUnit = NULL;
switch (type) { switch (type) {
@ -580,9 +616,8 @@ bool ProcessCmd(int fd, const PbCommand &command)
pUnit = new SCSIDaynaPort(); pUnit = new SCSIDaynaPort();
break; break;
default: default:
ostringstream error; error << "Received a command for an invalid drive type: " << PbDeviceType_Name(type);
error << "rasctl sent a command for an invalid drive type: " << PbDeviceType_Name(type); return ReturnStatus(fd, false, error);
return ReturnStatus(fd, false, error.str());
} }
// drive checks files // drive checks files
@ -594,25 +629,24 @@ bool ProcessCmd(int fd, const PbCommand &command)
filepath.SetPath(file.c_str()); filepath.SetPath(file.c_str());
// Open the file path // 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 // If the file does not exist search for it in the default image folder
string default_file = default_image_folder + "/" + file; string default_file = default_image_folder + "/" + file;
filepath.SetPath(default_file.c_str()); filepath.SetPath(default_file.c_str());
if (!pUnit->Open(filepath)) { result = pUnit->Open(filepath);
if (result) {
delete pUnit; delete pUnit;
LOGWARN("rasctl tried to open an invalid file %s", file.c_str()); error << "Tried to open an invalid file '" << file << "': " << result;
LOGWARN("%s", error.str().c_str());
ostringstream error; return ReturnStatus(fd, false, error);
error << "File open error [" << file << "]";
return ReturnStatus(fd, false, error.str());
} }
} }
if (files_in_use.find(filepath.GetPath()) != files_in_use.end()) { if (files_in_use.find(filepath.GetPath()) != files_in_use.end()) {
ostringstream error;
error << "Image file '" << file << "' is already in use"; 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()); files_in_use.insert(filepath.GetPath());
@ -627,30 +661,30 @@ bool ProcessCmd(int fd, const PbCommand &command)
// Re-map the controller // Re-map the controller
bool status = MapController(map); bool status = MapController(map);
if (status) { 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? // Does the controller exist?
if (ctrl[id] == NULL) { 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? // Does the unit exist?
pUnit = disk[id * UnitNum + un]; pUnit = disk[id * UnitNum + un];
if (pUnit == NULL) { 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 // Disconnect Command
if (cmd == DETACH) { 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 // Free the existing unit
map[id * UnitNum + un] = NULL; map[id * UnitNum + un] = NULL;
@ -662,54 +696,69 @@ bool ProcessCmd(int fd, const PbCommand &command)
// Re-map the controller // Re-map the controller
bool status = MapController(map); 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 // Only MOs or CDs may be inserted/ejected, only MOs, CDs or hard disks may be protected
if (!pUnit->IsMo() && !pUnit->IsCdRom()) { if ((cmd == INSERT || cmd == EJECT) && !pUnit->IsRemovable()) {
LOGWARN("rasctl sent an Insert/Eject/Protect command (%d) for incompatible type %s", cmd, pUnit->GetID().c_str()); LOGWARN("%s requested for incompatible type %s", PbOperation_Name(cmd).c_str(), pUnit->GetID().c_str());
ostringstream error; error << "Operation denied (Device type " << pUnit->GetID().c_str() << " isn't removable)";
error << "Operation denied (Device type " << type_str << " isn't removable)"; return ReturnStatus(fd, false, error);
return ReturnStatus(fd, false, error.str()); }
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) { switch (cmd) {
case INSERT: case INSERT:
if (params.empty()) {
return ReturnStatus(fd, false, "Missing filename");
}
filepath.SetPath(params.c_str()); 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)) { result = pUnit->Open(filepath);
ostringstream error; if (result) {
error << "File open error [" << params << "]"; // If the file does not exist search for it in the default image folder
string default_file = default_image_folder + "/" + params;
return ReturnStatus(fd, false, error.str()); 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; break;
case EJECT: case EJECT:
LOGINFO("rasctl commanded eject for %s ID: %d UN: %d", pUnit->GetID().c_str(), id, un); LOGINFO("Eject requested for %s ID: %d UN: %d", pUnit->GetID().c_str(), id, un);
pUnit->Eject(TRUE); pUnit->Eject(true);
break; break;
case PROTECT: case PROTECT:
if (!pUnit->IsMo()) { LOGINFO("Write protection requested for %s ID: %d UN: %d", pUnit->GetID().c_str(), id, un);
LOGWARN("rasctl sent an invalid PROTECT command 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)"); case UNPROTECT:
} LOGINFO("Write unprotection requested for %s ID: %d UN: %d", pUnit->GetID().c_str(), id, un);
LOGINFO("rasctl is setting write protect to %d for %s ID: %d UN: %d",!pUnit->IsWriteP(), pUnit->GetID().c_str(), id, un); pUnit->WriteP(false);
pUnit->WriteP(!pUnit->IsWriteP());
break; break;
default: default:
ostringstream error; error << "Received unknown command: " << PbOperation_Name(cmd);
error << "Received unknown command from rasctl: " << PbOperation_Name(cmd);
LOGWARN("%s", error.str().c_str()); 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) { bool has_suffix(const string& filename, const string& suffix) {
@ -788,32 +837,6 @@ bool ParseArgument(int argc, char* argv[], int& port)
break; 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; int un = 0;
if (is_sasi) { if (is_sasi) {
un = id % UnitNum; un = id % UnitNum;
@ -825,8 +848,7 @@ bool ParseArgument(int argc, char* argv[], int& port)
command.set_id(id); command.set_id(id);
command.set_un(un); command.set_un(un);
command.set_cmd(ATTACH); command.set_cmd(ATTACH);
command.set_type(type); command.set_params(optarg);
command.set_params(path);
if (!ProcessCmd(-1, command)) { if (!ProcessCmd(-1, command)) {
return false; return false;
} }
@ -920,7 +942,7 @@ static void *MonThread(void *param)
if (!status) { if (!status) {
ostringstream error; ostringstream error;
error << "Invalid log level: " << command.params(); error << "Invalid log level: " << command.params();
ReturnStatus(fd, false, error.str()); ReturnStatus(fd, false, error);
} }
else { else {
ReturnStatus(fd); ReturnStatus(fd);
@ -1003,16 +1025,18 @@ int main(int argc, char* argv[])
int ret = 0; int ret = 0;
int port = 6868; int port = 6868;
if (!InitBusAndDisks()) { InitDisks();
ret = EPERM;
goto init_exit;
}
if (!ParseArgument(argc, argv, port)) { if (!ParseArgument(argc, argv, port)) {
ret = EINVAL; ret = EINVAL;
goto err_exit; goto err_exit;
} }
if (!InitBus()) {
ret = EPERM;
goto init_exit;
}
if (!InitService(port)) { if (!InitService(port)) {
ret = EPERM; ret = EPERM;
goto init_exit; goto init_exit;

View File

@ -18,13 +18,14 @@ enum PbDeviceType {
enum PbOperation { enum PbOperation {
NONE = 0; NONE = 0;
SERVER_INFO = 1; SERVER_INFO = 1;
LIST = 2; LOG_LEVEL = 2;
ATTACH = 3; LIST = 3;
DETACH = 4; ATTACH = 4;
INSERT = 5; DETACH = 5;
EJECT = 6; INSERT = 6;
PROTECT = 7; EJECT = 7;
LOG_LEVEL = 8; PROTECT = 8;
UNPROTECT = 9;
} }
// Commands rascsi can execute // Commands rascsi can execute
@ -42,14 +43,23 @@ message PbResult {
string msg = 2; string msg = 2;
} }
// The device meta data // The device meta data
message PbDevice { message PbDevice {
int32 id = 1; int32 id = 1;
int32 un = 2; int32 un = 2;
string type = 3; PbDeviceType type = 3;
string file = 4; string file = 4;
bool removable = 5; bool read_only = 5;
bool read_only = 6; // 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 { message PbDevices {

View File

@ -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); memcpy(&server.sin_addr.s_addr, host->h_addr, host->h_length);
if (connect(fd, (struct sockaddr *)&server, sizeof(struct sockaddr_in)) < 0) { if (connect(fd, (struct sockaddr *)&server, sizeof(struct sockaddr_in)) < 0) {
ostringstream s; ostringstream error;
s << "Can't connect to rascsi process on host '" << hostname << "', port " << port; error << "Can't connect to rascsi process on host '" << hostname << "', port " << port;
throw ioexception(s.str()); throw ioexception(error.str());
} }
SerializeMessage(fd, command); SerializeMessage(fd, command);
@ -65,7 +65,7 @@ int SendCommand(const string& hostname, int port, const PbCommand& command)
close(fd); close(fd);
} }
return -1; exit(fd < 0 ? ENOTCONN : -1);
} }
return fd; return fd;
@ -76,32 +76,26 @@ int SendCommand(const string& hostname, int port, const PbCommand& command)
// Receive command result // Receive command result
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
bool ReceiveResult(int fd) { bool ReceiveResult(int fd)
bool status = true; {
try { try {
PbResult result; PbResult result;
DeserializeMessage(fd, result); DeserializeMessage(fd, result);
close(fd);
status = result.status(); if (!result.status()) {
if (status) { throw ioexception(result.msg());
cerr << result.msg();
}
else {
cout << result.msg();
} }
cout << result.msg() << endl;
} }
catch(const ioexception& e) { catch(const ioexception& e) {
cerr << "Error: " << e.getmsg() << endl; cerr << "Error: " << e.getmsg() << endl;
// Fall through return false;
status = false;
} }
close(fd); return true;
return status;
} }
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
@ -116,9 +110,6 @@ void CommandList(const string& hostname, int port)
command.set_cmd(LIST); command.set_cmd(LIST);
int fd = SendCommand(hostname.c_str(), port, command); int fd = SendCommand(hostname.c_str(), port, command);
if (fd < 0) {
exit(ENOTCONN);
}
PbDevices devices; PbDevices devices;
try { try {
@ -144,12 +135,7 @@ void CommandLogLevel(const string& hostname, int port, const string& log_level)
command.set_params(log_level); command.set_params(log_level);
int fd = SendCommand(hostname.c_str(), port, command); int fd = SendCommand(hostname.c_str(), port, command);
if (fd < 0) {
exit(ENOTCONN);
}
ReceiveResult(fd); ReceiveResult(fd);
close(fd); close(fd);
} }
@ -159,10 +145,6 @@ void CommandServerInfo(const string& hostname, int port)
command.set_cmd(SERVER_INFO); command.set_cmd(SERVER_INFO);
int fd = SendCommand(hostname.c_str(), port, command); int fd = SendCommand(hostname.c_str(), port, command);
if (fd < 0) {
exit(ENOTCONN);
}
PbServerInfo serverInfo; PbServerInfo serverInfo;
try { try {
@ -178,18 +160,18 @@ void CommandServerInfo(const string& hostname, int port)
close(fd); close(fd);
cout << "rascsi version: " << serverInfo.rascsi_version() << endl; cout << "rascsi server version: " << serverInfo.rascsi_version() << endl;
if (!serverInfo.available_log_levels_size()) { if (!serverInfo.available_log_levels_size()) {
cout << " No log level settings available" << endl; cout << " No log level settings available" << endl;
} }
else { 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++) { for (int i = 0; i < serverInfo.available_log_levels_size(); i++) {
cout << " " << serverInfo.available_log_levels(i) << endl; 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; 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 << "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 << " where ID := {0|1|2|3|4|5|6|7}" << endl;
cerr << " UNIT := {0|1} default setting is 0." << 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 << " TYPE := {hd|mo|cd|bridge|daynaport}" << endl;
cerr << " FILE := image file path" << endl; cerr << " FILE := image file path" << endl;
cerr << " HOST := rascsi host to connect to, default is 'localhost'" << 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; int port = 6868;
string params; string params;
opterr = 0; 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) { switch (opt) {
case 'i': case 'i':
id = optarg[0] - '0'; id = optarg[0] - '0';
@ -280,6 +262,10 @@ int main(int argc, char* argv[])
case 'p': case 'p':
cmd = PROTECT; cmd = PROTECT;
break; break;
case 'u':
cmd = UNPROTECT;
break;
} }
break; break;
@ -351,8 +337,6 @@ int main(int argc, char* argv[])
} }
} }
PbCommand command;
if (cmd == LOG_LEVEL) { if (cmd == LOG_LEVEL) {
CommandLogLevel(hostname, port, params); CommandLogLevel(hostname, port, params);
exit(0); exit(0);
@ -369,47 +353,8 @@ int main(int argc, char* argv[])
exit(0); 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 // Generate the command and send it
PbCommand command;
command.set_id(id); command.set_id(id);
command.set_un(un); command.set_un(un);
command.set_cmd(cmd); command.set_cmd(cmd);
@ -419,10 +364,6 @@ int main(int argc, char* argv[])
} }
int fd = SendCommand(hostname, port, command); int fd = SendCommand(hostname, port, command);
if (fd < 0) {
exit(ENOTCONN);
}
bool status = ReceiveResult(fd); bool status = ReceiveResult(fd);
close(fd); close(fd);

View File

@ -86,7 +86,8 @@ int ReadNBytes(int fd, uint8_t *buf, int n)
// List devices // List devices
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
string ListDevices(const PbDevices& devices) { string ListDevices(const PbDevices& devices)
{
ostringstream s; ostringstream s;
if (devices.devices_size()) { if (devices.devices_size()) {
@ -102,11 +103,64 @@ string ListDevices(const PbDevices& devices) {
for (int i = 0; i < devices.devices_size() ; i++) { for (int i = 0; i < devices.devices_size() ; i++) {
PbDevice device = devices.devices(i); PbDevice device = devices.devices(i);
s << "| " << device.id() << " | " << device.un() << " | " << device.type() << " | " s << "| " << device.id() << " | " << device.un() << " | " << MapTypeToId(device.type()) << " | "
<< device.file() << (device.read_only() ? " (WRITEPROTECT)" : "") << endl; << (device.file().empty() ? "NO MEDIA" : device.file())
<< (device.read_only() ? " (WRITEPROTECT)" : "") << endl;
} }
s << "+----+----+------+-------------------------------------" << endl; s << "+----+----+------+-------------------------------------" << endl;
return s.str(); 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 "????";
}
}

View File

@ -19,5 +19,7 @@ void SerializeMessage(int, const google::protobuf::MessageLite&);
void DeserializeMessage(int, google::protobuf::MessageLite&); void DeserializeMessage(int, google::protobuf::MessageLite&);
int ReadNBytes(int, uint8_t *, int); int ReadNBytes(int, uint8_t *, int);
string ListDevices(const rascsi_interface::PbDevices&); string ListDevices(const rascsi_interface::PbDevices&);
rascsi_interface::PbDeviceType MapIdToType(const string&, bool);
string MapTypeToId(const rascsi_interface::PbDeviceType);
#endif #endif