How do you construct a read-write pipe with lua? - lua

I'd like to do the equivalent of:
foo=$(echo "$foo"|someprogram)
within lua -- ie, I've got a variable containing a bunch of text, and I'd like to run it through a filter (implemented in python as it happens).
Any hints?
Added: would really like to do this without using a temporary file

As long as your Lua supports io.popen, this problem is easy. The solution is exactly as you have outlined, except instead of $(...) you need a function like this one:
function os.capture(cmd, raw)
local f = assert(io.popen(cmd, 'r'))
local s = assert(f:read('*a'))
f:close()
if raw then return s end
s = string.gsub(s, '^%s+', '')
s = string.gsub(s, '%s+$', '')
s = string.gsub(s, '[\n\r]+', ' ')
return s
end
You can then call
local foo = ...
local cmd = ("echo $foo | someprogram"):gsub('$foo', foo)
foo = os.capture(cmd)
I do stuff like this all the time. Here's a related useful function for forming commands:
local quote_me = '[^%w%+%-%=%#%_%/]' -- complement (needn't quote)
local strfind = string.find
function os.quote(s)
if strfind(s, quote_me) or s == '' then
return "'" .. string.gsub(s, "'", [['"'"']]) .. "'"
else
return s
end
end

I stumbled on this post while trying to do the same thing and never found a good solution, see the code below for how I solved my issues. This implementation allows users to access stdin, stdout, stderr and get the return status code. A simple wrapper is called for simple pipe calls.
require("posix")
--
-- Simple popen3() implementation
--
function popen3(path, ...)
local r1, w1 = posix.pipe()
local r2, w2 = posix.pipe()
local r3, w3 = posix.pipe()
assert((r1 ~= nil or r2 ~= nil or r3 ~= nil), "pipe() failed")
local pid, err = posix.fork()
assert(pid ~= nil, "fork() failed")
if pid == 0 then
posix.close(w1)
posix.close(r2)
posix.dup2(r1, posix.fileno(io.stdin))
posix.dup2(w2, posix.fileno(io.stdout))
posix.dup2(w3, posix.fileno(io.stderr))
posix.close(r1)
posix.close(w2)
posix.close(w3)
local ret, err = posix.execp(path, unpack({...}))
assert(ret ~= nil, "execp() failed")
posix._exit(1)
return
end
posix.close(r1)
posix.close(w2)
posix.close(w3)
return pid, w1, r2, r3
end
--
-- Pipe input into cmd + optional arguments and wait for completion
-- and then return status code, stdout and stderr from cmd.
--
function pipe_simple(input, cmd, ...)
--
-- Launch child process
--
local pid, w, r, e = popen3(cmd, unpack({...}))
assert(pid ~= nil, "filter() unable to popen3()")
--
-- Write to popen3's stdin, important to close it as some (most?) proccess
-- block until the stdin pipe is closed
--
posix.write(w, input)
posix.close(w)
local bufsize = 4096
--
-- Read popen3's stdout via Posix file handle
--
local stdout = {}
local i = 1
while true do
buf = posix.read(r, bufsize)
if buf == nil or #buf == 0 then break end
stdout[i] = buf
i = i + 1
end
--
-- Read popen3's stderr via Posix file handle
--
local stderr = {}
local i = 1
while true do
buf = posix.read(e, bufsize)
if buf == nil or #buf == 0 then break end
stderr[i] = buf
i = i + 1
end
--
-- Clean-up child (no zombies) and get return status
--
local wait_pid, wait_cause, wait_status = posix.wait(pid)
return wait_status, table.concat(stdout), table.concat(stderr)
end
--
-- Example usage
--
local my_in = io.stdin:read("*all")
--local my_cmd = "wc"
--local my_args = {"-l"}
local my_cmd = "spamc"
local my_args = {} -- no arguments
local my_status, my_out, my_err = pipe_simple(my_in, my_cmd, unpack(my_args))
-- Obviously not interleaved as they would have been if printed in realtime
io.stdout:write(my_out)
io.stderr:write(my_err)
os.exit(my_status)

There is nothing in the Lua standard library to allow this.
Here is an in-depth exploration of the difficulties of doing bidirectional communication properly, and a proposed solution:
if possible, redirect one end of the stream (input or output) to a file. I.e.:
fp = io.popen("foo >/tmp/unique", "w")
fp:write(anything)
fp:close()
fp = io.open("/tmp/unique")
x = read("*a")
fp:close()
You may be interested in this extension which adds functions to the os and io namespaces to make bidirectional communication with a subprocess possible.

Aha, a possibly better solution:
require('posix')
require('os')
require('io')
function splat_popen(data,cmd)
rd,wr = posix.pipe()
io.flush()
child = posix.fork()
if child == 0 then
rd:close()
wr:write(data)
io.flush()
os.exit(1)
end
wr:close()
rd2,wr2 = posix.pipe()
io.flush()
child2 = posix.fork()
if child2 == 0 then
rd2:close()
posix.dup(rd,io.stdin)
posix.dup(wr2,io.stdout)
posix.exec(cmd)
os.exit(2)
end
wr2:close()
rd:close()
y = rd2:read("*a")
rd2:close()
posix.wait(child2)
posix.wait(child)
return y
end
munged=splat_popen("hello, world","/usr/games/rot13")
print("munged: "..munged.." !")

A not very nice solution that avoids a temporary file...
require("io")
require("posix")
x="hello\nworld"
posix.setenv("LUA_X",x)
i=popen('echo "$LUA_X" | myfilter')
x=i.read("*a")

Here is how I solved the problem, it require lua posix.
p = require 'posix'
local r,w = p.pipe()
local r1,w1 = p.pipe()
local cpid = p.fork()
if cpid == 0 then -- child reads from pipe
w:close()
r1:close()
p.dup(r, io.stdin)
p.dup(w1 ,io.stdout)
p.exec('./myProgram')
r:close()
w1:close()
p._exit(0)
else -- parent writes to pipe
IN = r1
OUT = w
end
During myProgram execution, you'l read and write from normal io and after this part of code you just have to write/read on IN and OUT to comunicate with child program.

For a system I have running Lua 5.1 and luaposix 35.0-1, I started with the solution from Anthony Towns from this current page and made it work for this luaposix version for the purpose of calling openssl for encryption on a system without any other encryption capabilities. I have attempted to make the code more explicit in order to allow others to handle any potential API changes in luaposix in the future.
local posix = require('posix');
require('os');
require('io');
local function getOutputFromProcessProvidedInput( dataForProcess, command, commandArguments )
local MAXIMUM_BYTE_READ_COUNT = 100;
local readFileHandle1,writeFileHandle1 = posix.pipe()
io.flush();
local childProcessId1 = posix.fork();
if (childProcessId1 == 0)
then
posix.close( readFileHandle1 );
posix.write( writeFileHandle1, dataForProcess );
io.flush();
os.exit( 1 );
end
posix.close( writeFileHandle1 );
local readFileHandle2,writeFileHandle2 = posix.pipe();
io.flush();
local childProcessId2 = posix.fork();
if (childProcessId2 == 0)
then
posix.close( readFileHandle2 );
posix.dup2( readFileHandle1, posix.fileno( io.stdin ) );
posix.dup2( writeFileHandle2, posix.fileno( io.stdout ) );
posix.execp( command, commandArguments );
os.exit( 2 );
end
posix.close( writeFileHandle2 );
posix.close( readFileHandle1 );
local dataFromProcess = posix.read( readFileHandle2, MAXIMUM_BYTE_READ_COUNT );
posix.close( readFileHandle2 );
posix.wait( childProcessId2 );
posix.wait( childProcessId1 );
return dataFromProcess;
end
-- Command being executed
-- echo -n AAAAAAAAAAAAAAAA | openssl aes-128-cbc -e -nopad -a -K 30313233343536373839616263646566 -iv 1FF1ECB9000000000000000000000000
-- Expected result
-- 28iudIC31lHfDDxfa1/g9w==
result = openReadWritePipe("AAAAAAAAAAAAAAAA","openssl",{"aes-128-cbc", "-e", "-nopad", "-a", "-K", "30313233343536373839616263646566", "-iv", "1FF1ECB9000000000000000000000000"});
print("Result: "..result);

It's easy, no extensions necessary (tested with lua 5.3).
#!/usr/bin/lua
-- use always locals
local stdin = io.stdin:lines()
local stdout = io.write
for line in stdin do
stdout (line)
end
save as inout.lua and do chmod +x /tmp/inout.lua
20:30 $ foo=$(echo "bla"| /tmp/inout.lua)
20:30 $ echo $foo
bla

Related

I made a (very simple) programming language in Lua, but I can only execute one command

Hello people of stackoverflow, I've made a (very) simple programming language, it kind of looks like minecraft commands. Here is the code
function wait(cmdSleepGetNum)-- you can name this function
--what ever you want, it doesnt matter
local start = os.time()
repeat until os.time() > start + cmdSleepGetNum
end
local input = io.read()
if input == "/help" then
print"you'll find it out"
else if input == "/say" then
print"what do you want to say?"
local cmdSay = io.read()
print(cmdSay)
else if input == "/stop" then
os.exit()
else if input == "/sleep" then
print"how long?"
local cmdSleepGetNum = io.read()
wait(cmdSleepGetNum)
else if input == "/rand" then
local randNum = math.random()
print(randNum)
end
end
end
end
end
Now I know what you are thinking "what is the problem here?" the problem is that I can only execute one command and after that command is finished by the Lua interpreter, I cannot execute any other commands.
for example:
/rand
0.84018771715471 (the /rand command is executed and prints out a random number)
/rand
stdin:1: unexpected symbol near '/' (this happens when i try to execute another command)
If you replace else if with elseif, you won't need so many end's,
You can get rid of if's altogether, is you use a table,
As #Egor Skriptunoff said, you need to create a main loop to run many commands,
I suggest that you add an optional argument to /sleep and /say.
local function wait (cmdSleepGetNum) -- you can name this function
-- whatever you want, it doesn't matter.
local start = os.time ()
repeat until os.time () > start + cmdSleepGetNum
end
local commands = {
help = function ()
print "you'll find it out"
end,
say = function (arg)
if arg == '' then
print 'what do you want to say?'
arg = io.read ()
end
print (arg)
end,
stop = function ()
os.exit ()
end,
sleep = function (arg)
if arg == '' then
print 'how long?'
arg = tonumber (io.read ())
end
wait (tonumber (arg))
end,
rand = function ()
local randNum = math.random ()
print (randNum)
end,
[false] = function () -- fallback.
print 'Unknown command'
end
}
-- Main loop:
while true do
io.write '> '
local key, _, arg = io.read ():match '^%s*/(%S+)(%s*(.*))$' -- you can type /sleep 1, etc. in one line.
local command = key and key ~= '' and commands [key] or commands [false]
command (arg)
end

Tshark - How to show the Dissection Tree of tcp playload only with tshark?

I am writing a wireshark dissector.I want to show the Dissection Tree in console.
So I tried:
tshark -V
It will show something like:
Frame 105: 69 bytes on wire (552 bits)...
...
Ethernet II, Src: Giga-Byt_97:b3:26 (e0:d5:5e:97:b3:26), Dst: Cradlepo_68:04:37 (00:e0:1c:68:04:37)
...
Internet Protocol Version 4, Src: 192.168.1.153, Dst: 192.168.1.99
...
Transmission Control Protocol, Src Port: 7555, Dst Port: 50555, Seq: 10182, Ack: 485, Len: 15
....
erlang term
PackageLength: 11
compressFlag: 0
m_system_hb_toc(2) [SmallTuple: 2]
time: 1589549432 [Int]
But only the last part is what i need:
erlang term
PackageLength: 11
compressFlag: 0
m_system_hb_toc(2) [SmallTuple: 2]
time: 1589549432 [Int]
I have tried with '-T fields' and -e option,but can not find any thing help.
Here's my dissecter's code:
local tcpPortLs = {7555}
local SIZE_LEN = 4
local pErlangExt = Proto("ErlangExt", "erlang term")
local fLen = ProtoField.uint32("ErlangExt.len", "PackageLength", base.DEC)
local fCompressFlag = ProtoField.string("ErlangExt.compressFlag", "compressFlag", base.ASCII)
local fBytes = ProtoField.bytes("ErlangExt.data", "PackageData", base.COLON)
pErlangExt.fields = {
fLen,
fBytes,
fCompressFlag,
}
local function msg_pdu_length(buf, pkt, offset)
local size_tvbr = buf:range(offset, SIZE_LEN)
local size = size_tvbr:uint()
return size + SIZE_LEN
end
local function _headBytes(n, dataBuf)
local head = dataBuf(0, n)
if dataBuf:len() == n then
return head, nil
end
local tailDataBuf = dataBuf(n, dataBuf:len() - n)
return head, tailDataBuf
end
local function _addToGroup()
-- ...
end
local function _calcMainTree()
-- ...
end
local function msg_proto_dissector(buf, pkt, root)
local dataLenBuf, metaAndDataBytes = _headBytes(SIZE_LEN, buf)
local detail = root:add(pErlangExt, buf)
local dataLen = dataLenBuf:uint()
detail:add(fLen, dataLenBuf, dataLen)
local zlibFlagBuf, tupleDataBuf = _headBytes(1, metaAndDataBytes)
local zlibFlag = zlibFlagBuf:uint()
detail:add(fCompressFlag, zlibFlagBuf, zlibFlag)
local dataRoot = detail:add(fBytes, tupleDataBuf)
pkt.cols.protocol = "ErlangExt"
local tree = _calcMainTree(tupleDataBuf, zlibFlag)
_addToGroup(dataRoot, tree)
end
function pErlangExt.dissector(buf, pkt, root)
local pktLen = buf:len()
if pktLen ~= buf:reported_len() then
return 0
end
dissect_tcp_pdus(buf, root, 4, msg_pdu_length, msg_proto_dissector)
return pktLen
end
local tcp_encap_table = DissectorTable.get("tcp.port")
for _, port in pairs(tcpPortLs) do
tcp_encap_table:add(port, pErlangExt)
end
And the captured data is https://github.com/cmingjian/testData/blob/master/stage.pcapng
How can I display only the data that I need?
Thanks.
Maybe tshark -O ErlangExt will provide you with the results you're seeking? You'll still get summary lines of all lower layers (Ethernet, IP, TCP), but only your ErlangExt data will be expanded.
From the tshark man page:
-O < protocols >
Similar to the -V option, but causes TShark to only show a detailed view of the comma-separated list of protocols specified, and show only the top-level detail line for all other protocols, rather than a detailed view of all protocols. Use the output of "tshark -G protocols" to find the abbreviations of the protocols you can specify.

How to use strtok in luajit?

My code are as follow:
local ffi = require "ffi"
local ffi_C = ffi.C
local ffi_typeof = ffi.typeof
local ffi_new = ffi.new
local ffi_string = ffi.string
local NULL = ngx.null
local tostring = tostring
ffi.cdef[[
char * strtok(char * str, const char * delimiters);
]]
local p_char_type = ffi_typeof("char[?]")
function split(src, c)
local result = {}
local pch = ffi_new(p_char_type, 1)
local psrc = ffi_new(p_char_type, #src)
local pc = ffi_new(p_char_type, #c)
ffi.copy(psrc, src)
ffi.copy(pc, c)
pch = ffi_C.strtok(psrc, pc)
while pch do
table.insert(result, ffi_string(pch))
pch = ffi_C.strtok(NULL, pc)
ngx.log(ngx.ERR, "pch ok")
end
ngx.log(ngx.ERR, "split ok")
return result
end
When I run my nginx, there are something wrong happened!
After return by the while loop, the nginx worker process crashed with signal 11.
The last ngx.log can not run.
How can I deal with it?
local psrc = ffi_new(p_char_type, #src)
ffi.copy(psrc, src)
ffi.copy when given a string source also copies a null terminator, but your array is too small to hold it, resulting an overflow.
Also, instead of using strtok, consider using Lua patterns. They are safer, easier to use, and don't depend on the FFI.

Converting table to ... and return it

Good day, I would like to know how to convert table to ... and return it.
function GetArgsNormal(...)
return ...;
end
local a,b,c=GetArgsNormal(1,2,3); --this works
print("a b c:",a,b,c);
function GetArgsTable(t,...)
for i,v in pairs(t)do
({...})[i]=v;
end
return ...;
end
local d,e,f=GetArgsTable({1,2,3},nil); --result: d=nil, e=nil, f=nil
print("def:",d,e,f);
I tried all possible ways, which I had in my mind, but without success:(
Could anyone help me in this, please?
And yes, could you please answer instead of down-voting?!
local d,e,f = unpack({1,2,3}); --result: d=1, e=2, f=3
function f()
local t = {}
-- fill table t
return unpack(t)
end
You need to be careful with 'holes' in args
function GetArgsTable(t,...)
local argc, argv = select("#", ...), {...}
-- #argv ~= argc
-- unpack(argv) ~= ...
-- assume t is array
for i,v in ipairs(t) do
argc = argc + 1
argv[argc] = v;
end
return unpack(argv, 1, argc); -- use explicit array size
end
print(GetArgsTable({1,2}, nil,'hello', nil)) -- nil hello nil 1 2
Or you can look at lua-vararg library.

Hide password with asterisk in lua

I there a way to ask password in lua but hide with asterisks?
I'm asking about a console application
For Unix: use os.execute("stty -echo raw") to turn off echoing and enter raw mode (character-by-character input) and os.execute("stty echo cooked") to turn it on and exit raw mode when you are done. In raw mode, you can get each character of the input using io.stdin:read(1) and echo your asterisk as you go (use io.flush to ensure the character appears straight away). You will need to handle deletes and the end of line yourself.
For Windows, the situation is a bit trickier. Look at What would be the Windows batch equivalent for HTML's input type=“password”? for some approaches, the best of which seems to be a VB script.
Postscript
Thanks for lhf for pointing out that you need raw mode besides -echo on input and flush after each output asterisk to get the desired result: unless you have both, the asteriskes will not be echoed until the line is ended.
This code uses platform-specific features and works both on Linux and 32-bit Windows.
Compatible with Lua 5.1 and Lua 5.2
local console
local function enter_password(prompt_message, asterisk_char, max_length)
-- returns password string
-- "Enter" key finishes the password
-- "Backspace" key undoes last entered character
if not console then
if (os.getenv'os' or ''):lower():find'windows' then
------------------ Windows ------------------
local shift = 10
-- Create executable file which returns (getch()+shift) as exit code
local getch_filespec = 'getch.com'
-- mov AH,8
-- int 21h
-- add AL,shift
-- mov AH,4Ch
-- int 21h
local file = assert(io.open(getch_filespec, 'wb'))
file:write(string.char(0xB4,8,0xCD,0x21,4,shift,0xB4,0x4C,0xCD,0x21))
file:close()
console = {
wait_key = function()
local code_Lua51, _, code_Lua52 = os.execute(getch_filespec)
local code = (code_Lua52 or code_Lua51) - shift
assert(code >= 0, getch_filespec..' execution failed')
return string.char(code)
end,
on_start = function() end,
on_finish = function() end,
backspace_key = '\b'
}
-------------------------------------------
else
------------------ Linux ------------------
console = {
wait_key = function()
return io.read(1)
end,
on_start = function()
os.execute'stty -echo raw'
end,
on_finish = function()
os.execute'stty sane'
end,
backspace_key = '\127'
}
-------------------------------------------
end
end
io.write(prompt_message or '')
io.flush()
local pwd = ''
console.on_start()
repeat
local c = console.wait_key()
if c == console.backspace_key then
if #pwd > 0 then
io.write'\b \b'
pwd = pwd:sub(1, -2)
end
elseif c ~= '\r' and #pwd < (max_length or 32) then
io.write(asterisk_char or '*')
pwd = pwd..c
end
io.flush()
until c == '\r'
console.on_finish()
io.write'\n'
io.flush()
return pwd
end
-- Usage example
local pwd = enter_password'Enter password: '
print('You entered: '..pwd:gsub('%c','?'))
print(pwd:byte(1,-1))
Bug in code, for at least linux implementation. Need to add 'and c ~= nil`:
elseif c ~= '\r' and #pwd < (max_length or 32) and c ~= nil then

Resources