// Cyphesis Online RPG Server and AI Engine // Copyright (C) 2001 Alistair Riddoch // // 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 2 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, write to the Free Software Foundation, // Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA /// \page cydb_index /// /// \section Introduction /// /// cydb is a commandline tool to administrate the server database. For /// information on the commands available, please see the unix manual page. /// The manual page is generated from docbook sources, so can /// also be converted into other formats. #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "common/log.h" #include "common/globals.h" #include "common/system.h" #include "common/Storage.h" #include #include "common/compose.hpp" #ifdef HAVE_GETOPT_H #include #endif // HAVE_GETOPT_H #ifndef READLINE_CXX_SANE // defined in config.h extern "C" { #endif #define USE_VARARGS #define PREFER_STDARG #include #include #ifndef READLINE_CXX_SANE } #endif typedef int (*dbcmd_function)(Storage & ab, struct dbsys * system, int argc, char ** argv); /// \brief Entry in the global command table for cycmd struct dbsys { const char * sys_name; const char * sys_description; dbcmd_function sys_function; struct dbsys * sys_subsys; }; int dbs_help(Storage & ab, struct dbsys * system, int argc, char ** argv) { size_t max_length = 0; for (struct dbsys * I = system->sys_subsys; I->sys_name != NULL; ++I) { max_length = std::max(max_length, strlen(I->sys_name)); } max_length += 2; std::cout << "Cyphesis database tool." << std::endl << system->sys_name << " commands:" << std::endl << std::endl; for (struct dbsys * I = system->sys_subsys; I->sys_name != NULL; ++I) { std::cout << " " << I->sys_name << std::string(max_length - strlen(I->sys_name), ' ') << I->sys_description << std::endl; } std::cout << std::endl << std::flush; return 0; } static int world_purge(Storage & ab, struct dbsys * system, int argc, char ** argv) { std::string cmd = "DELETE FROM entities WHERE loc IS NOT null"; if (Database::instance()->runCommandQuery(cmd) != 0) { std::cout << "Entity purge fail" << std::endl << std::flush; return 1; } cmd = "DELETE FROM properties"; if (Database::instance()->runCommandQuery(cmd) != 0) { std::cout << "Property purge fail" << std::endl << std::flush; return 1; } return 0; } static int users_purge(Storage & ab, struct dbsys * system, int argc, char ** argv) { std::string cmd = "DELETE FROM accounts WHERE username != 'admin'"; if (Database::instance()->runCommandQuery(cmd) != 0) { std::cout << "User purge fail" << std::endl << std::flush; return 1; } return 0; } static int users_list(Storage & ab, struct dbsys * system, int argc, char ** argv) { std::string cmd = "SELECT username, type FROM accounts"; DatabaseResult res = Database::instance()->runSimpleSelectQuery(cmd); DatabaseResult::const_iterator I = res.begin(); DatabaseResult::const_iterator Iend = res.end(); for (; I != Iend; ++I) { std::string name = I.column("username"); std::string type = I.column("type"); std::cout << (type == "admin" ? "*" : " ") << name << std::endl << std::flush; } return 0; } static int users_del(Storage & ab, struct dbsys * system, int argc, char ** argv) { if (argc != 2) { std::cout << "usage: " << system->sys_name << " " << std::endl << std::flush; return 1; } std::string id = argv[1]; std::string cmd = String::compose("SELECT username FROM accounts " "WHERE username='%1'", id); DatabaseResult res = Database::instance()->runSimpleSelectQuery(cmd); if (res.size() == 0) { std::cout << "User account " << id << " not found" << std::endl << std::flush; return 1; } if (res.size() != 1) { std::cout << "ERROR: Multiple accounts match " << id << std::endl << std::flush; return 1; } cmd = String::compose("DELETE FROM accounts WHERE " "username = '%1'", id); if (Database::instance()->runCommandQuery(cmd) != 0) { std::cout << "User delete fail" << std::endl << std::flush; return 1; } return 0; } static int users_mod(Storage & ab, struct dbsys * system, int argc, char ** argv) { int opt; char * type = 0, * password = 0; while ((opt = getopt(argc, argv, "t:p:")) != -1) { switch (opt) { case 't': type = optarg; break; case 'p': password = optarg; break; default: std::cout << "usage: " << system->sys_name << " [-t TYPE|-p PASSWORD] USERNAME" << std::endl << std::flush; return 1; } } if (argc - optind != 1 || (type == 0 && password == 0)) { std::cout << "usage: " << system->sys_name << " [-t TYPE] [-p PASSWORD] USERNAME" << std::endl << std::flush; return 1; } std::string id = argv[optind]; std::string cmd = String::compose("SELECT username, type FROM accounts " "WHERE username='%1'", id); DatabaseResult res = Database::instance()->runSimpleSelectQuery(cmd); if (res.size() == 0) { std::cout << "User account " << id << " not found" << std::endl << std::flush; return 1; } if (res.size() != 1) { std::cout << "ERROR: Multiple accounts match " << id << std::endl << std::flush; return 1; } if (type != 0) { std::string new_type = type; if (new_type != "admin" && new_type != "player" && new_type != "disabled") { std::cout << "ERROR: Account type must be one of " "\"player\", \"admin\" or \"disabled\"" << std::endl << std::flush; } // FIXME Verify the account exists. cmd = String::compose("UPDATE accounts SET type = '%1' WHERE " "username = '%2'", new_type, id); if (Database::instance()->runCommandQuery(cmd) != 0) { std::cout << "User mod type fail" << std::endl << std::flush; return 1; } std::cout << "Account type updated." << std::endl << std::flush; } if (password != 0) { std::string new_pass; encrypt_password(password, new_pass); cmd = String::compose("UPDATE accounts SET password = '%1' WHERE " "username = '%2'", new_pass, id); if (Database::instance()->runCommandQuery(cmd) != 0) { std::cout << "User mod password fail" << std::endl << std::flush; return 1; } std::cout << "Account password updated." << std::endl << std::flush; } return 0; } static int rules_purge(Storage & ab, struct dbsys * system, int argc, char ** argv) { std::string cmd = "DELETE FROM rules"; if (Database::instance()->runCommandQuery(cmd) != 0) { std::cout << "Rule purge fail" << std::endl << std::flush; return 1; } return 0; } static int rules_list(Storage & ab, struct dbsys * system, int argc, char ** argv) { std::string cmd = "SELECT id FROM rules"; DatabaseResult res = Database::instance()->runSimpleSelectQuery(cmd); DatabaseResult::const_iterator I = res.begin(); DatabaseResult::const_iterator Iend = res.end(); for (; I != Iend; ++I) { std::string name = I.column("id"); std::cout << name << std::endl << std::flush; } return 0; } int dbs_generic(Storage & ab, struct dbsys * system, int argc, char ** argv) { struct dbsys * subsyss = system->sys_subsys; if (subsyss == 0) { std::cout << "INTERNAL ERROR" << std::endl << std::flush; return 1; } int nargc = argc - 1; if (nargc < 1) { dbs_help(ab, system, argc, argv); return 1; } char ** nargv = &argv[1]; for (struct dbsys * I = subsyss; I->sys_name != NULL; ++I) { if (strcmp(nargv[0], I->sys_name) == 0) { return I->sys_function(ab, I, nargc, nargv); } } std::cout << "ERROR: No such command: " << system->sys_name << " " << nargv[0] << std::endl << std::endl << std::flush; dbs_help(ab, system, argc, argv); return 0; } struct dbsys world_cmds[] = { { "purge", "Purge world data", &world_purge, 0 }, { "help", "Show world help", &dbs_help, &world_cmds[0] }, { NULL, "Guard", } }; struct dbsys users_cmds[] = { { "purge", "Purge users data", &users_purge, 0 }, { "list", "List user accounts", &users_list, 0 }, { "del", "Delete a user account", &users_del, 0 }, { "mod", "Modify a user account", &users_mod, 0 }, { "help", "Show users help", &dbs_help, &users_cmds[0] }, { NULL, "Guard", } }; struct dbsys rules_cmds[] = { { "purge", "Purge rules data", &rules_purge, 0 }, { "list", "List rules", &rules_list, 0 }, { "help", "Show rules help", &dbs_help, &rules_cmds[0] }, { NULL, "Guard", } }; struct dbsys systems[] = { { "rules", "Modify the rule storage table", &dbs_generic, &rules_cmds[0] }, { "user", "Modify the user account table", &dbs_generic, &users_cmds[0] }, { "world", "Modify the world storage tables", &dbs_generic, &world_cmds[0] }, { "help", "Show command help", &dbs_help, &systems[0] }, { NULL, "Guard", } }; struct dbsys tool = { "cydb", "Work with cyphesis databases", &dbs_help, &systems[0] }; int completion_iterator = 0; char * completion_generator(const char * text, int state) { if (state == 0) { completion_iterator = 0; } for (int i = completion_iterator; systems[i].sys_name != 0; ++i) { if (strncmp(text, systems[i].sys_name, strlen(text)) == 0) { completion_iterator = i + 1; return strdup(systems[i].sys_name); } } return 0; } void gotCommand(char * cmd) { } // FIXME void setup_completion() { std::string prompt="foobar>"; rl_callback_handler_install(prompt.c_str(), &gotCommand); rl_completion_entry_function = &completion_generator; std::cout << std::endl << std::flush; rl_callback_handler_remove(); } #if 0 // FIXME void updatePrompt() { std::string prompt; std::string designation(">"); if (!username.empty()) { prompt = username + "@"; if (accountType == "admin") { designation = "#"; } else { designation = "$"; } } else { prompt = ""; } prompt += serverName; prompt += " "; prompt += systemType; prompt += designation; prompt += " "; rl_set_prompt(prompt.c_str()); } void exec(const std::string & cmd, const std::string & arg) { bool reply_expected = true; reply_flag = false; error_flag = false; if (cmd == "stat") { Get g; encoder->streamObjectsMessage(g); } else if (cmd == "install") { size_t space = arg.find(' '); if (space == std::string::npos || space >= (arg.size() - 1)) { std::cout << "usage: install " << std::endl << std::flush; } else { Create c; c->setFrom(accountId); Anonymous ent; ent->setId(std::string(arg, 0, space)); ent->setObjtype("class"); ent->setParents(std::list(1, std::string(arg, space + 1))); c->setArgs1(ent); encoder->streamObjectsMessage(c); } reply_expected = false; } else if (cmd == "look") { Look l; l->setFrom(accountId); encoder->streamObjectsMessage(l); } else if (cmd == "logout") { Logout l; l->setFrom(accountId); if (!arg.empty()) { Anonymous lmap; lmap->setId(arg); l->setArgs1(lmap); reply_expected = false; } encoder->streamObjectsMessage(l); } else if (cmd == "say") { Talk t; Anonymous ent; ent->setAttr("say", arg); t->setArgs1(ent); t->setFrom(accountId); encoder->streamObjectsMessage(t); } else if (cmd == "help" || cmd == "?") { reply_expected = false; help(); } else if (cmd == "query") { Get g; Anonymous cmap; cmap->setObjtype("obj"); if (!arg.empty()) { cmap->setId(arg); } g->setArgs1(cmap); g->setFrom(accountId); encoder->streamObjectsMessage(g); } else if (cmd == "reload") { if (arg.empty()) { reply_expected = false; std::cout << "reload: Argument required" << std::endl << std::flush; } else { Set s; Anonymous tmap; tmap->setObjtype("class"); tmap->setId(arg); s->setArgs1(tmap); s->setFrom(accountId); encoder->streamObjectsMessage(s); } } else if (cmd == "get") { Get g; Anonymous cmap; cmap->setObjtype("class"); if (!arg.empty()) { cmap->setId(arg); } g->setArgs1(cmap); g->setFrom(accountId); encoder->streamObjectsMessage(g); } else if (cmd == "monitor") { AdminTask * task = new OperationMonitor; if (runTask(task, arg) == 0) { Monitor m; m->setArgs1(Anonymous()); m->setFrom(accountId); encoder->streamObjectsMessage(m); } reply_expected = false; } else if (cmd == "unmonitor") { OperationMonitor * om = dynamic_cast(currentTask); if (om != 0) { Monitor m; m->setFrom(accountId); encoder->streamObjectsMessage(m); reply_expected = false; struct timeval tv; gettimeofday(&tv, NULL); int monitor_time = tv.tv_sec - om->startTime(); std::cout << om->count() << " operations monitored in " << monitor_time << " seconds = " << om->count() / monitor_time << " operations per second" << std::endl << std::flush; endTask(); } } else if (cmd == "connect") { reply_expected = false; Connect m; Anonymous cmap; cmap->setAttr("hostname", arg); m->setArgs1(cmap); m->setFrom(accountId); encoder->streamObjectsMessage(m); } else if (cmd == "add_agent") { std::string agent_type("creator"); if (!arg.empty()) { agent_type = arg; } Create c; Anonymous cmap; cmap->setParents(std::list(1, agent_type)); cmap->setName("cycmd agent"); cmap->setObjtype("obj"); c->setArgs1(cmap); c->setFrom(accountId); avatar_flag = true; encoder->streamObjectsMessage(c); } else if (cmd == "delete") { if (agentId.empty()) { std::cout << "Use add_agent to add an in-game agent first" << std::endl << std::flush; reply_expected = false; } else if (arg.empty()) { std::cout << "Please specify the entity to delete" << std::endl << std::flush; reply_expected = false; } else { Delete del; Anonymous del_arg; del_arg->setId(arg); del->setArgs1(del_arg); del->setFrom(agentId); del->setTo(arg); encoder->streamObjectsMessage(del); reply_expected = false; } } else if (cmd == "find_by_name") { if (agentId.empty()) { std::cout << "Use add_agent to add an in-game agent first" << std::endl << std::flush; reply_expected = false; } else if (arg.empty()) { std::cout << "Please specify the name to search for" << std::endl << std::flush; reply_expected = false; } else { Look l; Anonymous lmap; lmap->setName(arg); l->setArgs1(lmap); l->setFrom(agentId); encoder->streamObjectsMessage(l); reply_expected = false; } } else if (cmd == "find_by_type") { if (agentId.empty()) { std::cout << "Use add_agent to add an in-game agent first" << std::endl << std::flush; reply_expected = false; } else if (arg.empty()) { std::cout << "Please specify the type to search for" << std::endl << std::flush; reply_expected = false; } else { Look l; Anonymous lmap; lmap->setParents(std::list(1, arg)); l->setArgs1(lmap); l->setFrom(agentId); encoder->streamObjectsMessage(l); reply_expected = false; } } else if (cmd == "flush") { if (agentId.empty()) { std::cout << "Use add_agent to add an in-game agent first" << std::endl << std::flush; reply_expected = false; } else if (arg.empty()) { std::cout << "Please specify the type to flush" << std::endl << std::flush; reply_expected = false; } else { AdminTask * task = new Flusher(agentId); runTask(task, arg); reply_expected = false; } } else if (cmd == "creator_create") { if (agentId.empty()) { std::cout << "Use add_agent to add an in-game agent first" << std::endl << std::flush; reply_expected = false; } else if (arg.empty()) { std::cout << "Use add_agent to add an in-game agent first" << std::endl << std::flush; reply_expected = false; } else { Create c; Anonymous thing; thing->setParents(std::list(1, arg)); c->setArgs1(thing); c->setFrom(agentId); encoder->streamObjectsMessage(c); reply_expected = false; } } else if (cmd == "cancel") { if (endTask() != 0) { std::cout << "No task currently running" << std::endl << std::flush; } } else { reply_expected = false; std::cout << cmd << ": Command not known" << std::endl << std::flush; } ios << std::flush; if (!reply_expected) { return; } // Wait for reply time_t wait_start_time = time(NULL); while (!reply_flag) { if (time(NULL) - wait_start_time > 5) { std::cout << cmd << ": No reply from server" << std::endl << std::flush; return; } poll(false); } } #endif static int run_command(Storage & ab, int argc, char ** argv) { for (struct dbsys * I = &systems[0]; I->sys_name != NULL; ++I) { if (strcmp(argv[0], I->sys_name) == 0) { return I->sys_function(ab, I, argc, argv); } } std::cout << "ERROR: No such command: " << argv[0] << std::endl << std::endl << std::flush; dbs_help(ab, &tool, argc, argv); return 1; } int main(int argc, char ** argv) { int config_status = loadConfig(argc, argv, USAGE_DBASE); if (config_status < 0) { if (config_status == CONFIG_VERSION) { reportVersion(argv[0]); return 0; } else if (config_status == CONFIG_HELP) { showUsage(argv[0], USAGE_DBASE, "[ cmd ]"); return 0; } else if (config_status != CONFIG_ERROR) { log(ERROR, "Unknown error reading configuration."); } // Fatal error loading config file return 1; } int optind = config_status; bool interactive = true; int ret = 0; std::string cmd; if (optind < argc) { cmd = argv[optind]; interactive = false; } Storage ab; if (ab.init() != 0) { log(ERROR, "Database setup failed."); return 1; } if (!interactive) { ret = run_command(ab, argc - optind, &argv[optind]); } else { dbs_help(ab, &tool, argc, argv); } delete global_conf; return ret; }