#include #include #include #include #include "db/database_manager.h" #include "machine_manager.h" #include "machine.h" #include "vfs.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); 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); _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 = entry.path().filename().string(); vfs_node* script_node = new_node(filename, FILE_NODE, bin); script_node->content = buffer.str(); bin->children[filename] = script_node; fprintf(stderr, "Loaded script: /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); /* Create directories for this specific VFS. */ vfs_node* home = new_node("home", DIR_NODE, root); vfs_node* user = new_node("user", DIR_NODE, home); home->children["user"] = user; vfs_node* readme = new_node("readme.txt", FILE_NODE, user); 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); /* 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); 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, DatabaseManager* db_manager) { printf("DEBUG: load_machine called for machine_id: %lld\n", machine_id); std::string hostname; db_manager->_db << "SELECT hostname FROM machines WHERE id = ?;" << machine_id >> hostname; Machine* machine = new Machine(machine_id, hostname); /* Load all VFS nodes for this machine from the database. */ std::map nodes; vfs_node* root = nullptr; db_manager->_db << "SELECT id, parent_id, name, type, content FROM vfs_nodes WHERE machine_id = ?;" << machine_id >> [&](long long id, long long parent_id, std::string name, int type, std::string content) { vfs_node* node = new vfs_node(); node->id = id; node->parent_id = parent_id; /* Store temp id for tree building. */ node->name = name; node->type = (vfs_node_type)type; node->content = content; nodes[id] = node; if(name == "/") { root = node; } }; db_manager->_db << "SELECT port, name FROM services WHERE machine_id = ?;" << machine_id >> [&](int port, std::string name) { machine->services[port] = name; }; if(root) { build_tree(root, nodes); machine->vfs_root = root; } return machine; }