diff --git a/doc/rascsi.1 b/doc/rascsi.1 index e8c8e1f4..5839a32c 100644 --- a/doc/rascsi.1 +++ b/doc/rascsi.1 @@ -5,6 +5,8 @@ rascsi \- Emulates SCSI devices using the Raspberry Pi GPIO pins .B rascsi [\fB\-F\f® \fIFOLDER\fR] [\fB\-L\f® \fILOG_LEVEL\fR] +[\fB\-P\f® \fIACCESS_TOKEN_FILE\fR] +[\fB\-R\fR \fISCAN_DEPTH\fR] [\fB\-h\fR] [\fB\-n\fR \fIVENDOR:PRODUCT:REVISION\fR] [\fB\-p\f® \fIPORT\fR] @@ -52,6 +54,12 @@ The default folder for image files. For files in this folder no absolute path ne .BR \-L\fI " " \fILOG_LEVEL The rascsi log level (trace, debug, info, warn, err, critical, off). The default log level is 'info'. .TP +.BR \-P\fI " " \fIACCESS_TOKEN_FILE +Enable authentication and read the access token from the specified file. The access token file must be owned by root and must be readable by root only. +.TP +.BR \-R\fI " " \fISCAN_DEPTH +Scan for image files recursively, up to a depth of SCAN_DEPTH. Be careful when using this option with many sub-folders in the default image folder. +.TP .BR \-h\fI " " \fI Show a help page. .TP diff --git a/doc/rascsi_man_page.txt b/doc/rascsi_man_page.txt index ca6b25cc..6198a438 100644 --- a/doc/rascsi_man_page.txt +++ b/doc/rascsi_man_page.txt @@ -1,88 +1,97 @@ !! ------ 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) NAME rascsi - Emulates SCSI devices using the Raspberry Pi GPIO pins SYNOPSIS - rascsi [-F[u00AE] FOLDER] [-L[u00AE] LOG_LEVEL] [-h] [-n VENDOR:PROD‐ - UCT:REVISION] [-p[u00AE] PORT] [-r RESERVED_IDS] [-n TYPE] [-v] - [-IDn:[u] FILE] [-HDn[:u] FILE]... + rascsi [-F[u00AE] FOLDER] [-L[u00AE] LOG_LEVEL] [-P[u00AE] ACCESS_TO‐ + KEN_FILE] [-R SCAN_DEPTH] [-h] [-n VENDOR:PRODUCT:REVISION] [-p[u00AE] + PORT] [-r RESERVED_IDS] [-n TYPE] [-v] [-IDn:[u] FILE] [-HDn[:u] + FILE]... DESCRIPTION rascsi Emulates SCSI devices using the Raspberry Pi GPIO pins. - In the arguments to RaSCSI, one or more SCSI (-IDn[:u]) or SASI + In the arguments to RaSCSI, one or more SCSI (-IDn[:u]) or SASI (-HDn[:u]) devices can be specified. The number (n) after the ID or HD identifier specifies the ID number for that device. The optional number - (u) specifies the LUN (logical unit) for that device. The default LUN + (u) specifies the LUN (logical unit) for that device. The default LUN is 0. For SCSI: The ID is limited from 0-7. However, typically SCSI ID - 7 is reserved for the "initiator" (the host computer). The LUN is lim‐ + 7 is reserved for the "initiator" (the host computer). The LUN is lim‐ ited from 0-31. 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 + 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 + 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 + 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 + 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 al‐ - low external management commands. If another process is using the + 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, + 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 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 + The default folder for image files. For files in this folder no + absolute path needs to be specified. The initial default folder is '~/images'. -L LOG_LEVEL - 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'. + -P ACCESS_TOKEN_FILE + Enable authentication and read the access token from the speci‐ + fied file. The access token file must be owned by root and must + be readable by root only. + + -R SCAN_DEPTH + Scan for image files recursively, up to a depth of SCAN_DEPTH. + Be careful when using this option with many sub-folders in the + default image folder. + -h Show a help page. -n VENDOR:PRODUCT:REVISION - Set the vendor, product and revision for the device, to be re‐ - turned with the INQUIRY data. A complete set of name components + 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 + 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, + 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. @@ -90,17 +99,17 @@ OPTIONS -v Display the rascsi version. -IDn[:u] FILE - n is the SCSI ID number (0-7). u (0-31) is the optional LUN + n is the SCSI ID number (0-7). u (0-31) is the optional LUN (logical unit). The default LUN is 0. - 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 + 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[:u] FILE - n is the SASI ID number (0-15). The effective SASI ID is calcu‐ - lated as n/2, the effective SASI LUN is calculated is the re‐ - mainder of n/2. Alternatively the n:u syntax can be used, where + n is the SASI ID number (0-15). The effective SASI ID is calcu‐ + lated as n/2, the effective SASI LUN is calculated is the re‐ + mainder of n/2. Alternatively the n:u syntax can be used, where ns is the SASI ID (0-7) and u the LUN (0-1). FILE is the name of the image file to use for the SASI device. @@ -117,7 +126,7 @@ EXAMPLES 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 de‐ - vice file /dev/hdb (e.g. a USB stick) and a DaynaPort network adapter + 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 diff --git a/doc/rasctl.1 b/doc/rasctl.1 index 9e91c8bd..7b9d41bb 100644 --- a/doc/rasctl.1 +++ b/doc/rasctl.1 @@ -13,6 +13,7 @@ rasctl \- Sends management commands to the rascsi process \fB\-I\fR | \fB\-L\fR | \fB\-O\fR | +\fB\-P\fR | \fB\-T\fR | \fB\-V\fR | \fB\-X\fR | @@ -73,6 +74,9 @@ Lists all available network interfaces provided that they are up. .BR \-O\fI Display the available rascsi server log levels and the current log level. .TP +.BR \-P\fI +Prompt for the access token in case rascsi requires authentication. +.TP .BR \-l\fI List all of the devices that are currently being emulated by RaSCSI, as well as their current status. .TP diff --git a/doc/rasctl_man_page.txt b/doc/rasctl_man_page.txt index c2da7b31..956c760e 100644 --- a/doc/rasctl_man_page.txt +++ b/doc/rasctl_man_page.txt @@ -6,8 +6,8 @@ NAME rasctl - Sends management commands to the rascsi process SYNOPSIS - rasctl -e | -l | -m | -o | -s | -v | -D | -I | -L | -O | -T | -V | -X | - [-C FILENAME:FILESIZE] [-E FILENAME] [-F IMAGE_FOLDER] [-R CUR‐ + rasctl -e | -l | -m | -o | -s | -v | -D | -I | -L | -O | -P | -T | -V | + -X | [-C FILENAME:FILESIZE] [-E FILENAME] [-F IMAGE_FOLDER] [-R CUR‐ RENT_NAME:NEW_NAME] [-c CMD] [-f FILE|PARAM] [-g LOG_LEVEL] [-h HOST] [-i ID [-n NAME] [-p PORT] [-r RESERVED_IDS] [-t TYPE] [-u UNIT] [-x CURRENT_NAME:NEW_NAME] @@ -53,10 +53,13 @@ OPTIONS -O Display the available rascsi server log levels and the current log level. - -l List all of the devices that are currently being emulated by + -P Prompt for the access token in case rascsi requires authentica‐ + tion. + + -l List all of the devices that are currently being emulated by RaSCSI, as well as their current status. - -m List all file extensions recognized by RaSCSI and the device + -m List all file extensions recognized by RaSCSI and the device types they map to. -o Display operation meta data information. @@ -70,7 +73,7 @@ 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. -T Display all device types and their properties. @@ -94,7 +97,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) @@ -104,18 +107,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 @@ -125,16 +128,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-31). This will default to 0. This option is only - used when there are multiple SCSI devices on a shared SCSI con‐ + Unit number (0-31). This will default to 0. This option is only + used when there are multiple SCSI devices on a shared SCSI con‐ troller. (This is not common) EXAMPLES diff --git a/src/raspberrypi/protobuf_util.cpp b/src/raspberrypi/protobuf_util.cpp index 6c5554c6..df672579 100644 --- a/src/raspberrypi/protobuf_util.cpp +++ b/src/raspberrypi/protobuf_util.cpp @@ -120,7 +120,7 @@ int protobuf_util::ReadNBytes(int fd, uint8_t *buf, int n) } -bool protobuf_util::ReturnStatus(int fd, bool status, const string msg) +bool protobuf_util::ReturnStatus(int fd, bool status, const string msg, const PbErrorCode error_code) { if (!status && !msg.empty()) { LOGERROR("%s", msg.c_str()); @@ -142,6 +142,7 @@ bool protobuf_util::ReturnStatus(int fd, bool status, const string msg) else { PbResult result; result.set_status(status); + result.set_error_code(error_code); result.set_msg(msg); SerializeMessage(fd, result); } diff --git a/src/raspberrypi/protobuf_util.h b/src/raspberrypi/protobuf_util.h index 8482fe16..9106d3e2 100644 --- a/src/raspberrypi/protobuf_util.h +++ b/src/raspberrypi/protobuf_util.h @@ -29,6 +29,6 @@ namespace protobuf_util void SerializeMessage(int, const google::protobuf::Message&); void DeserializeMessage(int, google::protobuf::Message&); int ReadNBytes(int, uint8_t *, int); - bool ReturnStatus(int, bool = true, const string = ""); + bool ReturnStatus(int, bool = true, const string = "", const PbErrorCode error_code = PbErrorCode::NO_ERROR_CODE); bool ReturnStatus(int, bool, const ostringstream&); } diff --git a/src/raspberrypi/rascsi.cpp b/src/raspberrypi/rascsi.cpp index 339a7071..675cbef9 100644 --- a/src/raspberrypi/rascsi.cpp +++ b/src/raspberrypi/rascsi.cpp @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -67,7 +68,9 @@ pthread_t monthread; // Monitor Thread pthread_mutex_t ctrl_mutex; // Semaphore for the ctrl array static void *MonThread(void *param); string current_log_level; // Some versions of spdlog do not support get_log_level() +string access_token; set reserved_ids; +int scan_depth = 0; DeviceFactory& device_factory = DeviceFactory::instance(); RascsiImage rascsi_image; RascsiResponse rascsi_response(&device_factory, &rascsi_image); @@ -373,6 +376,43 @@ bool MapController(Device **map) return status; } +bool ReadAccessToken(const char *filename) +{ + struct stat st; + if (stat(filename, &st) || !S_ISREG(st.st_mode)) { + cerr << "Can't access token file '" << optarg << "'" << endl; + return false; + } + + if (st.st_uid || st.st_gid || (st.st_mode & (S_IROTH | S_IWOTH | S_IRGRP | S_IWGRP))) { + cerr << "Access token file '" << optarg << "' must be owned by root and readable by root only" << endl; + return false; + } + + ifstream token_file(filename, ifstream::in); + if (token_file.fail()) { + cerr << "Can't open access token file '" << optarg << "'" << endl; + return false; + } + + getline(token_file, access_token); + if (token_file.fail()) { + token_file.close(); + cerr << "Can't read access token file '" << optarg << "'" << endl; + return false; + } + + if (access_token.empty()) { + token_file.close(); + cerr << "Access token file '" << optarg << "' must not be empty" << endl; + return false; + } + + token_file.close(); + + return true; +} + string ValidateLunSetup(const PbCommand& command, const vector& existing_devices) { // Mapping of available LUNs (bit vector) to devices @@ -972,7 +1012,8 @@ bool ProcessCmd(int fd, const PbDeviceDefinition& pb_device, const PbCommand& co assert(dryRun); break; - case NONE: + case CHECK_AUTHENTICATION: + case NO_OPERATION: // Do nothing, just log LOGTRACE("Received %s command", PbOperation_Name(operation).c_str()); break; @@ -1159,7 +1200,7 @@ bool ParseArgument(int argc, char* argv[], int& port) opterr = 1; 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:P:R:")) != -1) { switch (opt) { // The three options below are kind of a compound option with two letters case 'i': @@ -1204,6 +1245,13 @@ bool ParseArgument(int argc, char* argv[], int& port) log_level = optarg; continue; + case 'R': + if (!GetAsInt(optarg, scan_depth) || scan_depth < 0) { + cerr << "Invalid image file scan depth " << optarg << endl; + return false; + } + continue; + case 'n': name = optarg; continue; @@ -1215,6 +1263,12 @@ bool ParseArgument(int argc, char* argv[], int& port) } continue; + case 'P': + if (!ReadAccessToken(optarg)) { + return false; + } + continue; + case 'r': { string error = SetReservedIds(optarg); if (!error.empty()) { @@ -1370,6 +1424,13 @@ static void *MonThread(void *param) PbCommand command; DeserializeMessage(fd, command); + if (!access_token.empty()) { + if (access_token != GetParam(command, "token")) { + ReturnStatus(fd, false, "Authentication failed", PbErrorCode::UNAUTHORIZED); + continue; + } + } + switch(command.operation()) { case LOG_LEVEL: { LOGTRACE("Received %s command", PbOperation_Name(command.operation()).c_str()); @@ -1427,7 +1488,8 @@ static void *MonThread(void *param) PbResult result; 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); break; } @@ -1454,7 +1516,8 @@ static void *MonThread(void *param) LOGTRACE("Received %s command", PbOperation_Name(command.operation()).c_str()); 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); break; } diff --git a/src/raspberrypi/rascsi_interface.proto b/src/raspberrypi/rascsi_interface.proto index 77ba1ddf..554069f1 100644 --- a/src/raspberrypi/rascsi_interface.proto +++ b/src/raspberrypi/rascsi_interface.proto @@ -2,6 +2,8 @@ // Each rascsi message sent to the rascsi server is preceded by the magic string "RASCSI". // A message starts with a little endian 32 bit header which contains the protobuf message size. // Unless explicitly specified the order of repeated data returned is undefined. +// All operations accept an optional access token, specified by the "token" parameter. +// Only the VERSION_INFO operation never requires authentication. // syntax = "proto3"; @@ -29,7 +31,7 @@ enum PbDeviceType { // rascsi remote operations, returning PbResult enum PbOperation { - NONE = 0; + NO_OPERATION = 0; // Attach devices and return the new device list (PbDevicesInfo) // Parameters (mutually exclusive): @@ -64,7 +66,9 @@ enum PbOperation { // Make medium writable (not possible for read-only media) UNPROTECT = 9; - // Gets rascsi 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; // Get rascsi server version (PbVersionInfo) @@ -78,6 +82,8 @@ enum PbOperation { DEVICE_TYPES_INFO = 13; // 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; // Get information on an image file (not necessarily in the default image folder) based on an absolute path. @@ -155,8 +161,13 @@ enum PbOperation { // "file": The filename, relative to the default image folder. It must not contain a slash. UNPROTECT_IMAGE = 29; - // Get operation meta data (PbOperationParameters) - OPERATION_INFO = 30; + // Check whether an authentication token is valid. A client can use this in operationin order to + // find out whether rascsi authentication is enable or to use rascsi authentication for securing + // client-internal operations. + CHECK_AUTHENTICATION = 30; + + // Get operation meta data (PbOperationInfo) + OPERATION_INFO = 31; } // The operation parameter meta data. The parameter data type is provided by the protobuf API. @@ -186,6 +197,14 @@ message PbOperationInfo { map operations = 1; } +// rascsi special purpose error codes for cases where a textual error message is not sufficient +enum PbErrorCode { + // No error code available + NO_ERROR_CODE = 0; + // Authentication/Authorization error + UNAUTHORIZED = 1; +} + // The supported file extensions mapped to their respective device types message PbMappingInfo { map mapping = 1; @@ -335,6 +354,8 @@ message PbResult { bool status = 1; // An optional error or information message, depending on the status. A string without trailing CR/LF. string msg = 2; + // An optional error code. Only to be used in cases where textual information is not sufficient. + PbErrorCode error_code = 14; // Optional additional result data oneof result { // The result of a SERVER_INFO command diff --git a/src/raspberrypi/rascsi_response.cpp b/src/raspberrypi/rascsi_response.cpp index 51b0dbb6..73ada05f 100644 --- a/src/raspberrypi/rascsi_response.cpp +++ b/src/raspberrypi/rascsi_response.cpp @@ -140,51 +140,71 @@ bool RascsiResponse::GetImageFile(PbImageFile *image_file, const string& filenam 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(); string default_image_folder = rascsi_image->GetDefaultImageFolder(); 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; - } - } 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); - } + GetAvailableImages(*image_files_info, default_image_folder, default_image_folder, pattern, scan_depth); result.set_status(true); 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()); 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& devices, const set& reserved_ids, - const string& current_log_level) + const string& current_log_level, const string& filename_pattern, int scan_depth) { PbServerInfo *server_info = new PbServerInfo(); server_info->set_allocated_version_info(GetVersionInfo(result)); server_info->set_allocated_log_level_info(GetLogLevelInfo(result, current_log_level)); 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_mapping_info(GetMappingInfo(result)); GetDevices(*server_info, devices); diff --git a/src/raspberrypi/rascsi_response.h b/src/raspberrypi/rascsi_response.h index be07365a..b9cea859 100644 --- a/src/raspberrypi/rascsi_response.h +++ b/src/raspberrypi/rascsi_response.h @@ -29,16 +29,16 @@ public: ~RascsiResponse() {}; bool GetImageFile(PbImageFile *, const string&); - PbImageFilesInfo *GetAvailableImages(PbResult&); + PbImageFilesInfo *GetAvailableImages(PbResult&, const string&, int); + PbReservedIdsInfo *GetReservedIds(PbResult&, const set&); void GetDevices(PbServerInfo&, const vector&); void GetDevicesInfo(PbResult&, const PbCommand&, const vector&, int); PbDeviceTypesInfo *GetDeviceTypesInfo(PbResult&, const PbCommand&); PbVersionInfo *GetVersionInfo(PbResult&); - PbServerInfo *GetServerInfo(PbResult&, const vector&, const set&, const string&); + PbServerInfo *GetServerInfo(PbResult&, const vector&, const set&, const string&, const string&, int); PbNetworkInterfacesInfo *GetNetworkInterfacesInfo(PbResult&); - PbLogLevelInfo *GetLogLevelInfo(PbResult&, const string&); - PbReservedIdsInfo *GetReservedIds(PbResult&, const set&); PbMappingInfo *GetMappingInfo(PbResult&); + PbLogLevelInfo *GetLogLevelInfo(PbResult&, const string&); PbOperationInfo *GetOperationInfo(PbResult&); private: @@ -52,7 +52,8 @@ private: void GetDevice(const Device *, PbDevice *); void GetAllDeviceTypeProperties(PbDeviceTypesInfo&); 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); void CreateOperation(PbOperationInfo *, PbOperationMetaData *, const PbOperation&, const string&); PbOperationParameter *AddOperationParameter(PbOperationMetaData *, const string&, const string&, const string& = ""); diff --git a/src/raspberrypi/rasctl.cpp b/src/raspberrypi/rasctl.cpp index 94ed5cad..a04237be 100644 --- a/src/raspberrypi/rasctl.cpp +++ b/src/raspberrypi/rasctl.cpp @@ -52,7 +52,7 @@ PbOperation ParseOperation(const char *optarg) return DEVICES_INFO; default: - return NONE; + return NO_OPERATION; } } @@ -134,11 +134,12 @@ int main(int argc, char* argv[]) string reserved_ids; string image_params; string filename; + string token; bool list = false; opterr = 1; int opt; - while ((opt = getopt(argc, argv, "elmosvDINOTVXa:b:c:d:f:h:i:n:p:r:t:u:x:C:E:F:L:R:")) != -1) { + while ((opt = getopt(argc, argv, "elmosvDINOTVXa:b:c:d:f:h:i:n:p:r:t:u:x:C:E:F:L:R:P::")) != -1) { switch (opt) { case 'i': { int id; @@ -176,7 +177,7 @@ int main(int argc, char* argv[]) case 'c': command.set_operation(ParseOperation(optarg)); - if (command.operation() == NONE) { + if (command.operation() == NO_OPERATION) { cerr << "Error: Unknown operation '" << optarg << "'" << endl; exit(EXIT_FAILURE); } @@ -305,6 +306,10 @@ int main(int argc, char* argv[]) exit(EXIT_SUCCESS); break; + case 'P': + token = optarg ? optarg : getpass("Password: "); + break; + case 'V': command.set_operation(VERSION_INFO); break; @@ -333,7 +338,7 @@ int main(int argc, char* argv[]) if (list) { PbCommand command_list; command_list.set_operation(DEVICES_INFO); - RasctlCommands rasctl_commands(command_list, hostname, port); + RasctlCommands rasctl_commands(command_list, hostname, port, token); rasctl_commands.CommandDevicesInfo(); exit(EXIT_SUCCESS); } @@ -344,7 +349,7 @@ int main(int argc, char* argv[]) AddParam(*device, "file", param); } - RasctlCommands rasctl_commands(command, hostname, port); + RasctlCommands rasctl_commands(command, hostname, port, token); switch(command.operation()) { case LOG_LEVEL: diff --git a/src/raspberrypi/rasctl_commands.cpp b/src/raspberrypi/rasctl_commands.cpp index c7286e10..293321e6 100644 --- a/src/raspberrypi/rasctl_commands.cpp +++ b/src/raspberrypi/rasctl_commands.cpp @@ -25,15 +25,20 @@ using namespace std; using namespace rascsi_interface; using namespace protobuf_util; -RasctlCommands::RasctlCommands(PbCommand& command, const string& hostname, int port) +RasctlCommands::RasctlCommands(PbCommand& command, const string& hostname, int port, const string& token) { this->command = command; this->hostname = hostname; this->port = port; + this->token = token; } void RasctlCommands::SendCommand() { + if (!token.empty()) { + AddParam(command, "token", token); + } + // Send command int fd = -1; try { diff --git a/src/raspberrypi/rasctl_commands.h b/src/raspberrypi/rasctl_commands.h index d1b88347..2e35193f 100644 --- a/src/raspberrypi/rasctl_commands.h +++ b/src/raspberrypi/rasctl_commands.h @@ -20,7 +20,7 @@ class RasctlCommands { public: - RasctlCommands(PbCommand&, const string&, int); + RasctlCommands(PbCommand&, const string&, int, const string&); ~RasctlCommands() {}; void SendCommand(); @@ -49,6 +49,7 @@ private: PbCommand command; string hostname; int port; + string token; PbResult result;