bettola/common/src/machine_manager.cpp

150 lines
5.0 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"
#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<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) {
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<long long, vfs_node*> 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;
}
}