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.

dtch.c 6.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269
  1. /* Copyright (C) 2017, 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 <errno.h>
  18. #include <fcntl.h>
  19. #include <poll.h>
  20. #include <signal.h>
  21. #include <stdio.h>
  22. #include <stdlib.h>
  23. #include <sys/ioctl.h>
  24. #include <sys/socket.h>
  25. #include <sys/stat.h>
  26. #include <sys/stat.h>
  27. #include <sys/un.h>
  28. #include <sys/wait.h>
  29. #include <sysexits.h>
  30. #include <termios.h>
  31. #include <unistd.h>
  32. #if defined __FreeBSD__
  33. #include <libutil.h>
  34. #elif defined __linux__
  35. #include <pty.h>
  36. #else
  37. #include <util.h>
  38. #endif
  39. typedef unsigned char byte;
  40. static struct sockaddr_un sockAddr(const char *home, const char *name) {
  41. struct sockaddr_un addr = { .sun_family = AF_UNIX };
  42. snprintf(addr.sun_path, sizeof(addr.sun_path), "%s/.dtch/%s", home, name);
  43. return addr;
  44. }
  45. static byte z;
  46. static struct iovec iov = { .iov_base = &z, .iov_len = 1 };
  47. static ssize_t sendFD(int sock, int fd) {
  48. size_t size = CMSG_SPACE(sizeof(int));
  49. byte buf[size];
  50. struct msghdr msg = {
  51. .msg_iov = &iov,
  52. .msg_iovlen = 1,
  53. .msg_control = buf,
  54. .msg_controllen = size,
  55. };
  56. struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
  57. cmsg->cmsg_len = CMSG_LEN(sizeof(int));
  58. cmsg->cmsg_level = SOL_SOCKET;
  59. cmsg->cmsg_type = SCM_RIGHTS;
  60. *(int *)CMSG_DATA(cmsg) = fd;
  61. return sendmsg(sock, &msg, 0);
  62. }
  63. static int recvFD(int sock) {
  64. size_t size = CMSG_SPACE(sizeof(int));
  65. char buf[size];
  66. struct msghdr msg = {
  67. .msg_iov = &iov,
  68. .msg_iovlen = 1,
  69. .msg_control = buf,
  70. .msg_controllen = size,
  71. };
  72. ssize_t n = recvmsg(sock, &msg, 0);
  73. if (n < 0) return -1;
  74. struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
  75. if (!cmsg || cmsg->cmsg_type != SCM_RIGHTS) {
  76. errno = ENOMSG;
  77. return -1;
  78. }
  79. return *(int *)CMSG_DATA(cmsg);
  80. }
  81. static struct sockaddr_un addr;
  82. static void sigExit(int sig) {
  83. unlink(addr.sun_path);
  84. _exit(128 + sig);
  85. }
  86. static int dtch(int argc, char *argv[]) {
  87. int error;
  88. const char *name = "dtch";
  89. if (argc > 1) {
  90. name = argv[1];
  91. argv++;
  92. argc--;
  93. }
  94. if (argc > 1) {
  95. argv++;
  96. } else {
  97. argv[0] = getenv("SHELL");
  98. if (!argv[0]) argv[0] = "/bin/sh";
  99. }
  100. const char *home = getenv("HOME");
  101. if (!home) errx(EX_CONFIG, "HOME unset");
  102. int fd = open(home, 0);
  103. if (fd < 0) err(EX_CANTCREAT, "%s", home);
  104. error = mkdirat(fd, ".dtch", 0700);
  105. if (error && errno != EEXIST) err(EX_CANTCREAT, "%s/.dtch", home);
  106. close(fd);
  107. int server = socket(PF_UNIX, SOCK_STREAM, 0);
  108. if (server < 0) err(EX_OSERR, "socket");
  109. addr = sockAddr(home, name);
  110. error = bind(server, (struct sockaddr *)&addr, sizeof(addr));
  111. if (error) err(EX_CANTCREAT, "%s", addr.sun_path);
  112. fcntl(server, F_SETFD, FD_CLOEXEC);
  113. int pty;
  114. pid_t pid = forkpty(&pty, NULL, NULL, NULL);
  115. if (pid < 0) err(EX_OSERR, "forkpty");
  116. if (!pid) {
  117. execvp(argv[0], argv);
  118. err(EX_NOINPUT, "%s", argv[0]);
  119. }
  120. signal(SIGINT, sigExit);
  121. signal(SIGTERM, sigExit);
  122. error = listen(server, 0);
  123. if (error) err(EX_OSERR, "listen");
  124. for (;;) {
  125. int client = accept(server, NULL, NULL);
  126. if (client < 0) err(EX_IOERR, "accept");
  127. ssize_t size = sendFD(client, pty);
  128. if (size < 0) warn("sendmsg");
  129. size = recv(client, &z, sizeof(z), 0);
  130. if (size < 0) warn("recv");
  131. close(client);
  132. int status;
  133. pid_t dead = waitpid(pid, &status, WNOHANG);
  134. if (dead < 0) err(EX_OSERR, "waitpid");
  135. if (dead) {
  136. unlink(addr.sun_path);
  137. if (WIFEXITED(status)) return WEXITSTATUS(status);
  138. else return 128 + WTERMSIG(status);
  139. }
  140. }
  141. }
  142. static struct termios saveTerm;
  143. static void restoreTerm(void) {
  144. tcsetattr(STDIN_FILENO, TCSADRAIN, &saveTerm);
  145. printf("\x1B\x63"); // rs1
  146. }
  147. static void sigNop(int sig) {
  148. (void)sig;
  149. }
  150. static int atch(int argc, char *argv[]) {
  151. int error;
  152. const char *home = getenv("HOME");
  153. if (!home) errx(EX_CONFIG, "HOME unset");
  154. const char *name = (argc > 1) ? argv[1] : "dtch";
  155. int client = socket(PF_UNIX, SOCK_STREAM, 0);
  156. if (client < 0) err(EX_OSERR, "socket");
  157. struct sockaddr_un addr = sockAddr(home, name);
  158. error = connect(client, (struct sockaddr *)&addr, sizeof(addr));
  159. if (error) err(EX_NOINPUT, "%s", addr.sun_path);
  160. int pty = recvFD(client);
  161. if (pty < 0) err(EX_IOERR, "recvmsg");
  162. struct winsize window;
  163. error = ioctl(STDIN_FILENO, TIOCGWINSZ, &window);
  164. if (error) err(EX_IOERR, "ioctl");
  165. struct winsize redraw = { .ws_row = 1, .ws_col = 1 };
  166. error = ioctl(pty, TIOCSWINSZ, &redraw);
  167. if (error) err(EX_IOERR, "ioctl");
  168. error = ioctl(pty, TIOCSWINSZ, &window);
  169. if (error) err(EX_IOERR, "ioctl");
  170. error = tcgetattr(STDIN_FILENO, &saveTerm);
  171. if (error) err(EX_IOERR, "tcgetattr");
  172. atexit(restoreTerm);
  173. struct termios raw = saveTerm;
  174. cfmakeraw(&raw);
  175. error = tcsetattr(STDIN_FILENO, TCSADRAIN, &raw);
  176. if (error) err(EX_IOERR, "tcsetattr");
  177. signal(SIGWINCH, sigNop);
  178. byte buf[4096];
  179. struct pollfd fds[2] = {
  180. { .events = POLLIN, .fd = STDIN_FILENO },
  181. { .events = POLLIN, .fd = pty },
  182. };
  183. for (;;) {
  184. int n = poll(fds, 2, -1);
  185. if (n < 0) {
  186. if (errno != EINTR) err(EX_IOERR, "poll");
  187. error = ioctl(STDIN_FILENO, TIOCGWINSZ, &window);
  188. if (error) err(EX_IOERR, "ioctl");
  189. error = ioctl(pty, TIOCSWINSZ, &window);
  190. if (error) err(EX_IOERR, "ioctl");
  191. continue;
  192. }
  193. if (fds[0].revents) {
  194. ssize_t size = read(STDIN_FILENO, buf, sizeof(buf));
  195. if (size < 0) err(EX_IOERR, "read(%d)", STDIN_FILENO);
  196. if (size == 1 && buf[0] == CTRL('Q')) return EX_OK;
  197. size = write(pty, buf, size);
  198. if (size < 0) err(EX_IOERR, "write(%d)", pty);
  199. }
  200. if (fds[1].revents) {
  201. ssize_t size = read(pty, buf, sizeof(buf));
  202. if (size < 0) err(EX_IOERR, "read(%d)", pty);
  203. size = write(STDOUT_FILENO, buf, size);
  204. if (size < 0) err(EX_IOERR, "write(%d)", STDOUT_FILENO);
  205. }
  206. }
  207. }
  208. int main(int argc, char *argv[]) {
  209. switch (argv[0][0]) {
  210. case 'd': return dtch(argc, argv);
  211. case 'a': return atch(argc, argv);
  212. default: return EX_USAGE;
  213. }
  214. }