diff --git a/.gitignore b/.gitignore index 6826005a..04d0e8bd 100644 --- a/.gitignore +++ b/.gitignore @@ -35,6 +35,7 @@ apps/cli/test.c apps/dbctrl/clixon_dbctrl apps/netconf/clixon_netconf apps/restconf/clixon_restconf +apps/restconf/clixon_restconf_libhttp apps/xmldb/clixon_xmldb etc/clixonrc 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/Makefile.in b/apps/Makefile.in index 53eedb6f..47920830 100644 --- a/apps/Makefile.in +++ b/apps/Makefile.in @@ -48,7 +48,7 @@ SUBDIRS += cli SUBDIRS += netconf # See configure.ac -ifeq ($(with_restconf),yes) +ifdef with_restconf SUBDIRS += restconf endif diff --git a/apps/backend/backend_client.c b/apps/backend/backend_client.c index 98474914..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; } @@ -1510,8 +1510,10 @@ verify_nacm_user(enum nacm_credentials_t mode, if (mode == NC_EXCEPT){ if (strcmp(peername, "root") == 0) goto ok; +#ifdef WITH_RESTCONF if (strcmp(peername, WWWUSER) == 0) goto ok; +#endif } if (strcmp(peername, nacmname) != 0){ if ((cbmsg = cbuf_new()) == NULL){ 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/Makefile.in b/apps/cli/Makefile.in index 5d078fa1..edeb00c7 100644 --- a/apps/cli/Makefile.in +++ b/apps/cli/Makefile.in @@ -64,7 +64,7 @@ CLIXON_LIB = libclixon$(SH_SUFFIX).$(CLIXON_MAJOR).$(CLIXON_MINOR) # even though it may exist in $(libdir). But the new version may not have been installed yet. LIBDEPS = $(top_srcdir)/lib/src/$(CLIXON_LIB) -LIBS = -L$(top_srcdir)/lib/src @LIBS@ $(top_srcdir)/lib/src/$(CLIXON_LIB) -lpthread +LIBS = -L$(top_srcdir)/lib/src @LIBS@ $(top_srcdir)/lib/src/$(CLIXON_LIB) #-lpthread CPPFLAGS = @CPPFLAGS@ -fPIC INCLUDES = -I. -I$(top_srcdir)/lib -I$(top_srcdir)/include -I$(top_srcdir) @INCLUDES@ 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/cli/cli_main.c b/apps/cli/cli_main.c index b0f597e6..622a6023 100644 --- a/apps/cli/cli_main.c +++ b/apps/cli/cli_main.c @@ -272,7 +272,8 @@ usage(clicon_handle h, /* */ int -main(int argc, char **argv) +main(int argc, + char **argv) { int retval = -1; int c; 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 28a49e9c..92a9124b 100644 --- a/apps/restconf/Makefile.in +++ b/apps/restconf/Makefile.in @@ -32,6 +32,9 @@ # # ***** END LICENSE BLOCK ***** # +# This makefile supports several restconf web packages in compile-time depending +# on the value of the with_restconf configure option +# VPATH = @srcdir@ srcdir = @srcdir@ top_srcdir = @top_srcdir@ @@ -55,6 +58,8 @@ HOST_VENDOR = @host_vendor@ # XXX why is not wwwdir under prefix? wwwdir = @wwwdir@ wwwuser = @wwwuser@ +# one of fcgi or evhtp: +with_restconf = @with_restconf@ SH_SUFFIX = @SH_SUFFIX@ CLIXON_MAJOR = @CLIXON_VERSION_MAJOR@ @@ -67,27 +72,45 @@ CLIXON_LIB = libclixon$(SH_SUFFIX).$(CLIXON_MAJOR).$(CLIXON_MINOR) LIBDEPS = $(top_srcdir)/lib/src/$(CLIXON_LIB) LIBS = -L$(top_srcdir)/lib/src @LIBS@ $(top_srcdir)/lib/src/$(CLIXON_LIB) +#LIBS += -lpthread -levent -levent_openssl -lssl -lcrypto CPPFLAGS = @CPPFLAGS@ -fPIC INCLUDES = -I. -I$(top_srcdir)/lib/src -I$(top_srcdir)/lib -I$(top_srcdir)/include -I$(top_srcdir) @INCLUDES@ -# Name of application -APPL = clixon_restconf +# Applications +ifeq ($(with_restconf),fcgi) +APPL = clixon_restconf # fcgi / nginx +else +APPL = clixon_restconf_$(with_restconf) +endif -# Not accessible from plugin -APPSRC = restconf_main.c -APPSRC += restconf_methods.c +# Common source - not accessible from plugin - independent of restconf package (fcgi|evhtp) +#APPSRC = restconf_lib.c + +# Fcgi-specific source including main +ifeq ($(with_restconf),fcgi) +APPSRC = restconf_fcgi_lib.c +APPSRC += restconf_fcgi_main.c +APPSRC += restconf_methods.c # These should be moved ^ APPSRC += restconf_methods_post.c APPSRC += restconf_methods_get.c APPSRC += restconf_stream.c +endif + +# Evhtp-specific source including main +ifeq ($(with_restconf),evhtp) +APPSRC = restconf_evhtp_main.c +endif + APPOBJ = $(APPSRC:.c=.o) # Accessible from plugin LIBSRC = restconf_lib.c LIBOBJ = $(LIBSRC:.c=.o) -# Name of lib +# 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) @@ -100,7 +123,7 @@ $(top_srcdir)/lib/src/$(CLIXON_LIB): (cd $(top_srcdir)/lib/src && $(MAKE) $(MFLAGS) $(CLIXON_LIB)) clean: - rm -f $(LIBOBJ) *.core $(APPL) $(APPOBJ) $(MYLIB) $(MYLIBSO) $(MYLIBLINK) + 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 @@ -136,7 +159,7 @@ uninstall: .SUFFIXES: .c .o .c.o: - $(CC) $(INCLUDES) -D__PROGRAM__=\"$(APPL)\" $(CPPFLAGS) $(CFLAGS) -c $< + $(CC) $(INCLUDES) -D__PROGRAM__=\"clixon_restconf\" $(CPPFLAGS) $(CFLAGS) -c $< $(APPL) : $(APPOBJ) $(MYLIBLINK) $(LIBDEPS) $(CC) $(LDFLAGS) $(APPOBJ) -L. $(MYLIB) $(LIBS) -o $@ @@ -155,7 +178,7 @@ TAGS: find . -name '*.[chyl]' -print | etags - depend: - $(CC) $(DEPENDFLAGS) @DEFS@ $(INCLUDES) $(CFLAGS) -MM $(LIBSRC) $(APPSRC) > .depend + $(CC) $(DEPENDFLAGS) @DEFS@ $(INCLUDES) $(CFLAGS) -MM $(APPFCGI) $(APPSRC) > .depend #include .depend diff --git a/apps/restconf/clixon_restconf.h b/apps/restconf/clixon_restconf.h index 4a1b7b04..8cf332d0 100644 --- a/apps/restconf/clixon_restconf.h +++ b/apps/restconf/clixon_restconf.h @@ -54,19 +54,13 @@ typedef enum restconf_media restconf_media; */ int restconf_err2code(char *tag); const char *restconf_code2reason(int code); - -int badrequest(FCGX_Request *r); -int unauthorized(FCGX_Request *r); -int forbidden(FCGX_Request *r); -int notfound(FCGX_Request *r); -int conflict(FCGX_Request *r); -int internal_server_error(FCGX_Request *r); -int notimplemented(FCGX_Request *r); -int restconf_test(FCGX_Request *r, int dbg); -cbuf *readdata(FCGX_Request *r); -int get_user_cookie(char *cookiestr, char *attribute, char **val); -int api_return_err(clicon_handle h, FCGX_Request *r, cxobj *xerr, - int pretty, restconf_media media, 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 new file mode 100644 index 00000000..da646841 --- /dev/null +++ b/apps/restconf/restconf_evhtp_main.c @@ -0,0 +1,555 @@ +/* + * + ***** 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 _EVHTP_NYI + +#ifdef HAVE_CONFIG_H +#include "clixon_config.h" /* generated by config & autoconf */ +#endif +/* compilation withotu threading support + * XXX: could be disabled already in configure? + */ +#define EVHTP_DISABLE_EVTHR +#define EVHTP_DISABLE_REGEX + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* chmod */ + +/* evhtp */ +#include +#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:c:k:" + +/* 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 _EVHTP_NYI + stream_child_freeall(_CLICON_HANDLE); +#endif + restconf_terminate(_CLICON_HANDLE); + } + clicon_exit_set(); /* checked in clixon_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 _EVHTP_NYI + ; +#endif + } +} + +static void +cx_gencb(evhtp_request_t *req, + void *arg) +{ + evhtp_connection_t *conn; + // clicon_handle h = arg; + + fprintf(stderr, "%s\n", __FUNCTION__); + if (req == NULL){ + errno = EINVAL; + return; + } + if ((conn = evhtp_request_get_connection(req)) == NULL) + goto done; + htp_sslutil_add_xheaders( + req->headers_out, + conn->ssl, + HTP_SSLUTILS_XHDR_ALL); + evhtp_send_reply(req, EVHTP_RES_NOTFOUND); + done: + return; /* void */ +} + +static evhtp_res +cx_pre_accept(evhtp_connection_t *conn, + void *arg) +{ + fprintf(stderr, "%s\n", __FUNCTION__); + return EVHTP_RES_OK; +} + +static evhtp_res +cx_post_accept(evhtp_connection_t *conn, + void *arg) +{ + fprintf(stderr, "%s\n", __FUNCTION__); + return EVHTP_RES_OK; +} + +static int +print_header_(evhtp_header_t * header, void * arg) { + fprintf(stderr, "%s: %s\n", header->key, header->val); + return 0; +} + +/*! Generic callback called if no other callbacks are matched + */ +static void +cx_path_restconf(evhtp_request_t *req, + void *arg) +{ + evhtp_connection_t *conn; + // clicon_handle h = arg; + struct evbuffer *b = NULL; + htp_method meth; + + fprintf(stderr, "%s\n", __FUNCTION__); + if (req == NULL){ + errno = EINVAL; + goto done; + } + if ((conn = evhtp_request_get_connection(req)) == NULL) + goto done; + meth = evhtp_request_get_method(req); + fprintf(stderr, "%s method:%d\n", __FUNCTION__, meth); + evhtp_headers_for_each(req->headers_in, print_header_, NULL); + + if ((b = evbuffer_new()) == NULL){ + goto done; + } + htp_sslutil_add_xheaders( + req->headers_out, + conn->ssl, + HTP_SSLUTILS_XHDR_ALL); + evhtp_send_reply_start(req, EVHTP_RES_OK); + evbuffer_add(b, "hej\n", strlen("hej\n\n")); + evhtp_send_reply_body(req, b); + evhtp_send_reply_end(req); + + // evhtp_headers_add_header(request->headers_out, evhtp_header_new("Host", "localhost", 0, 0)); evhtp_headers_add_headers(request->headers_out, headers); + + + + done: + return; /* void */ +} + +/*! 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 \"