diff options
-rw-r--r-- | src/Client/CMakeLists.txt | 2 | ||||
-rw-r--r-- | src/Client/CommandParser.cpp | 459 | ||||
-rw-r--r-- | src/Client/CommandParser.h | 35 | ||||
-rw-r--r-- | src/Client/SystemCommands.cpp | 262 | ||||
-rw-r--r-- | src/Client/SystemCommands.h | 49 | ||||
-rw-r--r-- | src/Client/UserCommands.cpp | 230 | ||||
-rw-r--r-- | src/Client/UserCommands.h | 47 |
7 files changed, 624 insertions, 460 deletions
diff --git a/src/Client/CMakeLists.txt b/src/Client/CMakeLists.txt index e86a839..e663602 100644 --- a/src/Client/CMakeLists.txt +++ b/src/Client/CMakeLists.txt @@ -5,5 +5,7 @@ include_directories(${INCLUDES}) add_library(Client CommandParser.cpp CommandParser.h InformationManager.cpp InformationManager.h + SystemCommands.cpp SystemCommands.h + UserCommands.cpp UserCommands.h ) target_link_libraries(Client ClientRequests Common) diff --git a/src/Client/CommandParser.cpp b/src/Client/CommandParser.cpp index 2a67dab..19aa8a9 100644 --- a/src/Client/CommandParser.cpp +++ b/src/Client/CommandParser.cpp @@ -19,21 +19,15 @@ #include "CommandParser.h" #include "InformationManager.h" -#include "Requests/DaemonFSInfoRequest.h" -#include "Requests/DaemonCommandRequest.h" -#include "Requests/DaemonStatusRequest.h" +#include "SystemCommands.h" +#include "UserCommands.h" + #include <Common/Logger.h> #include <Common/RequestManager.h> -#include <Common/Requests/FSInfoRequest.h> -#include <Common/Requests/DisconnectRequest.h> -#include <Common/Requests/GroupListRequest.h> -#include <Common/Requests/GroupUserListRequest.h> -#include <Common/Requests/StatusRequest.h> -#include <Common/Requests/UserInfoRequest.h> -#include <Common/Requests/UserListRequest.h> -#include <Common/Requests/UserGroupListRequest.h> #include <Common/Tokenizer.h> +#include <Common/Requests/DisconnectRequest.h> + #include <iostream> #include <cstdio> @@ -43,17 +37,17 @@ namespace Mad { namespace Client { const CommandParser::Command CommandParser::commands[] = { - {{"fsinfo", 0}, "fsinfo [host]", "Display file system usage information", "Display file system usage information of a host or the server (if no host is given).", &CommandParser::fsinfoCommand}, + {{"fsinfo", 0}, "fsinfo [host]", "Display file system usage information", "Display file system usage information of a host or the server (if no host is given).", &SystemCommands::fsinfoCommand}, {{"help", "?", 0}, "help [command]", "Display usage information about commands", "Display usage information about a command. If no command is given, display a list of all available commands.", &CommandParser::helpCommand}, {{"list_hosts", "hosts", 0}, "list_hosts [-a]", "List the currently active hosts", "List the currently active hosts.\n\n -a\tAlso list inactive hosts", &CommandParser::listHostsCommand}, - {{"reboot", 0}, "reboot *|host...", "Reboot host", "Reboot hosts. * will reboot all hosts.", &CommandParser::rebootCommand}, - {{"shutdown", "halt", 0}, "shutdown *|host...", "Shut hosts down", "Shut hosts down. * will shut down all hosts.", &CommandParser::shutdownCommand}, - {{"status", "st", 0}, "status [host]", "Display status information", "Display host status information. If no host is given, display server status information.", &CommandParser::statusCommand}, - {{"user_info", "user", 0}, "user_info uid", "Search for a user id", "Search for a user id.", &CommandParser::userInfoCommand}, - {{"list_users", "users", 0}, "list_users", "Show the user account database", "Show the user account database.", &CommandParser::listUsersCommand}, - {{"list_user_groups", "user_groups", 0}, "list_user_groups uid", "List user's groups", "List the groups that the user is member of.", &CommandParser::listUserGroupsCommand}, - {{"list_groups", "groups", 0}, "list_groups", "Show the user group database", "Show the user group database.", &CommandParser::listGroupsCommand}, - {{"list_group_users", "group_users", 0}, "list_group_users gid", "List group's users", "List the users that are members of the group.", &CommandParser::listGroupUsersCommand}, + {{"reboot", 0}, "reboot *|host...", "Reboot host", "Reboot hosts. * will reboot all hosts.", &SystemCommands::rebootCommand}, + {{"shutdown", "halt", 0}, "shutdown *|host...", "Shut hosts down", "Shut hosts down. * will shut down all hosts.", &SystemCommands::shutdownCommand}, + {{"status", "st", 0}, "status [host]", "Display status information", "Display host status information. If no host is given, display server status information.", &SystemCommands::statusCommand}, + {{"user_info", "user", 0}, "user_info uid", "Search for a user id", "Search for a user id.", &UserCommands::userInfoCommand}, + {{"list_users", "users", 0}, "list_users", "Show the user account database", "Show the user account database.", &UserCommands::listUsersCommand}, + {{"list_user_groups", "user_groups", 0}, "list_user_groups uid", "List user's groups", "List the groups that the user is member of.", &UserCommands::listUserGroupsCommand}, + {{"list_groups", "groups", 0}, "list_groups", "Show the user group database", "Show the user group database.", &UserCommands::listGroupsCommand}, + {{"list_group_users", "group_users", 0}, "list_group_users gid", "List group's users", "List the users that are members of the group.", &UserCommands::listGroupUsersCommand}, {{"exit", "quit", 0}, "exit", "Close the connection and quit the client", "Close the connection and quit the client.", &CommandParser::exitCommand}, {{0}, 0, 0, 0, 0} }; @@ -115,142 +109,7 @@ std::map<std::string, Common::HostInfo> CommandParser::parseHostList(const std:: return ret; } - -void CommandParser::printFSInfo(boost::shared_ptr<const Common::XmlPacket> &packet) { - const std::string units[] = { - "kB", "MB", "GB", "TB", "" - }; - - for(size_t i = 0; i < (*packet)["filesystems"].getSize(); ++i) { - const Common::XmlPacket::Entry &fs = (*packet)["filesystems"][i]; - - unsigned usedUnit = 0, totalUnit = 0; - - float used = fs["usedSize"]; - float total = fs["totalSize"]; - float available = fs["availableSize"]; - - while(used >= 1024 && !units[usedUnit+1].empty()) { - ++usedUnit; - used /= 1024; - available /= 1024; - } - - while(total >= 1024 && !units[totalUnit+1].empty()) { - ++totalUnit; - total /= 1024; - } - - - std::string name = fs["name"]; - std::string mountedOn = fs["mountedOn"]; - - std::string nameString = mountedOn + " (" + name + ")"; - - if(nameString.length() < 32) { - nameString.resize(32, ' '); - } - else { - nameString += "\n\t"; - nameString.resize(nameString.length() + 32, ' '); - } - - std::printf("\t%s%.*f%s", nameString.c_str(), (used < 10) ? 2 : 1, used, (usedUnit == totalUnit) ? "" : (" " + units[usedUnit]).c_str()); - std::printf("/%.*f %s (%.1f%%)\n", (total < 10) ? 2 : 1, total, units[totalUnit].c_str(), std::min(100*used/(used+available), 100.0f)); - } - - std::printf("\n"); -} - -void CommandParser::printHostStatus(boost::shared_ptr<const Common::XmlPacket> &packet) { - unsigned long uptime = (*packet)["uptime"]; - - if(uptime) { - unsigned long uptime = (*packet)["uptime"]; - - unsigned long days = uptime/86400; - unsigned long hours = (uptime%86400)/3600; - unsigned long minutes = (uptime%3600)/60; - - std::printf("\tUptime:\t\t"); - - if(days) std::printf("%lu days ", days); - - std::printf("%lu:%02lu", hours, minutes); - - std::printf(" (load average: %.2f %.2f %.2f, %lu processes)", (float)(*packet)["loadAvg1"], (float)(*packet)["loadAvg5"], (float)(*packet)["loadAvg15"], (unsigned long)(*packet)["nProcesses"]); - - std::printf("\n\n"); - } - - if((unsigned long)(*packet)["totalMem"] && (unsigned long)(*packet)["freeMem"]) { - const std::string units[] = { - "kB", "MB", "GB", "TB", "" - }; - - unsigned unit = 0; - float totalMem = (*packet)["totalMem"], usedMem = totalMem-(float)(*packet)["freeMem"]; - - while(totalMem >= 1024 && !units[unit+1].empty()) { - ++unit; - totalMem /= 1024; - usedMem /= 1024; - } - - std::printf("\tMemory usage:\t%.*f/%.*f %s", (usedMem < 10) ? 2 : 1, usedMem, (totalMem < 10) ? 2 : 1, totalMem, units[unit].c_str()); - std::printf(" (%.1f%%)\n", usedMem*100.0f/totalMem); - - if((unsigned long)(*packet)["totalSwap"] && (unsigned long)(*packet)["freeSwap"]) { - unit = 0; - totalMem = (*packet)["totalSwap"]; usedMem = totalMem-(float)(*packet)["freeSwap"]; - - while(totalMem >= 1024 && !units[unit+1].empty()) { - ++unit; - totalMem /= 1024; - usedMem /= 1024; - } - - std::printf("\tSwap usage:\t%.*f/%.*f %s", (usedMem < 10) ? 2 : 1, usedMem, (totalMem < 10) ? 2 : 1, totalMem, units[unit].c_str()); - std::printf(" (%.1f%%)\n", usedMem*100.0f/totalMem); - } - - std::printf("\n"); - } -} - - -void CommandParser::fsinfoCommand(const std::vector<std::string> &args) { - boost::shared_ptr<Common::Request> request; - - if(args.size() == 1) - request = boost::shared_ptr<Common::Requests::FSInfoRequest>(new Common::Requests::FSInfoRequest); - else if(args.size() == 2) - request = boost::shared_ptr<Requests::DaemonFSInfoRequest>(new Requests::DaemonFSInfoRequest(args[1])); - else { - Common::Logger::logf(Common::Logger::ERROR, "%s: Too many arguments.", args[0].c_str()); - printUsage("fsinfo"); - return; - } - - Common::RequestManager::get()->sendRequest(connection, request); - request->wait(); - - std::pair<boost::shared_ptr<const Common::XmlPacket>, Net::Exception> result = request->getResult(); - - if(!result.first || result.second) { - Common::Logger::logf(Common::Logger::ERROR, "An error occurred during your request: %s.", result.second.strerror().c_str()); - } - else { - if(args.size() == 1) - std::cout << "Server file system usage:" << std::endl; - else - std::cout << "Host file system usage:" << std::endl; - - printFSInfo(result.first); - } -} - -void CommandParser::helpCommand(const std::vector<std::string> &args) { +void CommandParser::helpCommand(const std::vector<std::string> &args, Common::Connection *connection _UNUSED_PARAMETER_) { if(args.size() == 1) { std::cout << "Available commands:" << std::endl << std::endl; @@ -279,7 +138,7 @@ void CommandParser::helpCommand(const std::vector<std::string> &args) { } } -void CommandParser::listHostsCommand(const std::vector<std::string> &args) { +void CommandParser::listHostsCommand(const std::vector<std::string> &args, Common::Connection *connection _UNUSED_PARAMETER_) { const std::map<std::string, Common::HostInfo>& hosts = InformationManager::get()->getDaemons(); if(args.size() == 1) { @@ -331,287 +190,7 @@ void CommandParser::listHostsCommand(const std::vector<std::string> &args) { } } -void CommandParser::rebootCommand(const std::vector<std::string> &args) { - if(args.size() < 2) { - Common::Logger::logf(Common::Logger::ERROR, "%s: No host given.", args[0].c_str()); - printUsage("reboot"); - return; - } - - std::map<std::string, Common::HostInfo> hosts = parseHostList(std::vector<std::string>(args.begin()+1, args.end()), true); - - std::vector<boost::shared_ptr<Requests::DaemonCommandRequest> > requests; - - for(std::map<std::string, Common::HostInfo>::iterator host = hosts.begin(); host != hosts.end(); ++host) { - boost::shared_ptr<Requests::DaemonCommandRequest> request(new Requests::DaemonCommandRequest(host->first, true)); - Common::RequestManager::get()->sendRequest(connection, request); - - requests.push_back(request); - } - - for(std::vector<boost::shared_ptr<Requests::DaemonCommandRequest> >::iterator request = requests.begin(); request != requests.end(); ++request) - (*request)->wait(); - - for(std::vector<boost::shared_ptr<Requests::DaemonCommandRequest> >::iterator request = requests.begin(); request != requests.end(); ++request) { - std::pair<boost::shared_ptr<const Common::XmlPacket>, Net::Exception> result = (*request)->getResult(); - - if(result.second) - Common::Logger::logf(Common::Logger::ERROR, "An error occurred during your request: %s.", result.second.strerror().c_str()); - } -} - -void CommandParser::shutdownCommand(const std::vector<std::string> &args) { - if(args.size() < 2) { - Common::Logger::logf(Common::Logger::ERROR, "%s: No host given.", args[0].c_str()); - printUsage("shutdown"); - return; - } - - std::map<std::string, Common::HostInfo> hosts = parseHostList(std::vector<std::string>(args.begin()+1, args.end()), true); - - std::vector<boost::shared_ptr<Requests::DaemonCommandRequest> > requests; - - for(std::map<std::string, Common::HostInfo>::iterator host = hosts.begin(); host != hosts.end(); ++host) { - boost::shared_ptr<Requests::DaemonCommandRequest> request(new Requests::DaemonCommandRequest(host->first, false)); - Common::RequestManager::get()->sendRequest(connection, request); - - requests.push_back(request); - } - - for(std::vector<boost::shared_ptr<Requests::DaemonCommandRequest> >::iterator request = requests.begin(); request != requests.end(); ++request) - (*request)->wait(); - - for(std::vector<boost::shared_ptr<Requests::DaemonCommandRequest> >::iterator request = requests.begin(); request != requests.end(); ++request) { - std::pair<boost::shared_ptr<const Common::XmlPacket>, Net::Exception> result = (*request)->getResult(); - - if(result.second) - Common::Logger::logf(Common::Logger::ERROR, "An error occurred during your request: %s.", result.second.strerror().c_str()); - } -} - -void CommandParser::statusCommand(const std::vector<std::string> &args) { - boost::shared_ptr<Common::Request> request; - - if(args.size() == 1) - request = boost::shared_ptr<Common::Requests::StatusRequest>(new Common::Requests::StatusRequest); - else if(args.size() == 2) - request = boost::shared_ptr<Requests::DaemonStatusRequest>(new Requests::DaemonStatusRequest(args[1])); - else { - Common::Logger::logf(Common::Logger::ERROR, "%s: Too many arguments.", args[0].c_str()); - printUsage("status"); - return; - } - - Common::RequestManager::get()->sendRequest(connection, request); - request->wait(); - - std::pair<boost::shared_ptr<const Common::XmlPacket>, Net::Exception> result = request->getResult(); - - if(!result.first || result.second) { - Common::Logger::logf(Common::Logger::ERROR, "An error occurred during your request: %s.", result.second.strerror().c_str()); - } - else { - if(args.size() == 1) - std::cout << "Server status:" << std::endl; - else - std::cout << "Host status:" << std::endl; - - printHostStatus(result.first); - } -} - -void CommandParser::userInfoCommand(const std::vector<std::string> &args) { - if(args.size() == 1) { - Common::Logger::logf(Common::Logger::ERROR, "%s: No user id given.", args[0].c_str()); - printUsage("user_info"); - return; - } - if(args.size() > 2) { - Common::Logger::logf(Common::Logger::ERROR, "%s: Too many arguments.", args[0].c_str()); - printUsage("user_info"); - return; - } - - char *endptr; - unsigned long uid = std::strtoul(args[1].c_str(), &endptr, 10); - if(args[1].empty() || *endptr != '\0') { - Common::Logger::logf(Common::Logger::ERROR, "%s: Unable to parse user id.", args[0].c_str()); - printUsage("user_info"); - return; - } - - boost::shared_ptr<Common::Requests::UserInfoRequest> request(new Common::Requests::UserInfoRequest(uid)); - Common::RequestManager::get()->sendRequest(connection, request); - request->wait(); - - std::pair<boost::shared_ptr<const Common::XmlPacket>, Net::Exception> result = request->getResult(); - - if(!result.first || result.second) { - Common::Logger::logf(Common::Logger::ERROR, "An error occurred during your request: %s.", result.second.strerror().c_str()); - } - else { - const Common::XmlPacket &packet = *result.first; - - std::cout << " " << (unsigned long)packet["uid"] << ", " << (unsigned long)packet["gid"] << ", " << (const std::string&)packet["username"] << ", " - << (const std::string&)packet["fullName"] << std::endl << std::endl; - } -} - -void CommandParser::listUsersCommand(const std::vector<std::string>&) { - boost::shared_ptr<Common::Requests::UserListRequest> request(new Common::Requests::UserListRequest); - - Common::RequestManager::get()->sendRequest(connection, request); - request->wait(); - - std::pair<boost::shared_ptr<const Common::XmlPacket>, Net::Exception> result = request->getResult(); - - if(!result.first || result.second) { - Common::Logger::logf(Common::Logger::ERROR, "An error occurred during your request: %s.", result.second.strerror().c_str()); - } - else { - const Common::XmlPacket::Element &users = (*result.first)["users"]; - - if(users.isEmpty()) { - std::cout << "User list is empty." << std::endl; - } - else { - std::cout << "Found " << users.getSize() << " user" << (users.getSize()==1 ? "" : "s") << ":" << std::endl; - - - for(size_t i = 0; i < users.getSize(); ++i) - std::cout << " " << (unsigned long)users[i]["uid"] << ", " << (unsigned long)users[i]["gid"] << ", " << (const std::string&)users[i]["username"] << ", " << (const std::string&)users[i]["fullName"] << std::endl; - } - - std::cout << std::endl; - } -} - -void CommandParser::listUserGroupsCommand(const std::vector<std::string> &args) { - if(args.size() == 1) { - Common::Logger::logf(Common::Logger::ERROR, "%s: No user id given.", args[0].c_str()); - printUsage("list_user_groups"); - return; - } - if(args.size() > 2) { - Common::Logger::logf(Common::Logger::ERROR, "%s: Too many arguments.", args[0].c_str()); - printUsage("list_user_groups"); - return; - } - - char *endptr; - unsigned long uid = std::strtoul(args[1].c_str(), &endptr, 10); - if(args[1].empty() || *endptr != '\0') { - Common::Logger::logf(Common::Logger::ERROR, "%s: Unable to parse user id.", args[0].c_str()); - printUsage("list_user_groups"); - return; - } - - boost::shared_ptr<Common::Requests::UserGroupListRequest> request(new Common::Requests::UserGroupListRequest(uid)); - Common::RequestManager::get()->sendRequest(connection, request); - request->wait(); - - std::pair<boost::shared_ptr<const Common::XmlPacket>, Net::Exception> result = request->getResult(); - - if(!result.first || result.second) { - Common::Logger::logf(Common::Logger::ERROR, "An error occurred during your request: %s.", result.second.strerror().c_str()); - } - else { - const Common::XmlPacket::Element &groups = (*result.first)["groups"]; - - if(groups.isEmpty()) { - std::cout << "The user isn't member of any group." << std::endl; - } - else { - - std::cout << "User is member of " << groups.getSize() << " group" << (groups.getSize()==1 ? "" : "s") << ":" << std::endl; - - - for(size_t i = 0; i < groups.getSize(); ++i) - std::cout << " " << (unsigned long)groups[i]["gid"] << std::endl; - } - - std::cout << std::endl; - } -} - -void CommandParser::listGroupsCommand(const std::vector<std::string>&) { - boost::shared_ptr<Common::Requests::GroupListRequest> request(new Common::Requests::GroupListRequest); - - Common::RequestManager::get()->sendRequest(connection, request); - request->wait(); - - std::pair<boost::shared_ptr<const Common::XmlPacket>, Net::Exception> result = request->getResult(); - - if(!result.first || result.second) { - Common::Logger::logf(Common::Logger::ERROR, "An error occurred during your request: %s.", result.second.strerror().c_str()); - } - else { - const Common::XmlPacket::Element &groups = (*result.first)["groups"]; - - if(groups.isEmpty()) { - std::cout << "Group list is empty." << std::endl; - } - else { - std::cout << "Found " << groups.getSize() << " group" << (groups.getSize()==1 ? "" : "s") << ":" << std::endl; - - - for(size_t i = 0; i < groups.getSize(); ++i) - std::cout << " " << (unsigned long)groups[i]["gid"] << ", " << (const std::string&)groups[i]["name"] << std::endl; - } - - std::cout << std::endl; - } -} - -void CommandParser::listGroupUsersCommand(const std::vector<std::string> &args) { - if(args.size() == 1) { - Common::Logger::logf(Common::Logger::ERROR, "%s: No group id given.", args[0].c_str()); - printUsage("list_group_users"); - return; - } - if(args.size() > 2) { - Common::Logger::logf(Common::Logger::ERROR, "%s: Too many arguments.", args[0].c_str()); - printUsage("list_group_users"); - return; - } - - char *endptr; - unsigned long gid = std::strtoul(args[1].c_str(), &endptr, 10); - if(args[1].empty() || *endptr != '\0') { - Common::Logger::logf(Common::Logger::ERROR, "%s: Unable to parse group id.", args[0].c_str()); - printUsage("list_group_users"); - return; - } - - boost::shared_ptr<Common::Requests::GroupUserListRequest> request(new Common::Requests::GroupUserListRequest(gid)); - Common::RequestManager::get()->sendRequest(connection, request); - request->wait(); - - std::pair<boost::shared_ptr<const Common::XmlPacket>, Net::Exception> result = request->getResult(); - - if(!result.first || result.second) { - Common::Logger::logf(Common::Logger::ERROR, "An error occurred during your request: %s.", result.second.strerror().c_str()); - } - else { - const Common::XmlPacket::Element &users = (*result.first)["users"]; - - if(users.isEmpty()) { - std::cout << "The group doesn't have any members." << std::endl; - } - else { - - std::cout << "The group has " << users.getSize() << " member" << (users.getSize()==1 ? "" : "s") << ":" << std::endl; - - - for(size_t i = 0; i < users.getSize(); ++i) - std::cout << " " << (unsigned long)users[i]["uid"] << std::endl; - } - - std::cout << std::endl; - } -} - -void CommandParser::exitCommand(const std::vector<std::string>&) { +void CommandParser::exitCommand(const std::vector<std::string> &args _UNUSED_PARAMETER_, Common::Connection *connection) { boost::shared_ptr<Common::Requests::DisconnectRequest> request(new Common::Requests::DisconnectRequest); Common::RequestManager::get()->sendRequest(connection, request); @@ -622,7 +201,7 @@ void CommandParser::exitCommand(const std::vector<std::string>&) { if(result.second) Common::Logger::logf(Common::Logger::ERROR, "An error occurred during your request: %s.", result.second.strerror().c_str()); else - disconnect = true; + commandParser.disconnect = true; } bool CommandParser::parse(const std::string &cmd) { @@ -636,7 +215,7 @@ bool CommandParser::parse(const std::string &cmd) { const Command* command = findCommand(splitCmd[0]); if(command) - (this->*command->funcPtr)(splitCmd); + command->function(splitCmd, connection); else Common::Logger::logf(Common::Logger::ERROR, "Unknown command '%s'.", splitCmd[0].c_str()); diff --git a/src/Client/CommandParser.h b/src/Client/CommandParser.h index ca9b59e..a7157ed 100644 --- a/src/Client/CommandParser.h +++ b/src/Client/CommandParser.h @@ -23,6 +23,7 @@ #include <Common/HostInfo.h> #include <Common/XmlPacket.h> +#include <boost/function.hpp> #include <boost/smart_ptr.hpp> #include <map> @@ -37,15 +38,21 @@ class Connection; namespace Client { +class SystemCommands; +class UserCommands; + class CommandParser { private: + friend class SystemCommands; + friend class UserCommands; + struct Command { const char* commands[3]; const char* cmdline; const char* desc; const char* longdesc; - void (CommandParser::*funcPtr) (const std::vector<std::string> &args); + boost::function2<void, const std::vector<std::string>, Common::Connection*> function; }; static const Command commands[]; @@ -56,26 +63,14 @@ class CommandParser { bool disconnect; - const Command* findCommand(const std::string& command); - void printUsage(const std::string& command); - - std::map<std::string, Common::HostInfo> parseHostList(const std::vector<std::string> &args, bool mustBeActive = false); + static const Command* findCommand(const std::string& command); - void printFSInfo(boost::shared_ptr<const Common::XmlPacket> &packet); - void printHostStatus(boost::shared_ptr<const Common::XmlPacket> &packet); + static void printUsage(const std::string& command); + static std::map<std::string, Common::HostInfo> parseHostList(const std::vector<std::string> &args, bool mustBeActive = false); - void fsinfoCommand(const std::vector<std::string> &args); - void helpCommand(const std::vector<std::string> &args); - void listHostsCommand(const std::vector<std::string> &args); - void rebootCommand(const std::vector<std::string> &args); - void shutdownCommand(const std::vector<std::string> &args); - void statusCommand(const std::vector<std::string> &args); - void userInfoCommand(const std::vector<std::string> &args); - void listUsersCommand(const std::vector<std::string> &args); - void listUserGroupsCommand(const std::vector<std::string> &args); - void listGroupsCommand(const std::vector<std::string> &args); - void listGroupUsersCommand(const std::vector<std::string> &args); - void exitCommand(const std::vector<std::string>&); + static void helpCommand(const std::vector<std::string> &args, Common::Connection *connection); + static void listHostsCommand(const std::vector<std::string> &args, Common::Connection *connection); + static void exitCommand(const std::vector<std::string> &args, Common::Connection *connection); CommandParser() : connection(0), disconnect(false) {} @@ -95,7 +90,7 @@ class CommandParser { bool parse(const std::string &cmd); void requestDisconnect() { - exitCommand(std::vector<std::string>()); + exitCommand(std::vector<std::string>(), connection); } bool willDisconnect() const { diff --git a/src/Client/SystemCommands.cpp b/src/Client/SystemCommands.cpp new file mode 100644 index 0000000..5409828 --- /dev/null +++ b/src/Client/SystemCommands.cpp @@ -0,0 +1,262 @@ +/* + * SystemCommands.cpp + * + * Copyright (C) 2009 Matthias Schiffer <matthias@gamezock.de> + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "SystemCommands.h" +#include "CommandParser.h" + +#include <Common/Logger.h> +#include <Common/RequestManager.h> + +#include "Requests/DaemonFSInfoRequest.h" +#include "Requests/DaemonCommandRequest.h" +#include "Requests/DaemonStatusRequest.h" +#include <Common/Requests/FSInfoRequest.h> +#include <Common/Requests/StatusRequest.h> + +#include <iostream> + + +namespace Mad { +namespace Client { + +void SystemCommands::printFSInfo(boost::shared_ptr<const Common::XmlPacket> &packet) { + const std::string units[] = { + "kB", "MB", "GB", "TB", "" + }; + + for(size_t i = 0; i < (*packet)["filesystems"].getSize(); ++i) { + const Common::XmlPacket::Entry &fs = (*packet)["filesystems"][i]; + + unsigned usedUnit = 0, totalUnit = 0; + + float used = fs["usedSize"]; + float total = fs["totalSize"]; + float available = fs["availableSize"]; + + while(used >= 1024 && !units[usedUnit+1].empty()) { + ++usedUnit; + used /= 1024; + available /= 1024; + } + + while(total >= 1024 && !units[totalUnit+1].empty()) { + ++totalUnit; + total /= 1024; + } + + + std::string name = fs["name"]; + std::string mountedOn = fs["mountedOn"]; + + std::string nameString = mountedOn + " (" + name + ")"; + + if(nameString.length() < 32) { + nameString.resize(32, ' '); + } + else { + nameString += "\n\t"; + nameString.resize(nameString.length() + 32, ' '); + } + + std::printf("\t%s%.*f%s", nameString.c_str(), (used < 10) ? 2 : 1, used, (usedUnit == totalUnit) ? "" : (" " + units[usedUnit]).c_str()); + std::printf("/%.*f %s (%.1f%%)\n", (total < 10) ? 2 : 1, total, units[totalUnit].c_str(), std::min(100*used/(used+available), 100.0f)); + } + + std::printf("\n"); +} + +void SystemCommands::printHostStatus(boost::shared_ptr<const Common::XmlPacket> &packet) { + unsigned long uptime = (*packet)["uptime"]; + + if(uptime) { + unsigned long uptime = (*packet)["uptime"]; + + unsigned long days = uptime/86400; + unsigned long hours = (uptime%86400)/3600; + unsigned long minutes = (uptime%3600)/60; + + std::printf("\tUptime:\t\t"); + + if(days) std::printf("%lu days ", days); + + std::printf("%lu:%02lu", hours, minutes); + + std::printf(" (load average: %.2f %.2f %.2f, %lu processes)", (float)(*packet)["loadAvg1"], (float)(*packet)["loadAvg5"], (float)(*packet)["loadAvg15"], (unsigned long)(*packet)["nProcesses"]); + + std::printf("\n\n"); + } + + if((unsigned long)(*packet)["totalMem"] && (unsigned long)(*packet)["freeMem"]) { + const std::string units[] = { + "kB", "MB", "GB", "TB", "" + }; + + unsigned unit = 0; + float totalMem = (*packet)["totalMem"], usedMem = totalMem-(float)(*packet)["freeMem"]; + + while(totalMem >= 1024 && !units[unit+1].empty()) { + ++unit; + totalMem /= 1024; + usedMem /= 1024; + } + + std::printf("\tMemory usage:\t%.*f/%.*f %s", (usedMem < 10) ? 2 : 1, usedMem, (totalMem < 10) ? 2 : 1, totalMem, units[unit].c_str()); + std::printf(" (%.1f%%)\n", usedMem*100.0f/totalMem); + + if((unsigned long)(*packet)["totalSwap"] && (unsigned long)(*packet)["freeSwap"]) { + unit = 0; + totalMem = (*packet)["totalSwap"]; usedMem = totalMem-(float)(*packet)["freeSwap"]; + + while(totalMem >= 1024 && !units[unit+1].empty()) { + ++unit; + totalMem /= 1024; + usedMem /= 1024; + } + + std::printf("\tSwap usage:\t%.*f/%.*f %s", (usedMem < 10) ? 2 : 1, usedMem, (totalMem < 10) ? 2 : 1, totalMem, units[unit].c_str()); + std::printf(" (%.1f%%)\n", usedMem*100.0f/totalMem); + } + + std::printf("\n"); + } +} + + +void SystemCommands::fsinfoCommand(const std::vector<std::string> &args, Common::Connection *connection) { + boost::shared_ptr<Common::Request> request; + + if(args.size() == 1) + request = boost::shared_ptr<Common::Requests::FSInfoRequest>(new Common::Requests::FSInfoRequest); + else if(args.size() == 2) + request = boost::shared_ptr<Requests::DaemonFSInfoRequest>(new Requests::DaemonFSInfoRequest(args[1])); + else { + Common::Logger::logf(Common::Logger::ERROR, "%s: Too many arguments.", args[0].c_str()); + CommandParser::printUsage("fsinfo"); + return; + } + + Common::RequestManager::get()->sendRequest(connection, request); + request->wait(); + + std::pair<boost::shared_ptr<const Common::XmlPacket>, Net::Exception> result = request->getResult(); + + if(!result.first || result.second) { + Common::Logger::logf(Common::Logger::ERROR, "An error occurred during your request: %s.", result.second.strerror().c_str()); + } + else { + if(args.size() == 1) + std::cout << "Server file system usage:" << std::endl; + else + std::cout << "Host file system usage:" << std::endl; + + printFSInfo(result.first); + } +} + +void SystemCommands::rebootCommand(const std::vector<std::string> &args, Common::Connection *connection) { + if(args.size() < 2) { + Common::Logger::logf(Common::Logger::ERROR, "%s: No host given.", args[0].c_str()); + CommandParser::printUsage("reboot"); + return; + } + + std::map<std::string, Common::HostInfo> hosts = CommandParser::parseHostList(std::vector<std::string>(args.begin()+1, args.end()), true); + + std::vector<boost::shared_ptr<Requests::DaemonCommandRequest> > requests; + + for(std::map<std::string, Common::HostInfo>::iterator host = hosts.begin(); host != hosts.end(); ++host) { + boost::shared_ptr<Requests::DaemonCommandRequest> request(new Requests::DaemonCommandRequest(host->first, true)); + Common::RequestManager::get()->sendRequest(connection, request); + + requests.push_back(request); + } + + for(std::vector<boost::shared_ptr<Requests::DaemonCommandRequest> >::iterator request = requests.begin(); request != requests.end(); ++request) + (*request)->wait(); + + for(std::vector<boost::shared_ptr<Requests::DaemonCommandRequest> >::iterator request = requests.begin(); request != requests.end(); ++request) { + std::pair<boost::shared_ptr<const Common::XmlPacket>, Net::Exception> result = (*request)->getResult(); + + if(result.second) + Common::Logger::logf(Common::Logger::ERROR, "An error occurred during your request: %s.", result.second.strerror().c_str()); + } +} + +void SystemCommands::shutdownCommand(const std::vector<std::string> &args, Common::Connection *connection) { + if(args.size() < 2) { + Common::Logger::logf(Common::Logger::ERROR, "%s: No host given.", args[0].c_str()); + CommandParser::printUsage("shutdown"); + return; + } + + std::map<std::string, Common::HostInfo> hosts = CommandParser::parseHostList(std::vector<std::string>(args.begin()+1, args.end()), true); + + std::vector<boost::shared_ptr<Requests::DaemonCommandRequest> > requests; + + for(std::map<std::string, Common::HostInfo>::iterator host = hosts.begin(); host != hosts.end(); ++host) { + boost::shared_ptr<Requests::DaemonCommandRequest> request(new Requests::DaemonCommandRequest(host->first, false)); + Common::RequestManager::get()->sendRequest(connection, request); + + requests.push_back(request); + } + + for(std::vector<boost::shared_ptr<Requests::DaemonCommandRequest> >::iterator request = requests.begin(); request != requests.end(); ++request) + (*request)->wait(); + + for(std::vector<boost::shared_ptr<Requests::DaemonCommandRequest> >::iterator request = requests.begin(); request != requests.end(); ++request) { + std::pair<boost::shared_ptr<const Common::XmlPacket>, Net::Exception> result = (*request)->getResult(); + + if(result.second) + Common::Logger::logf(Common::Logger::ERROR, "An error occurred during your request: %s.", result.second.strerror().c_str()); + } +} + +void SystemCommands::statusCommand(const std::vector<std::string> &args, Common::Connection *connection) { + boost::shared_ptr<Common::Request> request; + + if(args.size() == 1) + request = boost::shared_ptr<Common::Requests::StatusRequest>(new Common::Requests::StatusRequest); + else if(args.size() == 2) + request = boost::shared_ptr<Requests::DaemonStatusRequest>(new Requests::DaemonStatusRequest(args[1])); + else { + Common::Logger::logf(Common::Logger::ERROR, "%s: Too many arguments.", args[0].c_str()); + CommandParser::printUsage("status"); + return; + } + + Common::RequestManager::get()->sendRequest(connection, request); + request->wait(); + + std::pair<boost::shared_ptr<const Common::XmlPacket>, Net::Exception> result = request->getResult(); + + if(!result.first || result.second) { + Common::Logger::logf(Common::Logger::ERROR, "An error occurred during your request: %s.", result.second.strerror().c_str()); + } + else { + if(args.size() == 1) + std::cout << "Server status:" << std::endl; + else + std::cout << "Host status:" << std::endl; + + printHostStatus(result.first); + } +} + +} +} diff --git a/src/Client/SystemCommands.h b/src/Client/SystemCommands.h new file mode 100644 index 0000000..142ebdc --- /dev/null +++ b/src/Client/SystemCommands.h @@ -0,0 +1,49 @@ +/* + * SystemCommands.h + * + * Copyright (C) 2009 Matthias Schiffer <matthias@gamezock.de> + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef MAD_CLIENT_SYSTEMCOMMANDS_H_ +#define MAD_CLIENT_SYSTEMCOMMANDS_H_ + +#include <Common/Connection.h> + +#include <string> +#include <vector> + + +namespace Mad { +namespace Client { + +class SystemCommands { + private: + SystemCommands(); + + static void printFSInfo(boost::shared_ptr<const Common::XmlPacket> &packet); + static void printHostStatus(boost::shared_ptr<const Common::XmlPacket> &packet); + + public: + static void fsinfoCommand(const std::vector<std::string> &args, Common::Connection *connection); + static void rebootCommand(const std::vector<std::string> &args, Common::Connection *connection); + static void shutdownCommand(const std::vector<std::string> &args, Common::Connection *connection); + static void statusCommand(const std::vector<std::string> &args, Common::Connection *connection); +}; + +} +} + +#endif /* MAD_CLIENT_SYSTEMCOMMANDS_H_ */ diff --git a/src/Client/UserCommands.cpp b/src/Client/UserCommands.cpp new file mode 100644 index 0000000..6defbcf --- /dev/null +++ b/src/Client/UserCommands.cpp @@ -0,0 +1,230 @@ +/* + * UserCommands.cpp + * + * Copyright (C) 2009 Matthias Schiffer <matthias@gamezock.de> + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "UserCommands.h" +#include "CommandParser.h" + +#include <Common/Logger.h> +#include <Common/RequestManager.h> + +#include <Common/Requests/GroupListRequest.h> +#include <Common/Requests/GroupUserListRequest.h> +#include <Common/Requests/UserInfoRequest.h> +#include <Common/Requests/UserListRequest.h> +#include <Common/Requests/UserGroupListRequest.h> + +#include <iostream> + + +namespace Mad { +namespace Client { + +void UserCommands::userInfoCommand(const std::vector<std::string> &args, Common::Connection *connection) { + if(args.size() == 1) { + Common::Logger::logf(Common::Logger::ERROR, "%s: No user id given.", args[0].c_str()); + CommandParser::printUsage("user_info"); + return; + } + if(args.size() > 2) { + Common::Logger::logf(Common::Logger::ERROR, "%s: Too many arguments.", args[0].c_str()); + CommandParser::printUsage("user_info"); + return; + } + + char *endptr; + unsigned long uid = std::strtoul(args[1].c_str(), &endptr, 10); + if(args[1].empty() || *endptr != '\0') { + Common::Logger::logf(Common::Logger::ERROR, "%s: Unable to parse user id.", args[0].c_str()); + CommandParser::printUsage("user_info"); + return; + } + + boost::shared_ptr<Common::Requests::UserInfoRequest> request(new Common::Requests::UserInfoRequest(uid)); + Common::RequestManager::get()->sendRequest(connection, request); + request->wait(); + + std::pair<boost::shared_ptr<const Common::XmlPacket>, Net::Exception> result = request->getResult(); + + if(!result.first || result.second) { + Common::Logger::logf(Common::Logger::ERROR, "An error occurred during your request: %s.", result.second.strerror().c_str()); + } + else { + const Common::XmlPacket &packet = *result.first; + + std::cout << " " << (unsigned long)packet["uid"] << ", " << (unsigned long)packet["gid"] << ", " << (const std::string&)packet["username"] << ", " + << (const std::string&)packet["fullName"] << std::endl << std::endl; + } +} + +void UserCommands::listUsersCommand(const std::vector<std::string> &args _UNUSED_PARAMETER_, Common::Connection *connection) { + boost::shared_ptr<Common::Requests::UserListRequest> request(new Common::Requests::UserListRequest); + + Common::RequestManager::get()->sendRequest(connection, request); + request->wait(); + + std::pair<boost::shared_ptr<const Common::XmlPacket>, Net::Exception> result = request->getResult(); + + if(!result.first || result.second) { + Common::Logger::logf(Common::Logger::ERROR, "An error occurred during your request: %s.", result.second.strerror().c_str()); + } + else { + const Common::XmlPacket::Element &users = (*result.first)["users"]; + + if(users.isEmpty()) { + std::cout << "User list is empty." << std::endl; + } + else { + std::cout << "Found " << users.getSize() << " user" << (users.getSize()==1 ? "" : "s") << ":" << std::endl; + + + for(size_t i = 0; i < users.getSize(); ++i) + std::cout << " " << (unsigned long)users[i]["uid"] << ", " << (unsigned long)users[i]["gid"] << ", " << (const std::string&)users[i]["username"] << ", " << (const std::string&)users[i]["fullName"] << std::endl; + } + + std::cout << std::endl; + } +} + +void UserCommands::listUserGroupsCommand(const std::vector<std::string> &args, Common::Connection *connection) { + if(args.size() == 1) { + Common::Logger::logf(Common::Logger::ERROR, "%s: No user id given.", args[0].c_str()); + CommandParser::printUsage("list_user_groups"); + return; + } + if(args.size() > 2) { + Common::Logger::logf(Common::Logger::ERROR, "%s: Too many arguments.", args[0].c_str()); + CommandParser::printUsage("list_user_groups"); + return; + } + + char *endptr; + unsigned long uid = std::strtoul(args[1].c_str(), &endptr, 10); + if(args[1].empty() || *endptr != '\0') { + Common::Logger::logf(Common::Logger::ERROR, "%s: Unable to parse user id.", args[0].c_str()); + CommandParser::printUsage("list_user_groups"); + return; + } + + boost::shared_ptr<Common::Requests::UserGroupListRequest> request(new Common::Requests::UserGroupListRequest(uid)); + Common::RequestManager::get()->sendRequest(connection, request); + request->wait(); + + std::pair<boost::shared_ptr<const Common::XmlPacket>, Net::Exception> result = request->getResult(); + + if(!result.first || result.second) { + Common::Logger::logf(Common::Logger::ERROR, "An error occurred during your request: %s.", result.second.strerror().c_str()); + } + else { + const Common::XmlPacket::Element &groups = (*result.first)["groups"]; + + if(groups.isEmpty()) { + std::cout << "The user isn't member of any group." << std::endl; + } + else { + + std::cout << "User is member of " << groups.getSize() << " group" << (groups.getSize()==1 ? "" : "s") << ":" << std::endl; + + + for(size_t i = 0; i < groups.getSize(); ++i) + std::cout << " " << (unsigned long)groups[i]["gid"] << std::endl; + } + + std::cout << std::endl; + } +} + +void UserCommands::listGroupsCommand(const std::vector<std::string> &args _UNUSED_PARAMETER_, Common::Connection *connection) { + boost::shared_ptr<Common::Requests::GroupListRequest> request(new Common::Requests::GroupListRequest); + + Common::RequestManager::get()->sendRequest(connection, request); + request->wait(); + + std::pair<boost::shared_ptr<const Common::XmlPacket>, Net::Exception> result = request->getResult(); + + if(!result.first || result.second) { + Common::Logger::logf(Common::Logger::ERROR, "An error occurred during your request: %s.", result.second.strerror().c_str()); + } + else { + const Common::XmlPacket::Element &groups = (*result.first)["groups"]; + + if(groups.isEmpty()) { + std::cout << "Group list is empty." << std::endl; + } + else { + std::cout << "Found " << groups.getSize() << " group" << (groups.getSize()==1 ? "" : "s") << ":" << std::endl; + + + for(size_t i = 0; i < groups.getSize(); ++i) + std::cout << " " << (unsigned long)groups[i]["gid"] << ", " << (const std::string&)groups[i]["name"] << std::endl; + } + + std::cout << std::endl; + } +} + +void UserCommands::listGroupUsersCommand(const std::vector<std::string> &args, Common::Connection *connection) { + if(args.size() == 1) { + Common::Logger::logf(Common::Logger::ERROR, "%s: No group id given.", args[0].c_str()); + CommandParser::printUsage("list_group_users"); + return; + } + if(args.size() > 2) { + Common::Logger::logf(Common::Logger::ERROR, "%s: Too many arguments.", args[0].c_str()); + CommandParser::printUsage("list_group_users"); + return; + } + + char *endptr; + unsigned long gid = std::strtoul(args[1].c_str(), &endptr, 10); + if(args[1].empty() || *endptr != '\0') { + Common::Logger::logf(Common::Logger::ERROR, "%s: Unable to parse group id.", args[0].c_str()); + CommandParser::printUsage("list_group_users"); + return; + } + + boost::shared_ptr<Common::Requests::GroupUserListRequest> request(new Common::Requests::GroupUserListRequest(gid)); + Common::RequestManager::get()->sendRequest(connection, request); + request->wait(); + + std::pair<boost::shared_ptr<const Common::XmlPacket>, Net::Exception> result = request->getResult(); + + if(!result.first || result.second) { + Common::Logger::logf(Common::Logger::ERROR, "An error occurred during your request: %s.", result.second.strerror().c_str()); + } + else { + const Common::XmlPacket::Element &users = (*result.first)["users"]; + + if(users.isEmpty()) { + std::cout << "The group doesn't have any members." << std::endl; + } + else { + + std::cout << "The group has " << users.getSize() << " member" << (users.getSize()==1 ? "" : "s") << ":" << std::endl; + + + for(size_t i = 0; i < users.getSize(); ++i) + std::cout << " " << (unsigned long)users[i]["uid"] << std::endl; + } + + std::cout << std::endl; + } +} + +} +} diff --git a/src/Client/UserCommands.h b/src/Client/UserCommands.h new file mode 100644 index 0000000..c4cd399 --- /dev/null +++ b/src/Client/UserCommands.h @@ -0,0 +1,47 @@ +/* + * UserCommands.h + * + * Copyright (C) 2009 Matthias Schiffer <matthias@gamezock.de> + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef MAD_CLIENT_USERCOMMANDS_H_ +#define MAD_CLIENT_USERCOMMANDS_H_ + +#include <Common/Connection.h> + +#include <string> +#include <vector> + + +namespace Mad { +namespace Client { + +class UserCommands { + private: + UserCommands(); + + public: + static void userInfoCommand(const std::vector<std::string> &args, Common::Connection *connection); + static void listUsersCommand(const std::vector<std::string> &args, Common::Connection *connection); + static void listUserGroupsCommand(const std::vector<std::string> &args, Common::Connection *connection); + static void listGroupsCommand(const std::vector<std::string> &args, Common::Connection *connection); + static void listGroupUsersCommand(const std::vector<std::string> &args, Common::Connection *connection); +}; + +} +} + +#endif /* MAD_CLIENT_USERCOMMANDS_H_ */ |