[Refactor] Implement scriptable Lua API
Large architecture refactor of the scripting system. Previous implementation required Lua scripts to return "action tables" which were interpreted by a large and not very flexible at all if-else ladder in C++. While fine for the initial implementation, it's not scalable, and it makes it impossible for players to write their own meaningful tools.
This commit is contained in:
		
							parent
							
								
									4b21d30567
								
							
						
					
					
						commit
						e06d6eec37
					
				@ -4,6 +4,7 @@ if not filename then
 | 
			
		||||
  return "" -- No arguments, return nothing.
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
local current_dir = bettola.get_current_dir(context)
 | 
			
		||||
local target_node = current_dir.children[filename]
 | 
			
		||||
 | 
			
		||||
if not target_node then
 | 
			
		||||
 | 
			
		||||
@ -4,4 +4,4 @@ if not target then
 | 
			
		||||
  return "" -- No argument, just return to prompt.
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
return { action = "cd", target = target }
 | 
			
		||||
return bettola.cd(context, target)
 | 
			
		||||
 | 
			
		||||
@ -18,7 +18,7 @@ if found_redirect then
 | 
			
		||||
    return "echo: syntax error: expected filename after '>'"
 | 
			
		||||
  end
 | 
			
		||||
  local content = table.concat(content_parts, " ")
 | 
			
		||||
  return { action = "write_file", target = filename, content = content }
 | 
			
		||||
  return bettola.write_file(context, filename, content)
 | 
			
		||||
else
 | 
			
		||||
  return table.concat(arg, " ")
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,6 @@
 | 
			
		||||
-- /bin/exit - Disconnects from a remote session or cloes terminal window.
 | 
			
		||||
if is_remote_session then
 | 
			
		||||
  return { action = "disconnect" }
 | 
			
		||||
  return bettola.disconnect(context)
 | 
			
		||||
else
 | 
			
		||||
  return { action = "close_terminal" }
 | 
			
		||||
  return bettola.close_terminal(context)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,5 @@
 | 
			
		||||
-- /bin/ls - Lists files in a directory.
 | 
			
		||||
local dir = current_dir -- Get directory object from C++.
 | 
			
		||||
local dir = bettola.get_current_dir(context)
 | 
			
		||||
local output = ""
 | 
			
		||||
 | 
			
		||||
-- Iterate over the 'children' map exposed via C++.
 | 
			
		||||
 | 
			
		||||
@ -6,4 +6,4 @@ if not target_ip then
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
-- TODO: Add args such as -sV for version detection etc.
 | 
			
		||||
return { action = "scan", target = target_ip }
 | 
			
		||||
return bettola.nmap(context, target_ip)
 | 
			
		||||
 | 
			
		||||
@ -1,3 +1,3 @@
 | 
			
		||||
local file_to_remove = arg[1]
 | 
			
		||||
if not file_to_remove then return "rm: missing operand" end
 | 
			
		||||
return { action = "rm", target = file_to_remove }
 | 
			
		||||
return bettola.rm(context, file_to_remove)
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,4 @@
 | 
			
		||||
-- /bin/ssh - Connects to a remote host.
 | 
			
		||||
local target = arg[1]
 | 
			
		||||
if not target then return "ssh: missing operand" end
 | 
			
		||||
return { action = "ssh", target = target }
 | 
			
		||||
return bettola.ssh(context, target)
 | 
			
		||||
 | 
			
		||||
@ -24,6 +24,25 @@ vfs_node* CommandProcessor::get_current_dir(void) {
 | 
			
		||||
  return _current_dir;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Machine* CommandProcessor::get_home_machine(void)     { return _home_machine; }
 | 
			
		||||
Machine* CommandProcessor::get_session_machine(void)  { return _session_machine; }
 | 
			
		||||
 | 
			
		||||
std::map<std::string, Machine*>&
 | 
			
		||||
CommandProcessor::get_world_machines(void) {
 | 
			
		||||
  return _world_machines;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void CommandProcessor::set_current_dir(vfs_node* node) {
 | 
			
		||||
  _current_dir = node;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void CommandProcessor::set_session_machine(Machine* machine) {
 | 
			
		||||
  _session_machine = machine;
 | 
			
		||||
  if(_session_machine) {
 | 
			
		||||
    _current_dir = _session_machine->vfs_root;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string CommandProcessor::process_command(const std::string& command) {
 | 
			
		||||
  /* Trim trailing whitespace. */
 | 
			
		||||
  std::string cmd = command;
 | 
			
		||||
@ -48,13 +67,9 @@ std::string CommandProcessor::process_command(const std::string& command) {
 | 
			
		||||
  if(root->children.count("bin") && root->children["bin"]->children.count(script_filename)) {
 | 
			
		||||
    vfs_node* script_node = root->children["bin"]->children[script_filename];
 | 
			
		||||
    bool is_remote = (_session_machine != _home_machine);
 | 
			
		||||
    sol::object result = _lua->execute(script_node->content, _current_dir, args, is_remote);
 | 
			
		||||
    if(result.is<std::string>()) {
 | 
			
		||||
      return result.as<std::string>();
 | 
			
		||||
    } else if(result.is<sol::table>()) {
 | 
			
		||||
      return _handle_vfs_action(result.as<sol::table>());
 | 
			
		||||
    }
 | 
			
		||||
    return "[Script returned an unexpected type]";
 | 
			
		||||
    sol::object result = _lua->execute(script_node->content, *this, args, is_remote);
 | 
			
		||||
    return result.is<std::string>() ? result.as<std::string>() : "[Script returned an unexpected type]";
 | 
			
		||||
 | 
			
		||||
  }
 | 
			
		||||
  return "Unknown command: " + command_name + "\n";
 | 
			
		||||
}
 | 
			
		||||
@ -83,145 +98,54 @@ vfs_node* find_node_by_path(vfs_node* root, const std::string& path) {
 | 
			
		||||
  return current;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string CommandProcessor::_handle_vfs_action(sol::table action) {
 | 
			
		||||
  std::string action_name = action["action"].get_or<std::string>("");
 | 
			
		||||
  /* Make the CoW check universal for any write operation. */
 | 
			
		||||
  if(action_name == "rm" || action_name == "write_file") {
 | 
			
		||||
    if(!_session_machine->is_vfs_a_copy) {
 | 
			
		||||
      /* VFS is shared, a copy is required. */
 | 
			
		||||
      if(_session_machine == _home_machine) {
 | 
			
		||||
        /* We are modifying our own home machine. */
 | 
			
		||||
        fprintf(stderr, "CoW: Write attempt on player's home machine"
 | 
			
		||||
                "Creating persistant copy.\n");
 | 
			
		||||
void CommandProcessor::ensure_vfs_is_writable(void) {
 | 
			
		||||
  if(!_session_machine->is_vfs_a_copy) {
 | 
			
		||||
    /* VFS shared, copy required. */
 | 
			
		||||
    if(_session_machine == _home_machine) {
 | 
			
		||||
      /* We are modifying our own home machine. */
 | 
			
		||||
      fprintf(stderr, "CoW: Write attempt on player's home machine."
 | 
			
		||||
              "Creating persistent copy.\n");
 | 
			
		||||
      std::string original_path = get_full_path(_current_dir);
 | 
			
		||||
 | 
			
		||||
      vfs_node* new_vfs_root = copy_vfs_node(_home_machine->vfs_root, nullptr);
 | 
			
		||||
      auto* new_machine = new Machine(_home_machine->id, _home_machine->hostname);
 | 
			
		||||
      new_machine->vfs_root       = new_vfs_root;
 | 
			
		||||
      new_machine->services       = _home_machine->services;
 | 
			
		||||
      new_machine->is_vfs_a_copy  = true;
 | 
			
		||||
 | 
			
		||||
      _home_machine = new_machine;
 | 
			
		||||
      set_session_machine(new_machine);
 | 
			
		||||
      _current_dir = find_node_by_path(_session_machine->vfs_root, original_path);
 | 
			
		||||
      if(!_current_dir) { _current_dir = _session_machine->vfs_root; }
 | 
			
		||||
    } else {
 | 
			
		||||
      /* we are modifying a remote NPC machine. */
 | 
			
		||||
      std::string remote_ip = "";
 | 
			
		||||
      for(auto const& [ip, machine] : _world_machines) {
 | 
			
		||||
        if(machine == _session_machine) {
 | 
			
		||||
          remote_ip = ip;
 | 
			
		||||
          break;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      if(!remote_ip.empty()) {
 | 
			
		||||
        fprintf(stderr, "CoW: Write attempt on remote machine '%s'."
 | 
			
		||||
                "Creating persistent copy.\n",
 | 
			
		||||
                remote_ip.c_str());
 | 
			
		||||
 | 
			
		||||
        std::string original_path = get_full_path(_current_dir);
 | 
			
		||||
 | 
			
		||||
        vfs_node* new_vfs_root = copy_vfs_node(_home_machine->vfs_root, nullptr);
 | 
			
		||||
        auto* new_machine = new Machine(_home_machine->id, _home_machine->hostname);
 | 
			
		||||
        new_machine->vfs_root = new_vfs_root;
 | 
			
		||||
        new_machine->services = _home_machine->services;
 | 
			
		||||
        new_machine->is_vfs_a_copy = true; /* Mark as copy. */
 | 
			
		||||
        vfs_node* new_vfs_root = copy_vfs_node(_session_machine->vfs_root, nullptr);
 | 
			
		||||
        auto* new_machine = new Machine(_session_machine->id, _session_machine->hostname);
 | 
			
		||||
        new_machine->vfs_root       = new_vfs_root;
 | 
			
		||||
        new_machine->services       = _session_machine->services;
 | 
			
		||||
        new_machine->is_vfs_a_copy  = true;
 | 
			
		||||
 | 
			
		||||
        _world_machines[remote_ip] = new_machine;
 | 
			
		||||
        set_session_machine(new_machine);
 | 
			
		||||
 | 
			
		||||
        _home_machine = new_machine;
 | 
			
		||||
        _session_machine = new_machine;
 | 
			
		||||
        _current_dir = find_node_by_path(_session_machine->vfs_root, original_path);
 | 
			
		||||
        if(!_current_dir) { _current_dir = _session_machine->vfs_root; }
 | 
			
		||||
      } else {
 | 
			
		||||
        std::string remote_ip = "";
 | 
			
		||||
        /* Check if we are on a known remote system. */
 | 
			
		||||
        for(auto const& [ip, machine] : _world_machines) {
 | 
			
		||||
          if(machine == _session_machine) {
 | 
			
		||||
            remote_ip = ip;
 | 
			
		||||
            break;
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
        if(!remote_ip.empty()) {
 | 
			
		||||
          fprintf(stderr, "CoW: Write attempt on remote machine '%s'. "
 | 
			
		||||
                  "Creating persistant copy.\n",
 | 
			
		||||
                  remote_ip.c_str());
 | 
			
		||||
          std::string original_path = get_full_path(_current_dir);
 | 
			
		||||
 | 
			
		||||
          vfs_node* new_vfs_root = copy_vfs_node(_session_machine->vfs_root, nullptr);
 | 
			
		||||
          auto* new_machine = new Machine(_session_machine->id, _session_machine->hostname);
 | 
			
		||||
          new_machine->vfs_root = new_vfs_root;
 | 
			
		||||
          new_machine->services = _session_machine->services;
 | 
			
		||||
          new_machine->is_vfs_a_copy = true; /* Mark as copy. */
 | 
			
		||||
 | 
			
		||||
          _world_machines[remote_ip] = new_machine;
 | 
			
		||||
          _session_machine = new_machine;
 | 
			
		||||
 | 
			
		||||
          _current_dir = find_node_by_path(_session_machine->vfs_root, original_path);
 | 
			
		||||
          if(!_current_dir) { _current_dir = _session_machine->vfs_root; }
 | 
			
		||||
        } else {
 | 
			
		||||
          return "Permission denied: Filesystem is read-only and not part of the world.";
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  if(action_name == "rm") {
 | 
			
		||||
    std::string path = action["target"].get_or<std::string>("");
 | 
			
		||||
    if(path.empty()) return "rm: missing operand";
 | 
			
		||||
 | 
			
		||||
    if(!_current_dir->children.count(path)) {
 | 
			
		||||
      return std::string("rm: cannot remove '") + path + "': No such file or directory.";
 | 
			
		||||
    }
 | 
			
		||||
    vfs_node* target_node = _current_dir->children[path];
 | 
			
		||||
    if(target_node->type == DIR_NODE) {
 | 
			
		||||
      return std::string("rm: cannot remove '") + path + "': Is a directory.";
 | 
			
		||||
    }
 | 
			
		||||
    _current_dir->children.erase(path);
 | 
			
		||||
    delete target_node;
 | 
			
		||||
    return ""; /* Success. */
 | 
			
		||||
  } else if(action_name == "write_file") {
 | 
			
		||||
    std::string filename  = action["target"].get_or<std::string>("");
 | 
			
		||||
    std::string content   = action["content"].get_or<std::string>("");
 | 
			
		||||
    if(filename.empty()) {
 | 
			
		||||
      return "write_file: missing filename";
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
    if(_current_dir->children.count(filename)) {
 | 
			
		||||
      vfs_node* target_node = _current_dir->children[filename];
 | 
			
		||||
      if(target_node->type == DIR_NODE) {
 | 
			
		||||
        return std::string("cannot write to '") + filename + "': Is a directory.";
 | 
			
		||||
      }
 | 
			
		||||
      target_node->content = content;
 | 
			
		||||
    } else {
 | 
			
		||||
      vfs_node* new_file = new vfs_node {
 | 
			
		||||
        .name = filename, .type = FILE_NODE, .content = content,
 | 
			
		||||
        .parent = _current_dir
 | 
			
		||||
      };
 | 
			
		||||
      _current_dir->children[filename] = new_file;
 | 
			
		||||
    }
 | 
			
		||||
    return ""; /* Success. */
 | 
			
		||||
  } else if(action_name == "ssh") {
 | 
			
		||||
    std::string target_ip = action["target"].get_or<std::string>("");
 | 
			
		||||
    if(_world_machines.count(target_ip)) {
 | 
			
		||||
      _session_machine = _world_machines[target_ip];
 | 
			
		||||
      _current_dir = _session_machine->vfs_root;
 | 
			
		||||
      return "Connected to " + target_ip;
 | 
			
		||||
    }
 | 
			
		||||
    return "ssh: Could not resolve hostname " + target_ip + ": Name or service not known";
 | 
			
		||||
  } else if(action_name == "cd") {
 | 
			
		||||
    std::string target_dir_name = action["target"].get_or<std::string>("");
 | 
			
		||||
    if(target_dir_name == "..") {
 | 
			
		||||
      if(_current_dir->parent) {
 | 
			
		||||
        _current_dir = _current_dir->parent;
 | 
			
		||||
      }
 | 
			
		||||
    } else if(_current_dir->children.count(target_dir_name)) {
 | 
			
		||||
      vfs_node* target_node = _current_dir->children[target_dir_name];
 | 
			
		||||
      if(target_node->type == DIR_NODE) {
 | 
			
		||||
        _current_dir = target_node;
 | 
			
		||||
      } else {
 | 
			
		||||
        return std::string("cd: not a directory: ") + target_dir_name;
 | 
			
		||||
      }
 | 
			
		||||
    } else {
 | 
			
		||||
        return std::string("cd: no such file or directory: ") + target_dir_name;
 | 
			
		||||
    }
 | 
			
		||||
      return ""; /* Success. */
 | 
			
		||||
  } else if(action_name == "disconnect") {
 | 
			
		||||
    _session_machine = _home_machine;
 | 
			
		||||
    _current_dir = _home_machine->vfs_root;
 | 
			
		||||
    return "Connection closed.";
 | 
			
		||||
  } else if(action_name == "close_terminal") {
 | 
			
		||||
    return "__CLOSE_CONNECTION__";
 | 
			
		||||
  } else if(action_name == "scan") {
 | 
			
		||||
    std::string target_ip = action["target"].get_or<std::string>("");
 | 
			
		||||
    if(!_world_machines.count(target_ip)) {
 | 
			
		||||
      return std::string("nmap: Could not resolve host: ") + target_ip;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Machine* target_machine = _world_machines[target_ip];
 | 
			
		||||
    if(target_machine->services.empty()) {
 | 
			
		||||
      return std::string("No open ports for ") + target_ip;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::stringstream ss;
 | 
			
		||||
    ss<<"Host: "<<target_ip<<"\n";
 | 
			
		||||
    ss<<"PORT\tSTATE\tSERVICE\n";
 | 
			
		||||
    for(auto const& [port, service_name] : target_machine->services) {
 | 
			
		||||
      ss<<port<<"/tcp\t"<<"open\t"<<service_name<<"\n";
 | 
			
		||||
    }
 | 
			
		||||
    return ss.str();
 | 
			
		||||
  }
 | 
			
		||||
  return "Error: Unknown VFS action '" + action_name + "'";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -13,10 +13,19 @@ public:
 | 
			
		||||
  ~CommandProcessor(void);
 | 
			
		||||
 | 
			
		||||
  std::string process_command(const std::string& command);
 | 
			
		||||
 | 
			
		||||
  /* Public interface for API functions. */
 | 
			
		||||
  vfs_node* get_current_dir(void);
 | 
			
		||||
  Machine* get_home_machine(void);
 | 
			
		||||
  Machine* get_session_machine(void);
 | 
			
		||||
  std::map<std::string, Machine*>& get_world_machines(void);
 | 
			
		||||
 
 | 
			
		||||
  void set_current_dir(vfs_node* node);
 | 
			
		||||
  void set_session_machine(Machine* machine);
 | 
			
		||||
 | 
			
		||||
  void ensure_vfs_is_writable(void);
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
  std::string _handle_vfs_action(sol::table action);
 | 
			
		||||
  Machine* _home_machine;
 | 
			
		||||
  Machine* _session_machine;
 | 
			
		||||
  vfs_node* _current_dir;
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										102
									
								
								common/src/lua_api.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										102
									
								
								common/src/lua_api.cpp
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,102 @@
 | 
			
		||||
#include <sstream>
 | 
			
		||||
 | 
			
		||||
#include "lua_api.h"
 | 
			
		||||
#include "command_processor.h"
 | 
			
		||||
#include "machine.h"
 | 
			
		||||
#include "vfs.h"
 | 
			
		||||
 | 
			
		||||
namespace api {
 | 
			
		||||
 | 
			
		||||
vfs_node* get_current_dir(CommandProcessor& context) {
 | 
			
		||||
  return context.get_current_dir();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string rm(CommandProcessor& context, const std::string& filename) {
 | 
			
		||||
  context.ensure_vfs_is_writable();
 | 
			
		||||
  vfs_node* current_dir = context.get_current_dir();
 | 
			
		||||
  if(!current_dir->children.count(filename)) {
 | 
			
		||||
    return "rm: cannot remove '" + filename + "': No such file or directory.";
 | 
			
		||||
  }
 | 
			
		||||
  vfs_node* target_node = current_dir->children[filename];
 | 
			
		||||
  if(target_node->type == DIR_NODE) {
 | 
			
		||||
    return "rm: cannot remove '" + filename + "': Is a directory.";
 | 
			
		||||
  }
 | 
			
		||||
  current_dir->children.erase(filename);
 | 
			
		||||
  delete target_node;
 | 
			
		||||
  return "";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string write_file(CommandProcessor& context, const std::string& filename,
 | 
			
		||||
                       const std::string& content) {
 | 
			
		||||
  context.ensure_vfs_is_writable();
 | 
			
		||||
  vfs_node* current_dir = context.get_current_dir();
 | 
			
		||||
  if(current_dir->children.count(filename)) {
 | 
			
		||||
    vfs_node* target_node = current_dir->children[filename];
 | 
			
		||||
    if(target_node->type == DIR_NODE) {
 | 
			
		||||
      return "cannot write to '" + filename + "': Is a directory.";
 | 
			
		||||
    }
 | 
			
		||||
    target_node->content = content;
 | 
			
		||||
  } else {
 | 
			
		||||
    vfs_node* new_file = new vfs_node {
 | 
			
		||||
      .name = filename, .type = FILE_NODE, .content = content, .parent = current_dir};
 | 
			
		||||
    current_dir->children[filename] = new_file;
 | 
			
		||||
  }
 | 
			
		||||
  return "";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string cd(CommandProcessor& context, const std::string& path) {
 | 
			
		||||
  vfs_node* current_dir = context.get_current_dir();
 | 
			
		||||
  if(path == "..") {
 | 
			
		||||
    if(current_dir->parent) {
 | 
			
		||||
      context.set_current_dir(current_dir->parent);
 | 
			
		||||
    }
 | 
			
		||||
  } else if(current_dir->children.count(path)) {
 | 
			
		||||
    vfs_node* target_node = current_dir->children[path];
 | 
			
		||||
    if(target_node->type == DIR_NODE) {
 | 
			
		||||
      context.set_current_dir(target_node);
 | 
			
		||||
    } else {
 | 
			
		||||
      return "cd: not a directory: " + path;
 | 
			
		||||
    }
 | 
			
		||||
  } else {
 | 
			
		||||
      return "cd: no such file or directory: " + path;
 | 
			
		||||
  }
 | 
			
		||||
  return "";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string ssh(CommandProcessor& context, const std::string& ip) {
 | 
			
		||||
  if(context.get_world_machines().count(ip)) {
 | 
			
		||||
    context.set_session_machine(context.get_world_machines().at(ip));
 | 
			
		||||
    return "Connected to " + ip;
 | 
			
		||||
  }
 | 
			
		||||
  return "ssh: Could not resolve hostname " + ip + ": Name or service not found";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string nmap(CommandProcessor& context, const std::string& ip) {
 | 
			
		||||
  if(!context.get_world_machines().count(ip)) {
 | 
			
		||||
    return "nmap: Could not resolve host: " + ip;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Machine* target_machine = context.get_world_machines().at(ip);
 | 
			
		||||
  if(target_machine->services.empty()) {
 | 
			
		||||
    return "No open ports for " + ip;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  std::stringstream ss;
 | 
			
		||||
  ss << "Host: " << ip << "\n";
 | 
			
		||||
  ss << "PORT\tSTATE\tSERVICE\n";
 | 
			
		||||
  for(auto const& [port, service_name] : target_machine->services) {
 | 
			
		||||
    ss << port << "/tcp\t" << "open\t" << service_name << "\n";
 | 
			
		||||
  }
 | 
			
		||||
  return ss.str();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string disconnect(CommandProcessor& context) {
 | 
			
		||||
  context.set_session_machine(context.get_home_machine());
 | 
			
		||||
  return "Connection closed.";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string close_terminal(CommandProcessor& context) {
 | 
			
		||||
  return "__CLOSE_CONNECTION__";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} /* namespace api */
 | 
			
		||||
							
								
								
									
										25
									
								
								common/src/lua_api.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								common/src/lua_api.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,25 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <string>
 | 
			
		||||
#include "vfs.h"
 | 
			
		||||
 | 
			
		||||
class CommandProcessor;
 | 
			
		||||
 | 
			
		||||
namespace api {
 | 
			
		||||
 | 
			
		||||
/* FILESYSTEM ACTIONS. */
 | 
			
		||||
vfs_node* get_current_dir(CommandProcessor& context);
 | 
			
		||||
std::string rm(CommandProcessor& context, const std::string& filename);
 | 
			
		||||
std::string write_file(CommandProcessor& context, const std::string& filename,
 | 
			
		||||
                       const std::string& content);
 | 
			
		||||
std::string cd(CommandProcessor& context, const std::string& path);
 | 
			
		||||
 | 
			
		||||
/* NETWORK ACTIONS. */
 | 
			
		||||
std::string ssh(CommandProcessor& context, const std::string& ip);
 | 
			
		||||
std::string nmap(CommandProcessor& context, const std::string& ip);
 | 
			
		||||
std::string disconnect(CommandProcessor& context);
 | 
			
		||||
 | 
			
		||||
/* SYSTEM ACTIONS. */
 | 
			
		||||
std::string close_terminal(CommandProcessor& context);
 | 
			
		||||
 | 
			
		||||
} /* namespace api */
 | 
			
		||||
@ -1,8 +1,12 @@
 | 
			
		||||
#include "lua_processor.h"
 | 
			
		||||
#include <sol/forward.hpp>
 | 
			
		||||
#include <sol/object.hpp>
 | 
			
		||||
#include <sol/protected_function_result.hpp>
 | 
			
		||||
#include <sol/raii.hpp>
 | 
			
		||||
 | 
			
		||||
#include "lua_processor.h"
 | 
			
		||||
#include "vfs.h"
 | 
			
		||||
#include "lua_api.h"
 | 
			
		||||
#include "command_processor.h"
 | 
			
		||||
 | 
			
		||||
LuaProcessor::LuaProcessor(void) {
 | 
			
		||||
  _lua.open_libraries(sol::lib::base, sol::lib::string, sol::lib::io, sol::lib::table);
 | 
			
		||||
@ -12,16 +16,31 @@ LuaProcessor::LuaProcessor(void) {
 | 
			
		||||
                              "name",       &vfs_node::name,
 | 
			
		||||
                              "type",       &vfs_node::type,
 | 
			
		||||
                              "children",   &vfs_node::children,
 | 
			
		||||
                              "content",    &vfs_node::content); }
 | 
			
		||||
                              "content",    &vfs_node::content);
 | 
			
		||||
 | 
			
		||||
  /* Expose CommandProcessor to Lua. DON'T ALLOW SCRIPTS TO CREATE IT THOUGH! */
 | 
			
		||||
  _lua.new_usertype<CommandProcessor>("CommandProcessor", sol::no_constructor);
 | 
			
		||||
 | 
			
		||||
  /* Create the 'bettola' API table. */
 | 
			
		||||
  sol::table bettola_api = _lua.create_named_table("bettola");
 | 
			
		||||
  bettola_api["rm"]             = &api::rm;
 | 
			
		||||
  bettola_api["write_file"]     = &api::write_file;
 | 
			
		||||
  bettola_api["get_current_dir"]= &api::get_current_dir;
 | 
			
		||||
  bettola_api["cd"]             = &api::cd;
 | 
			
		||||
  bettola_api["ssh"]            = &api::ssh;
 | 
			
		||||
  bettola_api["nmap"]           = &api::nmap;
 | 
			
		||||
  bettola_api["disconnect"]     = &api::disconnect;
 | 
			
		||||
  bettola_api["close_terminal"] = &api::close_terminal;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
LuaProcessor::~LuaProcessor(void) {}
 | 
			
		||||
 | 
			
		||||
sol::object LuaProcessor::execute(const std::string& script, vfs_node* current_dir,
 | 
			
		||||
sol::object LuaProcessor::execute(const std::string& script, CommandProcessor& context,
 | 
			
		||||
                                  const std::vector<std::string>& args, bool is_remote) {
 | 
			
		||||
  try {
 | 
			
		||||
    /* Pass C++ objects/points into the Lua env. */
 | 
			
		||||
    _lua["is_remote_session"] = is_remote;
 | 
			
		||||
    _lua["current_dir"] = current_dir;
 | 
			
		||||
    _lua["context"] = &context;
 | 
			
		||||
 | 
			
		||||
    /* Create and populate the 'arg' table for the script. */
 | 
			
		||||
    sol::table arg_table = _lua.create_table();
 | 
			
		||||
 | 
			
		||||
@ -5,13 +5,15 @@
 | 
			
		||||
#include <vfs.h>
 | 
			
		||||
#include <string>
 | 
			
		||||
 | 
			
		||||
class CommandProcessor;
 | 
			
		||||
 | 
			
		||||
class LuaProcessor {
 | 
			
		||||
public:
 | 
			
		||||
  LuaProcessor(void);
 | 
			
		||||
  ~LuaProcessor(void);
 | 
			
		||||
 | 
			
		||||
  /* Executes a string of lua code and returns result as a string. */
 | 
			
		||||
  sol::object execute(const std::string& script, vfs_node* current_dir,
 | 
			
		||||
  sol::object execute(const std::string& script, CommandProcessor& context,
 | 
			
		||||
                      const std::vector<std::string>& args, bool is_remote);
 | 
			
		||||
private:
 | 
			
		||||
  sol::state _lua;
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user