bettola/common/src/machine_manager.cpp

140 lines
4.5 KiB
C++

#include <filesystem>
#include <fstream>
#include <sstream>
#include <map>
#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<long long, vfs_node*>& 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<long long, vfs_node*> 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;
}