ptty.c 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. #include <sys/wait.h>
  2. #include <errno.h>
  3. #include <inttypes.h>
  4. #include <limits.h>
  5. #include <poll.h>
  6. #include <stdarg.h>
  7. #include <stdbool.h>
  8. #include <stdio.h>
  9. #include <stdlib.h>
  10. #include <string.h>
  11. #include <termios.h>
  12. #include <unistd.h>
  13. #if defined(__linux)
  14. #include <pty.h>
  15. #elif defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__)
  16. #include <util.h>
  17. #elif defined(__FreeBSD__) || defined(__DragonFly__)
  18. #include <libutil.h>
  19. #endif
  20. void
  21. die(const char *fmt, ...)
  22. {
  23. va_list ap;
  24. va_start(ap, fmt);
  25. vfprintf(stderr, fmt, ap);
  26. va_end(ap);
  27. if (fmt[0] && fmt[strlen(fmt)-1] == ':') {
  28. fputc(' ', stderr);
  29. perror(NULL);
  30. } else {
  31. fputc('\n', stderr);
  32. }
  33. exit(EXIT_FAILURE);
  34. }
  35. void
  36. usage(void)
  37. {
  38. fputs("ptty [-C] [-c cols] [-r rows] cmd\n", stderr);
  39. exit(EXIT_FAILURE);
  40. }
  41. int
  42. main(int argc, char *argv[])
  43. {
  44. struct winsize ws = {.ws_row = 25, .ws_col = 80, 0, 0};
  45. int ch;
  46. bool closeflag = false;
  47. while ((ch = getopt(argc, argv, "c:r:Ch")) != -1) {
  48. switch (ch) {
  49. case 'c': /* cols */
  50. ws.ws_col = strtoimax(optarg, NULL, 10);
  51. if (errno != 0)
  52. die("strtoimax: %s", optarg);
  53. break;
  54. case 'r': /* lines */
  55. ws.ws_row = strtoimax(optarg, NULL, 10);
  56. if (errno != 0)
  57. die("strtoimax: %s", optarg);
  58. break;
  59. case 'C':
  60. closeflag = true;
  61. break;
  62. case 'h':
  63. default:
  64. usage();
  65. }
  66. }
  67. argc -= optind;
  68. argv += optind;
  69. if (argc < 1)
  70. usage();
  71. int mfd;
  72. pid_t pid = forkpty(&mfd, NULL, NULL, &ws);
  73. switch (pid) {
  74. case -1:
  75. die("forkpty");
  76. case 0: /* child */
  77. execvp(argv[0], argv);
  78. die("exec");
  79. }
  80. /* parent */
  81. if (closeflag && close(mfd) == -1)
  82. die("close:");
  83. int pfds = 2;
  84. struct pollfd pfd[2] = {
  85. { STDIN_FILENO, POLLIN, 0},
  86. { mfd, POLLIN, 0}
  87. };
  88. for (;;) {
  89. char buf[BUFSIZ];
  90. ssize_t n;
  91. int r;
  92. if ((r = poll(pfd, pfds, -1)) == -1)
  93. die("poll:");
  94. if (pfd[0].revents & POLLIN) {
  95. if ((n = read(STDIN_FILENO, buf, sizeof buf)) == -1)
  96. die("read:");
  97. if (n == 0) {
  98. pfd[0].fd = -1;
  99. close(mfd);
  100. break;
  101. }
  102. if (write(mfd, buf, n) == -1)
  103. die("write:");
  104. }
  105. if (pfd[1].revents & POLLIN) {
  106. if ((n = read(mfd, buf, sizeof buf)) == -1)
  107. die("read:");
  108. if (n == 0) break;
  109. if (write(STDOUT_FILENO, buf, n) == -1)
  110. die("write:");
  111. }
  112. if (pfd[0].revents & POLLHUP) {
  113. pfd[0].fd = -1;
  114. close(mfd);
  115. break;
  116. }
  117. if (pfd[1].revents & POLLHUP)
  118. break;
  119. }
  120. int status;
  121. waitpid(pid, &status, 0);
  122. return WEXITSTATUS(status);
  123. }