diff options
-rw-r--r-- | catgirl.1 | 9 | ||||
-rw-r--r-- | chat.c | 41 | ||||
-rw-r--r-- | chat.h | 3 | ||||
-rw-r--r-- | command.c | 19 |
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 }, |