#include #include #include #include #include "db/database_manager.h" #include "machine_manager.h" #include "machine.h" #include "vfs.h" #include "util.h" vfs_node* copy_vfs_node(vfs_node* original, vfs_node* new_parent) { if(!original) { return nullptr; } /* Create the new node and copy its properties. */ vfs_node* new_copy = new_node(original->name, original->type, new_parent, original->owner_id, original->group_id, original->permissions); new_copy->content = original->content; /* Recursively copy all children. */ for(auto const& [key, child_node] : original->children) { new_copy->children[key] = copy_vfs_node(child_node, new_copy); } return new_copy; } MachineManager::MachineManager(DatabaseManager* db_manager) : _db_manager(db_manager) { /* Create template VFS that holds shared, read-only directories. */ _vfs_template_root = new_node("/", DIR_NODE, nullptr); vfs_node* bin = new_node("bin", DIR_NODE, _vfs_template_root, 0, 0, 0755); /* System owned. */ _vfs_template_root->children["bin"] = bin; /* Load all scripts from assets/scripts/bin into the VFS. */ const std::string path = "assets/scripts/bin"; for(const auto & entry : std::filesystem::directory_iterator(path)) { if(entry.is_regular_file() && entry.path().extension() == ".lua") { std::ifstream t(entry.path()); std::stringstream buffer; buffer << t.rdbuf(); std::string filename_with_ext = entry.path().filename().string(); std::string filename = filename_with_ext.substr(0, filename_with_ext.find_last_of('.')); vfs_node* script_node = new_node(filename, EXEC_NODE, bin, 0, 0, 0755); /* System owned. */ script_node->content = util::xor_string(buffer.str()); bin->children[filename] = script_node; fprintf(stderr, "Loaded executable: /bin/%s\n", filename.c_str()); } } } MachineManager::~MachineManager(void) { delete_vfs_tree(_vfs_template_root); } Machine* MachineManager::create_machine(uint32_t id, const std::string& hostname, const std::string& system_type) { auto* new_machine = new Machine(id, hostname); vfs_node* root = new_node("/", DIR_NODE, nullptr, 0, 0, 0755); /* Root owned by system (0). */ /* Create directories for this specific VFS. */ vfs_node* home = new_node("home", DIR_NODE, root, id, id, 0755); /* Owned by player. */ vfs_node* user = new_node("user", DIR_NODE, home); home->children["user"] = user; vfs_node* readme = new_node("readme.txt", FILE_NODE, user, id, id, 0644); /* Owned by player. */ readme->content = "Welcome to your new virtual machine."; user->children["readme.txt"] = readme; /* Link to the shared directories from the template. */ root->children["bin"] = copy_vfs_node(_vfs_template_root->children["bin"], root); /* Ensure copied bin directory has the correct owner/group/permissions. */ root->children["bin"]->owner_id = 0; /* System owned. */ root->children["bin"]->group_id = 0; root->children["bin"]->permissions = 0755; /* Assign the VFS to the new machine. */ new_machine->vfs_root = root; if(system_type == "npc") { vfs_node* npc_file = new_node("npc_system.txt", FILE_NODE, root, 0, 0, 0644); npc_file->content = "This guy sucks nuts!"; root->children["npc_system.txt"] = npc_file; } return new_machine; } /* Recursively build the VFS tree from database nodes. */ void build_tree(vfs_node* parent, const std::map& nodes) { for(auto const& [id, node] : nodes) { /* Inefficient but safe. Would be better to group nodes by parent_id. */ if(node->parent_id == parent->id) { parent->children[node->name] = node; node->parent = parent; if(node->type == DIR_NODE) { build_tree(node, nodes); } } } } Machine* MachineManager::load_machine(long long machine_id) { printf("DEBUG: load_machine called for machine_id: %lld\n", machine_id); std::string hostname = _db_manager->machines().get_hostname(machine_id); long long owner_id = _db_manager->machines().get_owner_id(machine_id); Machine* machine = new Machine(machine_id, hostname, owner_id); /* Load all VFS nodes for this machine from the database. */ std::map node_map; vfs_node* root = nullptr; auto nodes = _db_manager->vfs().get_nodes_for_machine(machine_id); for(vfs_node* node : nodes) { node_map[node->id] = node; if(node->name == "/") { root = node; } } machine->services = _db_manager->services().get_for_machine(machine_id); if(root) { build_tree(root, node_map); machine->vfs_root = root; } return machine; } long long MachineManager::get_machine_id_by_ip(const std::string& ip) { if(_ip_to_id_map.count(ip)) { return _ip_to_id_map[ip]; } return 0; } void MachineManager::init(void) { auto all_machines = _db_manager->machines().get_all(); for(const auto& machine_data : all_machines) { _ip_to_id_map[machine_data.ip_address] = machine_data.id; } }