/* * CommandParser.cpp * * Copyright (C) 2008 Matthias Schiffer * * 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 . */ #include "CommandParser.h" #include "Requests/CoreStatusRequest.h" #include "Requests/DaemonListRequest.h" #include "Requests/DaemonStatusRequest.h" #include #include #include #include #include #include #include #include #include namespace Mad { namespace Client { const CommandParser::Command CommandParser::commands[] = { {{"help", "?", 0}, "help [command]", "Displays usage information about commands", "Displays usage information about a command. If no command is given, a list of all available commands is displayed.", &CommandParser::helpCommand}, {{"list_hosts", "hosts", 0}, "list_hosts [-a]", "Lists the currently active hosts", "Lists the currently active hosts.\n\n -a\tAlso list inactive hosts", &CommandParser::listHostsCommand}, {{"status", "st", 0}, "status [host]", "Displays status information", "Displays host status information. If no host is given, server status information is displayed.", &CommandParser::statusCommand}, {{"exit", "quit", 0}, "exit", "Closes the connection and quits the client", "Closes the connection and quits the client.", &CommandParser::exitCommand}, {{0}, 0, 0, 0, 0} }; const CommandParser::Command* CommandParser::findCommand(const std::string& command) { for(int i = 0; commands[i].commands[0] != 0; ++i) { for(int j = 0; commands[i].commands[j] != 0; ++j) { if(command == commands[i].commands[j]) { return &commands[i]; } } } return 0; } void CommandParser::printUsage(const std::string& command) { const CommandParser::Command *cmd = findCommand(command); if(cmd) Common::Logger::log(std::string("Usage: ") + cmd->cmdline + "\n"); } void CommandParser::printHostStatus(const Net::Packets::HostStatusPacket &packet) { if(packet.getUptime()) { unsigned long days = packet.getUptime()/86400; unsigned long hours = (packet.getUptime()%86400)/3600; unsigned long minutes = (packet.getUptime()%3600)/60; std::printf("\tUptime:\t\t"); if(days) std::printf("%lu days ", days); std::printf("%lu:%02lu", hours, minutes); if(packet.getIdleTime()) std::printf(" (load average: %.1f%%)", 100.0f-(packet.getIdleTime()*100.0f/packet.getUptime())); std::printf("\n\n"); } if(packet.getTotalMem() && packet.getFreeMem()) { const std::string units[] = { "kB", "MB", "GB", "TB", "" }; unsigned unit = 0; float totalMem = packet.getTotalMem(), usedMem = packet.getTotalMem()-packet.getFreeMem(); 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(packet.getTotalSwap() && packet.getFreeSwap()) { unit = 0; totalMem = packet.getTotalSwap(); usedMem = packet.getTotalSwap()-packet.getFreeSwap(); 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::helpCommand(const std::vector &args) { if(args.size() == 1) { std::cout << "Available commands:" << std::endl << std::endl; for(int i = 0; commands[i].commands[0] != 0; ++i) { std::cout << commands[i].commands[0]; for(int j = 1; commands[i].commands[j] != 0; ++j) std::cout << ", " << commands[i].commands[j]; std::cout << std::endl << "\t" << commands[i].desc << std::endl; } } else if(args.size() == 2) { const Command* command = findCommand(args[1]); if(command) { std::cout << "Usage: " << command->cmdline << std::endl << std::endl; std::cout << command->longdesc << std::endl << std::endl; } else Common::Logger::log(Common::Logger::WARNING, args[0] + ": Command '" + args[1] + "' doesn't exist.\n"); } else { Common::Logger::log(Common::Logger::ERROR, args[0] + ": Too many arguments."); printUsage("help"); } } void CommandParser::listHostsCommand(const std::vector &args) { if(args.size() == 1) { Common::RequestManager::getRequestManager()->sendRequest(connection, std::auto_ptr( new Requests::DaemonListRequest( sigc::bind(sigc::mem_fun(this, &CommandParser::daemonListRequestFinished), false) ) ) ); } else if(args.size() > 2) { Common::Logger::log(Common::Logger::ERROR, args[0] + ": Too many arguments."); printUsage("list_hosts"); return; } else if(args[1] == "-a") { Common::RequestManager::getRequestManager()->sendRequest(connection, std::auto_ptr( new Requests::DaemonListRequest( sigc::bind(sigc::mem_fun(this, &CommandParser::daemonListRequestFinished), true) ) ) ); } else { Common::Logger::log(Common::Logger::ERROR, args[0] + ": Don't unterstand argument '" + args[1] + "'."); printUsage("list_hosts"); return; } activeRequests++; } void CommandParser::statusCommand(const std::vector &args) { if(args.size() == 1) Common::RequestManager::getRequestManager()->sendRequest(connection, std::auto_ptr(new Requests::CoreStatusRequest(sigc::mem_fun(this, &CommandParser::coreStatusRequestFinished)))); else if(args.size() == 2) Common::RequestManager::getRequestManager()->sendRequest(connection, std::auto_ptr(new Requests::DaemonStatusRequest(args[1], sigc::mem_fun(this, &CommandParser::daemonStatusRequestFinished)))); else { Common::Logger::log(Common::Logger::ERROR, args[0] + ": Too many arguments."); printUsage("status"); return; } activeRequests++; } void CommandParser::exitCommand(const std::vector&) { activeRequests++; Common::RequestManager::getRequestManager()->sendRequest(connection, std::auto_ptr(new Common::Requests::DisconnectRequest(sigc::mem_fun(this, &CommandParser::disconnectRequestFinished)))); } void CommandParser::coreStatusRequestFinished(const Common::Request &request) { try { const Net::Packets::HostStatusPacket &packet = request.getResult(); std::cout << "Server status:" << std::endl; printHostStatus(packet); } catch(Common::Exception &exception) { Common::Logger::log(Common::Logger::ERROR, std::string("An error occurred during your request: ") + exception.strerror() + ".\n"); } requestFinished(); } void CommandParser::daemonListRequestFinished(const Common::Request &request, bool all) { try { const std::vector& hosts = request.getResult().getHostInfo(); if(hosts.empty()) { std::cout << "The host list is empty." << std::endl << std::endl; } else { bool output = false; for(std::vector::const_iterator host = hosts.begin(); host != hosts.end(); ++host) { if(host->getStatus() == Common::HostInfo::INACTIVE && !all) continue; if(!output) { std::cout << (all ? "Host list:" : "Active hosts:") << std::endl; output = true; } std::cout << " " << host->getName(); if(all) std::cout << " (" << (host->getStatus() == Common::HostInfo::RUNNING ? "running" : "inactive") << ")"; std::cout << std::endl; } if(!output) std::cout << "No active hosts." << std::endl; std::cout << std::endl; } } catch(Common::Exception &exception) { Common::Logger::log(Common::Logger::ERROR, std::string("An error occurred during your request: ") + exception.strerror() + ".\n"); } requestFinished(); } void CommandParser::daemonStatusRequestFinished(const Common::Request &request) { try { const Net::Packets::HostStatusPacket &packet = request.getResult(); std::cout << "Host status:" << std::endl; printHostStatus(packet); } catch(Common::Exception &exception) { Common::Logger::log(Common::Logger::ERROR, std::string("An error occurred during your request: ") + exception.strerror() + ".\n"); } requestFinished(); } void CommandParser::disconnectRequestFinished(const Common::Request<> &request) { try { request.getResult(); disconnect = true; } catch(Common::Exception &exception) { Common::Logger::log(Common::Logger::ERROR, std::string("An error occurred during your request: ") + exception.strerror() + ".\n"); } requestFinished(); } bool CommandParser::split(const std::string &str, std::vector &ret) { std::string temp; bool quoteSingle = false, quoteDouble = false, escape = false; size_t beg = 0; for(size_t cur = 0; cur < str.length(); ++cur) { if(!escape) { if(str[cur] == ' ' && !quoteSingle && !quoteDouble) { if(cur == beg && temp.empty()) { ++beg; } else { temp += str.substr(beg, cur-beg); ret.push_back(temp); temp.clear(); beg = cur+1; } continue; } if(str[cur] == '"' && !quoteSingle) { temp += str.substr(beg, cur-beg); beg = cur+1; quoteDouble = !quoteDouble; continue; } if(str[cur] == '\'' && !quoteDouble) { temp += str.substr(beg, cur-beg); beg = cur+1; quoteSingle = !quoteSingle; continue; } if(str[cur] == '\\') { escape = true; continue; } } if(escape && ((!quoteSingle && !quoteDouble) || (quoteSingle && str[cur] == '\'') || (quoteDouble && (str[cur] == '"' || str[cur] == '\\')))) { temp += str.substr(beg, cur-beg-1); beg = cur; } escape = false; } temp += str.substr(beg, std::string::npos); ret.push_back(temp); return true; } bool CommandParser::parse(const std::string &cmd) { std::vector splitCmd; split(cmd, splitCmd); if(splitCmd.empty()) return true; const Command* command = findCommand(splitCmd[0]); if(command) (this->*command->funcPtr)(splitCmd); else Common::Logger::log(Common::Logger::ERROR, "Unknown command '" + splitCmd[0] + "'.\n"); return true; } } }