PageRenderTime 68ms CodeModel.GetById 10ms RepoModel.GetById 0ms app.codeStats 0ms

/windows/cthelper/cthelper.c

http://futty.googlecode.com/
C | 807 lines | 646 code | 61 blank | 100 comment | 143 complexity | 2259eb07ad9c7e864de7d2d0cca65130 MD5 | raw file
  1. #include <stdlib.h>
  2. #include <string.h>
  3. #include <ctype.h>
  4. #include <unistd.h>
  5. #include <pwd.h>
  6. #include <fcntl.h>
  7. #include <utmp.h>
  8. #include <sys/socket.h>
  9. #include <netinet/in.h>
  10. #include <arpa/inet.h>
  11. #include <sys/ioctl.h>
  12. #include <sys/termios.h>
  13. #include <errno.h>
  14. #include <signal.h>
  15. #include <sys/wait.h>
  16. #include <assert.h>
  17. #if __INTERIX
  18. #include <rpc/types.h> /* for INADDR_LOOPBACK */
  19. #else
  20. #include <stdint.h>
  21. #endif
  22. #if __INTERIX
  23. /* Under SFU, openpty(), and therefore forkpty(), require root privileges.
  24. * So instead, we write our own forkpty() which uses /dev/ptmx. I have
  25. * no why the builtin openpty() cannot do this internally.
  26. */
  27. #include "ptyfork.h"
  28. #else
  29. /* On Cygwin, forkpty() works without privileges */
  30. #include <pty.h>
  31. #define pty_fork forkpty
  32. #endif
  33. #include "cthelper.h"
  34. #include "buffer.h"
  35. #include "message.h"
  36. #include "debug.h"
  37. /* Buffer sizes */
  38. enum {
  39. CTLBUF = 32,
  40. PTOBUF = 256,
  41. PTIBUF = 256,
  42. };
  43. static int
  44. connect_cygterm(unsigned int port)
  45. {
  46. int sock;
  47. struct sockaddr_in sa;
  48. DBUG_ENTER("connect_cygterm");
  49. if (0 <= (sock = socket(PF_INET, SOCK_STREAM, 0))) {
  50. sa.sin_family = AF_INET;
  51. sa.sin_port = htons(port);
  52. sa.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
  53. DBUG_PRINT("startup", ("connecting to cygterm on port %u", port));
  54. if (0 != connect(sock, (struct sockaddr *)&sa, sizeof(sa))) {
  55. close(sock);
  56. DBUG_PRINT("startup", ("connect failed: %s", strerror(errno)));
  57. DBUG_RETURN(-1);
  58. }
  59. }
  60. DBUG_RETURN(sock);
  61. }
  62. static int
  63. resize(int pty, int h, int w)
  64. {
  65. struct winsize ws;
  66. DBUG_ENTER("resize");
  67. if (0 != ioctl(pty, TIOCGWINSZ, (void *)&ws)) {
  68. DBUG_PRINT("msg", ("TIOCGWINSZ failed: %s", strerror(errno)));
  69. DBUG_RETURN(-1);
  70. }
  71. ws.ws_row = h;
  72. ws.ws_col = w;
  73. DBUG_PRINT("msg", ("resizing terminal: %d x %d", ws.ws_col, ws.ws_row));
  74. if (0 != ioctl(pty, TIOCSWINSZ, (void *)&ws))
  75. DBUG_PRINT("msg", ("TIOCSWINSZ failed: %s", strerror(errno)));
  76. DBUG_RETURN(0);
  77. }
  78. static int
  79. parse_char(const char *desc)
  80. {
  81. int ch;
  82. char *err;
  83. DBUG_ENTER("parse_char");
  84. if (desc[0] == '\0' || desc[0] == ':')
  85. ch = -1;
  86. else if (desc[1] == '\0' || desc[1] == ':')
  87. ch = desc[0];
  88. else if (desc[0] == '^') {
  89. ch = toupper(desc[1]);
  90. if ('?' <= ch && ch <= '_')
  91. ch ^= '@';
  92. else
  93. ch = -1;
  94. }
  95. else {
  96. ch = strtol(desc, &err, 0);
  97. if (!err || err == desc)
  98. ch = -1;
  99. }
  100. DBUG_RETURN(ch);
  101. }
  102. /* attr is a colon-separated sequence. Each element of this sequence may be:
  103. * size=width,height to set the terminal size
  104. * charname=char to set a special character
  105. * [-]flag to enable or disable a terminal characteristic
  106. * Supported characters and characteristics are listed below.
  107. */
  108. static void
  109. init_pty(int pty, const char *attr)
  110. {
  111. enum flag_type_t { T_SIZE, T_K, T_I, T_O, T_C, T_L };
  112. static const struct flag_s {
  113. const char *name;
  114. enum flag_type_t type;
  115. long v;
  116. } flag[] = {
  117. { "brkint", T_I, BRKINT }, /* SUSv2 */
  118. #if _XOPEN_SOURCE >= 500
  119. { "bs0", T_O, BS0 }, /* SUSv2 Ex */
  120. { "bs1", T_O, BS1 }, /* SUSv2 Ex */
  121. #endif
  122. { "clocal", T_C, CLOCAL }, /* SUSv2 */
  123. #if _XOPEN_SOURCE >= 500
  124. { "cr0", T_O, CR0 }, /* SUSv2 Ex */
  125. { "cr1", T_O, CR1 }, /* SUSv2 Ex */
  126. { "cr2", T_O, CR2 }, /* SUSv2 Ex */
  127. { "cr3", T_O, CR3 }, /* SUSv2 Ex */
  128. #endif
  129. { "cread", T_C, CREAD }, /* SUSv2 */
  130. #if _XOPEN_SOURCE >= 600
  131. { "crterase", T_L, CRTERASE }, /* SUSv3 */
  132. { "crtkill", T_L, CRTKILL }, /* SUSv3 */
  133. #endif
  134. #if _XOPEN_SOURCE >= 600
  135. { "crtscts", T_C, CRTSCTS }, /* SUSv3 */
  136. #endif
  137. { "cs5", T_C, CS5 }, /* SUSv2 */
  138. { "cs6", T_C, CS6 }, /* SUSv2 */
  139. { "cs7", T_C, CS7 }, /* SUSv2 */
  140. { "cs8", T_C, CS8 }, /* SUSv2 */
  141. { "cstopb", T_C, CSTOPB }, /* SUSv2 */
  142. #if _XOPEN_SOURCE >= 600
  143. { "ctlecho", T_L, CTLECHO }, /* SUSv3 */
  144. #endif
  145. { "echo", T_L, ECHO }, /* SUSv2 */
  146. { "echoctl", T_L, ECHOCTL }, /* SUSv3 */
  147. { "echoe", T_L, ECHOE }, /* SUSv2 */
  148. { "echok", T_L, ECHOK }, /* SUSv2 */
  149. #if _XOPEN_SOURCE >= 600
  150. { "echoke", T_L, ECHOKE }, /* SUSv3 */
  151. #endif
  152. { "echonl", T_L, ECHONL }, /* SUSv2 */
  153. #if _XOPEN_SOURCE >= 600
  154. { "echoprt", T_L, ECHOPRT }, /* SUSv3 */
  155. #endif
  156. { "eof", T_K, VEOF }, /* SUSv2 */
  157. { "eol", T_K, VEOL }, /* SUSv2 */
  158. { "erase", T_K, VERASE }, /* SUSv2 */
  159. #if !__INTERIX
  160. { "ff0", T_O, FF0 }, /* SUSv2 Ex */
  161. { "ff1", T_O, FF1 }, /* SUSv2 Ex */
  162. #endif
  163. { "hup", T_C, HUPCL }, /* SUSv2 */
  164. { "hupcl", T_C, HUPCL }, /* SUSv2 */
  165. { "icanon", T_L, ICANON }, /* SUSv2 */
  166. { "icrnl", T_I, ICRNL }, /* SUSv2 */
  167. { "iexten", T_L, IEXTEN }, /* SUSv2 */
  168. { "ignbrk", T_I, IGNBRK }, /* SUSv2 */
  169. { "igncr", T_I, IGNCR }, /* SUSv2 */
  170. { "ignpar", T_I, IGNPAR }, /* SUSv2 */
  171. { "imaxbel", T_I, IMAXBEL },
  172. { "inlcr", T_I, INLCR }, /* SUSv2 */
  173. { "inpck", T_I, INPCK },
  174. { "intr", T_K, VINTR }, /* SUSv2 */
  175. { "isig", T_L, ISIG }, /* SUSv2 */
  176. { "istrip", T_I, ISTRIP }, /* SUSv2 */
  177. #if 0
  178. { "iuclc", T_I, IUCLC }, /* SUSv2 LEGACY */
  179. #endif
  180. { "ixany", T_I, IXANY }, /* SUSv2 Ex */
  181. { "ixoff", T_I, IXOFF },
  182. { "ixon", T_I, IXON },
  183. { "kill", T_K, VKILL }, /* SUSv2 */
  184. #if !__INTERIX
  185. { "nl0", T_O, NL0 }, /* SUSv2 Ex */
  186. { "nl1", T_O, NL1 }, /* SUSv2 Ex */
  187. #endif
  188. { "noflsh", T_L, NOFLSH }, /* SUSv2 */
  189. { "ocrnl", T_O, OCRNL }, /* SUSv2 Ex */
  190. #if _XOPEN_SOURCE >= 600
  191. { "ofdel", T_O, OFDEL }, /* SUSv3 */
  192. #endif
  193. #if !__INTERIX
  194. { "ofill", T_O, OFILL }, /* SUSv2 Ex */
  195. #endif
  196. #if 0
  197. { "olcuc", T_O, OLCUC }, /* SUSv2 LEGACY */
  198. #endif
  199. { "onlcr", T_O, ONLCR }, /* SUSv2 Ex */
  200. { "onlret", T_O, ONLRET }, /* SUSv2 Ex */
  201. { "onocr", T_O, ONOCR }, /* SUSv2 Ex */
  202. { "opost", T_O, OPOST }, /* SUSv2 */
  203. { "parenb", T_C, PARENB }, /* SUSv2 */
  204. { "parmrk", T_I, PARMRK }, /* SUSv2 */
  205. { "parodd", T_C, PARODD }, /* SUSv2 */
  206. #if _XOPEN_SOURCE >= 600
  207. { "prterase", T_L, PRTERASE }, /* SUSv3 */
  208. #endif
  209. { "quit", T_K, VQUIT }, /* SUSv2 */
  210. { "size", T_SIZE, 0 },
  211. { "start", T_K, VSTART }, /* SUSv2 */
  212. { "stop", T_K, VSTOP }, /* SUSv2 */
  213. { "susp", T_K, VSUSP }, /* SUSv2 */
  214. #if !__INTERIX
  215. { "tab0", T_O, TAB0 }, /* SUSv2 Ex */
  216. { "tab1", T_O, TAB1 }, /* SUSv2 Ex */
  217. { "tab2", T_O, TAB2 }, /* SUSv2 Ex */
  218. { "tab3", T_O, TAB3 }, /* SUSv2 Ex */
  219. #endif
  220. { "tostop", T_L, TOSTOP }, /* SUSv2 */
  221. #if !__INTERIX
  222. { "vt0", T_O, VT0 }, /* SUSv2 Ex */
  223. { "vt1", T_O, VT1 }, /* SUSv2 Ex */
  224. #endif
  225. { "werase", T_K, VWERASE },
  226. #if 0
  227. { "xcase", T_L, XCASE }, /* SUSv2 LEGACY */
  228. #endif
  229. { 0, 0, 0 },
  230. };
  231. struct termios ts;
  232. DBUG_ENTER("init_pty");
  233. /* get terminal attributes */
  234. tcgetattr(pty, &ts);
  235. /* parse attribute string */
  236. for (; attr && *attr; (attr = strchr(attr, ':')) && ++attr) {
  237. size_t attr_len;
  238. int negate;
  239. const struct flag_s *s;
  240. tcflag_t *which;
  241. int ch;
  242. int cmp;
  243. if (*attr == ':')
  244. continue;
  245. /* check for negated attributes */
  246. if ((negate = *attr == '-'))
  247. attr++;
  248. attr_len = strcspn(attr, "=");
  249. /* find attribute by name */
  250. for (s = flag; s->name; s++) {
  251. if ((cmp = s->name[0] - *attr) < 0)
  252. continue;
  253. if ((cmp = strncmp(s->name, attr, attr_len)) < 0)
  254. continue;
  255. break;
  256. }
  257. if (cmp != 0) {
  258. DBUG_PRINT("error", ("invalid attribute name %s", attr));
  259. continue;
  260. }
  261. DBUG_PRINT("info", ("attr=%s", s->name));
  262. /* attribute found */
  263. switch (s->type) {
  264. case T_SIZE: { /* set terminal size */
  265. int h, w;
  266. if (2 == sscanf(attr+attr_len, "=%d,%d", &h, &w))
  267. resize(pty, h, w);
  268. which = 0;
  269. break;
  270. }
  271. case T_K: /* set special character */
  272. if (attr[attr_len] == '=') {
  273. ch = parse_char(attr + attr_len + 1);
  274. DBUG_PRINT("info", ("parse_char() returns %d", ch));
  275. ts.c_cc[s->v] = (cc_t)ch;
  276. }
  277. which = 0;
  278. break;
  279. case T_I: which = &ts.c_iflag; break;
  280. case T_O: which = &ts.c_oflag; break;
  281. case T_C: which = &ts.c_cflag; break;
  282. case T_L: which = &ts.c_lflag; break;
  283. }
  284. if (which) {
  285. if (negate) /* negate terminal setting */
  286. *which &= ~(tcflag_t)s->v;
  287. else /* set terminal setting */
  288. *which |= (tcflag_t)s->v;
  289. }
  290. }
  291. tcsetattr(pty, TCSANOW, &ts);
  292. DBUG_VOID_RETURN;
  293. }
  294. static char *
  295. str_dup(const char *src)
  296. {
  297. size_t len = strlen(src) + 1;
  298. char *p;
  299. if ((p = malloc(len)))
  300. memcpy(p, src, len);
  301. return p;
  302. }
  303. static int
  304. setup_child(pid_t *pid, const char *term, const char *attr, char *const *argv)
  305. {
  306. int master;
  307. char ttyname[20];
  308. DBUG_ENTER("setup_child");
  309. switch ((*pid = pty_fork(&master, ttyname, 0, 0))) {
  310. case -1:
  311. DBUG_PRINT("startup", ("forkpty: failed"));
  312. DBUG_RETURN(-errno);
  313. /*NOTREACHED*/
  314. case 0: {
  315. const char *shell;
  316. DBUG_PROCESS("child");
  317. DBUG_PRINT("info", ("TERM=%s", term));
  318. if (term) setenv("TERM", term, 1);
  319. DBUG_PRINT("info", ("attributes=%s", attr));
  320. if (attr) init_pty(0, attr);
  321. if (!(shell = argv[0])) {
  322. char *s0;
  323. uid_t uid;
  324. struct passwd *pw;
  325. shell = "/bin/bash";
  326. s0 = "-bash";
  327. /* get user's login shell */
  328. if (!(pw = getpwuid(uid = getuid()))) {
  329. DBUG_PRINT("error", ("getpwuid(%ld) failed: %s",
  330. uid, strerror(errno)));
  331. }
  332. else if (!(shell = pw->pw_shell) || *shell != '/') {
  333. DBUG_PRINT("error", ("bad shell for user id %ld", uid));
  334. }
  335. else {
  336. DBUG_PRINT("info", ("got shell %s", shell));
  337. s0 = strrchr(shell, '/');
  338. s0 = str_dup(s0);
  339. assert(s0 != 0);
  340. s0[0] = '-';
  341. }
  342. DBUG_PRINT("info", ("startup %s (%s)", shell, s0));
  343. execl(shell, s0, (char *)0);
  344. }
  345. else {
  346. DBUG_PRINT("info", ("startup %s", *argv));
  347. execvp(*argv, argv);
  348. }
  349. DBUG_PRINT("error", ("exec* failed: %s", strerror(errno)));
  350. perror(shell);
  351. exit(111);
  352. /*NOTREACHED*/ }
  353. default: { // Parent process.
  354. fcntl(master, F_SETFL, O_NONBLOCK);
  355. struct utmp ut;
  356. memset(&ut, 0, sizeof ut);
  357. ut.ut_type = USER_PROCESS;
  358. ut.ut_pid = (long int)*pid;
  359. ut.ut_time = time(0);
  360. char *dev = ptsname(master);
  361. if (dev) {
  362. if (!strncmp(dev, "/dev/", 5))
  363. dev += 5;
  364. strlcpy(ut.ut_line, dev, sizeof ut.ut_line);
  365. if (!strncmp(dev, "tty", 3))
  366. dev += 3;
  367. strlcpy(ut.ut_id, dev, sizeof ut.ut_id);
  368. }
  369. strlcpy(ut.ut_user, getlogin() ?: "?", sizeof ut.ut_user);
  370. gethostname(ut.ut_host, sizeof ut.ut_host);
  371. login(&ut);
  372. }
  373. }
  374. DBUG_PRINT("startup", ("forkpty:pid=%ld:master=%d:ttyname=%s",
  375. (long int)*pid, master, ttyname));
  376. DBUG_RETURN(master);
  377. }
  378. static void
  379. process_message(Buffer b, int pty)
  380. {
  381. Message m;
  382. DBUG_ENTER("process_message");
  383. switch (message_get(&m, b->data, b->len)) {
  384. case -1:
  385. DBUG_PRINT("msg", ("invalid message"));
  386. buffer_consumed(b, b->len);
  387. break;
  388. case 0:
  389. DBUG_PRINT("msg", ("message too small"));
  390. break;
  391. default:
  392. switch (m.type) {
  393. /* the only message type yet supported */
  394. case MSG_RESIZE:
  395. if (pty > 0)
  396. resize(pty, m.msg.resize.height, m.msg.resize.width);
  397. else
  398. DBUG_PRINT("msg", ("ignoring RESIZE on closed pty"));
  399. break;
  400. default:
  401. DBUG_PRINT("msg", ("unknown message type: %d", m.type));
  402. break;
  403. }
  404. buffer_consumed(b, m.size);
  405. break;
  406. }
  407. DBUG_VOID_RETURN;
  408. }
  409. /* These need to be global so that the signal handler has access to them. */
  410. static int t; /* pty descriptor */
  411. static pid_t child;
  412. static volatile sig_atomic_t child_signalled;
  413. static int exit_status;
  414. #define PID_NONE 1
  415. /* child is set to 1 when it is dead. Hopefully, an accidental kill(1,HUP)
  416. * will have no effect as pid 1 (init) does not exist under Cygwin.
  417. */
  418. #define child_alive() (child != PID_NONE)
  419. static void
  420. handle_sigchld(int sig)
  421. {
  422. child_signalled = sig;
  423. }
  424. static void
  425. check_child(void)
  426. {
  427. int status;
  428. DBUG_ENTER("check_child");
  429. DBUG_PRINT("sig", ("child signalled: %d", child_signalled));
  430. switch (waitpid(child, &status, WNOHANG)) {
  431. case -1:
  432. case 0: break;
  433. default:
  434. if (WIFEXITED(status))
  435. exit_status = WEXITSTATUS(status);
  436. else
  437. exit_status = 112;
  438. }
  439. DBUG_PRINT("sig", ("child is gone"));
  440. child = PID_NONE;
  441. child_signalled = 0;
  442. DBUG_VOID_RETURN;
  443. }
  444. static void
  445. setnonblock(int d)
  446. {
  447. fcntl(d, F_SETFL, O_NONBLOCK | fcntl(d, F_GETFL));
  448. }
  449. #ifdef DEBUG
  450. static struct termios save_termios;
  451. static void raw(void)
  452. {
  453. struct termios termios;
  454. assert(tcgetattr(STDOUT_FILENO, &save_termios) == 0);
  455. termios = save_termios;
  456. termios.c_lflag &= ~(ECHO | ICANON);
  457. termios.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
  458. termios.c_oflag &= ~(OPOST);
  459. termios.c_cc[VMIN] = 1;
  460. termios.c_cc[VTIME] = 0;
  461. assert(tcsetattr(STDOUT_FILENO, TCSAFLUSH, &termios) == 0);
  462. }
  463. static void restore(void)
  464. {
  465. assert(tcsetattr(STDOUT_FILENO, TCSAFLUSH, &save_termios) == 0);
  466. }
  467. #endif
  468. #define obligatory_max(a,b) ((a)>(b)?(a):(b))
  469. int
  470. main(int argc, char *const *argv)
  471. {
  472. int
  473. c, /* control descriptor (stdin) */
  474. s; /* socket descriptor (PuTTY) */
  475. Buffer
  476. cbuf, /* control buffer */
  477. pbuf, /* pty buffer */
  478. sbuf; /* socket buffer */
  479. DBUG_INIT_ENV("main",argv[0],"DBUG_OPTS");
  480. #ifndef DBUG_OFF
  481. setvbuf(DBUG_FILE, 0, _IONBF, 0);
  482. #endif
  483. /* General steps:
  484. 1. connect to cygterm backend
  485. 2. create pty
  486. 3. fork child process (/bin/bash)
  487. 4. select on pty, cygterm backend forwarding pty data and messages
  488. */
  489. if (argc < 4) {
  490. DBUG_PRINT("error", ("Too few arguments"));
  491. DBUG_RETURN(CthelperInvalidUsage);
  492. }
  493. DBUG_PRINT("startup", ("isatty: (%d,%d,%d)",
  494. isatty(STDIN_FILENO), isatty(STDOUT_FILENO), isatty(STDERR_FILENO)));
  495. DBUG_PRINT("startup", (
  496. "cmdline: [%s] %s %s %s ...", argv[0], argv[1], argv[2], argv[3]));
  497. {
  498. extern char **environ;
  499. char **envp;
  500. for (envp = environ; *envp; envp++)
  501. DBUG_PRINT("startup", ("%s", *envp));
  502. }
  503. /* It is not necessary to close all open descriptors. There are no
  504. * files inherited from the PuTTY process except standard input.
  505. */
  506. #ifndef DEBUG
  507. close(STDERR_FILENO);
  508. #endif
  509. /* Duplicate c and open /dev/null as 0 so that 0 can mean "closed". */
  510. c = dup(STDIN_FILENO); close(STDIN_FILENO);
  511. open("/dev/null", O_RDWR);
  512. /* Command line:
  513. * argv[1] = port number
  514. * argv[2] = terminal name
  515. * argv[3] = terminal characteristics string
  516. * Any remaining arguments are the command to execute. If there are no
  517. * other arguments, use the user's default login shell with a - prefix
  518. * for its argv[0].
  519. */
  520. /*
  521. cthelper command line parameters:
  522. cthelper PORT TERM ATTRS [COMMAND [ARGS]]
  523. PORT
  524. port number for PuTTY pty input data socket
  525. TERM
  526. name of terminal (set TERM environment variable)
  527. ATTRS
  528. a colon-separated list of terminal attributes
  529. See init_pty() for details.
  530. COMMAND
  531. Runs COMMAND with ARGS as child process. If COMMAND is not
  532. supplied, cthelper will run the user's login shell as specified in
  533. /etc/passwd specifying "-" for its argv[0] as typical.
  534. */
  535. /* connect to cygterm */
  536. {
  537. int ct_port = strtol(argv[1], 0, 0);
  538. #ifdef DEBUG
  539. if (ct_port == 0) {
  540. /* For debugging purposes, make the tty we are started
  541. * in the "socket". This allows to test cthelper without
  542. * putty.exe */
  543. assert(isatty(STDOUT_FILENO));
  544. raw();
  545. atexit(restore);
  546. c = open("/dev/null", O_RDONLY);
  547. s = dup(STDOUT_FILENO);
  548. }
  549. else
  550. #endif
  551. if (ct_port <= 0) {
  552. DBUG_PRINT("startup", ("invalid port"));
  553. DBUG_RETURN(CthelperInvalidPort);
  554. }
  555. DBUG_PRINT("startup", ("connect cygterm"));
  556. if (0 > (s = connect_cygterm(ct_port))) {
  557. DBUG_PRINT("startup", ("connect_cygterm: bad"));
  558. DBUG_RETURN(CthelperConnectFailed);
  559. }
  560. DBUG_PRINT("startup", ("OK"));
  561. }
  562. /* initialize buffers */
  563. DBUG_PRINT("startup", ("initialize buffers"));
  564. BUFFER_ALLOCA(cbuf, CTLBUF);
  565. BUFFER_ALLOCA(pbuf, PTOBUF);
  566. BUFFER_ALLOCA(sbuf, PTIBUF);
  567. /* set up signal handling */
  568. signal(SIGCHLD, handle_sigchld);
  569. /* start child process */
  570. if (0 > (t = setup_child(&child, argv[2], argv[3], argv + 4))) {
  571. DBUG_PRINT("startup", ("setup_child failed: %s", strerror(-t)));
  572. DBUG_RETURN(CthelperPtyforkFailure);
  573. }
  574. /* To explain what is happening here:
  575. * 's' is the socket between PuTTY and cthelper; it is read to get
  576. * input for the tty and written to display output from the pty.
  577. * 't' is the pseudo terminal; it is read to get pty input which is sent to
  578. * PuTTY and written to pass input from PuTTY to the pty.
  579. * 'c' is standard input, which is a one-way anonymous pipe from PuTTY.
  580. * It is read to receive special messages from PuTTY such as
  581. * terminal resize events.
  582. *
  583. * This is the flow of data through the buffers:
  584. * s => sbuf => t
  585. * t => pbuf => s
  586. * c => cbuf => process_message()
  587. *
  588. * When 't' is closed, we close(s) to signal PuTTY we are done.
  589. * When 's' is closed, we kill(child, HUP) to kill the child process.
  590. */
  591. setnonblock(c);
  592. setnonblock(s);
  593. setnonblock(t);
  594. DBUG_PRINT("info", ("c==%d, s==%d, t==%d", c, s, t));
  595. /* allow easy select() and FD_ISSET() stuff */
  596. assert(0 < c && c < s && s < t);
  597. DBUG_PRINT("startup", ("starting select loop"));
  598. while (s || t) {
  599. int n = 0;
  600. fd_set r, w;
  601. DBUG_ENTER("select");
  602. FD_ZERO(&r); FD_ZERO(&w);
  603. if (c && !buffer_isfull(cbuf)) { FD_SET(c, &r); n = c; }
  604. if (s && !buffer_isfull(sbuf)) { FD_SET(s, &r); n = s; }
  605. if (s && !buffer_isempty(pbuf)) { FD_SET(s, &w); n = s; }
  606. if (t && !buffer_isfull(pbuf)) { FD_SET(t, &r); n = t; }
  607. if (t && !buffer_isempty(sbuf)) { FD_SET(t, &w); n = t; }
  608. switch (n = select(n + 1, &r, &w, 0, 0)) {
  609. case -1:
  610. DBUG_PRINT("error", ("%s", strerror(errno)));
  611. if (errno != EINTR) {
  612. /* Something bad happened */
  613. close(c); c = 0;
  614. close(s); s = 0;
  615. close(t); t = 0;
  616. }
  617. break;
  618. case 0:
  619. DBUG_PRINT("info", ("select timeout"));
  620. break;
  621. default:
  622. DBUG_PRINT("info", ("%d ready descriptors [[r==%lx,w==%lx]]", n, *(unsigned long *)&r, *(unsigned long *)&w));
  623. if (FD_ISSET(c, &r)) {
  624. DBUG_ENTER("c=>cbuf");
  625. switch (buffer_read(cbuf, c)) {
  626. case -1:
  627. DBUG_PRINT("error", ("error reading c: %s", strerror(errno)));
  628. if (errno == EINTR || errno == EWOULDBLOCK) break;
  629. /*FALLTHRU*/
  630. case 0:
  631. /* PuTTY closed the message pipe */
  632. DBUG_PRINT("io", ("c closed"));
  633. close(c); c = 0;
  634. break;
  635. default:
  636. DBUG_PRINT("io", ("cbuf => process_message()"));
  637. process_message(cbuf, t);
  638. break;
  639. }
  640. DBUG_LEAVE;
  641. if (!--n) break;
  642. }
  643. if (FD_ISSET(s, &r)) {
  644. DBUG_ENTER("s=>sbuf");
  645. switch (buffer_read(sbuf, s)) {
  646. case -1:
  647. DBUG_PRINT("error", ("error reading s: %s", strerror(errno)));
  648. if (errno == EINTR || errno == EWOULDBLOCK) break;
  649. /*FALLTHRU*/
  650. case 0:
  651. /* PuTTY closed the socket */
  652. DBUG_PRINT("io", ("s closed"));
  653. close(s); s = 0;
  654. break;
  655. default:
  656. FD_SET(t, &w);
  657. break;
  658. }
  659. DBUG_LEAVE;
  660. if (!--n) break;
  661. }
  662. if (FD_ISSET(t, &r)) {
  663. DBUG_ENTER("t=>pbuf");
  664. switch (buffer_read(pbuf, t)) {
  665. case -1:
  666. DBUG_PRINT("error", ("error reading t: %s", strerror(errno)));
  667. if (errno == EINTR || errno == EWOULDBLOCK) break;
  668. /*FALLTHRU*/
  669. case 0:
  670. /* pty closed */
  671. DBUG_PRINT("io", ("t closed"));
  672. if (!FD_ISSET(t, &w)) {
  673. close(t); t = 0;
  674. }
  675. break;
  676. default:
  677. FD_SET(s, &w);
  678. break;
  679. }
  680. DBUG_LEAVE;
  681. if (!--n) break;
  682. }
  683. if (FD_ISSET(t, &w)) {
  684. DBUG_ENTER("sbuf=>t");
  685. switch (buffer_write(sbuf, t)) {
  686. case -1:
  687. DBUG_PRINT("error", ("error writing t: %s", strerror(errno)));
  688. if (errno == EINTR || errno == EWOULDBLOCK) break;
  689. /*FALLTHRU*/
  690. case 0:
  691. /* pty closed */
  692. DBUG_PRINT("io", ("t closed"));
  693. close(t); t = 0;
  694. break;
  695. }
  696. DBUG_LEAVE;
  697. if (!--n) break;
  698. }
  699. if (FD_ISSET(s, &w)) {
  700. DBUG_ENTER("pbuf=>s");
  701. switch (buffer_write(pbuf, s)) {
  702. case -1:
  703. DBUG_PRINT("error", ("error writing s: %s", strerror(errno)));
  704. if (errno == EINTR || errno == EWOULDBLOCK) break;
  705. /*FALLTHRU*/
  706. case 0:
  707. /* PuTTY closed the socket */
  708. DBUG_PRINT("io", ("s closed"));
  709. close(s); s = 0;
  710. break;
  711. }
  712. DBUG_LEAVE;
  713. if (!--n) break;
  714. }
  715. DBUG_PRINT("info", ("[[n==%d,r==%lx,w==%lx]]", n, *(unsigned long *)&r, *(unsigned long *)&w));
  716. assert(n == 0);
  717. break;
  718. }
  719. if (child_signalled) check_child();
  720. if (!t && buffer_isempty(pbuf)) {
  721. DBUG_PRINT("info", ("shutdown socket"));
  722. shutdown(s, SHUT_WR);
  723. }
  724. if (!s && buffer_isempty(sbuf) && child_alive()) {
  725. DBUG_PRINT("sig", ("kill child"));
  726. kill(child, SIGHUP);
  727. /* handle_sigchld() will close(t) */
  728. }
  729. DBUG_LEAVE;
  730. }
  731. DBUG_PRINT("info", ("end of select loop"));
  732. /* ensure child process killed */
  733. /* XXX I'm not sure if all of this is necessary, but it probably won't
  734. * hurt anything. */
  735. if (child_alive() && sleep(1) == 0) {
  736. DBUG_PRINT("sig", ("waiting for child"));
  737. waitpid(child, 0, WNOHANG);
  738. }
  739. DBUG_PRINT("info", ("goodbye"));
  740. if (exit_status == 111)
  741. DBUG_RETURN(CthelperExecFailure);
  742. DBUG_RETURN(EXIT_SUCCESS);
  743. }
  744. /* ex:set sw=4 smarttab: */