diff options
Diffstat (limited to '')
-rw-r--r-- | contrib/fmunflatten/fmunflatten.c | 243 |
1 files changed, 243 insertions, 0 deletions
diff --git a/contrib/fmunflatten/fmunflatten.c b/contrib/fmunflatten/fmunflatten.c new file mode 100644 index 0000000..f1e4e69 --- /dev/null +++ b/contrib/fmunflatten/fmunflatten.c @@ -0,0 +1,243 @@ +/* + * This is a function module providing ability to unflatten a JSON tree. + * + * Copyright 2020 Julien Thomas < jthomas @ zenetys.com > + * + * 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 + * -or- + * see COPYING.ASL20 in the source distribution + * + * 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. + */ +#include "config.h" + +#include <stdint.h> +#include <stddef.h> +#ifndef _AIX +#include <typedefs.h> +#endif +#include <sys/types.h> +#include <string.h> + +#include "rsyslog.h" + +#include "errmsg.h" +#include "msg.h" +#include "parserif.h" +#include "module-template.h" +#include "rainerscript.h" +#include "wti.h" + +#define FMUNFLATTEN_KBUFLEN 256 +#define _jso_type(x) json_type_to_name(json_object_get_type(x)) + +MODULE_TYPE_FUNCTION +MODULE_TYPE_NOKEEP +DEF_FMOD_STATIC_DATA + +struct unflatten_ctx { + char *kbuf; + size_t kbuf_len; + char delim; +}; + +/* forward declarations */ +void unflatten_add(struct unflatten_ctx *ctx, struct json_object *dst, const char *key, struct json_object *value); +void unflatten(struct unflatten_ctx *ctx, struct json_object *src, struct json_object *dst); + +void unflatten_add(struct unflatten_ctx *ctx, + struct json_object *dst, + const char *key, + struct json_object *value) +{ + const char *p = key; + const char *q = p; + int depth = 0; + size_t klen; + struct json_object *o; + json_bool exists_already; + int create; + + while (1) { + while (*q != ctx->delim && *q != '\0') + q++; + klen = q - p; + if (klen + 1 > ctx->kbuf_len) { + DBGPRINTF("warning: flat key \"%.20s...\" truncated at depth #%d, buffer too " + "small (max %zd)\n", key, depth, ctx->kbuf_len); + klen = ctx->kbuf_len - 1; + } + memcpy(ctx->kbuf, p, klen); + ctx->kbuf[klen] = '\0'; + exists_already = json_object_object_get_ex(dst, ctx->kbuf, &o); + + if (*q == ctx->delim) { + if (!exists_already) + create = 1; + else if (json_object_is_type(o, json_type_object)) + create = 0; + else { + DBGPRINTF("warning: while processing flat key \"%s\" at depth #%d (intermediate " + "node), overriding existing value of type %s by an object\n", key, depth, + _jso_type(o)); + json_object_object_del(dst, ctx->kbuf); + create = 1; + } + if (create) { + o = json_object_new_object(); + json_object_object_add(dst, ctx->kbuf, o); + } + dst = o; + p = q + 1; + q = p; + depth++; + } + else if (*q == '\0') { + if (json_object_is_type(value, json_type_object)) { + if (exists_already) { + if (!json_object_is_type(o, json_type_object)) { + DBGPRINTF("warning: while processing flat key \"%s\" at depth #%d " + "(final node), overriding existing value of type %s by an " + "object\n", key, depth, _jso_type(o)); + json_object_object_del(dst, ctx->kbuf); + o = json_object_new_object(); + json_object_object_add(dst, ctx->kbuf, o); + } + } + else { + o = json_object_new_object(); + json_object_object_add(dst, ctx->kbuf, o); + } + unflatten(ctx, value, o); + } + else { + if (exists_already) { + DBGPRINTF("warning: while processing flat key \"%s\" at depth #%d " + "(final node), overriding existing value\n", key, depth); + json_object_object_del(dst, ctx->kbuf); + } + o = jsonDeepCopy(value); + json_object_object_add(dst, ctx->kbuf, o); + } + break; + } + } +} + +void unflatten(struct unflatten_ctx *ctx, + struct json_object *src, + struct json_object *dst) +{ + struct json_object_iterator it = json_object_iter_begin(src); + struct json_object_iterator itEnd = json_object_iter_end(src); + const char *key; + struct json_object *value; + + while (!json_object_iter_equal(&it, &itEnd)) { + key = json_object_iter_peek_name(&it); + value = json_object_iter_peek_value(&it); + unflatten_add(ctx, dst, key, value); + json_object_iter_next(&it); + } +} + +static void ATTR_NONNULL() +doFunc_unflatten(struct cnffunc *__restrict__ const func, + struct svar *__restrict__ const ret, + void *__restrict__ const usrptr, + wti_t *__restrict__ const pWti) +{ + struct svar src_var; + struct svar delim_var; + + char kbuf[FMUNFLATTEN_KBUFLEN]; + struct unflatten_ctx ctx = { + .kbuf = kbuf, + .kbuf_len = sizeof(kbuf), + .delim = 0 + }; + + /* A dummy value of 0 (number) is returned by default. Callers should also + * call script_error() to check if this script function succeeded before + * using the value it returns. */ + ret->datatype = 'N'; + ret->d.n = 0; + wtiSetScriptErrno(pWti, RS_SCRIPT_EINVAL); + + cnfexprEval(func->expr[0], &src_var, usrptr, pWti); + cnfexprEval(func->expr[1], &delim_var, usrptr, pWti); + + /* Check argument 2 (delimiter character). */ + if (delim_var.datatype == 'S' && es_strlen(delim_var.d.estr) == 1) + ctx.delim = *es_getBufAddr(delim_var.d.estr); + else if (delim_var.datatype == 'N') + ctx.delim = delim_var.d.n; + if (ctx.delim == 0) { + LogError(0, RS_RET_INVALID_PARAMS, "unflatten: invalid argument 2 (delim), single character " + "required (as string or decimal charcode)"); + FINALIZE; + } + + /* Check argument 1 (source). Not logging an error avoids emitting logs for + * messages when $! was not touched. */ + if (src_var.datatype != 'J') { + DBGPRINTF("unsupported argument 1 (src) datatype %c\n", src_var.datatype); + FINALIZE; + } + + ret->datatype = 'J'; + if (json_object_is_type(src_var.d.json, json_type_object)) { + ret->d.json = json_object_new_object(); + unflatten(&ctx, src_var.d.json, ret->d.json); + } + else + ret->d.json = jsonDeepCopy(src_var.d.json); + wtiSetScriptErrno(pWti, RS_SCRIPT_EOK); + +finalize_it: + varFreeMembers(&src_var); + varFreeMembers(&delim_var); +} + +static rsRetVal ATTR_NONNULL(1) +initFunc_unflatten(struct cnffunc *const func) +{ + DEFiRet; + func->destructable_funcdata = 0; + RETiRet; +} + +static struct scriptFunct functions[] = { + { "unflatten", 2, 2, doFunc_unflatten, initFunc_unflatten, NULL }, + { NULL, 0, 0, NULL, NULL, NULL } /* last element to check end of array */ +}; + +BEGINgetFunctArray +CODESTARTgetFunctArray + *version = 1; + *functArray = functions; +ENDgetFunctArray + +BEGINmodExit +CODESTARTmodExit +ENDmodExit + +BEGINqueryEtryPt +CODESTARTqueryEtryPt +CODEqueryEtryPt_STD_FMOD_QUERIES +ENDqueryEtryPt + +BEGINmodInit() +CODESTARTmodInit + *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */ +CODEmodInit_QueryRegCFSLineHdlr + dbgprintf("rsyslog fmunflatten init called, compiled with version %s\n", VERSION); +ENDmodInit |