140 lines
		
	
	
		
			4.5 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			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;
 | 
						|
}
 |