diff --git a/CHANGELOG.md b/CHANGELOG.md index 92cf17ee..3d0fb14a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -64,6 +64,11 @@ Users may have to change how they access the system * Fixed: Configure option `CLICON_RESTCONF_PRETTY` was marked as obsolete but was still used. * `CLICON_RESTCONF_PRETTY` is now obsolete for sure * Instead restconf/pretty is used with API function restconf_pretty_get() + * Fixed: Configure option `CLICON_RESTCONF_PATH` was marked as obsolete but was still used. + * `CLICON_RESTCONF_PATH` is now obsolete for sure + * Instead if you use fgci/nginx: + * Use `restconf/fcgi-socket` + * Ensure `clixon-restconf:fcgi` is set ### Minor features diff --git a/apps/restconf/restconf_handle.c b/apps/restconf/restconf_handle.c index 8bf57f1f..45696646 100644 --- a/apps/restconf/restconf_handle.c +++ b/apps/restconf/restconf_handle.c @@ -94,6 +94,7 @@ struct restconf_handle { clicon_hash_t *rh_params; /* restconf parameters, including http headers */ clixon_auth_type_t rh_auth_type; /* authentication type */ int rh_pretty; /* pretty-print for http replies */ + char *rh_fcgi_socket; /* if-feature fcgi, XXX: use WITH_RESTCONF_FCGI ? */ }; /*! Creates and returns a clicon config handle for other CLICON API calls @@ -115,6 +116,10 @@ restconf_handle_init(void) int restconf_handle_exit(clicon_handle h) { + struct restconf_handle *rh = handle(h); + + if (rh->rh_fcgi_socket) + free(rh->rh_fcgi_socket); clicon_handle_exit(h); /* frees h and options (and streams) */ return 0; } @@ -240,3 +245,36 @@ restconf_pretty_set(clicon_handle h, rh->rh_pretty = pretty; return 0; } + +/*! Get restconf fcgi socket path + * @param[in] h Clicon handle + * @retval socketpath + */ +char* +restconf_fcgi_socket_get(clicon_handle h) +{ + struct restconf_handle *rh = handle(h); + + return rh->rh_fcgi_socket; +} + +/*! Set restconf fcgi socketpath + * @param[in] h Clicon handle + * @param[in] name Data name + * @param[in] val Data value as null-terminated string + * @retval 0 OK + * @retval -1 Error + * Currently using clixon runtime data but there is risk for colliding names + */ +int +restconf_fcgi_socket_set(clicon_handle h, + char *socketpath) +{ + struct restconf_handle *rh = handle(h); + + if ((rh->rh_fcgi_socket = strdup(socketpath)) == NULL){ + clicon_err(OE_RESTCONF, errno, "strdup"); + return -1; + } + return 0; +} diff --git a/apps/restconf/restconf_handle.h b/apps/restconf/restconf_handle.h index b19b7aca..f567c4ea 100644 --- a/apps/restconf/restconf_handle.h +++ b/apps/restconf/restconf_handle.h @@ -51,5 +51,7 @@ clixon_auth_type_t restconf_auth_type_get(clicon_handle h); int restconf_auth_type_set(clicon_handle h, clixon_auth_type_t type); int restconf_pretty_get(clicon_handle h); int restconf_pretty_set(clicon_handle h, int pretty); +char *restconf_fcgi_socket_get(clicon_handle h); +int restconf_fcgi_socket_set(clicon_handle h, char *socketpath); #endif /* _RESTCONF_HANDLE_H_ */ diff --git a/apps/restconf/restconf_lib.c b/apps/restconf/restconf_lib.c index ec245721..4376a3cc 100644 --- a/apps/restconf/restconf_lib.c +++ b/apps/restconf/restconf_lib.c @@ -754,7 +754,15 @@ restconf_config_init(clicon_handle h, char *bstr; cvec *nsc = NULL; int auth_type; - + yang_stmt *yspec; + + if ((yspec = clicon_dbspec_yang(h)) == NULL){ + clicon_err(OE_FATAL, 0, "No DB_SPEC"); + goto done; + } + /* Apply default values (removed in clear function) */ + if (xml_default_recurse(xrestconf, 0) < 0) + goto done; if ((x = xpath_first(xrestconf, nsc, "enable")) != NULL && (enable = xml_body(x)) != NULL){ if (strcmp(enable, "false") == 0){ @@ -778,6 +786,11 @@ restconf_config_init(clicon_handle h, else if (strcmp(bstr, "false") == 0) restconf_pretty_set(h, 0); } + if ((x = xpath_first(xrestconf, nsc, "fcgi-socket")) != NULL && + (bstr = xml_body(x)) != NULL){ + if (restconf_fcgi_socket_set(h, bstr) < 0) + goto done; + } retval = 1; done: return retval; diff --git a/apps/restconf/restconf_main_fcgi.c b/apps/restconf/restconf_main_fcgi.c index e886789d..f10d4b2e 100644 --- a/apps/restconf/restconf_main_fcgi.c +++ b/apps/restconf/restconf_main_fcgi.c @@ -125,6 +125,90 @@ fcgi_params_set(clicon_handle h, return retval; } +/*! Try to get config: inline, config-file or query backend + */ +static int +restconf_main_config(clicon_handle h, + yang_stmt *yspec, + const char *inline_config) +{ + int retval = -1; + struct passwd *pw; + cxobj *xconfig = NULL; + cxobj *xrestconf = NULL; + uint32_t id = 0; + cxobj *xerr = NULL; + int configure_done = 0; /* First try local then backend */ + cvec *nsc = NULL; + int ret; + + /* 1. try inline configure option */ + if (inline_config != NULL && strlen(inline_config)){ + clicon_debug(1, "restconf_main_fcgi using restconf inline config"); + if ((ret = clixon_xml_parse_string(inline_config, YB_MODULE, yspec, &xrestconf, &xerr)) < 0) + goto done; + if (ret == 0){ + clixon_netconf_error(xerr, "Inline restconf config", NULL); + goto done; + } + /* Replace parent w first child */ + if (xml_rootchild(xrestconf, 0, &xrestconf) < 0) + goto done; + } + else if (clicon_option_bool(h, "CLICON_BACKEND_RESTCONF_PROCESS") == 0){ + /* 2. If not read from backend, try to get restconf config from local config-file */ + xrestconf = clicon_conf_restconf(h); + } + /* 3. If no local config, or it is disabled, try to query backend of config. */ + else { + /* Loop to wait for backend starting, try again if not done */ + while (1){ + if (clicon_hello_req(h, &id) < 0){ + if (errno == ENOENT){ + fprintf(stderr, "waiting"); + sleep(1); + continue; + } + clicon_err(OE_UNIX, errno, "clicon_session_id_get"); + goto done; + } + clicon_session_id_set(h, id); + break; + } + if ((nsc = xml_nsctx_init(NULL, CLIXON_RESTCONF_NS)) == NULL) + goto done; + if ((pw = getpwuid(getuid())) == NULL){ + clicon_err(OE_UNIX, errno, "getpwuid"); + goto done; + } + if (clicon_rpc_get_config(h, pw->pw_name, "running", "/restconf", nsc, &xconfig) < 0) + goto done; + if ((xerr = xpath_first(xconfig, NULL, "/rpc-error")) != NULL){ + clixon_netconf_error(xerr, "Get backend restconf config", NULL); + goto done; + } + /* Extract restconf configuration */ + xrestconf = xpath_first(xconfig, nsc, "restconf"); + } + configure_done = 0; + if (xrestconf != NULL && + (configure_done = restconf_config_init(h, xrestconf)) < 0) + goto done; + if (!configure_done){ /* Query backend of config. */ + clicon_err(OE_DAEMON, EFAULT, "Restconf daemon config not found or disabled"); + goto done; + } + retval = 0; + done: + if (nsc) + cvec_free(nsc); + if (inline_config != NULL && strlen(inline_config) && xrestconf) + xml_free(xrestconf); + if (xconfig) + xml_free(xconfig); + return retval; +} + /* XXX Need global variable to for SIGCHLD signal handler */ static clicon_handle _CLICON_HANDLE = NULL; @@ -221,20 +305,11 @@ main(int argc, int finish = 0; char *str; clixon_plugin_t *cp = NULL; - uint32_t id = 0; cvec *nsctx_global = NULL; /* Global namespace context */ size_t cligen_buflen; size_t cligen_bufthreshold; int dbg = 0; - int ret; - cxobj *xrestconf1 = NULL; /* Inline */ - cxobj *xrestconf2 = NULL; /* Local config file */ - cxobj *xconfig3 = NULL; - cxobj *xrestconf3 = NULL; /* Config from backend */ - int configure_done = 0; /* First try local then backend */ - cvec *nsc = NULL; cxobj *xerr = NULL; - struct passwd *pw; char *wwwuser; char *inline_config = NULL; @@ -457,83 +532,13 @@ main(int argc, if (clixon_plugin_start_all(h) < 0) goto done; - /* 1. try inline configure option */ - if (inline_config != NULL && strlen(inline_config)){ - clicon_debug(1, "restconf_main_fcgi using restconf inline config"); - if ((ret = clixon_xml_parse_string(inline_config, YB_MODULE, yspec, &xrestconf1, &xerr)) < 0) - goto done; - if (ret == 0){ - clixon_netconf_error(xerr, "Inline restconf config", NULL); - goto done; - } - /* Replace parent w first child */ - if (xml_rootchild(xrestconf1, 0, &xrestconf1) < 0) - goto done; - if ((ret = restconf_config_init(h, xrestconf1)) < 0) - goto done; - if (ret == 1) - configure_done = 1; - } - else if (clicon_option_bool(h, "CLICON_BACKEND_RESTCONF_PROCESS") == 0){ - /* 2. If not read from backend, try to get restconf config from local config-file */ - if ((xrestconf2 = clicon_conf_restconf(h)) != NULL){ - if ((ret = restconf_config_init(h, xrestconf2)) < 0) - goto done; - if (ret == 1) - configure_done = 1; - } - } - /* 3. If no local config, or it is disabled, try to query backend of config. */ - else { - /* Loop to wait for backend starting, try again if not done */ - while (1){ - if (clicon_hello_req(h, &id) < 0){ - if (errno == ENOENT){ - fprintf(stderr, "waiting"); - sleep(1); - continue; - } - clicon_err(OE_UNIX, errno, "clicon_session_id_get"); - goto done; - } - clicon_session_id_set(h, id); - break; - } - if ((nsc = xml_nsctx_init(NULL, CLIXON_RESTCONF_NS)) == NULL) - goto done; - if ((pw = getpwuid(getuid())) == NULL){ - clicon_err(OE_UNIX, errno, "getpwuid"); - goto done; - } - if (clicon_rpc_get_config(h, pw->pw_name, "running", "/restconf", nsc, &xconfig3) < 0) - goto done; - if ((xerr = xpath_first(xconfig3, NULL, "/rpc-error")) != NULL){ - clixon_netconf_error(xerr, "Get backend restconf config", NULL); - goto done; - } - /* Extract restconf configuration */ - if ((xrestconf3 = xpath_first(xconfig3, nsc, "restconf")) != NULL){ - if ((ret = restconf_config_init(h, xrestconf3)) < 0) - goto done; - if (ret == 1) - configure_done = 1; - } - } - if (!configure_done){ /* Query backend of config. */ - clicon_err(OE_DAEMON, EFAULT, "Restconf daemon config not found or disabled"); + /* Try to get config: inline, config-file or query backend */ + if (restconf_main_config(h, yspec, inline_config) < 0) goto done; - } - /* XXX see restconf_config_init access directly */ - if ((sockpath = clicon_option_str(h, "CLICON_RESTCONF_PATH")) == NULL){ - clicon_err(OE_CFG, errno, "No CLICON_RESTCONF_PATH in clixon configure file"); + if ((sockpath = restconf_fcgi_socket_get(h)) == NULL){ + clicon_err(OE_CFG, 0, "No restconf fcgi-socket (have you set FEATURE fcgi in config?)"); goto done; } - /* XXX CLICON_RESTCONF_PATH is marked as obsolete and should use - * fcgi-socket in clixon-restconf.yang instead */ - if ((sockpath = clicon_option_str(h, "CLICON_RESTCONF_PATH")) == NULL){ - clicon_err(OE_CFG, errno, "No CLICON_RESTCONF_PATH in clixon configure file"); - goto done; - } if (FCGX_Init() != 0){ /* How to cleanup memory after this? */ clicon_err(OE_CFG, errno, "FCGX_Init"); goto done; @@ -649,12 +654,6 @@ main(int argc, } /* while */ retval = 0; done: - if (xrestconf1) - xml_free(xrestconf1); - if (xconfig3) - xml_free(xconfig3); - if (nsc) - cvec_free(nsc); stream_child_freeall(h); restconf_terminate(h); return retval; diff --git a/test/lib.sh b/test/lib.sh index a70766cd..12d5ba50 100755 --- a/test/lib.sh +++ b/test/lib.sh @@ -226,8 +226,13 @@ function restconf_config() AUTH=$1 PRETTY=$2 + if [ ${WITH_RESTCONF} = "fcgi" ]; then + FEATURES="clixon-restconf:fcgi" + else + FEATURES="" + fi if [ $RCPROTO = http ]; then - echo "true$AUTH$PRETTY$DBGdefault
0.0.0.0
80false
" + echo "${FEATURES}true$AUTH$PRETTY$DBGdefault
0.0.0.0
80false
" else certdir=$dir/certs if [ ! -f ${dir}/clixon-server-crt.pem ]; then @@ -240,7 +245,7 @@ function restconf_config() cacerts $cakey $cacert servercerts $cakey $cacert $srvkey $srvcert fi - echo "true$AUTH$PRETTY${certdir}/clixon-server-crt.pem${certdir}/clixon-server-key.pem${certdir}/clixon-ca-crt.pem$DBGdefault
0.0.0.0
443true
" + echo "${FEATURES}true$AUTH$PRETTY${certdir}/clixon-server-crt.pem${certdir}/clixon-server-key.pem${certdir}/clixon-ca-crt.pem$DBGdefault
0.0.0.0
443true
" fi } diff --git a/test/test_restconf.sh b/test/test_restconf.sh index e13d6222..fe6b1088 100755 --- a/test/test_restconf.sh +++ b/test/test_restconf.sh @@ -537,7 +537,9 @@ function testrun() } # Go thru all combinations of IPv4/IPv6, http/https, local/backend config -if ${HAVE_LIBEVHTP}; then +if [ "${WITH_RESTCONF}" = "fcgi" ]; then + protos="http" +elif ${HAVE_LIBEVHTP}; then protos="http" # No plain http for http/2 only fi if [ "${WITH_RESTCONF}" = "native" ]; then diff --git a/test/test_restconf_err.sh b/test/test_restconf_err.sh index 9d0d85eb..bebd0a6b 100755 --- a/test/test_restconf_err.sh +++ b/test/test_restconf_err.sh @@ -23,7 +23,8 @@ # Magic line must be first in script (see README.md) s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exit 0; else return 0; fi -if ! ${HAVE_LIBEVHTP}; then +# Does not work with evhtpnative http/2-only +if [ "${WITH_RESTCONF}" = "native" -a ${HAVE_LIBEVHTP} = false ]; then echo "...skipped: LIBEVHTP is false, must run with http/1 (evhtp)" if [ "$s" = $0 ]; then exit 0; else return 0; fi fi diff --git a/test/test_restconf_internal.sh b/test/test_restconf_internal.sh index 216b9e38..e7004d75 100755 --- a/test/test_restconf_internal.sh +++ b/test/test_restconf_internal.sh @@ -14,7 +14,8 @@ # Magic line must be first in script (see README.md) s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exit 0; else return 0; fi -if ! ${HAVE_LIBEVHTP}; then +# Does not work with evhtpnative http/2-only +if [ "${WITH_RESTCONF}" = "native" -a ${HAVE_LIBEVHTP} = false ]; then echo "...skipped: LIBEVHTP is false, must run with http/1 (evhtp)" if [ "$s" = $0 ]; then exit 0; else return 0; fi fi diff --git a/test/test_restconf_internal_usecases.sh b/test/test_restconf_internal_usecases.sh index 74d61316..9631f5e1 100755 --- a/test/test_restconf_internal_usecases.sh +++ b/test/test_restconf_internal_usecases.sh @@ -25,7 +25,8 @@ # Magic line must be first in script (see README.md) s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exit 0; else return 0; fi -if ! ${HAVE_LIBEVHTP}; then +# Does not work with evhtpnative http/2-only +if [ "${WITH_RESTCONF}" = "native" -a ${HAVE_LIBEVHTP} = false ]; then echo "...skipped: LIBEVHTP is false, must run with http/1 (evhtp)" if [ "$s" = $0 ]; then exit 0; else return 0; fi fi