aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPřemysl Eric Janouch <p@janouch.name>2025-01-03 02:00:22 +0100
committerPřemysl Eric Janouch <p@janouch.name>2025-01-03 02:00:22 +0100
commitf7c177e23608f7f583f513428748c66ce6674890 (patch)
tree22721be522e2c743a1e5363deb6d554b4ac3eb8c
parent97faae7abe0ecf9ae2e9e80eba0234b4b7abe158 (diff)
downloadliberty-f7c177e23608f7f583f513428748c66ce6674890.tar.gz
liberty-f7c177e23608f7f583f513428748c66ce6674890.tar.xz
liberty-f7c177e23608f7f583f513428748c66ce6674890.zip
WIP
-rw-r--r--tools/wdye/wdye.c126
1 files changed, 88 insertions, 38 deletions
diff --git a/tools/wdye/wdye.c b/tools/wdye/wdye.c
index 3b0f2b5..cb3c97c 100644
--- a/tools/wdye/wdye.c
+++ b/tools/wdye/wdye.c
@@ -115,23 +115,52 @@ static luaL_Reg xlua_process_table[] =
{ NULL, NULL }
};
+// --- Terminal ----------------------------------------------------------------
+
+// TODO(p): Probably create an object to interact with tinfo.
+
// --- Library -----------------------------------------------------------------
-static struct str_map
-environ_map_make (void)
+struct xlua_spawn_context
+{
+ struct str_map env; ///< Subprocess environment map
+ struct strv envv; ///< Subprocess environment vector
+ struct strv argv; ///< Subprocess argument vector
+
+ pid_t child; ///< Child process ID
+};
+
+static struct xlua_spawn_context
+xlua_spawn_context_make (void)
{
- struct str_map env = str_map_make (free);
+ struct xlua_spawn_context self = {};
+ self.env = str_map_make (free);
for (char **p = environ; *p; p++)
{
const char *equals = strchr (*p, '=');
- if (equals)
- {
- char *key = xstrndup (*p, equals - *p);
- str_map_set (&env, key, xstrdup (equals + 1));
- free (key);
- }
+ if (!equals)
+ continue;
+
+ char *key = xstrndup (*p, equals - *p);
+ str_map_set (&self.env, key, xstrdup (equals + 1));
+ free (key);
}
- return env;
+ self.envv = strv_make ();
+ self.argv = strv_make ();
+
+ self.child = -1;
+ return self;
+}
+
+static void
+xlua_spawn_context_free (struct xlua_spawn_context *self)
+{
+ str_map_free (&self->env);
+ strv_free (&self->envv);
+ strv_free (&self->argv);
+
+ if (self->child != -1)
+ kill (self->child, SIGKILL);
}
// -0, +0, e
@@ -151,44 +180,40 @@ environ_map_update (struct str_map *env, lua_State *L)
// The environment will get pseudo-randomly reordered,
// which is fine by POSIX.
-static struct strv
-environ_map_serialize (struct str_map *env)
+static void
+environ_map_serialize (struct str_map *env, struct strv *envv)
{
- struct strv envv = strv_make ();
struct str_map_iter iter = str_map_iter_make (env);
const char *value;
while ((value = str_map_iter_next (&iter)))
- strv_append_owned (&envv,
+ strv_append_owned (envv,
xstrdup_printf ("%s=%s", iter.link->key, value));
- str_map_free (env);
- return envv;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
static int
-xlua_spawn (lua_State *L)
+xlua_spawn_protected (lua_State *L)
{
- luaL_checktype (L, 1, LUA_TTABLE);
+ struct xlua_spawn_context *ctx = lua_touserdata (L, 1);
+ // Step 1: Prepare a terminal object.
(void) xlua_getfield (L, 1, "term", LUA_TSTRING, true);
const char *term = lua_tostring (L, -1);
// TODO(p): Process the terminal name,
- // possibly by creating another indexable Lua object/table.
+ // possibly by creating another indexable Lua object/table.
+ // - Remember to use a default when `term == NULL`.
lua_pop (L, 1);
- struct str_map env = environ_map_make ();
+ // Step 2: Prepare process environment.
if (xlua_getfield (L, 1, "environ", LUA_TTABLE, true))
{
- environ_map_update (&env, L);
+ environ_map_update (&ctx->env, L);
lua_pop (L, 1);
}
+ environ_map_serialize (&ctx->env, &ctx->envv);
- // TODO(p): Make sure these do not get leaked.
- // - Probably a protected call.
- struct strv envv = environ_map_serialize (&env);
- struct strv args = strv_make ();
-
+ // Step 3: Prepare process command line.
size_t argc = lua_rawlen (L, 1);
for (size_t i = 1; i <= argc; i++)
{
@@ -198,36 +223,60 @@ xlua_spawn (lua_State *L)
if (!arg)
return luaL_error (L, "spawn arguments must be strings");
- strv_append (&args, arg);
+ strv_append (&ctx->argv, arg);
lua_pop (L, 1);
}
- if (args.len < 1)
+ if (ctx->argv.len < 1)
return luaL_error (L, "missing argument");
- // Keeping the process group for simplicity right now.
+ // Step 4: Spawn the process.
+ // Keeping the same process group for simplicity right now.
+
// TODO(p): Allocate a pty, pipes in and out.
- pid_t child = fork ();
- switch (child)
+ // See Advanced Programming in the UNIX® Environment.
+ switch ((ctx->child = fork ()))
{
case -1:
// TODO(p): Consider luaL_error().
print_error ("failed to spawn %s: %s",
- args.vector[0], strerror (errno));
+ ctx->argv.vector[0], strerror (errno));
break;
case 0:
- execve (args.vector[0], args.vector, envv.vector);
+ execve (ctx->argv.vector[0], ctx->argv.vector, ctx->envv.vector);
print_error ("failed to spawn %s: %s",
- args.vector[0], strerror (errno));
+ ctx->argv.vector[0], strerror (errno));
_exit (EXIT_FAILURE);
default:
break;
}
- strv_free (&args);
- strv_free (&envv);
-
+ // Step 5: Return a process object.
struct process *process = xlua_process_new (L);
- process->pid = child;
+ process->pid = ctx->child;
+
+ ctx->child = -1;
+ return 1;
+}
+
+static int
+xlua_spawn (lua_State *L)
+{
+ luaL_checktype (L, 1, LUA_TTABLE);
+
+ lua_pushcfunction (L, xlua_error_handler);
+ lua_pushcfunction (L, xlua_spawn_protected);
+
+ // There are way too many opportunities for Lua to throw,
+ // so maintain a context to clean up in one go.
+ struct xlua_spawn_context ctx = xlua_spawn_context_make ();
+ lua_pushlightuserdata (L, &ctx);
+ int result = lua_pcall (L, 1, 1, -3);
+ xlua_spawn_context_free (&ctx);
+ if (result)
+ lua_error (L);
+
+ // Remove the error handler ("good programming practice").
+ lua_remove (L, -2);
return 1;
}
@@ -298,6 +347,7 @@ main (int argc, char *argv[])
lua_pop (g.L, 1);
}
+ // TODO(p): Do we need to pop the error handler?
lua_close (g.L);
return 0;
}