From 586dc96f5ff0d87276ee6ae2eddc06e1482b4b02 Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Fri, 7 Feb 2020 19:14:32 +0000 Subject: [PATCH] Factor out common code from Unix CLI main loops. Unix Plink, Unix Pageant in server mode, Uppity, and the post- connection form of PSFTP's command-line reading code all had very similar loops in them, which run a pollwrapper and mediate between that, timers, and toplevel callbacks. It's long past time the common code between all of those became a reusable shared routine. So, this commit introduces uxcliloop.c, and turns all the previous copies of basically the same loop into a call to cli_main_loop with various callback functions to configure the parts that differ. --- Recipe | 10 +- unix/unix.h | 16 +++ unix/uxcliloop.c | 125 +++++++++++++++++++++ unix/uxpgnt.c | 278 +++++++++++++++++------------------------------ unix/uxplink.c | 244 ++++++++++++++--------------------------- unix/uxserver.c | 104 +----------------- unix/uxsftp.c | 156 ++++++++------------------ 7 files changed, 373 insertions(+), 560 deletions(-) create mode 100644 unix/uxcliloop.c diff --git a/Recipe b/Recipe index b3f1667e..7c251d30 100644 --- a/Recipe +++ b/Recipe @@ -364,7 +364,7 @@ puttytel : [X] GTKTERM uxmisc misc ldisc settings uxsel U_BE_NOSSH + nogss utils memory GTKMAIN plink : [U] uxplink uxcons NONSSH UXSSH U_BE_ALL logging UXMISC uxsignal - + ux_x11 noterm uxnogtk sessprep cmdline clicons + + ux_x11 noterm uxnogtk sessprep cmdline clicons uxcliloop PUTTYGEN_UNIX = KEYGEN sshprime sshdes ARITH sshmd5 version sshprng + sshrand uxnoise sshsha MISC sshrsa sshdss uxcons uxstore uxmisc @@ -375,15 +375,15 @@ puttygen : [U] cmdgen PUTTYGEN_UNIX cgtest : [UT] cgtest PUTTYGEN_UNIX pscp : [U] pscp uxsftp uxcons UXSSH BE_SSH SFTP wildcard UXMISC uxnogtk - + clicons + + clicons uxcliloop psftp : [U] psftp uxsftp uxcons UXSSH BE_SSH SFTP wildcard UXMISC uxnogtk - + clicons + + clicons uxcliloop pageant : [X] uxpgnt uxagentc aqsync pageant sshrsa sshpubk sshdes ARITH + sshmd5 version tree234 misc sshaes sshsha sshdss sshsh256 sshsh512 + sshecc CONF uxsignal nocproxy nogss be_none x11fwd ux_x11 uxcons + gtkask gtkmisc nullplug logging UXMISC uxagentsock utils memory - + sshauxcrypt sshhmac sshprng uxnoise + + sshauxcrypt sshhmac sshprng uxnoise uxcliloop ptermapp : [XT] GTKTERM uxmisc misc ldisc settings uxpty uxsel BE_NONE uxstore + uxsignal CHARSET uxpterm version time xpmpterm xpmptcfg @@ -404,7 +404,7 @@ testsc : [UT] testsc SSHCRYPTO marshal utils memory tree234 wildcard testzlib : [UT] testzlib sshzlib utils marshal memory uppity : [UT] uxserver SSHSERVER UXMISC uxsignal uxnoise uxgss uxnogtk - + uxpty uxsftpserver ux_x11 uxagentsock procnet + + uxpty uxsftpserver ux_x11 uxagentsock procnet uxcliloop # ---------------------------------------------------------------------- # On Windows, provide a means of removing local test binaries that we diff --git a/unix/unix.h b/unix/unix.h index 3831f9b6..80bfbeaf 100644 --- a/unix/unix.h +++ b/unix/unix.h @@ -444,4 +444,20 @@ static inline bool pollwrap_check_fd_rwx(pollwrapper *pw, int fd, int rwx) return (pollwrap_get_fd_rwx(pw, fd) & rwx) != 0; } +/* + * uxcliloop.c. + */ +typedef bool (*cliloop_pw_setup_t)(void *ctx, pollwrapper *pw); +typedef void (*cliloop_pw_check_t)(void *ctx, pollwrapper *pw); +typedef bool (*cliloop_continue_t)(void *ctx, bool found_any_fd, + bool ran_any_callback); + +void cli_main_loop(cliloop_pw_setup_t pw_setup, + cliloop_pw_check_t pw_check, + cliloop_continue_t cont, void *ctx); + +bool cliloop_no_pw_setup(void *ctx, pollwrapper *pw); +void cliloop_no_pw_check(void *ctx, pollwrapper *pw); +bool cliloop_always_continue(void *ctx, bool, bool); + #endif /* PUTTY_UNIX_H */ diff --git a/unix/uxcliloop.c b/unix/uxcliloop.c new file mode 100644 index 00000000..23a808da --- /dev/null +++ b/unix/uxcliloop.c @@ -0,0 +1,125 @@ +#include + +#include "putty.h" + +void cli_main_loop(cliloop_pw_setup_t pw_setup, + cliloop_pw_check_t pw_check, + cliloop_continue_t cont, void *ctx) +{ + unsigned long now = GETTICKCOUNT(); + + int *fdlist = NULL; + size_t fdsize = 0; + + pollwrapper *pw = pollwrap_new(); + + while (true) { + int rwx; + int ret; + int fdstate; + unsigned long next; + + pollwrap_clear(pw); + + if (!pw_setup(ctx, pw)) + break; /* our client signalled emergency exit */ + + /* Count the currently active fds. */ + size_t nfds = 0; + for (int fd = first_fd(&fdstate, &rwx); fd >= 0; + fd = next_fd(&fdstate, &rwx)) + nfds++; + + /* Expand the fdlist buffer if necessary. */ + sgrowarray(fdlist, fdsize, nfds); + + /* + * Add all currently open uxsel fds to pw, and store them in + * fdlist as well. + */ + size_t fdcount = 0; + for (int fd = first_fd(&fdstate, &rwx); fd >= 0; + fd = next_fd(&fdstate, &rwx)) { + fdlist[fdcount++] = fd; + pollwrap_add_fd_rwx(pw, fd, rwx); + } + + if (toplevel_callback_pending()) { + ret = pollwrap_poll_instant(pw); + } else if (run_timers(now, &next)) { + do { + unsigned long then; + long ticks; + + then = now; + now = GETTICKCOUNT(); + if (now - then > next - then) + ticks = 0; + else + ticks = next - now; + + bool overflow = false; + if (ticks > INT_MAX) { + ticks = INT_MAX; + overflow = true; + } + + ret = pollwrap_poll_timeout(pw, ticks); + if (ret == 0 && !overflow) + now = next; + else + now = GETTICKCOUNT(); + } while (ret < 0 && errno == EINTR); + } else { + ret = pollwrap_poll_endless(pw); + } + + if (ret < 0 && errno == EINTR) + continue; + + if (ret < 0) { + perror("poll"); + exit(1); + } + + bool found_fd = (ret > 0); + + for (size_t i = 0; i < fdcount; i++) { + int fd = fdlist[i]; + int rwx = pollwrap_get_fd_rwx(pw, fd); + /* + * We must process exceptional notifications before + * ordinary readability ones, or we may go straight + * past the urgent marker. + */ + if (rwx & SELECT_X) + select_result(fd, SELECT_X); + if (rwx & SELECT_R) + select_result(fd, SELECT_R); + if (rwx & SELECT_W) + select_result(fd, SELECT_W); + } + + pw_check(ctx, pw); + + bool ran_callback = run_toplevel_callbacks(); + + if (!cont(ctx, found_fd, ran_callback)) + break; + } + + pollwrap_free(pw); + sfree(fdlist); +} + +bool cliloop_no_pw_setup(void *ctx, pollwrapper *pw) { return true; } +void cliloop_no_pw_check(void *ctx, pollwrapper *pw) {} +bool cliloop_always_continue(void *ctx, bool fd, bool cb) { return true; } + +/* + * Any application using this main loop doesn't need to do anything + * when uxsel adds or removes an fd, because we synchronously re-check + * the current list every time we go round the main loop above. + */ +uxsel_id *uxsel_input_add(int fd, int rwx) { return NULL; } +void uxsel_input_remove(uxsel_id *id) { } diff --git a/unix/uxpgnt.c b/unix/uxpgnt.c index e73e5f0b..9978ed4d 100644 --- a/unix/uxpgnt.c +++ b/unix/uxpgnt.c @@ -35,6 +35,8 @@ struct uxpgnt_client { strbuf *debug_prompt_buf; bool debug_prompt_active, debug_prompt_possible; PageantClientDialogId *dlgid; + int passphrase_fd; + int termination_pid; PageantListenerClient plc; }; @@ -92,13 +94,6 @@ static const PageantListenerClientVtable uxpgnt_vtable = { uxpgnt_ask_passphrase, }; -/* - * In Pageant our selects are synchronous, so these functions are - * empty stubs. - */ -uxsel_id *uxsel_input_add(int fd, int rwx) { return NULL; } -void uxsel_input_remove(uxsel_id *id) { } - /* * More stubs. */ @@ -776,6 +771,93 @@ static const PlugVtable X11Connection_plugvt = { NULL }; + +static bool agent_loop_pw_setup(void *vctx, pollwrapper *pw) +{ + struct uxpgnt_client *upc = (struct uxpgnt_client *)vctx; + + if (signalpipe[0] >= 0) { + pollwrap_add_fd_rwx(pw, signalpipe[0], SELECT_R); + } + + if (upc->debug_prompt_active) + pollwrap_add_fd_rwx(pw, upc->passphrase_fd, SELECT_R); + + return true; +} + +static void agent_loop_pw_check(void *vctx, pollwrapper *pw) +{ + struct uxpgnt_client *upc = (struct uxpgnt_client *)vctx; + + if (life == LIFE_TTY) { + /* + * Every time we wake up (whether it was due to tty_timer + * elapsing or for any other reason), poll to see if we still + * have a controlling terminal. If we don't, then our + * containing tty session has ended, so it's time to clean up + * and leave. + */ + if (!have_controlling_tty()) { + time_to_die = true; + return; + } + } + + if (signalpipe[0] >= 0 && + pollwrap_check_fd_rwx(pw, signalpipe[0], SELECT_R)) { + char c[1]; + if (read(signalpipe[0], c, 1) <= 0) + /* ignore error */; + /* ignore its value; it'll be `x' */ + while (1) { + int status; + pid_t pid; + pid = waitpid(-1, &status, WNOHANG); + if (pid <= 0) + break; + if (pid == upc->termination_pid) + time_to_die = true; + } + } + + if (upc->debug_prompt_active && + pollwrap_check_fd_rwx(pw, upc->passphrase_fd, SELECT_R)) { + char c; + int retd = read(upc->passphrase_fd, &c, 1); + if (retd <= 0) { + passphrase_done(upc, false); + /* Now never try to read from stdin again */ + upc->debug_prompt_possible = false; + } else { + switch (c) { + case '\n': + case '\r': + passphrase_done(upc, true); + break; + case '\004': + passphrase_done(upc, false); + break; + case '\b': + case '\177': + strbuf_shrink_by(upc->debug_prompt_buf, 1); + break; + case '\025': + strbuf_clear(upc->debug_prompt_buf); + break; + default: + put_byte(upc->debug_prompt_buf, c); + break; + } + } + } +} + +static bool agent_loop_continue(void *vctx, bool fd, bool cb) +{ + return !time_to_die; +} + void run_agent(FILE *logfp, const char *symlink_path) { const char *err; @@ -783,20 +865,10 @@ void run_agent(FILE *logfp, const char *symlink_path) struct pageant_listen_state *pl; Plug *pl_plug; Socket *sock; - unsigned long now; - int *fdlist; - int fd; - int i, fdstate; - size_t fdsize; - int passphrase_fd = -1; - int termination_pid = -1; bool errors = false; Conf *conf; const struct cmdline_key_action *act; - fdlist = NULL; - fdsize = 0; - pageant_init(); /* @@ -817,6 +889,8 @@ void run_agent(FILE *logfp, const char *symlink_path) memset(upc, 0, sizeof(upc)); upc->plc.vt = &uxpgnt_vtable; upc->logfp = logfp; + upc->passphrase_fd = -1; + upc->termination_pid = -1; pl = pageant_listener_new(&pl_plug, &upc->plc); sock = platform_make_agent_socket(pl_plug, PAGEANT_DIR_PREFIX, &errw, &socketname); @@ -906,8 +980,8 @@ void run_agent(FILE *logfp, const char *symlink_path) upc->logfp = stdout; struct termios orig_termios; - passphrase_fd = fileno(stdin); - if (tcgetattr(passphrase_fd, &orig_termios) == 0) { + upc->passphrase_fd = fileno(stdin); + if (tcgetattr(upc->passphrase_fd, &orig_termios) == 0) { struct termios new_termios = orig_termios; new_termios.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL | ICANON); @@ -920,11 +994,11 @@ void run_agent(FILE *logfp, const char *symlink_path) if (pipe(pipefd) == 0) { pid_t pid = fork(); if (pid == 0) { - tcsetattr(passphrase_fd, TCSADRAIN, &new_termios); + tcsetattr(upc->passphrase_fd, TCSADRAIN, &new_termios); close(pipefd[1]); char buf[4096]; while (read(pipefd[0], buf, sizeof(buf)) > 0); - tcsetattr(passphrase_fd, TCSADRAIN, &new_termios); + tcsetattr(upc->passphrase_fd, TCSADRAIN, &new_termios); _exit(0); } else if (pid > 0) { upc->debug_prompt_possible = true; @@ -961,170 +1035,18 @@ void run_agent(FILE *logfp, const char *symlink_path) perror("exec"); _exit(127); } else { - termination_pid = pid; + upc->termination_pid = pid; } } if (!upc->logfp) upc->plc.suppress_logging = true; - now = GETTICKCOUNT(); - - pollwrapper *pw = pollwrap_new(); - - while (!time_to_die) { - int rwx; - int ret; - unsigned long next; - - pollwrap_clear(pw); - - if (signalpipe[0] >= 0) { - pollwrap_add_fd_rwx(pw, signalpipe[0], SELECT_R); - } - - /* Count the currently active fds. */ - i = 0; - for (fd = first_fd(&fdstate, &rwx); fd >= 0; - fd = next_fd(&fdstate, &rwx)) i++; - - /* Expand the fdlist buffer if necessary. */ - sgrowarray(fdlist, fdsize, i); - - /* - * Add all currently open fds to pw, and store them in fdlist - * as well. - */ - int fdcount = 0; - for (fd = first_fd(&fdstate, &rwx); fd >= 0; - fd = next_fd(&fdstate, &rwx)) { - fdlist[fdcount++] = fd; - pollwrap_add_fd_rwx(pw, fd, rwx); - } - - if (upc->debug_prompt_active) - pollwrap_add_fd_rwx(pw, passphrase_fd, SELECT_R); - - if (toplevel_callback_pending()) { - ret = pollwrap_poll_instant(pw); - } else if (run_timers(now, &next)) { - unsigned long then; - long ticks; - - then = now; - now = GETTICKCOUNT(); - if (now - then > next - then) - ticks = 0; - else - ticks = next - now; - - bool overflow = false; - if (ticks > INT_MAX) { - ticks = INT_MAX; - overflow = true; - } - - ret = pollwrap_poll_timeout(pw, ticks); - if (ret == 0 && !overflow) - now = next; - else - now = GETTICKCOUNT(); - } else { - ret = pollwrap_poll_endless(pw); - } - - if (ret < 0 && errno == EINTR) - continue; - - if (ret < 0) { - perror("poll"); - exit(1); - } - - if (life == LIFE_TTY) { - /* - * Every time we wake up (whether it was due to tty_timer - * elapsing or for any other reason), poll to see if we - * still have a controlling terminal. If we don't, then - * our containing tty session has ended, so it's time to - * clean up and leave. - */ - if (!have_controlling_tty()) { - time_to_die = true; - break; - } - } - - for (i = 0; i < fdcount; i++) { - fd = fdlist[i]; - int rwx = pollwrap_get_fd_rwx(pw, fd); - /* - * We must process exceptional notifications before - * ordinary readability ones, or we may go straight - * past the urgent marker. - */ - if (rwx & SELECT_X) - select_result(fd, SELECT_X); - if (rwx & SELECT_R) - select_result(fd, SELECT_R); - if (rwx & SELECT_W) - select_result(fd, SELECT_W); - } - - if (signalpipe[0] >= 0 && - pollwrap_check_fd_rwx(pw, signalpipe[0], SELECT_R)) { - char c[1]; - if (read(signalpipe[0], c, 1) <= 0) - /* ignore error */; - /* ignore its value; it'll be `x' */ - while (1) { - int status; - pid_t pid; - pid = waitpid(-1, &status, WNOHANG); - if (pid <= 0) - break; - if (pid == termination_pid) - time_to_die = true; - } - } - - if (upc->debug_prompt_active && - pollwrap_check_fd_rwx(pw, passphrase_fd, SELECT_R)) { - char c; - int retd = read(passphrase_fd, &c, 1); - if (retd <= 0) { - passphrase_done(upc, false); - /* Now never try to read from stdin again */ - upc->debug_prompt_possible = false; - } else { - switch (c) { - case '\n': - case '\r': - passphrase_done(upc, true); - break; - case '\004': - passphrase_done(upc, false); - break; - case '\b': - case '\177': - strbuf_shrink_by(upc->debug_prompt_buf, 1); - break; - case '\025': - strbuf_clear(upc->debug_prompt_buf); - break; - default: - put_byte(upc->debug_prompt_buf, c); - break; - } - } - } - - run_toplevel_callbacks(); - } + cli_main_loop(agent_loop_pw_setup, agent_loop_pw_check, + agent_loop_continue, upc); /* - * When we come here, we're terminating, and should clean up our - * Unix socket file if possible. + * Before terminating, clean up our Unix socket file if possible. */ if (unlink(socketname) < 0) { fprintf(stderr, "pageant: %s: %s\n", socketname, strerror(errno)); @@ -1132,8 +1054,6 @@ void run_agent(FILE *logfp, const char *symlink_path) } conf_free(conf); - pollwrap_free(pw); - sfree(fdlist); } int main(int argc, char **argv) diff --git a/unix/uxplink.c b/unix/uxplink.c index a341d88b..d49aa8f0 100644 --- a/unix/uxplink.c +++ b/unix/uxplink.c @@ -484,13 +484,6 @@ void sigwinch(int signum) /* not much we can do about it */; } -/* - * In Plink our selects are synchronous, so these functions are - * empty stubs. - */ -uxsel_id *uxsel_input_add(int fd, int rwx) { return NULL; } -void uxsel_input_remove(uxsel_id *id) { } - /* * Short description of parameters. */ @@ -575,24 +568,95 @@ const unsigned cmdline_tooltype = TOOLTYPE_HOST_ARG_PROTOCOL_PREFIX | TOOLTYPE_HOST_ARG_FROM_LAUNCHABLE_LOAD; +static bool sending; + +static bool plink_pw_setup(void *vctx, pollwrapper *pw) +{ + pollwrap_add_fd_rwx(pw, signalpipe[0], SELECT_R); + + if (!sending && + backend_connected(backend) && + backend_sendok(backend) && + backend_sendbuffer(backend) < MAX_STDIN_BACKLOG) { + /* If we're OK to send, then try to read from stdin. */ + pollwrap_add_fd_rwx(pw, STDIN_FILENO, SELECT_R); + } + + if (bufchain_size(&stdout_data) > 0) { + /* If we have data for stdout, try to write to stdout. */ + pollwrap_add_fd_rwx(pw, STDOUT_FILENO, SELECT_W); + } + + if (bufchain_size(&stderr_data) > 0) { + /* If we have data for stderr, try to write to stderr. */ + pollwrap_add_fd_rwx(pw, STDERR_FILENO, SELECT_W); + } + + return true; +} + +static void plink_pw_check(void *vctx, pollwrapper *pw) +{ + if (pollwrap_check_fd_rwx(pw, signalpipe[0], SELECT_R)) { + char c[1]; + struct winsize size; + if (read(signalpipe[0], c, 1) <= 0) + /* ignore error */; + /* ignore its value; it'll be `x' */ + if (ioctl(STDIN_FILENO, TIOCGWINSZ, (void *)&size) >= 0) + backend_size(backend, size.ws_col, size.ws_row); + } + + if (pollwrap_check_fd_rwx(pw, STDIN_FILENO, SELECT_R)) { + char buf[4096]; + int ret; + + if (backend_connected(backend)) { + ret = read(STDIN_FILENO, buf, sizeof(buf)); + noise_ultralight(NOISE_SOURCE_IOLEN, ret); + if (ret < 0) { + perror("stdin: read"); + exit(1); + } else if (ret == 0) { + backend_special(backend, SS_EOF, 0); + sending = false; /* send nothing further after this */ + } else { + if (local_tty) + from_tty(buf, ret); + else + backend_send(backend, buf, ret); + } + } + } + + if (pollwrap_check_fd_rwx(pw, STDOUT_FILENO, SELECT_W)) { + backend_unthrottle(backend, try_output(false)); + } + + if (pollwrap_check_fd_rwx(pw, STDERR_FILENO, SELECT_W)) { + backend_unthrottle(backend, try_output(true)); + } +} + +static bool plink_continue(void *vctx, bool found_any_fd, + bool ran_any_callback) +{ + if (!backend_connected(backend) && + bufchain_size(&stdout_data) == 0 && bufchain_size(&stderr_data) == 0) + return false; /* terminate main loop */ + return true; +} + int main(int argc, char **argv) { - bool sending; - int *fdlist; - int fd; - int i, fdstate; - size_t fdsize; int exitcode; bool errors; enum TriState sanitise_stdout = AUTO, sanitise_stderr = AUTO; bool use_subsystem = false; bool just_test_share_exists = false; - unsigned long now; struct winsize size; const struct BackendVtable *backvt; - fdlist = NULL; - fdsize = 0; /* * Initialise port and protocol to sensible defaults. (These * will be overridden by more or less anything.) @@ -875,157 +939,9 @@ int main(int argc, char **argv) atexit(cleanup_termios); seat_echoedit_update(plink_seat, 1, 1); sending = false; - now = GETTICKCOUNT(); - pollwrapper *pw = pollwrap_new(); + cli_main_loop(plink_pw_setup, plink_pw_check, plink_continue, NULL); - while (1) { - int rwx; - int ret; - unsigned long next; - - pollwrap_clear(pw); - - pollwrap_add_fd_rwx(pw, signalpipe[0], SELECT_R); - - if (!sending && - backend_connected(backend) && - backend_sendok(backend) && - backend_sendbuffer(backend) < MAX_STDIN_BACKLOG) { - /* If we're OK to send, then try to read from stdin. */ - pollwrap_add_fd_rwx(pw, STDIN_FILENO, SELECT_R); - } - - if (bufchain_size(&stdout_data) > 0) { - /* If we have data for stdout, try to write to stdout. */ - pollwrap_add_fd_rwx(pw, STDOUT_FILENO, SELECT_W); - } - - if (bufchain_size(&stderr_data) > 0) { - /* If we have data for stderr, try to write to stderr. */ - pollwrap_add_fd_rwx(pw, STDERR_FILENO, SELECT_W); - } - - /* Count the currently active fds. */ - i = 0; - for (fd = first_fd(&fdstate, &rwx); fd >= 0; - fd = next_fd(&fdstate, &rwx)) i++; - - /* Expand the fdlist buffer if necessary. */ - sgrowarray(fdlist, fdsize, i); - - /* - * Add all currently open fds to pw, and store them in fdlist - * as well. - */ - int fdcount = 0; - for (fd = first_fd(&fdstate, &rwx); fd >= 0; - fd = next_fd(&fdstate, &rwx)) { - fdlist[fdcount++] = fd; - pollwrap_add_fd_rwx(pw, fd, rwx); - } - - if (toplevel_callback_pending()) { - ret = pollwrap_poll_instant(pw); - } else if (run_timers(now, &next)) { - do { - unsigned long then; - long ticks; - - then = now; - now = GETTICKCOUNT(); - if (now - then > next - then) - ticks = 0; - else - ticks = next - now; - - bool overflow = false; - if (ticks > INT_MAX) { - ticks = INT_MAX; - overflow = true; - } - - ret = pollwrap_poll_timeout(pw, ticks); - if (ret == 0 && !overflow) - now = next; - else - now = GETTICKCOUNT(); - } while (ret < 0 && errno == EINTR); - } else { - ret = pollwrap_poll_endless(pw); - } - - if (ret < 0 && errno == EINTR) - continue; - - if (ret < 0) { - perror("poll"); - exit(1); - } - - for (i = 0; i < fdcount; i++) { - fd = fdlist[i]; - int rwx = pollwrap_get_fd_rwx(pw, fd); - /* - * We must process exceptional notifications before - * ordinary readability ones, or we may go straight - * past the urgent marker. - */ - if (rwx & SELECT_X) - select_result(fd, SELECT_X); - if (rwx & SELECT_R) - select_result(fd, SELECT_R); - if (rwx & SELECT_W) - select_result(fd, SELECT_W); - } - - if (pollwrap_check_fd_rwx(pw, signalpipe[0], SELECT_R)) { - char c[1]; - struct winsize size; - if (read(signalpipe[0], c, 1) <= 0) - /* ignore error */; - /* ignore its value; it'll be `x' */ - if (ioctl(STDIN_FILENO, TIOCGWINSZ, (void *)&size) >= 0) - backend_size(backend, size.ws_col, size.ws_row); - } - - if (pollwrap_check_fd_rwx(pw, STDIN_FILENO, SELECT_R)) { - char buf[4096]; - int ret; - - if (backend_connected(backend)) { - ret = read(STDIN_FILENO, buf, sizeof(buf)); - noise_ultralight(NOISE_SOURCE_IOLEN, ret); - if (ret < 0) { - perror("stdin: read"); - exit(1); - } else if (ret == 0) { - backend_special(backend, SS_EOF, 0); - sending = false; /* send nothing further after this */ - } else { - if (local_tty) - from_tty(buf, ret); - else - backend_send(backend, buf, ret); - } - } - } - - if (pollwrap_check_fd_rwx(pw, STDOUT_FILENO, SELECT_W)) { - backend_unthrottle(backend, try_output(false)); - } - - if (pollwrap_check_fd_rwx(pw, STDERR_FILENO, SELECT_W)) { - backend_unthrottle(backend, try_output(true)); - } - - run_toplevel_callbacks(); - - if (!backend_connected(backend) && - bufchain_size(&stdout_data) == 0 && - bufchain_size(&stderr_data) == 0) - break; /* we closed the connection */ - } exitcode = backend_exitcode(backend); if (exitcode < 0) { fprintf(stderr, "Remote process exit code unavailable\n"); diff --git a/unix/uxserver.c b/unix/uxserver.c index b851a817..cbb8a256 100644 --- a/unix/uxserver.c +++ b/unix/uxserver.c @@ -95,12 +95,6 @@ char *x_get_default(const char *key) return NULL; /* this is a stub */ } -/* - * Our selects are synchronous, so these functions are empty stubs. - */ -uxsel_id *uxsel_input_add(int fd, int rwx) { return NULL; } -void uxsel_input_remove(uxsel_id *id) { } - void old_keyfile_warning(void) { } void timer_change_notify(unsigned long next) @@ -519,11 +513,6 @@ static const PlugVtable server_plugvt = { int main(int argc, char **argv) { - int *fdlist; - int fd; - int i, fdstate; - size_t fdsize; - unsigned long now; int listen_port = -1; ssh_key **hostkeys = NULL; @@ -598,7 +587,7 @@ int main(int argc, char **argv) sfree(uk->comment); sfree(uk); - for (i = 0; i < nhostkeys; i++) + for (int i = 0; i < nhostkeys; i++) if (ssh_key_alg(hostkeys[i]) == ssh_key_alg(key)) { fprintf(stderr, "%s: host key '%s' duplicates key " "type %s\n", appname, val, @@ -792,9 +781,6 @@ int main(int argc, char **argv) exit(1); } - fdlist = NULL; - fdsize = 0; - random_ref(); /* @@ -832,90 +818,8 @@ int main(int argc, char **argv) log_to_stderr(inst->id, "speaking SSH on stdio"); } - now = GETTICKCOUNT(); + cli_main_loop(cliloop_no_pw_setup, cliloop_no_pw_check, + cliloop_always_continue, NULL); - pollwrapper *pw = pollwrap_new(); - while (!finished) { - int rwx; - int ret; - unsigned long next; - - pollwrap_clear(pw); - - /* Count the currently active fds. */ - i = 0; - for (fd = first_fd(&fdstate, &rwx); fd >= 0; - fd = next_fd(&fdstate, &rwx)) i++; - - /* Expand the fdlist buffer if necessary. */ - sgrowarray(fdlist, fdsize, i); - - /* - * Add all currently open fds to the select sets, and store - * them in fdlist as well. - */ - int fdcount = 0; - for (fd = first_fd(&fdstate, &rwx); fd >= 0; - fd = next_fd(&fdstate, &rwx)) { - fdlist[fdcount++] = fd; - pollwrap_add_fd_rwx(pw, fd, rwx); - } - - if (toplevel_callback_pending()) { - ret = pollwrap_poll_instant(pw); - } else if (run_timers(now, &next)) { - do { - unsigned long then; - long ticks; - - then = now; - now = GETTICKCOUNT(); - if (now - then > next - then) - ticks = 0; - else - ticks = next - now; - - bool overflow = false; - if (ticks > INT_MAX) { - ticks = INT_MAX; - overflow = true; - } - - ret = pollwrap_poll_timeout(pw, ticks); - if (ret == 0 && !overflow) - now = next; - else - now = GETTICKCOUNT(); - } while (ret < 0 && errno == EINTR); - } else { - ret = pollwrap_poll_endless(pw); - } - - if (ret < 0 && errno == EINTR) - continue; - - if (ret < 0) { - perror("poll"); - exit(1); - } - - for (i = 0; i < fdcount; i++) { - fd = fdlist[i]; - int rwx = pollwrap_get_fd_rwx(pw, fd); - /* - * We must process exceptional notifications before - * ordinary readability ones, or we may go straight - * past the urgent marker. - */ - if (rwx & SELECT_X) - select_result(fd, SELECT_X); - if (rwx & SELECT_R) - select_result(fd, SELECT_R); - if (rwx & SELECT_W) - select_result(fd, SELECT_W); - } - - run_toplevel_callbacks(); - } - exit(0); + return 0; } diff --git a/unix/uxsftp.c b/unix/uxsftp.c index b0dbd3d1..89a81c92 100644 --- a/unix/uxsftp.c +++ b/unix/uxsftp.c @@ -21,13 +21,6 @@ #include #endif -/* - * In PSFTP our selects are synchronous, so these functions are - * empty stubs. - */ -uxsel_id *uxsel_input_add(int fd, int rwx) { return NULL; } -void uxsel_input_remove(uxsel_id *id) { } - char *x_get_default(const char *key) { return NULL; /* this is a stub */ @@ -465,116 +458,55 @@ char *dir_file_cat(const char *dir, const char *file) /* * Do a select() between all currently active network fds and - * optionally stdin. + * optionally stdin, using cli_main_loop. */ + +struct ssh_sftp_mainloop_ctx { + bool include_stdin, no_fds_ok; + int toret; +}; +static bool ssh_sftp_pw_setup(void *vctx, pollwrapper *pw) +{ + struct ssh_sftp_mainloop_ctx *ctx = (struct ssh_sftp_mainloop_ctx *)vctx; + int fdstate, rwx; + + if (!ctx->no_fds_ok && !toplevel_callback_pending() && + first_fd(&fdstate, &rwx) < 0) { + ctx->toret = -1; + return false; /* terminate cli_main_loop */ + } + + if (ctx->include_stdin) + pollwrap_add_fd_rwx(pw, 0, SELECT_R); + + return true; +} +static void ssh_sftp_pw_check(void *vctx, pollwrapper *pw) +{ + struct ssh_sftp_mainloop_ctx *ctx = (struct ssh_sftp_mainloop_ctx *)vctx; + + if (ctx->include_stdin && pollwrap_check_fd_rwx(pw, 0, SELECT_R)) + ctx->toret = 1; +} +static bool ssh_sftp_mainloop_continue(void *vctx, bool found_any_fd, + bool ran_any_callback) +{ + struct ssh_sftp_mainloop_ctx *ctx = (struct ssh_sftp_mainloop_ctx *)vctx; + if (ctx->toret != 0 || found_any_fd || ran_any_callback) + return false; /* finish the loop */ + return true; +} static int ssh_sftp_do_select(bool include_stdin, bool no_fds_ok) { - int i, *fdlist; - size_t fdsize; - int fd, fdcount, fdstate, rwx, ret; - unsigned long now = GETTICKCOUNT(); - unsigned long next; - bool done_something = false; + struct ssh_sftp_mainloop_ctx ctx[1]; + ctx->include_stdin = include_stdin; + ctx->no_fds_ok = no_fds_ok; + ctx->toret = 0; - fdlist = NULL; - fdsize = 0; + cli_main_loop(ssh_sftp_pw_setup, ssh_sftp_pw_check, + ssh_sftp_mainloop_continue, ctx); - pollwrapper *pw = pollwrap_new(); - - do { - - /* Count the currently active fds. */ - i = 0; - for (fd = first_fd(&fdstate, &rwx); fd >= 0; - fd = next_fd(&fdstate, &rwx)) i++; - - if (i < 1 && !no_fds_ok && !toplevel_callback_pending()) { - pollwrap_free(pw); - return -1; /* doom */ - } - - /* Expand the fdlist buffer if necessary. */ - sgrowarray(fdlist, fdsize, i); - - pollwrap_clear(pw); - - /* - * Add all currently open fds to the select sets, and store - * them in fdlist as well. - */ - fdcount = 0; - for (fd = first_fd(&fdstate, &rwx); fd >= 0; - fd = next_fd(&fdstate, &rwx)) { - fdlist[fdcount++] = fd; - pollwrap_add_fd_rwx(pw, fd, rwx); - } - - if (include_stdin) - pollwrap_add_fd_rwx(pw, 0, SELECT_R); - - if (toplevel_callback_pending()) { - ret = pollwrap_poll_instant(pw); - if (ret == 0) - done_something |= run_toplevel_callbacks(); - } else if (run_timers(now, &next)) { - do { - unsigned long then; - long ticks; - - then = now; - now = GETTICKCOUNT(); - if (now - then > next - then) - ticks = 0; - else - ticks = next - now; - - bool overflow = false; - if (ticks > INT_MAX) { - ticks = INT_MAX; - overflow = true; - } - - ret = pollwrap_poll_timeout(pw, ticks); - if (ret == 0 && !overflow) - now = next; - else - now = GETTICKCOUNT(); - } while (ret < 0 && errno == EINTR); - } else { - do { - ret = pollwrap_poll_endless(pw); - } while (ret < 0 && errno == EINTR); - } - } while (ret == 0 && !done_something); - - if (ret < 0) { - perror("poll"); - exit(1); - } - - for (i = 0; i < fdcount; i++) { - fd = fdlist[i]; - int rwx = pollwrap_get_fd_rwx(pw, fd); - /* - * We must process exceptional notifications before - * ordinary readability ones, or we may go straight - * past the urgent marker. - */ - if (rwx & SELECT_X) - select_result(fd, SELECT_X); - if (rwx & SELECT_R) - select_result(fd, SELECT_R); - if (rwx & SELECT_W) - select_result(fd, SELECT_W); - } - - sfree(fdlist); - - run_toplevel_callbacks(); - - int toret = pollwrap_check_fd_rwx(pw, 0, SELECT_R) ? 1 : 0; - pollwrap_free(pw); - return toret; + return ctx->toret; } /*