From e410a9c2e542bc550c7fa619db550242cda6b345 Mon Sep 17 00:00:00 2001
From: "aiyion.prime" <git@aiyionpri.me>
Date: Thu, 17 Jun 2021 22:25:05 +0200
Subject: [PATCH] gluon-core: implement popen3() in gluon/util.lua

[Matthias Schiffer: simplify close_fds() helper and use in child process]
---
 .../luasrc/usr/lib/lua/gluon/util.lua         | 74 +++++++++++++++++++
 1 file changed, 74 insertions(+)

diff --git a/package/gluon-core/luasrc/usr/lib/lua/gluon/util.lua b/package/gluon-core/luasrc/usr/lib/lua/gluon/util.lua
index a5d13a5b..48b8340e 100644
--- a/package/gluon-core/luasrc/usr/lib/lua/gluon/util.lua
+++ b/package/gluon-core/luasrc/usr/lib/lua/gluon/util.lua
@@ -1,6 +1,8 @@
 local bit = require 'bit'
+local posix_fcntl = require 'posix.fcntl'
 local posix_glob = require 'posix.glob'
 local posix_syslog = require 'posix.syslog'
+local posix_unistd = require 'posix.unistd'
 local hash = require 'hash'
 local sysconfig = require 'gluon.sysconfig'
 local site = require 'gluon.site'
@@ -193,4 +195,76 @@ function M.log(message, verbose)
 	posix_syslog.syslog(posix_syslog.LOG_INFO, message)
 end
 
+local function close_fds(fds)
+	for _, fd in pairs(fds) do
+		posix_unistd.close(fd)
+	end
+end
+
+M.subprocess = {}
+
+M.subprocess.DEVNULL = -1
+M.subprocess.PIPE = 1
+
+-- Execute a program found using command PATH search, like the shell.
+-- Return the pid, as well as the I/O streams as pipes or nil on error.
+function M.subprocess.popen(path, argt, options)
+	argt = argt or {}
+	local childfds = {}
+	local parentfds = {}
+	local stdiostreams = {stdin = 0, stdout = 1, stderr = 2}
+
+	for iostream in pairs(stdiostreams) do
+		if options[iostream] == M.subprocess.PIPE then
+			local piper, pipew = posix_unistd.pipe()
+			if iostream == "stdin" then
+				childfds[iostream] = piper
+				parentfds[iostream] = pipew
+			else
+				childfds[iostream] = pipew
+				parentfds[iostream] = piper
+			end
+		end
+	end
+
+	-- childfds: r0, w1, w2
+	-- parentfds: w0, r1, r2
+
+	local pid, errmsg, errnum = posix_unistd.fork()
+
+	if pid == nil then
+		close_fds(parentfds)
+		close_fds(childfds)
+		return nil, errmsg, errnum
+	elseif pid == 0 then
+		local null = -1
+		if M.contains(options, M.subprocess.DEVNULL) then
+			-- only open if there's anything to discard
+			null = posix_fcntl.open('/dev/null', posix_fcntl.O_RDWR)
+		end
+
+		for iostream, fd in pairs(stdiostreams) do
+			local option = options[iostream]
+			if option == M.subprocess.DEVNULL then
+				posix_unistd.dup2(null, fd)
+			elseif option == M.subprocess.PIPE then
+				posix_unistd.dup2(childfds[iostream], fd)
+			end
+		end
+		close_fds(childfds)
+		close_fds(parentfds)
+
+		-- close potential null
+		if null > 2 then
+			posix_unistd.close(null)
+		end
+
+		posix_unistd.execp(path, argt)
+		posix_unistd._exit(127)
+	end
+
+	close_fds(childfds)
+
+	return pid, parentfds
+end
 return M
-- 
GitLab