The repository formerly known as dotfiles
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

modem.c 2.6KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
  1. /* Copyright (C) 2018 Curtis McEnroe <june@causal.agency>
  2. *
  3. * This program is free software: you can redistribute it and/or modify
  4. * it under the terms of the GNU Affero General Public License as published by
  5. * the Free Software Foundation, either version 3 of the License, or
  6. * (at your option) any later version.
  7. *
  8. * This program is distributed in the hope that it will be useful,
  9. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. * GNU Affero General Public License for more details.
  12. *
  13. * You should have received a copy of the GNU Affero General Public License
  14. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  15. */
  16. #include <err.h>
  17. #include <poll.h>
  18. #include <stdlib.h>
  19. #include <sys/ioctl.h>
  20. #include <sys/wait.h>
  21. #include <sysexits.h>
  22. #include <termios.h>
  23. #include <unistd.h>
  24. #if defined __FreeBSD__
  25. #include <libutil.h>
  26. #elif defined __linux__
  27. #include <pty.h>
  28. #else
  29. #include <util.h>
  30. #endif
  31. typedef unsigned uint;
  32. typedef unsigned char byte;
  33. static const uint BaudRate = 19200;
  34. static struct termios saveTerm;
  35. static void restoreTerm(void) {
  36. tcsetattr(STDIN_FILENO, TCSADRAIN, &saveTerm);
  37. }
  38. int main(int argc, char *argv[]) {
  39. int error;
  40. if (argc < 2) return EX_USAGE;
  41. error = tcgetattr(STDIN_FILENO, &saveTerm);
  42. if (error) err(EX_IOERR, "tcgetattr");
  43. atexit(restoreTerm);
  44. struct termios raw = saveTerm;
  45. cfmakeraw(&raw);
  46. error = tcsetattr(STDIN_FILENO, TCSADRAIN, &raw);
  47. if (error) err(EX_IOERR, "tcsetattr");
  48. struct winsize window;
  49. error = ioctl(STDIN_FILENO, TIOCGWINSZ, &window);
  50. if (error) err(EX_IOERR, "TIOCGWINSZ");
  51. int pty;
  52. pid_t pid = forkpty(&pty, NULL, NULL, &window);
  53. if (pid < 0) err(EX_OSERR, "forkpty");
  54. if (!pid) {
  55. execvp(argv[1], &argv[1]);
  56. err(EX_NOINPUT, "%s", argv[1]);
  57. }
  58. byte c;
  59. struct pollfd fds[2] = {
  60. { .events = POLLIN, .fd = STDIN_FILENO },
  61. { .events = POLLIN, .fd = pty },
  62. };
  63. while (usleep(8 * 1000000 / BaudRate), 0 < poll(fds, 2, -1)) {
  64. if (fds[0].revents) {
  65. ssize_t size = read(STDIN_FILENO, &c, 1);
  66. if (size < 0) err(EX_IOERR, "read(%d)", STDIN_FILENO);
  67. size = write(pty, &c, 1);
  68. if (size < 0) err(EX_IOERR, "write(%d)", pty);
  69. }
  70. if (fds[1].revents) {
  71. ssize_t size = read(pty, &c, 1);
  72. if (size < 0) err(EX_IOERR, "read(%d)", pty);
  73. size = write(STDOUT_FILENO, &c, 1);
  74. if (size < 0) err(EX_IOERR, "write(%d)", STDOUT_FILENO);
  75. }
  76. int status;
  77. pid_t dead = waitpid(pid, &status, WNOHANG);
  78. if (dead < 0) err(EX_OSERR, "waitpid");
  79. if (dead) return WIFEXITED(status) ? WEXITSTATUS(status) : EX_SOFTWARE;
  80. }
  81. err(EX_IOERR, "poll");
  82. }