#!/usr/bin/env lua
--[[ Operating System Interface Nativ Implemented Functions
--




Library is DEPRECATED and split into two:

https://holmeinbuch.de/repo/osm/ -> more functions - extends Lua os
https://holmeinbuch.de/repo/osn/ -> network (socket) part: connect/serve













There is no goal to implement every posix call for every operating system.
It supports the calls I need to interact with the system.
The interface uses Lua style (string instead of int flags)
The library is implemented for Linux.
For general purpose, check out:
https://github.com/luaposix/luaposix
https://github.com/wahern/cqueues
]]
local osinif = require("osinif")

--[[ bgexec
arg1: external program
optional arg2 - argX
return:
arg1: pid
return on error: nil, message[, errno]
]]
print("bgexec(\"echo\", \"  bgexec child: Hello\")", osinif.bgexec("echo", "  bgexec child: Hello"))

--[[ bgwait
optional arg1: number pid or -1
  -> without arg1 (or 0) bgwait will return immediately
  -> -1 will wait for any child
return:
arg1: pid or 0
  -> 0, if no bgexec ended, there will be no other args returned
optional arg2: exit code
  -> if signal exit code will be 128 + signal number
optional arg3: signal / error message
  -> error message will be set, if bgexec failed to start program
return on error: nil, message[, errno]
]]
print("bgwait(-1)", osinif.bgwait(-1))
print("bgwait(-1)", osinif.bgwait(-1))

local s_test_temp_path = "/tmp/test_osinif.dir"
--[[ mkdir
arg1: string path -> no recursive creation
optional arg2: string mode
return:
arg1: boolean true
return on error: nil, message[, errno]
]]
print(string.format("mkdir(\"%s\")", s_test_temp_path), osinif.mkdir(s_test_temp_path))

--[[ mkfile
WHY? -> will "exclusive" create a file, fail, if it exists
arg1: string path
optional arg2: string mode
optional arg3: boolean -> request file
return:
arg1: boolean true or file (if in arg3 is true)
return on file exist: false, message
return on error: nil, message[, errno]
]]
local s_test_temp_file = "/tmp/test_osinif.file"
local f_test_temp = osinif.mkfile(s_test_temp_file, nil, true)
print(string.format("mkfile(\"%s\", nil, true)", s_test_temp_file), f_test_temp)
f_test_temp:close()

--[[ chmod
arg1: string path
arg2: string mode (set permission in numeric mode, by up to 4 octal digits)
       -> 1st: suid=4, sgid=2, sticky=1 / 2nd=u,3rd=g,4th=o: r=4, w=2, x=1
       -> no parsing of "u+rw" style - can be implemented in Lua, if required
return:
arg1: boolean true
return on error: nil, message[, errno]
]]
print(string.format("chmod(\"%s\", \"%s\")", s_test_temp_path, "700"), osinif.chmod(s_test_temp_path, "700"))

--[[ fmode
arg1: string filename (or stat table)

return:
arg1: string octal mode
return on error: nil, message[, errno]
]]
print(string.format("fmode(\"%s\")", s_test_temp_path), osinif.fmode(s_test_temp_path))
print(string.format("fmode(\"%s\")", s_test_temp_file), osinif.fmode(s_test_temp_file))

--[[ ftype
arg1: string filename (or stat table)

return:
arg1: string: "socket", "symlink", "file", "bdev", "dir", "cdev", "fifo"
return on error: nil, message[, errno]
]]
print(string.format("ftype(\"%s\")", s_test_temp_path), osinif.ftype(s_test_temp_path))
print(string.format("ftype(\"%s\")", s_test_temp_file), osinif.ftype(s_test_temp_file))
print(string.format("os.remove(\"%s\")", s_test_temp_file), os.remove(s_test_temp_file))

--[[ getpid
return:
arg1: integer pid
]]
print("getpid()", osinif.getpid())

--[[ gettime
optional arg1: string: "r[ealtime]" (default), "t[ai]", "m[onotonic]", "c[putime]"
               - only the first letter (lowercase) is important
return:
arg1: integer seconds
arg2: integer nanoseconds
return on error: nil, message[, errno]
]]
print("gettime()", osinif.gettime())

--[[ lip -- list ip -> iterator
arg1: string name
optional arg2: string: "[IPv]4" (default), [IPv]6, ANY
return:
iteratorfunction return:
  arg1: string ip
  arg2: string family (IPv4, IPv6)
return on error: iteratorfunction, message
  error iteratorfunction return:
    arg1: nil
    arg2: error
]]
print("lip(\"localhost\", \"\")")
-- set to "ANY"
for ip, family in osinif.lip("localhost", "") do
  print(" -", ip, family)
end

--[[ ls -- list (dir) -> iterator
optional arg1: string dir - default: current dir
iteratorfunction return:
  arg1: string file
   -> it will NOT return "." and ".."
return on error: iteratorfunction, message
  error iteratorfunction return:
    arg1: nil
    arg2: error
]]
print("ls()")
for file in osinif.ls() do
  print(" -", file)
end

--[[ realpath
arg1: string filename
return:
arg1: string realpath
return on error: nil, message[, errno]
]]
print(string.format("realpath(\"%s\")", s_test_temp_path), osinif.realpath(s_test_temp_path))

--[[ stat
arg1: string filename
return:
arg1: table stat: "dev", "inode", "mode", "nlink", "uid", "gid", "size", "blksize", "blocks", "atime", "mtime", "mtins", "ctime"
      -> fmode and ftype are extra (metatable) fields, which will call these functions
return on error: nil, message[, errno]
]]
print(string.format("stat(\"%s\")", s_test_temp_path), osinif.stat(s_test_temp_path))
local t_stat = osinif.stat(s_test_temp_path)
for k, v in pairs(t_stat) do
  print(" -", k, v)
end
print(" +", "fmode", t_stat.fmode)
print(" +", "ftype", t_stat.ftype)

--[[ utime
arg1: string filename
optional arg2: number atime or false
optional arg3: number mtime or false
-> false will not update the time, if no time given NOW (in ns) is used
return on error: nil, message[, errno]
]]
print(string.format("utime(\"%s\", false, 12345678)", s_test_temp_path), osinif.utime(s_test_temp_path, false, 12345678))
print(" - atime:", osinif.stat(s_test_temp_path).atime)
print(" - mtime:", osinif.stat(s_test_temp_path).mtime)

--[[ rmdir
arg1: string path
return:
arg1: boolean true
return on error: nil, message[, errno]
]]
print(string.format("rmdir(\"%s\") => %s", s_test_temp_path, osinif.rmdir(s_test_temp_path)))


--[[

==== socket ====
socket errors:
nil, error message[, errno]
special error messages:
nil, "closed" socket is closed (may apply only to receive OR send)
nil, "timeout" receiving/sending did run into a timeout
               -> on receive there might be:
                  "", "timeout" (just retry)
                  nil, "timeout" (better give up)
nil, "signal", sig, message, signo
               -> sig is one of: {"int", "term", "pipe", "usr1", "usr2"}

== connect / serve ==
connect to a server / start a server
arg1: table:
 => port or usock must be set
 - usock: string unix socket path
 - port: string port (or service)
 - ip: string ip (or name)
 => all other are optional
 - family: string - default, depends on IP
 - protocol: string - "[t]cp" (default), "[u]dp"
 - tcp_nodelay: boolean (advanced setting, default false)
 => connect supports:
 - connect_timeout: integer timeout in milliseconds
 => serve supports:
 - remove_socket_before_start: boolean (default false)
 - socket_mode: string mode
return:
arg1: socket (connected or bind)
arg2 (udp): -> server address (required for send)
return on error: nil, message[, errno]

== close ==
return:
arg1: true
return on error: nil, message[, errno]

=== listen socket ===
exist only for tcp -> serve

== accept ==
return:
arg1: socket (connected)
return on error: nil, message[, errno]

=== connected socket ===

== getpeername ==
return:
arg1: string ip / unix socket path
arg2: port
return on error: nil, message[, errno]

== receive ==
arg1: integer size
optional arg2: long polling - "BLOCK" (forever) or n seconds (0 < n)
return:
arg1: string read
arg2 (udp): -> sender address (required for send)
return on error: nil, message[, errno]

== send ==
arg1: string data
arg2 (udp): -> receiver address
return:
arg1: integer size
return on error: nil, message[, errno]

]]

