From a2e0b6901cb4eef7ad1d78bee0d70c577c61e0f5 Mon Sep 17 00:00:00 2001 From: Curtis McEnroe Date: Sun, 25 Feb 2018 19:16:56 -0500 Subject: Rewrite everything --- .gitignore | 3 +- Makefile | 10 +++- broadcast.c | 132 ------------------------------------------ ingest.c | 87 ++++++++++++++++++---------- ptee.c | 155 ++++++++++++++++++++++++++++++++++++++++++++++++++ setup.c | 12 ++-- ssh-command.sh | 10 ++-- stream.h | 17 ------ view.c | 177 ++++++++++++++++++++++++++------------------------------- winch.c | 57 +++++++++++++++++++ 10 files changed, 373 insertions(+), 287 deletions(-) delete mode 100644 broadcast.c create mode 100644 ptee.c delete mode 100644 stream.h create mode 100644 winch.c diff --git a/.gitignore b/.gitignore index bba9e65..b1b280d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ -broadcast +*.o chroot.tar ingest +ptee root setup ssh-command diff --git a/Makefile b/Makefile index 57af159..ecc777e 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -BINS = broadcast ingest setup ssh-command view +BINS = ingest ptee setup ssh-command view USER = stream CFLAGS += -Wall -Wextra -Wpedantic @@ -6,6 +6,12 @@ LDLIBS = -lutil all: tags $(BINS) +ingest: ingest.o winch.o + +ptee: ptee.o winch.o + +view: view.o winch.o + tags: *.c ctags -w *.c @@ -33,6 +39,6 @@ chroot.tar: $(BINS) tar -c -f chroot.tar -C root bin home lib libexec usr clean: - rm -f tags $(BINS) chroot.tar + rm -f tags *.o $(BINS) chroot.tar .PHONY: all clean diff --git a/broadcast.c b/broadcast.c deleted file mode 100644 index 280a552..0000000 --- a/broadcast.c +++ /dev/null @@ -1,132 +0,0 @@ -/* Copyright (c) 2018, Curtis McEnroe - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#if defined __FreeBSD__ -#include -#elif defined __linux__ -#include -#else -#include -#endif - -#include "stream.h" - -static struct termios saveTerm; -static void restoreTerm(void) { - tcsetattr(STDERR_FILENO, TCSADRAIN, &saveTerm); -} - -int main(int argc, char *argv[]) { - int error; - - if (argc > 1) { - argv++; - } else { - uid_t uid = getuid(); - struct passwd *user = getpwuid(uid); - if (!user) err(EX_OSFILE, "/etc/passwd"); - argv[0] = user->pw_shell; - } - - int input = STDIN_FILENO; - int local = STDERR_FILENO; - int remote = STDOUT_FILENO; - - if (isatty(remote)) { - errx(EX_USAGE, "stdout is not redirected"); - } - - error = tcgetattr(local, &saveTerm); - if (error) err(EX_IOERR, "tcgetattr"); - atexit(restoreTerm); - - struct termios raw; - cfmakeraw(&raw); - error = tcsetattr(local, TCSADRAIN, &raw); - if (error) err(EX_IOERR, "tcsetattr"); - - struct winsize window; - error = ioctl(local, TIOCGWINSZ, &window); - if (error) err(EX_IOERR, "ioctl(%d, TIOCGWINSZ, ...)", local); - - ssize_t size = write(remote, &window, sizeof(window)); - if (size < 0) err(EX_IOERR, "write(%d)", remote); - if ((size_t)size < sizeof(window)) errx(EX_IOERR, "short write(%d)", remote); - - int pty; - pid_t pid = forkpty(&pty, NULL, NULL, &window); - if (pid < 0) err(EX_OSERR, "forkpty"); - - if (!pid) { - execvp(*argv, argv); - err(EX_USAGE, "%s", *argv); - } - - char buf[4096]; - ssize_t totalSize = 0; - struct pollfd fds[2] = { - { .fd = input, .events = POLLIN }, - { .fd = pty, .events = POLLIN }, - }; - while (0 < poll(fds, 2, -1)) { - if (fds[0].revents) { - ssize_t readSize = read(input, buf, sizeof(buf)); - if (readSize < 0) err(EX_IOERR, "read(%d)", input); - - ssize_t writeSize = write(pty, buf, readSize); - if (writeSize < 0) err(EX_IOERR, "write(%d)", pty); - if (writeSize < readSize) errx(EX_IOERR, "short write(%d)", pty); - } - - if (fds[1].revents) { - ssize_t readSize = read(pty, buf, sizeof(buf)); - if (readSize < 0) err(EX_IOERR, "read(%d)", pty); - - ssize_t writeSize = write(local, buf, readSize); - if (writeSize < 0) err(EX_IOERR, "write(%d)", local); - if (writeSize < readSize) errx(EX_IOERR, "short write(%d)", local); - - writeSize = write(remote, buf, readSize); - if (writeSize < 0) err(EX_IOERR, "write(%d)", remote); - if (writeSize < readSize) errx(EX_IOERR, "short write(%d)", remote); - - totalSize += readSize; - if (totalSize >= STREAM_SIZE) { - buf[0] = CTRL('L'); - writeSize = write(pty, buf, 1); - if (writeSize < 0) err(EX_IOERR, "write(%d)", pty); - totalSize = 0; - } - } - - int status; - pid_t dead = waitpid(pid, &status, WNOHANG); - if (dead < 0) err(EX_OSERR, "waitpid"); - if (dead) return WIFEXITED(status) ? WEXITSTATUS(status) : EX_SOFTWARE; - } - err(EX_IOERR, "poll"); -} diff --git a/ingest.c b/ingest.c index 79b0e38..52a50af 100644 --- a/ingest.c +++ b/ingest.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2018, Curtis McEnroe +/* Copyright (C) 2018 Causal Agent June * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by @@ -15,53 +15,82 @@ */ #include +#include #include -#include -#include +#include +#include #include #include #include -#include "stream.h" +extern int winch(void); -int main(int argc, char *argv[]) { - if (argc < 2) return EX_USAGE; - const char *path = argv[1]; +static struct { + int winch; + int local; + int remote; +} fd = { -1, -1, STDIN_FILENO }; - int remote = STDIN_FILENO; +int main(int argc, char *argv[]) { + if (argc < 2) errx(EX_USAGE, "missing private id"); + if (!isatty(STDERR_FILENO)) errx(EX_USAGE, "no terminal (use ssh -t)"); - int stream = open(path, O_WRONLY); - if (stream < 0) err(EX_NOINPUT, "%s", path); + const char *path = argv[1]; + fd.local = open(path, O_WRONLY); + if (fd.local < 0) err(EX_NOINPUT, "%s", path); struct winsize window; - ssize_t size = read(remote, &window, sizeof(window)); - if (size < 0) err(EX_IOERR, "read(%d)", remote); - if ((size_t)size < sizeof(window)) errx(EX_IOERR, "short read(%d)", remote); + int error = ioctl(STDERR_FILENO, TIOCGWINSZ, &window); + if (error) err(EX_IOERR, "TIOCGWINSZ"); + fd.winch = winch(); for (;;) { - int error = ftruncate(stream, 0); + int error = ftruncate(fd.local, 0); if (error) err(EX_IOERR, "%s", path); - off_t offset = lseek(stream, 0, SEEK_SET); - if (offset < 0) err(EX_IOERR, "%s", path); + off_t off = lseek(fd.local, 0, SEEK_SET); + if (off < 0) err(EX_IOERR, "%s", path); - ssize_t size = write(stream, &window, sizeof(window)); - if (size < 0) err(EX_IOERR, "write(%d)", stream); - if ((size_t)size < sizeof(window)) errx(EX_IOERR, "short write(%d)", stream); + ssize_t size = write(fd.local, &window, sizeof(window)); + if (size < 0) err(EX_IOERR, "write(%d)", fd.local); + if ((size_t)size < sizeof(window)) { + errx(EX_IOERR, "short write(%d)", fd.local); + } char buf[4096]; - ssize_t totalSize = 0; - while (totalSize < STREAM_SIZE) { - ssize_t readSize = read(remote, buf, sizeof(buf)); - if (readSize < 0) err(EX_IOERR, "read(%d)", remote); - if (!readSize) return EX_OK; + struct pollfd fds[2] = { + { .fd = fd.remote, .events = POLLIN }, + { .fd = fd.winch, .events = POLLIN }, + }; + for (ssize_t totalSize = 0; totalSize < 1024 * 1024;) { + int nfds = poll(fds, 2, -1); + if (nfds < 0) { + if (errno == EINTR) continue; + err(EX_IOERR, "poll"); + } + + if (fds[0].revents & POLLIN) { + ssize_t readSize = read(fd.remote, buf, sizeof(buf)); + if (readSize < 0) err(EX_IOERR, "read(%d)", fd.remote); + if (!readSize) return EX_OK; + + ssize_t writeSize = write(fd.local, buf, readSize); + if (writeSize < 0) err(EX_IOERR, "write(%d)", fd.local); + if (writeSize < readSize) errx(EX_IOERR, "short write(%d)", fd.local); + + totalSize += readSize; + } - ssize_t writeSize = write(stream, buf, readSize); - if (writeSize < 0) err(EX_IOERR, "write(%d)", stream); - if (writeSize < readSize) errx(EX_IOERR, "short write(%d)", stream); + if (fds[0].revents & POLLHUP) return EX_OK; - totalSize += readSize; + if (fds[1].revents & POLLIN) { + ssize_t readSize = read(fd.winch, &window, sizeof(window)); + if (readSize < 0) err(EX_IOERR, "read(%d)", fd.winch); + if ((size_t)readSize < sizeof(window)) { + errx(EX_IOERR, "short read(%d)", fd.winch); + } + break; + } } - sleep(1); } } diff --git a/ptee.c b/ptee.c new file mode 100644 index 0000000..0c93ab0 --- /dev/null +++ b/ptee.c @@ -0,0 +1,155 @@ +/* Copyright (C) 2018 Causal Agent June + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined __FreeBSD__ +#include +#elif defined __linux__ +#include +#else +#include +#endif + +extern int winch(void); + +static struct { + int pty; + int winch; + int input; + int local; + int remote; +} fd = { -1, -1, STDIN_FILENO, STDERR_FILENO, STDOUT_FILENO }; + +static struct termios saveTerm; +static void restoreTerm(void) { + tcsetattr(fd.local, TCSADRAIN, &saveTerm); +} + +int main(int argc, char *argv[]) { + int error; + + if (isatty(fd.remote)) { + errx(EX_USAGE, "stdout is not redirected"); + } + + if (argc > 1) { + argv++; + } else { + uid_t uid = getuid(); + struct passwd *user = getpwuid(uid); + if (!user) err(EX_OSFILE, "/etc/passwd"); + argv[0] = user->pw_shell; + } + + error = tcgetattr(fd.local, &saveTerm); + if (error) err(EX_IOERR, "tcgetattr"); + atexit(restoreTerm); + + struct termios raw; + cfmakeraw(&raw); + error = tcsetattr(fd.local, TCSADRAIN, &raw); + if (error) err(EX_IOERR, "tcsetattr"); + + struct winsize window; + error = ioctl(fd.local, TIOCGWINSZ, &window); + if (error) err(EX_IOERR, "TIOCGWINSZ"); + fd.winch = winch(); + + pid_t pid = forkpty(&fd.pty, NULL, NULL, &window); + if (pid < 0) err(EX_OSERR, "forkpty"); + + if (!pid) { + execvp(*argv, argv); + err(EX_NOINPUT, "%s", *argv); + } + + char buf[4096]; + ssize_t totalSize = 0; + struct pollfd fds[3] = { + { .fd = fd.input, .events = POLLIN }, + { .fd = fd.pty, .events = POLLIN }, + { .fd = fd.winch, .events = POLLIN }, + }; + for (;;) { + int nfds = poll(fds, 3, -1); + if (nfds < 0) { + if (errno == EINTR) continue; + err(EX_IOERR, "poll"); + } + + if (fds[0].revents == POLLIN) { + ssize_t readSize = read(fd.input, buf, sizeof(buf)); + if (readSize < 0) err(EX_IOERR, "read(%d)", fd.input); + + ssize_t writeSize = write(fd.pty, buf, readSize); + if (writeSize < 0) err(EX_IOERR, "write(%d)", fd.pty); + if (writeSize < readSize) errx(EX_IOERR, "short write(%d)", fd.pty); + } + + if (fds[1].revents == POLLIN) { + ssize_t readSize = read(fd.pty, buf, sizeof(buf)); + if (readSize < 0) err(EX_IOERR, "read(%d)", fd.pty); + + ssize_t writeSize = write(fd.local, buf, readSize); + if (writeSize < 0) err(EX_IOERR, "write(%d)", fd.local); + if (writeSize < readSize) err(EX_IOERR, "short write(%d)", fd.local); + + writeSize = write(fd.remote, buf, readSize); + if (writeSize < 0) err(EX_IOERR, "write(%d)", fd.remote); + if (writeSize < readSize) err(EX_IOERR, "short write(%d)", fd.remote); + + if ((totalSize += readSize) >= 1024 * 1024) { + struct winsize original = window; + window.ws_row = 1; + window.ws_col = 1; + + int error = ioctl(fd.pty, TIOCSWINSZ, &window); + if (error) err(EX_IOERR, "TIOCSWINSZ"); + + window = original; + error = ioctl(fd.pty, TIOCSWINSZ, &window); + if (error) err(EX_IOERR, "TIOCWINSZ"); + + totalSize = 0; + } + } + + if (fds[2].revents == POLLIN) { + ssize_t readSize = read(fd.winch, &window, sizeof(window)); + if (readSize < 0) err(EX_IOERR, "read(%d)", fd.winch); + if ((size_t)readSize < sizeof(window)) { + errx(EX_IOERR, "short read(%d)", fd.winch); + } + + int error = ioctl(fd.pty, TIOCSWINSZ, &window); + if (error) err(EX_IOERR, "TIOCSWINSZ"); + } + + int status; + pid_t dead = waitpid(pid, &status, WNOHANG); + if (dead < 0) err(EX_OSERR, "waitpid(%d)", pid); + if (dead) return WIFEXITED(status) ? WEXITSTATUS(status) : EX_SOFTWARE; + } +} diff --git a/setup.c b/setup.c index 633a1f4..92509e3 100644 --- a/setup.c +++ b/setup.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2018, Curtis McEnroe +/* Copyright (C) 2018 Causal Agent June * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by @@ -23,18 +23,18 @@ #include int main(int argc, char *argv[]) { - if (argc < 2) return EX_USAGE; + if (argc < 2) errx(EX_USAGE, "missing public id"); - uint32_t key[4]; + uint32_t id[4]; char public[7 + 32 + 1]; char private[8 + 32 + 1]; + arc4random_buf(id, sizeof(id)); snprintf(public, sizeof(public), "public/%s", argv[1]); - arc4random_buf(key, sizeof(key)); snprintf( private, sizeof(private), "private/%08x%08x%08x%08x", - key[0], key[1], key[2], key[3] + id[0], id[1], id[2], id[3] ); int fd = open(public, O_CREAT | O_EXCL, 0644); @@ -44,6 +44,6 @@ int main(int argc, char *argv[]) { int error = symlink(public, private); if (error) err(EX_CANTCREAT, "%s", private); - printf("%08x%08x%08x%08x\n", key[0], key[1], key[2], key[3]); + printf("%08x%08x%08x%08x\n", id[0], id[1], id[2], id[3]); return EX_OK; } diff --git a/ssh-command.sh b/ssh-command.sh index 8461d3b..84bc350 100644 --- a/ssh-command.sh +++ b/ssh-command.sh @@ -6,7 +6,7 @@ GIT_URL=git@ascii.town:stream.git welcome() { echo - echo 'Welcome to Twinch!' + echo 'Welcome to Twinch dot ptee vee!' echo echo 'To view a public stream, run one of the following commands:' echo @@ -19,8 +19,8 @@ welcome() { } start() { - local key - key=$(setup $1) + local id + id=$(setup $1) echo echo 'Stream created!' echo @@ -32,8 +32,8 @@ start() { echo echo " git clone $GIT_URL" echo " cd stream" - echo " make broadcast" - echo " ./broadcast | ssh $SSH_URL ingest $key" + echo " make ptee" + echo " ./ptee | ssh -t $SSH_URL ingest $id" echo } diff --git a/stream.h b/stream.h deleted file mode 100644 index d719b7b..0000000 --- a/stream.h +++ /dev/null @@ -1,17 +0,0 @@ -/* Copyright (c) 2018, Curtis McEnroe - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -#define STREAM_SIZE (1024 * 1024) diff --git a/view.c b/view.c index 9ae0ec6..47c6c5b 100644 --- a/view.c +++ b/view.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2018, Curtis McEnroe +/* Copyright (C) 2018 Causal Agent June * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by @@ -19,10 +19,6 @@ #include #include #include -#include -#include -#include -#include #include #include #include @@ -30,133 +26,124 @@ #include #include -static struct termios saveTerm; -static void restoreTerm(void) { - tcsetattr(STDOUT_FILENO, TCSADRAIN, &saveTerm); -} +extern int winch(void); static struct { - int read; - int write; -} winch; - -static void sigwinch() { - struct winsize localWindow; - int error = ioctl(STDERR_FILENO, TIOCGWINSZ, &localWindow); - if (error) err(EX_IOERR, "ioctl(%d, TIOCGWINSZ, ...)", STDERR_FILENO); - - ssize_t size = write(winch.write, &localWindow, sizeof(localWindow)); - if (size < 0) err(EX_IOERR, "write(%d)", winch.write); - if ((size_t)size < sizeof(localWindow)) { - errx(EX_IOERR, "short write(%d)", winch.write); - } -} + int winch; + int remote; + int local; + int input; +} fd = { -1, -1, STDOUT_FILENO, STDIN_FILENO }; -static struct winsize remoteWindow; -static void readRemoteWindow(int remote) { - ssize_t size = read(remote, &remoteWindow, sizeof(remoteWindow)); - if (size < 0) err(EX_IOERR, "read(%d)", remote); - if ((size_t)size < sizeof(remoteWindow)) errx(EX_IOERR, "short read(%d)", remote); +static struct termios saveTerm; +static void restoreTerm(void) { + tcsetattr(fd.input, TCSADRAIN, &saveTerm); } int main(int argc, char *argv[]) { int error; - if (argc < 2) return EX_USAGE; - const char *path = argv[1]; + if (argc < 2) errx(EX_USAGE, "missing public id"); + if (!isatty(fd.local)) errx(EX_USAGE, "no terminal (use ssh -t)"); - int input = STDIN_FILENO; - int local = STDOUT_FILENO; + const char *path = argv[1]; + fd.remote = open(path, O_RDONLY); + if (fd.remote < 0) err(EX_NOINPUT, "%s", path); - error = tcgetattr(local, &saveTerm); + error = tcgetattr(fd.input, &saveTerm); if (error) err(EX_IOERR, "tcgetattr"); atexit(restoreTerm); struct termios term = saveTerm; term.c_lflag &= ~(ICANON | ECHO); - error = tcsetattr(local, TCSADRAIN, &term); + error = tcsetattr(fd.input, TCSADRAIN, &term); if (error) err(EX_IOERR, "tcsetattr"); - error = pipe((int *)&winch); - if (error) err(EX_OSERR, "pipe"); - signal(SIGWINCH, sigwinch); - sigwinch(); - - int remote = open(path, O_RDONLY); - if (remote < 0) err(EX_NOINPUT, "%s", path); - readRemoteWindow(remote); + struct winsize localWindow; + error = ioctl(fd.local, TIOCGWINSZ, &localWindow); + if (error) err(EX_IOERR, "TIOCGWINSZ"); + fd.winch = winch(); int kq = kqueue(); if (kq < 0) err(EX_OSERR, "kqueue"); struct kevent events[3] = { - { .ident = winch.read, .filter = EVFILT_READ, .flags = EV_ADD }, - { .ident = input, .filter = EVFILT_READ, .flags = EV_ADD }, - { .ident = remote, .filter = EVFILT_READ, .flags = EV_ADD }, + { .ident = fd.input, .filter = EVFILT_READ, .flags = EV_ADD }, + { .ident = fd.remote, .filter = EVFILT_READ, .flags = EV_ADD }, + { .ident = fd.winch, .filter = EVFILT_READ, .flags = EV_ADD }, }; int nevents = kevent(kq, events, 3, NULL, 0, NULL); if (nevents < 0) err(EX_OSERR, "kevent"); - char buf[4096]; - bool truncated = false; for (;;) { - struct kevent event; - int nevents = kevent(kq, NULL, 0, &event, 1, NULL); - if (nevents < 0) { - if (errno == EINTR) continue; - err(EX_OSERR, "kevent"); + off_t off = lseek(fd.remote, 0, SEEK_SET); + if (off < 0) err(EX_IOERR, "%s", path); + + // FIXME: Hack spin waiting for remote window. + struct winsize remoteWindow; + ssize_t size; + for (int i = 0; i < 100; ++i, usleep(100)) { + size = read(fd.remote, &remoteWindow, sizeof(remoteWindow)); + if (size < 0) err(EX_IOERR, "read(%d)", fd.remote); + if (size) break; } - if (!nevents) continue; - - if (event.ident == (uintptr_t)winch.read) { - for (;;) { - struct winsize localWindow; - ssize_t size = read(winch.read, &localWindow, sizeof(localWindow)); - if (size < 0) err(EX_IOERR, "read(%d)", winch.read); - if ((size_t)size < sizeof(localWindow)) { - errx(EX_IOERR, "short read(%d)", winch.read); - } - - if ( - localWindow.ws_col == remoteWindow.ws_col - && localWindow.ws_row == remoteWindow.ws_row - ) break; - - warnx( - "Please resize your terminal %hux%hu -> %hux%hu", - localWindow.ws_col, localWindow.ws_row, - remoteWindow.ws_col, remoteWindow.ws_row - ); - } + if ((size_t)size < sizeof(remoteWindow)) { + errx(EX_DATAERR, "no window size (stream not started)"); } - if (event.ident == (uintptr_t)input) { - ssize_t size = read(input, buf, sizeof(buf)); - if (size < 0) err(EX_IOERR, "read(%d)", input); - if (size == 1 && buf[0] == 'q') return EX_OK; + if ( + localWindow.ws_col < remoteWindow.ws_col + || localWindow.ws_row < remoteWindow.ws_row + ) { + warnx( + "resize window %hux%hu to at least %hux%hu", + localWindow.ws_col, localWindow.ws_row, + remoteWindow.ws_col, remoteWindow.ws_row + ); + + ssize_t size = read(fd.winch, &localWindow, sizeof(localWindow)); + if (size < 0) err(EX_IOERR, "read(%d)", fd.winch); + if ((size_t)size < sizeof(localWindow)) { + errx(EX_IOERR, "short read(%d)", fd.winch); + } + continue; } - if (event.ident == (uintptr_t)remote) { - if (event.data < 0) { - off_t offset = lseek(remote, 0, SEEK_SET); - if (offset < 0) err(EX_IOERR, "lseek(%d)", remote); - truncated = true; - continue; + char buf[4096]; + for (;;) { + struct kevent event; + int nevents = kevent(kq, NULL, 0, &event, 1, NULL); + if (nevents < 0) { + if (errno == EINTR) continue; + err(EX_OSERR, "kevent"); } + if (!nevents) continue; - if (truncated) { - readRemoteWindow(remote); - sigwinch(); - truncated = false; - continue; + if (event.ident == (uintptr_t)fd.input) { + ssize_t size = read(fd.input, buf, sizeof(buf)); + if (size < 0) err(EX_IOERR, "read(%d)", fd.input); + if (size == 1 && buf[0] == 'q') return EX_OK; } - ssize_t readSize = read(remote, buf, sizeof(buf)); - if (readSize < 0) err(EX_IOERR, "read(%d)", remote); + if (event.ident == (uintptr_t)fd.remote) { + if (event.data < 0) break; - ssize_t writeSize = write(local, buf, readSize); - if (writeSize < 0) err(EX_IOERR, "write(%d)", local); - if (writeSize < readSize) errx(EX_IOERR, "short write(%d)", local); + ssize_t readSize = read(fd.remote, buf, sizeof(buf)); + if (readSize < 0) err(EX_IOERR, "read(%d)", fd.remote); + + ssize_t writeSize = write(fd.local, buf, readSize); + if (writeSize < 0) err(EX_IOERR, "write(%d)", fd.local); + if (writeSize < readSize) errx(EX_IOERR, "short write(%d)", fd.local); + } + + if (event.ident == (uintptr_t)fd.winch) { + ssize_t size = read(fd.winch, &localWindow, sizeof(localWindow)); + if (size < 0) err(EX_IOERR, "read(%d)", fd.winch); + if ((size_t)size < sizeof(localWindow)) { + errx(EX_IOERR, "short read(%d)", fd.winch); + } + break; + } } } } diff --git a/winch.c b/winch.c new file mode 100644 index 0000000..be63c52 --- /dev/null +++ b/winch.c @@ -0,0 +1,57 @@ +/* Copyright (c) 2018, Curtis McEnroe + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include + +static struct { + int read; + int write; +} fd; + +static void sigwinch() { + struct winsize window; + int error = ioctl(STDERR_FILENO, TIOCGWINSZ, &window); + if (error) err(EX_IOERR, "TIOCGWINSZ"); + + ssize_t size = write(fd.write, &window, sizeof(window)); + if (size < 0) err(EX_IOERR, "write(%d)", fd.write); + if ((size_t)size < sizeof(window)) errx(EX_IOERR, "short write(%d)", fd.write); +} + +int winch(void) { + int error = pipe((int *)&fd); + if (error) err(EX_OSERR, "pipe"); + + error = fcntl(fd.read, F_SETFD, FD_CLOEXEC); + if (error) err(EX_IOERR, "fcntl"); + + error = fcntl(fd.write, F_SETFD, FD_CLOEXEC); + if (error) err(EX_IOERR, "fcntl"); + + struct sigaction action = { + .sa_handler = sigwinch, + .sa_flags = SA_RESTART, + }; + error = sigaction(SIGWINCH, &action, NULL); + if (error) err(EX_OSERR, "sigaction"); + + return fd.read; +} -- cgit 1.4.1