Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: allow passing G_SPAWN stdio flags to awesome.spawn #3932

Merged
merged 1 commit into from
Nov 19, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
feat: allow passing G_SPAWN stdio flags to awesome.spawn
Fixes: #3865
Currently works by allowing the exact strings "DEV_NULL" or "INHERIT" to
be passed to return_std*.

Signed-off-by: aarondill <[email protected]>
aarondill committed Nov 14, 2024
commit fa9b07a735eabb203c76ec09fc4f8cd697027721
84 changes: 75 additions & 9 deletions spawn.c
Original file line number Diff line number Diff line change
@@ -412,9 +412,21 @@ spawn_child_exited(pid_t pid, int status)
*
* @tparam string|table cmd The command to launch.
* @tparam[opt=true] boolean use_sn Use startup-notification?
* @tparam[opt=false] boolean stdin Return a fd for stdin?
* @tparam[opt=false] boolean stdout Return a fd for stdout?
* @tparam[opt=false] boolean stderr Return a fd for stderr?
* @tparam[opt="DEV_NULL"] boolean|string stdin Pass `true` to return a fd for
* stdin. Use `"DEV_NULL"` to redirect to /dev/null, or `"INHERIT"` to inherit
* the parent's stdin. Implementation note: Pre-2.74 glib doesn't support
* *explicit* `DEV_NULL`. When `DEV_NULL` is passed on glib <2.74, Awesome will
* use glib's default behaviour.
* @tparam[opt="INHERIT"] boolean|string stdout Pass `true` to return a fd for
* stdout. Use `"DEV_NULL"` to redirect to /dev/null, or `"INHERIT"` to
* inherit the parent's stdout. Implementation note: Pre-2.74 glib doesn't
* support *explicit* `INHERIT`. When `INHERIT` is passed on glib <2.74,
* Awesome will use glib's default behaviour.
* @tparam[opt="INHERIT"] boolean|string stderr Pass `true` to return a fd for
* stderr. Use `"DEV_NULL"` to redirect to /dev/null, or `"INHERIT"` to
* inherit the parent's stderr. Implementation note: Pre-2.74 glib doesn't
* support *explicit* `INHERIT`. When `INHERIT` is passed on glib <2.74,
* Awesome will use glib's default behaviour.
* @tparam[opt=nil] function exit_callback Function to call on process exit. The
* function arguments will be type of exit ("exit" or "signal") and the exit
* code / the signal number causing process termination.
@@ -441,12 +453,66 @@ luaA_spawn(lua_State *L)

if(lua_gettop(L) >= 2)
use_sn = luaA_checkboolean(L, 2);
if(lua_gettop(L) >= 3)
return_stdin = luaA_checkboolean(L, 3);
if(lua_gettop(L) >= 4)
return_stdout = luaA_checkboolean(L, 4);
if(lua_gettop(L) >= 5)
return_stderr = luaA_checkboolean(L, 5);
/* Valid values for return_std* are:
* true -> return a fd
* false -> keep glib's default behaviour
* "DEV_NULL" -> use direct output to /dev/null
* "INHERIT" -> use the same fd as the parent
*/
if(lua_gettop(L) >= 3) {
if (lua_isstring(L, 3)) {
const char *str = lua_tostring(L, 3);
if (a_strcmp(str, "DEV_NULL") == 0){
// This is the default behaviour. Compiles to a no-op before 2.74.
#if GLIB_CHECK_VERSION(2, 74, 0)
flags |= G_SPAWN_STDIN_FROM_DEV_NULL;
# endif
} else if (a_strcmp(str, "INHERIT") == 0)
flags |= G_SPAWN_CHILD_INHERITS_STDIN;
else
luaA_typerror(L, 3, "DEV_NULL or INHERIT");
} else if(lua_isboolean(L, 3)) {
return_stdin = lua_toboolean(L, 3);
} else {
luaA_typerror(L, 3, "boolean or string");
}
}
if(lua_gettop(L) >= 4) {
if (lua_isstring(L, 4)) {
const char *str = lua_tostring(L, 4);
if (a_strcmp(str, "DEV_NULL") == 0)
flags |= G_SPAWN_STDOUT_TO_DEV_NULL;
else if (a_strcmp(str, "INHERIT") == 0) {
// This is the default behaviour. Compiles to a no-op before 2.74.
#if GLIB_CHECK_VERSION(2, 74, 0)
flags |= G_SPAWN_CHILD_INHERITS_STDOUT;
# endif
} else
luaA_typerror(L, 4, "DEV_NULL or INHERIT");
} else if(lua_isboolean(L, 4)) {
return_stdout = lua_toboolean(L, 4);
} else {
luaA_typerror(L, 4, "boolean or string");
}
}
if(lua_gettop(L) >= 5) {
if (lua_isstring(L, 5)) {
const char *str = lua_tostring(L, 5);
if (a_strcmp(str, "DEV_NULL") == 0)
flags |= G_SPAWN_STDERR_TO_DEV_NULL;
else if (a_strcmp(str, "INHERIT") == 0) {
// This is the default behaviour. Compiles to a no-op before 2.74.
#if GLIB_CHECK_VERSION(2, 74, 0)
flags |= G_SPAWN_CHILD_INHERITS_STDERR;
# endif
} else
luaA_typerror(L, 5, "DEV_NULL or INHERIT");
} else if(lua_isboolean(L, 5)) {
return_stderr = lua_toboolean(L, 5);
} else {
luaA_typerror(L, 5, "boolean or string");
}
}
if (!lua_isnoneornil(L, 6))
{
luaA_checkfunction(L, 6);
67 changes: 67 additions & 0 deletions tests/test-spawn.lua
Original file line number Diff line number Diff line change
@@ -10,6 +10,7 @@ end

local spawns_done = 0
local async_spawns_done = 0
local io_spawns_done = 0
local exit_yay, exit_snd = nil, nil

-- * Using spawn with array is already covered by the test client.
@@ -161,6 +162,72 @@ local steps = {
return true
end
end,
-- Test inheriting stdio
function(count)
if count == 1 then
do -- Test that DEV_NULL works and doesn't return a fd
local read_line = false
local pid, _, _, stdout, stderr = awesome.spawn({ 'readlink', '/proc/self/fd/2' },
false, false, true, "DEV_NULL")
assert(type(pid) ~= "string", pid)
assert(stderr == nil)
spawn.read_lines(require("lgi").Gio.UnixInputStream.new(stdout, true),
function(line)
assert(not read_line)
read_line = true
assert(line == "/dev/null", line)
io_spawns_done = io_spawns_done + 1
end, nil, true)
end

do -- Test that INHERIT works and doesn't return a fd
-- Note: if this is /dev/null, this test is useless.
local test_stdin = require('lgi').GLib.file_read_link('/proc/self/fd/0')

local read_line = false
local pid, _, stdin, stdout = awesome.spawn({ 'readlink', '/proc/self/fd/0' },
false, "INHERIT", true, false)
assert(type(pid) ~= "string", pid)
assert(stdin == nil)
spawn.read_lines(require("lgi").Gio.UnixInputStream.new(stdout, true),
function(line)
assert(not read_line)
read_line = true
assert(line == test_stdin, line)
io_spawns_done = io_spawns_done + 1
end, nil, true)
end

do -- Test that false doesn't return a pointer (behavior is untested - GLib defaults)
local pid, _, stdin, stdout, stderr = awesome.spawn({"true"},
false, false, false, false)
assert(type(pid) ~= "string", pid)
assert(stdin == nil)
assert(stdout == nil)
assert(stderr == nil)
end

do -- Test that true returns a pipe
local read_line = false
local pid, _, stdin, stdout, stderr = awesome.spawn({ 'readlink', '/proc/self/fd/0' },
false, true, true, true)
assert(type(pid) ~= "string", pid)
assert(stdin ~= nil)
assert(stdout ~= nil)
assert(stderr ~= nil)
spawn.read_lines(require("lgi").Gio.UnixInputStream.new(stdout, true),
function(line)
assert(not read_line)
read_line = true
assert(line:find("^pipe:%[[0-9]+%]$"), line)
io_spawns_done = io_spawns_done + 1
end, nil, true)
end
end
if io_spawns_done == 3 then
return true
end
end,
-- Test spawn_once
function()
if #client.get() ~= 1 then return end