static bool check_with_child(struct ptr_valid_batch *batch, const void *p, size_t size, bool is_write) { char ret; if (!child_alive(batch)) { if (!create_child(batch)) return false; } if (write(batch->to_child, &p, sizeof(p)) + write(batch->to_child, &size, sizeof(size)) + write(batch->to_child, &is_write, sizeof(is_write)) != sizeof(p) + sizeof(size) + sizeof(is_write)) { finish_child(batch); errno = EFAULT; return false; } if (read(batch->from_child, &ret, sizeof(ret)) != sizeof(ret)) { finish_child(batch); errno = EFAULT; return false; } return true; }
void ptr_valid_batch_end(struct ptr_valid_batch *batch) { if (child_alive(batch)) finish_child(batch); free(batch->maps); }
int main(int argc, char *const *argv) { int c, /* control descriptor (stdin) */ s; /* socket descriptor (PuTTY) */ Buffer cbuf, /* control buffer */ pbuf, /* pty buffer */ sbuf; /* socket buffer */ DBUG_INIT_ENV("main",argv[0],"DBUG_OPTS"); #ifndef DBUG_OFF setvbuf(DBUG_FILE, 0, _IONBF, 0); #endif /* General steps: 1. connect to cygterm backend 2. create pty 3. fork child process (/bin/bash) 4. select on pty, cygterm backend forwarding pty data and messages */ if (argc < 4) { DBUG_PRINT("error", ("Too few arguments")); DBUG_RETURN(CthelperInvalidUsage); } DBUG_PRINT("startup", ("isatty: (%d,%d,%d)", isatty(STDIN_FILENO), isatty(STDOUT_FILENO), isatty(STDERR_FILENO))); DBUG_PRINT("startup", ( "cmdline: [%s] %s %s %s ...", argv[0], argv[1], argv[2], argv[3])); { extern char **environ; char **envp; for (envp = environ; *envp; envp++) DBUG_PRINT("startup", ("%s", *envp)); } /* It is not necessary to close all open descriptors. There are no * files inherited from the PuTTY process except standard input. */ #ifndef DEBUG close(STDERR_FILENO); #endif /* Duplicate c and open /dev/null as 0 so that 0 can mean "closed". */ c = dup(STDIN_FILENO); close(STDIN_FILENO); open("/dev/null", O_RDWR); /* Command line: * argv[1] = port number * argv[2] = terminal name * argv[3] = terminal characteristics string * Any remaining arguments are the command to execute. If there are no * other arguments, use the user's default login shell with a - prefix * for its argv[0]. */ /* cthelper command line parameters: cthelper PORT TERM ATTRS [COMMAND [ARGS]] PORT port number for PuTTY pty input data socket TERM name of terminal (set TERM environment variable) ATTRS a colon-separated list of terminal attributes See init_pty() for details. COMMAND Runs COMMAND with ARGS as child process. If COMMAND is not supplied, cthelper will run the user's login shell as specified in /etc/passwd specifying "-" for its argv[0] as typical. */ /* connect to cygterm */ { int ct_port = strtol(argv[1], 0, 0); #ifdef DEBUG if (ct_port == 0) { /* For debugging purposes, make the tty we are started * in the "socket". This allows to test cthelper without * putty.exe */ assert(isatty(STDOUT_FILENO)); raw(); atexit(restore); c = open("/dev/null", O_RDONLY); s = dup(STDOUT_FILENO); } else #endif if (ct_port <= 0) { DBUG_PRINT("startup", ("invalid port")); DBUG_RETURN(CthelperInvalidPort); } DBUG_PRINT("startup", ("connect cygterm")); if (0 > (s = connect_cygterm(ct_port))) { DBUG_PRINT("startup", ("connect_cygterm: bad")); DBUG_RETURN(CthelperConnectFailed); } DBUG_PRINT("startup", ("OK")); } /* initialize buffers */ DBUG_PRINT("startup", ("initialize buffers")); BUFFER_ALLOCA(cbuf, CTLBUF); BUFFER_ALLOCA(pbuf, PTOBUF); BUFFER_ALLOCA(sbuf, PTIBUF); /* set up signal handling */ signal(SIGCHLD, handle_sigchld); /* start child process */ if (0 > (t = setup_child(&child, argv[2], argv[3], argv + 4))) { DBUG_PRINT("startup", ("setup_child failed: %s", strerror(-t))); DBUG_RETURN(CthelperPtyforkFailure); } /* To explain what is happening here: * 's' is the socket between PuTTY and cthelper; it is read to get * input for the tty and written to display output from the pty. * 't' is the pseudo terminal; it is read to get pty input which is sent to * PuTTY and written to pass input from PuTTY to the pty. * 'c' is standard input, which is a one-way anonymous pipe from PuTTY. * It is read to receive special messages from PuTTY such as * terminal resize events. * * This is the flow of data through the buffers: * s => sbuf => t * t => pbuf => s * c => cbuf => process_message() * * When 't' is closed, we close(s) to signal PuTTY we are done. * When 's' is closed, we kill(child, HUP) to kill the child process. */ setnonblock(c); setnonblock(s); setnonblock(t); DBUG_PRINT("info", ("c==%d, s==%d, t==%d", c, s, t)); /* allow easy select() and FD_ISSET() stuff */ assert(0 < c && c < s && s < t); DBUG_PRINT("startup", ("starting select loop")); while (s || t) { int n = 0; fd_set r, w; DBUG_ENTER("select"); FD_ZERO(&r); FD_ZERO(&w); if (c && !buffer_isfull(cbuf)) { FD_SET(c, &r); n = c; } if (s && !buffer_isfull(sbuf)) { FD_SET(s, &r); n = s; } if (s && !buffer_isempty(pbuf)) { FD_SET(s, &w); n = s; } if (t && !buffer_isfull(pbuf)) { FD_SET(t, &r); n = t; } if (t && !buffer_isempty(sbuf)) { FD_SET(t, &w); n = t; } switch (n = select(n + 1, &r, &w, 0, 0)) { case -1: DBUG_PRINT("error", ("%s", strerror(errno))); if (errno != EINTR) { /* Something bad happened */ close(c); c = 0; close(s); s = 0; close(t); t = 0; } break; case 0: DBUG_PRINT("info", ("select timeout")); break; default: DBUG_PRINT("info", ("%d ready descriptors [[r==%lx,w==%lx]]", n, *(unsigned long *)&r, *(unsigned long *)&w)); if (FD_ISSET(c, &r)) { DBUG_ENTER("c=>cbuf"); switch (buffer_read(cbuf, c)) { case -1: DBUG_PRINT("error", ("error reading c: %s", strerror(errno))); if (errno == EINTR || errno == EWOULDBLOCK) break; /*FALLTHRU*/ case 0: /* PuTTY closed the message pipe */ DBUG_PRINT("io", ("c closed")); close(c); c = 0; break; default: DBUG_PRINT("io", ("cbuf => process_message()")); process_message(cbuf, t); break; } DBUG_LEAVE; if (!--n) break; } if (FD_ISSET(s, &r)) { DBUG_ENTER("s=>sbuf"); switch (buffer_read(sbuf, s)) { case -1: DBUG_PRINT("error", ("error reading s: %s", strerror(errno))); if (errno == EINTR || errno == EWOULDBLOCK) break; /*FALLTHRU*/ case 0: /* PuTTY closed the socket */ DBUG_PRINT("io", ("s closed")); close(s); s = 0; break; default: FD_SET(t, &w); break; } DBUG_LEAVE; if (!--n) break; } if (FD_ISSET(t, &r)) { DBUG_ENTER("t=>pbuf"); switch (buffer_read(pbuf, t)) { case -1: DBUG_PRINT("error", ("error reading t: %s", strerror(errno))); if (errno == EINTR || errno == EWOULDBLOCK) break; /*FALLTHRU*/ case 0: /* pty closed */ DBUG_PRINT("io", ("t closed")); if (!FD_ISSET(t, &w)) { close(t); t = 0; } break; default: FD_SET(s, &w); break; } DBUG_LEAVE; if (!--n) break; } if (FD_ISSET(t, &w)) { DBUG_ENTER("sbuf=>t"); switch (buffer_write(sbuf, t)) { case -1: DBUG_PRINT("error", ("error writing t: %s", strerror(errno))); if (errno == EINTR || errno == EWOULDBLOCK) break; /*FALLTHRU*/ case 0: /* pty closed */ DBUG_PRINT("io", ("t closed")); close(t); t = 0; break; } DBUG_LEAVE; if (!--n) break; } if (FD_ISSET(s, &w)) { DBUG_ENTER("pbuf=>s"); switch (buffer_write(pbuf, s)) { case -1: DBUG_PRINT("error", ("error writing s: %s", strerror(errno))); if (errno == EINTR || errno == EWOULDBLOCK) break; /*FALLTHRU*/ case 0: /* PuTTY closed the socket */ DBUG_PRINT("io", ("s closed")); close(s); s = 0; break; } DBUG_LEAVE; if (!--n) break; } DBUG_PRINT("info", ("[[n==%d,r==%lx,w==%lx]]", n, *(unsigned long *)&r, *(unsigned long *)&w)); assert(n == 0); break; } if (child_signalled) check_child(); if (!t && buffer_isempty(pbuf)) { DBUG_PRINT("info", ("shutdown socket")); shutdown(s, SHUT_WR); } if (!s && buffer_isempty(sbuf) && child_alive()) { DBUG_PRINT("sig", ("kill child")); kill(child, SIGHUP); /* handle_sigchld() will close(t) */ } DBUG_LEAVE; } DBUG_PRINT("info", ("end of select loop")); /* ensure child process killed */ /* XXX I'm not sure if all of this is necessary, but it probably won't * hurt anything. */ if (child_alive() && sleep(1) == 0) { DBUG_PRINT("sig", ("waiting for child")); waitpid(child, 0, WNOHANG); } DBUG_PRINT("info", ("goodbye")); if (exit_status == 111) DBUG_RETURN(CthelperExecFailure); DBUG_RETURN(EXIT_SUCCESS); }