diff options
author | Přemysl Eric Janouch <p@janouch.name> | 2025-01-03 02:00:22 +0100 |
---|---|---|
committer | Přemysl Eric Janouch <p@janouch.name> | 2025-01-03 02:00:22 +0100 |
commit | f7c177e23608f7f583f513428748c66ce6674890 (patch) | |
tree | 22721be522e2c743a1e5363deb6d554b4ac3eb8c | |
parent | 97faae7abe0ecf9ae2e9e80eba0234b4b7abe158 (diff) | |
download | liberty-f7c177e23608f7f583f513428748c66ce6674890.tar.gz liberty-f7c177e23608f7f583f513428748c66ce6674890.tar.xz liberty-f7c177e23608f7f583f513428748c66ce6674890.zip |
WIP
-rw-r--r-- | tools/wdye/wdye.c | 126 |
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; } |