Added CLICON_LOG_DESTINATION and CLICON_LOG_FILE for al applications

This commit is contained in:
Olof hagsand 2024-06-19 12:28:39 +02:00
parent 26062d7003
commit 0234ed94bc
22 changed files with 498 additions and 163 deletions

View file

@ -29,7 +29,9 @@ Expected: June 2024
* CLI support for multiple inline commands separated by semi-colon
* New `clixon-config@2024-04-01.yang` revision
* Added options:
- `CLICON_DEBUG`: Debug flags, partly implemented.
- `CLICON_LOG_DESTINATION`: Default log destination
- `CLICON_LOG_FILE`: Which file to log to if file logging
- `CLICON_DEBUG`: Debug flags
- `CLICON_YANG_SCHEMA_MOUNT_SHARE`: Share same YANGs of several moint-points
- `CLICON_SOCK_PRIO`: Enable socket event priority
- `CLICON_XMLDB_MULTI`: Split datastore into multiple sub files

View file

@ -537,6 +537,7 @@ main(int argc,
int config_dump;
enum format_enum config_dump_format = FORMAT_XML;
int print_version = 0;
int32_t d;
/* Initiate CLICON handle */
if ((h = backend_handle_init()) == NULL)
@ -572,16 +573,15 @@ main(int argc,
cligen_output(stdout, "Clixon version: %s\n", CLIXON_GITHASH);
print_version++; /* plugins may also print versions w ca-version callback */
break;
case 'D' : { /* debug */
int d = 0;
/* Try first symbolic, then numeric match */
case 'D' : /* debug */
/* Try first symbolic, then numeric match
* Cant use yang_bits_map, too early in bootstrap, there is no yang */
if ((d = clixon_debug_str2key(optarg)) < 0 &&
sscanf(optarg, "%d", &d) != 1){
sscanf(optarg, "%u", &d) != 1){
usage(h, argv[0]);
}
dbg |= d;
break;
}
case 'f': /* config file */
if (!strlen(optarg))
usage(h, argv[0]);
@ -592,13 +592,18 @@ main(int argc,
usage(h, argv[0]);
clicon_option_str_set(h, "CLICON_CONFIGDIR", optarg);
break;
case 'l': /* Log destination: s|e|o */
if ((logdst = clixon_log_opt(optarg[0])) < 0)
usage(h, argv[0]);
if (logdst == CLIXON_LOG_FILE &&
strlen(optarg)>1 &&
clixon_log_file(optarg+1) < 0)
goto done;
case 'l': /* Log destination: s|e|o|f<file> */
if ((d = clixon_logdst_str2key(optarg)) < 0){
if (optarg[0] == 'f'){ /* Check for special -lf<file> syntax */
d = CLIXON_LOG_FILE;
if (strlen(optarg) > 1 &&
clixon_log_file(optarg+1) < 0)
goto done;
}
else
usage(h, argv[0]);
}
logdst = d;
break;
}
/*
@ -618,7 +623,9 @@ main(int argc,
usage(h, argv[0]);
goto done;
}
/* Read debug and log options from config file if not given by command-line */
if (clixon_options_main_helper(h, dbg, logdst, __PROGRAM__) < 0)
goto done;
/* Initialize plugin module by creating a handle holding plugin and callback lists */
if (clixon_plugin_module_init(h) < 0)
goto done;

View file

@ -562,7 +562,7 @@ main(int argc,
clixon_handle h;
int logclisyntax = 0;
int help = 0;
int logdst = CLIXON_LOG_STDERR;
uint32_t logdst = 0;
char *restarg = NULL; /* what remains after options */
yang_stmt *yspec;
struct passwd *pw;
@ -571,11 +571,12 @@ main(int argc,
cvec *nsctx_global = NULL; /* Global namespace context */
size_t cligen_buflen;
size_t cligen_bufthreshold;
int dbg=0;
uint32_t dbg=0;
int nr;
int config_dump;
enum format_enum config_dump_format = FORMAT_XML;
int print_version = 0;
int32_t d;
/* Defaults */
once = 0;
@ -608,7 +609,7 @@ main(int argc,
*/
optind = 1;
opterr = 0;
while ((c = getopt(argc, argv, CLI_OPTS)) != -1)
while ((c = getopt(argc, argv, CLI_OPTS)) != -1) {
switch (c) {
case 'h':
/* Defer the call to usage() to later. Reason is that for helpful
@ -622,16 +623,15 @@ main(int argc,
cligen_output(stdout, "Clixon version: %s\n", CLIXON_GITHASH);
print_version++; /* plugins may also print versions w ca-version callback */
break;
case 'D' : { /* debug */
int d = 0;
/* Try first symbolic, then numeric match */
case 'D' : /* debug, if set here overrides option CLICON_DEBUG */
/* Try first symbolic, then numeric match
* Cant use yang_bits_map, too early in bootstrap, there is no yang */
if ((d = clixon_debug_str2key(optarg)) < 0 &&
sscanf(optarg, "%d", &d) != 1){
usage(h, argv[0]);
}
dbg |= d;
break;
}
case 'f': /* config file */
if (!strlen(optarg))
usage(h, argv[0]);
@ -642,19 +642,25 @@ main(int argc,
usage(h, argv[0]);
clicon_option_str_set(h, "CLICON_CONFIGDIR", optarg);
break;
case 'l': /* Log destination: s|e|o|f */
if ((logdst = clixon_log_opt(optarg[0])) < 0)
usage(h, argv[0]);
if (logdst == CLIXON_LOG_FILE &&
strlen(optarg)>1 &&
clixon_log_file(optarg+1) < 0)
goto done;
case 'l': /* Log destination: s|e|o|f<file> */
if ((d = clixon_logdst_str2key(optarg)) < 0){
if (optarg[0] == 'f'){ /* Check for special -lf<file> syntax */
d = CLIXON_LOG_FILE;
if (strlen(optarg) > 1 &&
clixon_log_file(optarg+1) < 0)
goto done;
}
else
usage(h, argv[0]);
}
logdst = d;
break;
}
}
/*
* Logs, error and debug to stderr or syslog, set debug level
*/
clixon_log_init(h, __PROGRAM__, dbg?LOG_DEBUG:LOG_INFO, logdst);
clixon_log_init(h, __PROGRAM__, dbg?LOG_DEBUG:LOG_INFO, logdst?logdst:CLIXON_LOG_STDERR);
clixon_debug_init(h, dbg);
yang_init(h);
@ -763,25 +769,10 @@ main(int argc,
/* Defer: Wait to the last minute to print help message */
if (help)
usage(h, argv[0]);
/* Unless -D, set debug level to CLICON_DEBUG set
* Only works for one value.
*/
{
char *dstr;
int d = 0;
dstr = clicon_option_str(h, "CLICON_DEBUG");
if (dbg == 0 && dstr && strlen(dstr)){
if ((d = clixon_debug_str2key(dstr)) < 0 &&
sscanf(optarg, "%d", &d) != 1){
clixon_err(OE_CFG, 0, "Parsing CLICON_DEBUG: %s", dstr);
goto done;
}
clixon_debug_init(h, d);
clixon_log_init(h, __PROGRAM__, d?LOG_DEBUG:LOG_INFO, logdst);
}
}
/* Read debug and log options from config file if not given by command-line */
if (clixon_options_main_helper(h, dbg, logdst, __PROGRAM__) < 0)
goto done;
/* Split remaining argv/argc into <cmd> and <extra-options> */
if (options_split(h, argv0, argc, argv, &restarg) < 0)
goto done;
@ -983,8 +974,9 @@ main(int argc,
done:
if (restarg)
free(restarg);
// Gets in your face if we log on stderr
clixon_log_init(h, __PROGRAM__, LOG_INFO, 0); /* Log on syslog no stderr */
/* Dont log terminate on stderr or stdout */
clixon_log_init(h, __PROGRAM__, LOG_INFO,
clixon_get_logflags() & ~(CLIXON_LOG_STDERR|CLIXON_LOG_STDOUT));
clixon_log(h, LOG_NOTICE, "%s: %u Terminated", __PROGRAM__, getpid());
if (h)
cli_terminate(h);

View file

@ -116,7 +116,7 @@ netconf_add_request_attr(cxobj *xrpc,
if (xml_find_type(xrep, NULL, xml_name(xa), CX_ATTR) != NULL)
continue; /* Skip already present (dont overwrite) */
/* Filter all clixon-lib attributes and namespace declaration
* to acvoid leaking internal attributes to external NETCONF
* to avoid leaking internal attributes to external NETCONF
* note this is only done on top-level.
*/
if (xml_prefix(xa) && strcmp(xml_prefix(xa), CLIXON_LIB_PREFIX) == 0)
@ -687,6 +687,7 @@ main(int argc,
int config_dump = 0;
enum format_enum config_dump_format = FORMAT_XML;
int print_version = 0;
int32_t d;
/* Create handle */
if ((h = clixon_handle_init()) == NULL)
@ -703,7 +704,7 @@ main(int argc,
}
if (clicon_username_set(h, pw->pw_name) < 0)
goto done;
while ((c = getopt(argc, argv, NETCONF_OPTS)) != -1)
while ((c = getopt(argc, argv, NETCONF_OPTS)) != -1) {
switch (c) {
case 'h' : /* help */
usage(h, argv[0]);
@ -712,16 +713,14 @@ main(int argc,
cligen_output(stdout, "Clixon version: %s\n", CLIXON_GITHASH);
print_version++; /* plugins may also print versions w ca-version callback */
break;
case 'D' : { /* debug */
int d = 0;
case 'D' : /* debug */
/* Try first symbolic, then numeric match */
if ((d = clixon_debug_str2key(optarg)) < 0 &&
sscanf(optarg, "%d", &d) != 1){
sscanf(optarg, "%u", &d) != 1){
usage(h, argv[0]);
}
dbg |= d;
break;
}
case 'f': /* override config file */
if (!strlen(optarg))
usage(h, argv[0]);
@ -733,14 +732,22 @@ main(int argc,
clicon_option_str_set(h, "CLICON_CONFIGDIR", optarg);
break;
case 'l': /* Log destination: s|e|o */
if ((logdst = clixon_log_opt(optarg[0])) < 0)
usage(h, argv[0]);
if (logdst == CLIXON_LOG_FILE &&
strlen(optarg)>1 &&
clixon_log_file(optarg+1) < 0)
goto done;
int32_t d;
d = 0;
if ((d = clixon_logdst_str2key(optarg)) < 0){
if (optarg[0] == 'f'){ /* Check for special -lf<file> syntax */
d = CLIXON_LOG_FILE;
if (strlen(optarg) > 1 &&
clixon_log_file(optarg+1) < 0)
goto done;
}
else
usage(h, argv[0]);
}
logdst = d;
break;
}
}
/*
* Logs, error and debug to stderr or syslog, set debug level
@ -833,6 +840,9 @@ main(int argc,
argc -= optind;
argv += optind;
/* Read debug and log options from config file if not given by command-line */
if (clixon_options_main_helper(h, dbg, logdst, __PROGRAM__) < 0)
goto done;
/* Access the remaining argv/argc options (after --) w clicon-argv_get() */
clicon_argv_set(h, argv0, argc, argv);

View file

@ -329,6 +329,7 @@ main(int argc,
enum format_enum config_dump_format = FORMAT_XML;
int print_version = 0;
int stream_timeout = 0;
int32_t d;
/* Create handle */
if ((h = restconf_handle_init()) == NULL)
@ -351,7 +352,6 @@ main(int argc,
print_version++; /* plugins may also print versions w ca-version callback */
break;
case 'D' : { /* debug */
int d = 0;
/* Try first symbolic, then numeric match */
if ((d = clixon_debug_str2key(optarg)) < 0 &&
sscanf(optarg, "%d", &d) != 1){
@ -371,12 +371,17 @@ main(int argc,
clicon_option_str_set(h, "CLICON_CONFIGDIR", optarg);
break;
case 'l': /* Log destination: s|e|o */
if ((logdst = clixon_log_opt(optarg[0])) < 0)
usage(h, argv[0]);
if (logdst == CLIXON_LOG_FILE &&
strlen(optarg)>1 &&
clixon_log_file(optarg+1) < 0)
goto done;
if ((d = clixon_logdst_str2key(optarg)) < 0){
if (optarg[0] == 'f'){ /* Check for special -lf<file> syntax */
d = CLIXON_LOG_FILE;
if (strlen(optarg) > 1 &&
clixon_log_file(optarg+1) < 0)
goto done;
}
else
usage(h, argv[0]);
}
logdst = d;
break;
} /* switch getopt */
@ -466,6 +471,9 @@ main(int argc,
argc -= optind;
argv += optind;
/* Read debug and log options from config file if not given by command-line */
if (clixon_options_main_helper(h, dbg, logdst, __PROGRAM__) < 0)
goto done;
/* Access the remaining argv/argc options (after --) w clicon-argv_get() */
clicon_argv_set(h, argv0, argc, argv);

View file

@ -1168,6 +1168,7 @@ main(int argc,
enum format_enum config_dump_format = FORMAT_XML;
int print_version = 0;
int stream_timeout = 0;
int32_t d;
/* Create handle */
if ((h = restconf_handle_init()) == NULL)
@ -1187,17 +1188,13 @@ main(int argc,
cligen_output(stdout, "Clixon version: %s\n", CLIXON_GITHASH);
print_version++; /* plugins may also print versions w ca-version callback */
break;
case 'D' : { /* debug. Note this overrides any setting in the config */
int d = 0;
case 'D' : /* debug. Note this overrides any setting in the config */
/* Try first symbolic, then numeric match */
if ((d = clixon_debug_str2key(optarg)) < 0 &&
sscanf(optarg, "%d", &d) != 1){
usage(h, argv[0]);
}
dbg |= d;
break;
}
break;
case 'f': /* override config file */
if (!strlen(optarg))
@ -1210,13 +1207,18 @@ main(int argc,
clicon_option_str_set(h, "CLICON_CONFIGDIR", optarg);
break;
case 'l': /* Log destination: s|e|o */
if ((logdst = clixon_log_opt(optarg[0])) < 0)
usage(h, argv0);
if (logdst == CLIXON_LOG_FILE &&
strlen(optarg)>1 &&
clixon_log_file(optarg+1) < 0)
goto done;
break;
if ((d = clixon_logdst_str2key(optarg)) < 0){
if (optarg[0] == 'f'){ /* Check for special -lf<file> syntax */
d = CLIXON_LOG_FILE;
if (strlen(optarg) > 1 &&
clixon_log_file(optarg+1) < 0)
goto done;
}
else
usage(h, argv[0]);
}
logdst = d;
break;
} /* switch getopt */
/*
@ -1324,6 +1326,10 @@ main(int argc,
argc -= optind;
argv += optind;
/* Read debug and log options from config file if not given by command-line */
if (clixon_options_main_helper(h, dbg, logdst, __PROGRAM__) < 0)
goto done;
/* Access the remaining argv/argc options (after --) w clicon-argv_get() */
clicon_argv_set(h, argv0, argc, argv);

View file

@ -359,6 +359,7 @@ main(int argc,
int config_dump = 0;
enum format_enum config_dump_format = FORMAT_XML;
int print_version = 0;
int32_t d;
/* Create handle */
if ((h = clixon_handle_init()) == NULL)
@ -386,7 +387,7 @@ main(int argc,
print_version++; /* plugins may also print versions w ca-version callback */
break;
case 'D' : { /* debug */
int d = 0;
int32_t d = 0;
/* Try first symbolic, then numeric match */
if ((d = clixon_debug_str2key(optarg)) < 0 &&
sscanf(optarg, "%d", &d) != 1){
@ -400,13 +401,18 @@ main(int argc,
usage(h, argv[0]);
clicon_option_str_set(h, "CLICON_CONFIGFILE", optarg);
break;
case 'l': /* Log destination: s|e|o */
if ((logdst = clixon_log_opt(optarg[0])) < 0)
usage(h, argv[0]);
if (logdst == CLIXON_LOG_FILE &&
strlen(optarg)>1 &&
clixon_log_file(optarg+1) < 0)
goto done;
case 'l': /* Log destination: s|e|o */
if ((d = clixon_logdst_str2key(optarg)) < 0){
if (optarg[0] == 'f'){ /* Check for special -lf<file> syntax */
d = CLIXON_LOG_FILE;
if (strlen(optarg) > 1 &&
clixon_log_file(optarg+1) < 0)
goto done;
}
else
usage(h, argv[0]);
}
logdst = d;
break;
}
if (print_version)
@ -467,6 +473,9 @@ main(int argc,
argc -= optind;
argv += optind;
/* Read debug and log options from config file if not given by command-line */
if (clixon_options_main_helper(h, dbg, logdst, __PROGRAM__) < 0)
goto done;
/* Access the remaining argv/argc options (after --) w clicon-argv_get() */
clicon_argv_set(h, argv0, argc, argv);

View file

@ -46,9 +46,10 @@
* Constants
*/
/* Debug flags are seperated into subject areas and detail
/*! Debug flags are separated into subject areas and detail
*
* @see dbgmap Symbolic mapping (if you change here you may need to change dbgmap)
* @see clixon_debug in clixon-lib.yang
* @see also clixon_debug_t in clixon-lib.yang
*/
/* Detail level */
#define CLIXON_DBG_ALWAYS 0x00000000 /* Unconditionally logged */

View file

@ -45,11 +45,15 @@
/*
* Constants
*/
/* Where to log (masks) */
#define CLIXON_LOG_SYSLOG 1 /* print logs on syslog */
#define CLIXON_LOG_STDERR 2 /* print logs on stderr */
#define CLIXON_LOG_STDOUT 4 /* print logs on stdout */
#define CLIXON_LOG_FILE 8 /* print logs on clicon_log_filename */
/*! Log destination as bitfields (masks)
*
* @see logdstmap Symbolic mapping (if you change here you may need to change logdstmap)
* @see also log_desination_t in clixon-config.yang
*/
#define CLIXON_LOG_SYSLOG 0x01 /* print logs on syslog */
#define CLIXON_LOG_STDERR 0x02 /* print logs on stderr */
#define CLIXON_LOG_STDOUT 0x04 /* print logs on stdout */
#define CLIXON_LOG_FILE 0x08 /* print logs on clixon_log_filename */
/* What kind of log (only for customizable error/logs) */
enum clixon_log_type{
@ -67,6 +71,8 @@ enum clixon_log_type{
/*
* Prototypes
*/
char *clixon_logdst_key2str(int keyword);
int clixon_logdst_str2key(char *str);
int clixon_log_init(clixon_handle h, char *ident, int upto, int flags);
int clixon_log_exit(void);
int clixon_log_opt(char c);

View file

@ -112,6 +112,9 @@ int clicon_option_add(clixon_handle h, const char *name, char *value);
/* Initialize options: set defaults, read config-file, etc */
int clicon_options_main(clixon_handle h);
/* Options debug and log helper function */
int clixon_options_main_helper(clixon_handle h, uint32_t dbg, uint32_t logdst, char *ident);
/*! Check if a clicon option has a value */
int clicon_option_exists(clixon_handle h, const char *name);

View file

@ -77,8 +77,10 @@ int assign_namespace_element(cxobj *x0, cxobj *x1, cxobj *x1p);
int assign_namespace_body(cxobj *x0, cxobj *x1);
int xml_merge(cxobj *x0, cxobj *x1, yang_stmt *yspec, char **reason);
int yang_valstr2enum(yang_stmt *ytype, char *valstr, char **enumstr);
int yang_bitsstr2val(clixon_handle h, yang_stmt *ytype, char *bitsstr, unsigned char **snmpval, size_t *snmplen);
int yang_val2bitsstr(clixon_handle h, yang_stmt *ytype, unsigned char *snmpval, size_t snmplen, cbuf *cb);
int yang_bitsstr2val(clixon_handle h, yang_stmt *ytype, char *bitsstr, unsigned char **outval, size_t *outlen);
int yang_bitsstr2flags(yang_stmt *ytype, char *bitsstr, uint32_t *flags);
int yang_val2bitsstr(clixon_handle h, yang_stmt *ytype, unsigned char *outval, size_t snmplen, cbuf *cb);
int yang_bits_map(yang_stmt *yt, char *str, char *nodeid, uint32_t *flags);
int yang_enum2valstr(yang_stmt *ytype, char *enumstr, char **valstr);
int yang_enum2int(yang_stmt *ytype, char *enumstr, int32_t *val);
int yang_enum_int_value(cxobj *node, int32_t *val);

View file

@ -79,9 +79,20 @@
/* Cache handle since debug calls do not have handle parameter */
static clixon_handle _debug_clixon_h = NULL;
/*! The global debug level. 0 means no debug
*
* @note There are pros and cons in having the debug state as a global variable. The
* alternative to bind it to the clicon handle (h) was considered but it limits its
* usefulness, since not all functions have access to a handle.
* A compromise solution is now in place where h can be provided in the function call, but
* tolerates NULL, in which case a cached handle is used.
*/
static int _debug_level = 0;
/*! Mapping between Clixon debug symbolic names <--> bitfields
*
* Mapping between specific bitfields and symbolic names, note only perfect matches
* @note yang_bits_map can be used as alternative but this still neeeded in bootstrapping
*/
static const map_str2int dbgmap[] = {
{"default", CLIXON_DBG_DEFAULT},
@ -152,16 +163,6 @@ clixon_debug_key_dump(FILE *f)
return -1;
}
/*! The global debug level. 0 means no debug
*
* @note There are pros and cons in having the debug state as a global variable. The
* alternative to bind it to the clicon handle (h) was considered but it limits its
* usefulness, since not all functions have access to a handle.
* A compromise solution is now in place where h can be provided in the function call, but
* tolerates NULL, in which case a cached handle is used.
*/
static int _debug_level = 0;
/*! Initialize debug messages. Set debug level.
*
* Initialize debug module. The level is used together with clixon_debug(dbglevel) calls as follows:

View file

@ -60,6 +60,7 @@
/* clixon */
#include "clixon_queue.h"
#include "clixon_hash.h"
#include "clixon_string.h"
#include "clixon_handle.h"
#include "clixon_yang.h"
#include "clixon_xml.h"
@ -87,6 +88,47 @@ static FILE *_log_file = NULL;
/* Truncate debug strings to this length. 0 means unlimited */
static int _log_trunc = 0;
/*! Mapping between Clixon debug symbolic names <--> bitfields
*
* Also inclode shorthands: s|e|o|f|n
* Mapping between specific bitfields and symbolic names, note only perfect matches
* @see typedef log_destination_t in clixon-config.yang
*/
static const map_str2int logdstmap[] = {
{"syslog", CLIXON_LOG_SYSLOG},
{"s", CLIXON_LOG_SYSLOG},
{"stderr", CLIXON_LOG_STDERR},
{"e", CLIXON_LOG_STDERR},
{"stdout", CLIXON_LOG_STDOUT},
{"o", CLIXON_LOG_STDOUT},
{"file", CLIXON_LOG_FILE},
{"f", CLIXON_LOG_FILE},
{"n", 0x0},
{NULL, -1}
};
/*! Map from clixon debug (specific) bitmask to string
*
* @param[in] int Bitfield, see CLIXON_LOG_SYSLOG and others
* @retval str String representation of bitfield
*/
char *
clixon_logdst_key2str(int keyword)
{
return (char*)clicon_int2str(logdstmap, keyword);
}
/*! Map from clixon log destination symbolic string to bitfield
*
* @param[in] str String representation of Clixon log destination bit
* @retval int Bit representation of bitfield
*/
int
clixon_logdst_str2key(char *str)
{
return clicon_str2int(logdstmap, str);
}
/*! Initialize system logger.
*
* Make syslog(3) calls with specified ident and gates calls of level upto specified level (upto).
@ -96,9 +138,7 @@ static int _log_trunc = 0;
* @param[in] h Clixon handle
* @param[in] ident prefix that appears on syslog (eg 'cli')
* @param[in] upto log priority, eg LOG_DEBUG,LOG_INFO,...,LOG_EMERG (see syslog(3)).
* @param[in] flags bitmask: if CLIXON_LOG_STDERR, then print logs to stderr
* if CLIXON_LOG_SYSLOG, then print logs to syslog
* You can do a combination of both
* @param[in] flags Log destination bitmask
* @retval 0 OK
* @code
* clixon_log_init(__PROGRAM__, LOG_INFO, CLIXON_LOG_STDERR);

View file

@ -73,7 +73,6 @@
#include "clixon_debug.h"
#include "clixon_string.h"
#include "clixon_file.h"
#include "clixon_xml_sort.h"
#include "clixon_json.h"
#include "clixon_text_syntax.h"
#include "clixon_proto.h"
@ -83,8 +82,10 @@
#include "clixon_xpath.h"
#include "clixon_yang_parse_lib.h"
#include "clixon_netconf_lib.h"
#include "clixon_xml_sort.h"
#include "clixon_xml_nsctx.h"
#include "clixon_xml_io.h"
#include "clixon_xml_map.h"
#include "clixon_validate.h"
#include "clixon_xml_default.h"
@ -725,6 +726,59 @@ clicon_options_main(clixon_handle h)
return retval;
}
/*! Options debug and log helper function
*
* If no debug or log option set in command-line, read debug or log options from
* configure file.
* Also set log file if CLICON_LOG_FILE is set
* @param[in] h Clixon handle
* @param[in] dbg Debug bitmask
* @param[in] logdst Log destination bitmask
* @param[in] ident prefix that appears on syslog
* @retval 0 OK
* @retval -1 Error
*/
int
clixon_options_main_helper(clixon_handle h,
uint32_t dbg,
uint32_t logdst,
char *ident)
{
int retval = -1;
int relog = 0;
char *dstr;
relog = 0;
dstr = clicon_option_str(h, "CLICON_DEBUG");
if (dbg == 0 && dstr && strlen(dstr)){
if (yang_bits_map(clicon_config_yang(h),
dstr,
"/cc:clixon-config/cc:CLICON_DEBUG",
&dbg) < 0)
goto done;
relog++;
}
dstr = clicon_option_str(h, "CLICON_LOG_DESTINATION");
if (logdst == 0 && dstr && strlen(dstr)){
logdst = 0;
if (yang_bits_map(clicon_config_yang(h),
dstr,
"/cc:clixon-config/cc:CLICON_LOG_DESTINATION",
&logdst) < 0)
goto done;
relog++;
}
if (relog){
clixon_debug_init(h, dbg);
clixon_log_init(h, ident, dbg?LOG_DEBUG:LOG_INFO, logdst?logdst:CLIXON_LOG_STDERR);
}
if ((dstr = clicon_option_str(h, "CLICON_LOG_FILE")) != NULL)
clixon_log_file(dstr);
retval = 0;
done:
return retval;
}
/*! Check if a clicon option has a value
*
* @param[in] h clixon_handle

View file

@ -1669,25 +1669,25 @@ yang_bits_pos(yang_stmt *ytype,
goto done;
}
/*! Given a YANG (bits) type node and string value, return SNMP value for bits set.
/*! Given a YANG (bits) type node and string value, return value for bits set.
*
* @param[in] h Clixon handle
* @param[in] ytype YANG type noden
* @param[in] bitsstr Value of bits as space separated string
* @param[out] snmpval SNMP value with all bits set for given bitsstr
* @param[out] snmplen length of snmpval
* @retval 1 OK, result in snmpval
* @param[out] outval Value with all bits set for given bitsstr (free with free)
* @param[out] outlen Length of outval
* @retval 1 OK, result in outval
* @retval 0 Invalid, not found
* @retval -1 Error
* @see yang_val2bitsstr
* XXX de-snmp:ize
* @note that the output is a vector of bits originally made for SNMP bitvectors (not integers)
*/
int
yang_bitsstr2val(clixon_handle h,
yang_stmt *ytype,
char *bitsstr,
unsigned char **snmpval,
size_t *snmplen)
unsigned char **outval,
size_t *outlen)
{
int retval = -1;
int i = 0;
@ -1699,7 +1699,7 @@ yang_bitsstr2val(clixon_handle h,
int ret = 0;
uint32_t bitpos;
*snmplen = 0;
*outlen = 0;
if ((buffer = calloc(CLIXON_BITS_POS_MAX / 8, sizeof(unsigned char))) == NULL){
clixon_err(OE_UNIX, errno, "calloc");
goto done;
@ -1719,19 +1719,19 @@ yang_bitsstr2val(clixon_handle h,
/* Set bit at correct byte and bit position */
byte = bitpos / 8;
buffer[byte] = buffer[byte] | (1 << (7 - (bitpos % 8)));
*snmplen = byte + 1;
if (*snmplen >= CLIXON_BITS_POS_MAX) {
*outlen = byte + 1;
if (*outlen >= CLIXON_BITS_POS_MAX) {
clixon_err(OE_UNIX, EINVAL, "bit position %zu out of range. (max. allowed %d)",
*snmplen, CLIXON_BITS_POS_MAX);
*outlen, CLIXON_BITS_POS_MAX);
goto done;
}
}
}
if ((*snmpval = malloc(*snmplen)) == NULL){
if ((*outval = malloc(*outlen)) == NULL){
clixon_err(OE_UNIX, errno, "calloc");
goto done;
}
memcpy(*snmpval, buffer, *snmplen);
memcpy(*outval, buffer, *outlen);
retval = 1;
done:
if (buffer)
@ -1744,24 +1744,81 @@ yang_bitsstr2val(clixon_handle h,
goto done;
}
/*! Given a YANG (bits) type node and SNMP value, return the string value for all bits (flags) that are set.
/*! Given a YANG (bits) type node and string value, return bit values in a uint64
*
* @param[in] ytype YANG type noden
* @param[in] bitsstr Value of bits as space separated string
* @param[out] flags Pointer to integer with bit values set according to C type
* @retval 1 OK, result in u64
* @retval 0 Invalid, not found
* @retval -1 Error
* @see yang_bitsstr2val for bit vector (snmp-like)
*/
int
yang_bitsstr2flags(yang_stmt *ytype,
char *bitsstr,
uint32_t *flags)
{
int retval = -1;
int i = 0;
char **vec = NULL;
char *v;
int nvec;
int ret = 0;
uint32_t bitpos;
if (flags == NULL){
clixon_err(OE_UNIX, EINVAL, "flags is NULL");
goto done;
}
if ((vec = clicon_strsep(bitsstr, " ", &nvec)) == NULL){
clixon_err(OE_UNIX, EINVAL, "split string failed");
goto done;
}
/* Go over all set flags in given bitstring */
for (i=0; i<nvec; i++){
v = clixon_trim(vec[i]);
if (strlen(v) > 0) {
if ((ret = yang_bits_pos(ytype, v, &bitpos)) < 0)
goto done;
if (ret == 0)
goto fail;
if (bitpos >= 32) {
clixon_err(OE_UNIX, EINVAL, "bit position %u out of range. (max. allowed %d)",
bitpos, 32);
goto done;
}
*flags |= (1 << bitpos);
}
}
retval = 1;
done:
if (vec)
free(vec);
return retval;
fail:
retval = 0;
goto done;
}
/*! Given a YANG (bits) type node and value, return the string value for all bits (flags) that are set.
*
* @param[in] h Clixon handle
* @param[in] ytype YANG type noden
* @param[in] snmpval SNMP value
* @param[in] snmplen length of snmpval
* @param[out] cb space separated string with bit labes for all bits that are set in snmpval
* @param[in] inval Input string
* @param[in] inlen Length of inval
* @param[out] cb space separated string with bit labels for all bits that are set in inval
* @retval 1 OK, result in cb
* @retval 0 Invalid, not found
* @retval -1 Error
* @see yang_bitsstr2val
* XXX de-snmp:ize
* @note that the output is a vector of bits originally made for SNMP bitvectors (not integers)
*/
int
yang_val2bitsstr(clixon_handle h,
yang_stmt *ytype,
unsigned char *snmpval,
size_t snmplen,
unsigned char *inval,
size_t inlen,
cbuf *cb)
{
int retval = -1;
@ -1778,7 +1835,7 @@ yang_val2bitsstr(clixon_handle h,
goto done;
}
/* Go over all defined bits and check if it is seet in intval */
while ((yprev = yn_each(ytype, yprev)) != NULL && byte < snmplen){
while ((yprev = yn_each(ytype, yprev)) != NULL && byte < inlen){
if (yang_keyword_get(yprev) == Y_BIT) {
/* Use position from Y_POSITION statement if defined */
if ((ypos = yang_find(yprev, Y_POSITION, NULL)) != NULL){
@ -1793,7 +1850,7 @@ yang_val2bitsstr(clixon_handle h,
if (is_first == 0) bitpos++;
}
byte = bitpos / 8;
if (snmpval[byte] & (1 << (7 - (bitpos % 8)))){
if (inval[byte] & (1 << (7 - (bitpos % 8)))){
if (is_first == 0) cbuf_append_str(cb, " ");
cbuf_append_str(cb, yang_argument_get(yprev));
}
@ -1813,6 +1870,47 @@ yang_val2bitsstr(clixon_handle h,
goto done;
}
/*! Map from bit string to integer bitfield given YANG mapping
*
* Given YANG node, schema-nodeid and a bits string, return a bitmap as u64
* Example: "default app2" --> CLIXON_DBG_DEFAULT | CLIXON_DBG_APP2
* @param[in] yt YANG node in tree (eg yspec)
* @param[in] str String representation of Clixon debug bits, such as "msg app2"
* @param[in] nodeid Absolute schema node identifier to leaf of option
* @param[out] u64 Bit representation
*/
int
yang_bits_map(yang_stmt *yt,
char *str,
char *nodeid,
uint32_t *flags)
{
int retval = -1;
yang_stmt *yn = NULL;
yang_stmt *yrestype;
int ret;
if (yang_abs_schema_nodeid(yt, nodeid, &yn) < 0)
goto done;
if (yn == NULL){
clixon_err(OE_YANG, 0, "yang node not found: %s", nodeid);
goto done;
}
if (yang_type_get(yn, NULL, &yrestype, NULL, NULL, NULL, NULL, NULL) < 0)
goto done;
if (yrestype != NULL) {
if ((ret = yang_bitsstr2flags(yrestype, str, flags)) < 0)
goto done;
if (ret == 0){
clixon_err(OE_YANG, 0, "Bit string invalid: %s", str);
goto done;
}
}
retval = 0;
done:
return retval;
}
/*! Get integer value from xml node from yang enumeration
*
* @param[in] node XML node in a tree

View file

@ -3313,9 +3313,9 @@ schema_nodeid_iterate(yang_stmt *yn,
* @see yang_desc_schema_nodeid
*/
int
yang_abs_schema_nodeid(yang_stmt *yn,
char *schema_nodeid,
yang_stmt **yres)
yang_abs_schema_nodeid(yang_stmt *yn,
char *schema_nodeid,
yang_stmt **yres)
{
int retval = -1;
cvec *nodeid_cvv = NULL;

View file

@ -838,12 +838,12 @@ yang_metadata_init(clixon_handle h)
* Skip module if already loaded
* This function is used where a yang-lib module-set is available to populate
* an XML mount-point.
* @param[in] h Clixon handle
* @param[in] h Clixon handle
* @param[in] xyanglib XML tree on the form <yang-lib>...
* @param[in] yspec Will be populated with YANGs, is consumed
* @retval 1 OK
* @retval 0 Parse error
* @retval -1 Error
* @param[in] yspec Will be populated with YANGs, is consumed
* @retval 1 OK
* @retval 0 Parse error
* @retval -1 Error
* @see xml_schema_add_mount_points
* XXX: Ensure yang-lib is always there otherwise get state dont work for mountpoint
*/

View file

@ -120,10 +120,10 @@ expectpart "$($clixon_cli -1 -f $cfg show conf x)" 0 "x m1 a (null) b 22/22 c 44
# Negative tests
new "err x"
expectpart "$($clixon_cli -1 -f $cfg -l n err x)" 255 "Config error: api-path syntax error \"/example2:x\": application unknown-element No such yang module prefix <bad-element>example2</bad-element>: Invalid argument"
expectpart "$($clixon_cli -1 -f $cfg -l o err x)" 255 "Config error: api-path syntax error \"/example2:x\": application unknown-element No such yang module prefix <bad-element>example2</bad-element>: Invalid argument"
new "err x a"
expectpart "$($clixon_cli -1 -f $cfg -l n err x a 99)" 255 "Config error: api-path syntax error \"/example:x/m1=%s\": rpc malformed-message List key m1 length mismatch : Invalid argument"
expectpart "$($clixon_cli -1 -f $cfg -l o err x a 99)" 255 "Config error: api-path syntax error \"/example:x/m1=%s\": rpc malformed-message List key m1 length mismatch : Invalid argument"
if [ $BE -ne 0 ]; then
new "Kill backend"

View file

@ -89,11 +89,11 @@ new "wait backend"
wait_backend
new "orig error"
expectpart "$($clixon_cli -1 -f $cfg -l n example error orig)" 255 "Config error: api-path syntax error " ": application invalid-value Invalid api-path: (must start with '/')"
expectpart "$($clixon_cli -1 -f $cfg -l o example error orig)" 255 "Config error: api-path syntax error " ": application invalid-value Invalid api-path: (must start with '/')"
if [ ${LINKAGE} = dynamic ]; then
new "customized error"
expectpart "$($clixon_cli -1 -f $cfg -l n example error custom)" 255 "My new err-string"
expectpart "$($clixon_cli -1 -f $cfg -l o example error custom)" 255 "My new err-string"
fi
if [ $BE -ne 0 ]; then

View file

@ -1,7 +1,7 @@
#!/usr/bin/env bash
# Turn on debug on backend/cli/netconf
# Note, restconf debug used to be tested but is no longer tested here,
# maybe in test_restconf_internal?
# Also some log destination tests
# Note no restconf debug test
# Magic line must be first in script (see README.md)
s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exit 0; else return 0; fi
@ -84,15 +84,57 @@ wait_restconf
new "Set backend debug using netconf"
expecteof_netconf "$clixon_netconf -qf $cfg" 0 "$DEFAULTHELLO" "<rpc $DEFAULTNS><debug $LIBNS><level>1</level></debug></rpc>" "" "<rpc-reply $DEFAULTNS><ok/></rpc-reply>"
new "Set cli debug using cli"
# Debug
new "cli debug cli"
expectpart "$($clixon_cli -1 -f $cfg -l o debug cli 1)" 0 "^$"
# Run cli debug
new "get cli debug, expect 0"
new "cli debug, expect 0"
expectpart "$($clixon_cli -1 -f $cfg show debug cli)" 0 "CLI debug:0x0"
new "get cli debug expect 2"
expectpart "$($clixon_cli -1 -f $cfg -o CLICON_DEBUG=msg show debug cli)" 0 "CLI debug:0x2"
new "cli debug -o single"
expectpart "$($clixon_cli -1 -f $cfg -o CLICON_DEBUG=msg show debug cli)" 0 "CLI debug:0x2" --not-- "CLI debug:0x20"
new "cli debug -o multi"
expectpart "$($clixon_cli -1 -f $cfg -o CLICON_DEBUG="msg app2" show debug cli)" 0 "CLI debug:0x200002"
new "cli debug -D multi"
expectpart "$($clixon_cli -1 -f $cfg -D msg -D app2 show debug cli)" 0
# Log destination
new "cli log -lf<file>"
rm -f $dir/clixon.log
expectpart "$($clixon_cli -1 -lf$dir/clixon.log -f $cfg show version)" 0
if [ ! -f "$dir/clixon.log" ]; then
err "$dir/clixon.log" "No file"
fi
new "cli log -lfile"
rm -f $dir/clixon.log
expectpart "$($clixon_cli -1 -lfile -f $cfg show version)" 0
if [ -f "$dir/clixon.log" ]; then
err "No file" "$dir/clixon.log"
fi
new "cli log -lfile + CLICON_LOG_FILE"
rm -f $dir/clixon.log
expectpart "$($clixon_cli -1 -lfile -o CLICON_LOG_FILE=$dir/clixon.log -f $cfg show version)" 0
if [ ! -f "$dir/clixon.log" ]; then
err "$dir/clixon.log" "No file"
fi
rm -f $dir/clixon.log
new "cli log -o CLICON_LOG_DESTINATION + CLICON_LOG_FILE"
expectpart "$($clixon_cli -1 -o CLICON_LOG_DESTINATION=file -o CLICON_LOG_FILE=$dir/clixon.log -f $cfg show version)" 0
if [ ! -f "$dir/clixon.log" ]; then
err "$dir/clixon.log" "No file"
fi
rm -f $dir/clixon.log
new "cli log -o CLICON_LOG_DESTINATION + CLICON_LOG_FILE multi"
expectpart "$($clixon_cli -1 -o CLICON_LOG_DESTINATION="stdout file" -o CLICON_LOG_FILE=$dir/clixon.log -f $cfg show version)" 0
if [ ! -f "$dir/clixon.log" ]; then
err "$dir/clixon.log" "No file"
fi
new "Set backend debug using cli"
expectpart "$($clixon_cli -1 -f $cfg -l o debug backend 1)" 0 "^$"

View file

@ -52,6 +52,8 @@ module clixon-config {
revision 2024-04-01 {
description
"Added options:
CLICON_LOG_DESTINATION: Default log destination
CLICON_LOG_FILE: Which file to log to if file logging
CLICON_DEBUG: Debug flags.
CLICON_YANG_SCHEMA_MOUNT_SHARE: Share same YANGs of equal moint-points.
CLICON_SOCK_PRIO: Enable socket event priority
@ -426,6 +428,33 @@ module clixon-config {
}
}
}
typedef log_destination_t {
description
"Log destination flags
Can also be given directly as -l <flag> to clixon commands
Note there are also constants in the code (logdstmap) that need to be
in sync with these values.
The duplication is because of bootstrapping, logging is needed before YANG
loaded";
type bits {
bit syslog {
position 0;
description "Syslog";
}
bit stderr {
position 1;
description "Standard I/O Error";
}
bit stdout {
position 2;
description "Standard I/O Output";
}
bit file {
position 3;
description "Log to file. By default clixon.log int current directory";
}
}
}
container clixon-config {
container restconf {
uses clrc:clixon-restconf;
@ -456,14 +485,7 @@ module clixon-config {
Ensure that YANG_INSTALLDIR (default
/usr/local/share/clixon) is present in the path";
}
leaf CLICON_DEBUG{
type cl:clixon_debug_t;
description
"Debug flags as bitfields.
Can also be given directly as -D <flag> to clixon commands (which overrides this)
Note only partly implemented;
- Only CLI, only single value, cannot be combined with -D, not in RPC";
}
/* Configuration */
leaf CLICON_CONFIGFILE{
type string;
description
@ -497,6 +519,7 @@ module clixon-config {
This field is a 'bootstrap' field.
";
}
/* YANG */
leaf CLICON_YANG_MAIN_FILE {
type string;
description
@ -599,6 +622,7 @@ module clixon-config {
See also CLICON_XMLDB_MODSTATE where the module state info is used to tag datastores
with module information.";
}
/* Backend */
leaf CLICON_BACKEND_DIR {
type string;
description
@ -653,6 +677,7 @@ module clixon-config {
- on enable change, make the state as configured
Disable if you start the restconf daemon by other means.";
}
/* Netconf */
leaf CLICON_NETCONF_DIR{
type string;
description "Location of netconf (frontend) .so plugins";
@ -725,6 +750,7 @@ module clixon-config {
apart from NETCONF.
Only if CLICON_NETCONF_MONITORING";
}
/* HTTP and Restconf */
leaf CLICON_RESTCONF_API_ROOT {
type string;
default "/restconf";
@ -840,6 +866,7 @@ module clixon-config {
Both feature clixon-restconf:http-data and restconf/enable-http-data
must be enabled for this match to occur.";
}
/* Clixon CLI */
leaf CLICON_CLI_DIR {
type string;
description
@ -987,6 +1014,7 @@ module clixon-config {
description
"Default CLI output format.";
}
/* Internal socket */
leaf CLICON_SOCK_FAMILY {
type socket_address_family;
default UNIX;
@ -1056,6 +1084,7 @@ module clixon-config {
Also, any edits in candidate are discarded if the client closes the connection.
This effectively disables shared candidate";
}
/* Datastore XMLDB */
leaf CLICON_DATASTORE_CACHE {
type datastore_cache;
default cache;
@ -1201,6 +1230,7 @@ module clixon-config {
The current only case where such a user is used is in RESTCONF authentication when
auth-type=none and no known user is known.";
}
/* Network Configuration Access Control Model (NACM) */
leaf CLICON_NACM_MODE {
type nacm_mode;
default disabled;
@ -1255,6 +1285,7 @@ module clixon-config {
If CLICON_MODULE_LIBRARY_RFC7895 is enabled, it sets the modules-state/module-set-id
instead";
}
/* Notification streams */
leaf CLICON_STREAM_DISCOVERY_RFC5277 {
type boolean;
default false;
@ -1311,6 +1342,27 @@ module clixon-config {
description "Retention for stream replay buffers in seconds, ie how much
data to store before dropping. 0 means no retention";
}
/* Log and debug */
leaf CLICON_DEBUG{
type cl:clixon_debug_t;
description
"Debug flags as bitfields.
Can also be given directly as -D <flag> to clixon commands (which overrides this).";
}
leaf CLICON_LOG_DESTINATION {
type log_destination_t;
description
"Log destination.
If not given, default log destination is syslog for all applications,
except clixon_cli where default is stderr.
See also command-line option -l <s|e|o|n|f>";
}
leaf CLICON_LOG_FILE {
type string;
description
"Which file to log to if log destination is file
That is CLIXON_LOG_DESTINATION is FILE or command started with -l f";
}
leaf CLICON_LOG_STRING_LIMIT {
type uint32;
default 0;
@ -1318,8 +1370,8 @@ module clixon-config {
"Length limitation of debug and log strings.
Especially useful for dynamic debug strings, such as packet dumps.
0 means no limit";
}
/* SNMP */
leaf-list CLICON_SNMP_MIB {
description
"Names of MIBs that are used by clixon_snmp.

View file

@ -193,7 +193,7 @@ module clixon-lib {
"Debug flags.
Flags are seperated into subject areas and detail
Can also be given directly as -D <flag> to clixon commands
Note there are also constants in the code thaht need to be in sync with these values";
Note there are also constants in the code that need to be in sync with these values";
type bits {
/* Subjects: */
bit default {
@ -345,7 +345,9 @@ module clixon-lib {
A sub-object will not be noted";
}
rpc debug {
description "Set debug level of backend.";
description
"Set debug flags of backend.
Note only numerical values";
input {
leaf level {
type uint32;