pty.c 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. /*
  2. * pty.c - pseudo-terminal handling
  3. */
  4. #define _XOPEN_SOURCE 500
  5. #include <features.h>
  6. #include <stdio.h>
  7. #include <stdlib.h>
  8. #include <string.h>
  9. #include <assert.h>
  10. #include <unistd.h>
  11. #include <fcntl.h>
  12. #include <termios.h>
  13. #include <sys/ioctl.h>
  14. #include <sys/types.h>
  15. #include <pwd.h>
  16. #include "pty.h"
  17. #include "malloc.h"
  18. static char ptyname[FILENAME_MAX];
  19. int master = -1;
  20. void pty_preinit(void)
  21. {
  22. /*
  23. * Allocate the pty.
  24. */
  25. master = open("/dev/ptmx", O_RDWR);
  26. if (master < 0) {
  27. perror("/dev/ptmx: open");
  28. exit(1);
  29. }
  30. if (grantpt(master) < 0) {
  31. perror("grantpt");
  32. exit(1);
  33. }
  34. if (unlockpt(master) < 0) {
  35. perror("unlockpt");
  36. exit(1);
  37. }
  38. }
  39. void pty_resize(int w, int h)
  40. {
  41. struct winsize sz;
  42. assert(master >= 0);
  43. sz.ws_row = h;
  44. sz.ws_col = w;
  45. sz.ws_xpixel = sz.ws_ypixel = 0;
  46. ioctl(master, TIOCSWINSZ, &sz);
  47. }
  48. int run_program_in_pty(const struct shell_data *shdata,
  49. char *directory, char **program_args)
  50. {
  51. int slave, pid;
  52. char *fallback_args[2];
  53. assert(master >= 0);
  54. ptyname[FILENAME_MAX-1] = '\0';
  55. strncpy(ptyname, ptsname(master), FILENAME_MAX-1);
  56. #if 0
  57. {
  58. struct winsize ws;
  59. struct termios ts;
  60. /*
  61. * FIXME: think up some good defaults here
  62. */
  63. if (!ioctl(0, TIOCGWINSZ, &ws))
  64. ioctl(master, TIOCSWINSZ, &ws);
  65. if (!tcgetattr(0, &ts))
  66. tcsetattr(master, TCSANOW, &ts);
  67. }
  68. #endif
  69. slave = open(ptyname, O_RDWR | O_NOCTTY);
  70. if (slave < 0) {
  71. perror("slave pty: open");
  72. return 1;
  73. }
  74. /*
  75. * Fork and execute the command.
  76. */
  77. pid = fork();
  78. if (pid < 0) {
  79. perror("fork");
  80. return 1;
  81. }
  82. if (pid == 0) {
  83. int i, fd;
  84. /*
  85. * We are the child.
  86. */
  87. close(master);
  88. fcntl(slave, F_SETFD, 0); /* don't close on exec */
  89. dup2(slave, 0);
  90. dup2(slave, 1);
  91. if (slave != 0 && slave != 1)
  92. close(slave);
  93. dup2(1, 2);
  94. setsid();
  95. setpgrp();
  96. i = 0;
  97. #ifdef TIOCNOTTY
  98. if ((fd = open("/dev/tty", O_RDWR)) >= 0) {
  99. ioctl(fd, TIOCNOTTY, &i);
  100. close(fd);
  101. }
  102. #endif
  103. /*
  104. * Make the new pty our controlling terminal. On some OSes
  105. * this is done with TIOCSCTTY; Cygwin doesn't have that, so
  106. * instead it's done by simply opening the pty without
  107. * O_NOCTTY. This code is primarily intended for Cygwin, but
  108. * it's useful to have it work in other contexts for testing
  109. * purposes, so I leave the TIOCSCTTY here anyway.
  110. */
  111. if ((fd = open(ptyname, O_RDWR)) >= 0) {
  112. #ifdef TIOCSCTTY
  113. ioctl(fd, TIOCSCTTY, &i);
  114. #endif
  115. close(fd);
  116. } else {
  117. perror("slave pty: open");
  118. exit(127);
  119. }
  120. tcsetpgrp(0, getpgrp());
  121. for (i = 0; i < shdata->nenvvars; i++)
  122. putenv(shdata->envvars[i]);
  123. if (shdata->termtype)
  124. putenv(shdata->termtype);
  125. if (directory)
  126. chdir(directory);
  127. /*
  128. * Use the provided shell program name, if the user gave
  129. * one. Failing that, use $SHELL; failing that, look up
  130. * the user's default shell in the password file; failing
  131. * _that_, revert to the bog-standard /bin/sh.
  132. */
  133. if (!program_args) {
  134. char *shell;
  135. shell = getenv("SHELL");
  136. if (!shell) {
  137. const char *login;
  138. uid_t uid;
  139. struct passwd *pwd;
  140. /*
  141. * For maximum generality in the face of multiple
  142. * /etc/passwd entries with different login names and
  143. * shells but a shared uid, we start by using
  144. * getpwnam(getlogin()) if it's available - but we
  145. * insist that its uid must match our real one, or we
  146. * give up and fall back to getpwuid(getuid()).
  147. */
  148. uid = getuid();
  149. login = getlogin();
  150. if (login && (pwd = getpwnam(login)) && pwd->pw_uid == uid)
  151. shell = pwd->pw_shell;
  152. else if ((pwd = getpwuid(uid)))
  153. shell = pwd->pw_shell;
  154. }
  155. if (!shell)
  156. shell = "/bin/sh";
  157. fallback_args[0] = shell;
  158. fallback_args[1] = NULL;
  159. program_args = fallback_args;
  160. }
  161. execv(program_args[0], program_args);
  162. /*
  163. * If we're here, exec has gone badly foom.
  164. */
  165. perror("exec");
  166. exit(127);
  167. }
  168. close(slave);
  169. return master;
  170. }