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

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
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
84 changes: 75 additions & 9 deletions spawn.c
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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);
Expand Down
67 changes: 67 additions & 0 deletions tests/test-spawn.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand Down
Loading