From b13335b841d9fc482f532f441d64e7729594d204 Mon Sep 17 00:00:00 2001 From: vax-r Date: Thu, 29 Feb 2024 19:33:19 +0800 Subject: [PATCH] Introduce eventmux callback function for linenoise In the previous version of console implementation, we tried to integrate tiny-web-server to enable the ability of processing commands from web requests. As the result, the package linenoise which is responsible for command-line auto-complete needs to be disabled during the running time of tiny-web-server. Because the main loop in "line_edit()" function in linenoise doesn't have the ability to handle web requests correctly. When we start the web server, we use "cmd_select" in console.c and use "select" function to monitor web socket file descriptor and stdin_fd at the same time. I re-design the control flow of web request and command-line input by implement the function "web_eventmux()", and register it as a hook function of type "line_eventmux_callback_t" inside linenoise package. As the result, we can utilize function inside the main loop of linenoise which is located inside the function "line_edit()". "web_eventmux()" is a function which use the function "select()" to monitor both input file descriptor and web file descriptor and modify "line_edit()" to use "eventmux_callback()" so we can process command-line input as normal and provide the command-line auto-complete feature alongside with the abitlity to deal with inputs from different input sources, such as web request. One may wonder why don't we simply modify the function "line_edit()", the reason is that linenoise is an upstream package so we only want to do the miminal changes to this package. --- console.c | 54 +++++++++++------------------------------------------ linenoise.c | 23 ++++++++++++++++++++++- linenoise.h | 4 +++- web.c | 40 +++++++++++++++++++++++++++++++++++++++ web.h | 4 +++- 5 files changed, 79 insertions(+), 46 deletions(-) diff --git a/console.c b/console.c index 4470bbd27..009ce5c8d 100644 --- a/console.c +++ b/console.c @@ -405,6 +405,7 @@ static bool do_web(int argc, char *argv[]) web_fd = web_open(port); if (web_fd > 0) { printf("listen on port %d, fd is %d\n", port, web_fd); + line_set_eventmux_callback(web_eventmux); use_linenoise = false; } else { perror("ERROR"); @@ -560,13 +561,13 @@ static int cmd_select(int nfds, fd_set *exceptfds, struct timeval *timeout) { - int infd; fd_set local_readset; if (cmd_done()) return 0; if (!block_flag) { + int infd; /* Process any commands in input buffer */ if (!readfds) readfds = &local_readset; @@ -581,51 +582,18 @@ static int cmd_select(int nfds, FD_SET(web_fd, readfds); if (infd == STDIN_FILENO && prompt_flag) { - printf("%s", prompt); + char *cmdline = linenoise(prompt); + if (cmdline) + interpret_cmd(cmdline); fflush(stdout); prompt_flag = true; + } else if (infd != STDIN_FILENO) { + char *cmdline = readline(); + if (cmdline) + interpret_cmd(cmdline); } - - if (infd >= nfds) - nfds = infd + 1; - if (web_fd >= nfds) - nfds = web_fd + 1; } - if (nfds == 0) - return 0; - - int result = select(nfds, readfds, writefds, exceptfds, timeout); - if (result <= 0) - return result; - - infd = buf_stack->fd; - if (readfds && FD_ISSET(infd, readfds)) { - /* Commandline input available */ - FD_CLR(infd, readfds); - result--; - - set_echo(0); - char *cmdline = readline(); - if (cmdline) - interpret_cmd(cmdline); - } else if (readfds && FD_ISSET(web_fd, readfds)) { - FD_CLR(web_fd, readfds); - result--; - struct sockaddr_in clientaddr; - socklen_t clientlen = sizeof(clientaddr); - web_connfd = - accept(web_fd, (struct sockaddr *) &clientaddr, &clientlen); - - char *p = web_recv(web_connfd, &clientaddr); - char *buffer = "HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\n\r\n"; - web_send(web_connfd, buffer); - - if (p) - interpret_cmd(p); - free(p); - close(web_connfd); - } - return result; + return 0; } bool finish_cmd() @@ -706,4 +674,4 @@ bool run_console(char *infile_name) } return err_cnt == 0; -} +} \ No newline at end of file diff --git a/linenoise.c b/linenoise.c index aa1f362e8..eee1cc55c 100644 --- a/linenoise.c +++ b/linenoise.c @@ -124,6 +124,7 @@ static char *unsupported_term[] = {"dumb", "cons25", "emacs", NULL}; static line_completion_callback_t *completion_callback = NULL; static line_hints_callback_t *hints_callback = NULL; static line_free_hints_callback_t *free_hints_callback = NULL; +static line_eventmux_callback_t *eventmux_callback = NULL; static struct termios orig_termios; /* In order to restore at exit.*/ static bool maskmode = false; /* Show "***" instead of input. For passwords. */ @@ -472,6 +473,20 @@ void line_set_free_hints_callback(line_free_hints_callback_t *fn) free_hints_callback = fn; } +/* Register a function to perform I/O multiplexing to monitor multiple file + * descriptor from different input at the same time, so we can allow the ability + * of receiving commands from different input sources and still provides the + * command-line auto-complete feature of this package. For example, the main + * loop of this package can only deal with standard input file descriptor + * originally. When this callback function is invoked, it allows the main loop + * of this package to deal with multiple file descriptors from different input + * alongside with the origin feature to deal with the standard input. + */ +void line_set_eventmux_callback(line_eventmux_callback_t *fn) +{ + eventmux_callback = fn; +} + /* This function is used by the callback function registered by the user * in order to add completion options given the input string when the * user typed . @@ -932,6 +947,12 @@ static int line_edit(int stdin_fd, int nread; char seq[5]; + if (eventmux_callback != NULL) { + int result = eventmux_callback(l.buf); + if (result != 0) + return result; + } + nread = read(l.ifd, &c, 1); if (nread <= 0) return l.len; @@ -1362,4 +1383,4 @@ int line_history_load(const char *filename) } fclose(fp); return 0; -} +} \ No newline at end of file diff --git a/linenoise.h b/linenoise.h index 4468e8bbd..86b73d7e6 100644 --- a/linenoise.h +++ b/linenoise.h @@ -54,9 +54,11 @@ typedef struct { typedef void(line_completion_callback_t)(const char *, line_completions_t *); typedef char *(line_hints_callback_t)(const char *, int *color, int *bold); typedef void(line_free_hints_callback_t)(void *); +typedef int(line_eventmux_callback_t)(char *); void line_set_completion_callback(line_completion_callback_t *); void line_set_hints_callback(line_hints_callback_t *); void line_set_free_hints_callback(line_free_hints_callback_t *); +void line_set_eventmux_callback(line_eventmux_callback_t *); void line_add_completion(line_completions_t *, const char *); /* clang-format on */ @@ -74,4 +76,4 @@ void line_mask_mode_disable(void); } #endif -#endif /* __LINENOISE_H */ +#endif /* __LINENOISE_H */ \ No newline at end of file diff --git a/web.c b/web.c index 0994a47f5..5c573040d 100644 --- a/web.c +++ b/web.c @@ -23,6 +23,8 @@ #define TCP_CORK TCP_NOPUSH #endif +static int server_fd; + typedef struct { int fd; /* descriptor for this buf */ int count; /* unread byte in this buf */ @@ -153,6 +155,9 @@ int web_open(int port) /* Make it a listening socket ready to accept connection requests */ if (listen(listenfd, LISTENQ) < 0) return -1; + + server_fd = listenfd; + return listenfd; } @@ -228,3 +233,38 @@ char *web_recv(int fd, struct sockaddr_in *clientaddr) return ret; } + +int web_eventmux(char *buf) +{ + fd_set listenset; + + FD_ZERO(&listenset); + FD_SET(STDIN_FILENO, &listenset); + int max_fd = STDIN_FILENO; + if (server_fd > 0) { + FD_SET(server_fd, &listenset); + max_fd = max_fd > server_fd ? max_fd : server_fd; + } + int result = select(max_fd + 1, &listenset, NULL, NULL, NULL); + if (result < 0) + return -1; + + if (server_fd > 0 && FD_ISSET(server_fd, &listenset)) { + FD_CLR(server_fd, &listenset); + struct sockaddr_in clientaddr; + socklen_t clientlen = sizeof(clientaddr); + int web_connfd = + accept(server_fd, (struct sockaddr *) &clientaddr, &clientlen); + + char *p = web_recv(web_connfd, &clientaddr); + char *buffer = "HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\n\r\n"; + web_send(web_connfd, buffer); + strncpy(buf, p, strlen(p) + 1); + free(p); + close(web_connfd); + return strlen(buf); + } + + FD_CLR(STDIN_FILENO, &listenset); + return 0; +} \ No newline at end of file diff --git a/web.h b/web.h index b04bc179d..3f5982a04 100644 --- a/web.h +++ b/web.h @@ -9,4 +9,6 @@ char *web_recv(int fd, struct sockaddr_in *clientaddr); void web_send(int out_fd, char *buffer); -#endif +int web_eventmux(char *buf); + +#endif \ No newline at end of file