details: https://hg.nginx.org/njs/rev/8163612eb950 branches: changeset: 2006:8163612eb950 user: Dmitry Volyntsev <xei...@nginx.com> date: Thu Nov 17 20:38:23 2022 -0800 description: Shell: added signal support in CLI.
diffstat: src/njs_shell.c | 184 +++++++++++++++++++++++++++++++++++++++++++-------- test/shell_test.exp | 32 ++++---- 2 files changed, 169 insertions(+), 47 deletions(-) diffs (414 lines): diff -r b3eeac9ee9f4 -r 8163612eb950 src/njs_shell.c --- a/src/njs_shell.c Wed Nov 16 18:47:36 2022 -0800 +++ b/src/njs_shell.c Thu Nov 17 20:38:23 2022 -0800 @@ -10,6 +10,8 @@ #if (!defined NJS_FUZZER_TARGET && defined NJS_HAVE_READLINE) #include <locale.h> +#include <signal.h> +#include <sys/select.h> #if (NJS_HAVE_EDITLINE) #include <editline/readline.h> #elif (NJS_HAVE_EDIT_READLINE) @@ -24,13 +26,15 @@ #endif +typedef void (*njs_console_output_pt)(njs_vm_t *vm, njs_int_t ret); + + typedef struct { uint8_t disassemble; uint8_t denormals; uint8_t interactive; uint8_t module; uint8_t quiet; - uint8_t silent; uint8_t sandbox; uint8_t safe; uint8_t version; @@ -90,10 +94,12 @@ typedef struct { static njs_int_t njs_console_init(njs_vm_t *vm, njs_console_t *console); +static void njs_console_output(njs_vm_t *vm, njs_int_t ret); static njs_int_t njs_externals_init(njs_vm_t *vm); static njs_vm_t *njs_create_vm(njs_opts_t *opts, njs_vm_opt_t *vm_options); -static njs_int_t njs_process_script(njs_vm_t *vm, njs_opts_t *opts, - void *runtime, const njs_str_t *script); +static void njs_process_output(njs_vm_t *vm, njs_int_t ret); +static njs_int_t njs_process_script(njs_vm_t *vm, void *runtime, + const njs_str_t *script); #ifndef NJS_FUZZER_TARGET @@ -243,6 +249,15 @@ main(int argc, char **argv) njs_str_t command; njs_vm_opt_t vm_options; + static uintptr_t uptr[] = { + (uintptr_t) njs_console_output, + }; + + static njs_vm_meta_t metas = { + .size = njs_nitems(uptr), + .values = uptr + }; + njs_memzero(&opts, sizeof(njs_opts_t)); opts.interactive = 1; @@ -287,6 +302,7 @@ main(int argc, char **argv) vm_options.ops = &njs_console_ops; vm_options.addons = njs_console_addon_modules; + vm_options.metas = &metas; vm_options.external = &njs_console; vm_options.argv = opts.argv; vm_options.argc = opts.argc; @@ -311,7 +327,7 @@ main(int argc, char **argv) if (vm != NULL) { command.start = (u_char *) opts.command; command.length = njs_strlen(opts.command); - ret = njs_process_script(vm, &opts, vm_options.external, &command); + ret = njs_process_script(vm, vm->external, &command); njs_vm_destroy(vm); } @@ -659,7 +675,7 @@ njs_process_file(njs_opts_t *opts, njs_v } } - ret = njs_process_script(vm, opts, vm_options->external, &script); + ret = njs_process_script(vm, vm_options->external, &script); if (ret != NJS_OK) { ret = NJS_ERROR; goto done; @@ -696,18 +712,26 @@ LLVMFuzzerTestOneInput(const uint8_t* da njs_str_t script; njs_vm_opt_t vm_options; + static uintptr_t uptr[] = { + (uintptr_t) NULL, + }; + + static njs_vm_meta_t metas = { + .size = njs_nitems(uptr), + .values = uptr + }; + if (size == 0) { return 0; } njs_memzero(&opts, sizeof(njs_opts_t)); - opts.silent = 1; - njs_vm_opt_init(&vm_options); vm_options.init = 1; vm_options.backtrace = 0; + vm_options.metas = &metas; vm_options.ops = &njs_console_ops; vm = njs_create_vm(&opts, &vm_options); @@ -716,7 +740,7 @@ LLVMFuzzerTestOneInput(const uint8_t* da script.length = size; script.start = (u_char *) data; - (void) njs_process_script(vm, &opts, NULL, &script); + (void) njs_process_script(vm, NULL, &script); njs_vm_destroy(vm); } @@ -853,14 +877,10 @@ njs_create_vm(njs_opts_t *opts, njs_vm_o static void -njs_output(njs_opts_t *opts, njs_vm_t *vm, njs_int_t ret) +njs_console_output(njs_vm_t *vm, njs_int_t ret) { njs_str_t out; - if (opts->silent) { - return; - } - if (ret == NJS_OK) { if (njs_vm_retval_dump(vm, &out, 1) != NJS_OK) { njs_stderror("Shell:failed to get retval from VM\n"); @@ -917,8 +937,7 @@ njs_process_events(void *runtime) static njs_int_t -njs_process_script(njs_vm_t *vm, njs_opts_t *opts, void *runtime, - const njs_str_t *script) +njs_process_script(njs_vm_t *vm, void *runtime, const njs_str_t *script) { u_char *start, *end; njs_int_t ret; @@ -938,14 +957,15 @@ njs_process_script(njs_vm_t *vm, njs_opt } } - njs_output(opts, vm, ret); + njs_process_output(vm, ret); - if (!opts->interactive && ret == NJS_ERROR) { + if (!vm->options.interactive && ret == NJS_ERROR) { return NJS_ERROR; } for ( ;; ) { if (!njs_vm_pending(vm) && !njs_vm_unhandled_rejection(vm)) { + ret = NJS_OK; break; } @@ -967,9 +987,9 @@ njs_process_script(njs_vm_t *vm, njs_opt ret = njs_vm_run(vm); if (ret == NJS_ERROR) { - njs_output(opts, vm, ret); + njs_process_output(vm, ret); - if (!opts->interactive) { + if (!vm->options.interactive) { return NJS_ERROR; } } @@ -979,13 +999,64 @@ njs_process_script(njs_vm_t *vm, njs_opt } +static void +njs_process_output(njs_vm_t *vm, njs_int_t ret) +{ + njs_console_output_pt pt; + + pt = (njs_console_output_pt) njs_vm_meta(vm, 0); + + if (pt != NULL) { + pt(vm, ret); + } +} + + #if (!defined NJS_FUZZER_TARGET && defined NJS_HAVE_READLINE) + +volatile sig_atomic_t njs_running; +volatile sig_atomic_t njs_sigint_count; +volatile sig_atomic_t njs_sigint_received; + + +static void +njs_cb_line_handler(char *line_in) +{ + njs_int_t ret; + njs_str_t line; + + if (line_in == NULL || strcmp(line_in, ".exit") == 0) { + njs_running = NJS_DONE; + return; + } + + njs_sigint_count = 0; + + line.start = (u_char *) line_in; + line.length = njs_strlen(line.start); + + if (line.length == 0) { + return; + } + + add_history((char *) line.start); + + ret = njs_process_script(njs_console.vm, &njs_console, &line); + if (ret == NJS_ERROR) { + njs_running = NJS_ERROR; + } + + free(line.start); +} + + static njs_int_t njs_interactive_shell(njs_opts_t *opts, njs_vm_opt_t *vm_options) { + fd_set fds; njs_vm_t *vm; - njs_str_t line; + njs_int_t ret; if (njs_editline_init() != NJS_OK) { njs_stderror("failed to init completions\n"); @@ -1003,27 +1074,64 @@ njs_interactive_shell(njs_opts_t *opts, njs_printf("v.<Tab> -> the properties and prototype methods of v.\n\n"); } - for ( ;; ) { - line.start = (u_char *) readline(">> "); - if (line.start == NULL) { + rl_callback_handler_install(">> ", njs_cb_line_handler); + + njs_running = NJS_OK; + + while (njs_running == NJS_OK) { + FD_ZERO(&fds); + FD_SET(fileno(rl_instream), &fds); + + ret = select(FD_SETSIZE, &fds, NULL, NULL, NULL); + if (ret < 0 && errno != EINTR) { + njs_stderror("select() failed\n"); + njs_running = NJS_ERROR; break; } - line.length = njs_strlen(line.start); + if (njs_sigint_received) { + if (njs_sigint_count > 1) { + njs_running = NJS_DONE; + break; + } + + if (rl_end != 0) { + njs_printf("\n"); + + njs_sigint_count = 0; - if (line.length != 0) { - add_history((char *) line.start); + } else { + njs_printf("(To exit, press Ctrl+C again or Ctrl+D " + "or type .exit)\n"); - njs_process_script(vm, opts, vm_options->external, &line); + njs_sigint_count = 1; + } + + rl_point = rl_end = 0; + rl_on_new_line(); + rl_redisplay(); + + njs_sigint_received = 0; } - /* editline allocs a new buffer every time. */ - free(line.start); + if (ret < 0) { + continue; + } + + if (FD_ISSET(fileno(rl_instream), &fds)) { + rl_callback_read_char(); + } + } + + rl_callback_handler_remove(); + + if (njs_running == NJS_DONE) { + njs_printf("exiting\n"); } njs_vm_destroy(vm); - return NJS_OK; + return njs_running == NJS_DONE ? NJS_OK : njs_running; } @@ -1036,6 +1144,20 @@ njs_completion_handler(const char *text, } +static void +njs_signal_handler(int signal) +{ + switch (signal) { + case SIGINT: + njs_sigint_received = 1; + njs_sigint_count += 1; + break; + default: + break; + } +} + + static njs_int_t njs_editline_init(void) { @@ -1045,6 +1167,8 @@ njs_editline_init(void) setlocale(LC_ALL, ""); + signal(SIGINT, njs_signal_handler); + return NJS_OK; } diff -r b3eeac9ee9f4 -r 8163612eb950 test/shell_test.exp --- a/test/shell_test.exp Wed Nov 16 18:47:36 2022 -0800 +++ b/test/shell_test.exp Thu Nov 17 20:38:23 2022 -0800 @@ -36,8 +36,8 @@ proc njs_test {body {opts ""}} { expect [lindex $pair 1] } - # Ctrl-C - send \x03 + send "\n" + send ".exit\r\n" expect eof } @@ -62,10 +62,11 @@ njs_test { } # Global completions, no -njs_test { - {"\t\tn" - "\a\r\nDisplay all*possibilities? (y or n)*>> "} -} +# Disabled: readline does not support it in callback mode +# njs_test { +# {"\t\tn" +# "\a\r\nDisplay all*possibilities? (y or n)*>> "} +# } # Global completions, yes njs_test { @@ -87,13 +88,10 @@ njs_test { "Ma\a*th"} } -# FIXME: completions for external objects -# are not supported - -# njs_test { -# {"conso\t" -# "conso\a*le"} -# } + njs_test { + {"conso\t" + "conso\a*le"} + } # Global completions, multiple partial match njs_test { @@ -145,10 +143,10 @@ njs_test { # Global completions, global vars njs_test { - {"var a = 1; var aa = 2\r\n" - "var a = 1; var aa = 2\r\nundefined\r\n>> "} - {"a\t\t" - "a*aa*arguments*await"} + {"var AA = 1; var AAA = 2\r\n" + "var AA = 1; var AAA = 2\r\nundefined\r\n>> "} + {"AA\t\t" + "AA*AAA*"} } # z*z is WORKAROUND for libedit-20170329.3.1-r3 _______________________________________________ nginx-devel mailing list -- nginx-devel@nginx.org To unsubscribe send an email to nginx-devel-le...@nginx.org