diff options
Diffstat (limited to '')
-rw-r--r-- | broadcast.c | 117 |
1 files changed, 117 insertions, 0 deletions
diff --git a/broadcast.c b/broadcast.c new file mode 100644 index 0000000..e5957ae --- /dev/null +++ b/broadcast.c @@ -0,0 +1,117 @@ +/* Copyright (c) 2018, Curtis McEnroe <programble@gmail.com> + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include <err.h> +#include <poll.h> +#include <pwd.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/ioctl.h> +#include <sys/wait.h> +#include <sysexits.h> +#include <termios.h> +#include <unistd.h> + +#if defined __FreeBSD__ +#include <libutil.h> +#elif defined __linux__ +#include <pty.h> +#else +#include <util.h> +#endif + +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; + + 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]; + 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); + } + + 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"); +} |