aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPřemysl Eric Janouch <p@janouch.name>2025-01-03 07:50:50 +0100
committerPřemysl Eric Janouch <p@janouch.name>2025-01-03 08:04:50 +0100
commit66733cef1cf1af2f2610f1cb816a9e39caf01202 (patch)
treeed569c58a566f5d9e88672e1b1473c4ced9d2dc0
parentf7c177e23608f7f583f513428748c66ce6674890 (diff)
downloadliberty-66733cef1cf1af2f2610f1cb816a9e39caf01202.tar.gz
liberty-66733cef1cf1af2f2610f1cb816a9e39caf01202.tar.xz
liberty-66733cef1cf1af2f2610f1cb816a9e39caf01202.zip
WIP
-rw-r--r--tools/wdye/wdye.c177
1 files changed, 148 insertions, 29 deletions
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 <lua.h>
#include <lualib.h>
#include <lauxlib.h>
+#include <termios.h>
+#if defined SOLARIS
+#include <stropts.h>
+#endif
+
#include <term.h>
+// --- 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;
}