2019-11-26 21:34:40 +11:00
local serial = require " serialization "
local minitel = require " minitel "
local event = require " event "
local rpc = { }
_G.rpcf = { }
rpc.port = 111
2023-09-20 00:01:21 +10:00
local function setacl ( self , fname , host )
self [ fname ] = self [ fname ] or { }
self [ fname ] [ host ] = true
end
rpc.allow = setmetatable ( { } , { __call = setacl } )
rpc.deny = setmetatable ( { } , { __call = setacl } )
local function isPermitted ( host , fn )
if rpc.allow [ fn ] then
return rpc.allow [ fn ] [ host ] or false
end
if rpc.deny [ fn ] and rpc.deny [ fn ] [ host ] then
return false
end
return true
end
2019-11-26 21:34:40 +11:00
local function rpcexec ( _ , from , port , data )
2023-09-20 00:01:21 +10:00
os.spawn ( function ( )
if port == rpc.port then
local rpcrq = serial.unserialize ( data ) or { }
if rpcf [ rpcrq [ 1 ] ] and isPermitted ( from , rpcrq [ 1 ] ) then
minitel.send ( from , port , serial.serialize ( { rpcrq [ 2 ] , pcall ( rpcf [ rpcrq [ 1 ] ] , table.unpack ( rpcrq , 3 ) ) } ) )
elseif type ( rpcrq [ 2 ] ) == " string " then
minitel.send ( from , port , serial.serialize ( { rpcrq [ 2 ] , false , " function unavailable " } ) )
2019-11-26 21:34:40 +11:00
end
end
2023-09-20 00:01:21 +10:00
end , " rpc worker for " .. tostring ( from ) )
2019-11-26 21:34:40 +11:00
end
function rpcf . list ( )
local rt = { }
for k , v in pairs ( rpcf ) do
rt [ # rt + 1 ] = k
end
return rt
end
2020-05-12 10:57:13 +10:00
function rpc . call ( hostname , fn , ... ) -- string string -- boolean -- Calls exported function *fn* on host *hostname*, with parameters *...*, returning whatever the function returns, or false.
2019-11-26 21:34:40 +11:00
if hostname == " localhost " then
return rpcf [ fn ] ( ... )
end
local rv = minitel.genPacketID ( )
minitel.rsend ( hostname , rpc.port , serial.serialize ( { fn , rv , ... } ) , true )
local st = computer.uptime ( )
local rt = { }
repeat
local _ , from , port , data = event.pull ( 30 , " net_msg " , hostname , rpc.port )
2020-03-26 17:33:18 +11:00
rt = serial.unserialize ( tostring ( data ) ) or { }
until ( type ( rt ) == " table " and rt [ 1 ] == rv ) or computer.uptime ( ) > st + 30
2023-09-20 00:01:21 +10:00
if rt [ 1 ] == rv then
if rt [ 2 ] then
return table.unpack ( rt , 3 )
end
error ( rt [ 3 ] )
2019-11-26 21:34:40 +11:00
end
2023-09-20 00:01:21 +10:00
error ( " timed out " )
2019-11-26 21:34:40 +11:00
end
2020-05-12 10:57:13 +10:00
function rpc . proxy ( hostname , filter ) -- string string -- table -- Returns a component.proxy()-like table from the functions on *hostname* with names matching *filter*.
2019-11-26 21:34:40 +11:00
filter = ( filter or " " ) .. " (.+) "
local fnames = rpc.call ( hostname , " list " )
if not fnames then return false end
local rt = { }
for k , v in pairs ( fnames ) do
fv = v : match ( filter )
if fv then
rt [ fv ] = function ( ... )
return rpc.call ( hostname , v , ... )
end
end
end
return rt
end
2020-05-12 10:57:13 +10:00
function rpc . register ( name , fn ) -- string function -- -- Registers a function to be exported by the RPC library.
2019-11-26 21:34:40 +11:00
local rpcrunning = false
for k , v in pairs ( os.tasks ( ) ) do
if os.taskInfo ( v ) . name == " rpc daemon " then
rpcrunning = true
end
end
if not rpcrunning then
os.spawn ( function ( )
while true do
2023-09-20 00:01:21 +10:00
pcall ( rpcexec , event.pull ( " net_msg " ) )
2019-11-26 21:34:40 +11:00
end
end , " rpc daemon " )
end
rpcf [ name ] = fn
end
return rpc