diff options
| -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;  } | 
