summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--catgirl.19
-rw-r--r--chat.c41
-rw-r--r--chat.h3
-rw-r--r--command.c19
4 files changed, 63 insertions, 9 deletions
diff --git a/catgirl.1 b/catgirl.1
index ca3976b..5e5205b 100644
--- a/catgirl.1
+++ b/catgirl.1
@@ -93,6 +93,7 @@ The default is the first available of
 Disable the
 .Ic /copy ,
 .Ic /debug ,
+.Ic /exec ,
 .Ic /join ,
 .Ic /msg ,
 .Ic /open ,
@@ -280,6 +281,14 @@ or matching
 Toggle logging in the
 .Sy <debug>
 window.
+.It Ic /exec Ar command
+Run
+.Ar command
+with
+.Ev SHELL
+and interpret its output
+as input to the current window,
+including as commands.
 .It Ic /help Op Ar search
 View this manual.
 Type
diff --git a/chat.c b/chat.c
index 7bb0538..fd1d33e 100644
--- a/chat.c
+++ b/chat.c
@@ -79,17 +79,31 @@ static void exitSave(void) {
 uint32_t hashInit;
 
 int utilPipe[2] = { -1, -1 };
+int execPipe[2] = { -1, -1 };
 
 static void utilRead(void) {
 	char buf[1024];
 	ssize_t len = read(utilPipe[0], buf, sizeof(buf) - 1);
 	if (len < 0) err(EX_IOERR, "read");
 	if (!len) return;
-	buf[len - 1] = '\0';
+	buf[len] = '\0';
 	char *ptr = buf;
 	while (ptr) {
 		char *line = strsep(&ptr, "\n");
-		uiFormat(Network, Warm, NULL, "%s", line);
+		if (line[0]) uiFormat(Network, Warm, NULL, "%s", line);
+	}
+}
+
+static void execRead(void) {
+	char buf[1024];
+	ssize_t len = read(execPipe[0], buf, sizeof(buf) - 1);
+	if (len < 0) err(EX_IOERR, "read");
+	if (!len) return;
+	buf[len] = '\0';
+	char *ptr = buf;
+	while (ptr) {
+		char *line = strsep(&ptr, "\n");
+		if (line[0]) command(execID, line);
 	}
 }
 
@@ -221,25 +235,34 @@ int main(int argc, char *argv[]) {
 	signal(SIGCHLD, signalHandler);
 	sig_t cursesWinch = signal(SIGWINCH, signalHandler);
 
-	int error = pipe(utilPipe);
-	if (error) err(EX_OSERR, "pipe");
-
 	fcntl(irc, F_SETFD, FD_CLOEXEC);
-	fcntl(utilPipe[0], F_SETFD, FD_CLOEXEC);
-	fcntl(utilPipe[1], F_SETFD, FD_CLOEXEC);
+	if (!self.restricted) {
+		int error = pipe(utilPipe);
+		if (error) err(EX_OSERR, "pipe");
+
+		error = pipe(execPipe);
+		if (error) err(EX_OSERR, "pipe");
+
+		fcntl(utilPipe[0], F_SETFD, FD_CLOEXEC);
+		fcntl(utilPipe[1], F_SETFD, FD_CLOEXEC);
+		fcntl(execPipe[0], F_SETFD, FD_CLOEXEC);
+		fcntl(execPipe[1], F_SETFD, FD_CLOEXEC);
+	}
 
-	struct pollfd fds[3] = {
+	struct pollfd fds[] = {
 		{ .events = POLLIN, .fd = STDIN_FILENO },
 		{ .events = POLLIN, .fd = irc },
 		{ .events = POLLIN, .fd = utilPipe[0] },
+		{ .events = POLLIN, .fd = execPipe[0] },
 	};
 	while (!self.quit) {
-		int nfds = poll(fds, ARRAY_LEN(fds), -1);
+		int nfds = poll(fds, (self.restricted ? 2 : ARRAY_LEN(fds)), -1);
 		if (nfds < 0 && errno != EINTR) err(EX_IOERR, "poll");
 		if (nfds > 0) {
 			if (fds[0].revents) uiRead();
 			if (fds[1].revents) ircRecv();
 			if (fds[2].revents) utilRead();
+			if (fds[3].revents) execRead();
 		}
 
 		if (signals[SIGHUP]) self.quit = "zzz";
diff --git a/chat.h b/chat.h
index b852c44..afa8ef9 100644
--- a/chat.h
+++ b/chat.h
@@ -138,6 +138,9 @@ extern struct Replies {
 	size_t whois;
 } replies;
 
+size_t execID;
+int execPipe[2];
+
 void handle(struct Message msg);
 void command(size_t id, char *input);
 const char *commandIsPrivmsg(size_t id, const char *input);
diff --git a/command.c b/command.c
index ffbea9f..e0a2e2b 100644
--- a/command.c
+++ b/command.c
@@ -195,6 +195,24 @@ static void commandCopy(size_t id, char *params) {
 	urlCopyMatch(id, params);
 }
 
+static void commandExec(size_t id, char *params) {
+	execID = id;
+
+	pid_t pid = fork();
+	if (pid < 0) err(EX_OSERR, "fork");
+	if (pid) return;
+
+	const char *shell = getenv("SHELL");
+	if (!shell) shell = "/bin/sh";
+
+	close(STDIN_FILENO);
+	dup2(execPipe[1], STDOUT_FILENO);
+	dup2(utilPipe[1], STDERR_FILENO);
+	execlp(shell, shell, "-c", params, NULL);
+	warn("%s", shell);
+	_exit(EX_UNAVAILABLE);
+}
+
 static void commandHelp(size_t id, char *params) {
 	(void)id;
 	uiHide();
@@ -220,6 +238,7 @@ static const struct Handler {
 	{ "/close", .fn = commandClose },
 	{ "/copy", .fn = commandCopy, .restricted = true },
 	{ "/debug", .fn = commandDebug, .restricted = true },
+	{ "/exec", .fn = commandExec, .restricted = true },
 	{ "/help", .fn = commandHelp },
 	{ "/join", .fn = commandJoin, .restricted = true },
 	{ "/list", .fn = commandList },
mit/home/.xsession?id=1577ad23c4cd5417fbddbec457041823168ddbda&follow=1'>Add initial OpenBSD X configurationJune McEnroe 2021-02-06Add xterm output to schemeJune McEnroe