From ecac027d18de7931d5b0ca929c66a82207453b68 Mon Sep 17 00:00:00 2001 From: Olof hagsand Date: Sun, 10 Apr 2022 10:31:03 +0200 Subject: [PATCH] CLI argument to shell example callback Save state of netconf chunked framing between inputs Treat EBADF in internal protocol (triggered by freebsd+notifications) Test: chunked encoding: use printf instead of echo --- CHANGELOG.md | 2 +- apps/cli/cli_common.c | 23 +++++++++++++++++++---- apps/netconf/netconf_main.c | 19 +++++++++++++++++-- example/main/example_cli.cli | 4 +++- lib/src/clixon_proto.c | 3 ++- test/lib.sh | 9 ++------- test/test_autocli_strict_expand.sh | 2 +- 7 files changed, 45 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6a08b110..bb9e0f56 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -73,7 +73,7 @@ Users may have to change how they access the system * There used to be some cornercases where function-names could not be used as nodes * For example, `node()` is a nodetest, so `/node/` caused an error. * In the grammar these include: axisnames, nodetests, functionnames - * The NCNames vs functionnames is now impölemented according to the lexical structure section + * The NCNames vs functionnames is now implemented according to the lexical structure section * [provide support for load config of cli format along with json and xml format as save config is supported for all 3 formats](https://github.com/clicon/clixon/issues/320) * [prevent clixon-restconf@2021-05-20.yang module from loading](https://github.com/clicon/clixon/issues/318) * Instead of always loading it, load it to datastore YANGs only if `CLICON_BACKEND_RESTCONF_PROCESS` is `true` diff --git a/apps/cli/cli_common.c b/apps/cli/cli_common.c index ff2e8001..3e327cdf 100644 --- a/apps/cli/cli_common.c +++ b/apps/cli/cli_common.c @@ -556,6 +556,9 @@ cli_set_mode(clicon_handle h, /*! Start bash from cli callback * Typical usage: shell("System Bash") , cli_start_shell(); + * @param[in] h Clixon handle + * @param[in] cvv Vector of command variables + * @param[in] argv [], defaults to "sh" */ int cli_start_shell(clicon_handle h, @@ -563,6 +566,7 @@ cli_start_shell(clicon_handle h, cvec *argv) { char *cmd; + char *shcmd = "sh"; struct passwd *pw; int retval = -1; char bcmd[128]; @@ -570,6 +574,14 @@ cli_start_shell(clicon_handle h, sigset_t oldsigset; struct sigaction oldsigaction[32] = {0,}; + if (cvec_len(argv) > 1){ + clicon_err(OE_PLUGIN, EINVAL, "Received %d arguments. Expected: []", + cvec_len(argv)); + goto done; + } + if (cvec_len(argv) == 1){ + shcmd = cv_string_get(cvec_i(argv, 0)); + } cmd = (cvec_len(vars)>1 ? cv_string_get(cv1) : NULL); if ((pw = getpwuid(getuid())) == NULL){ clicon_err(OE_UNIX, errno, "getpwuid"); @@ -587,19 +599,21 @@ cli_start_shell(clicon_handle h, cli_signal_flush(h); cli_signal_unblock(h); if (cmd){ - snprintf(bcmd, 128, "bash -l -c \"%s\"", cmd); + snprintf(bcmd, 128, "%s -l -c \"%s\"", shcmd, cmd); if (system(bcmd) < 0){ cli_signal_block(h); clicon_err(OE_UNIX, errno, "system(bash -c)"); goto done; } } - else - if (system("bash -l") < 0){ + else{ + snprintf(bcmd, 128, "%s -l", shcmd); + if (system(bcmd) < 0){ cli_signal_block(h); clicon_err(OE_UNIX, errno, "system(bash)"); goto done; } + } cli_signal_block(h); #if 0 /* Allow errcodes from bash */ if (retval != 0){ @@ -729,7 +743,7 @@ compare_xmls(cxobj *xc1, /*! Compare two dbs using XML. Write to file and run diff * @param[in] h Clicon handle * @param[in] cvv - * @param[in] arg arg: 0 as xml, 1: as text + * @param[in] argv arg: 0 as xml, 1: as text */ int compare_dbs(clicon_handle h, @@ -1459,3 +1473,4 @@ cli_restart_plugin(clicon_handle h, done: return retval; } + diff --git a/apps/netconf/netconf_main.c b/apps/netconf/netconf_main.c index bde8cda4..4132092d 100644 --- a/apps/netconf/netconf_main.c +++ b/apps/netconf/netconf_main.c @@ -79,6 +79,8 @@ * ..wait 1min ]]>]]> */ #define NETCONF_HASH_BUF "netconf_input_cbuf" +#define NETCONF_FRAME_STATE "netconf_input_frame_state" +#define NETCONF_FRAME_SIZE "netconf_input_frame_size" /*! Ignore errors on packet errors: continue */ static int ignore_packet_errors = 1; @@ -509,13 +511,23 @@ netconf_input_cb(int s, size_t cdatlen = 0; clicon_hash_t *cdat = clicon_data(h); /* Save cbuf between calls if not done */ int poll; - int frame_state = 0; int i; int len; + int frame_state; size_t frame_size; int ret; int eof = 0; /* Set to 1 if pending close socket */ + if (clicon_option_exists(h, NETCONF_FRAME_STATE) == 0) + frame_state = 0; + else + if ((frame_state = clicon_option_int(h, NETCONF_FRAME_STATE)) < 0) + goto done; + if (clicon_option_exists(h, NETCONF_FRAME_SIZE) == 0) + frame_size = 0; + else + if ((frame_size = clicon_option_int(h, NETCONF_FRAME_SIZE)) < 0) + goto done; if ((ptr = clicon_hash_value(cdat, NETCONF_HASH_BUF, &cdatlen)) != NULL){ if (cdatlen != sizeof(cb)){ clicon_err(OE_XML, errno, "size mismatch %lu %lu", @@ -577,6 +589,7 @@ netconf_input_cb(int s, else{ cprintf(cb, "%c", buf[i]); if (detect_endtag("]]>]]>", buf[i], &frame_state)){ + frame_state = 0; /* OK, we have an xml string from a client */ /* Remove trailer */ *(((char*)cbuf_get(cb)) + cbuf_len(cb) - strlen("]]>]]>")) = '\0'; @@ -596,13 +609,15 @@ netconf_input_cb(int s, /* No data to read, save data and continue on next round */ if (cbuf_len(cb) != 0){ if (clicon_hash_add(cdat, NETCONF_HASH_BUF, &cb, sizeof(cb)) == NULL) - return -1; + goto done; cb = NULL; } break; } } /* while */ ok: + clicon_option_int_set(h, NETCONF_FRAME_STATE, frame_state); + clicon_option_int_set(h, NETCONF_FRAME_SIZE, frame_size); retval = 0; done: if (cb) diff --git a/example/main/example_cli.cli b/example/main/example_cli.cli index 1f61764c..bb0443c8 100644 --- a/example/main/example_cli.cli +++ b/example/main/example_cli.cli @@ -57,7 +57,9 @@ debug("Debugging parts of the system"){ restconf("Set restconf debug") ("Set debug level (0..n)"), cli_debug_restconf(); } -shell("System command") , cli_start_shell(); +shell("System command"), cli_start_shell("bash");{ + ("Single shell command"), cli_start_shell("bash"); +} copy("Copy and create a new object") { running("Copy from running db") startup("Copy to startup config"), db_copy("running", "startup"); interface("Copy interface"){ diff --git a/lib/src/clixon_proto.c b/lib/src/clixon_proto.c index 099e3acb..7ed6628b 100644 --- a/lib/src/clixon_proto.c +++ b/lib/src/clixon_proto.c @@ -281,7 +281,8 @@ atomicio(ssize_t (*fn) (int, void *, size_t), res = 0; else if (errno == EPIPE) /* Client shutdown */ res = 0; - /* SIGPIPE if client is killed */ + else if (errno == EBADF) /* client shutdown - freebsd */ + res = 0; case 0: /* fall thru */ return (res); default: diff --git a/test/lib.sh b/test/lib.sh index 65227afe..9e0034a8 100755 --- a/test/lib.sh +++ b/test/lib.sh @@ -375,13 +375,8 @@ function chunked_framing() { str=$1 length=${#str} - echo -n " -#${length} -" - echo -n "$str" - echo -n " -## -" + + printf "\n#%s\n%s\n##\n" ${length} "${str}" } # Start backend with all varargs. diff --git a/test/test_autocli_strict_expand.sh b/test/test_autocli_strict_expand.sh index c7ff5e06..307a33fd 100755 --- a/test/test_autocli_strict_expand.sh +++ b/test/test_autocli_strict_expand.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash # autocli extension strict expansion # See https://github.com/clicon/clixon/issues/163 -# test is: add a couple of expansion alternatives, ensure cli cannot select any oother option +# test is: add a couple of expansion alternatives, ensure cli cannot select any other option # Magic line must be first in script (see README.md) s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exit 0; else return 0; fi