From 66733cef1cf1af2f2610f1cb816a9e39caf01202 Mon Sep 17 00:00:00 2001 From: Přemysl Eric Janouch Date: Fri, 3 Jan 2025 07:50:50 +0100 Subject: WIP --- tools/wdye/wdye.c | 177 +++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 148 insertions(+), 29 deletions(-) (limited to 'tools/wdye') diff --git a/tools/wdye/wdye.c b/tools/wdye/wdye.c index cb3c97c..47e485b 100644 --- a/tools/wdye/wdye.c +++ b/tools/wdye/wdye.c @@ -16,18 +16,141 @@ * */ -#define LIBERTY_WANT_POLLER - #define PROGRAM_NAME "wdye" #define PROGRAM_VERSION "1" + +#define LIBERTY_WANT_POLLER #include "../../liberty.c" #include #include #include +#include +#if defined SOLARIS +#include +#endif + #include +// --- Pseudoterminal ---------------------------------------------------------- +// This is largely taken from Advanced Programming in the UNIX® Environment, +// just without a bunch of bugs. + +static int +ptym_open (char **pts_name) +{ + int fdm = -1, err = 0; + if ((fdm = posix_openpt (O_RDWR | O_NOCTTY)) < 0) + return -1; + if (grantpt (fdm) < 0 + || unlockpt (fdm) < 0) + goto errout; + + char *ptr = NULL; + if ((ptr = ptsname (fdm)) == NULL) + goto errout; + + cstr_set (pts_name, xstrdup (ptr)); + return fdm; + +errout: + err = errno; + xclose (fdm); + errno = err; + return -1; +} + +static int +ptys_open (const char *pts_name) +{ + int fds = -1; +#if defined SOLARIS + int err = 0, setup = 0; +#endif + if ((fds = open (pts_name, O_RDWR)) < 0) + return -1; +#if defined SOLARIS + if ((setup = ioctl (fds, I_FIND, "ldterm")) < 0) + goto errout; + if (setup == 0) + { + if (ioctl (fds, I_PUSH, "ptem") < 0 + || ioctl (fds, I_PUSH, "ldterm") < 0) + goto errout; + + if (ioctl (fds, I_PUSH, "ttcompat") < 0) + { +errout: + err = errno; + xclose (fds); + errno = err; + return -1; + } + } +#endif + return fds; +} + +static pid_t +pty_fork (int *ptrfdm, char **slave_name, + const struct termios *slave_termios, const struct winsize *slave_winsize, + struct error **e) +{ + int fdm = -1, fds = -1; + + char *pts_name = NULL; + if ((fdm = ptym_open (&pts_name)) < 0) + { + error_set (e, "can’t open master pty: %s", strerror (errno)); + return -1; + } + if (slave_name != NULL) + cstr_set (slave_name, xstrdup (pts_name)); + + pid_t pid = fork (); + if (pid < 0) + { + error_set (e, "fork: %s", strerror (errno)); + xclose (fdm); + } + else if (pid != 0) + *ptrfdm = fdm; + else + { + if (setsid () < 0) + exit_fatal ("setsid: %s", strerror (errno)); + if ((fds = ptys_open (pts_name)) < 0) + exit_fatal ("can’t open slave pty: %s", strerror (errno)); + xclose (fdm); + +#if defined BSD + if (ioctl (fds, TIOCSCTTY, (char *) 0) < 0) + exit_fatal ("TIOCSCTTY: %s", strerror (errno)); +#endif + + if (slave_termios != NULL + && tcsetattr (fds, TCSANOW, slave_termios) < 0) + exit_fatal ("tcsetattr error on slave pty: %s", strerror (errno)); + if (slave_winsize != NULL + && ioctl (fds, TIOCSWINSZ, slave_winsize) < 0) + exit_fatal ("TIOCSWINSZ error on slave pty: %s", strerror (errno)); + + if (dup2 (fds, STDIN_FILENO) != STDIN_FILENO) + exit_fatal ("dup2 error to stdin"); + if (dup2 (fds, STDOUT_FILENO) != STDOUT_FILENO) + exit_fatal ("dup2 error to stdout"); + if (dup2 (fds, STDERR_FILENO) != STDERR_FILENO) + exit_fatal ("dup2 error to stderr"); + if (fds != STDIN_FILENO && fds != STDOUT_FILENO && fds != STDERR_FILENO) + xclose (fds); + } + free (pts_name); + return pid; +} + +// --- Global state ------------------------------------------------------------ + static struct { lua_State *L; ///< Lua state @@ -65,8 +188,8 @@ xlua_getfield (lua_State *L, int idx, const char *name, struct process { + int terminal_fd; ///< Process stdin/stdout/stderr pid_t pid; ///< Process ID - // TODO(p): File descriptors. // TODO(p): Either `TERMINAL *` or read-out values. }; @@ -83,7 +206,8 @@ static int xlua_process_gc (lua_State *L) { struct process *self = luaL_checkudata (L, 1, XLUA_PROCESS_METATABLE); - // TODO(p): Clean up file descriptors. + if (self->terminal_fd != -1) + xclose (self->terminal_fd); if (self->pid != -1) kill (self->pid, SIGKILL); return 0; @@ -93,7 +217,11 @@ static int xlua_process_expect (lua_State *L) { struct process *self = luaL_checkudata (L, 1, XLUA_PROCESS_METATABLE); - // TODO(p) + // TODO(p): Arguments. + // TODO(p): Keep reading input into a buffer, + // until there's a match. + // - It seems we need to keep the buffer within the process object, + // because we can read too much. return 0; } @@ -127,7 +255,7 @@ struct xlua_spawn_context struct strv envv; ///< Subprocess environment vector struct strv argv; ///< Subprocess argument vector - pid_t child; ///< Child process ID + struct error *error; ///< Error }; static struct xlua_spawn_context @@ -147,8 +275,6 @@ xlua_spawn_context_make (void) } self.envv = strv_make (); self.argv = strv_make (); - - self.child = -1; return self; } @@ -159,8 +285,8 @@ xlua_spawn_context_free (struct xlua_spawn_context *self) strv_free (&self->envv); strv_free (&self->argv); - if (self->child != -1) - kill (self->child, SIGKILL); + if (self->error) + error_free (self->error); } // -0, +0, e @@ -229,32 +355,25 @@ xlua_spawn_protected (lua_State *L) if (ctx->argv.len < 1) return luaL_error (L, "missing argument"); - // Step 4: Spawn the process. - // Keeping the same process group for simplicity right now. + // Step 4: Create a process object. + // This will get garbage collected as appropriate on failure. + struct process *process = xlua_process_new (L); - // TODO(p): Allocate a pty, pipes in and out. - // See Advanced Programming in the UNIX® Environment. - switch ((ctx->child = fork ())) + // Step 5: Spawn the process, which gets a new process group. + process->pid = + pty_fork (&process->terminal_fd, NULL, NULL, NULL, &ctx->error); + if (process->pid < 0) + { + return luaL_error (L, "failed to spawn %s: %s", + ctx->argv.vector[0], ctx->error->message); + } + if (!process->pid) { - case -1: - // TODO(p): Consider luaL_error(). - print_error ("failed to spawn %s: %s", - ctx->argv.vector[0], strerror (errno)); - break; - case 0: execve (ctx->argv.vector[0], ctx->argv.vector, ctx->envv.vector); print_error ("failed to spawn %s: %s", ctx->argv.vector[0], strerror (errno)); _exit (EXIT_FAILURE); - default: - break; } - - // Step 5: Return a process object. - struct process *process = xlua_process_new (L); - process->pid = ctx->child; - - ctx->child = -1; return 1; } -- cgit v1.2.3-70-g09d2