From d85780ce9c65b6a38d7149c71e93e91d904f66a2 Mon Sep 17 00:00:00 2001 From: Sebastian Harl Date: Tue, 4 Feb 2014 22:57:46 +0100 Subject: [PATCH] sysdb: Use the asynchronous readline interface; handle asynch server replies. For this, setup the terminal in non-canonical, raw mode in order to be able to handle each single character input. This allows to handle user input and asynchronous server replies at the same time but without the need to give up sequential operation (no threads are required for the asynchronous operation). Instead, select() is used to do the input multiplexing allowing to easily handle different output. --- src/tools/sysdb/input.c | 121 ++++++++++++++++++++++++++++++++++------ src/tools/sysdb/input.h | 4 +- 2 files changed, 107 insertions(+), 18 deletions(-) diff --git a/src/tools/sysdb/input.c b/src/tools/sysdb/input.c index e08ad5b..c42390f 100644 --- a/src/tools/sysdb/input.c +++ b/src/tools/sysdb/input.c @@ -34,11 +34,16 @@ #include "utils/strbuf.h" +#include + #include #include #include +#include +#include + #if HAVE_EDITLINE_READLINE_H # include # if HAVE_EDITLINE_HISTORY_H @@ -62,32 +67,109 @@ sdb_input_t *sysdb_input = NULL; +/* + * private variables + */ + +static struct termios orig_term_attrs; + /* * private helper functions */ -static size_t -input_readline(void) +static void +reset_term_attrs(void) { - const char *prompt = "sysdb=> "; - char *line; + tcsetattr(STDIN_FILENO, TCSANOW, &orig_term_attrs); +} /* reset_term_attrs */ + +static void +term_rawmode(void) +{ + struct termios attrs; + + /* setup terminal to operate in non-canonical mode + * and single character input */ + memset(&orig_term_attrs, 0, sizeof(orig_term_attrs)); + tcgetattr(STDIN_FILENO, &orig_term_attrs); + atexit(reset_term_attrs); + + memset(&attrs, 0, sizeof(attrs)); + tcgetattr(STDIN_FILENO, &attrs); + attrs.c_lflag &= (tcflag_t)(~ICANON); + attrs.c_cc[VMIN] = 1; + tcsetattr(STDIN_FILENO, TCSANOW, &attrs); +} /* term_rawmode */ + +static void +handle_input(char *line) +{ + if (! line) { + sysdb_input->eof = 1; + return; + } + sdb_strbuf_append(sysdb_input->input, line); + sdb_strbuf_append(sysdb_input->input, "\n"); + free(line); + + rl_callback_handler_remove(); +} /* handle_input */ + +/* wait for a new line of data to be available */ +static ssize_t +input_readline(void) +{ size_t len; + fd_set fds; + int client_fd; + + const char *prompt = "sysdb=> "; + if (sysdb_input->query_len) prompt = "sysdb-> "; - line = readline(prompt); + rl_callback_handler_install(prompt, handle_input); + client_fd = sdb_client_sockfd(sysdb_input->client); - if (! line) - return 0; + len = sdb_strbuf_len(sysdb_input->input); + while ((sdb_strbuf_len(sysdb_input->input) == len) + && (! sysdb_input->eof)) { + int n; - len = strlen(line) + 1; + /* XXX: some versions of libedit don't properly reset the terminal in + * rl_callback_read_char(); detect those versions */ + term_rawmode(); - sdb_strbuf_append(sysdb_input->input, line); - sdb_strbuf_append(sysdb_input->input, "\n"); - free(line); - return len; + FD_ZERO(&fds); + FD_SET(STDIN_FILENO, &fds); + FD_SET(client_fd, &fds); + + n = select(client_fd + 1, &fds, NULL, NULL, /* timeout = */ NULL); + if (n < 0) + return (ssize_t)n; + else if (! n) + continue; + + /* handle user input with highest priority */ + if (FD_ISSET(STDIN_FILENO, &fds)) { + rl_callback_read_char(); + continue; + } + + if (! FD_ISSET(client_fd, &fds)) + continue; + + /* some response / error message from the server pending */ + /* XXX: clear current line */ + printf("\n"); + sdb_command_print_reply(sysdb_input); + rl_forced_update_display(); + } + + /* new data available */ + return (ssize_t)(sdb_strbuf_len(sysdb_input->input) - len); } /* input_readline */ /* @@ -99,6 +181,11 @@ sdb_input_init(sdb_input_t *input) { /* register input handler */ sysdb_input = input; + + if (! isatty(STDIN_FILENO)) + return -1; + + term_rawmode(); return 0; } /* sdb_input_init */ @@ -111,12 +198,12 @@ sdb_input_readline(char *buf, int *n_chars, size_t max_chars) len = sdb_strbuf_len(sysdb_input->input) - sysdb_input->tokenizer_pos; if (! len) { - size_t n = input_readline(); - if (! n) { + ssize_t n = input_readline(); + if (n <= 0) { *n_chars = 0; /* YY_NULL */ - return 0; + return n; } - len += n; + len += (size_t)n; } len = (len < max_chars) ? len : max_chars; @@ -130,7 +217,7 @@ sdb_input_readline(char *buf, int *n_chars, size_t max_chars) } /* sdb_input_readline */ int -sdb_input_exec_query() +sdb_input_exec_query(void) { char *query = sdb_command_exec(sysdb_input); diff --git a/src/tools/sysdb/input.h b/src/tools/sysdb/input.h index ef60bf4..80f1bb5 100644 --- a/src/tools/sysdb/input.h +++ b/src/tools/sysdb/input.h @@ -37,9 +37,11 @@ typedef struct { sdb_strbuf_t *input; size_t tokenizer_pos; size_t query_len; + + _Bool eof; } sdb_input_t; -#define SDB_INPUT_INIT { NULL, NULL, 0, 0 } +#define SDB_INPUT_INIT { NULL, NULL, 0, 0, 0 } /* * sysdb_input: -- 2.30.2