diff --git a/CHANGELOG.md b/CHANGELOG.md index 1c89b007..d0f3a50c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,17 @@ ## 4.6.0 Expected: July 2020 +### C-API changes on existing features (For developers) + +* Due to name collision with libevent, all clixon event functions prepended with `clixon_`. You need to rename your event functions as follows: + * event_reg_fd() -> clixon_event_reg_fd() + * event_unreg_fd() -> clixon_event_unreg_fd() + * event_reg_timeout() -> clixon_event_reg_timeout() + * event_unreg_timeout() -> clixon_event_unreg_timeout() + * event_poll() -> clixon_event_poll() + * event_loop() -> clixon_event_loop() + * event_exit() -> clixon_event_exit() + ### Minor changes * Added new function `clicon_xml2str()` to complement xml_print and others that returns a malloced string. diff --git a/apps/backend/backend_client.c b/apps/backend/backend_client.c index 4b445fc8..43c4f6b2 100644 --- a/apps/backend/backend_client.c +++ b/apps/backend/backend_client.c @@ -144,7 +144,7 @@ backend_client_rm(clicon_handle h, for (c = *ce_prev; c; c = c->ce_next){ if (c == ce){ if (ce->ce_s){ - event_unreg_fd(ce->ce_s, from_client); + clixon_event_unreg_fd(ce->ce_s, from_client); close(ce->ce_s); ce->ce_s = 0; } diff --git a/apps/backend/backend_main.c b/apps/backend/backend_main.c index acd17847..55fb6ac9 100644 --- a/apps/backend/backend_main.c +++ b/apps/backend/backend_main.c @@ -134,7 +134,7 @@ backend_terminate(clicon_handle h) if (sockfamily==AF_UNIX && lstat(sockpath, &st) == 0) unlink(sockpath); backend_handle_exit(h); /* Also deletes streams. Cannot use h after this. */ - event_exit(); + clixon_event_exit(); clicon_debug(1, "%s done", __FUNCTION__); clicon_log_exit(); return 0; @@ -150,7 +150,7 @@ backend_sig_term(int arg) if (i++ == 0) clicon_log(LOG_NOTICE, "%s: %s: pid: %u Signal %d", __PROGRAM__, __FUNCTION__, getpid(), arg); - clicon_exit_set(); /* checked in event_loop() */ + clicon_exit_set(); /* checked in clixon_event_loop() */ } /*! Create backend server socket and register callback @@ -168,7 +168,7 @@ backend_server_socket(clicon_handle h) return -1; /* ss is a server socket that the clients connect to. The callback therefore accepts clients on ss */ - if (event_reg_fd(ss, backend_accept_client, h, "server socket") < 0) { + if (clixon_event_reg_fd(ss, backend_accept_client, h, "server socket") < 0) { close(ss); return -1; } @@ -923,7 +923,7 @@ main(int argc, if (stream_timer_setup(0, h) < 0) goto done; - if (event_loop() < 0) + if (clixon_event_loop() < 0) goto done; ok: retval = 0; diff --git a/apps/backend/backend_socket.c b/apps/backend/backend_socket.c index 5d6bc82f..75837e61 100644 --- a/apps/backend/backend_socket.c +++ b/apps/backend/backend_socket.c @@ -289,7 +289,7 @@ backend_accept_client(int fd, /* * Here we register callbacks for actual data socket */ - if (event_reg_fd(s, from_client, (void*)ce, "local netconf client socket") < 0) + if (clixon_event_reg_fd(s, from_client, (void*)ce, "local netconf client socket") < 0) goto done; retval = 0; done: diff --git a/apps/cli/cli_common.c b/apps/cli/cli_common.c index 6a691e79..3ab4115b 100644 --- a/apps/cli/cli_common.c +++ b/apps/cli/cli_common.c @@ -987,7 +987,7 @@ cli_notification_cb(int s, clicon_err(OE_PROTO, ESHUTDOWN, "Socket unexpected close"); close(s); errno = ESHUTDOWN; - event_unreg_fd(s, cli_notification_cb); + clixon_event_unreg_fd(s, cli_notification_cb); goto done; } /* XXX pass yang_spec and use xerr*/ diff --git a/apps/netconf/netconf_main.c b/apps/netconf/netconf_main.c index 968d0fb8..376edd89 100644 --- a/apps/netconf/netconf_main.c +++ b/apps/netconf/netconf_main.c @@ -274,7 +274,7 @@ netconf_input_cb(int s, } } /* poll==1 if more, poll==0 if none */ - if ((poll = event_poll(s)) < 0) + if ((poll = clixon_event_poll(s)) < 0) goto done; if (poll == 0) break; /* No data to read */ @@ -338,7 +338,7 @@ netconf_terminate(clicon_handle h) if ((x = clicon_conf_xml(h)) != NULL) xml_free(x); xpath_optimize_exit(); - event_exit(); + clixon_event_exit(); clicon_handle_exit(h); clicon_log_exit(); return 0; @@ -590,7 +590,7 @@ main(int argc, /* Send hello to northbound client */ if (!quiet) send_hello(h, 1, id); - if (event_reg_fd(0, netconf_input_cb, h, "netconf socket") < 0) + if (clixon_event_reg_fd(0, netconf_input_cb, h, "netconf socket") < 0) goto done; if (debug) clicon_option_dump(h, debug); @@ -598,10 +598,10 @@ main(int argc, struct timeval t; gettimeofday(&t, NULL); timeradd(&t, &tv, &t); - if (event_reg_timeout(t, timeout_fn, NULL, "timeout") < 0) + if (clixon_event_reg_timeout(t, timeout_fn, NULL, "timeout") < 0) goto done; } - if (event_loop() < 0) + if (clixon_event_loop() < 0) goto done; retval = 0; done: diff --git a/apps/netconf/netconf_rpc.c b/apps/netconf/netconf_rpc.c index 39b003c1..ff9ba58a 100644 --- a/apps/netconf/netconf_rpc.c +++ b/apps/netconf/netconf_rpc.c @@ -437,7 +437,7 @@ netconf_notification_cb(int s, clicon_err(OE_PROTO, ESHUTDOWN, "Socket unexpected close"); close(s); errno = ESHUTDOWN; - event_unreg_fd(s, netconf_notification_cb); + clixon_event_unreg_fd(s, netconf_notification_cb); goto done; } yspec = clicon_dbspec_yang(h); @@ -520,7 +520,7 @@ netconf_create_subscription(clicon_handle h, goto done; if (xpath_first(*xret, NULL, "rpc-reply/rpc-error") != NULL) goto ok; - if (event_reg_fd(s, + if (clixon_event_reg_fd(s, netconf_notification_cb, h, "notification socket") < 0) diff --git a/apps/restconf/Makefile.in b/apps/restconf/Makefile.in index ce811dfa..92a9124b 100644 --- a/apps/restconf/Makefile.in +++ b/apps/restconf/Makefile.in @@ -86,11 +86,11 @@ APPL = clixon_restconf_$(with_restconf) endif # Common source - not accessible from plugin - independent of restconf package (fcgi|evhtp) -APPSRC = restconf_lib.c +#APPSRC = restconf_lib.c # Fcgi-specific source including main ifeq ($(with_restconf),fcgi) -APPSRC += restconf_fcgi_lib.c +APPSRC = restconf_fcgi_lib.c APPSRC += restconf_fcgi_main.c APPSRC += restconf_methods.c # These should be moved ^ APPSRC += restconf_methods_post.c @@ -100,19 +100,30 @@ endif # Evhtp-specific source including main ifeq ($(with_restconf),evhtp) -APPSRC += restconf_evhtp_main.c +APPSRC = restconf_evhtp_main.c endif APPOBJ = $(APPSRC:.c=.o) -all: $(APPL) +# Accessible from plugin +LIBSRC = restconf_lib.c +LIBOBJ = $(LIBSRC:.c=.o) + +# This lib is very small but used for clixon restconf applications to access clixon restconf lib +# functions. Mostly for future use +MYNAME = clixon_restconf +MYLIBLINK = lib$(MYNAME)$(SH_SUFFIX) +MYLIB = $(MYLIBLINK).$(CLIXON_MAJOR).$(CLIXON_MINOR) +MYLIBSO = $(MYLIBLINK).$(CLIXON_MAJOR) + +all: $(MYLIB) $(APPL) # Dependency of clixon library $(top_srcdir)/lib/src/$(CLIXON_LIB): (cd $(top_srcdir)/lib/src && $(MAKE) $(MFLAGS) $(CLIXON_LIB)) clean: - rm -f *.core $(APPL) $(APPOBJ) *.o # extra .o to clean residue if with_restconf changes + rm -f $(LIBOBJ) *.core $(APPL) $(APPOBJ) *.o $(MYLIB) $(MYLIBSO) $(MYLIBLINK) # extra .o to clean residue if with_restconf changes distclean: clean rm -f Makefile *~ .depend @@ -122,7 +133,7 @@ distclean: clean # Also create a libexec/ directory for writeable/temporary files. # Put config file in etc/ # Also a rule for letting www-dir be owned by www-data, which only works for sudo -install: $(APPL) +install: install-lib $(APPL) ifeq ($(shell whoami),root) install -d -m 0755 -o $(wwwuser) -g $(wwwuser) $(DESTDIR)$(wwwdir) else @@ -130,10 +141,19 @@ else endif install -m 0755 $(INSTALLFLAGS) $(APPL) $(DESTDIR)$(wwwdir) -install-include: +install-lib: $(MYLIB) + install -d -m 0755 $(DESTDIR)$(libdir) + install -m 0644 $(INSTALLFLAGS) $(MYLIB) $(DESTDIR)$(libdir) + ln -sf $(MYLIB) $(DESTDIR)$(libdir)/$(MYLIBSO) # -l:libclixon_restconf.so.2 + ln -sf $(MYLIBSO) $(DESTDIR)$(libdir)/$(MYLIBLINK) # -l:libclixon_restconf.so + +install-include: clixon_restconf.h + install -d -m 0755 $(DESTDIR)$(includedir)/clixon + install -m 0644 $^ $(DESTDIR)$(includedir)/clixon uninstall: rm -f $(DESTDIR)$(wwwdir)/$(APPL) + rm -f $(DESTDIR)$(libdir)/$(MYLIBLINK)* .SUFFIXES: .SUFFIXES: .c .o @@ -141,8 +161,18 @@ uninstall: .c.o: $(CC) $(INCLUDES) -D__PROGRAM__=\"clixon_restconf\" $(CPPFLAGS) $(CFLAGS) -c $< -$(APPL) : $(APPOBJ) - $(CC) $(LDFLAGS) $(APPOBJ) $(LIBS) -o $@ +$(APPL) : $(APPOBJ) $(MYLIBLINK) $(LIBDEPS) + $(CC) $(LDFLAGS) $(APPOBJ) -L. $(MYLIB) $(LIBS) -o $@ + +$(MYLIB) : $(LIBOBJ) $(LIBDEPS) +ifeq ($(HOST_VENDOR),apple) + $(CC) $(LDFLAGS) -shared -undefined dynamic_lookup -o $@ $(LIBOBJ) $(LIBS) +else + $(CC) $(LDFLAGS) -shared -Wl,-soname,$(MYLIBSO) -o $@ $(LIBOBJ) $(LIBS) -Wl,-soname=$(MYLIBSO) +endif + +# link-name is needed for application linking, eg for clixon_cli and clixon_config +$(MYLIBLINK) : $(MYLIB) TAGS: find . -name '*.[chyl]' -print | etags - diff --git a/apps/restconf/clixon_restconf.h b/apps/restconf/clixon_restconf.h new file mode 100644 index 00000000..8cf332d0 --- /dev/null +++ b/apps/restconf/clixon_restconf.h @@ -0,0 +1,66 @@ +/* + * + ***** BEGIN LICENSE BLOCK ***** + + Copyright (C) 2009-2019 Olof Hagsand + + This file is part of CLIXON. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + Alternatively, the contents of this file may be used under the terms of + the GNU General Public License Version 3 or later (the "GPL"), + in which case the provisions of the GPL are applicable instead + of those above. If you wish to allow use of your version of this file only + under the terms of the GPL, and not to allow others to + use your version of this file under the terms of Apache License version 2, + indicate your decision by deleting the provisions above and replace them with + the notice and other provisions required by the GPL. If you do not delete + the provisions above, a recipient may use your version of this file under + the terms of any one of the Apache License version 2 or the GPL. + + ***** END LICENSE BLOCK ***** + + * The exported interface to plugins. External apps (eg frontend restconf plugins) + * should only include this file (not the restconf_*.h) + */ + +#ifndef _CLIXON_RESTCONF_H_ +#define _CLIXON_RESTCONF_H_ + +/* + * Types (also in restconf_lib.h) + */ +enum restconf_media{ + YANG_DATA_JSON, /* "application/yang-data+json" */ + YANG_DATA_XML, /* "application/yang-data+xml" */ + YANG_PATCH_JSON, /* "application/yang-patch+json" */ + YANG_PATCH_XML /* "application/yang-patch+xml" */ +}; +typedef enum restconf_media restconf_media; + +/* + * Prototypes (also in restconf_lib.h) + */ +int restconf_err2code(char *tag); +const char *restconf_code2reason(int code); +const restconf_media restconf_media_str2int(char *media); +const char *restconf_media_int2str(restconf_media media); +int get_user_cookie(char *cookiestr, char *attribute, char **val); +int restconf_terminate(clicon_handle h); +int restconf_insert_attributes(cxobj *xdata, cvec *qvec); +int restconf_main_extension_cb(clicon_handle h, yang_stmt *yext, yang_stmt *ys); +char *clixon_restconf_param_get(clicon_handle h, char *param); +int clixon_restconf_param_set(clicon_handle h, char *param, char *val); + +#endif /* _CLIXON_RESTCONF_H_ */ diff --git a/apps/restconf/restconf_evhtp_main.c b/apps/restconf/restconf_evhtp_main.c index ec499578..da646841 100644 --- a/apps/restconf/restconf_evhtp_main.c +++ b/apps/restconf/restconf_evhtp_main.c @@ -105,7 +105,7 @@ restconf_sig_term(int arg) #endif restconf_terminate(_CLICON_HANDLE); } - clicon_exit_set(); /* checked in event_loop() */ + clicon_exit_set(); /* checked in clixon_event_loop() */ exit(-1); } @@ -240,7 +240,7 @@ usage(clicon_handle h, exit(0); } -/*! Main routine for libhttp restconf +/*! Main routine for libevhtp restconf */ int main(int argc, diff --git a/apps/restconf/restconf_fcgi_lib.c b/apps/restconf/restconf_fcgi_lib.c index b817738d..ea2371d4 100644 --- a/apps/restconf/restconf_fcgi_lib.c +++ b/apps/restconf/restconf_fcgi_lib.c @@ -64,36 +64,16 @@ #include "restconf_lib.h" #include "restconf_fcgi_lib.h" -/*! Return media_in from Content-Type, -1 if not found or unrecognized - * @note media-type syntax does not support parameters - * @see RFC7231 Sec 3.1.1.1 for media-type syntax type: - * media-type = type "/" subtype *( OWS ";" OWS parameter ) - * type = token - * subtype = token - * - */ -restconf_media -restconf_content_type(FCGX_Request *r) -{ - char *str; - restconf_media m; - - if ((str = FCGX_GetParam("HTTP_CONTENT_TYPE", r->envp)) == NULL) - return -1; - if ((int)(m = restconf_media_str2int(str)) == -1) - return -1; - return m; -} - /*! HTTP error 400 * @param[in] r Fastcgi request handle */ int -restconf_badrequest(FCGX_Request *r) +restconf_badrequest(clicon_handle h, + FCGX_Request *r) { char *path; - path = FCGX_GetParam("REQUEST_URI", r->envp); + path = clixon_restconf_param_get(h, "REQUEST_URI"); FCGX_SetExitStatus(400, r->out); FCGX_FPrintF(r->out, "Status: 400 Bad Request\r\n"); /* 400 bad request */ FCGX_FPrintF(r->out, "Content-Type: text/html\r\n\r\n"); @@ -107,11 +87,12 @@ restconf_badrequest(FCGX_Request *r) * @param[in] r Fastcgi request handle */ int -restconf_unauthorized(FCGX_Request *r) +restconf_unauthorized(clicon_handle h, + FCGX_Request *r) { char *path; - path = FCGX_GetParam("REQUEST_URI", r->envp); + path = clixon_restconf_param_get(h, "REQUEST_URI"); FCGX_SetExitStatus(401, r->out); FCGX_FPrintF(r->out, "Status: 401 Unauthorized\r\n"); /* 401 unauthorized */ FCGX_FPrintF(r->out, "Content-Type: text/html\r\n\r\n"); @@ -124,11 +105,12 @@ restconf_unauthorized(FCGX_Request *r) * @param[in] r Fastcgi request handle */ int -restconf_forbidden(FCGX_Request *r) +restconf_forbidden(clicon_handle h, + FCGX_Request *r) { char *path; - path = FCGX_GetParam("REQUEST_URI", r->envp); + path = clixon_restconf_param_get(h, "REQUEST_URI"); FCGX_SetExitStatus(403, r->out); FCGX_FPrintF(r->out, "Status: 403 Forbidden\r\n"); /* 403 forbidden */ FCGX_FPrintF(r->out, "Content-Type: text/html\r\n\r\n"); @@ -141,11 +123,12 @@ restconf_forbidden(FCGX_Request *r) * @param[in] r Fastcgi request handle */ int -restconf_notfound(FCGX_Request *r) +restconf_notfound(clicon_handle h, + FCGX_Request *r) { char *path; - path = FCGX_GetParam("REQUEST_URI", r->envp); + path = clixon_restconf_param_get(h, "REQUEST_URI"); FCGX_SetExitStatus(404, r->out); FCGX_FPrintF(r->out, "Status: 404 Not Found\r\n"); /* 404 not found */ FCGX_FPrintF(r->out, "Content-Type: text/html\r\n\r\n"); @@ -160,11 +143,12 @@ restconf_notfound(FCGX_Request *r) * @param[in] r Fastcgi request handle */ int -restconf_notacceptable(FCGX_Request *r) +restconf_notacceptable(clicon_handle h, + FCGX_Request *r) { char *path; - path = FCGX_GetParam("REQUEST_URI", r->envp); + path = clixon_restconf_param_get(h, "REQUEST_URI"); FCGX_SetExitStatus(406, r->out); FCGX_FPrintF(r->out, "Status: 406 Not Acceptable\r\n"); /* 406 not acceptible */ @@ -206,12 +190,13 @@ restconf_unsupported_media(FCGX_Request *r) * @param[in] r Fastcgi request handle */ int -restconf_internal_server_error(FCGX_Request *r) +restconf_internal_server_error(clicon_handle h, + FCGX_Request *r) { char *path; clicon_debug(1, "%s", __FUNCTION__); - path = FCGX_GetParam("REQUEST_URI", r->envp); + path = clixon_restconf_param_get(h, "REQUEST_URI"); FCGX_FPrintF(r->out, "Status: 500 Internal Server Error\r\n"); /* 500 internal server error */ FCGX_FPrintF(r->out, "Content-Type: text/html\r\n\r\n"); FCGX_FPrintF(r->out, "

Internal server error when accessing %s

\n", path); @@ -231,23 +216,6 @@ restconf_notimplemented(FCGX_Request *r) return 0; } -/*! - * @param[in] r Fastcgi request handle - */ -static int -printparam(FCGX_Request *r, - char *e, - int dbgp) -{ - char *p = FCGX_GetParam(e, r->envp); - - if (dbgp) - clicon_debug(1, "%s = '%s'", e, p?p:""); - else - FCGX_FPrintF(r->out, "%s = '%s'\n", e, p?p:""); - return 0; -} - /*! Print all FCGI headers * @param[in] r Fastcgi request handle * @see https://nginx.org/en/docs/http/ngx_http_core_module.html#var_https @@ -256,43 +224,73 @@ int restconf_test(FCGX_Request *r, int dbg) { - printparam(r, "QUERY_STRING", dbg); - printparam(r, "REQUEST_METHOD", dbg); - printparam(r, "CONTENT_TYPE", dbg); - printparam(r, "CONTENT_LENGTH", dbg); - printparam(r, "SCRIPT_FILENAME", dbg); - printparam(r, "SCRIPT_NAME", dbg); - printparam(r, "REQUEST_URI", dbg); - printparam(r, "DOCUMENT_URI", dbg); - printparam(r, "DOCUMENT_ROOT", dbg); - printparam(r, "SERVER_PROTOCOL", dbg); - printparam(r, "GATEWAY_INTERFACE", dbg); - printparam(r, "SERVER_SOFTWARE", dbg); - printparam(r, "REMOTE_ADDR", dbg); - printparam(r, "REMOTE_PORT", dbg); - printparam(r, "SERVER_ADDR", dbg); - printparam(r, "SERVER_PORT", dbg); - printparam(r, "SERVER_NAME", dbg); - printparam(r, "HTTP_COOKIE", dbg); - printparam(r, "HTTPS", dbg); - printparam(r, "HTTP_HOST", dbg); - printparam(r, "HTTP_ACCEPT", dbg); - printparam(r, "HTTP_CONTENT_TYPE", dbg); - printparam(r, "HTTP_AUTHORIZATION", dbg); -#if 0 /* For debug */ + char **environ = r->envp; + int i; + clicon_debug(1, "All environment vars:"); - { - extern char **environ; - int i; - for (i = 0; environ[i] != NULL; i++){ - clicon_debug(1, "%s", environ[i]); - } + for (i = 0; environ[i] != NULL; i++){ + clicon_debug(1, "%s", environ[i]); } - clicon_debug(1, "End environment vars:"); -#endif + clicon_debug(1, "End environment vars"); return 0; } +/*! Convert FCGI parameters to clixon runtime data + * @param[in] h Clixon handle + * @param[in] envp Fastcgi request handle parameter array on the format "=" + * @see https://nginx.org/en/docs/http/ngx_http_core_module.html#var_https + */ +int +clixon_restconf_params_set(clicon_handle h, + char **envp) +{ + int retval = -1; + int i; + char *param; + char *val; + + clicon_debug(1, "%s", __FUNCTION__); + for (i = 0; envp[i] != NULL; i++){ /* on the form = */ + if (clixon_strsplit(envp[i], '=', ¶m, &val) < 0) + goto done; + clicon_debug(1, "%s param:%s val:%s", __FUNCTION__, param, val); + if (clixon_restconf_param_set(h, param, val) < 0) + goto done; + } + retval = 0; + done: + clicon_debug(1, "%s %d", __FUNCTION__, retval); + return retval; +} + +/*! Clear all FCGI parameters in an environment + * @param[in] h Clixon handle + * @param[in] envp Fastcgi request handle parameter array on the format "=" + * @see https://nginx.org/en/docs/http/ngx_http_core_module.html#var_https + */ +int +clixon_restconf_params_clear(clicon_handle h, + char **envp) +{ + int retval = -1; + int i; + char *param; + char *val; + + clicon_debug(1, "%s", __FUNCTION__); + for (i = 0; envp[i] != NULL; i++){ /* on the form = */ + if (clixon_strsplit(envp[i], '=', ¶m, &val) < 0) + goto done; + clicon_debug(1, "%s param:%s val:%s", __FUNCTION__, param, val); + if (clixon_restconf_param_del(h, param) < 0) + goto done; + } + retval = 0; + done: + clicon_debug(1, "%s %d", __FUNCTION__, retval); + return retval; +} + /*! * @param[in] r Fastcgi request handle */ @@ -431,7 +429,8 @@ api_return_err(clicon_handle h, * @note ports are ignored */ int -http_location(FCGX_Request *r, +http_location(clicon_handle h, + FCGX_Request *r, cxobj *xobj) { int retval = -1; @@ -440,9 +439,9 @@ http_location(FCGX_Request *r, char *request_uri; cbuf *cb = NULL; - https = FCGX_GetParam("HTTPS", r->envp); - host = FCGX_GetParam("HTTP_HOST", r->envp); - request_uri = FCGX_GetParam("REQUEST_URI", r->envp); + https = clixon_restconf_param_get(h, "HTTPS"); + host = clixon_restconf_param_get(h, "HTTP_HOST"); + request_uri = clixon_restconf_param_get(h, "REQUEST_URI"); if (xobj != NULL){ if ((cb = cbuf_new()) == NULL){ clicon_err(OE_UNIX, 0, "cbuf_new"); @@ -468,23 +467,4 @@ http_location(FCGX_Request *r, return retval; } -/*! Extract uri-encoded uri-path from fastcgi parameters - * Use REQUEST_URI parameter and strip ?args - * REQUEST_URI have args and is encoded - * eg /interface=eth%2f0%2f0?insert=first - * DOCUMENT_URI dont have args and is not encoded - * eg /interface=eth/0/0 - * causes problems with eg /interface=eth%2f0%2f0 - */ -char * -restconf_uripath(FCGX_Request *r) -{ - char *path; - char *q; - - path = FCGX_GetParam("REQUEST_URI", r->envp); - if ((q = index(path, '?')) != NULL) - *q = '\0'; - return path; -} diff --git a/apps/restconf/restconf_fcgi_lib.h b/apps/restconf/restconf_fcgi_lib.h index 9d9ce829..23142089 100644 --- a/apps/restconf/restconf_fcgi_lib.h +++ b/apps/restconf/restconf_fcgi_lib.h @@ -40,21 +40,21 @@ /* * Prototypes */ -restconf_media restconf_content_type(FCGX_Request *r); -int restconf_badrequest(FCGX_Request *r); -int restconf_unauthorized(FCGX_Request *r); -int restconf_forbidden(FCGX_Request *r); -int restconf_notfound(FCGX_Request *r); -int restconf_notacceptable(FCGX_Request *r); +int restconf_badrequest(clicon_handle h, FCGX_Request *r); +int restconf_unauthorized(clicon_handle h, FCGX_Request *r); +int restconf_forbidden(clicon_handle h, FCGX_Request *r); +int restconf_notfound(clicon_handle h, FCGX_Request *r); +int restconf_notacceptable(clicon_handle h, FCGX_Request *r); int restconf_conflict(FCGX_Request *r); int restconf_unsupported_media(FCGX_Request *r); -int restconf_internal_server_error(FCGX_Request *r); +int restconf_internal_server_error(clicon_handle h, FCGX_Request *r); int restconf_notimplemented(FCGX_Request *r); int restconf_test(FCGX_Request *r, int dbg); +int clixon_restconf_params_set(clicon_handle h, char **envp); +int clixon_restconf_params_clear(clicon_handle h, char **envp); cbuf *readdata(FCGX_Request *r); int api_return_err(clicon_handle h, FCGX_Request *r, cxobj *xerr, int pretty, enum restconf_media media, int code); -int http_location(FCGX_Request *r, cxobj *xobj); -char *restconf_uripath(FCGX_Request *r); +int http_location(clicon_handle h, FCGX_Request *r, cxobj *xobj); #endif /* _RESTCONF_FCGI_LIB_H_ */ diff --git a/apps/restconf/restconf_fcgi_main.c b/apps/restconf/restconf_fcgi_main.c index 118ad915..f5f07c23 100644 --- a/apps/restconf/restconf_fcgi_main.c +++ b/apps/restconf/restconf_fcgi_main.c @@ -114,7 +114,7 @@ api_data(clicon_handle h, char *request_method; clicon_debug(1, "%s", __FUNCTION__); - request_method = FCGX_GetParam("REQUEST_METHOD", r->envp); + request_method = clixon_restconf_param_get(h, "REQUEST_METHOD"); clicon_debug(1, "%s method:%s", __FUNCTION__, request_method); if (strcmp(request_method, "OPTIONS")==0) retval = api_data_options(h, r); @@ -131,7 +131,7 @@ api_data(clicon_handle h, else if (strcmp(request_method, "DELETE")==0) retval = api_data_delete(h, r, api_path, pi, pretty, media_out); else - retval = restconf_notfound(r); + retval = restconf_notfound(h, r); clicon_debug(1, "%s retval:%d", __FUNCTION__, retval); return retval; } @@ -161,7 +161,7 @@ api_operations(clicon_handle h, char *request_method; clicon_debug(1, "%s", __FUNCTION__); - request_method = FCGX_GetParam("REQUEST_METHOD", r->envp); + request_method = clixon_restconf_param_get(h, "REQUEST_METHOD"); clicon_debug(1, "%s method:%s", __FUNCTION__, request_method); if (strcmp(request_method, "GET")==0) retval = api_operations_get(h, r, path, pi, qvec, data, pretty, media_out); @@ -169,7 +169,7 @@ api_operations(clicon_handle h, retval = api_operations_post(h, r, path, pi, qvec, data, pretty, media_out); else - retval = restconf_notfound(r); + retval = restconf_notfound(h, r); return retval; } @@ -340,8 +340,8 @@ api_restconf(clicon_handle h, cxobj *xerr; clicon_debug(1, "%s", __FUNCTION__); - path = restconf_uripath(r); - query = FCGX_GetParam("QUERY_STRING", r->envp); + path = restconf_uripath(h); + query = clixon_restconf_param_get(h, "QUERY_STRING"); pretty = clicon_option_bool(h, "CLICON_RESTCONF_PRETTY"); /* Get media for output (proactive negotiation) RFC7231 by using @@ -349,7 +349,7 @@ api_restconf(clicon_handle h, * operation POST, etc * If accept is * default is yang-json */ - if ((media_str = FCGX_GetParam("HTTP_ACCEPT", r->envp)) == NULL){ + if ((media_str = clixon_restconf_param_get(h, "HTTP_ACCEPT")) == NULL){ // retval = restconf_unsupported_media(r); // goto done; } @@ -367,15 +367,15 @@ api_restconf(clicon_handle h, goto done; /* Sanity check of path. Should be /restconf/ */ if (pn < 2){ - restconf_notfound(r); + restconf_notfound(h, r); goto ok; } if (strlen(pvec[0]) != 0){ - retval = restconf_notfound(r); + retval = restconf_notfound(h, r); goto done; } if (strcmp(pvec[1], RESTCONF_API)){ - retval = restconf_notfound(r); + retval = restconf_notfound(h, r); goto done; } restconf_test(r, 1); @@ -385,7 +385,7 @@ api_restconf(clicon_handle h, goto done; } if ((method = pvec[2]) == NULL){ - retval = restconf_notfound(r); + retval = restconf_notfound(h, r); goto done; } clicon_debug(1, "%s: method=%s", __FUNCTION__, method); @@ -441,7 +441,7 @@ api_restconf(clicon_handle h, else if (strcmp(method, "test") == 0) restconf_test(r, 0); else - restconf_notfound(r); + restconf_notfound(h, r); ok: retval = 0; done: @@ -483,7 +483,7 @@ restconf_sig_term(int arg) stream_child_freeall(_CLICON_HANDLE); restconf_terminate(_CLICON_HANDLE); } - clicon_exit_set(); /* checked in event_loop() */ + clicon_exit_set(); /* checked in clixon_event_loop() */ exit(-1); } @@ -790,8 +790,12 @@ main(int argc, clicon_session_id_set(h, id); start++; } - - if ((path = FCGX_GetParam("REQUEST_URI", r->envp)) != NULL){ + /* Translate from FCGI parameter form to Clixon runtime data + * XXX: potential name collision? + */ + if (clixon_restconf_params_set(h, r->envp) < 0) + goto done; + if ((path = clixon_restconf_param_get(h, "REQUEST_URI")) != NULL){ clicon_debug(1, "path: %s", path); if (strncmp(path, "/" RESTCONF_API, strlen("/" RESTCONF_API)) == 0) api_restconf(h, r); /* This is the function */ @@ -803,11 +807,13 @@ main(int argc, } else{ clicon_debug(1, "top-level %s not found", path); - restconf_notfound(r); + restconf_notfound(h, r); } } else clicon_debug(1, "NULL URI"); + if (clixon_restconf_params_clear(h, r->envp) < 0) + goto done; if (finish) FCGX_Finish_r(r); else{ /* A handler is forked so we initiate a new request after instead @@ -817,7 +823,8 @@ main(int argc, goto done; } } - } + + } /* while */ retval = 0; done: stream_child_freeall(h); diff --git a/apps/restconf/restconf_lib.c b/apps/restconf/restconf_lib.c index adf30863..a361f581 100644 --- a/apps/restconf/restconf_lib.c +++ b/apps/restconf/restconf_lib.c @@ -176,6 +176,27 @@ restconf_media_int2str(restconf_media media) return clicon_int2str(http_media_map, media); } +/*! Return media_in from Content-Type, -1 if not found or unrecognized + * @note media-type syntax does not support parameters + * @see RFC7231 Sec 3.1.1.1 for media-type syntax type: + * media-type = type "/" subtype *( OWS ";" OWS parameter ) + * type = token + * subtype = token + * + */ +restconf_media +restconf_content_type(clicon_handle h) +{ + char *str; + restconf_media m; + + if ((str = clixon_restconf_param_get(h, "HTTP_CONTENT_TYPE")) == NULL) + return -1; + if ((int)(m = restconf_media_str2int(str)) == -1) + return -1; + return m; +} + /*! Parse a cookie string and return value of cookie attribute * @param[in] cookiestr cookie string according to rfc6265 (modified) * @param[in] attribute cookie attribute @@ -389,3 +410,62 @@ restconf_main_extension_cb(clicon_handle h, done: return retval; } + +/*! Get restconf http parameter + * @param[in] h Clicon handle + * @param[in] name Data name + * @retval val Data value as string + * Currently using clixon runtime data but there is risk for colliding names + */ +char * +clixon_restconf_param_get(clicon_handle h, + char *param) +{ + char *val; + if (clicon_data_get(h, param, &val) < 0) + return NULL; + return val; +} + +/*! Set restconf http parameter + * @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 +clixon_restconf_param_set(clicon_handle h, + char *param, + char *val) +{ + return clicon_data_set(h, param, val); +} + +int +clixon_restconf_param_del(clicon_handle h, + char *param) +{ + return clicon_data_del(h, param); +} + +/*! Extract uri-encoded uri-path from fastcgi parameters + * Use REQUEST_URI parameter and strip ?args + * REQUEST_URI have args and is encoded + * eg /interface=eth%2f0%2f0?insert=first + * DOCUMENT_URI dont have args and is not encoded + * eg /interface=eth/0/0 + * causes problems with eg /interface=eth%2f0%2f0 + */ +char * +restconf_uripath(clicon_handle h) +{ + char *path; + char *q; + + path = clixon_restconf_param_get(h, "REQUEST_URI"); + if ((q = index(path, '?')) != NULL) + *q = '\0'; + return path; +} diff --git a/apps/restconf/restconf_lib.h b/apps/restconf/restconf_lib.h index f1ac15e3..fa9f56bf 100644 --- a/apps/restconf/restconf_lib.h +++ b/apps/restconf/restconf_lib.h @@ -72,9 +72,14 @@ int restconf_err2code(char *tag); const char *restconf_code2reason(int code); const restconf_media restconf_media_str2int(char *media); const char *restconf_media_int2str(restconf_media media); -int get_user_cookie(char *cookiestr, char *attribute, char **val); -int restconf_terminate(clicon_handle h); -int restconf_insert_attributes(cxobj *xdata, cvec *qvec); -int restconf_main_extension_cb(clicon_handle h, yang_stmt *yext, yang_stmt *ys); +restconf_media restconf_content_type(clicon_handle h); +int get_user_cookie(char *cookiestr, char *attribute, char **val); +int restconf_terminate(clicon_handle h); +int restconf_insert_attributes(cxobj *xdata, cvec *qvec); +int restconf_main_extension_cb(clicon_handle h, yang_stmt *yext, yang_stmt *ys); +char *clixon_restconf_param_get(clicon_handle h, char *param); +int clixon_restconf_param_set(clicon_handle h, char *param, char *val); +int clixon_restconf_param_del(clicon_handle h, char *param); +char *restconf_uripath(clicon_handle h); #endif /* _RESTCONF_LIB_H_ */ diff --git a/apps/restconf/restconf_libhttp_main.c b/apps/restconf/restconf_libhttp_main.c deleted file mode 100644 index 74c214d4..00000000 --- a/apps/restconf/restconf_libhttp_main.c +++ /dev/null @@ -1,605 +0,0 @@ -/* - * - ***** BEGIN LICENSE BLOCK ***** - - Copyright (C) 2009-2019 Olof Hagsand - Copyright (C) 2020 Olof Hagsand and Rubicon Communications, LLC(Netgate) - - This file is part of CLIXON. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - - Alternatively, the contents of this file may be used under the terms of - the GNU General Public License Version 3 or later (the "GPL"), - in which case the provisions of the GPL are applicable instead - of those above. If you wish to allow use of your version of this file only - under the terms of the GPL, and not to allow others to - use your version of this file under the terms of Apache License version 2, - indicate your decision by deleting the provisions above and replace them with - the notice and other provisions required by the GPL. If you do not delete - the provisions above, a recipient may use your version of this file under - the terms of any one of the Apache License version 2 or the GPL. - - ***** END LICENSE BLOCK ***** - - */ - -/* XXX temp constant should go away, */ -#undef _LIBHTTP_NYI - -#ifdef HAVE_CONFIG_H -#include "clixon_config.h" /* generated by config & autoconf */ -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include /* chmod */ - -/* libhttp */ -#include - -/* cligen */ -#include - -/* clicon */ -#include - -/* restconf */ - -#include "restconf_lib.h" -#if 0 /* These are all dependent on FCGX */ -#include "restconf_methods.h" -#include "restconf_methods_get.h" -#include "restconf_methods_post.h" -#include "restconf_stream.h" -#endif - -/* Command line options to be passed to getopt(3) */ -#define RESTCONF_OPTS "hD:f:l:p:d:y:a:u:o:P:" - -/* Need global variable to for signal handler XXX */ -static clicon_handle _CLICON_HANDLE = NULL; - -/*! Signall terminates process - */ -static void -restconf_sig_term(int arg) -{ - static int i=0; - - if (i++ == 0) - clicon_log(LOG_NOTICE, "%s: %s: pid: %u Signal %d", - __PROGRAM__, __FUNCTION__, getpid(), arg); - else - exit(-1); - if (_CLICON_HANDLE){ -#ifdef _LIBHTTP_NYI - stream_child_freeall(_CLICON_HANDLE); -#endif - restconf_terminate(_CLICON_HANDLE); - } - clicon_exit_set(); /* checked in event_loop() */ - exit(-1); -} - -static void -restconf_sig_child(int arg) -{ - int status; - int pid; - - if ((pid = waitpid(-1, &status, 0)) != -1 && WIFEXITED(status)) -#ifdef _LIBHTTP_NYI - stream_child_free(_CLICON_HANDLE, pid); -#else - ; -#endif -} - -/*! Usage help routine - * @param[in] argv0 command line - * @param[in] h Clicon handle - */ -static void -usage(clicon_handle h, - char *argv0) - -{ - fprintf(stderr, "usage:%s [options]\n" - "where options are\n" - "\t-h \t\t Help\n" - "\t-D \t Debug level\n" - "\t-f \t Configuration file (mandatory)\n" - "\t-l > \t Log on (s)yslog, (f)ile (syslog is default)\n" - "\t-p \t Yang directory path (see CLICON_YANG_DIR)\n" - "\t-d \t Specify restconf plugin directory dir (default: %s)\n" - "\t-y \t Load yang spec file (override yang main module)\n" - "\t-a UNIX|IPv4|IPv6 Internal backend socket family\n" - "\t-u \t Internal socket domain path or IP addr (see -a)\n" - "\t-o \"