Recursive image file scan and image file filter (#532)

* Extracted code

* Initial support for (optional) recursive image file listings

* Manpage update

* Added support for image file filter

* Updated filtering

* Made image scan depth configurable
This commit is contained in:
Uwe Seimet 2021-12-19 11:49:17 +01:00 committed by GitHub
parent 39c65beb99
commit e32211ef73
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 89 additions and 47 deletions

View File

@ -5,6 +5,7 @@ rascsi \- Emulates SCSI devices using the Raspberry Pi GPIO pins
.B rascsi .B rascsi
[\fB\-F\f® \fIFOLDER\fR] [\fB\-F\f® \fIFOLDER\fR]
[\fB\-L\f® \fILOG_LEVEL\fR] [\fB\-L\f® \fILOG_LEVEL\fR]
[\fB\-R\fR \fISCAN_DEPTH\fR]
[\fB\-h\fR] [\fB\-h\fR]
[\fB\-n\fR \fIVENDOR:PRODUCT:REVISION\fR] [\fB\-n\fR \fIVENDOR:PRODUCT:REVISION\fR]
[\fB\-p\f® \fIPORT\fR] [\fB\-p\f® \fIPORT\fR]
@ -52,6 +53,9 @@ The default folder for image files. For files in this folder no absolute path ne
.BR \-L\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'. The rascsi log level (trace, debug, info, warn, err, critical, off). The default log level is 'info'.
.TP .TP
.BR \-R\fI " " \fISCAN_DEPTH
Scan for image files recursively, up to a depth -f SCAN_DEPTH. Be careful when using this option with many sub-folders in the default image folder.
.TP
.BR \-h\fI " " \fI .BR \-h\fI " " \fI
Show a help page. Show a help page.
.TP .TP

View File

@ -1,16 +1,14 @@
!! ------ THIS FILE IS AUTO_GENERATED! DO NOT MANUALLY UPDATE!!! !! ------ THIS FILE IS AUTO_GENERATED! DO NOT MANUALLY UPDATE!!!
!! ------ The native file is rascsi.1. Re-run 'make docs' after updating !! ------ The native file is rascsi.1. Re-run 'make docs' after updating\n\n
rascsi(1) General Commands Manual rascsi(1) rascsi(1) General Commands Manual rascsi(1)
NAME NAME
rascsi - Emulates SCSI devices using the Raspberry Pi GPIO pins rascsi - Emulates SCSI devices using the Raspberry Pi GPIO pins
SYNOPSIS SYNOPSIS
rascsi [-F[u00AE] FOLDER] [-L[u00AE] LOG_LEVEL] [-h] [-n VENDOR:PROD rascsi [-F[u00AE] FOLDER] [-L[u00AE] LOG_LEVEL] [-R SCAN_DEPTH] [-h]
UCT:REVISION] [-p[u00AE] PORT] [-r RESERVED_IDS] [-n TYPE] [-v] [-n VENDOR:PRODUCT:REVISION] [-p[u00AE] PORT] [-r RESERVED_IDS] [-n
[-IDn:[u] FILE] [-HDn[:u] FILE]... TYPE] [-v] [-IDn:[u] FILE] [-HDn[:u] FILE]...
DESCRIPTION DESCRIPTION
rascsi Emulates SCSI devices using the Raspberry Pi GPIO pins. rascsi Emulates SCSI devices using the Raspberry Pi GPIO pins.
@ -67,6 +65,11 @@ OPTIONS
The rascsi log level (trace, debug, info, warn, err, critical, The rascsi log level (trace, debug, info, warn, err, critical,
off). The default log level is 'info'. off). The default log level is 'info'.
-R SCAN_DEPTH
Scan for image files recursively, up to a depth -f SCAN_DEPTH.
Be careful when using this option with many sub-folders in the
default image folder.
-h Show a help page. -h Show a help page.
-n VENDOR:PRODUCT:REVISION -n VENDOR:PRODUCT:REVISION

View File

@ -68,6 +68,7 @@ pthread_mutex_t ctrl_mutex; // Semaphore for the ctrl array
static void *MonThread(void *param); static void *MonThread(void *param);
string current_log_level; // Some versions of spdlog do not support get_log_level() string current_log_level; // Some versions of spdlog do not support get_log_level()
set<int> reserved_ids; set<int> reserved_ids;
int scan_depth = 0;
DeviceFactory& device_factory = DeviceFactory::instance(); DeviceFactory& device_factory = DeviceFactory::instance();
RascsiImage rascsi_image; RascsiImage rascsi_image;
RascsiResponse rascsi_response(&device_factory, &rascsi_image); RascsiResponse rascsi_response(&device_factory, &rascsi_image);
@ -1159,7 +1160,7 @@ bool ParseArgument(int argc, char* argv[], int& port)
opterr = 1; opterr = 1;
int opt; int opt;
while ((opt = getopt(argc, argv, "-IiHhb:d:n:p:r:t:D:F:L:")) != -1) { while ((opt = getopt(argc, argv, "-IiHhb:d:n:p:r:t:D:F:L:R:")) != -1) {
switch (opt) { switch (opt) {
// The three options below are kind of a compound option with two letters // The three options below are kind of a compound option with two letters
case 'i': case 'i':
@ -1204,6 +1205,13 @@ bool ParseArgument(int argc, char* argv[], int& port)
log_level = optarg; log_level = optarg;
continue; continue;
case 'R':
if (!GetAsInt(optarg, scan_depth) || scan_depth < 0) {
cerr << "Invalid image file scan depth " << optarg << endl;
return false;
}
continue;
case 'n': case 'n':
name = optarg; name = optarg;
continue; continue;
@ -1427,7 +1435,8 @@ static void *MonThread(void *param)
PbResult result; PbResult result;
result.set_allocated_server_info(rascsi_response.GetServerInfo( result.set_allocated_server_info(rascsi_response.GetServerInfo(
result, devices, reserved_ids, current_log_level)); result, devices, reserved_ids, current_log_level, GetParam(command, "filename_pattern"),
scan_depth));
SerializeMessage(fd, result); SerializeMessage(fd, result);
break; break;
} }
@ -1454,7 +1463,8 @@ static void *MonThread(void *param)
LOGTRACE("Received %s command", PbOperation_Name(command.operation()).c_str()); LOGTRACE("Received %s command", PbOperation_Name(command.operation()).c_str());
PbResult result; PbResult result;
result.set_allocated_image_files_info(rascsi_response.GetAvailableImages(result)); result.set_allocated_image_files_info(rascsi_response.GetAvailableImages(result,
GetParam(command, "filename_pattern"), scan_depth));
SerializeMessage(fd, result); SerializeMessage(fd, result);
break; break;
} }

View File

@ -64,7 +64,9 @@ enum PbOperation {
// Make medium writable (not possible for read-only media) // Make medium writable (not possible for read-only media)
UNPROTECT = 9; UNPROTECT = 9;
// Gets the server information (PbServerInfo) // Gets the server information (PbServerInfo). Calling this operation should be avoided because it
// may return a lot of data. More specific other operations should be used instead.
// "filename_pattern": Optional filter, only filenames containing the case-insensitive pattern are returned
SERVER_INFO = 10; SERVER_INFO = 10;
// Get rascsi version information (PbVersionInfo) // Get rascsi version information (PbVersionInfo)
@ -78,6 +80,8 @@ enum PbOperation {
DEVICE_TYPES_INFO = 13; DEVICE_TYPES_INFO = 13;
// Get information on available image files in the default image folder (PbImageFilesInfo) // Get information on available image files in the default image folder (PbImageFilesInfo)
// Parameters:
// "filename_pattern": Optional filter, only filenames containing the case-insensitive pattern are returned
DEFAULT_IMAGE_FILES_INFO = 14; DEFAULT_IMAGE_FILES_INFO = 14;
// Get information on an image file (not necessarily in the default image folder) based on an absolute path. // Get information on an image file (not necessarily in the default image folder) based on an absolute path.

View File

@ -140,51 +140,71 @@ bool RascsiResponse::GetImageFile(PbImageFile *image_file, const string& filenam
return false; return false;
} }
PbImageFilesInfo *RascsiResponse::GetAvailableImages(PbResult& result) void RascsiResponse::GetAvailableImages(PbImageFilesInfo& image_files_info, const string& default_image_folder,
const string& folder, const string& pattern, int scan_depth) {
string pattern_lower = pattern;
transform(pattern_lower.begin(), pattern_lower.end(), pattern_lower.begin(), ::tolower);
if (scan_depth-- >= 0) {
DIR *d = opendir(folder.c_str());
if (d) {
struct dirent *dir;
while ((dir = readdir(d))) {
string filename = folder + "/" + dir->d_name;
string name_lower = filename;
if (!pattern.empty()) {
transform(name_lower.begin(), name_lower.end(), name_lower.begin(), ::tolower);
}
bool is_supported_type = dir->d_type == DT_REG || dir->d_type == DT_DIR || dir->d_type == DT_LNK || dir->d_type == DT_BLK;
if (is_supported_type && dir->d_name[0] != '.') {
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, 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, folder.c_str());
continue;
} else if (dir->d_type == DT_DIR) {
GetAvailableImages(image_files_info, default_image_folder, filename, pattern, scan_depth);
continue;
}
if (pattern.empty() || name_lower.find(pattern_lower) != string::npos) {
PbImageFile *image_file = new PbImageFile();
if (GetImageFile(image_file, filename)) {
GetImageFile(image_files_info.add_image_files(), filename.substr(default_image_folder.length() + 1));
}
delete image_file;
}
}
}
closedir(d);
}
}
}
PbImageFilesInfo *RascsiResponse::GetAvailableImages(PbResult& result, const string& pattern, int scan_depth)
{ {
PbImageFilesInfo *image_files_info = new PbImageFilesInfo(); PbImageFilesInfo *image_files_info = new PbImageFilesInfo();
string default_image_folder = rascsi_image->GetDefaultImageFolder(); string default_image_folder = rascsi_image->GetDefaultImageFolder();
image_files_info->set_default_image_folder(default_image_folder); 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 GetAvailableImages(*image_files_info, default_image_folder, default_image_folder, pattern, scan_depth);
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;
}
} 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;
}
PbImageFile *image_file = new PbImageFile();
if (GetImageFile(image_file, dir->d_name)) {
GetImageFile(image_files_info->add_image_files(), dir->d_name);
}
delete image_file;
}
}
closedir(d);
}
result.set_status(true); result.set_status(true);
return image_files_info; return image_files_info;
} }
void RascsiResponse::GetAvailableImages(PbResult& result, PbServerInfo& server_info) void RascsiResponse::GetAvailableImages(PbResult& result, PbServerInfo& server_info, const string& pattern, int scan_depth)
{ {
PbImageFilesInfo *image_files_info = GetAvailableImages(result); PbImageFilesInfo *image_files_info = GetAvailableImages(result, pattern, scan_depth);
image_files_info->set_default_image_folder(rascsi_image->GetDefaultImageFolder()); image_files_info->set_default_image_folder(rascsi_image->GetDefaultImageFolder());
server_info.set_allocated_image_files_info(image_files_info); server_info.set_allocated_image_files_info(image_files_info);
@ -263,14 +283,14 @@ PbDeviceTypesInfo *RascsiResponse::GetDeviceTypesInfo(PbResult& result, const Pb
} }
PbServerInfo *RascsiResponse::GetServerInfo(PbResult& result, const vector<Device *>& devices, const set<int>& reserved_ids, PbServerInfo *RascsiResponse::GetServerInfo(PbResult& result, const vector<Device *>& devices, const set<int>& reserved_ids,
const string& current_log_level) const string& current_log_level, const string& filename_pattern, int scan_depth)
{ {
PbServerInfo *server_info = new PbServerInfo(); PbServerInfo *server_info = new PbServerInfo();
server_info->set_allocated_version_info(GetVersionInfo(result)); server_info->set_allocated_version_info(GetVersionInfo(result));
server_info->set_allocated_log_level_info(GetLogLevelInfo(result, current_log_level)); server_info->set_allocated_log_level_info(GetLogLevelInfo(result, current_log_level));
GetAllDeviceTypeProperties(*server_info->mutable_device_types_info()); GetAllDeviceTypeProperties(*server_info->mutable_device_types_info());
GetAvailableImages(result, *server_info); GetAvailableImages(result, *server_info, filename_pattern, scan_depth);
server_info->set_allocated_network_interfaces_info(GetNetworkInterfacesInfo(result)); server_info->set_allocated_network_interfaces_info(GetNetworkInterfacesInfo(result));
server_info->set_allocated_mapping_info(GetMappingInfo(result)); server_info->set_allocated_mapping_info(GetMappingInfo(result));
GetDevices(*server_info, devices); GetDevices(*server_info, devices);

View File

@ -29,13 +29,13 @@ public:
~RascsiResponse() {}; ~RascsiResponse() {};
bool GetImageFile(PbImageFile *, const string&); bool GetImageFile(PbImageFile *, const string&);
PbImageFilesInfo *GetAvailableImages(PbResult&); PbImageFilesInfo *GetAvailableImages(PbResult&, const string&, int);
PbReservedIdsInfo *GetReservedIds(PbResult&, const set<int>&); PbReservedIdsInfo *GetReservedIds(PbResult&, const set<int>&);
void GetDevices(PbServerInfo&, const vector<Device *>&); void GetDevices(PbServerInfo&, const vector<Device *>&);
void GetDevicesInfo(PbResult&, const PbCommand&, const vector<Device *>&, int); void GetDevicesInfo(PbResult&, const PbCommand&, const vector<Device *>&, int);
PbDeviceTypesInfo *GetDeviceTypesInfo(PbResult&, const PbCommand&); PbDeviceTypesInfo *GetDeviceTypesInfo(PbResult&, const PbCommand&);
PbVersionInfo *GetVersionInfo(PbResult&); PbVersionInfo *GetVersionInfo(PbResult&);
PbServerInfo *GetServerInfo(PbResult&, const vector<Device *>&, const set<int>&, const string&); PbServerInfo *GetServerInfo(PbResult&, const vector<Device *>&, const set<int>&, const string&, const string&, int);
PbNetworkInterfacesInfo *GetNetworkInterfacesInfo(PbResult&); PbNetworkInterfacesInfo *GetNetworkInterfacesInfo(PbResult&);
PbMappingInfo *GetMappingInfo(PbResult&); PbMappingInfo *GetMappingInfo(PbResult&);
PbLogLevelInfo *GetLogLevelInfo(PbResult&, const string&); PbLogLevelInfo *GetLogLevelInfo(PbResult&, const string&);
@ -51,5 +51,6 @@ private:
void GetDevice(const Device *, PbDevice *); void GetDevice(const Device *, PbDevice *);
void GetAllDeviceTypeProperties(PbDeviceTypesInfo&); void GetAllDeviceTypeProperties(PbDeviceTypesInfo&);
void GetDeviceTypeProperties(PbDeviceTypesInfo&, PbDeviceType); void GetDeviceTypeProperties(PbDeviceTypesInfo&, PbDeviceType);
void GetAvailableImages(PbResult& result, PbServerInfo&); void GetAvailableImages(PbImageFilesInfo&, const string&, const string&, const string&, int);
void GetAvailableImages(PbResult& result, PbServerInfo&, const string&, int);
}; };