Merge branch 'master' of https://github.com/clicon/clixon
This commit is contained in:
commit
4021d65d7a
15 changed files with 2168 additions and 1984 deletions
|
|
@ -107,6 +107,8 @@
|
|||
* Added libgen.h for baseline()
|
||||
|
||||
### Corrected Bugs
|
||||
* List ordering bug - lists with ints as keys behaved wrongly and slow.
|
||||
* NACM read default rule did not work properly if nacm was enabled AND no groups were defined
|
||||
* Re-inserted `cli_output_reset` for what was erroneuos thought to be an obsolete function
|
||||
* See in 3.9.0 minro changes: Replaced all calls to (obsolete) `cli_output` with `fprintf`
|
||||
* Allowed Yang extended Xpath functions (syntax only):
|
||||
|
|
|
|||
993
config.guess
vendored
993
config.guess
vendored
File diff suppressed because it is too large
Load diff
2771
config.sub
vendored
2771
config.sub
vendored
File diff suppressed because it is too large
Load diff
|
|
@ -141,11 +141,12 @@ server {
|
|||
```
|
||||
Start nginx daemon
|
||||
```
|
||||
sudo /etc/init.d/nginx start
|
||||
sudo /etc/init.d/nginx start
|
||||
sudo systemctl start nginx.service # alternative using systemd
|
||||
```
|
||||
Start the clixon restconf daemon
|
||||
```
|
||||
sudo su -c "/www-data/clixon_restconf -f /usr/local/etc/example.xml " -s /bin/sh www-data
|
||||
sudo su -c "/www-data/clixon_restconf -f /usr/local/etc/example.xml " -s /bin/sh www-data
|
||||
```
|
||||
then access using curl or wget:
|
||||
```
|
||||
|
|
|
|||
|
|
@ -542,7 +542,7 @@ clixon_plugin_init(clicon_handle h)
|
|||
struct timeval retention = {0,0};
|
||||
int argc; /* command-line options (after --) */
|
||||
char **argv;
|
||||
char c;
|
||||
int c;
|
||||
|
||||
clicon_debug(1, "%s backend", __FUNCTION__);
|
||||
|
||||
|
|
|
|||
|
|
@ -40,7 +40,6 @@
|
|||
#include <ctype.h>
|
||||
#include <signal.h>
|
||||
#include <fcgiapp.h>
|
||||
#include <curl/curl.h>
|
||||
|
||||
/* cligen */
|
||||
#include <cligen/cligen.h>
|
||||
|
|
@ -329,7 +328,7 @@ clixon_plugin_init(clicon_handle h)
|
|||
{
|
||||
int argc; /* command-line options (after --) */
|
||||
char **argv = NULL;
|
||||
char c;
|
||||
int c;
|
||||
|
||||
clicon_debug(1, "%s restconf", __FUNCTION__);
|
||||
/* Get user command-line options (after --) */
|
||||
|
|
|
|||
|
|
@ -567,22 +567,27 @@ nacm_datanode_read(cxobj *xt,
|
|||
/* User's group */
|
||||
if (xpath_vec(xnacm, "groups/group[user-name='%s']", &gvec, &glen, username) < 0)
|
||||
goto done;
|
||||
/* 4. If no groups are found, continue with step 9. */
|
||||
if (glen == 0)
|
||||
goto step9;
|
||||
/* 4. If no groups are found (glen=0), continue and check read-default
|
||||
in step 11. */
|
||||
/* 5. Process all rule-list entries, in the order they appear in the
|
||||
configuration. If a rule-list's "group" leaf-list does not
|
||||
match any of the user's groups, proceed to the next rule-list
|
||||
entry. */
|
||||
if (xpath_vec(xnacm, "rule-list", &rlistvec, &rlistlen) < 0)
|
||||
goto done;
|
||||
/* read-default has default permit so should never be NULL */
|
||||
if ((read_default = xml_find_body(xnacm, "read-default")) == NULL){
|
||||
clicon_err(OE_XML, EINVAL, "No nacm read-default rule");
|
||||
goto done;
|
||||
}
|
||||
for (i=0; i<xrlen; i++){ /* Loop through requested nodes */
|
||||
xr = xrvec[i]; /* requested node XR */
|
||||
/* Loop through rule-list (steps 5,6,7) to find match of requested node
|
||||
*/
|
||||
xrule = NULL;
|
||||
if (nacm_data_read_xr(xt, xr, gvec, glen, rlistvec, rlistlen,
|
||||
&xrule) < 0)
|
||||
/* Skip if no groups */
|
||||
if (glen && nacm_data_read_xr(xt, xr, gvec, glen, rlistvec, rlistlen,
|
||||
&xrule) < 0)
|
||||
goto done;
|
||||
if (xrule){ /* xrule match requested node xr */
|
||||
if ((action = xml_find_body(xrule, "action")) == NULL)
|
||||
|
|
@ -601,8 +606,7 @@ nacm_datanode_read(cxobj *xt,
|
|||
to "permit", then include the requested data node in the reply;
|
||||
otherwise, do not include the requested data node or any of its
|
||||
descendants in the reply.*/
|
||||
read_default = xml_find_body(xnacm, "read-default");
|
||||
if (read_default == NULL || strcmp(read_default, "deny")==0)
|
||||
if (strcmp(read_default, "deny")==0)
|
||||
if (xml_purge(xr) < 0)
|
||||
goto done;
|
||||
}
|
||||
|
|
@ -668,7 +672,7 @@ nacm_datanode_write(cxobj *xt,
|
|||
cxobj *xrule;
|
||||
int match = 0;
|
||||
char *action;
|
||||
char *write_default;
|
||||
char *write_default = NULL;
|
||||
|
||||
if (xnacm == NULL)
|
||||
goto permit;
|
||||
|
|
@ -747,8 +751,12 @@ nacm_datanode_write(cxobj *xt,
|
|||
/*12. For a "write" access operation, if the "write-default" leaf is
|
||||
set to "permit", then permit the data node access request;
|
||||
otherwise, deny the request.*/
|
||||
write_default = xml_find_body(xnacm, "write-default");
|
||||
if (write_default == NULL || strcmp(write_default, "permit") != 0){
|
||||
/* write-default has default permit so should never be NULL */
|
||||
if ((write_default = xml_find_body(xnacm, "write-default")) == NULL){
|
||||
clicon_err(OE_XML, EINVAL, "No nacm write-default rule");
|
||||
goto done;
|
||||
}
|
||||
if (strcmp(write_default, "permit") != 0){
|
||||
if (netconf_access_denied(cbret, "application", "default deny") < 0)
|
||||
goto done;
|
||||
goto deny;
|
||||
|
|
|
|||
|
|
@ -730,8 +730,7 @@ match_base_child(cxobj *x0,
|
|||
goto ok;
|
||||
if ((b = xml_body(xb)) == NULL)
|
||||
goto ok;
|
||||
// if ((b = xml_find_body(x1c, keyname)) == NULL)
|
||||
// goto ok; /* not found */
|
||||
|
||||
keyval[i] = b;
|
||||
if (xml_cv_cache(xb, &keycvec[i]) < 0) /* error case */
|
||||
goto done;
|
||||
|
|
@ -741,9 +740,7 @@ match_base_child(cxobj *x0,
|
|||
default:
|
||||
break;
|
||||
}
|
||||
/* Get match. Sorting mode(optimized) or not?
|
||||
* This conditional is a mystery , but test_yang_namespace.sh breaks if set to (0)
|
||||
*/
|
||||
/* Get match. */
|
||||
yorder = yang_order(yc);
|
||||
x0c = xml_search(x0, xml_name(x1c), yorder, yc->ys_keyword, keynr, keyvec, keyval, keycvec);
|
||||
ok:
|
||||
|
|
|
|||
|
|
@ -1,126 +0,0 @@
|
|||
/*
|
||||
* Utility debug tool
|
||||
* Static Compile: gcc -Wall json_xpath.c -o json_xpath -l clixon
|
||||
gcc -O2 -o json_xpath json_xpath.c clixon_log.o clixon_err.o clixon_json.o clixon_xml.o clixon_xsl.o clixon_json_parse.tab.o clixon_xml_parse.tab.o lex.clixon_json_parse.o lex.clixon_xml_parse.o ../../../cligen/cligen_buf.o ../../../cligen/cligen_var.o ../../../cligen/cligen_gen.o ../../../cligen/cligen_cvec.o ../../../cligen/cligen_handle.o ../../../cligen/getline.o ../../../cligen/cligen_read.o ../../../cligen/cligen_match.o ../../../cligen/cligen_expand.o ../../../cligen/cligen_print.o
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
/* cligen */
|
||||
#include <cligen/cligen.h>
|
||||
|
||||
/* clicon */
|
||||
#include <clixon/clixon.h>
|
||||
|
||||
static int
|
||||
usage(char *argv0)
|
||||
{
|
||||
fprintf(stderr, "usage:%s <xpath> # XML/JSON expected on stdin\n"
|
||||
"\t-h Help\n"
|
||||
"\t-b Strip to body value (eg \"<a>val</a>\" --> \"val\"\n"
|
||||
"\t-j json input (not xml)\n",
|
||||
argv0);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
int i;
|
||||
cxobj **xv;
|
||||
cxobj *x;
|
||||
cxobj *xn;
|
||||
cxobj *xb;
|
||||
char *xpath;
|
||||
int body = 0;
|
||||
int json = 0;
|
||||
size_t xlen = 0;
|
||||
size_t len;
|
||||
size_t buflen = 128;
|
||||
char *buf;
|
||||
char *p;
|
||||
int retval;
|
||||
int c;
|
||||
|
||||
while ((c = getopt(argc, argv, "?hbj")) != -1)
|
||||
switch (c) {
|
||||
case '?':
|
||||
case 'h':
|
||||
usage(argv[0]);
|
||||
break;
|
||||
case 'b':
|
||||
body++;
|
||||
break;
|
||||
case 'j':
|
||||
json++;
|
||||
break;
|
||||
default:
|
||||
usage(argv[0]);
|
||||
}
|
||||
if (optind >= argc)
|
||||
usage(argv[0]);
|
||||
xpath=argv[optind];
|
||||
|
||||
clicon_log_init("xpath", 0, CLICON_LOG_STDERR);
|
||||
if ((buf = malloc(buflen)) == NULL){
|
||||
perror("malloc");
|
||||
return -1;
|
||||
}
|
||||
/* |---------------|-------------|
|
||||
* buf p buf+buflen
|
||||
*/
|
||||
p = buf;
|
||||
memset(p, 0, buflen);
|
||||
while (1){
|
||||
if ((retval = read(0, p, buflen-(p-buf))) < 0){
|
||||
perror("read");
|
||||
return -1;
|
||||
}
|
||||
if (retval == 0)
|
||||
break;
|
||||
p += retval;
|
||||
len = p-buf;
|
||||
|
||||
if (buf+buflen-p < 10){ /* allocate more */
|
||||
buflen *= 2;
|
||||
if ((buf = realloc(buf, buflen)) == NULL){
|
||||
perror("realloc");
|
||||
return -1;
|
||||
}
|
||||
p = buf+len;
|
||||
memset(p, 0, (buf+buflen)-p);
|
||||
}
|
||||
}
|
||||
if (json){
|
||||
if (json_parse_str(buf, &x) < 0)
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
if (xml_parse_string(buf, &x) < 0)
|
||||
return -1;
|
||||
|
||||
if (xpath_vec(x, xpath, &xv, &xlen) < 0)
|
||||
return -1;
|
||||
if (xv){
|
||||
for (i=0; i<xlen; i++){
|
||||
xn = xv[i];
|
||||
if (body)
|
||||
xb = xml_find(xn, "body");
|
||||
else
|
||||
xb = xn;
|
||||
if (xb){
|
||||
// xml2json(stdout, xb, 0);
|
||||
clicon_xml2file(stdout, xb, 0, 0);
|
||||
fprintf(stdout,"\n");
|
||||
}
|
||||
}
|
||||
free(xv);
|
||||
}
|
||||
if (x)
|
||||
xml_free(x);
|
||||
if (buf)
|
||||
free(buf);
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -26,9 +26,11 @@ You need to build and install the clixon utility programs before running the tes
|
|||
sudo make install
|
||||
```
|
||||
|
||||
You need to start nginx for some of the text. There are instructions in
|
||||
You need to configure and start nginx for the restconf tests:
|
||||
* The [example](../example/main/README.md) has instructions on how to edit your nginx config files
|
||||
* If you run systemd: `sudo systemctl start nginx.service`
|
||||
* The [example](../example/README.md) has instructions
|
||||
|
||||
You may need to install the `time` utility (`/usr/bin/time`).
|
||||
|
||||
## Prefix variable
|
||||
|
||||
|
|
@ -65,7 +67,7 @@ You may add your site-specific modifications in a `site.sh` file. Example:
|
|||
# Add test to this list that you dont want run
|
||||
SKIPLIST="test_openconfig.sh test_yangmodels.sh"
|
||||
# Parse yang openconfig models from https://github.com/openconfig/public
|
||||
OPENCONFIG=/home/olof/src/clixon/test/public
|
||||
OPENCONFIG=/usr/local/share/openconfig/public
|
||||
# Parse yangmodels from https://github.com/YangModels/yang
|
||||
YANGMODELS=/usr/local/share/yangmodels
|
||||
# Standard IETF RFC yang files.
|
||||
|
|
|
|||
|
|
@ -78,10 +78,10 @@ testname=
|
|||
# Parse yangmodels from https://github.com/YangModels/yang
|
||||
# Recommended: checkout yangmodels elsewhere in the tree and set the env
|
||||
# to that
|
||||
: ${YANGMODELS=$(pwd)/yang}
|
||||
#: ${YANGMODELS=$(pwd)/yang} # just skip if not set
|
||||
|
||||
# Parse yang openconfig models from https://github.com/openconfig/public
|
||||
: ${OPENCONFIG=$(pwd)/public}
|
||||
#: ${OPENCONFIG=$(pwd)/public} # just skip if not set
|
||||
|
||||
# Standard IETF RFC yang files.
|
||||
: ${IETFRFC=../yang/standard}
|
||||
|
|
|
|||
193
test/test_nacm_default.sh
Executable file
193
test/test_nacm_default.sh
Executable file
|
|
@ -0,0 +1,193 @@
|
|||
#!/bin/bash
|
||||
# Basic NACM default rulw without any groups
|
||||
# Start from startup db
|
||||
|
||||
# Magic line must be first in script (see README.md)
|
||||
s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exit 0; else return 0; fi
|
||||
|
||||
APPNAME=example
|
||||
|
||||
cfg=$dir/conf_yang.xml
|
||||
fyang=$dir/nacm-example.yang
|
||||
|
||||
cat <<EOF > $cfg
|
||||
<clixon-config xmlns="http://clicon.org/config">
|
||||
<CLICON_CONFIGFILE>$cfg</CLICON_CONFIGFILE>
|
||||
<CLICON_YANG_DIR>/usr/local/share/clixon</CLICON_YANG_DIR>
|
||||
<CLICON_YANG_DIR>$IETFRFC</CLICON_YANG_DIR>
|
||||
<CLICON_YANG_MAIN_FILE>$fyang</CLICON_YANG_MAIN_FILE>
|
||||
<CLICON_CLISPEC_DIR>/usr/local/lib/$APPNAME/clispec</CLICON_CLISPEC_DIR>
|
||||
<CLICON_RESTCONF_DIR>/usr/local/lib/$APPNAME/restconf</CLICON_RESTCONF_DIR>
|
||||
<CLICON_CLI_DIR>/usr/local/lib/$APPNAME/cli</CLICON_CLI_DIR>
|
||||
<CLICON_CLI_MODE>$APPNAME</CLICON_CLI_MODE>
|
||||
<CLICON_SOCK>/usr/local/var/$APPNAME/$APPNAME.sock</CLICON_SOCK>
|
||||
<CLICON_BACKEND_DIR>/usr/local/lib/$APPNAME/backend</CLICON_BACKEND_DIR>
|
||||
<CLICON_BACKEND_PIDFILE>/usr/local/var/$APPNAME/$APPNAME.pidfile</CLICON_BACKEND_PIDFILE>
|
||||
<CLICON_CLI_GENMODEL_COMPLETION>1</CLICON_CLI_GENMODEL_COMPLETION>
|
||||
<CLICON_XMLDB_DIR>$dir</CLICON_XMLDB_DIR>
|
||||
<CLICON_XMLDB_PLUGIN>/usr/local/lib/xmldb/text.so</CLICON_XMLDB_PLUGIN>
|
||||
<CLICON_RESTCONF_PRETTY>false</CLICON_RESTCONF_PRETTY>
|
||||
<CLICON_NACM_MODE>internal</CLICON_NACM_MODE>
|
||||
</clixon-config>
|
||||
EOF
|
||||
|
||||
cat <<EOF > $fyang
|
||||
module nacm-example{
|
||||
yang-version 1.1;
|
||||
namespace "urn:example:nacm";
|
||||
prefix nacm;
|
||||
import clixon-example {
|
||||
prefix ex;
|
||||
}
|
||||
import ietf-netconf-acm {
|
||||
prefix nacm;
|
||||
}
|
||||
leaf x{
|
||||
type int32;
|
||||
description "something to edit";
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
#
|
||||
# startup db with default values:
|
||||
# 1: enable-nacm (true|false)
|
||||
# 2: read-default (deny|permit)
|
||||
# 3: write-default (deny|permit)
|
||||
# 4: exec-defautl (deny|permit)
|
||||
# 5: expected return value of test1
|
||||
# 6: expected return value of test2
|
||||
# 7: expected return value of test3
|
||||
testrun(){
|
||||
enablenacm=$1
|
||||
readdefault=$2
|
||||
writedefault=$3
|
||||
execdefault=$4
|
||||
ret1=$5
|
||||
ret2=$6
|
||||
ret3=$7
|
||||
|
||||
# NACM in startup
|
||||
sudo tee $dir/startup_db > /dev/null << EOF
|
||||
<config>
|
||||
<nacm xmlns="urn:ietf:params:xml:ns:yang:ietf-netconf-acm">
|
||||
<enable-nacm>${enablenacm}</enable-nacm>
|
||||
<read-default>${readdefault}</read-default>
|
||||
<write-default>${writedefault}</write-default>
|
||||
<exec-default>${execdefault}</exec-default>
|
||||
<enable-external-groups>true</enable-external-groups>
|
||||
</nacm>
|
||||
<x xmlns="urn:example:nacm">42</x>
|
||||
</config>
|
||||
EOF
|
||||
if [ $BE -ne 0 ]; then # Bring your own backend
|
||||
new "kill old backend"
|
||||
sudo clixon_backend -zf $cfg
|
||||
if [ $? -ne 0 ]; then
|
||||
err
|
||||
fi
|
||||
new "start backend -s init -f $cfg"
|
||||
start_backend -s startup -f $cfg
|
||||
else
|
||||
new "Restart backend as eg follows: -Ff $cfg -s startup"
|
||||
sleep $BETIMEOUT
|
||||
fi
|
||||
|
||||
new "kill old restconf daemon"
|
||||
sudo pkill -u www-data -f "/www-data/clixon_restconf"
|
||||
|
||||
sleep 1
|
||||
new "start restconf daemon (-a is enable basic authentication)"
|
||||
start_restconf -f $cfg -- -a
|
||||
|
||||
new "waiting"
|
||||
sleep $RCWAIT
|
||||
|
||||
#----------- First get
|
||||
case "$ret1" in
|
||||
0) ret='{"nacm-example:x": 42}
|
||||
'
|
||||
;;
|
||||
1) ret='{"ietf-restconf:errors" : {"error": {"error-type": "application","error-tag": "access-denied","error-severity": "error","error-message": "default deny"}}}
'
|
||||
;;
|
||||
2) ret='null
|
||||
'
|
||||
;;
|
||||
esac
|
||||
new "get startup 42"
|
||||
expecteq "$(curl -u guest:bar -sS -X GET http://localhost/restconf/data/nacm-example:x)" 0 "$ret"
|
||||
|
||||
#----------- Then edit
|
||||
case "$ret2" in
|
||||
0) ret=''
|
||||
;;
|
||||
1) ret='{"ietf-restconf:errors" : {"error": {"error-type": "application","error-tag": "access-denied","error-severity": "error","error-message": "default deny"}}}
'
|
||||
;;
|
||||
esac
|
||||
new "edit new 99"
|
||||
expecteq "$(curl -u guest:bar -sS -X PUT -d '{"nacm-example:x": 99}' http://localhost/restconf/data/nacm-example:x)" 0 "$ret"
|
||||
|
||||
#----------- Then second get
|
||||
case "$ret3" in
|
||||
0) ret='{"nacm-example:x": 99}
|
||||
'
|
||||
;;
|
||||
1) ret='{"ietf-restconf:errors" : {"error": {"error-type": "application","error-tag": "access-denied","error-severity": "error","error-message": "default deny"}}}
'
|
||||
;;
|
||||
2) ret='null
|
||||
'
|
||||
;;
|
||||
3) ret='{"nacm-example:x": 42}
|
||||
'
|
||||
esac
|
||||
new "get 99"
|
||||
expecteq "$(curl -u guest:bar -sS -X GET http://localhost/restconf/data/nacm-example:x)" 0 "$ret"
|
||||
|
||||
new "Kill restconf daemon"
|
||||
stop_restconf
|
||||
|
||||
if [ $BE -ne 0 ]; then # Bring your own backend
|
||||
new "Kill backend"
|
||||
# Check if premature kill
|
||||
pid=`pgrep -u root -f clixon_backend`
|
||||
if [ -z "$pid" ]; then
|
||||
err "backend already dead"
|
||||
fi
|
||||
# kill backend
|
||||
stop_backend -f $cfg
|
||||
fi
|
||||
} # testrun
|
||||
|
||||
# Run a lot of tests with different settings of default read/write/exec
|
||||
new "nacm enabled and all defaults permit"
|
||||
testrun true permit permit permit 0 0 0
|
||||
|
||||
new "nacm disabled and all defaults permit"
|
||||
testrun false permit permit permit 0 0 0
|
||||
|
||||
new "nacm disabled and all defaults deny"
|
||||
testrun false deny deny deny 0 0 0
|
||||
|
||||
new "nacm enabled, all defaults deny (expect fail)"
|
||||
testrun true deny deny deny 1 1 1
|
||||
|
||||
new "nacm enabled, exec default deny - read permit (expect fail)"
|
||||
testrun true permit deny deny 1 1 1
|
||||
|
||||
new "nacm enabled, exec default deny - write permit (expect fail)"
|
||||
testrun true deny permit deny 1 1 1
|
||||
|
||||
new "nacm enabled, exec default deny read/write permit (expect fail)"
|
||||
testrun true permit permit deny 1 1 1
|
||||
|
||||
new "nacm enabled, exec default permit, all others deny (expect fail)"
|
||||
testrun true deny deny permit 2 1 2
|
||||
|
||||
new "nacm enabled, exec default permit, read permit (expect fail)"
|
||||
testrun true permit deny permit 0 1 3
|
||||
|
||||
new "nacm enabled, exec default permit, write permit (expect fail)"
|
||||
testrun true deny permit permit 2 0 2
|
||||
|
||||
|
||||
rm -rf $dir
|
||||
|
|
@ -18,7 +18,7 @@ fyang=$dir/test.yang
|
|||
new "openconfig"
|
||||
if [ ! -d "$OPENCONFIG" ]; then
|
||||
# err "Hmm Openconfig dir does not seem to exist, try git clone https://github.com/openconfig/public?"
|
||||
exit
|
||||
if [ "$s" = $0 ]; then exit 0; else return 0; fi
|
||||
fi
|
||||
|
||||
OCDIR=$OPENCONFIG/release/models
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ fyang=$dir/test.yang
|
|||
|
||||
if [ ! -d "$YANGMODELS" ]; then
|
||||
# err "Hmm Yangmodels dir does not seem to exist, try git clone https://github.com/YangModels/yang?"
|
||||
exit
|
||||
if [ "$s" = $0 ]; then exit 0; else return 0; fi
|
||||
fi
|
||||
|
||||
# Experimental IEEE
|
||||
|
|
|
|||
|
|
@ -3,3 +3,5 @@
|
|||
This directory contains Clixon utility programs, ie, programs that are
|
||||
good to have for testing, analysis, etc, but not an actual part of
|
||||
delivered code.
|
||||
|
||||
Note, streams utility may need: libcurl4-openssl-dev or corresponding.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue