From ecda757ee341b982330ed22c8c60d744e4d0c2c4 Mon Sep 17 00:00:00 2001 From: payonel Date: Thu, 23 Jun 2016 07:09:30 -0700 Subject: [PATCH 1/3] fixes for buffer, install, ls, package, profile, rc, sh buffer: don't allow reading zero bytes from a stream, and fix libraries that should be allowed to set zero size buffer install: report setting boot address, ^d should not accept prompts ls: use shell to get working dir, not PWD package: remove clutter for library keys and _G rc: add unload to lib methods and fix typo in source sh: remove clutter from _G and use zero sized buffer for piping --- .../opencomputers/loot/openos/bin/install.lua | 9 ++++++--- .../assets/opencomputers/loot/openos/bin/ls.lua | 2 +- .../assets/opencomputers/loot/openos/bin/rc.lua | 14 +++++++------- .../opencomputers/loot/openos/lib/buffer.lua | 2 +- .../opencomputers/loot/openos/lib/package.lua | 4 +--- .../assets/opencomputers/loot/openos/lib/pipes.lua | 2 +- .../assets/opencomputers/loot/openos/lib/sh.lua | 8 ++++---- 7 files changed, 21 insertions(+), 20 deletions(-) diff --git a/src/main/resources/assets/opencomputers/loot/openos/bin/install.lua b/src/main/resources/assets/opencomputers/loot/openos/bin/install.lua index d063cc208..31e48d79d 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/bin/install.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/bin/install.lua @@ -31,13 +31,16 @@ if options.setlabel then end if options.setboot then - computer.setBootAddress(options.target.dev.address) + local address = options.target.dev.address + if computer.setBootAddress(address) then + write("Boot address set to " .. address) + end end if options.reboot then write("Reboot now? [Y/n] ") - local result = read() - if not result or result == "" or result:sub(1, 1):lower() == "y" then + local result = read() or "n" + if result:sub(1, 1):lower() == "y" then write("\nRebooting now!\n") computer.shutdown(true) end diff --git a/src/main/resources/assets/opencomputers/loot/openos/bin/ls.lua b/src/main/resources/assets/opencomputers/loot/openos/bin/ls.lua index cdeea8316..9a878862c 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/bin/ls.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/bin/ls.lua @@ -284,7 +284,7 @@ local function displayDirList(dirs) end end end -local tr,cp={},{path=os.getenv("PWD")} +local tr,cp={},{path=shell.getWorkingDirectory()} for _,dir in ipairs(dirsArg) do local path = shell.resolve(dir) if not fs.exists(path) then diff --git a/src/main/resources/assets/opencomputers/loot/openos/bin/rc.lua b/src/main/resources/assets/opencomputers/loot/openos/bin/rc.lua index 02fcef4d8..018f79efe 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/bin/rc.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/bin/rc.lua @@ -44,7 +44,7 @@ local function load(name, args) return nil, reason end -function unload(name) +function rc.unload(name) rc.loaded[name] = nil end @@ -60,15 +60,15 @@ local function rawRunCommand(conf, name, cmd, args, ...) end return true elseif type(result[cmd]) == "function" then - res, what = xpcall(result[cmd], debug.traceback, ...) - if res then + result, what = xpcall(result[cmd], debug.traceback, ...) + if result then return true end elseif cmd == "restart" and type(result["stop"]) == "function" and type(result["start"]) == "function" then - res, what = xpcall(result["stop"], debug.traceback, ...) - if res then - res, what = xpcall(result["start"], debug.traceback, ...) - if res then + result, what = xpcall(result["stop"], debug.traceback, ...) + if result then + result, what = xpcall(result["start"], debug.traceback, ...) + if result then return true end end diff --git a/src/main/resources/assets/opencomputers/loot/openos/lib/buffer.lua b/src/main/resources/assets/opencomputers/loot/openos/lib/buffer.lua index 6a0c45845..233a62505 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/lib/buffer.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/lib/buffer.lua @@ -73,7 +73,7 @@ function buffer:read(...) if computer.uptime() > timeout then error("timeout") end - local result, reason = self.stream:read(self.bufferSize) + local result, reason = self.stream:read(math.max(1,self.bufferSize)) if result then self.bufferRead = self.bufferRead .. result return self diff --git a/src/main/resources/assets/opencomputers/loot/openos/lib/package.lua b/src/main/resources/assets/opencomputers/loot/openos/lib/package.lua index dc1185535..f1910cda3 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/lib/package.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/lib/package.lua @@ -61,15 +61,13 @@ end local delay_data = {} local delay_tools = setmetatable({},{__mode="v"}) -package.delay_data = delay_data - function delay_data.__index(tbl,key) delay_data.lookup = delay_data.lookup or loadfile("/lib/tools/delayLookup.lua") return delay_data.lookup(delay_data, tbl, key) end delay_data.__pairs = delay_data.__index -- nil key acts like pairs -function delaySearcher(module) +local function delaySearcher(module) if not delayed[module] then return "\tno field package.delayed['" .. module .. "']" end diff --git a/src/main/resources/assets/opencomputers/loot/openos/lib/pipes.lua b/src/main/resources/assets/opencomputers/loot/openos/lib/pipes.lua index 8c1e972a8..43568d218 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/lib/pipes.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/lib/pipes.lua @@ -367,7 +367,7 @@ function plib.popen(prog, mode, env) pm.pco=plib.internal.create(pm.root) local pfd = require("buffer").new(mode, pipeStream.new(pm)) - pfd:setvbuf("no", nil) -- 2nd are to read chunk size + pfd:setvbuf("no", 0) -- 2nd are to read chunk size -- popen processes start on create (which is LAME :P) pfd.stream:resume() diff --git a/src/main/resources/assets/opencomputers/loot/openos/lib/sh.lua b/src/main/resources/assets/opencomputers/loot/openos/lib/sh.lua index 868f7ea58..4a3a12803 100644 --- a/src/main/resources/assets/opencomputers/loot/openos/lib/sh.lua +++ b/src/main/resources/assets/opencomputers/loot/openos/lib/sh.lua @@ -12,8 +12,8 @@ sh.internal = {} -- --[[@@]] are not just comments, but custom annotations for delayload methods. -- See package.lua and the api wiki for more information -function isWordOf(w, vs) return w and #w == 1 and not w[1].qr and tx.first(vs,{{w[1].txt}}) ~= nil end -function isWord(w,v) return isWordOf(w,{v}) end +local function isWordOf(w, vs) return w and #w == 1 and not w[1].qr and tx.first(vs,{{w[1].txt}}) ~= nil end +local function isWord(w,v) return isWordOf(w,{v}) end local local_env = {event=event,fs=fs,process=process,shell=shell,term=term,text=text,tx=tx,unicode=unicode,isWordOf=isWordOf,isWord=isWord} ------------------------------------------------------------------------------- @@ -424,7 +424,7 @@ function --[[@delayloaded-start@]] sh.internal.buildPipeChain(threads) local pipe if i < #threads then pipe = require("buffer").new("rw", sh.internal.newMemoryStream()) - pipe:setvbuf("no") + pipe:setvbuf("no", 0) -- buffer close flushes the buffer, but we have no buffer -- also, when the buffer is closed, read and writes don't pass through -- simply put, we don't want buffer:close @@ -461,7 +461,7 @@ function --[[@delayloaded-start@]] sh.internal.glob(glob_pattern) end local is_abs = glob_pattern:sub(1, 1) == "/" - local root = is_abs and '' or shell.getWorkingDirectory() + local root = is_abs and '' or shell.getWorkingDirectory():gsub("([^/])$","%1/") local paths = {is_abs and "/" or ''} local relative_separator = '' From a9f2c6d77b7659f7fc58ce7a99f8640abfd283f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20N=C3=BCcke?= Date: Sat, 25 Jun 2016 12:50:44 +0200 Subject: [PATCH 2/3] Readonly logger. --- src/main/scala/li/cil/oc/OpenComputers.scala | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/scala/li/cil/oc/OpenComputers.scala b/src/main/scala/li/cil/oc/OpenComputers.scala index 4f6aae03e..31b06e99a 100644 --- a/src/main/scala/li/cil/oc/OpenComputers.scala +++ b/src/main/scala/li/cil/oc/OpenComputers.scala @@ -10,6 +10,7 @@ import li.cil.oc.common.IMC import li.cil.oc.common.Proxy import li.cil.oc.server.command.CommandHandler import org.apache.logging.log4j.LogManager +import org.apache.logging.log4j.Logger @Mod(modid = OpenComputers.ID, name = OpenComputers.Name, version = OpenComputers.Version, @@ -21,7 +22,9 @@ object OpenComputers { final val Version = "@VERSION@" - var log = LogManager.getLogger(Name) + def log = logger.getOrElse(LogManager.getLogger(Name)) + + var logger: Option[Logger] = None @SidedProxy(clientSide = "li.cil.oc.client.Proxy", serverSide = "li.cil.oc.server.Proxy") var proxy: Proxy = null @@ -30,7 +33,7 @@ object OpenComputers { @EventHandler def preInit(e: FMLPreInitializationEvent) { - log = e.getModLog + logger = Option(e.getModLog) proxy.preInit(e) OpenComputers.log.info("Done with pre init phase.") } From 422592d84e83c5c293bd0eb5e5f103ab590fa076 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Sat, 25 Jun 2016 13:17:06 +0200 Subject: [PATCH 3/3] Send internet events when ready to read. --- .../oc/server/component/InternetCard.scala | 80 ++++++++++++++++--- 1 file changed, 70 insertions(+), 10 deletions(-) diff --git a/src/main/scala/li/cil/oc/server/component/InternetCard.scala b/src/main/scala/li/cil/oc/server/component/InternetCard.scala index ec8f74da4..a56d1d5e7 100644 --- a/src/main/scala/li/cil/oc/server/component/InternetCard.scala +++ b/src/main/scala/li/cil/oc/server/component/InternetCard.scala @@ -7,25 +7,26 @@ import java.io.InputStream import java.io.OutputStreamWriter import java.net._ import java.nio.ByteBuffer +import java.nio.channels.SelectionKey +import java.nio.channels.Selector import java.nio.channels.SocketChannel import java.util -import java.util.concurrent.Callable -import java.util.concurrent.ConcurrentLinkedQueue -import java.util.concurrent.ExecutionException -import java.util.concurrent.Future +import java.util.UUID +import java.util.concurrent._ import li.cil.oc.Constants +import li.cil.oc.OpenComputers +import li.cil.oc.Settings +import li.cil.oc.api.Network +import li.cil.oc.api.driver.DeviceInfo import li.cil.oc.api.driver.DeviceInfo.DeviceAttribute import li.cil.oc.api.driver.DeviceInfo.DeviceClass -import li.cil.oc.Settings import li.cil.oc.api.machine.Arguments import li.cil.oc.api.machine.Callback import li.cil.oc.api.machine.Context import li.cil.oc.api.network._ -import li.cil.oc.api.prefab.AbstractValue -import li.cil.oc.api.Network -import li.cil.oc.api.driver.DeviceInfo import li.cil.oc.api.prefab +import li.cil.oc.api.prefab.AbstractValue import li.cil.oc.util.ThreadPoolFactory import net.minecraft.server.MinecraftServer @@ -183,6 +184,41 @@ object InternetCard { def close(): Unit } + object TCPNotifier extends Thread { + private val selector = Selector.open() + private val toAccept = new ConcurrentLinkedQueue[(SocketChannel, () => Unit)] + + override def run(): Unit = { + while (true) { + try { + Stream.continually(toAccept.poll).takeWhile(_ != null).foreach({ + case (channel: SocketChannel, action: (() => Unit)) => + channel.register(selector, SelectionKey.OP_READ, action) + }) + + selector.select() + + import scala.collection.JavaConversions._ + val selectedKeys = selector.selectedKeys + selectedKeys.filter(_.isReadable).foreach(key => { + key.cancel() + key.attachment().asInstanceOf[() => Unit].apply() + }) + } catch { + case e: IOException => + OpenComputers.log.error("Error in TCP selector loop.", e) + } + } + } + + def add(e: (SocketChannel, () => Unit)) { + toAccept.offer(e) + selector.wakeup() + } + } + + TCPNotifier.start() + class TCPSocket extends AbstractValue with Closable { def this(owner: InternetCard, uri: URI, port: Int) { this() @@ -196,9 +232,25 @@ object InternetCard { private var address: Future[InetAddress] = null private var channel: SocketChannel = null private var isAddressResolved = false + private val id = UUID.randomUUID() + + private def setupSelector() { + TCPNotifier.add((channel, () => { + owner match { + case Some(internetCard) => + internetCard.node.sendToVisible("computer.signal", "internet_ready", internetCard.node.address(), id.toString) + case _ => + channel.close() + } + })) + } @Callback(doc = """function():boolean -- Ensures a socket is connected. Errors if the connection failed.""") - def finishConnect(context: Context, args: Arguments): Array[AnyRef] = this.synchronized(result(checkConnected())) + def finishConnect(context: Context, args: Arguments): Array[AnyRef] = { + val r = this.synchronized(result(checkConnected())) + setupSelector() + r + } @Callback(doc = """function([n:number]):string -- Tries to read data from the socket stream. Returns the read byte array.""") def read(context: Context, args: Arguments): Array[AnyRef] = this.synchronized { @@ -207,7 +259,10 @@ object InternetCard { val buffer = ByteBuffer.allocate(n) val read = channel.read(buffer) if (read == -1) result(Unit) - else result(buffer.array.view(0, read).toArray) + else { + setupSelector() + result(buffer.array.view(0, read).toArray) + } } else result(Array.empty[Byte]) } @@ -227,6 +282,11 @@ object InternetCard { null } + @Callback(direct = true, doc = """function():string -- Returns connection ID.""") + def id(context: Context, args: Arguments): Array[AnyRef] = this.synchronized { + result(id.toString) + } + override def dispose(context: Context): Unit = { super.dispose(context) close()