/* * 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 "InformationManager.h" #include "Requests/DaemonFSInfoRequest.h" #include "Requests/DaemonCommandRequest.h" #include "Requests/DaemonStatusRequest.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include 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}, {{"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}, {{"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} }; CommandParser CommandParser::commandParser; 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::logf("Usage: %s\n", cmd->cmdline); } std::map CommandParser::parseHostList(const std::vector &args, bool mustBeActive) { const std::map& hosts = InformationManager::get()->getDaemons(); std::map ret; for(std::vector::const_iterator arg = args.begin(); arg != args.end(); ++arg) { if(*arg == "*") { if(mustBeActive) { for(std::map::const_iterator host = hosts.begin(); host != hosts.end(); ++host) { if(host->second.getState() != Common::HostInfo::INACTIVE) ret.insert(*host); } if(ret.empty()) Common::Logger::log("No hosts active."); } else { ret = hosts; } } else { std::map::const_iterator host = hosts.find(*arg); if(host == hosts.end()) Common::Logger::logf(Common::Logger::ERROR, "Host '%s' doesn't exist.", arg->c_str()); else if(mustBeActive && host->second.getState() == Common::HostInfo::INACTIVE) Common::Logger::logf(Common::Logger::WARNING, "Host '%s' is inactive.", arg->c_str()); else ret.insert(*host); } } return ret; } void CommandParser::printFSInfo(boost::shared_ptr &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 &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 &args) { boost::shared_ptr request; if(args.size() == 1) request = boost::shared_ptr(new Common::Requests::FSInfoRequest); else if(args.size() == 2) request = boost::shared_ptr(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, 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 &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::logf(Common::Logger::WARNING, "%s: Command '%s' doesn't exist.", args[0].c_str(), args[1].c_str()); } else { Common::Logger::logf(Common::Logger::ERROR, "%s: Too many arguments.", args[0].c_str()); printUsage("help"); } } void CommandParser::listHostsCommand(const std::vector &args) { const std::map& hosts = InformationManager::get()->getDaemons(); if(args.size() == 1) { if(hosts.empty()) { std::cout << "The host list is empty." << std::endl << std::endl; return; } bool output = false; for(std::map::const_iterator host = hosts.begin(); host != hosts.end(); ++host) { if(host->second.getState() == Common::HostInfo::INACTIVE) continue; if(!output) { std::cout << "Active hosts:" << std::endl; output = true; } std::cout << " " << host->first << std::endl; } if(!output) std::cout << "No active hosts." << std::endl; std::cout << std::endl; } else if(args.size() > 2) { Common::Logger::logf(Common::Logger::ERROR, "%s: Too many arguments.", args[0].c_str()); printUsage("list_hosts"); } else if(args[1] == "-a") { if(hosts.empty()) { std::cout << "The host list is empty." << std::endl << std::endl; return; } std::cout << "Host list:" << std::endl; for(std::map::const_iterator host = hosts.begin(); host != hosts.end(); ++host) { std::cout << " " << host->first << " (" << (host->second.getState() == Common::HostInfo::RUNNING ? "running" : "inactive") << ")" << std::endl; } std::cout << std::endl; } else { Common::Logger::logf(Common::Logger::ERROR, "%s: Don't understand argument '%s'.", args[0].c_str(), args[1].c_str()); printUsage("list_hosts"); } } void CommandParser::rebootCommand(const std::vector &args) { if(args.size() < 2) { Common::Logger::logf(Common::Logger::ERROR, "%s: No host given.", args[0].c_str()); printUsage("reboot"); return; } std::map hosts = parseHostList(std::vector(args.begin()+1, args.end()), true); std::vector > requests; for(std::map::iterator host = hosts.begin(); host != hosts.end(); ++host) { boost::shared_ptr request(new Requests::DaemonCommandRequest(host->first, true)); Common::RequestManager::get()->sendRequest(connection, request); requests.push_back(request); } for(std::vector >::iterator request = requests.begin(); request != requests.end(); ++request) (*request)->wait(); for(std::vector >::iterator request = requests.begin(); request != requests.end(); ++request) { std::pair, 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 &args) { if(args.size() < 2) { Common::Logger::logf(Common::Logger::ERROR, "%s: No host given.", args[0].c_str()); printUsage("shutdown"); return; } std::map hosts = parseHostList(std::vector(args.begin()+1, args.end()), true); std::vector > requests; for(std::map::iterator host = hosts.begin(); host != hosts.end(); ++host) { boost::shared_ptr request(new Requests::DaemonCommandRequest(host->first, false)); Common::RequestManager::get()->sendRequest(connection, request); requests.push_back(request); } for(std::vector >::iterator request = requests.begin(); request != requests.end(); ++request) (*request)->wait(); for(std::vector >::iterator request = requests.begin(); request != requests.end(); ++request) { std::pair, 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 &args) { boost::shared_ptr request; if(args.size() == 1) request = boost::shared_ptr(new Common::Requests::StatusRequest); else if(args.size() == 2) request = boost::shared_ptr(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, 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 &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 request(new Common::Requests::UserInfoRequest(uid)); Common::RequestManager::get()->sendRequest(connection, request); request->wait(); std::pair, 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&) { boost::shared_ptr request(new Common::Requests::UserListRequest); Common::RequestManager::get()->sendRequest(connection, request); request->wait(); std::pair, 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 &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 request(new Common::Requests::UserGroupListRequest(uid)); Common::RequestManager::get()->sendRequest(connection, request); request->wait(); std::pair, 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&) { boost::shared_ptr request(new Common::Requests::GroupListRequest); Common::RequestManager::get()->sendRequest(connection, request); request->wait(); std::pair, 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 &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 request(new Common::Requests::GroupUserListRequest(gid)); Common::RequestManager::get()->sendRequest(connection, request); request->wait(); std::pair, 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&) { boost::shared_ptr request(new Common::Requests::DisconnectRequest); Common::RequestManager::get()->sendRequest(connection, request); request->wait(); std::pair, 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()); else disconnect = true; } bool CommandParser::parse(const std::string &cmd) { std::vector splitCmd; Common::Tokenizer::tokenize(cmd, splitCmd); if(splitCmd.empty()) return true; const Command* command = findCommand(splitCmd[0]); if(command) (this->*command->funcPtr)(splitCmd); else Common::Logger::logf(Common::Logger::ERROR, "Unknown command '%s'.", splitCmd[0].c_str()); return true; } } }