* Major rewrite of event streams

* If you used old event callbacks API, you need to switch to the streams API
    * See clixon_stream.[ch]
  * Old streams API which needs to be removed include:
    * clicon_log_register_callback()
    * subscription_add() --> stream_register()
    * backend_notify() and backend_notify_xml() - use stream_notify() instead
* Example uses "NETCONF" stream instead of "ROUTING"
* Added timeout option -t for clixon_netconf - quit after max time.
This commit is contained in:
Olof hagsand 2018-09-30 14:51:30 +02:00
parent d7fbe75c9e
commit 98f3cd0e32
31 changed files with 597 additions and 635 deletions

View file

@ -136,7 +136,7 @@ clicon_handle_exit(clicon_handle h)
hash_free(copt);
if ((data = clicon_data(h)) != NULL)
hash_free(data);
stream_free(clicon_stream(h));
stream_delete_all(clicon_stream(h));
free(ch);
return 0;
}

View file

@ -52,6 +52,9 @@
#include <sys/time.h>
#include <sys/types.h>
/* cligen */
#include <cligen/cligen.h>
/* clicon */
#include "clixon_err.h"
#include "clixon_log.h"
@ -62,10 +65,6 @@ int debug = 0;
/* Bitmask whether to log to syslog or stderr: CLICON_LOG_STDERR | CLICON_LOG_SYSLOG */
static int _logflags = 0x0;
/* Function pointer to log notify callback */
static clicon_log_notify_t *_log_notify_cb = NULL;
static void *_log_notify_arg = NULL;
/* Set to open file to write debug messages directly to file */
static FILE *_logfile = NULL;
@ -81,15 +80,14 @@ static FILE *_logfile = NULL;
* if CLICON_LOG_SYSLOG, then print logs to syslog
* You can do a combination of both
* @code
* clicon_log_init(__PROGRAM__, LOG_INFO, CLICON_LOG_STDERR);
* clicon_log_init(h, __PROGRAM__, LOG_INFO, CLICON_LOG_STDERR);
* @endcode
*/
int
clicon_log_init(char *ident,
int upto,
int flags)
clicon_log_init(char *ident,
int upto,
int flags)
{
_logflags = flags;
if (flags & CLICON_LOG_SYSLOG){
if (setlogmask(LOG_UPTO(upto)) < 0)
@ -160,18 +158,6 @@ clicon_get_logflags(void)
return _logflags;
}
/*! Register log callback, return old setting
*/
clicon_log_notify_t *
clicon_log_register_callback(clicon_log_notify_t *cb,
void *arg)
{
clicon_log_notify_t *old = _log_notify_cb;
_log_notify_cb = cb;
_log_notify_arg = arg;
return old;
}
/*! Mimic syslog and print a time on file f
*/
static int
@ -188,6 +174,7 @@ flogtime(FILE *f)
return 0;
}
#ifdef NOTUSED
/*
* Mimic syslog and print a time on string s
* String returned needs to be freed.
@ -211,19 +198,19 @@ slogtime(void)
tm->tm_hour, tm->tm_min, tm->tm_sec);
return str;
}
#endif
/*! Make a logging call to syslog (or stderr).
*
* @param[in] level log level, eg LOG_DEBUG,LOG_INFO,...,LOG_EMERG. Thisis OR:d with facility == LOG_USER
* @param[in] msg Message to print as argv.
* This is the _only_ place the actual syslog (or stderr) logging is made in clicon,..
* @note syslog makes itw own filtering, but if log to stderr we do it here
* @note syslog makes its own filtering, but if log to stderr we do it here
* @see clicon_debug
*/
int
clicon_log_str(int level,
char *msg)
static int
clicon_log_str(int level,
char *msg)
{
if (_logflags & CLICON_LOG_SYSLOG)
syslog(LOG_MAKEPRI(LOG_USER, level), "%s", msg);
@ -245,30 +232,10 @@ clicon_log_str(int level,
fprintf(_logfile, "%s\n", msg);
fflush(_logfile);
}
if (_log_notify_cb){
static int cb = 0;
char *d, *msg2;
int len;
if (cb++ == 0){
/* Here there is danger of recursion: if callback in turn logs, therefore
make static check (should be stack-based - now global)
*/
if ((d = slogtime()) == NULL)
return -1;
len = strlen(d) + strlen(msg) + 1;
if ((msg2 = malloc(len)) == NULL){
fprintf(stderr, "%s: malloc: %s\n", __FUNCTION__, strerror(errno));
return -1;
}
snprintf(msg2, len, "%s%s", d, msg);
assert(_log_notify_arg);
_log_notify_cb(level, msg2, _log_notify_arg);
free(d);
free(msg2);
}
cb--;
}
/* Enable this if you want syslog in a stream. But there are problems with
* recursion
*/
done:
return 0;
}

View file

@ -64,10 +64,10 @@
/* clicon */
#include "clixon_err.h"
#include "clixon_log.h"
#include "clixon_queue.h"
#include "clixon_hash.h"
#include "clixon_handle.h"
#include "clixon_log.h"
#include "clixon_yang.h"
#include "clixon_sig.h"
#include "clixon_xml.h"
@ -564,16 +564,16 @@ send_msg_reply(int s,
* @param[in] event
* @retval 0 OK
* @retval -1 Error
* @see send_msg_notify_xml
*/
int
static int
send_msg_notify(int s,
int level,
char *event)
{
int retval = -1;
struct clicon_msg *msg = NULL;
if ((msg=clicon_msg_encode("<notification><event>%s</event></notification>", event)) == NULL)
if ((msg=clicon_msg_encode("%s", event)) == NULL)
goto done;
if (clicon_msg_send(s, msg) < 0)
goto done;
@ -584,6 +584,37 @@ send_msg_notify(int s,
return retval;
}
/*! Send a clicon_msg NOTIFY message asynchronously to client
*
* @param[in] s Socket to communicate with client
* @param[in] level
* @param[in] xml Event as XML
* @retval 0 OK
* @retval -1 Error
* @see send_msg_notify XXX beauty contest
*/
int
send_msg_notify_xml(int s,
cxobj *xev)
{
int retval = -1;
cbuf *cb = NULL;
if ((cb = cbuf_new()) == NULL){
clicon_err(OE_PLUGIN, errno, "cbuf_new");
goto done;
}
if (clicon_xml2cbuf(cb, xev, 0, 0) < 0)
goto done;
if (send_msg_notify(s, cbuf_get(cb)) < 0)
goto done;
retval = 0;
done:
if (cb)
cbuf_free(cb);
return retval;
}
/*! Look for a text pattern in an input string, one char at a time
* @param[in] tag What to look for
* @param[in] ch New input character

View file

@ -57,9 +57,9 @@
/* clicon */
#include "clixon_queue.h"
#include "clixon_log.h"
#include "clixon_hash.h"
#include "clixon_handle.h"
#include "clixon_log.h"
#include "clixon_yang.h"
#include "clixon_options.h"
#include "clixon_xml.h"
@ -775,7 +775,7 @@ clicon_rpc_create_subscription(clicon_handle h,
username = clicon_username_get(h);
if ((msg = clicon_msg_encode("<rpc username=\"%s\"><create-subscription>"
"<stream>%s</stream>"
"<filter>%s</filter>"
"<filter type=\"xpath\" select=\"%s\" />"
"</create-subscription></rpc>",
username?username:"",
stream?stream:"", filter?filter:"")) == NULL)

View file

@ -44,6 +44,7 @@
#include <string.h>
#include <errno.h>
#include <inttypes.h>
#include <sys/time.h>
/* cligen */
#include <cligen/cligen.h>
@ -51,8 +52,14 @@
/* clicon */
#include "clixon_queue.h"
#include "clixon_err.h"
#include "clixon_string.h"
#include "clixon_hash.h"
#include "clixon_handle.h"
#include "clixon_yang.h"
#include "clixon_xml.h"
#include "clixon_options.h"
#include "clixon_xpath_ctx.h"
#include "clixon_xpath.h"
#include "clixon_stream.h"
/*! Find an event notification stream given name
@ -74,6 +81,7 @@ stream_find(clicon_handle h,
}
/*! Add notification event stream
*
*/
int
stream_register(clicon_handle h,
@ -105,10 +113,10 @@ stream_register(clicon_handle h,
return retval;
}
/*! Delete complete notification event stream list
/*! Delete complete notification event stream list (not just single stream)
*/
int
stream_free(event_stream_t *es)
stream_delete_all(event_stream_t *es)
{
event_stream_t *e_next;
@ -148,8 +156,11 @@ stream_get_xml(clicon_handle h,
if (access){
cprintf(cb, "<access>");
cprintf(cb, "<encoding>xml</encoding>");
/* Note /stream need to be in http proxy declaration */
cprintf(cb, "<location>https://example.com/stream/%s</location>", es->es_name);
/* Note /stream need to be in http proxy declaration
* XXX
*/
cprintf(cb, "<location>/stream/%s</location>", es->es_name);
cprintf(cb, "</access>");
}
cprintf(cb, "</stream>");
@ -173,6 +184,7 @@ stream_del()
/*! Add an event notification callback to a stream given a callback function
* @param[in] h Clicon handle
* @param[in] stream Name of stream
* @param[in] xpath Filter selector - xpath
* @param[in] fn Callback when event occurs
* @param[in] arg Argument to use with callback. Also handle when deleting
* @retval 0 OK
@ -182,6 +194,7 @@ stream_del()
int
stream_cb_add(clicon_handle h,
char *stream,
char *xpath,
stream_fn_t fn,
void *arg)
{
@ -190,7 +203,7 @@ stream_cb_add(clicon_handle h,
struct stream_subscription *ss;
if ((es = stream_find(h, stream)) == NULL){
clicon_err(OE_CFG, ENOENT, "Stream not found");
clicon_err(OE_CFG, ENOENT, "Stream %s not found", stream);
goto done;
}
if ((ss = malloc(sizeof(*ss))) == NULL){
@ -198,7 +211,14 @@ stream_cb_add(clicon_handle h,
goto done;
}
memset(ss, 0, sizeof(*ss));
ss->ss_stream = strdup(stream);
if ((ss->ss_stream = strdup(stream)) == NULL){
clicon_err(OE_CFG, errno, "strdup");
goto done;
}
if (xpath && (ss->ss_xpath = strdup(xpath)) == NULL){
clicon_err(OE_CFG, errno, "strdup");
goto done;
}
ss->ss_fn = fn;
ss->ss_arg = arg;
ss->ss_next = es->es_subscription;
@ -208,34 +228,149 @@ stream_cb_add(clicon_handle h,
return retval;
}
/*! Delete event notification callback to a stream given a callback function
* Alt just send in an ss struct?
/*! Delete event notification callback to a stream given a callback and arg
* @param[in] h Clicon handle
* @param[in] stream Name of stream or NULL for all streams
* @param[in] fn Callback when event occurs
* @param[in] arg Argument to use with callback. Also handle when deleting
* @retval 0 OK
* @retval -1 Error
*/
int
stream_cb_delete(clicon_handle h,
char *stream,
stream_fn_t fn)
stream_fn_t fn,
void *arg)
{
int retval = -1;
event_stream_t *es;
struct stream_subscription *ss;
struct stream_subscription **ss_prev;
if ((es = stream_find(h, stream)) == NULL)
goto ok;
ss_prev = &es->es_subscription;
for (ss = *ss_prev; ss; ss = ss->ss_next){
if (ss->ss_fn == fn){
*ss_prev = ss->ss_next;
free(ss->ss_stream);
if (ss->ss_arg)
free(ss->ss_arg);
free(ss);
break;
struct stream_subscription *ss;
struct stream_subscription *ss_next;
for (es=clicon_stream(h); es; es=es->es_next){
if (stream && strcmp(stream, es->es_name)!=0)
continue;
ss_prev = &es->es_subscription;
for (ss = *ss_prev; ss; ss = ss_next){
ss_next = ss->ss_next;
if (fn == ss->ss_fn && arg == ss->ss_arg){
*ss_prev = ss->ss_next;
if (ss->ss_stream)
free(ss->ss_stream);
if (ss->ss_xpath)
free(ss->ss_xpath);
free(ss);
continue;
// break; if more > 1
}
ss_prev = &ss->ss_next;
}
ss_prev = &ss->ss_next;
}
ok:
retval = 0;
return retval;
}
/*! Stream notify event and distribute to all registered callbacks
* @param[in] h Clicon handle
* @param[in] stream Name of event stream. CLICON is predefined as LOG stream
* @param[in] event Notification as xml tree
* @retval 0 OK
* @retval -1 Error with clicon_err called
* @see stream_notify
*/
int
stream_notify_xml(clicon_handle h,
char *stream,
cxobj *xevent)
{
int retval = -1;
event_stream_t *es;
struct stream_subscription *ss;
if ((es = stream_find(h, stream)) == NULL)
goto ok;
/* Go thru all global (handle) subscriptions and find matches */
for (ss = es->es_subscription; ss; ss = ss->ss_next){
if (ss->ss_xpath == NULL ||
strlen(ss->ss_xpath)==0 ||
xpath_first(xevent, "%s", ss->ss_xpath) != NULL)
if ((*ss->ss_fn)(h, xevent, ss->ss_arg) < 0)
goto done;
}
ok:
retval = 0;
done:
return retval;
}
/*! Stream notify event and distribute to all registered callbacks
* @param[in] h Clicon handle
* @param[in] stream Name of event stream. CLICON is predefined as LOG stream
* @param[in] event Notification as format string according to printf(3)
* @retval 0 OK
* @retval -1 Error with clicon_err called
* @code
* if (stream_notify(h, "NETCONF", "<event><event-class>fault</event-class><reportingEntity><card>Ethernet0</card></reportingEntity><severity>major</severity></event>") < 0)
* err;
* @endcode
* @see stream_notify_xml
* @see backend_notify
*/
int
stream_notify(clicon_handle h,
char *stream,
const char *event, ...)
{
int retval = -1;
va_list args;
int len;
cxobj *xev = NULL;
yang_spec *yspec = NULL;
char *str = NULL;
cbuf *cb = NULL;
char timestr[27];
struct timeval tv;
va_start(args, event);
len = vsnprintf(NULL, 0, event, args) + 1;
va_end(args);
if ((str = malloc(len)) == NULL){
clicon_err(OE_UNIX, errno, "malloc");
goto done;
}
memset(str, 0, len);
va_start(args, event);
len = vsnprintf(str, len, event, args) + 1;
va_end(args);
if ((yspec = clicon_dbspec_yang(h)) == NULL){
clicon_err(OE_YANG, 0, "No yang spec");
goto done;
}
if ((cb = cbuf_new()) == NULL){
clicon_err(OE_UNIX, errno, "cbuf_new");
goto done;
}
gettimeofday(&tv, NULL);
if (time2str(tv, timestr, sizeof(timestr)) < 0){
clicon_err(OE_UNIX, errno, "time2str");
goto done;
}
cprintf(cb, "<notification xmlns=\"urn:ietf:params:xml:ns:netconf:notification:1.0\"><eventTime>%s</eventTime>%s</notification>", timestr, str);
if (xml_parse_string(cbuf_get(cb), yspec, &xev) < 0)
goto done;
if (xml_rootchild(xev, 0, &xev) < 0)
goto done;
if (stream_notify_xml(h, stream, xev) < 0)
goto done;
retval = 0;
done:
if (cb)
cbuf_free(cb);
if (xev)
xml_free(xev);
if (str)
free(str);
return retval;
}

View file

@ -55,11 +55,11 @@
/* clixon */
#include "clixon_err.h"
#include "clixon_log.h"
#include "clixon_string.h"
#include "clixon_queue.h"
#include "clixon_hash.h"
#include "clixon_handle.h"
#include "clixon_log.h"
#include "clixon_yang.h"
#include "clixon_xml.h"
#include "clixon_xml_sort.h"
@ -1412,7 +1412,8 @@ xml_parse_file(int fd,
* cxobj *xt = NULL;
* if (xml_parse_string(str, yspec, &xt) < 0)
* err;
* xml_free(xt);
* if (xml_root_child(xt, 0, &xt) < 0) # If you want to remove TOP
* err;
* @endcode
* @see xml_parse_file
* @see xml_parse_va