Commit e6c61373 authored by Phil Hagelberg's avatar Phil Hagelberg

Threads, channels, and RPC.

I had this as like ten separate commits but git corrupted my repo; argh.
parent 185c75aa
......@@ -2,9 +2,9 @@ run: ; love .
VERSION=beta-2
SHIP_LUA=ship/*.lua doc/init.lua
SHIP_LUA=ship/*.lua doc/init.lua os/client.lua
ENGINE_LUA=*.lua
OS_LUA=os/orb/*.lua os/lisp/*.lua os/rover/*.lua
OS_LUA=os/orb/*.lua os/lisp/*.lua os/rover/*.lua os/server.lua
IN_OS_LUA=os/orb/resources/*
IN_SHIP_LUA=data/src/*
DEPS_LUA=globtopattern/*.lua lume/*.lua md5/*.lua os/lisp/l2l/*.lua \
......@@ -36,8 +36,8 @@ check:
ssh_get_connection rover_operate \
-- $(IN_SHIP_LUA)
luacheck --no-color --std luajit --ignore 21/_.* --exclude-files=*.lsp \
--globals lume pack orb station buy_user ship cargo_transfer refuel pps \
accept_mission term buy_upgrade sell_upgrade \
--globals lume orb station buy_user ship cargo_transfer refuel pps \
accept_mission get_prompt set_prompt buy_upgrade sell_upgrade \
subnet logout upgrade_help port loan \
-- $(IN_OS_LUA)
luacheck --no-color --std luajit --ignore 21/_.* --globals love lume term \
......
local utils = require("utils")
local portal_motd = "Connected to portal, checking for clearance..."
local hostname = function(body_name)
return body_name:lower():gsub(" ", "-")
end
......@@ -122,10 +120,9 @@ return {
start = function(b)
if(b.world and not b.thread) then
b.thread = love.thread.newThread("os/orb/thread.lua")
b.thread:start()
local ch = love.thread.getChannel(tostring(b.thread))
ch:push({command="init", name=b.name})
b.input, b.output = love.thread.newChannel(), love.thread.newChannel()
b.thread = love.thread.newThread("os/server.lua")
b.thread:start(b.input, b.output, b.os, hostname(b.name))
end
end,
......
......@@ -11,10 +11,10 @@ local go4_source = love.filesystem.read("os/orb/resources/gangof4")
local go4 = {files={["/home/guest/bin/gangof4"] = go4_source}}
local data = { ["Merdeka Station"] = { root = go4_files },
["Kembali Station"] = { root = go4_files },
["Kenapa Station"] = { root = go4_files },
["Earth"] = { root = go4_files },}
local data = { ["Merdeka Station"] = { root = go4 },
["Kembali Station"] = { root = go4 },
["Kenapa Station"] = { root = go4 },
["Earth"] = { root = go4 },}
for _,sys in pairs(systems) do
for _,body in pairs(sys.bodies) do
......
......@@ -2,15 +2,14 @@
-- with another OS over an SSH connection.
-- initiate the connection. command may be nil.
ssh = function(username, password, command)
ssh = function(username, password)
editor.change_buffer("*console*")
if(ship.status.target and ship.status.target.os) then
ssh_connect(username or "guest", password or "", command)
local send = ssh_connect(username or "guest", password or "")
if(send) then
editor.activate_mode("ssh")
editor.end_of_buffer()
editor.no_mark()
editor.activate_mode("ssh")
else
print("Cannot log into target.")
editor.set_prop("ssh_send", send)
end
return editor.invisible
end
......@@ -30,16 +29,16 @@ define_mode("ssh", "console") -- inherit bindings from console
bind("ssh", "return", function()
local input = lume.trim(editor.get_input())
if(input == "logout") then
editor.history_push(input)
local send = editor.get_prop("ssh_send")
if(input and send) then
send(input)
editor.newline()
logout()
else
editor.history_push(input)
ssh_send_line(input)
editor.newline(2)
editor.print_prompt()
editor.end_of_buffer()
editor.no_mark()
elseif(input) then
print("Not connected.")
end
end)
......
This diff is collapsed.
......@@ -8,6 +8,7 @@ local save = require("save")
local pause = require("pause")
local flight_draw = require("draw")
local systems = require("data.systems")
local os_client = require("os.client")
local quit = function()
save.save(ship)
......@@ -202,6 +203,7 @@ local update = safely(function(dt)
body.update(ship.bodies, real_time_factor)
body.gravitate_all(ship.bodies, ship, real_time_factor)
ai.update(ship, ship.bodies, real_time_factor)
os_client.update(ship, dt)
end
end)
......@@ -234,5 +236,3 @@ ui.play = function()
love.update,love.keypressed,love.wheelmoved,love.textinput,love.draw =
update, keypressed, wheelmoved, textinput, safely(draw)
end
return ship -- for headless.lua
local lume = require("lume")
local rpcs = require("rpcs")
local sessions = {}
local send = function(channel, session, data)
if(type(data) == "string") then
channel:push({op="stdin", stdin=data, session=session})
else
error("Unsupported message type: " .. tostring(data))
end
end
return {
connect = function(ship, username, password)
-- TODO: range check
if(not ship.target) then return end
local i, o = ship.target.input, ship.target.output
if(not i and not o) then
ship.api.print("Connection refused: " .. ship.target.name)
return
end
o:push({op="login", username=username, password=password})
local response = i:demand()
print("<", require("serpent").block(response))
ship.api.print(response.out)
if(response.ok) then
sessions[response["new-session"]] = {input=i, output=o,
port=ship.target}
return lume.fn(send, o, response["new-session"] or "session")
else
return false
end
end,
update = function(ship, _dt)
for _, session in pairs(sessions) do
local msg = session.input:pop()
if(msg) then
print("<", require("serpent").block(msg))
if(msg.out) then ship.api.write(msg.out) end
if(msg.op == "rpc") then
msg.chan:push({rpcs[msg.fn](ship, session.port,
unpack(msg.args or {}))})
end
end
end
end,
logout = function(_ship) end, -- TODO
}
......@@ -140,14 +140,7 @@ You can refer to environment variables in shell commands, but the
traditional Unix `$VAR` does not work; you must use the less-ambiguous
`${VAR}` instead.
## Portability
Currently when running outside of minetest it needs to shell out to
the `sha1sum` executable because Lua does not have any built-in
checksumming functionality. This could pose a problem when running on
platforms that lack this executable.
## License
Copyright © 2015 Phil Hagelberg and contributors. Licensed under the
Copyright © 2015-2017 Phil Hagelberg and contributors. Licensed under the
GPLv3 or later; see the file COPYING.
This diff is collapsed.
-- a fake lil' OS
local serpent = require("serpent")
local utils = require("utils")
assert(setfenv, "Needs lua 5.1; sorry.")
orb = { dir = "os/orb", name="orb" }
require("os.orb.utils")
require("os.orb.fs")
require("os.orb.shell")
require("os.orb.process")
orb.sandbox = function(ship, env, fs_raw, disconnect)
local services = require("services")
local serpent_opts = {maxlevel=8,maxnum=64,nocode=true}
local sb = {
buy_user = lume.fn(services.buy_user, ship, ship.target, fs_raw),
buy_upgrade = lume.fn(services.buy_upgrade, ship),
sell_upgrade = lume.fn(services.sell_upgrade, ship),
refuel = lume.fn(services.refuel, ship, ship.target),
cargo_transfer = lume.fn(services.cargo_transfer, ship.target, ship),
port = lume.fn(services.port, ship),
loan = lume.fn(services.loan, ship),
upgrade_help = ship.api.help.get,
station = utils.readonly_proxy(ship.target),
ship = ship.api,
distance = lume.fn(utils.distance, ship, ship.target),
os = {time = lume.fn(utils.time, ship)},
term = { set_prompt = ship.api.editor.set_prompt,
get_prompt = ship.api.editor.get_prompt },
set_prompt = ship.api.editor.set_prompt,
get_prompt = ship.api.editor.get_prompt,
pps = function(x) return serpent.block(x, serpent_opts) end,
original_env = utils.readonly_proxy(env),
}
sb.pp = function(x) sb.print(serpent.block(x, serpent_opts)) end
ship.sandbox.logout = function()
disconnect()
ship.comm_connected = false
end
if(ship.target and ship.target.subnet and env.USER=="subnet") then
sb.subnet = lume.fn(services.subnet.request, ship)
end
return lume.merge(utils.sandbox, sb)
end
orb.login = function(fs, env, fs_raw, disconnect, ship, command)
env.IN, env.OUT = "/tmp/in", "/tmp/out"
orb.shell.exec(fs, env, "mkfifo " .. env.IN)
fs[env.OUT] = ship.api.write
local fifo = fs[env.IN]
fs[env.IN] = function(x)
assert((not x) or (type(x)=="string") or
((type(x)=="table") and not pairs(x)(x)),
"Error transmitting non-text data")
return fifo(x)
end
assert((not command) or (type(command)=="string"),
"Error running non-string command.")
orb.process.spawn(fs, env, command,
orb.sandbox(ship, env, fs_raw, disconnect))
-- without this you can't have non-shell SSH commands
orb.process.scheduler(fs)
end
return orb
local shell = require("os.orb.shell")
local rpcs = require("os.orb.rpcs")
return {
new_session = function(stdin, output, username, hostname)
local env = shell.new_env(username, hostname)
local thread = love.thread.newThread("os/orb/session.lua")
thread:start(env, "smash", stdin, output, hostname, rpcs)
return env
end,
}
orb.process = {
-- Create a coroutine for a command to run inside and place it into the
-- process table. The process table is stored in the filesystem under
-- f.proc[user]
spawn = function(f, env, command, extra_sandbox)
command = command or "smash"
local co = coroutine.create(function()
orb.shell.exec(f, env, command, extra_sandbox) end)
local id = orb.process.id_for(co)
f.proc[env.USER][id] = { thread = co,
command = command,
id = id,
_user = env.USER,
}
return co, id
end,
-- The process ID is taken from lua's own tostring called on a coroutine.
id_for = function(p)
return tostring(p):match(": 0x(.+)")
end,
-- Loop through all the coroutines in the process table and give them all
-- a chance to run till they yield. No attempt at fairness or time limits
-- yet.
scheduler = function(f)
for _,procs in pairs(f.proc) do
if(type(procs) == "table") then
for k,p in pairs(procs) do
if(type(p) == "table" and p.thread) then
if(coroutine.status(p.thread) == "dead") then
procs[k] = nil
else
local _, err = coroutine.resume(p.thread)
if err then print(err) end
end
end
end
end
end
end,
}
-- -*- lua -*-
local f, env, args = ...
local env, args = ...
if(#args > 1) then
local group = table.remove(args,1)
local group = table.remove(args, 1)
if(env.USER == "root" or orb.in_group(f, env.USER, group)) then
if(env.USER == "root" or orb.in_group(env.USER, group)) then
for _,user in ipairs(args) do
orb.add_to_group(f, user, group)
orb.add_to_group(user, group)
end
else
print("Not a member of " .. group)
......
-- -*- lua -*-
local f, _env, args = ...
local _env, args = ...
if(#args == 2) then
orb.add_user(f, args[1], args[2])
orb.add_user(args[1], args[2])
else
print("Add a new user. Requires sudo privileges.\n")
print("Usage:")
......
-- -*- lua -*-
local _f, _env, args = ...
local _env, args = ...
if(not station.prices) then
print("This station does not sell cargo.")
......
-- -*- lua -*-
local f, env, args = ...
local env, args = ...
if(args[1] == "--help") then
print("Print the contents of a file or files.")
......@@ -8,9 +8,9 @@ if(args[1] == "--help") then
print(" cat FILE1 [FILE2 ...]")
else
for _,filename in ipairs(args) do
if(f[orb.normalize(filename, env.CWD)]) then
if(io.exists(filename, env.CWD)) then
print(filename .. ":")
print(orb.read(orb.normalize(filename, env.CWD)))
print(io.readfile(filename, env.CWD))
print("")
else
print(filename .. " not found.")
......
-- -*- lua -*-
local f, env, args = ...
local group = args[1]
local dirname = args[2] or env.CWD
local dir = f[orb.normalize(dirname, env.CWD)]
if(args[1] == "--help" or (not group)) then
print("Change the group of a given directory, or the current directory.")
print("Requires write privileges to the directory.\n")
print("Usage:")
print(" chgrp GROUP [DIR]")
elseif(not dir) then
print("Not found: " .. dir)
else
-- TODO: assert arg is an actual group
dir._group = group
end
-- -*- lua -*-
local f, env, args = ...
local env, args = ...
local perms = args[1]
local dirname = args[2] or env.CWD
local dir = f[orb.normalize(dirname, env.CWD)]
local dir = orb.normalize(args[2] or ".", env.CWD)
-- TODO: fix
local usage = function()
print("Add or remove group write privileges to a directory.\n")
print("Usage:")
......@@ -14,7 +14,7 @@ end
if(#args < 1 or args[1] == "--help") then
usage()
elseif(not dir) then
print("Not found: " .. dirname)
print("Not found: " .. dir)
elseif(perms == "+") then
dir._group_write = "true"
elseif(perms == "-") then
......
-- -*- lua -*-
local f, env, args = ...
local env, args = ...
local user = args[1]
local dirname = args[2] or env.CWD
local dir = f[orb.normalize(dirname, env.CWD)]
local dir = orb.normalize(dirname, env.CWD)
-- TODO: fix
if(not args[1] or args[1] == "--help") then
print("Change the owner of a given directory or current directory.\n")
......
-- -*- lua -*-
local f, env, args = ...
local env, args = ...
if(#args == 2) then
local to_dir, to_base = orb.dirname(orb.normalize(args[2], env.CWD))
f[to_dir][to_base] = f[orb.normalize(args[1], env.CWD)]
local contents = io.readfile(args[1], env.CWD)
io.writefile(args[2], env.CWD, contents)
else
print("Copy files.\n")
print("Usage:")
......
-- -*- lua -*-
local _f, _env, args = ...
local _env, args = ...
if(args[1] == "-n") then
table.remove(args, 1)
......
-- -*- lua -*-
local _f, env, args = ...
local env, args = ...
if(args[1] == "--help") then
print("Print all environment variables.")
......
-- -*- lua -*-
local original_prompt = term.get_prompt()
term.set_prompt("] ")
local original_prompt = get_prompt()
set_prompt("] ")
local clone = function(x)
return lume.deserialize(lume.serialize(x))
......@@ -311,7 +311,7 @@ local function round(hands, turn, played, players)
p:close()
end
end
term.set_prompt(original_prompt)
set_prompt(original_prompt)
else
local play = get_cards(hand, input)
printall(players, "Player " .. turn .. " played: " ..
......@@ -350,7 +350,7 @@ local begin = function(players, seed)
return round(hands, starting_player, {}, players)
end
local _f, _env, args = ...
local _env, args = ...
if(args[1] == "--help") then
print("Play a card game.")
......
-- -*- lua -*-
local _f, _env, args = ...
local _env, args = ...
if(args[1] == "--help") then
print("Print all lines from input which patch a pattern.\n")
......@@ -9,7 +9,7 @@ if(args[1] == "--help") then
else
local lines = {}
local read_line = function()
if(#lines == 0) then lines = orb.utils.split(io.read(), "\n") end
if(#lines == 0) then lines = lume.split(io.read(), "\n") end
return table.remove(lines, 1)
end
......
-- -*- lua -*-
local f, env, args = ...
local env, args = ...
local f = {} -- TODO: fix
if(args[1] == "--help" or #args == 0) then
print("Kill a process. Use `ps' to list processes.\n")
......
-- -*- lua -*-
local _f, _env, args = ...
local _env, args = ...
if((args[1] == "borrow" or args[1] == "repay") and #args == 2) then
print(loan(unpack(args)))
......
-- -*- lua -*-
local f, env, args = ...
local env, args = ...
local ls = function(dirname)
local dir = f[orb.normalize(dirname, env.CWD)]
if(not dir) then
print("Not found: " .. dirname)
elseif(type(dir) == "table") then
for name,contents in pairs(dir) do
name = tostring(name)
if(not name:match("^_")) then
if(type(contents) == "table") then
print(name .. "/")
elseif(type(contents) == "function") then
print("*" .. name)
elseif(type(contents) == "thread") then
print("%" .. name)
else
print(name)
end
if(not io.isdir(dirname, env.CWD)) then
print(dirname .. " is not a directory")
else
for _,name in ipairs(io.ls(dirname, env.CWD)) do
if(io.isdir(dirname .. "/" .. name)) then
print(name .. "/")
else
print(name)
end
end
else
print(dirname)
end
end
......
-- -*- lua -*-
local f, _env, args = ...
local _env, args = ...
local eval = function(input)
local chunk, err = loadstring("return " .. input)
......@@ -20,7 +20,7 @@ local eval = function(input)
end
if(#args == 1) then
local contents = f[args[1]]
local contents = io.readfile(args[1])
if(contents) then
eval(contents)
else
......@@ -34,7 +34,7 @@ elseif(args[1] == "--help" or #args > 0) then
else
print("Lua 5.1.6259 Copyright (C) 1994-2422 Lua.org, PUC-Rio")
print("Run `exit()` to exit.")
term.set_prompt(">> ")
set_prompt(">> ")
while true do
local input = io.read()
if(not input or input == "exit" or input == "exit()") then return end
......
-- -*- lua -*-
local f, env, args = ...
local env, args = ...
if(args[1] == "--help" or #args == 0) then
print("Create a directory, and its parent directories if needed.\n")
print("Usage:")
print(" mkdir DIR [DIR2...]")
else
for _,d in ipairs(args) do
orb.mkdir(f, d, env)
io.mkdir(d, env.CWD, env)
end
end
-- -*- lua -*-
local f, env, args = ...
if(#args == 0 or args[1] == "--help") then
print("Create a fifo special file node.")
print("You can read and write to this node like a file.\n")
print("Usage:")
print(" mkfifo PATH_TO_FIFO")
else
local dir, base = orb.dirname(orb.normalize(args[1], env.CWD))
local buffer = {}
local max_buffer_size = 1024
f[dir][base] = function(...)
local arg = {...}
if(#arg == 0) then
while #buffer == 0 do coroutine.yield() end
return table.remove(buffer, 1)
elseif(arg[1] == {}) then
return buffer
else -- write
while(#buffer > max_buffer_size) do coroutine.yield() end
for _,output in pairs(arg) do
table.insert(buffer, output)
end
end
end
end
-- -*- lua -*-
local f, env, args = ...
local env, args = ...
if(#args == 0 or args[1] == "--help") then
print("Move files.\n")
print("Usage:")
print(" mv FROM TO")
else
orb.exec(f, env, "cp " .. args[1] .. " " .. args[2])
orb.exec(f, env, "rm " .. args[1])
orb.exec(env, "cp " .. args[1] .. " " .. args[2])
orb.exec(env, "rm " .. args[1])
end
-- -*- lua -*-
local f, env, args = ...
local env, args = ...
if(#args == 2) then
orb.change_password(f, env.USER, args[1], args[2])
orb.change_password(env.USER, args[1], args[2])
else
print("Change your password.\n")
print("Usage:")
......
-- -*- lua -*-
local _f, _env, args = ...
local _env, args = ...
if(args[1] == "fine" and #args == 1) then
print(port("fine"))
......
-- -*- lua -*-
local f, env, args = ...
if(args[1] == "--help") then
print("Print a listing of all running processes. Shows, id, status, and command.\n")
print("Usage:")
print(" ps")
else
for _,v in pairs(f.proc[env.USER]) do
if(type(v) == "table") then
print(v.id .. " " .. coroutine.status(v.thread) .. ": " .. v.command)
end
end
end