Feature server information (#163)

* Extended protobuf interface with rascsi server information

* Added default_image_folder (to be used in a different feature)

* Remember log level because not all versions of spdlog support get_log_level()

* Display enum names instead of values

* Added support for default image folder

* Added method that returns the available image files

* Added list of image files to protobuf interface

* Error handling update

* Filter image files

* Update setting up image file list

* Message update

* Replaced if/else cascade by switch

* Updated log level handling

* Renaming, sort rasctl list output

* Added -lstdc++fs (required by gcc 8.3.0)

* Changed option shortcut

* Sorted log levels by severity

* Fixed sorting of log levels

* Use map for storing available log levels

* Replaced explicit typ

* Revert "Replaced explicit typ"

This reverts commit 4106c15d6f391b840d109d4284a5c8854c42af21.

* Revert "Use map for storing available log levels"

This reverts commit 505751e64a34f49055bd54812c03d20056bd3872.

* Added comments

* Removed needless CR/LF when logging

Co-authored-by: akuker <akuker@gmail.com>
This commit is contained in:
Uwe Seimet 2021-08-06 03:56:07 +02:00 committed by GitHub
parent dd7ce23adc
commit 735aff6cd1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 198 additions and 44 deletions

View File

@ -161,10 +161,10 @@ ALL: all
docs: $(DOC_DIR)/rascsi_man_page.txt $(DOC_DIR)/rasctl_man_page.txt $(DOC_DIR)/scsimon_man_page.txt docs: $(DOC_DIR)/rascsi_man_page.txt $(DOC_DIR)/rasctl_man_page.txt $(DOC_DIR)/scsimon_man_page.txt
$(BINDIR)/$(RASCSI): $(SRC_PROTOBUF) $(OBJ_RASCSI) | $(BINDIR) $(BINDIR)/$(RASCSI): $(SRC_PROTOBUF) $(OBJ_RASCSI) | $(BINDIR)
$(CXX) $(CXXFLAGS) -o $@ $(OBJ_RASCSI) -lpthread -lz -lpcap -lprotobuf $(CXX) $(CXXFLAGS) -o $@ $(OBJ_RASCSI) -lpthread -lz -lpcap -lprotobuf -lstdc++fs
$(BINDIR)/$(RASCTL): $(SRC_PROTOBUF) $(OBJ_RASCTL) | $(BINDIR) $(BINDIR)/$(RASCTL): $(SRC_PROTOBUF) $(OBJ_RASCTL) | $(BINDIR)
$(CXX) $(CXXFLAGS) -o $@ $(OBJ_RASCTL) -lprotobuf $(CXX) $(CXXFLAGS) -o $@ $(OBJ_RASCTL) -lprotobuf -lstdc++fs
$(BINDIR)/$(RASDUMP): $(OBJ_RASDUMP) | $(BINDIR) $(BINDIR)/$(RASDUMP): $(OBJ_RASDUMP) | $(BINDIR)
$(CXX) $(CXXFLAGS) -o $@ $(OBJ_RASDUMP) $(CXX) $(CXXFLAGS) -o $@ $(OBJ_RASDUMP)

View File

@ -35,6 +35,8 @@
#include <string> #include <string>
#include <sstream> #include <sstream>
#include <iostream> #include <iostream>
#include <list>
#include <filesystem>
using namespace std; using namespace std;
using namespace spdlog; using namespace spdlog;
@ -63,6 +65,9 @@ int monsocket; // Monitor Socket
pthread_t monthread; // Monitor Thread pthread_t monthread; // Monitor Thread
pthread_mutex_t ctrl_mutex; // Semaphore for the ctrl array pthread_mutex_t ctrl_mutex; // Semaphore for the ctrl array
static void *MonThread(void *param); static void *MonThread(void *param);
list<string> available_log_levels;
string current_log_level; // Some versions of spdlog do not support get_log_level()
string default_image_folder = "/home/pi/images";
set<string> files_in_use; set<string> files_in_use;
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
@ -123,16 +128,14 @@ void Banner(int argc, char* argv[])
BOOL InitService(int port) BOOL InitService(int port)
{ {
struct sockaddr_in server; int result = pthread_mutex_init(&ctrl_mutex,NULL);
int yes, result;
result = pthread_mutex_init(&ctrl_mutex,NULL);
if(result != EXIT_SUCCESS){ if(result != EXIT_SUCCESS){
LOGERROR("Unable to create a mutex. Err code: %d", result); LOGERROR("Unable to create a mutex. Err code: %d", result);
return FALSE; return FALSE;
} }
// Create socket for monitor // Create socket for monitor
struct sockaddr_in server;
monsocket = socket(PF_INET, SOCK_STREAM, 0); monsocket = socket(PF_INET, SOCK_STREAM, 0);
memset(&server, 0, sizeof(server)); memset(&server, 0, sizeof(server));
server.sin_family = PF_INET; server.sin_family = PF_INET;
@ -140,7 +143,7 @@ BOOL InitService(int port)
server.sin_addr.s_addr = htonl(INADDR_ANY); server.sin_addr.s_addr = htonl(INADDR_ANY);
// allow address reuse // allow address reuse
yes = 1; int yes = 1;
if (setsockopt( if (setsockopt(
monsocket, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0){ monsocket, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0){
return FALSE; return FALSE;
@ -431,7 +434,7 @@ bool ReturnStatus(int fd, bool status = true, const string msg = "") {
return status; return status;
} }
void SetLogLevel(const string& log_level) { bool SetLogLevel(const string& log_level) {
if (log_level == "trace") { if (log_level == "trace") {
set_level(level::trace); set_level(level::trace);
} }
@ -454,9 +457,12 @@ void SetLogLevel(const string& log_level) {
set_level(level::off); set_level(level::off);
} }
else { else {
LOGWARN("Invalid log level '%s', falling back to 'trace'", log_level.c_str()); return false;
set_level(level::trace);
} }
current_log_level = log_level;
return true;
} }
void LogDeviceList(const string& device_list) void LogDeviceList(const string& device_list)
@ -469,6 +475,24 @@ void LogDeviceList(const string& device_list)
} }
} }
void GetAvailableLogLevels(PbServerInfo& serverInfo)
{
for (auto it = available_log_levels.begin(); it != available_log_levels.end(); ++it) {
serverInfo.add_available_log_levels(*it);
}
}
void GetAvailableImages(PbServerInfo& serverInfo)
{
if (access(default_image_folder.c_str(), F_OK) != -1) {
for (const auto& entry : filesystem::directory_iterator(default_image_folder)) {
if (entry.is_regular_file()) {
serverInfo.add_available_image_files(entry.path().filename());
}
}
}
}
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
// //
// Command Processing // Command Processing
@ -488,7 +512,7 @@ bool ProcessCmd(int fd, const PbCommand &command)
string params = command.params().c_str(); string params = command.params().c_str();
ostringstream s; ostringstream s;
s << "Processing: cmd=" << cmd << ", id=" << id << ", un=" << un << ", type=" << type << ", params=" << params << endl; s << "Processing: cmd=" << PbOperation_Name(cmd) << ", id=" << id << ", un=" << un << ", type=" << PbDeviceType_Name(type) << ", params=" << params;
LOGINFO("%s", s.str().c_str()); LOGINFO("%s", s.str().c_str());
// Copy the Unit List // Copy the Unit List
@ -555,7 +579,7 @@ bool ProcessCmd(int fd, const PbCommand &command)
break; break;
default: default:
ostringstream error; ostringstream error;
error << "rasctl sent a command for an invalid drive type: " << type; error << "rasctl sent a command for an invalid drive type: " << PbDeviceType_Name(type);
return ReturnStatus(fd, false, error.str()); return ReturnStatus(fd, false, error.str());
} }
@ -576,13 +600,18 @@ bool ProcessCmd(int fd, const PbCommand &command)
// Open the file path // Open the file path
if (!pUnit->Open(filepath)) { if (!pUnit->Open(filepath)) {
delete pUnit; // If the file does not exist search for it in the default image folder
string default_file = default_image_folder + "/" + file;
filepath.SetPath(default_file.c_str());
if (!pUnit->Open(filepath)) {
delete pUnit;
LOGWARN("rasctl tried to open an invalid file %s", file.c_str()); LOGWARN("rasctl tried to open an invalid file %s", file.c_str());
ostringstream error; ostringstream error;
error << "File open error [" << file << "]"; error << "File open error [" << file << "]";
return ReturnStatus(fd, false, error.str()); return ReturnStatus(fd, false, error.str());
}
} }
} }
@ -674,7 +703,7 @@ bool ProcessCmd(int fd, const PbCommand &command)
default: default:
ostringstream error; ostringstream error;
error << "Received unknown command from rasctl: " << 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.str());
} }
@ -699,7 +728,7 @@ bool ParseArgument(int argc, char* argv[], int& port)
string log_level = "trace"; string log_level = "trace";
int opt; int opt;
while ((opt = getopt(argc, argv, "-IiHhG:g:D:d:p:")) != -1) { while ((opt = getopt(argc, argv, "-IiHhG:g:D:d:p:f:")) != -1) {
switch (tolower(opt)) { switch (tolower(opt)) {
case 'i': case 'i':
is_sasi = false; is_sasi = false;
@ -735,6 +764,17 @@ bool ParseArgument(int argc, char* argv[], int& port)
} }
continue; continue;
case 'f':
struct stat folder_stat;
stat(optarg, &folder_stat);
if (!S_ISDIR(folder_stat.st_mode) || access(optarg, F_OK) == -1) {
cerr << "Default image folder '" << optarg << "' is not accessible" << endl;
return false;
}
default_image_folder = optarg;
continue;
default: default:
return false; return false;
@ -787,7 +827,9 @@ bool ParseArgument(int argc, char* argv[], int& port)
id = -1; id = -1;
} }
SetLogLevel(log_level); if (!SetLogLevel(log_level)) {
LOGWARN("Invalid log level '%s'", log_level.c_str());
}
// Display and log the device list // Display and log the device list
const PbDevices devices = GetDevices(); const PbDevices devices = GetDevices();
@ -859,22 +901,47 @@ static void *MonThread(void *param)
PbCommand command; PbCommand command;
DeserializeMessage(fd, command); DeserializeMessage(fd, command);
// List and log all of the devices switch(command.cmd()) {
if (command.cmd() == LIST) { case LIST: {
const PbDevices devices = GetDevices(); const PbDevices devices = GetDevices();
SerializeMessage(fd, devices); SerializeMessage(fd, devices);
LogDeviceList(ListDevices(devices)); LogDeviceList(ListDevices(devices));
} break;
else if (command.cmd() == LOG_LEVEL) {
SetLogLevel(command.params());
}
else {
// Wait until we become idle
while (active) {
usleep(500 * 1000);
} }
ProcessCmd(fd, command); case LOG_LEVEL: {
bool status = SetLogLevel(command.params());
if (!status) {
ostringstream error;
error << "Invalid log level: " << command.params();
ReturnStatus(fd, false, error.str());
}
else {
ReturnStatus(fd);
}
break;
}
case SERVER_INFO: {
PbServerInfo serverInfo;
serverInfo.set_rascsi_version(rascsi_get_version_string());
GetAvailableLogLevels(serverInfo);
serverInfo.set_current_log_level(current_log_level);
serverInfo.set_default_image_folder(default_image_folder);
GetAvailableImages(serverInfo);
SerializeMessage(fd, serverInfo);
break;
}
default: {
// Wait until we become idle
while (active) {
usleep(500 * 1000);
}
ProcessCmd(fd, command);
break;
}
} }
close(fd); close(fd);
@ -912,7 +979,15 @@ int main(int argc, char* argv[])
setvbuf(stdout, NULL, _IONBF, 0); setvbuf(stdout, NULL, _IONBF, 0);
struct sched_param schparam; struct sched_param schparam;
set_level(level::trace); available_log_levels.push_back("trace");
available_log_levels.push_back("debug");
available_log_levels.push_back("info");
available_log_levels.push_back("warn");
available_log_levels.push_back("err");
available_log_levels.push_back("critical");
available_log_levels.push_back("off");
SetLogLevel("trace");
// Create a thread-safe stdout logger to process the log messages // Create a thread-safe stdout logger to process the log messages
auto logger = stdout_color_mt("rascsi stdout logger"); auto logger = stdout_color_mt("rascsi stdout logger");

View File

@ -17,13 +17,14 @@ enum PbDeviceType {
// rascsi remote operations // rascsi remote operations
enum PbOperation { enum PbOperation {
NONE = 0; NONE = 0;
LIST = 1; SERVER_INFO = 1;
ATTACH = 2; LIST = 2;
DETACH = 3; ATTACH = 3;
INSERT = 4; DETACH = 4;
EJECT = 5; INSERT = 5;
PROTECT = 6; EJECT = 6;
LOG_LEVEL = 7; PROTECT = 7;
LOG_LEVEL = 8;
} }
// Commands rascsi can execute // Commands rascsi can execute
@ -52,4 +53,15 @@ message PbDevice {
message PbDevices { message PbDevices {
repeated PbDevice devices = 1; repeated PbDevice devices = 1;
}
// The rascsi server information
message PbServerInfo {
string rascsi_version = 1;
// Sorted by severity
repeated string available_log_levels = 2;
string current_log_level = 3;
string default_image_folder = 4;
// Files in the default folder
repeated string available_image_files = 5;
} }

View File

@ -18,6 +18,7 @@
#include "rascsi_interface.pb.h" #include "rascsi_interface.pb.h"
#include <sstream> #include <sstream>
#include <iostream> #include <iostream>
#include <list>
using namespace std; using namespace std;
using namespace rascsi_interface; using namespace rascsi_interface;
@ -152,6 +153,63 @@ void CommandLogLevel(const string& hostname, int port, const string& log_level)
close(fd); close(fd);
} }
void CommandServerInfo(const string& hostname, int port)
{
PbCommand command;
command.set_cmd(SERVER_INFO);
int fd = SendCommand(hostname.c_str(), port, command);
if (fd < 0) {
exit(ENOTCONN);
}
PbServerInfo serverInfo;
try {
DeserializeMessage(fd, serverInfo);
}
catch(const ioexception& e) {
cerr << "Error: " << e.getmsg() << endl;
close(fd);
exit(-1);
}
close(fd);
cout << "rascsi version: " << serverInfo.rascsi_version() << endl;
if (!serverInfo.available_log_levels_size()) {
cout << " No log level settings available" << endl;
}
else {
cout << "Available log levels, sorted by severity:" << endl;
for (int i = 0; i < serverInfo.available_log_levels_size(); i++) {
cout << " " << serverInfo.available_log_levels(i) << endl;
}
cout << "Current log level: " << serverInfo.current_log_level() << endl;
}
cout << "Default image file folder: " << serverInfo.default_image_folder() << endl;
if (!serverInfo.available_image_files_size()) {
cout << " No image files available in the default folder" << endl;
}
else {
list<string> sorted_image_files;
for (int i = 0; i < serverInfo.available_image_files_size(); i++) {
sorted_image_files.push_back(serverInfo.available_image_files(i));
}
sorted_image_files.sort();
cout << "Image files available in the default folder:" << endl;
for (auto it = sorted_image_files.begin(); it != sorted_image_files.end(); ++it) {
cout << " " << *it << endl;
}
}
}
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
// //
// Main processing // Main processing
@ -165,7 +223,7 @@ int main(int argc, char* argv[])
if (argc < 2) { if (argc < 2) {
cerr << "SCSI Target Emulator RaSCSI Controller" << endl; cerr << "SCSI Target Emulator RaSCSI Controller" << endl;
cerr << "version " << rascsi_get_version_string() << " (" << __DATE__ << ", " << __TIME__ << ")" << endl; cerr << "version " << rascsi_get_version_string() << " (" << __DATE__ << ", " << __TIME__ << ")" << endl;
cerr << "Usage: " << argv[0] << " -i ID [-u UNIT] [-c CMD] [-t TYPE] [-f FILE] [-h HOSTNAME] [-p PORT] [-g LOG_LEVEL]" << endl; cerr << "Usage: " << argv[0] << " -i ID [-u UNIT] [-c CMD] [-t TYPE] [-f FILE] [-h HOSTNAME] [-p PORT] [-g LOG_LEVEL] [-s]" << 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}" << endl;
@ -191,7 +249,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:l")) != -1) { while ((opt = getopt(argc, argv, "i:u:c:t:f:h:p:g:ls")) != -1) {
switch (opt) { switch (opt) {
case 'i': case 'i':
id = optarg[0] - '0'; id = optarg[0] - '0';
@ -281,6 +339,10 @@ int main(int argc, char* argv[])
cmd = LOG_LEVEL; cmd = LOG_LEVEL;
params = optarg; params = optarg;
break; break;
case 's':
cmd = SERVER_INFO;
break;
} }
} }
@ -291,6 +353,11 @@ int main(int argc, char* argv[])
exit(0); exit(0);
} }
if (cmd == SERVER_INFO) {
CommandServerInfo(hostname, port);
exit(0);
}
// List display only // List display only
if (cmd == LIST || (id < 0 && type == UNDEFINED && params.empty())) { if (cmd == LIST || (id < 0 && type == UNDEFINED && params.empty())) {
CommandList(hostname, port); CommandList(hostname, port);