logger CHANGE support for validation errors - work in progress
diff --git a/src/common.h b/src/common.h
index 7e7801a..c73eec9 100644
--- a/src/common.h
+++ b/src/common.h
@@ -47,12 +47,22 @@
ILO_ERR2WRN, /* change errors to warnings */
};
+enum LY_VLOG_ELEM {
+ LY_VLOG_NONE = 0,
+ LY_VLOG_LINE,/* line number */
+ LY_VLOG_LYS, /* struct lysc_node* */
+ LY_VLOG_LYD, /* struct lyd_node* */
+ LY_VLOG_STR, /* const char* */
+ LY_VLOG_PREV /* use exact same previous path */
+};
+
extern THREAD_LOCAL enum int_log_opts log_opt;
extern volatile uint8_t ly_log_level;
extern volatile uint8_t ly_log_opts;
void ly_err_free(void *ptr);
void ly_log(const struct ly_ctx *ctx, LY_LOG_LEVEL level, LY_ERR no, const char *format, ...);
+void ly_vlog(const struct ly_ctx *ctx, enum LY_VLOG_ELEM elem_type, const void *elem, LY_VECODE code, const char *format, ...);
#define LOGERR(ctx, errno, str, args...) ly_log(ctx, LY_LLERR, errno, str, ##args)
#define LOGWRN(ctx, str, args...) ly_log(ctx, LY_LLWRN, 0, str, ##args)
@@ -68,6 +78,11 @@
#define LOGMEM(CTX) LOGERR(CTX, LY_EMEM, "Memory allocation failed (%s()).", __func__)
#define LOGINT(CTX) LOGERR(CTX, LY_EINT, "Internal error (%s:%d).", __FILE__, __LINE__)
#define LOGARG(CTX, ARG) LOGERR(CTX, LY_EINVAL, "Invalid argument %s (%s()).", #ARG, __func__)
+#define LOGVAL(CTX, ELEM_TYPE, ELEM, CODE, FORMAT, args...) ly_vlog(CTX, ELEM_TYPE, ELEM, FORMAT ##args)
+
+#define LOGMEM_RET(CTX) LOGMEM(CTX); return LY_EMEM
+#define LOGINT_RET(CTX) LOGINT(CTX); return LY_EINT
+#define LOGARG_RET(CTX) LOGARG(CTX); return LY_EINVAL
/*
* Common code to check return value and perform appropriate action.
@@ -87,6 +102,13 @@
#define LY_CHECK_ARG_RET3(CTX, ARG1, ARG2, ARG3, RETVAL) LY_CHECK_ARG_RET2(CTX, ARG1, ARG2, RETVAL);LY_CHECK_ARG_RET1(CTX, ARG3, RETVAL)
#define LY_CHECK_ARG_RET(CTX, ...) GETMACRO4(__VA_ARGS__, LY_CHECK_ARG_RET3, LY_CHECK_ARG_RET2, LY_CHECK_ARG_RET1)(CTX, __VA_ARGS__)
+#define LY_VCODE_MISSING LYVE_SYNTAX, "Missing %s \"%s\"."
+#define LY_VCODE_INVAL LYVE_SYNTAX, "Invalid %s."
+#define LY_VCODE_INCHAR LYVE_SYNTAX, "Encountered invalid character sequence \"%.10s\"."
+#define LY_VCODE_EOF LYVE_SYNTAX, "Unexpected end of input data."
+#define LY_VCODE_INSTMT LYVE_SYNTAX_YANG, "Invalid keyword \"%s\"."
+#define LY_VCODE_INCHILDSTMT LYVE_SYNTAX_YANG, "Invalid keyword \"%s\" as a child to \"%s\"."
+
/*
* If the compiler supports attribute to mark objects as hidden, mark all
* objects as hidden and export only objects explicitly marked to be part of
diff --git a/src/log.c b/src/log.c
index d61895e..d1a6dd3 100644
--- a/src/log.c
+++ b/src/log.c
@@ -30,6 +30,9 @@
volatile int ly_log_dbg_groups = 0;
#endif
+/* how many bytes add when enlarging buffers */
+#define LY_BUF_STEP 128
+
API LY_VECODE
ly_vecode(const struct ly_ctx *ctx)
{
@@ -369,6 +372,259 @@
va_end(ap);
}
+static LY_ERR
+ly_vlog_build_path_print(const struct ly_ctx *ctx, char **path, uint16_t *index, const char *str, uint16_t str_len, uint16_t *length)
+{
+ void *mem;
+ uint16_t step;
+
+ if ((*index) < str_len) {
+ /* enlarge buffer */
+ step = (str_len < LY_BUF_STEP) ? LY_BUF_STEP : str_len;
+ mem = realloc(*path, *length + *index + step + 1);
+ LY_CHECK_ERR_RET(!mem, LOGMEM(ctx), LY_EMEM);
+ *path = mem;
+
+ /* move data, lengths */
+ memmove(&(*path)[*index + step], &(*path)[*index], *length);
+ (*index) += step;
+ }
+
+ (*index) -= str_len;
+ memcpy(&(*path)[*index], str, str_len);
+ *length += str_len;
+
+ return 0;
+}
+
+LY_ERR
+ly_vlog_build_path(const struct ly_ctx *ctx, enum LY_VLOG_ELEM elem_type, const void *elem, char **path, int UNUSED(schema_all_prefixes))
+{
+ uint16_t length, index;
+ size_t len;
+ LY_ERR rc = LY_SUCCESS;
+
+ length = 0;
+ *path = malloc(1);
+ LY_CHECK_ERR_RET(!(*path), LOGMEM(ctx), LY_EMEM);
+ index = 0;
+
+ while (elem) {
+ switch (elem_type) {
+ case LY_VLOG_LYS:
+ /* TODO */
+ break;
+ case LY_VLOG_LYD:
+#if 0 /* TODO when data tree present */
+ name = ((struct lyd_node *)elem)->schema->name;
+ if (!((struct lyd_node *)elem)->parent ||
+ lyd_node_module((struct lyd_node *)elem) != lyd_node_module(((struct lyd_node *)elem)->parent)) {
+ prefix = lyd_node_module((struct lyd_node *)elem)->name;
+ } else {
+ prefix = NULL;
+ }
+
+ /* handle predicates (keys) in case of lists */
+ if (((struct lyd_node *)elem)->schema->nodetype == LYS_LIST) {
+ dlist = (struct lyd_node *)elem;
+ slist = (struct lys_node_list *)((struct lyd_node *)elem)->schema;
+ if (slist->keys_size) {
+ /* schema list with keys - use key values in predicates */
+ for (i = slist->keys_size - 1; i > -1; i--) {
+ LY_TREE_FOR(dlist->child, diter) {
+ if (diter->schema == (struct lys_node *)slist->keys[i]) {
+ break;
+ }
+ }
+ if (diter && ((struct lyd_node_leaf_list *)diter)->value_str) {
+ if (strchr(((struct lyd_node_leaf_list *)diter)->value_str, '\'')) {
+ val_start = "=\"";
+ val_end = "\"]";
+ } else {
+ val_start = "='";
+ val_end = "']";
+ }
+
+ /* print value */
+ if (ly_vlog_build_path_print(path, &index, val_end, 2, &length)) {
+ return -1;
+ }
+ len = strlen(((struct lyd_node_leaf_list *)diter)->value_str);
+ if (ly_vlog_build_path_print(path, &index,
+ ((struct lyd_node_leaf_list *)diter)->value_str, len, &length)) {
+ return -1;
+ }
+
+ /* print schema name */
+ if (ly_vlog_build_path_print(path, &index, val_start, 2, &length)) {
+ return -1;
+ }
+ len = strlen(diter->schema->name);
+ if (ly_vlog_build_path_print(path, &index, diter->schema->name, len, &length)) {
+ return -1;
+ }
+
+ if (lyd_node_module(dlist) != lyd_node_module(diter)) {
+ if (ly_vlog_build_path_print(path, &index, ":", 1, &length)) {
+ return -1;
+ }
+ len = strlen(lyd_node_module(diter)->name);
+ if (ly_vlog_build_path_print(path, &index, lyd_node_module(diter)->name, len, &length)) {
+ return -1;
+ }
+ }
+
+ if (ly_vlog_build_path_print(path, &index, "[", 1, &length)) {
+ return -1;
+ }
+ }
+ }
+ } else {
+ /* schema list without keys - use instance position */
+ i = j = lyd_list_pos(dlist);
+ len = 1;
+ while (j > 9) {
+ ++len;
+ j /= 10;
+ }
+
+ if (ly_vlog_build_path_print(path, &index, "]", 1, &length)) {
+ return -1;
+ }
+
+ str = malloc(len + 1);
+ LY_CHECK_ERR_RETURN(!str, LOGMEM(NULL), -1);
+ sprintf(str, "%d", i);
+
+ if (ly_vlog_build_path_print(path, &index, str, len, &length)) {
+ free(str);
+ return -1;
+ }
+ free(str);
+
+ if (ly_vlog_build_path_print(path, &index, "[", 1, &length)) {
+ return -1;
+ }
+ }
+ } else if (((struct lyd_node *)elem)->schema->nodetype == LYS_LEAFLIST &&
+ ((struct lyd_node_leaf_list *)elem)->value_str) {
+ if (strchr(((struct lyd_node_leaf_list *)elem)->value_str, '\'')) {
+ val_start = "[.=\"";
+ val_end = "\"]";
+ } else {
+ val_start = "[.='";
+ val_end = "']";
+ }
+
+ if (ly_vlog_build_path_print(path, &index, val_end, 2, &length)) {
+ return -1;
+ }
+ len = strlen(((struct lyd_node_leaf_list *)elem)->value_str);
+ if (ly_vlog_build_path_print(path, &index, ((struct lyd_node_leaf_list *)elem)->value_str, len, &length)) {
+ return -1;
+ }
+ if (ly_vlog_build_path_print(path, &index, val_start, 4, &length)) {
+ return -1;
+ }
+ }
+
+ /* check if it is yang-data top element */
+ if (!((struct lyd_node *)elem)->parent) {
+ ext_name = lyp_get_yang_data_template_name(elem);
+ if (ext_name) {
+ if (ly_vlog_build_path_print(path, &index, name, strlen(name), &length)) {
+ return -1;
+ }
+ if (ly_vlog_build_path_print(path, &index, "/", 1, &length)) {
+ return -1;
+ }
+ yang_data_extension = 1;
+ name = ext_name;
+ }
+ }
+
+ elem = ((struct lyd_node *)elem)->parent;
+ break;
+#endif
+ case LY_VLOG_STR:
+ len = strlen((const char *)elem);
+ rc = ly_vlog_build_path_print(ctx, path, &index, (const char *)elem, len, &length);
+ LY_CHECK_RET(rc != LY_SUCCESS, rc);
+ goto success;
+ case LY_VLOG_LINE:
+
+ goto success;
+ default:
+ /* shouldn't be here */
+ LOGINT_RET(ctx);
+ }
+
+#if 0 /* TODO when data/schema tree present */
+ if (name) {
+ if (ly_vlog_build_path_print(ctx, path, &index, name, strlen(name), &length)) {
+ return -1;
+ }
+ if (prefix) {
+ if (yang_data_extension && ly_vlog_build_path_print(path, &index, "#", 1, &length)) {
+ return -1;
+ }
+ if (ly_vlog_build_path_print(ctx, path, &index, ":", 1, &length)) {
+ return -1;
+ }
+ if (ly_vlog_build_path_print(ctx, path, &index, prefix, strlen(prefix), &length)) {
+ return -1;
+ }
+ }
+ }
+ if (ly_vlog_build_path_print(ctx, path, &index, "/", 1, &length)) {
+ return -1;
+ }
+ if ((elem_type == LY_VLOG_LYS) && !elem && sparent && (sparent->nodetype == LYS_AUGMENT)) {
+ len = strlen(((struct lys_node_augment *)sparent)->target_name);
+ if (ly_vlog_build_path_print(ctx, path, &index, ((struct lys_node_augment *)sparent)->target_name, len, &length)) {
+ return -1;
+ }
+ }
+#endif
+ }
+
+success:
+ memmove(*path, (*path) + index, length);
+ (*path)[length] = '\0';
+ return LY_SUCCESS;
+}
+
+void
+ly_vlog(const struct ly_ctx *ctx, enum LY_VLOG_ELEM elem_type, const void *elem, LY_VECODE code, const char *format, ...)
+{
+ va_list ap;
+ char* path = NULL;
+ const struct ly_err_item *first;
+
+ if (path_flag && (elem_type != LY_VLOG_NONE)) {
+ if (elem_type == LY_VLOG_PREV) {
+ /* use previous path */
+ first = ly_err_first(ctx);
+ if (first && first->prev->path) {
+ path = strdup(first->prev->path);
+ }
+ } else {
+ /* print path */
+ if (!elem) {
+ /* top-level */
+ path = strdup("/");
+ } else {
+ ly_vlog_build_path(ctx, elem_type, elem, &path, 0);
+ }
+ }
+ }
+
+ va_start(ap, format);
+ log_vprintf(ctx, LY_LLERR, LY_EVALID, code, path, format, ap);
+ /* path is spent and should not be freed! */
+ va_end(ap);
+}
+
API void
ly_err_print(struct ly_err_item *eitem)
{
diff --git a/src/log.h b/src/log.h
index 8ab9703..d1b159e 100644
--- a/src/log.h
+++ b/src/log.h
@@ -159,6 +159,8 @@
*/
typedef enum {
LYVE_SUCCESS = 0, /**< no error */
+ LYVE_SYNTAX, /**< generic syntax error */
+ LYVE_SYNTAX_YANG, /**< YANG-related syntax error */
} LY_VECODE;
/**