diff --git a/doc/rascsi.1 b/doc/rascsi.1
index b4cd6e07..3b86b8e9 100644
--- a/doc/rascsi.1
+++ b/doc/rascsi.1
@@ -3,8 +3,8 @@
rascsi \- Emulates SCSI devices using the Raspberry Pi GPIO pins
.SH SYNOPSIS
.B rascsi
-[\fB\-f\f® \fIFOLDER\fR]
-[\fB\-g\f® \fILOG_LEVEL\fR]
+[\fB\-F\f® \fIFOLDER\fR]
+[\fB\-L\f® \fILOG_LEVEL\fR]
[\fB\-h\fR]
[\fB\-n\fR \fIVENDOR:PRODUCT:REVISION\fR]
[\fB\-p\f® \fIPORT\fR]
@@ -46,10 +46,10 @@ To quit RaSCSI, press Control + C. If it is running in the background, you can k
.BR \-b\fI " " \fIBLOCK_SIZE
The optional block size. For SCSI drives 512, 1024, 2048 or 4096 bytes, default size is 512 bytes. For SASI drives 256 or 1024 bytes, default is 256 bytes.
.TP
-.BR \-f\fI " " \fIFOLDER
+.BR \-F\fI " " \fIFOLDER
The default folder for image files. For files in this folder no absolute path needs to be specified. The initial default folder is '~/images'.
.TP
-.BR \-g\fI " " \fILOG_LEVEL
+.BR \-L\fI " " \fILOG_LEVEL
The rascsi log level (trace, debug, info, warn, err, critical, off). The default log level is 'info'.
.TP
.BR \-h\fI " " \fI
diff --git a/doc/rascsi_man_page.txt b/doc/rascsi_man_page.txt
index 7a1d03ca..cbf0d294 100644
--- a/doc/rascsi_man_page.txt
+++ b/doc/rascsi_man_page.txt
@@ -1,97 +1,129 @@
!! ------ THIS FILE IS AUTO_GENERATED! DO NOT MANUALLY UPDATE!!!
-!! ------ The native file is rascsi.1. Re-run 'make docs' after updating
-
-
-rascsi(1) General Commands Manual rascsi(1)
+!! ------ The native file is rascsi.1. Re-run 'make docs' after updating\n\n
+rascsi(1) General Commands Manual rascsi(1)
NAME
rascsi - Emulates SCSI devices using the Raspberry Pi GPIO pins
SYNOPSIS
- rascsi [-f[u00AE] FOLDER] [-g[u00AE] LOG_LEVEL] [-h] [-n VENDOR:PRODUCT:REVISION] [-p[u00AE] PORT] [-r RESERVED_IDS] [-n TYPE] [-v] [-IDn FILE] [-HDn FILE]...
+ rascsi [-F[u00AE] FOLDER] [-L[u00AE] LOG_LEVEL] [-h] [-n VENDOR:PROD‐
+ UCT:REVISION] [-p[u00AE] PORT] [-r RESERVED_IDS] [-n TYPE] [-v] [-IDn
+ FILE] [-HDn FILE]...
DESCRIPTION
rascsi Emulates SCSI devices using the Raspberry Pi GPIO pins.
- In the arguments to RaSCSI, one or more SCSI (-IDn) or SASI (-HDn) devices can be specified. The number (n) after the ID or HD identifier specifies the ID number for that device.
- For SCSI: The ID is limited from 0-7. However, typically SCSI ID 7 is reserved for the "initiator" (the host computer). Note that SASI is considered rare and only used on very
- early Sharp X68000 computers.
+ In the arguments to RaSCSI, one or more SCSI (-IDn) or SASI (-HDn) de‐
+ vices can be specified. The number (n) after the ID or HD identifier
+ specifies the ID number for that device. For SCSI: The ID is limited
+ from 0-7. However, typically SCSI ID 7 is reserved for the "initiator"
+ (the host computer). Note that SASI is considered rare and only used on
+ very early Sharp X68000 computers.
- RaSCSI will determine the type of device based upon the file extension of the FILE argument.
- hdf: SASI Hard Disk image (XM6 SASI HD image - typically only used with X68000)
+ RaSCSI will determine the type of device based upon the file extension
+ of the FILE argument.
+ hdf: SASI Hard Disk image (XM6 SASI HD image - typically only used
+ with X68000)
hds: SCSI Hard Disk image (generic, non-removable)
hdr: SCSI Hard Disk image (generic, removable)
hdn: SCSI Hard Disk image (NEC GENUINE)
hdi: SCSI Hard Disk image (Anex86 HD image)
nhd: SCSI Hard Disk image (T98Next HD image)
- hda: SCSI Hard Disk image (APPLE GENUINE - typically used with Mac SCSI emulation)
- mos: SCSI Magneto-optical image (XM6 SCSI MO image - typically only used with X68000)
+ hda: SCSI Hard Disk image (APPLE GENUINE - typically used with Mac
+ SCSI emulation)
+ mos: SCSI Magneto-optical image (XM6 SCSI MO image - typically only
+ used with X68000)
iso: SCSI CD-ROM image (ISO 9660 image)
- For example, if you want to specify an Apple-compatible HD image on ID 0, you can use the following command:
+ For example, if you want to specify an Apple-compatible HD image on ID
+ 0, you can use the following command:
sudo rascsi -ID0 /path/to/drive/hdimage.hda
- Once RaSCSI starts, it will open a socket (default port is 6868) to allow external management commands. If another process is using the rascsi port, RaSCSI will terminate, since
- it is likely another instance of RaSCSI. Once RaSCSI has initialized, the rasctl utility can be used to send commands.
+ Once RaSCSI starts, it will open a socket (default port is 6868) to al‐
+ low external management commands. If another process is using the
+ rascsi port, RaSCSI will terminate, since it is likely another instance
+ of RaSCSI. Once RaSCSI has initialized, the rasctl utility can be used
+ to send commands.
- To quit RaSCSI, press Control + C. If it is running in the background, you can kill it using an INT signal.
+ To quit RaSCSI, press Control + C. If it is running in the background,
+ you can kill it using an INT signal.
OPTIONS
-b BLOCK_SIZE
- The optional block size. For SCSI drives 512, 1024, 2048 or 4096 bytes, default size is 512 bytes. For SASI drives 256 or 1024 bytes, default is 256 bytes.
+ The optional block size. For SCSI drives 512, 1024, 2048 or 4096
+ bytes, default size is 512 bytes. For SASI drives 256 or 1024
+ bytes, default is 256 bytes.
- -f FOLDER
- The default folder for image files. For files in this folder no absolute path needs to be specified. The initial default folder is '~/images'.
+ -F FOLDER
+ The default folder for image files. For files in this folder no
+ absolute path needs to be specified. The initial default folder
+ is '~/images'.
- -g LOG_LEVEL
- The rascsi log level (trace, debug, info, warn, err, critical, off). The default log level is 'info'.
+ -L LOG_LEVEL
+ The rascsi log level (trace, debug, info, warn, err, critical,
+ off). The default log level is 'info'.
-h Show a help page.
-n VENDOR:PRODUCT:REVISION
- Set the vendor, product and revision for the device, to be returned with the INQUIRY data. A complete set of name components must be provided. VENDOR may have up to 8,
- PRODUCT up to 16, REVISION up to 4 characters. Padding with blanks to the maxium length is automatically applied. Once set the name of a device cannot be changed.
+ Set the vendor, product and revision for the device, to be re‐
+ turned with the INQUIRY data. A complete set of name components
+ must be provided. VENDOR may have up to 8, PRODUCT up to 16, RE‐
+ VISION up to 4 characters. Padding with blanks to the maxium
+ length is automatically applied. Once set the name of a device
+ cannot be changed.
-p PORT
The rascsi server port, default is 6868.
-r RESERVED_IDS
- Comma-separated list of IDs to reserve. -p TYPE The optional case-insensitive device type (SAHD, SCHD, SCRM, SCCD, SCMO, SCBR, SCDP). If no type is specified for devices
- that support an image file, rascsi tries to derive the type from the file extension.
+ Comma-separated list of IDs to reserve. -p TYPE The optional
+ case-insensitive device type (SAHD, SCHD, SCRM, SCCD, SCMO,
+ SCBR, SCDP). If no type is specified for devices that support an
+ image file, rascsi tries to derive the type from the file exten‐
+ sion.
-v Display the rascsi version.
-IDn FILE
n is the SCSI ID number (0-7)
- FILE is the name of the image file to use for the SCSI device. For devices that do not support an image file (SCBR, SCDP) a dummy name must be provided.
+ FILE is the name of the image file to use for the SCSI device.
+ For devices that do not support an image file (SCBR, SCDP) a
+ dummy name must be provided.
-HDn FILE
n is the SASI ID number (0-15)
FILE is the name of the image file to use for the SASI device.
- Note: SASI usage is rare, and is typically limited to early Unix workstations and Sharp X68000 systems.
+ Note: SASI usage is rare, and is typically limited to early Unix
+ workstations and Sharp X68000 systems.
EXAMPLES
Launch RaSCSI with no emulated drives attached:
rascsi
- Launch RaSCSI with an Apple hard drive image as ID 0 and a CD-ROM as ID 2
+ Launch RaSCSI with an Apple hard drive image as ID 0 and a CD-ROM as ID
+ 2
rascsi -ID0 /path/to/harddrive.hda -ID2 /path/to/cdimage.iso
- Launch RaSCSI with a removable SCSI drive image as ID 0 and the raw device file /dev/hdb (e.g. a USB stick) and a DaynaPort network adapter as ID 6:
+ Launch RaSCSI with a removable SCSI drive image as ID 0 and the raw de‐
+ vice file /dev/hdb (e.g. a USB stick) and a DaynaPort network adapter
+ as ID 6:
rascsi -ID0 -t scrm /dev/hdb -ID6 -t scdp DUMMY_FILENAME
To create an empty, 100MB HD image, use the following command:
dd if=/dev/zero of=/path/to/newimage.hda bs=512 count=204800
- In case the fallocate command is available a much faster alternative to the dd command is:
+ In case the fallocate command is available a much faster alternative to
+ the dd command is:
fallocate -l 104857600 /path/to/newimage.hda
SEE ALSO
rasctl(1), scsimon(1)
- Full documentation is available at:
+ Full documentation is available at:
+
- rascsi(1)
+ rascsi(1)
diff --git a/doc/rasctl.1 b/doc/rasctl.1
index f9c80f06..0268e62b 100644
--- a/doc/rasctl.1
+++ b/doc/rasctl.1
@@ -3,12 +3,16 @@
rasctl \- Sends management commands to the rascsi process
.SH SYNOPSIS
.B rasctl
+\fB\-L\fR |
\fB\-e\fR |
\fB\-k\fR |
\fB\-l\fR |
\fB\-s\fR |
-\fB\-y\fR |
-[\fB\-d\fR \fIIMAGE_FOLDER\fR]
+\fB\-T\fR |
+[\fB\-F\fR \fIIMAGE_FOLDER\fR]
+[\fB\-C\fR \fIFILENAME:FILESIZE\fR]
+[\fB\-x\fR \fICURRENT_NAME:NEW_NAME\fR]
+[\fB\-R\fR \fICURRENT_NAME:NEW_NAME\fR]
[\fB\-g\fR \fILOG_LEVEL\fR]
[\fB\-h\fR \fIHOST\fR]
[\fB\-p\fR \fIPORT\fR]
@@ -32,13 +36,13 @@ Note: The command and type arguments are case insensitive. Only the first letter
.SH OPTIONS
.TP
-.BR \-a\fI " "\fIFILENAME:FILESIZE
+.BR \-C\fI " "\fIFILENAME:FILESIZE
Create an image file in the default image folder with the specified name and size in bytes.
.TP
-.BR \-d\fI " "\fIIMAGE_FOLDER
+.BR \-F\fI " "\fIIMAGE_FOLDER
Set the default image folder.
.TP
-.BR \-g\fI " "\fILOG_LEVEL
+.BR \-L\fI " "\fILOG_LEVEL
Set the rascsi log level (trace, debug, info, warn, err, critical, off).
.TP
.BR \-h\fI " " \fIHOST
@@ -47,13 +51,13 @@ The rascsi host to connect to, default is 'localhost'.
.BR \-e\fI
List all images files in the default image folder.
.TP
-.BR \-k\fI
+.BR \-N\fI
Lists all available network interfaces provided that they are up.
.TP
.BR \-l\fI
List all of the devices that are currently being emulated by RaSCSI, as well as their current status.
.TP
-.BR \-m\fI " "\fICURRENT_NAME:NEW_NAME
+.BR \-R\fI " "\fICURRENT_NAME:NEW_NAME
Rename an image file in the default image folder.
.TP
.BR \-p\fI " " \fIPORT
@@ -65,13 +69,13 @@ Comma-separated list of IDs to reserve.
.BR \-s\fI
Display server-side settings like available images or supported device types.
.TP
-.BR \-y\fI
+.BR \-T\fI
Display all device types and their properties.
.TP
.BR \-v\fI " " \fI
Display the rascsi version.
.TP
-.BR \-w\fI " "\fIFILENAME
+.BR \-D\fI " "\fIFILENAME
Delete an image file in the default image folder.
.TP
.BR \-x\fI " "\fICURRENT_NAME:NEW_NAME
diff --git a/doc/rasctl_man_page.txt b/doc/rasctl_man_page.txt
index 670860d7..49e38e93 100644
--- a/doc/rasctl_man_page.txt
+++ b/doc/rasctl_man_page.txt
@@ -6,31 +6,32 @@ NAME
rasctl - Sends management commands to the rascsi process
SYNOPSIS
- rasctl -e | -k | -l | -s | -y | [-d IMAGE_FOLDER] [-g LOG_LEVEL] [-h
- HOST] [-p PORT] [-r RESERVED_IDS] [-v] -i ID [-c CMD] [-f FILE|PARAM]
- [-n NAME] [-t TYPE] [-u UNIT]
+ rasctl -L | -e | -k | -l | -s | -T | [-F IMAGE_FOLDER] [-C FILE‐
+ NAME:FILESIZE] [-x CURRENT_NAME:NEW_NAME] [-R CURRENT_NAME:NEW_NAME]
+ [-g LOG_LEVEL] [-h HOST] [-p PORT] [-r RESERVED_IDS] [-v] -i ID [-c
+ CMD] [-f FILE|PARAM] [-n NAME] [-t TYPE] [-u UNIT]
DESCRIPTION
- rasctl Sends commands to the rascsi process to make configuration ad‐
+ rasctl Sends commands to the rascsi process to make configuration ad‐
justments at runtime or to check the status of the devices.
Either the -i or -l option should be specified at one time. Not both.
You do NOT need root privileges to use rasctl.
- Note: The command and type arguments are case insensitive. Only the
+ Note: The command and type arguments are case insensitive. Only the
first letter of the command/type is evaluated by the tool.
OPTIONS
- -a FILENAME:FILESIZE
+ -C FILENAME:FILESIZE
Create an image file in the default image folder with the speci‐
fied name and size in bytes.
- -d IMAGE_FOLDER
+ -F IMAGE_FOLDER
Set the default image folder.
- -g LOG_LEVEL
- Set the rascsi log level (trace, debug, info, warn, err, criti‐
+ -L LOG_LEVEL
+ Set the rascsi log level (trace, debug, info, warn, err, criti‐
cal, off).
-h HOST
@@ -38,13 +39,13 @@ OPTIONS
-e List all images files in the default image folder.
- -k Lists all available network interfaces provided that they are
+ -N Lists all available network interfaces provided that they are
up.
- -l List all of the devices that are currently being emulated by
+ -l List all of the devices that are currently being emulated by
RaSCSI, as well as their current status.
- -m CURRENT_NAME:NEW_NAME
+ -R CURRENT_NAME:NEW_NAME
Rename an image file in the default image folder.
-p PORT
@@ -53,14 +54,14 @@ OPTIONS
-r RESERVED_IDS
Comma-separated list of IDs to reserve.
- -s Display server-side settings like available images or supported
+ -s Display server-side settings like available images or supported
device types.
- -y Display all device types and their properties.
+ -T Display all device types and their properties.
-v Display the rascsi version.
- -w FILENAME
+ -D FILENAME
Delete an image file in the default image folder.
-x CURRENT_NAME:NEW_NAME
@@ -73,7 +74,7 @@ OPTIONS
d(etach): Detach disk
i(nsert): Insert media (removable media devices only)
e(ject): Eject media (removable media devices only)
- p(rotect): Write protect the medium (not for CD-ROMs, which
+ p(rotect): Write protect the medium (not for CD-ROMs, which
are always read-only)
u(nprotect): Remove write protection from the medium (not for
CD-ROMs, which are always read-only)
@@ -83,18 +84,18 @@ OPTIONS
-b BLOCK_SIZE
The optional block size. For SCSI drives 512, 1024, 2048 or 4096
- bytes, default size is 512 bytes. For SASI drives 256 or 1024
+ bytes, default size is 512 bytes. For SASI drives 256 or 1024
bytes, default is 256 bytes.
-f FILE|PARAM
Device-specific: Either a path to a disk image file, or a param‐
- eter for a non-disk device. See the rascsi(1) man page for per‐
+ eter for a non-disk device. See the rascsi(1) man page for per‐
mitted file types.
-t TYPE
- Specifies the device type. This type overrides the type derived
+ Specifies the device type. This type overrides the type derived
from the file extension of the specified image. See the
- rascsi(1) man page for the available device types. For some
+ rascsi(1) man page for the available device types. For some
types there are shortcuts (only the first letter is required):
hd: SCSI hard disk drive
rm: SCSI removable media drive
@@ -104,16 +105,16 @@ OPTIONS
daynaport: DaynaPORT network adapter
-n VENDOR:PRODUCT:REVISION
- The vendor, product and revision for the device, to be returned
+ The vendor, product and revision for the device, to be returned
with the INQUIRY data. A complete set of name components must be
provided. VENDOR may have up to 8, PRODUCT up to 16, REVISION up
to 4 characters. Padding with blanks to the maxium length is au‐
- tomatically applied. Once set the name of a device cannot be
+ tomatically applied. Once set the name of a device cannot be
changed.
-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
diff --git a/src/raspberrypi/Makefile b/src/raspberrypi/Makefile
index 41bfcdb5..c3fee54c 100644
--- a/src/raspberrypi/Makefile
+++ b/src/raspberrypi/Makefile
@@ -26,8 +26,8 @@ else
CXXFLAGS += -O3 -Wall -Werror -DNDEBUG
BUILD_TYPE = Release
endif
-CFLAGS += -iquote . -MD -MP
-CXXFLAGS += -std=c++17 -Wno-psabi -iquote . -MD -MP
+CFLAGS += -iquote . -D_FILE_OFFSET_BITS=64 -MD -MP
+CXXFLAGS += -std=c++17 -Wno-psabi -iquote . -D_FILE_OFFSET_BITS=64 -MD -MP
## EXTRA_FLAGS : Can be used to pass special purpose flags
CFLAGS += $(EXTRA_FLAGS)
diff --git a/src/raspberrypi/devices/device_factory.cpp b/src/raspberrypi/devices/device_factory.cpp
index ce773e31..8fbef222 100644
--- a/src/raspberrypi/devices/device_factory.cpp
+++ b/src/raspberrypi/devices/device_factory.cpp
@@ -72,8 +72,11 @@ DeviceFactory& DeviceFactory::instance()
return instance;
}
-Device *DeviceFactory::CreateDevice(PbDeviceType type, const string& filename, const string& ext)
+Device *DeviceFactory::CreateDevice(PbDeviceType type, const string& filename, const string& extension)
{
+ string ext = extension;
+ std::transform(ext.begin(), ext.end(), ext.begin(), [](unsigned char c){ return std::tolower(c); });
+
// If no type was specified try to derive the device type from the filename and extension
if (type == UNDEFINED) {
if (ext == "hdf") {
diff --git a/src/raspberrypi/rascsi.cpp b/src/raspberrypi/rascsi.cpp
index d7be00f0..09c42a45 100644
--- a/src/raspberrypi/rascsi.cpp
+++ b/src/raspberrypi/rascsi.cpp
@@ -32,6 +32,7 @@
#include "spdlog/sinks/stdout_color_sinks.h"
#include
#include
+#include
#include
#include
#include
@@ -117,7 +118,6 @@ void Banner(int argc, char* argv[])
FPRT(stdout," hdn : SCSI HD image (NEC GENUINE)\n");
FPRT(stdout," hdi : SCSI HD image (Anex86 HD image)\n");
FPRT(stdout," nhd : SCSI HD image (T98Next HD image)\n");
- FPRT(stdout," hda : SCSI HD image (APPLE GENUINE)\n");
FPRT(stdout," mos : SCSI MO image (MO image)\n");
FPRT(stdout," iso : SCSI CD image (ISO 9660 image)\n");
@@ -527,6 +527,45 @@ void GetAllDeviceTypeProperties(PbDeviceTypesInfo& device_types_info)
GetDeviceTypeProperties(device_types_info, SCDP);
}
+void GetAvailableImages(PbImageFilesInfo& image_files_info)
+{
+ image_files_info.set_default_image_folder(default_image_folder);
+
+ // filesystem::directory_iterator cannot be used because libstdc++ 8.3.0 does not support big files
+ DIR *d = opendir(default_image_folder.c_str());
+ if (d) {
+ struct dirent *dir;
+ while ((dir = readdir(d))) {
+ if (dir->d_type == DT_REG || dir->d_type == DT_LNK || dir->d_type == DT_BLK) {
+ string filename = default_image_folder + "/" + dir->d_name;
+
+ struct stat st;
+ if (dir->d_type == DT_REG && !stat(filename.c_str(), &st)) {
+ if (!st.st_size) {
+ LOGTRACE("File '%s' in image folder '%s' has a size of 0 bytes", dir->d_name,
+ default_image_folder.c_str());
+ continue;
+ }
+
+ if (st.st_size % 512) {
+ LOGTRACE("Size of file '%s' in image folder '%s' is not a multiple of 512", dir->d_name,
+ default_image_folder.c_str());
+ continue;
+ }
+ } else if (dir->d_type == DT_LNK && stat(filename.c_str(), &st)) {
+ LOGTRACE("Symlink '%s' in image folder '%s' is broken", dir->d_name,
+ default_image_folder.c_str());
+ continue;
+ }
+
+ GetImageFile(image_files_info.add_image_files(), dir->d_name);
+ }
+ }
+
+ closedir(d);
+ }
+}
+
void GetAvailableImages(PbServerInfo& server_info)
{
PbImageFilesInfo *image_files_info = new PbImageFilesInfo();
@@ -534,26 +573,7 @@ void GetAvailableImages(PbServerInfo& server_info)
image_files_info->set_default_image_folder(default_image_folder);
- if (!access(default_image_folder.c_str(), F_OK)) {
- for (const auto& entry : filesystem::directory_iterator(default_image_folder, filesystem::directory_options::skip_permission_denied)) {
- if (entry.is_regular_file() && entry.file_size() && !(entry.file_size() & 0x1ff)) {
- GetImageFile(image_files_info->add_image_files(), entry.path().filename());
- }
- }
- }
-}
-
-void GetAvailableImages(PbImageFilesInfo& image_files_info)
-{
- image_files_info.set_default_image_folder(default_image_folder);
-
- if (!access(default_image_folder.c_str(), F_OK)) {
- for (const auto& entry : filesystem::directory_iterator(default_image_folder, filesystem::directory_options::skip_permission_denied)) {
- if (entry.is_regular_file() && entry.file_size() && !(entry.file_size() & 0x1ff)) {
- GetImageFile(image_files_info.add_image_files(), entry.path().filename());
- }
- }
- }
+ GetAvailableImages(*image_files_info);
}
void GetNetworkInterfacesInfo(PbNetworkInterfacesInfo& network_interfaces_info)
@@ -687,7 +707,13 @@ bool SetDefaultImageFolder(const string& f)
// If a relative path is specified the path is assumed to be relative to the user's home directory
if (folder[0] != '/') {
- const passwd *passwd = getpwuid(getuid());
+ int uid = getuid();
+ const char *sudo_user = getenv("SUDO_UID");
+ if (sudo_user) {
+ uid = stoi(sudo_user);
+ }
+
+ const passwd *passwd = getpwuid(uid);
if (passwd) {
folder = passwd->pw_dir;
folder += "/";
@@ -742,7 +768,7 @@ string SetReservedIds(const string& ids)
s << id;
}
- LOGINFO("Reserved IDs set to: %s", s.str().c_str());
+ LOGINFO("Reserved ID(s) set to %s", s.str().c_str());
}
else {
LOGINFO("Cleared reserved IDs");
@@ -864,7 +890,7 @@ bool DeleteImage(int fd, const PbCommand& command)
return ReturnStatus(fd, false, "Can't delete image file '" + filename + "': " + string(strerror(errno)));
}
- LOGINFO("%s", string("Deleted image file '" + filename + "'").c_str());
+ LOGINFO("Deleted image file '%s'", filename.c_str());
return ReturnStatus(fd);
}
@@ -908,7 +934,7 @@ bool RenameImage(int fd, const PbCommand& command)
return ReturnStatus(fd, false, "Can't rename image file '" + from + "' to '" + to + "': " + string(strerror(errno)));
}
- LOGINFO("%s", string("Renamed image file '" + from + "' to '" + to + "'").c_str());
+ LOGINFO("Renamed image file '%s' to '%s'", from.c_str(), to.c_str());
return ReturnStatus(fd);
}
@@ -943,24 +969,35 @@ bool CopyImage(int fd, const PbCommand& command)
from = default_image_folder + "/" + from;
to = default_image_folder + "/" + to;
- struct stat st;
- if (!stat(to.c_str(), &st)) {
+ struct stat st_dst;
+ if (!stat(to.c_str(), &st_dst)) {
return ReturnStatus(fd, false, "Image file '" + to + "' already exists");
}
+ struct stat st_src;
+ if (lstat(from.c_str(), &st_src)) {
+ return ReturnStatus(fd, false, "Can't access source image file '" + from + "': " + string(strerror(errno)));
+ }
+
+ // Symbolic links need a special handling
+ if ((st_src.st_mode & S_IFMT) == S_IFLNK) {
+ if (symlink(filesystem::read_symlink(from).c_str(), to.c_str())) {
+ return ReturnStatus(fd, false, "Can't copy symlink '" + from + "': " + string(strerror(errno)));
+ }
+
+ LOGINFO("Copied symlink '%s' to '%s'", from.c_str(), to.c_str());
+
+ return ReturnStatus(fd);
+ }
+
int fd_src = open(from.c_str(), O_RDONLY, 0);
if (fd_src == -1) {
return ReturnStatus(fd, false, "Can't open source image file '" + from + "': " + string(strerror(errno)));
}
- struct stat st_src;
- if (fstat(fd_src, &st_src) == -1) {
- return ReturnStatus(fd, false, "Can't read source image file '" + from + "': " + string(strerror(errno)));
- }
-
int fd_dst = open(to.c_str(), O_WRONLY | O_CREAT, st_src.st_mode);
if (fd_dst == -1) {
- close (fd_dst);
+ close(fd_src);
return ReturnStatus(fd, false, "Can't open destination image file '" + to + "': " + string(strerror(errno)));
}
@@ -975,7 +1012,7 @@ bool CopyImage(int fd, const PbCommand& command)
close(fd_dst);
close(fd_src);
- LOGINFO("%s", string("Copied image file '" + from + "' to '" + to + "'").c_str());
+ LOGINFO("Copied image file '%s' to '%s'", from.c_str(), to.c_str());
return ReturnStatus(fd);
}
@@ -1008,10 +1045,10 @@ bool SetImagePermissions(int fd, const PbCommand& command)
}
if (protect) {
- LOGINFO("%s", string("Protected image file '" + filename + "'").c_str());
+ LOGINFO("Protected image file '%s'", filename.c_str());
}
else {
- LOGINFO("%s", string("Unprotected image file '" + filename + "'").c_str());
+ LOGINFO("Unprotected image file '%s'", filename.c_str());
}
return ReturnStatus(fd);
@@ -1532,29 +1569,25 @@ bool ParseArgument(int argc, char* argv[], int& port)
opterr = 1;
int opt;
- while ((opt = getopt(argc, argv, "-IiHhG:g:D:d:B:b:N:n:T:t:P:p:R:r:F:f:")) != -1) {
- switch (tolower(opt)) {
+ while ((opt = getopt(argc, argv, "-IiHhb:d:n:p:r:t:D:F:L:")) != -1) {
+ switch (opt) {
+ // The three options below are kind of a compound option with two letters
case 'i':
+ case 'I':
is_sasi = false;
max_id = 7;
id = -1;
continue;
case 'h':
+ case 'H':
is_sasi = true;
max_id = 15;
id = -1;
continue;
- case 'b': {
- if (!GetAsInt(optarg, block_size)) {
- cerr << "Invalid block size " << optarg << endl;
- return false;
- }
- continue;
- }
-
- case 'd': {
+ case 'd':
+ case 'D': {
char* end;
id = strtol(optarg, &end, 10);
if (*end || id < 0 || max_id < id) {
@@ -1564,14 +1597,22 @@ bool ParseArgument(int argc, char* argv[], int& port)
continue;
}
- case 'f':
+ case 'b': {
+ if (!GetAsInt(optarg, block_size)) {
+ cerr << "Invalid block size " << optarg << endl;
+ return false;
+ }
+ continue;
+ }
+
+ case 'F':
if (!SetDefaultImageFolder(optarg)) {
cerr << "Folder '" << optarg << "' does not exist or is not accessible";
return false;
}
continue;
- case 'g':
+ case 'L':
log_level = optarg;
continue;
@@ -1739,7 +1780,7 @@ static void *MonThread(void *param)
switch(command.operation()) {
case LOG_LEVEL: {
- LOGTRACE(string("Received " + PbOperation_Name(command.operation()) + " command").c_str());
+ LOGTRACE("Received %s command", PbOperation_Name(command.operation()).c_str());
string log_level = GetParam(command, "level");
bool status = SetLogLevel(log_level);
@@ -1753,7 +1794,7 @@ static void *MonThread(void *param)
}
case DEFAULT_FOLDER: {
- LOGTRACE(string("Received " + PbOperation_Name(command.operation()) + " command").c_str());
+ LOGTRACE("Received %s command", PbOperation_Name(command.operation()).c_str());
string folder = GetParam(command, "folder");
if (folder.empty()) {
@@ -1770,7 +1811,7 @@ static void *MonThread(void *param)
}
case DEVICES_INFO: {
- LOGTRACE(string("Received " + PbOperation_Name(command.operation()) + " command").c_str());
+ LOGTRACE("Received %s command", PbOperation_Name(command.operation()).c_str());
PbResult result;
result.set_status(true);
@@ -1786,7 +1827,7 @@ static void *MonThread(void *param)
}
case DEVICE_TYPES_INFO: {
- LOGTRACE(string("Received " + PbOperation_Name(command.operation()) + " command").c_str());
+ LOGTRACE("Received %s command", PbOperation_Name(command.operation()).c_str());
PbResult result;
result.set_status(true);
@@ -1797,7 +1838,7 @@ static void *MonThread(void *param)
case SERVER_INFO: {
- LOGTRACE(string("Received " + PbOperation_Name(command.operation()) + " command").c_str());
+ LOGTRACE("Received %s command", PbOperation_Name(command.operation()).c_str());
PbResult result;
result.set_status(true);
@@ -1807,7 +1848,7 @@ static void *MonThread(void *param)
}
case IMAGE_FILES_INFO: {
- LOGTRACE(string("Received " + PbOperation_Name(command.operation()) + " command").c_str());
+ LOGTRACE("Received %s command", PbOperation_Name(command.operation()).c_str());
PbImageFilesInfo *image_files_info = new PbImageFilesInfo();
GetAvailableImages(*image_files_info);
@@ -1819,7 +1860,7 @@ static void *MonThread(void *param)
}
case NETWORK_INTERFACES_INFO: {
- LOGTRACE(string("Received " + PbOperation_Name(command.operation()) + " command").c_str());
+ LOGTRACE("Received %s command", PbOperation_Name(command.operation()).c_str());
PbNetworkInterfacesInfo *network_interfaces_info = new PbNetworkInterfacesInfo();
GetNetworkInterfacesInfo(*network_interfaces_info);
@@ -1894,13 +1935,16 @@ int main(int argc, char* argv[])
// Create a thread-safe stdout logger to process the log messages
auto logger = stdout_color_mt("rascsi stdout logger");
- // ~/images is the default folder for device image file. For the root user /home/pi/images is the default.
- const int uid = getuid();
+ // ~/images is the default folder for device image files, for the root user it is /home/pi/images
+ int uid = getuid();
+ const char *sudo_user = getenv("SUDO_UID");
+ if (sudo_user) {
+ uid = stoi(sudo_user);
+ }
const passwd *passwd = getpwuid(uid);
if (uid && passwd) {
- string folder = passwd->pw_dir;
- folder += "/images";
- default_image_folder = folder;
+ default_image_folder = passwd->pw_dir;
+ default_image_folder += "/images";
}
else {
default_image_folder = "/home/pi/images";
diff --git a/src/raspberrypi/rascsi_interface.proto b/src/raspberrypi/rascsi_interface.proto
index 7edd6822..2264d91e 100644
--- a/src/raspberrypi/rascsi_interface.proto
+++ b/src/raspberrypi/rascsi_interface.proto
@@ -182,6 +182,7 @@ message PbDeviceTypesInfo {
message PbImageFile {
string name = 1;
bool read_only = 2;
+ // The file size in bytes, 0 for block devices
int64 size = 3;
}
diff --git a/src/raspberrypi/rasctl.cpp b/src/raspberrypi/rasctl.cpp
index 966efe01..2effa75b 100644
--- a/src/raspberrypi/rasctl.cpp
+++ b/src/raspberrypi/rasctl.cpp
@@ -574,10 +574,10 @@ int main(int argc, char* argv[])
if (argc < 2) {
cerr << "SCSI Target Emulator RaSCSI Controller" << endl;
cerr << "version " << rascsi_get_version_string() << " (" << __DATE__ << ", " << __TIME__ << ")" << endl;
- cerr << "Usage: " << argv[0] << " -i ID [-u UNIT] [-c CMD] [-t TYPE] [-b BLOCK_SIZE] [-n NAME] [-f FILE|PARAM] ";
- cerr << "[-d IMAGE_FOLDER] [-g LOG_LEVEL] [-h HOST] [-p PORT] [-r RESERVED_IDS] ";
- cerr << "[-a FILENAME:FILESIZE] [-w FILENAME] [-m CURRENT_NAME:NEW_NAME] [-x CURRENT_NAME:NEW_NAME] ";
- cerr << "[-e] [-k] [-l] [-v] [-y]" << endl;
+ cerr << "Usage: " << argv[0] << " -i ID [-u UNIT] [-c CMD] [-C FILE] [-t TYPE] [-b BLOCK_SIZE] [-n NAME] [-f FILE|PARAM] ";
+ cerr << "[-F IMAGE_FOLDER] [-L LOG_LEVEL] [-h HOST] [-p PORT] [-r RESERVED_IDS] ";
+ cerr << "[-C FILENAME:FILESIZE] [-w FILENAME] [-R CURRENT_NAME:NEW_NAME] [-x CURRENT_NAME:NEW_NAME] ";
+ cerr << "[-L] [-k] [-l] [-v] [-y]" << endl;
cerr << " where ID := {0-7}" << endl;
cerr << " UNIT := {0|1}, default is 0" << endl;
cerr << " CMD := {attach|detach|insert|eject|protect|unprotect|show}" << endl;
@@ -613,7 +613,7 @@ int main(int argc, char* argv[])
opterr = 1;
int opt;
- while ((opt = getopt(argc, argv, "a:b:c:d:f:g:h:i:m:n:p:r:t:u:x:w:eklsvy")) != -1) {
+ while ((opt = getopt(argc, argv, "elsvNTD:L:R:a:b:c:f:h:i:n:p:r:t:u:x:C:F:L:")) != -1) {
switch (opt) {
case 'i':
device->set_id(optarg[0] - '0');
@@ -623,7 +623,7 @@ int main(int argc, char* argv[])
device->set_unit(optarg[0] - '0');
break;
- case 'a':
+ case 'C':
command.set_operation(CREATE_IMAGE);
image_params = optarg;
break;
@@ -645,12 +645,12 @@ int main(int argc, char* argv[])
}
break;
- case 'd':
+ case 'F':
command.set_operation(DEFAULT_FOLDER);
default_folder = optarg;
break;
- case'e':
+ case 'e':
command.set_operation(IMAGE_FILES_INFO);
break;
@@ -658,7 +658,7 @@ int main(int argc, char* argv[])
param = optarg;
break;
- case 'k':
+ case 'N':
command.set_operation(NETWORK_INTERFACES_INFO);
break;
@@ -670,7 +670,7 @@ int main(int argc, char* argv[])
}
break;
- case 'g':
+ case 'L':
command.set_operation(LOG_LEVEL);
log_level = optarg;
break;
@@ -683,7 +683,7 @@ int main(int argc, char* argv[])
list = true;
break;
- case 'm':
+ case 'R':
command.set_operation(RENAME_IMAGE);
image_params = optarg;
break;
@@ -743,11 +743,11 @@ int main(int argc, char* argv[])
image_params = optarg;
break;
- case 'y':
+ case 'T':
command.set_operation(DEVICE_TYPES_INFO);
break;
- case 'w':
+ case 'D':
command.set_operation(DELETE_IMAGE);
image_params = optarg;
break;