* 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:
parent
d7fbe75c9e
commit
98f3cd0e32
31 changed files with 597 additions and 635 deletions
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue