summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Client/CMakeLists.txt2
-rw-r--r--src/Client/CommandParser.cpp459
-rw-r--r--src/Client/CommandParser.h35
-rw-r--r--src/Client/SystemCommands.cpp262
-rw-r--r--src/Client/SystemCommands.h49
-rw-r--r--src/Client/UserCommands.cpp230
-rw-r--r--src/Client/UserCommands.h47
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_ */