diff --git a/CHANGELOG b/CHANGELOG index 29816d40..34d6ef13 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -29,6 +29,8 @@ # # ***** END LICENSE BLOCK ***** +- Added --with-cligen and --with-qdbm configure options +- Added union type check for non-cli (eg xml) input - Empty yang type. Relaxed yang types for unions, eg two strings with different length. Dec 2016: Dual license: both GPLv3 and APLv2 diff --git a/apps/cli/cli_plugin.c b/apps/cli/cli_plugin.c index 51c650fd..6db0ed9b 100644 --- a/apps/cli/cli_plugin.c +++ b/apps/cli/cli_plugin.c @@ -410,7 +410,8 @@ cli_load_syntax(clicon_handle h, const char *filename, const char *clispec_dir) } } - /* Resolve callback names to function pointers */ + /* Resolve callback names to function pointers. + * XXX: consider using cligen_callback_str2fnv instead */ if (cligen_callback_str2fn(pt, load_str2fn, handle) < 0){ clicon_err(OE_PLUGIN, 0, "Mismatch between CLIgen file '%s' and CLI plugin file '%s'. Some possible errors:\n\t1. A function given in the CLIgen file does not exist in the plugin (ie link error)\n\t2. The CLIgen spec does not point to the correct plugin .so file (CLICON_PLUGIN=\"%s\" is wrong)", filename, plgnam, plgnam); diff --git a/configure b/configure index 03fa00cd..27fa3d26 100755 --- a/configure +++ b/configure @@ -701,6 +701,8 @@ SHELL' ac_subst_files='' ac_user_opts=' enable_option_checking +with_cligen +with_qdbm enable_keycontent ' ac_precious_vars='build_alias @@ -1328,6 +1330,12 @@ Optional Features: --enable-FEATURE[=ARG] include FEATURE [ARG=yes] --disable-keycontent Disable reverse lookup content keys +Optional Packages: + --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] + --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) + --with-cligen=dir Use CLIGEN here + --with-qdbm=dir Use QDBM here + Some influential environment variables: CC C compiler command CFLAGS C compiler flags @@ -2165,7 +2173,6 @@ _ACEOF # Bind to specific CLIgen version - { $as_echo "$as_me:${as_lineno-$LINENO}: result: CLIXON version is ${CLIXON_VERSION}_PRE1" >&5 $as_echo "CLIXON version is ${CLIXON_VERSION}_PRE1" >&6; } @@ -2322,7 +2329,6 @@ test -n "$target_alias" && # Some stuff installed in /usr/local/. Such as qdbm -LIBS="-L /usr/local/lib" # ac_ext=c @@ -3475,14 +3481,9 @@ if test "$YACC" != "bison -y"; then fi if test "$prefix" = "NONE"; then - CPPFLAGS="-I${ac_default_prefix}/include ${CPPFLAGS}" - LDFLAGS="-L${ac_default_prefix}/lib ${LDFLAGS}" -else - CPPFLAGS="-I${prefix}/include ${CPPFLAGS}" - LDFLAGS="-L${prefix}/lib ${LDFLAGS}" + prefix=${ac_default_prefix} fi -LDFLAGS="" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for main in -lm" >&5 $as_echo_n "checking for main in -lm... " >&6; } if ${ac_cv_lib_m_main+:} false; then : @@ -3528,8 +3529,24 @@ AR_SUFFIX=".a" SH_SUFFIX=".so" AR="ar" +CPPFLAGS="-I${prefix}/include ${CPPFLAGS}" +LDFLAGS="-L${prefix}/lib ${LDFLAGS}" + # This is for cligen +# Check whether --with-cligen was given. +if test "${with_cligen+set}" = set; then : + withval=$with_cligen; +fi + +if test "${with_cligen}"; then + echo "Using CLIGEN here: ${with_cligen}" + CPPFLAGS="-I${with_cligen}/include ${CPPFLAGS}" + LDFLAGS="-L${with_cligen}/lib ${LDFLAGS}" +fi + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5 $as_echo_n "checking for grep that handles long lines and -e... " >&6; } if ${ac_cv_path_GREP+:} false; then : @@ -3854,7 +3871,21 @@ else fi -# This is for qdbm. +# This is for qdbm + +# Check whether --with-qdbm was given. +if test "${with_qdbm+set}" = set; then : + withval=$with_qdbm; +fi + +if test "${with_qdbm}"; then + echo "Using QDBM here: ${with_qdbm}" + CPPFLAGS="-I${with_qdbm}/include ${CPPFLAGS}" + LDFLAGS="-L${with_qdbm}/lib ${LDFLAGS}" +fi + +LIBS="${LIBS} ${LDFLAGS}" + # Problem: depot.h may be in qdbm/depot.h. for ac_header in depot.h do : diff --git a/configure.ac b/configure.ac index 85853f0f..b1eb08db 100644 --- a/configure.ac +++ b/configure.ac @@ -62,7 +62,6 @@ AC_SUBST(CLIXON_VERSION_MAJOR) AC_SUBST(CLIXON_VERSION_MINOR) AC_SUBST(CLIGEN_VERSION) # Bind to specific CLIgen version - AC_MSG_RESULT(CLIXON version is ${CLIXON_VERSION}_PRE1) AC_CANONICAL_TARGET @@ -80,7 +79,6 @@ AC_SUBST(AR) AC_SUBST(RANLIB) # Some stuff installed in /usr/local/. Such as qdbm -LIBS="-L /usr/local/lib" # AC_PROG_CC() @@ -105,14 +103,9 @@ if test "$YACC" != "bison -y"; then fi if test "$prefix" = "NONE"; then - CPPFLAGS="-I${ac_default_prefix}/include ${CPPFLAGS}" - LDFLAGS="-L${ac_default_prefix}/lib ${LDFLAGS}" -else - CPPFLAGS="-I${prefix}/include ${CPPFLAGS}" - LDFLAGS="-L${prefix}/lib ${LDFLAGS}" + prefix=${ac_default_prefix} fi -LDFLAGS="" AC_CHECK_LIB(m, main) EXE_SUFFIX="" OBJ_SUFFIX=".o" @@ -120,12 +113,32 @@ AR_SUFFIX=".a" SH_SUFFIX=".so" AR="ar" +CPPFLAGS="-I${prefix}/include ${CPPFLAGS}" +LDFLAGS="-L${prefix}/lib ${LDFLAGS}" + # This is for cligen +AC_ARG_WITH(cligen, [ --with-cligen=dir Use CLIGEN here ] ) +if test "${with_cligen}"; then + echo "Using CLIGEN here: ${with_cligen}" + CPPFLAGS="-I${with_cligen}/include ${CPPFLAGS}" + LDFLAGS="-L${with_cligen}/lib ${LDFLAGS}" +fi + + AC_CHECK_HEADERS(cligen/cligen.h,, AC_MSG_ERROR(cligen missing. Try: git clone https://github.com/olofhagsand/cligen.git)) AC_CHECK_LIB(:libcligen.so.${CLIGEN_VERSION}, cligen_init,, AC_MSG_ERROR([CLIgen${CLIGEN_VERSION} missing. Try: git clone https://github.com/olofhagsand/cligen.git])) -# This is for qdbm. +# This is for qdbm +AC_ARG_WITH(qdbm, [ --with-qdbm=dir Use QDBM here ] ) +if test "${with_qdbm}"; then + echo "Using QDBM here: ${with_qdbm}" + CPPFLAGS="-I${with_qdbm}/include ${CPPFLAGS}" + LDFLAGS="-L${with_qdbm}/lib ${LDFLAGS}" +fi + +LIBS="${LIBS} ${LDFLAGS}" + # Problem: depot.h may be in qdbm/depot.h. AC_CHECK_HEADERS(depot.h,,[AC_CHECK_HEADERS(qdbm/depot.h,,AC_MSG_ERROR(libqdbm-dev required))]) AC_CHECK_LIB(qdbm, dpopen,, AC_MSG_ERROR(libqdbm-dev required)) diff --git a/lib/src/clixon_proto.c b/lib/src/clixon_proto.c index 33f5c5da..7e8bc392 100644 --- a/lib/src/clixon_proto.c +++ b/lib/src/clixon_proto.c @@ -432,7 +432,10 @@ clicon_rpc(int s, case CLICON_MSG_ERR: if (clicon_msg_err_decode(reply, &err, &suberr, &reason, label) < 0) goto done; - clicon_err(err, suberr, "%s msgtype:%hu", reason, ntohs(msg->op_type)); + if (debug) + clicon_err(err, suberr, "%s msgtype:%hu", reason, ntohs(msg->op_type)); + else + clicon_err(err, suberr, "%s", reason); goto done; break; default: diff --git a/lib/src/clixon_xml_map.c b/lib/src/clixon_xml_map.c index fc74b136..3f962e1d 100644 --- a/lib/src/clixon_xml_map.c +++ b/lib/src/clixon_xml_map.c @@ -288,7 +288,7 @@ xml_yang_validate(cxobj *xt, { int retval = -1; cg_var *cv = NULL; - char *reason; + char *reason = NULL; yang_stmt *yc; int i; yang_stmt *ys; @@ -318,6 +318,9 @@ xml_yang_validate(cxobj *xt, clicon_err(OE_UNIX, errno, "cv_dup"); goto done; } + /* In the union case, value is parsed as generic REST type, + * needs to be reparsed when concrete type is selected + */ if (cv_parse(xml_body(xt), cv) <0){ clicon_err(OE_UNIX, errno, "cv_parse"); goto done; diff --git a/lib/src/clixon_yang_type.c b/lib/src/clixon_yang_type.c index 0cb1dbf3..6acb1e36 100644 --- a/lib/src/clixon_yang_type.c +++ b/lib/src/clixon_yang_type.c @@ -377,59 +377,32 @@ clicon_type2cv(char *origtype, char *restype, enum cv_type *cvtype) (rmax && (i) > cv_##type##_get(rmax))) -/*! Validate cligen variable cv using yang statement as spec - * - * @param [in] cv A cligen variable to validate. This is a correctly parsed cv. - * @param [in] ys A yang statement, must be leaf of leaf-list. - * @param [out] reason If given, and if return value is 0, contains a malloced string - * describing the reason why the validation failed. Must be freed. +/*! * @retval -1 Error (fatal), with errno set to indicate error * @retval 0 Validation not OK, malloced reason is returned. Free reason with free() * @retval 1 Validation OK - * See also cv_validate - the code is similar. */ -int -ys_cv_validate(cg_var *cv, yang_stmt *ys, char **reason) +static int +cv_validate1(cg_var *cv, + enum cv_type cvtype, + int options, + cg_var *range_min, + cg_var *range_max, + char *pattern, + yang_stmt *yrestype, + char *restype, + char **reason) { int retval = 1; /* OK */ - cg_var *ycv; /* cv of yang-statement */ - int64_t i = 0; - uint64_t u = 0; - char *str; - int options; - cg_var *range_min; - cg_var *range_max; - char *pattern; int retval2; - enum cv_type cvtype; - char *type; /* orig type */ - yang_stmt *yrestype; /* resolved type */ - char *restype; - uint8_t fraction; yang_stmt *yi = NULL; + uint64_t u = 0; + int64_t i = 0; + char *str; - if (reason) + if (reason && *reason){ + free(*reason); *reason = NULL; - if (ys->ys_keyword != Y_LEAF && ys->ys_keyword != Y_LEAF_LIST) - return 0; - ycv = ys->ys_cv; - if (yang_type_get(ys, &type, &yrestype, - &options, &range_min, &range_max, &pattern, - &fraction) < 0) - goto err; - restype = yrestype?yrestype->ys_argument:NULL; - if (clicon_type2cv(type, restype, &cvtype) < 0) - goto err; - - if (cv_type_get(ycv) != cvtype){ - /* special case: dbkey has rest syntax-> cv but yang cant have that */ - if (cvtype == CGV_STRING && cv_type_get(ycv) == CGV_REST) - ; - else { - clicon_err(OE_DB, 0, "%s: Type mismatch data:%s != yang:%s", - __FUNCTION__, cv_type2str(cvtype), cv_type2str(cv_type_get(ycv))); - goto err; - } } switch (cvtype){ case CGV_INT8: @@ -597,10 +570,112 @@ ys_cv_validate(cg_var *cv, yang_stmt *ys, char **reason) } if (reason && *reason) - assert(retval == 0); + assert(retval == 0); /* validation failed with error reason */ + return retval; +} + +/*! Validate cligen variable cv using yang statement as spec + * + * @param[in] cv A cligen variable to validate. This is a correctly parsed cv. + * @param[in] ys A yang statement, must be leaf of leaf-list. + * @param[out] reason If given, and if return value is 0, contains a malloced string + * describing the reason why the validation failed. Must be freed. + * @retval -1 Error (fatal), with errno set to indicate error + * @retval 0 Validation not OK, malloced reason is returned. Free reason with free() + * @retval 1 Validation OK + * See also cv_validate - the code is similar. + */ +int +ys_cv_validate(cg_var *cv, + yang_stmt *ys, + char **reason) +{ + int retval = -1; + cg_var *ycv; /* cv of yang-statement */ + int options; + cg_var *range_min; + cg_var *range_max; + char *pattern; + enum cv_type cvtype; + char *type; /* orig type */ + yang_stmt *yrestype; /* resolved type */ + yang_stmt *yrt; /* union subtype */ + char *restype; + uint8_t fraction; + yang_stmt *yt = NULL; + int retval2; + char *val; + cg_var *cvt=NULL; + + if (reason) + *reason=NULL; + if (ys->ys_keyword != Y_LEAF && ys->ys_keyword != Y_LEAF_LIST){ + retval = 1; + goto done; + } + ycv = ys->ys_cv; + if (yang_type_get(ys, &type, &yrestype, + &options, &range_min, &range_max, &pattern, + &fraction) < 0) + goto done; + restype = yrestype?yrestype->ys_argument:NULL; + if (clicon_type2cv(type, restype, &cvtype) < 0) + goto done; + + if (cv_type_get(ycv) != cvtype){ + /* special case: dbkey has rest syntax-> cv but yang cant have that */ + if (cvtype == CGV_STRING && cv_type_get(ycv) == CGV_REST) + ; + else { + clicon_err(OE_DB, 0, "%s: Type mismatch data:%s != yang:%s", + __FUNCTION__, cv_type2str(cvtype), cv_type2str(cv_type_get(ycv))); + goto done; + } + } + /* Note restype can be NULL here for example with unresolved hardcoded uuid */ + if (restype && strcmp(restype, "union") == 0){ + yt = NULL; + retval2 = 1; /* valid */ + assert(cvtype == CGV_REST); + val = cv_string_get(cv); + while ((yt = yn_each((yang_node*)yrestype, yt)) != NULL){ + if (yt->ys_keyword != Y_TYPE) + continue; + if (yang_type_resolve(ys, yt, &yrt, + &options, &range_min, &range_max, &pattern, + &fraction) < 0) + goto done; + restype = yrestype?yrt->ys_argument:NULL; + if (clicon_type2cv(type, restype, &cvtype) < 0) + goto done; + /* reparse value with the new type */ + if ((cvt = cv_new(cvtype)) == NULL){ + clicon_err(OE_UNIX, errno, "cv_new"); + goto done; + } + if (cv_parse(val, cvt) <0){ + clicon_err(OE_UNIX, errno, "cv_parse"); + goto done; + } + if ((retval2 = cv_validate1(cvt, cvtype, options, range_min, range_max, + pattern, yrt, restype, reason)) < 0) + goto done; + if (retval2 == 1) /* done */ + break; + /* Here retval == 0, validation failed */ + cv_free(cvt); + cvt=NULL; + } + retval = retval2; /* invalid (0) with latest reason or valid 1 */ + } + else + if ((retval = cv_validate1(cv, cvtype, options, range_min, range_max, pattern, + yrestype, restype, reason)) < 0) + goto done; + done: + if (cvt) + cv_free(cvt); return retval; - err: - return -1; } /*