From 45f82efd0138df65431d9937eeb11514e3e19cd7 Mon Sep 17 00:00:00 2001 From: CentOS Sources Date: May 07 2019 05:17:51 +0000 Subject: import rsyslog-8.37.0-9.el8 --- diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b93d279 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +SOURCES/rsyslog-8.37.0.tar.gz +SOURCES/rsyslog-doc-8.37.0.tar.gz diff --git a/.rsyslog.metadata b/.rsyslog.metadata new file mode 100644 index 0000000..7b6aed8 --- /dev/null +++ b/.rsyslog.metadata @@ -0,0 +1,2 @@ +43076e3010fc3fd5178201a916beb93848b5249c SOURCES/rsyslog-8.37.0.tar.gz +4c75f56e2d55c4c87d07781fb6d9deabf63395fb SOURCES/rsyslog-doc-8.37.0.tar.gz diff --git a/SOURCES/rsyslog-8.32.0-service.patch b/SOURCES/rsyslog-8.32.0-service.patch new file mode 100644 index 0000000..cb59f3f --- /dev/null +++ b/SOURCES/rsyslog-8.32.0-service.patch @@ -0,0 +1,21 @@ +diff -up ./rsyslog.service.in.service ./rsyslog.service.in +--- ./rsyslog.service.in.service 2018-03-01 13:58:11.480598935 +0100 ++++ ./rsyslog.service.in 2018-03-01 13:58:25.433518607 +0100 +@@ -1,12 +1,16 @@ + [Unit] + Description=System Logging Service + Requires=syslog.socket ++Wants=network.target network-online.target ++After=network.target network-online.target + Documentation=man:rsyslogd(8) + Documentation=http://www.rsyslog.com/doc/ + + [Service] + Type=notify +-ExecStart=@sbindir@/rsyslogd -n -iNONE ++EnvironmentFile=-/etc/sysconfig/rsyslog ++ExecStart=@sbindir@/rsyslogd -n $SYSLOGD_OPTIONS ++UMask=0066 + StandardOutput=null + Restart=on-failure + diff --git a/SOURCES/rsyslog-8.37.0-rhbz1614179-imfile-symlink-support.patch b/SOURCES/rsyslog-8.37.0-rhbz1614179-imfile-symlink-support.patch new file mode 100644 index 0000000..125846d --- /dev/null +++ b/SOURCES/rsyslog-8.37.0-rhbz1614179-imfile-symlink-support.patch @@ -0,0 +1,416 @@ +From 3822da837e4d531e8a9cd78ae76359a410f8d98d Mon Sep 17 00:00:00 2001 +From: Jiri Vymazal +Date: Thu, 31 May 2018 16:30:08 +0200 +Subject: [PATCH] Symlink support for imfile + +this introduces symlink detection and following as well +as monitoring changes on them. Also added test for the new +functionality and ensuring the original symlink behavior +stays as well. +--- + plugins/imfile/imfile.c | 182 +++++++++++++++++++++++++++---------- + 1 file changed, 133 insertions(+), 49 deletions(-) + +diff --git a/plugins/imfile/imfile.c b/plugins/imfile/imfile.c +index 3c9308bfe..4ca23d2ca 100644 +--- a/plugins/imfile/imfile.c ++++ b/plugins/imfile/imfile.c +@@ -152,6 +152,7 @@ struct act_obj_s { + fs_edge_t *edge; /* edge which this object belongs to */ + char *name; /* full path name of active object */ + char *basename; /* only basename */ //TODO: remove when refactoring rename support ++ char *source_name; /* if this object is target of a symlink, source_name is its name (else NULL) */ + //char *statefile; /* base name of state file (for move operations) */ + int wd; + #if defined(OS_SOLARIS) && defined (HAVE_PORT_SOURCE_FILE) +@@ -167,6 +168,7 @@ struct act_obj_s { + int nRecords; /**< How many records did we process before persisting the stream? */ + ratelimit_t *ratelimiter; + multi_submit_t multiSub; ++ int is_symlink; + }; + struct fs_edge_s { + fs_node_t *parent; +@@ -181,7 +182,8 @@ struct act_obj_s { + instanceConf_t **instarr; + }; + struct fs_node_s { +- fs_edge_t *edges; ++ fs_edge_t *edges; /* NULL in leaf nodes */ ++ fs_node_t *root; /* node one level up (NULL for file system root) */ + }; + + +@@ -189,7 +191,7 @@ static rsRetVal persistStrmState(act_obj_t *); + static rsRetVal resetConfigVariables(uchar __attribute__((unused)) *pp, void __attribute__((unused)) *pVal); + static rsRetVal ATTR_NONNULL(1) pollFile(act_obj_t *act); + static int ATTR_NONNULL() getBasename(uchar *const __restrict__ basen, uchar *const __restrict__ path); +-static void ATTR_NONNULL() act_obj_unlink(act_obj_t *const act); ++static void ATTR_NONNULL() act_obj_unlink(act_obj_t *act); + static uchar * ATTR_NONNULL(1, 2) getStateFileName(const act_obj_t *, uchar *, const size_t); + static int ATTR_NONNULL() getFullStateFileName(const uchar *const, uchar *const pszout, const size_t ilenout); + +@@ -483,14 +485,17 @@ in_setupWatch(act_obj_t *const act, const int is_file) + goto done; + + wd = inotify_add_watch(ino_fd, act->name, +- (is_file) ? IN_MODIFY : IN_CREATE|IN_DELETE|IN_MOVED_FROM|IN_MOVED_TO); ++ (is_file) ? IN_MODIFY|IN_DONT_FOLLOW : IN_CREATE|IN_DELETE|IN_MOVED_FROM|IN_MOVED_TO); + if(wd < 0) { +- LogError(errno, RS_RET_IO_ERROR, "imfile: cannot watch object '%s'", +- act->name); ++ if (errno == EACCES) { /* There is high probability of selinux denial on top-level paths */ ++ DBGPRINTF("imfile: permission denied when adding watch for '%s'\n", act->name); ++ } else { ++ LogError(errno, RS_RET_IO_ERROR, "imfile: cannot watch object '%s'", act->name); ++ } + goto done; + } + wdmapAdd(wd, act); +- DBGPRINTF("in_setupDirWatch: watch %d added for dir %s(%p)\n", wd, act->name, act); ++ DBGPRINTF("in_setupWatch: watch %d added for %s(object %p)\n", wd, act->name, act); + done: return wd; + } + +@@ -605,7 +610,7 @@ done: return; + static void ATTR_NONNULL() + fen_setupWatch(act_obj_t *const act __attribute__((unused))) + { +- DBGPRINTF("fen_setupWatch: DUMMY CALLED - not on Solaris?"); ++ DBGPRINTF("fen_setupWatch: DUMMY CALLED - not on Solaris?\n"); + } + #endif /* FEN */ + +@@ -633,38 +638,48 @@ fs_node_print(const fs_node_t *const node, const int level) + } + } + +- + /* add a new file system object if it not yet exists, ignore call + * if it already does. + */ +-static rsRetVal ATTR_NONNULL() ++static rsRetVal ATTR_NONNULL(1,2) + act_obj_add(fs_edge_t *const edge, const char *const name, const int is_file, +- const ino_t ino) ++ const ino_t ino, const int is_symlink, const char *const source) + { + act_obj_t *act; + char basename[MAXFNAME]; + DEFiRet; + +- DBGPRINTF("act_obj_add: edge %p, name '%s'\n", edge, name); ++ DBGPRINTF("act_obj_add: edge %p, name '%s' (source '%s')\n", edge, name, source? source : "---"); + for(act = edge->active ; act != NULL ; act = act->next) { + if(!strcmp(act->name, name)) { +- DBGPRINTF("active object '%s' already exists in '%s' - no need to add\n", +- name, edge->path); +- FINALIZE; ++ if (!source || !act->source_name || !strcmp(act->source_name, source)) { ++ DBGPRINTF("active object '%s' already exists in '%s' - no need to add\n", ++ name, edge->path); ++ FINALIZE; ++ } + } + } + DBGPRINTF("add new active object '%s' in '%s'\n", name, edge->path); + CHKmalloc(act = calloc(sizeof(act_obj_t), 1)); + CHKmalloc(act->name = strdup(name)); +- getBasename((uchar*)basename, (uchar*)name); +- CHKmalloc(act->basename = strdup(basename)); ++ if (-1 == getBasename((uchar*)basename, (uchar*)name)) { ++ CHKmalloc(act->basename = strdup(name)); /* assume basename is same as name */ ++ } else { ++ CHKmalloc(act->basename = strdup(basename)); ++ } + act->edge = edge; + act->ino = ino; ++ act->is_symlink = is_symlink; ++ if (source) { /* we are target of symlink */ ++ CHKmalloc(act->source_name = strdup(source)); ++ } else { ++ act->source_name = NULL; ++ } + #ifdef HAVE_INOTIFY_INIT + act->wd = in_setupWatch(act, is_file); + #endif + fen_setupWatch(act); +- if(is_file) { ++ if(is_file && !is_symlink) { + const instanceConf_t *const inst = edge->instarr[0];// TODO: same file, multiple instances? + CHKiRet(ratelimitNew(&act->ratelimiter, "imfile", name)); + CHKmalloc(act->multiSub.ppMsgs = MALLOC(inst->nMultiSub * sizeof(smsg_t *))); +@@ -702,27 +717,24 @@ detect_updates(fs_edge_t *const edge) + { + act_obj_t *act; + struct stat fileInfo; ++ int restart = 0; + +- for(act = edge->active ; act != NULL ; ) { ++ for(act = edge->active ; act != NULL ; act = act->next) { + DBGPRINTF("detect_updates checking active obj '%s'\n", act->name); +- const int r = stat(act->name, &fileInfo); ++ const int r = lstat(act->name, &fileInfo); + if(r == -1) { /* object gone away? */ + DBGPRINTF("object gone away, unlinking: '%s'\n", act->name); +- act_obj_t *toDel = act; +- act = act->next; +- DBGPRINTF("new next act %p\n", act); +- act_obj_unlink(toDel); +- continue; ++ act_obj_unlink(act); ++ restart = 1; ++ break; + } + // TODO: add inode check for change notification! + +- /* Note: active nodes may get deleted, so we need to do the +- * pointer advancement at the end of the for loop! +- */ +- act = act->next; + } + +- ++ if (restart) { ++ detect_updates(edge); ++ } + } + + +@@ -746,14 +758,52 @@ poll_active_files(fs_edge_t *const edge) + } + } + ++static rsRetVal ATTR_NONNULL() ++process_symlink(fs_edge_t *const chld, const char *symlink) ++{ ++ DEFiRet; ++ char *target = NULL; ++ CHKmalloc(target = realpath(symlink, target)); ++ struct stat fileInfo; ++ if(lstat(target, &fileInfo) != 0) { ++ LogError(errno, RS_RET_ERR, "imfile: process_symlink: cannot stat file '%s' - ignored", target); ++ FINALIZE; ++ } ++ const int is_file = (S_ISREG(fileInfo.st_mode)); ++ DBGPRINTF("process_symlink: found '%s', File: %d (config file: %d), symlink: %d\n", ++ target, is_file, chld->is_file, 0); ++ if (act_obj_add(chld, target, is_file, fileInfo.st_ino, 0, symlink) == RS_RET_OK) { ++ /* need to watch parent target as well for proper rotation support */ ++ uint idx = ustrlen(chld->active->name) - ustrlen(chld->active->basename); ++ if (idx) { /* basename is different from name */ ++ char parent[MAXFNAME]; ++ idx--; /* move past trailing slash */ ++ memcpy(parent, chld->active->name, idx); ++ parent[idx] = '\0'; ++ if(lstat(parent, &fileInfo) != 0) { ++ LogError(errno, RS_RET_ERR, ++ "imfile: process_symlink: cannot stat directory '%s' - ignored", parent); ++ FINALIZE; ++ } ++ if (chld->parent->root->edges) { ++ DBGPRINTF("process_symlink: adding parent '%s' of target '%s'\n", parent, target); ++ act_obj_add(chld->parent->root->edges, parent, 0, fileInfo.st_ino, 0, NULL); ++ } ++ } ++ } ++ ++finalize_it: ++ free(target); ++ RETiRet; ++} + +-static void ATTR_NONNULL() poll_tree(fs_edge_t *const chld); + static void ATTR_NONNULL() + poll_tree(fs_edge_t *const chld) + { + struct stat fileInfo; + glob_t files; + int need_globfree = 0; ++ int issymlink; + DBGPRINTF("poll_tree: chld %p, name '%s', path: %s\n", chld, chld->name, chld->path); + detect_updates(chld); + const int ret = glob((char*)chld->path, runModConf->sortFiles|GLOB_BRACE, NULL, &files); +@@ -766,18 +803,27 @@ poll_tree(fs_edge_t *const chld) + goto done; + } + char *const file = files.gl_pathv[i]; +- if(stat(file, &fileInfo) != 0) { ++ if(lstat(file, &fileInfo) != 0) { + LogError(errno, RS_RET_ERR, + "imfile: poll_tree cannot stat file '%s' - ignored", file); + continue; + } + +- const int is_file = S_ISREG(fileInfo.st_mode); +- DBGPRINTF("poll_tree: found '%s', File: %d (config file: %d)\n", +- file, is_file, chld->is_file); ++ if (S_ISLNK(fileInfo.st_mode)) { ++ rsRetVal slink_ret = process_symlink(chld, file); ++ if (slink_ret != RS_RET_OK) { ++ continue; ++ } ++ issymlink = 1; ++ } else { ++ issymlink = 0; ++ } ++ const int is_file = (S_ISREG(fileInfo.st_mode) || issymlink); ++ DBGPRINTF("poll_tree: found '%s', File: %d (config file: %d), symlink: %d\n", ++ file, is_file, chld->is_file, issymlink); + if(!is_file && S_ISREG(fileInfo.st_mode)) { + LogMsg(0, RS_RET_ERR, LOG_WARNING, +- "imfile: '%s' is neither a regular file nor a " ++ "imfile: '%s' is neither a regular file, symlink, nor a " + "directory - ignored", file); + continue; + } +@@ -788,7 +834,7 @@ poll_tree(fs_edge_t *const chld) + (chld->is_file) ? "FILE" : "DIRECTORY"); + continue; + } +- act_obj_add(chld, file, is_file, fileInfo.st_ino); ++ act_obj_add(chld, file, is_file, fileInfo.st_ino, issymlink, NULL); + } + } + +@@ -829,8 +875,20 @@ act_obj_destroy(act_obj_t *const act, const int is_deleted) + if(act == NULL) + return; + +- DBGPRINTF("act_obj_destroy: act %p '%s', wd %d, pStrm %p, is_deleted %d, in_move %d\n", +- act, act->name, act->wd, act->pStrm, is_deleted, act->in_move); ++ DBGPRINTF("act_obj_destroy: act %p '%s' (source '%s'), wd %d, pStrm %p, is_deleted %d, in_move %d\n", ++ act, act->name, act->source_name? act->source_name : "---", act->wd, act->pStrm, is_deleted, ++ act->in_move); ++ if(act->is_symlink && is_deleted) { ++ act_obj_t *target_act; ++ for(target_act = act->edge->active ; target_act != NULL ; target_act = target_act->next) { ++ if(target_act->source_name && !strcmp(target_act->source_name, act->name)) { ++ DBGPRINTF("act_obj_destroy: unlinking slink target %s of %s " ++ "symlink\n", target_act->name, act->name); ++ act_obj_unlink(target_act); ++ break; ++ } ++ } ++ } + if(act->ratelimiter != NULL) { + ratelimitDestruct(act->ratelimiter); + } +@@ -862,6 +920,7 @@ act_obj_destroy(act_obj_t *const act, const int is_deleted) + } + #endif + free(act->basename); ++ free(act->source_name); + //free(act->statefile); + free(act->multiSub.ppMsgs); + #if defined(OS_SOLARIS) && defined (HAVE_PORT_SOURCE_FILE) +@@ -909,7 +968,7 @@ chk_active(const act_obj_t *act, const act_obj_t *const deleted) + * destruct it. + */ + static void //ATTR_NONNULL() +-act_obj_unlink(act_obj_t *const act) ++act_obj_unlink(act_obj_t *act) + { + DBGPRINTF("act_obj_unlink %p: %s\n", act, act->name); + if(act->prev == NULL) { +@@ -921,6 +980,7 @@ act_obj_unlink(act_obj_t *const act) + act->next->prev = act->prev; + } + act_obj_destroy(act, 1); ++ act = NULL; + //dbgprintf("printout of fs tree post unlink\n"); + //fs_node_print(runModConf->conf_tree, 0); + //dbg_wdmapPrint("wdmap after"); +@@ -1025,6 +1038,7 @@ fs_node_walk(fs_node_t *const node, + */ + static rsRetVal + fs_node_add(fs_node_t *const node, ++ fs_node_t *const source, + const uchar *const toFind, + const size_t pathIdx, + instanceConf_t *const inst) +@@ -1053,6 +1067,7 @@ fs_node_add(fs_node_t *const node, + memcpy(name, toFind+pathIdx, len); + name[len] = '\0'; + DBGPRINTF("fs_node_add: name '%s'\n", name); ++ node->root = source; + + fs_edge_t *chld; + for(chld = node->edges ; chld != NULL ; chld = chld->next) { +@@ -1064,7 +1079,7 @@ fs_node_add(fs_node_t *const node, + chld->instarr[chld->ninst-1] = inst; + /* recurse */ + if(!isFile) { +- CHKiRet(fs_node_add(chld->node, toFind, nextPathIdx, inst)); ++ CHKiRet(fs_node_add(chld->node, node, toFind, nextPathIdx, inst)); + } + FINALIZE; + } +@@ -1086,7 +1101,7 @@ fs_node_add(fs_node_t *const node, + DBGPRINTF("fs_node_add(%p, '%s') returns %p\n", node, toFind, newchld->node); + + if(!isFile) { +- CHKiRet(fs_node_add(newchld->node, toFind, nextPathIdx, inst)); ++ CHKiRet(fs_node_add(newchld->node, node, toFind, nextPathIdx, inst)); + } + + /* link to list */ +@@ -1162,7 +1222,11 @@ enqLine(act_obj_t *const act, + msgSetPRI(pMsg, inst->iFacility | inst->iSeverity); + MsgSetRuleset(pMsg, inst->pBindRuleset); + if(inst->addMetadata) { +- metadata_values[0] = (const uchar*)act->name; ++ if (act->source_name) { ++ metadata_values[0] = (const uchar*)act->source_name; ++ } else { ++ metadata_values[0] = (const uchar*)act->name; ++ } + snprintf((char *)file_offset, MAX_OFFSET_REPRESENTATION_NUM_BYTES+1, "%lld", strtOffs); + metadata_values[1] = file_offset; + msgAddMultiMetadata(pMsg, metadata_names, metadata_values, 2); +@@ -1389,13 +1453,16 @@ pollFile(act_obj_t *const act) + { + cstr_t *pCStr = NULL; + DEFiRet; ++ if (act->is_symlink) { ++ FINALIZE; /* no reason to poll symlink file */ ++ } + /* Note: we must do pthread_cleanup_push() immediately, because the POSIX macros + * otherwise do not work if I include the _cleanup_pop() inside an if... -- rgerhards, 2008-08-14 + */ + pthread_cleanup_push(pollFileCancelCleanup, &pCStr); + iRet = pollFileReal(act, &pCStr); + pthread_cleanup_pop(0); +- RETiRet; ++finalize_it: RETiRet; + } + + +@@ -1931,7 +1946,7 @@ CODESTARTactivateCnf + "be processed. Reason", inst->pszFileName); + } + } +- fs_node_add(runModConf->conf_tree, inst->pszFileName, 0, inst); ++ fs_node_add(runModConf->conf_tree, NULL, inst->pszFileName, 0, inst); + } + + if(Debug) { +@@ -2031,6 +2113,9 @@ flag_in_move(fs_edge_t *const edge, const char *name_moved) + DBGPRINTF("name check fails, '%s' != '%s'\n", act->basename, name_moved); + } + } ++ if (!act && edge->next) { ++ flag_in_move(edge->next, name_moved); ++ } + } + + static void ATTR_NONNULL(1) +@@ -2057,7 +2145,7 @@ in_processEvent(struct inotify_event *ev) + } + if(ev->mask & (IN_MOVED_FROM | IN_MOVED_TO)) { + fs_node_walk(etry->act->edge->node, poll_tree); +- } else if(etry->act->edge->is_file) { ++ } else if(etry->act->edge->is_file && !(etry->act->is_symlink)) { + in_handleFileEvent(ev, etry); // esentially poll_file()! + } else { + fs_node_walk(etry->act->edge->node, poll_tree); diff --git a/SOURCES/rsyslog-8.37.0-rhbz1622768-kubernetes-404-handling.patch b/SOURCES/rsyslog-8.37.0-rhbz1622768-kubernetes-404-handling.patch new file mode 100644 index 0000000..9b6db7b --- /dev/null +++ b/SOURCES/rsyslog-8.37.0-rhbz1622768-kubernetes-404-handling.patch @@ -0,0 +1,761 @@ +From 3987cd929d859f900318b393133c3bdde8dfffd5 Mon Sep 17 00:00:00 2001 +From: Rich Megginson +Date: Tue, 28 Aug 2018 12:44:23 -0600 +Subject: [PATCH] mmkubertnetes: action fails preparation cycle if kubernetes + API destroys resource during bootup sequence + +The plugin was not handling 404 Not Found correctly when looking +up pods and namespaces. In this case, we assume the pod/namespace +was deleted, annotate the record with whatever metadata we have, +and cache the fact that the pod/namespace is missing so we don't +attempt to look it up again. +In addition, the plugin was not handling error 429 Busy correctly. +In this case, it should also annotate the record with whatever +metadata it has, and _not_ cache anything. By default the plugin +will retry every 5 seconds to connect to Kubernetes. This +behavior is controlled by the new config param `busyretryinterval`. +This commit also adds impstats counters so that admins can +view the state of the plugin to see if the lookups are working +or are returning errors. The stats are reported per-instance +or per-action to facilitate using multiple different actions +for different Kubernetes servers. +This commit also adds support for client cert auth to +Kubernetes via the two new config params `tls.mycert` and +`tls.myprivkey`. +--- + contrib/mmkubernetes/mmkubernetes.c | 296 ++++++++++++++++++++++++---- + 7 files changed, 160 insertions(+), 36 deletions(-) + +diff --git a/contrib/mmkubernetes/mmkubernetes.c b/contrib/mmkubernetes/mmkubernetes.c +index 422cb2577..5bf5b049d 100644 +--- a/contrib/mmkubernetes/mmkubernetes.c ++++ b/contrib/mmkubernetes/mmkubernetes.c +@@ -52,9 +52,12 @@ + #include "syslogd-types.h" + #include "module-template.h" + #include "errmsg.h" ++#include "statsobj.h" + #include "regexp.h" + #include "hashtable.h" + #include "srUtils.h" ++#include "unicode-helper.h" ++#include "datetime.h" + + /* static data */ + MODULE_TYPE_OUTPUT /* this is technically an output plugin */ +@@ -62,6 +65,8 @@ MODULE_TYPE_KEEP /* releasing the module would cause a leak through libcurl */ + MODULE_CNFNAME("mmkubernetes") + DEF_OMOD_STATIC_DATA + DEFobjCurrIf(regexp) ++DEFobjCurrIf(statsobj) ++DEFobjCurrIf(datetime) + + #define HAVE_LOADSAMPLESFROMSTRING 1 + #if defined(NO_LOADSAMPLESFROMSTRING) +@@ -95,12 +100,14 @@ DEFobjCurrIf(regexp) + #define DFLT_CONTAINER_NAME "$!CONTAINER_NAME" /* name of variable holding CONTAINER_NAME value */ + #define DFLT_CONTAINER_ID_FULL "$!CONTAINER_ID_FULL" /* name of variable holding CONTAINER_ID_FULL value */ + #define DFLT_KUBERNETES_URL "https://kubernetes.default.svc.cluster.local:443" ++#define DFLT_BUSY_RETRY_INTERVAL 5 /* retry every 5 seconds */ + + static struct cache_s { + const uchar *kbUrl; + struct hashtable *mdHt; + struct hashtable *nsHt; + pthread_mutex_t *cacheMtx; ++ int lastBusyTime; + } **caches; + + typedef struct { +@@ -116,6 +123,8 @@ struct modConfData_s { + uchar *srcMetadataPath; /* where to get data for kubernetes queries */ + uchar *dstMetadataPath; /* where to put metadata obtained from kubernetes */ + uchar *caCertFile; /* File holding the CA cert (+optional chain) of CA that issued the Kubernetes server cert */ ++ uchar *myCertFile; /* File holding cert corresponding to private key used for client cert auth */ ++ uchar *myPrivKeyFile; /* File holding private key corresponding to cert used for client cert auth */ + sbool allowUnsignedCerts; /* For testing/debugging - do not check for CA certs (CURLOPT_SSL_VERIFYPEER FALSE) */ + uchar *token; /* The token value to use to authenticate to Kubernetes - takes precedence over tokenFile */ + uchar *tokenFile; /* The file whose contents is the token value to use to authenticate to Kubernetes */ +@@ -127,6 +136,7 @@ struct modConfData_s { + uchar *fnRulebase; /* lognorm rulebase filename for container log filename match */ + char *contRules; /* lognorm rules for CONTAINER_NAME value match */ + uchar *contRulebase; /* lognorm rulebase filename for CONTAINER_NAME value match */ ++ int busyRetryInterval; /* how to handle 429 response - 0 means error, non-zero means retry every N seconds */ + }; + + /* action (instance) configuration data */ +@@ -135,6 +145,8 @@ typedef struct _instanceData { + msgPropDescr_t *srcMetadataDescr; /* where to get data for kubernetes queries */ + uchar *dstMetadataPath; /* where to put metadata obtained from kubernetes */ + uchar *caCertFile; /* File holding the CA cert (+optional chain) of CA that issued the Kubernetes server cert */ ++ uchar *myCertFile; /* File holding cert corresponding to private key used for client cert auth */ ++ uchar *myPrivKeyFile; /* File holding private key corresponding to cert used for client cert auth */ + sbool allowUnsignedCerts; /* For testing/debugging - do not check for CA certs (CURLOPT_SSL_VERIFYPEER FALSE) */ + uchar *token; /* The token value to use to authenticate to Kubernetes - takes precedence over tokenFile */ + uchar *tokenFile; /* The file whose contents is the token value to use to authenticate to Kubernetes */ +@@ -151,6 +163,7 @@ typedef struct _instanceData { + msgPropDescr_t *contNameDescr; /* CONTAINER_NAME field */ + msgPropDescr_t *contIdFullDescr; /* CONTAINER_ID_FULL field */ + struct cache_s *cache; ++ int busyRetryInterval; /* how to handle 429 response - 0 means error, non-zero means retry every N seconds */ + } instanceData; + + typedef struct wrkrInstanceData { +@@ -159,6 +172,16 @@ typedef struct wrkrInstanceData { + struct curl_slist *curlHdr; + char *curlRply; + size_t curlRplyLen; ++ statsobj_t *stats; /* stats for this instance */ ++ STATSCOUNTER_DEF(k8sRecordSeen, mutK8sRecordSeen) ++ STATSCOUNTER_DEF(namespaceMetadataSuccess, mutNamespaceMetadataSuccess) ++ STATSCOUNTER_DEF(namespaceMetadataNotFound, mutNamespaceMetadataNotFound) ++ STATSCOUNTER_DEF(namespaceMetadataBusy, mutNamespaceMetadataBusy) ++ STATSCOUNTER_DEF(namespaceMetadataError, mutNamespaceMetadataError) ++ STATSCOUNTER_DEF(podMetadataSuccess, mutPodMetadataSuccess) ++ STATSCOUNTER_DEF(podMetadataNotFound, mutPodMetadataNotFound) ++ STATSCOUNTER_DEF(podMetadataBusy, mutPodMetadataBusy) ++ STATSCOUNTER_DEF(podMetadataError, mutPodMetadataError) + } wrkrInstanceData_t; + + /* module parameters (v6 config format) */ +@@ -167,6 +190,8 @@ static struct cnfparamdescr modpdescr[] = { + { "srcmetadatapath", eCmdHdlrString, 0 }, + { "dstmetadatapath", eCmdHdlrString, 0 }, + { "tls.cacert", eCmdHdlrString, 0 }, ++ { "tls.mycert", eCmdHdlrString, 0 }, ++ { "tls.myprivkey", eCmdHdlrString, 0 }, + { "allowunsignedcerts", eCmdHdlrBinary, 0 }, + { "token", eCmdHdlrString, 0 }, + { "tokenfile", eCmdHdlrString, 0 }, +@@ -174,7 +199,8 @@ static struct cnfparamdescr modpdescr[] = { + { "de_dot", eCmdHdlrBinary, 0 }, + { "de_dot_separator", eCmdHdlrString, 0 }, + { "filenamerulebase", eCmdHdlrString, 0 }, +- { "containerrulebase", eCmdHdlrString, 0 } ++ { "containerrulebase", eCmdHdlrString, 0 }, ++ { "busyretryinterval", eCmdHdlrInt, 0 } + #if HAVE_LOADSAMPLESFROMSTRING == 1 + , + { "filenamerules", eCmdHdlrArray, 0 }, +@@ -193,6 +219,8 @@ static struct cnfparamdescr actpdescr[] = { + { "srcmetadatapath", eCmdHdlrString, 0 }, + { "dstmetadatapath", eCmdHdlrString, 0 }, + { "tls.cacert", eCmdHdlrString, 0 }, ++ { "tls.mycert", eCmdHdlrString, 0 }, ++ { "tls.myprivkey", eCmdHdlrString, 0 }, + { "allowunsignedcerts", eCmdHdlrBinary, 0 }, + { "token", eCmdHdlrString, 0 }, + { "tokenfile", eCmdHdlrString, 0 }, +@@ -200,7 +228,8 @@ static struct cnfparamdescr actpdescr[] = { + { "de_dot", eCmdHdlrBinary, 0 }, + { "de_dot_separator", eCmdHdlrString, 0 }, + { "filenamerulebase", eCmdHdlrString, 0 }, +- { "containerrulebase", eCmdHdlrString, 0 } ++ { "containerrulebase", eCmdHdlrString, 0 }, ++ { "busyretryinterval", eCmdHdlrInt, 0 } + #if HAVE_LOADSAMPLESFROMSTRING == 1 + , + { "filenamerules", eCmdHdlrArray, 0 }, +@@ -493,8 +522,9 @@ ENDbeginCnfLoad + BEGINsetModCnf + struct cnfparamvals *pvals = NULL; + int i; +- FILE *fp; ++ FILE *fp = NULL; + int ret; ++ char errStr[1024]; + CODESTARTsetModCnf + pvals = nvlstGetParams(lst, &modpblk, NULL); + if(pvals == NULL) { +@@ -509,6 +539,7 @@ CODESTARTsetModCnf + } + + loadModConf->de_dot = DFLT_DE_DOT; ++ loadModConf->busyRetryInterval = DFLT_BUSY_RETRY_INTERVAL; + for(i = 0 ; i < modpblk.nParams ; ++i) { + if(!pvals[i].bUsed) { + continue; +@@ -528,15 +559,42 @@ CODESTARTsetModCnf + loadModConf->caCertFile = (uchar *) es_str2cstr(pvals[i].val.d.estr, NULL); + fp = fopen((const char*)loadModConf->caCertFile, "r"); + if(fp == NULL) { +- char errStr[1024]; + rs_strerror_r(errno, errStr, sizeof(errStr)); + iRet = RS_RET_NO_FILE_ACCESS; + LogError(0, iRet, +- "error: certificate file %s couldn't be accessed: %s\n", ++ "error: 'tls.cacert' file %s couldn't be accessed: %s\n", + loadModConf->caCertFile, errStr); + ABORT_FINALIZE(iRet); + } else { + fclose(fp); ++ fp = NULL; ++ } ++ } else if(!strcmp(modpblk.descr[i].name, "tls.mycert")) { ++ free(loadModConf->myCertFile); ++ loadModConf->myCertFile = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); ++ fp = fopen((const char*)loadModConf->myCertFile, "r"); ++ if(fp == NULL) { ++ rs_strerror_r(errno, errStr, sizeof(errStr)); ++ iRet = RS_RET_NO_FILE_ACCESS; ++ LogError(0, iRet, ++ "error: 'tls.mycert' file %s couldn't be accessed: %s\n", ++ loadModConf->myCertFile, errStr); ++ } else { ++ fclose(fp); ++ fp = NULL; ++ } ++ } else if(!strcmp(modpblk.descr[i].name, "tls.myprivkey")) { ++ loadModConf->myPrivKeyFile = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); ++ fp = fopen((const char*)loadModConf->myPrivKeyFile, "r"); ++ if(fp == NULL) { ++ rs_strerror_r(errno, errStr, sizeof(errStr)); ++ iRet = RS_RET_NO_FILE_ACCESS; ++ LogError(0, iRet, ++ "error: 'tls.myprivkey' file %s couldn't be accessed: %s\n", ++ loadModConf->myPrivKeyFile, errStr); ++ } else { ++ fclose(fp); ++ fp = NULL; + } + } else if(!strcmp(modpblk.descr[i].name, "allowunsignedcerts")) { + loadModConf->allowUnsignedCerts = pvals[i].val.d.n; +@@ -548,7 +606,6 @@ CODESTARTsetModCnf + loadModConf->tokenFile = (uchar *) es_str2cstr(pvals[i].val.d.estr, NULL); + fp = fopen((const char*)loadModConf->tokenFile, "r"); + if(fp == NULL) { +- char errStr[1024]; + rs_strerror_r(errno, errStr, sizeof(errStr)); + iRet = RS_RET_NO_FILE_ACCESS; + LogError(0, iRet, +@@ -557,6 +614,7 @@ CODESTARTsetModCnf + ABORT_FINALIZE(iRet); + } else { + fclose(fp); ++ fp = NULL; + } + } else if(!strcmp(modpblk.descr[i].name, "annotation_match")) { + free_annotationmatch(&loadModConf->annotation_match); +@@ -577,7 +635,6 @@ CODESTARTsetModCnf + loadModConf->fnRulebase = (uchar *) es_str2cstr(pvals[i].val.d.estr, NULL); + fp = fopen((const char*)loadModConf->fnRulebase, "r"); + if(fp == NULL) { +- char errStr[1024]; + rs_strerror_r(errno, errStr, sizeof(errStr)); + iRet = RS_RET_NO_FILE_ACCESS; + LogError(0, iRet, +@@ -586,6 +643,7 @@ CODESTARTsetModCnf + ABORT_FINALIZE(iRet); + } else { + fclose(fp); ++ fp = NULL; + } + #if HAVE_LOADSAMPLESFROMSTRING == 1 + } else if(!strcmp(modpblk.descr[i].name, "containerrules")) { +@@ -597,7 +655,6 @@ CODESTARTsetModCnf + loadModConf->contRulebase = (uchar *) es_str2cstr(pvals[i].val.d.estr, NULL); + fp = fopen((const char*)loadModConf->contRulebase, "r"); + if(fp == NULL) { +- char errStr[1024]; + rs_strerror_r(errno, errStr, sizeof(errStr)); + iRet = RS_RET_NO_FILE_ACCESS; + LogError(0, iRet, +@@ -606,7 +663,10 @@ CODESTARTsetModCnf + ABORT_FINALIZE(iRet); + } else { + fclose(fp); ++ fp = NULL; + } ++ } else if(!strcmp(modpblk.descr[i].name, "busyretryinterval")) { ++ loadModConf->busyRetryInterval = pvals[i].val.d.n; + } else { + dbgprintf("mmkubernetes: program error, non-handled " + "param '%s' in module() block\n", modpblk.descr[i].name); +@@ -650,6 +710,8 @@ CODESTARTsetModCnf + caches = calloc(1, sizeof(struct cache_s *)); + + finalize_it: ++ if (fp) ++ fclose(fp); + if(pvals != NULL) + cnfparamvalsDestruct(pvals, &modpblk); + ENDsetModCnf +@@ -667,6 +729,8 @@ CODESTARTfreeInstance + free(pData->srcMetadataDescr); + free(pData->dstMetadataPath); + free(pData->caCertFile); ++ free(pData->myCertFile); ++ free(pData->myPrivKeyFile); + free(pData->token); + free(pData->tokenFile); + free(pData->fnRules); +@@ -710,6 +774,45 @@ CODESTARTcreateWrkrInstance + char *tokenHdr = NULL; + FILE *fp = NULL; + char *token = NULL; ++ char *statsName = NULL; ++ ++ CHKiRet(statsobj.Construct(&(pWrkrData->stats))); ++ if ((-1 == asprintf(&statsName, "mmkubernetes(%s)", pWrkrData->pData->kubernetesUrl)) || ++ (!statsName)) { ++ ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY); ++ } ++ CHKiRet(statsobj.SetName(pWrkrData->stats, (uchar *)statsName)); ++ free(statsName); ++ statsName = NULL; ++ CHKiRet(statsobj.SetOrigin(pWrkrData->stats, UCHAR_CONSTANT("mmkubernetes"))); ++ STATSCOUNTER_INIT(pWrkrData->k8sRecordSeen, pWrkrData->mutK8sRecordSeen); ++ CHKiRet(statsobj.AddCounter(pWrkrData->stats, UCHAR_CONSTANT("recordseen"), ++ ctrType_IntCtr, CTR_FLAG_RESETTABLE, &(pWrkrData->k8sRecordSeen))); ++ STATSCOUNTER_INIT(pWrkrData->namespaceMetadataSuccess, pWrkrData->mutNamespaceMetadataSuccess); ++ CHKiRet(statsobj.AddCounter(pWrkrData->stats, UCHAR_CONSTANT("namespacemetadatasuccess"), ++ ctrType_IntCtr, CTR_FLAG_RESETTABLE, &(pWrkrData->namespaceMetadataSuccess))); ++ STATSCOUNTER_INIT(pWrkrData->namespaceMetadataNotFound, pWrkrData->mutNamespaceMetadataNotFound); ++ CHKiRet(statsobj.AddCounter(pWrkrData->stats, UCHAR_CONSTANT("namespacemetadatanotfound"), ++ ctrType_IntCtr, CTR_FLAG_RESETTABLE, &(pWrkrData->namespaceMetadataNotFound))); ++ STATSCOUNTER_INIT(pWrkrData->namespaceMetadataBusy, pWrkrData->mutNamespaceMetadataBusy); ++ CHKiRet(statsobj.AddCounter(pWrkrData->stats, UCHAR_CONSTANT("namespacemetadatabusy"), ++ ctrType_IntCtr, CTR_FLAG_RESETTABLE, &(pWrkrData->namespaceMetadataBusy))); ++ STATSCOUNTER_INIT(pWrkrData->namespaceMetadataError, pWrkrData->mutNamespaceMetadataError); ++ CHKiRet(statsobj.AddCounter(pWrkrData->stats, UCHAR_CONSTANT("namespacemetadataerror"), ++ ctrType_IntCtr, CTR_FLAG_RESETTABLE, &(pWrkrData->namespaceMetadataError))); ++ STATSCOUNTER_INIT(pWrkrData->podMetadataSuccess, pWrkrData->mutPodMetadataSuccess); ++ CHKiRet(statsobj.AddCounter(pWrkrData->stats, UCHAR_CONSTANT("podmetadatasuccess"), ++ ctrType_IntCtr, CTR_FLAG_RESETTABLE, &(pWrkrData->podMetadataSuccess))); ++ STATSCOUNTER_INIT(pWrkrData->podMetadataNotFound, pWrkrData->mutPodMetadataNotFound); ++ CHKiRet(statsobj.AddCounter(pWrkrData->stats, UCHAR_CONSTANT("podmetadatanotfound"), ++ ctrType_IntCtr, CTR_FLAG_RESETTABLE, &(pWrkrData->podMetadataNotFound))); ++ STATSCOUNTER_INIT(pWrkrData->podMetadataBusy, pWrkrData->mutPodMetadataBusy); ++ CHKiRet(statsobj.AddCounter(pWrkrData->stats, UCHAR_CONSTANT("podmetadatabusy"), ++ ctrType_IntCtr, CTR_FLAG_RESETTABLE, &(pWrkrData->podMetadataBusy))); ++ STATSCOUNTER_INIT(pWrkrData->podMetadataError, pWrkrData->mutPodMetadataError); ++ CHKiRet(statsobj.AddCounter(pWrkrData->stats, UCHAR_CONSTANT("podmetadataerror"), ++ ctrType_IntCtr, CTR_FLAG_RESETTABLE, &(pWrkrData->podMetadataError))); ++ CHKiRet(statsobj.ConstructFinalize(pWrkrData->stats)); + + hdr = curl_slist_append(hdr, "Content-Type: text/json; charset=utf-8"); + if (pWrkrData->pData->token) { +@@ -749,12 +852,20 @@ CODESTARTcreateWrkrInstance + curl_easy_setopt(ctx, CURLOPT_WRITEDATA, pWrkrData); + if(pWrkrData->pData->caCertFile) + curl_easy_setopt(ctx, CURLOPT_CAINFO, pWrkrData->pData->caCertFile); ++ if(pWrkrData->pData->myCertFile) ++ curl_easy_setopt(ctx, CURLOPT_SSLCERT, pWrkrData->pData->myCertFile); ++ if(pWrkrData->pData->myPrivKeyFile) ++ curl_easy_setopt(ctx, CURLOPT_SSLKEY, pWrkrData->pData->myPrivKeyFile); + if(pWrkrData->pData->allowUnsignedCerts) + curl_easy_setopt(ctx, CURLOPT_SSL_VERIFYPEER, 0); + + pWrkrData->curlCtx = ctx; + finalize_it: + free(token); ++ free(statsName); ++ if ((iRet != RS_RET_OK) && pWrkrData->stats) { ++ statsobj.Destruct(&(pWrkrData->stats)); ++ } + if (fp) { + fclose(fp); + } +@@ -765,6 +876,7 @@ BEGINfreeWrkrInstance + CODESTARTfreeWrkrInstance + curl_easy_cleanup(pWrkrData->curlCtx); + curl_slist_free_all(pWrkrData->curlHdr); ++ statsobj.Destruct(&(pWrkrData->stats)); + ENDfreeWrkrInstance + + +@@ -790,6 +902,8 @@ cacheNew(const uchar *const url) + key_equals_string, hashtable_json_object_put); + cache->nsHt = create_hashtable(100, hash_from_string, + key_equals_string, hashtable_json_object_put); ++ dbgprintf("mmkubernetes: created cache mdht [%p] nsht [%p]\n", ++ cache->mdHt, cache->nsHt); + cache->cacheMtx = malloc(sizeof(pthread_mutex_t)); + if (!cache->mdHt || !cache->nsHt || !cache->cacheMtx) { + free (cache); +@@ -797,6 +911,7 @@ cacheNew(const uchar *const url) + FINALIZE; + } + pthread_mutex_init(cache->cacheMtx, NULL); ++ cache->lastBusyTime = 0; + + finalize_it: + return cache; +@@ -816,9 +931,10 @@ static void cacheFree(struct cache_s *cache) + BEGINnewActInst + struct cnfparamvals *pvals = NULL; + int i; +- FILE *fp; ++ FILE *fp = NULL; + char *rxstr = NULL; + char *srcMetadataPath = NULL; ++ char errStr[1024]; + CODESTARTnewActInst + DBGPRINTF("newActInst (mmkubernetes)\n"); + +@@ -840,6 +956,7 @@ CODESTARTnewActInst + + pData->de_dot = loadModConf->de_dot; + pData->allowUnsignedCerts = loadModConf->allowUnsignedCerts; ++ pData->busyRetryInterval = loadModConf->busyRetryInterval; + for(i = 0 ; i < actpblk.nParams ; ++i) { + if(!pvals[i].bUsed) { + continue; +@@ -863,7 +980,6 @@ CODESTARTnewActInst + pData->caCertFile = (uchar *) es_str2cstr(pvals[i].val.d.estr, NULL); + fp = fopen((const char*)pData->caCertFile, "r"); + if(fp == NULL) { +- char errStr[1024]; + rs_strerror_r(errno, errStr, sizeof(errStr)); + iRet = RS_RET_NO_FILE_ACCESS; + LogError(0, iRet, +@@ -872,6 +988,33 @@ CODESTARTnewActInst + ABORT_FINALIZE(iRet); + } else { + fclose(fp); ++ fp = NULL; ++ } ++ } else if(!strcmp(actpblk.descr[i].name, "tls.mycert")) { ++ pData->myCertFile = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); ++ fp = fopen((const char*)pData->myCertFile, "r"); ++ if(fp == NULL) { ++ rs_strerror_r(errno, errStr, sizeof(errStr)); ++ iRet = RS_RET_NO_FILE_ACCESS; ++ LogError(0, iRet, ++ "error: 'tls.mycert' file %s couldn't be accessed: %s\n", ++ pData->myCertFile, errStr); ++ } else { ++ fclose(fp); ++ fp = NULL; ++ } ++ } else if(!strcmp(actpblk.descr[i].name, "tls.myprivkey")) { ++ pData->myPrivKeyFile = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL); ++ fp = fopen((const char*)pData->myPrivKeyFile, "r"); ++ if(fp == NULL) { ++ rs_strerror_r(errno, errStr, sizeof(errStr)); ++ iRet = RS_RET_NO_FILE_ACCESS; ++ LogError(0, iRet, ++ "error: 'tls.myprivkey' file %s couldn't be accessed: %s\n", ++ pData->myPrivKeyFile, errStr); ++ } else { ++ fclose(fp); ++ fp = NULL; + } + } else if(!strcmp(actpblk.descr[i].name, "allowunsignedcerts")) { + pData->allowUnsignedCerts = pvals[i].val.d.n; +@@ -883,7 +1026,6 @@ CODESTARTnewActInst + pData->tokenFile = (uchar *) es_str2cstr(pvals[i].val.d.estr, NULL); + fp = fopen((const char*)pData->tokenFile, "r"); + if(fp == NULL) { +- char errStr[1024]; + rs_strerror_r(errno, errStr, sizeof(errStr)); + iRet = RS_RET_NO_FILE_ACCESS; + LogError(0, iRet, +@@ -892,6 +1034,7 @@ CODESTARTnewActInst + ABORT_FINALIZE(iRet); + } else { + fclose(fp); ++ fp = NULL; + } + } else if(!strcmp(actpblk.descr[i].name, "annotation_match")) { + free_annotationmatch(&pData->annotation_match); +@@ -912,7 +1055,6 @@ CODESTARTnewActInst + pData->fnRulebase = (uchar *) es_str2cstr(pvals[i].val.d.estr, NULL); + fp = fopen((const char*)pData->fnRulebase, "r"); + if(fp == NULL) { +- char errStr[1024]; + rs_strerror_r(errno, errStr, sizeof(errStr)); + iRet = RS_RET_NO_FILE_ACCESS; + LogError(0, iRet, +@@ -921,6 +1063,7 @@ CODESTARTnewActInst + ABORT_FINALIZE(iRet); + } else { + fclose(fp); ++ fp = NULL; + } + #if HAVE_LOADSAMPLESFROMSTRING == 1 + } else if(!strcmp(modpblk.descr[i].name, "containerrules")) { +@@ -932,7 +1075,6 @@ CODESTARTnewActInst + pData->contRulebase = (uchar *) es_str2cstr(pvals[i].val.d.estr, NULL); + fp = fopen((const char*)pData->contRulebase, "r"); + if(fp == NULL) { +- char errStr[1024]; + rs_strerror_r(errno, errStr, sizeof(errStr)); + iRet = RS_RET_NO_FILE_ACCESS; + LogError(0, iRet, +@@ -941,7 +1083,10 @@ CODESTARTnewActInst + ABORT_FINALIZE(iRet); + } else { + fclose(fp); ++ fp = NULL; + } ++ } else if(!strcmp(actpblk.descr[i].name, "busyretryinterval")) { ++ pData->busyRetryInterval = pvals[i].val.d.n; + } else { + dbgprintf("mmkubernetes: program error, non-handled " + "param '%s' in action() block\n", actpblk.descr[i].name); +@@ -982,6 +1127,10 @@ CODESTARTnewActInst + pData->dstMetadataPath = (uchar *) strdup((char *) loadModConf->dstMetadataPath); + if(pData->caCertFile == NULL && loadModConf->caCertFile) + pData->caCertFile = (uchar *) strdup((char *) loadModConf->caCertFile); ++ if(pData->myCertFile == NULL && loadModConf->myCertFile) ++ pData->myCertFile = (uchar *) strdup((char *) loadModConf->myCertFile); ++ if(pData->myPrivKeyFile == NULL && loadModConf->myPrivKeyFile) ++ pData->myPrivKeyFile = (uchar *) strdup((char *) loadModConf->myPrivKeyFile); + if(pData->token == NULL && loadModConf->token) + pData->token = (uchar *) strdup((char *) loadModConf->token); + if(pData->tokenFile == NULL && loadModConf->tokenFile) +@@ -1018,6 +1167,8 @@ CODESTARTnewActInst + CODE_STD_FINALIZERnewActInst + if(pvals != NULL) + cnfparamvalsDestruct(pvals, &actpblk); ++ if(fp) ++ fclose(fp); + free(rxstr); + free(srcMetadataPath); + ENDnewActInst +@@ -1061,6 +1212,8 @@ CODESTARTfreeCnf + free(pModConf->srcMetadataPath); + free(pModConf->dstMetadataPath); + free(pModConf->caCertFile); ++ free(pModConf->myCertFile); ++ free(pModConf->myPrivKeyFile); + free(pModConf->token); + free(pModConf->tokenFile); + free(pModConf->de_dot_separator); +@@ -1069,8 +1222,11 @@ CODESTARTfreeCnf + free(pModConf->contRules); + free(pModConf->contRulebase); + free_annotationmatch(&pModConf->annotation_match); +- for(i = 0; caches[i] != NULL; i++) ++ for(i = 0; caches[i] != NULL; i++) { ++ dbgprintf("mmkubernetes: freeing cache [%d] mdht [%p] nsht [%p]\n", ++ i, caches[i]->mdHt, caches[i]->nsHt); + cacheFree(caches[i]); ++ } + free(caches); + ENDfreeCnf + +@@ -1082,6 +1238,8 @@ CODESTARTdbgPrintInstInfo + dbgprintf("\tsrcMetadataPath='%s'\n", pData->srcMetadataDescr->name); + dbgprintf("\tdstMetadataPath='%s'\n", pData->dstMetadataPath); + dbgprintf("\ttls.cacert='%s'\n", pData->caCertFile); ++ dbgprintf("\ttls.mycert='%s'\n", pData->myCertFile); ++ dbgprintf("\ttls.myprivkey='%s'\n", pData->myPrivKeyFile); + dbgprintf("\tallowUnsignedCerts='%d'\n", pData->allowUnsignedCerts); + dbgprintf("\ttoken='%s'\n", pData->token); + dbgprintf("\ttokenFile='%s'\n", pData->tokenFile); +@@ -1093,6 +1251,7 @@ CODESTARTdbgPrintInstInfo + dbgprintf("\tfilenamerules='%s'\n", pData->fnRules); + dbgprintf("\tcontainerrules='%s'\n", pData->contRules); + #endif ++ dbgprintf("\tbusyretryinterval='%d'\n", pData->busyRetryInterval); + ENDdbgPrintInstInfo + + +@@ -1206,6 +1365,24 @@ queryKB(wrkrInstanceData_t *pWrkrData, char *url, struct json_object **rply) + struct json_object *jo; + long resp_code = 400; + ++ if (pWrkrData->pData->cache->lastBusyTime) { ++ time_t now; ++ datetime.GetTime(&now); ++ now -= pWrkrData->pData->cache->lastBusyTime; ++ if (now < pWrkrData->pData->busyRetryInterval) { ++ LogMsg(0, RS_RET_RETRY, LOG_DEBUG, ++ "mmkubernetes: Waited [%ld] of [%d] seconds for the requested url [%s]\n", ++ now, pWrkrData->pData->busyRetryInterval, url); ++ ABORT_FINALIZE(RS_RET_RETRY); ++ } else { ++ LogMsg(0, RS_RET_OK, LOG_DEBUG, ++ "mmkubernetes: Cleared busy status after [%d] seconds - " ++ "will retry the requested url [%s]\n", ++ pWrkrData->pData->busyRetryInterval, url); ++ pWrkrData->pData->cache->lastBusyTime = 0; ++ } ++ } ++ + /* query kubernetes for pod info */ + ccode = curl_easy_setopt(pWrkrData->curlCtx, CURLOPT_URL, url); + if(ccode != CURLE_OK) +@@ -1238,17 +1415,23 @@ queryKB(wrkrInstanceData_t *pWrkrData, char *url, struct json_object **rply) + ABORT_FINALIZE(RS_RET_ERR); + } + if(resp_code == 404) { +- LogMsg(0, RS_RET_ERR, LOG_ERR, ++ LogMsg(0, RS_RET_NOT_FOUND, LOG_INFO, + "mmkubernetes: Not Found: the resource does not exist at url [%s]\n", + url); +- ABORT_FINALIZE(RS_RET_ERR); ++ ABORT_FINALIZE(RS_RET_NOT_FOUND); + } + if(resp_code == 429) { +- LogMsg(0, RS_RET_ERR, LOG_ERR, ++ if (pWrkrData->pData->busyRetryInterval) { ++ time_t now; ++ datetime.GetTime(&now); ++ pWrkrData->pData->cache->lastBusyTime = now; ++ } ++ ++ LogMsg(0, RS_RET_RETRY, LOG_INFO, + "mmkubernetes: Too Many Requests: the server is too heavily loaded " + "to provide the data for the requested url [%s]\n", + url); +- ABORT_FINALIZE(RS_RET_ERR); ++ ABORT_FINALIZE(RS_RET_RETRY); + } + if(resp_code != 200) { + LogMsg(0, RS_RET_ERR, LOG_ERR, +@@ -1299,12 +1482,14 @@ BEGINdoAction + char *mdKey = NULL; + struct json_object *jMetadata = NULL, *jMetadataCopy = NULL, *jMsgMeta = NULL, + *jo = NULL; +- int add_ns_metadata = 0; ++ int add_pod_metadata = 1; + CODESTARTdoAction + CHKiRet_Hdlr(extractMsgMetadata(pMsg, pWrkrData->pData, &jMsgMeta)) { + ABORT_FINALIZE((iRet == RS_RET_NOT_FOUND) ? RS_RET_OK : iRet); + } + ++ STATSCOUNTER_INC(pWrkrData->k8sRecordSeen, pWrkrData->mutK8sRecordSeen); ++ + if (fjson_object_object_get_ex(jMsgMeta, "pod_name", &jo)) + podName = json_object_get_string(jo); + if (fjson_object_object_get_ex(jMsgMeta, "namespace_name", &jo)) +@@ -1347,28 +1532,49 @@ CODESTARTdoAction + } + iRet = queryKB(pWrkrData, url, &jReply); + free(url); +- /* todo: implement support for the .orphaned namespace */ +- if (iRet != RS_RET_OK) { ++ if (iRet == RS_RET_NOT_FOUND) { ++ /* negative cache namespace - make a dummy empty namespace metadata object */ ++ jNsMeta = json_object_new_object(); ++ STATSCOUNTER_INC(pWrkrData->namespaceMetadataNotFound, ++ pWrkrData->mutNamespaceMetadataNotFound); ++ } else if (iRet == RS_RET_RETRY) { ++ /* server is busy - retry or error */ ++ STATSCOUNTER_INC(pWrkrData->namespaceMetadataBusy, ++ pWrkrData->mutNamespaceMetadataBusy); ++ if (0 == pWrkrData->pData->busyRetryInterval) { ++ pthread_mutex_unlock(pWrkrData->pData->cache->cacheMtx); ++ ABORT_FINALIZE(RS_RET_ERR); ++ } ++ add_pod_metadata = 0; /* don't cache pod metadata either - retry both */ ++ } else if (iRet != RS_RET_OK) { ++ /* hard error - something the admin needs to fix e.g. network, config, auth */ + json_object_put(jReply); + jReply = NULL; ++ STATSCOUNTER_INC(pWrkrData->namespaceMetadataError, ++ pWrkrData->mutNamespaceMetadataError); + pthread_mutex_unlock(pWrkrData->pData->cache->cacheMtx); + FINALIZE; +- } +- +- if(fjson_object_object_get_ex(jReply, "metadata", &jNsMeta)) { ++ } else if (fjson_object_object_get_ex(jReply, "metadata", &jNsMeta)) { + jNsMeta = json_object_get(jNsMeta); + parse_labels_annotations(jNsMeta, &pWrkrData->pData->annotation_match, + pWrkrData->pData->de_dot, + (const char *)pWrkrData->pData->de_dot_separator, + pWrkrData->pData->de_dot_separator_len); +- add_ns_metadata = 1; ++ STATSCOUNTER_INC(pWrkrData->namespaceMetadataSuccess, ++ pWrkrData->mutNamespaceMetadataSuccess); + } else { + /* namespace with no metadata??? */ + LogMsg(0, RS_RET_ERR, LOG_INFO, + "mmkubernetes: namespace [%s] has no metadata!\n", ns); +- jNsMeta = NULL; ++ /* negative cache namespace - make a dummy empty namespace metadata object */ ++ jNsMeta = json_object_new_object(); ++ STATSCOUNTER_INC(pWrkrData->namespaceMetadataSuccess, ++ pWrkrData->mutNamespaceMetadataSuccess); + } + ++ if(jNsMeta) { ++ hashtable_insert(pWrkrData->pData->cache->nsHt, strdup(ns), jNsMeta); ++ } + json_object_put(jReply); + jReply = NULL; + } +@@ -1381,14 +1587,28 @@ CODESTARTdoAction + } + iRet = queryKB(pWrkrData, url, &jReply); + free(url); +- if(iRet != RS_RET_OK) { +- if(jNsMeta && add_ns_metadata) { +- hashtable_insert(pWrkrData->pData->cache->nsHt, strdup(ns), jNsMeta); ++ if (iRet == RS_RET_NOT_FOUND) { ++ /* negative cache pod - make a dummy empty pod metadata object */ ++ iRet = RS_RET_OK; ++ STATSCOUNTER_INC(pWrkrData->podMetadataNotFound, pWrkrData->mutPodMetadataNotFound); ++ } else if (iRet == RS_RET_RETRY) { ++ /* server is busy - retry or error */ ++ STATSCOUNTER_INC(pWrkrData->podMetadataBusy, pWrkrData->mutPodMetadataBusy); ++ if (0 == pWrkrData->pData->busyRetryInterval) { ++ pthread_mutex_unlock(pWrkrData->pData->cache->cacheMtx); ++ ABORT_FINALIZE(RS_RET_ERR); + } ++ add_pod_metadata = 0; /* do not cache so that we can retry */ ++ iRet = RS_RET_OK; ++ } else if(iRet != RS_RET_OK) { ++ /* hard error - something the admin needs to fix e.g. network, config, auth */ + json_object_put(jReply); + jReply = NULL; ++ STATSCOUNTER_INC(pWrkrData->podMetadataError, pWrkrData->mutPodMetadataError); + pthread_mutex_unlock(pWrkrData->pData->cache->cacheMtx); + FINALIZE; ++ } else { ++ STATSCOUNTER_INC(pWrkrData->podMetadataSuccess, pWrkrData->mutPodMetadataSuccess); + } + + jo = json_object_new_object(); +@@ -1435,11 +1655,9 @@ CODESTARTdoAction + json_object_object_add(jo, "container_id", json_object_get(jo2)); + json_object_object_add(jMetadata, "docker", jo); + +- hashtable_insert(pWrkrData->pData->cache->mdHt, mdKey, jMetadata); +- mdKey = NULL; +- if(jNsMeta && add_ns_metadata) { +- hashtable_insert(pWrkrData->pData->cache->nsHt, strdup(ns), jNsMeta); +- ns = NULL; ++ if (add_pod_metadata) { ++ hashtable_insert(pWrkrData->pData->cache->mdHt, mdKey, jMetadata); ++ mdKey = NULL; + } + } + +@@ -1450,6 +1668,11 @@ CODESTARTdoAction + * outside of the cache lock + */ + jMetadataCopy = json_tokener_parse(json_object_get_string(jMetadata)); ++ if (!add_pod_metadata) { ++ /* jMetadata object was created from scratch and not cached */ ++ json_object_put(jMetadata); ++ jMetadata = NULL; ++ } + pthread_mutex_unlock(pWrkrData->pData->cache->cacheMtx); + /* the +1 is there to skip the leading '$' */ + msgAddJSON(pMsg, (uchar *) pWrkrData->pData->dstMetadataPath + 1, jMetadataCopy, 0, 0); +@@ -1470,7 +1693,9 @@ BEGINmodExit + CODESTARTmodExit + curl_global_cleanup(); + ++ objRelease(datetime, CORE_COMPONENT); + objRelease(regexp, LM_REGEXP_FILENAME); ++ objRelease(statsobj, CORE_COMPONENT); + ENDmodExit + + +@@ -1489,8 +1714,9 @@ CODESTARTmodInit + *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */ + CODEmodInit_QueryRegCFSLineHdlr + DBGPRINTF("mmkubernetes: module compiled with rsyslog version %s.\n", VERSION); ++ CHKiRet(objUse(statsobj, CORE_COMPONENT)); + CHKiRet(objUse(regexp, LM_REGEXP_FILENAME)); +- ++ CHKiRet(objUse(datetime, CORE_COMPONENT)); + /* CURL_GLOBAL_ALL initializes more than is needed but the + * libcurl documentation discourages use of other values + */ diff --git a/SOURCES/rsyslog-8.37.0-rhbz1659898-imjournal-default-tag.patch b/SOURCES/rsyslog-8.37.0-rhbz1659898-imjournal-default-tag.patch new file mode 100644 index 0000000..cab8873 --- /dev/null +++ b/SOURCES/rsyslog-8.37.0-rhbz1659898-imjournal-default-tag.patch @@ -0,0 +1,93 @@ +diff -up ./plugins/imjournal/imjournal.c.default-tag ./plugins/imjournal/imjournal.c +--- ./plugins/imjournal/imjournal.c.default-tag 2018-05-17 08:50:11.416418022 -0400 ++++ ./plugins/imjournal/imjournal.c 2018-05-17 08:53:02.884418022 -0400 +@@ -78,6 +78,7 @@ static struct configSettings_s { + int bUseJnlPID; + char *usePid; + int bWorkAroundJournalBug; ++ char *dfltTag; + } cs; + + static rsRetVal facilityHdlr(uchar **pp, void *pVal); +@@ -93,7 +94,8 @@ static struct cnfparamdescr modpdescr[] + { "defaultfacility", eCmdHdlrString, 0 }, + { "usepidfromsystem", eCmdHdlrBinary, 0 }, + { "usepid", eCmdHdlrString, 0 }, +- { "workaroundjournalbug", eCmdHdlrBinary, 0 } ++ { "workaroundjournalbug", eCmdHdlrBinary, 0 }, ++ { "defaulttag", eCmdHdlrGetWord, 0 } + }; + static struct cnfparamblk modpblk = + { CNFPARAMBLK_VERSION, +@@ -104,6 +106,7 @@ static struct cnfparamblk modpblk = + #define DFLT_persiststateinterval 10 + #define DFLT_SEVERITY pri2sev(LOG_NOTICE) + #define DFLT_FACILITY pri2fac(LOG_USER) ++#define DFLT_TAG "journal" + + static int bLegacyCnfModGlobalsPermitted = 1;/* are legacy module-global config parameters permitted? */ + +@@ -268,7 +271,7 @@ readjournal(void) + + /* Information from messages */ + char *message = NULL; +- char *sys_iden; ++ char *sys_iden = NULL; + char *sys_iden_help = NULL; + char *c = NULL; + +@@ -331,7 +334,7 @@ readjournal(void) + if (sd_journal_get_data(j, "SYSLOG_IDENTIFIER", &get, &length) >= 0) { + CHKiRet(sanitizeValue(((const char *)get) + 18, length - 18, &sys_iden)); + } else { +- CHKmalloc(sys_iden = strdup("journal")); ++ CHKmalloc(sys_iden = strdup(cs.dfltTag)); + } + + /* trying to get PID, default is "SYSLOG_PID" property */ +@@ -654,6 +657,11 @@ CODESTARTrunInput + "\"usepidfromsystem\" is depricated, use \"usepid\" instead"); + } + ++ if (cs.dfltTag == NULL) { ++ cs.dfltTag = strdup(DFLT_TAG); ++ } ++ ++ + if (cs.usePid && (strcmp(cs.usePid, "system") == 0)) { + pidFieldName = "_PID"; + bPidFallBack = 0; +@@ -732,6 +740,7 @@ CODESTARTbeginCnfLoad + cs.bUseJnlPID = -1; + cs.usePid = NULL; + cs.bWorkAroundJournalBug = 0; ++ cs.dfltTag = NULL; + ENDbeginCnfLoad + + +@@ -754,6 +763,7 @@ BEGINfreeCnf + CODESTARTfreeCnf + free(cs.stateFile); + free(cs.usePid); ++ free(cs.dfltTag); + statsobj.Destruct(&(statsCounter.stats)); + ENDfreeCnf + +@@ -832,6 +842,8 @@ CODESTARTsetModCnf + cs.usePid = (char *)es_str2cstr(pvals[i].val.d.estr, NULL); + } else if (!strcmp(modpblk.descr[i].name, "workaroundjournalbug")) { + cs.bWorkAroundJournalBug = (int) pvals[i].val.d.n; ++ } else if (!strcmp(modpblk.descr[i].name, "defaulttag")) { ++ cs.dfltTag = (char *)es_str2cstr(pvals[i].val.d.estr, NULL); + } else { + dbgprintf("imjournal: program error, non-handled " + "param '%s' in beginCnfLoad\n", modpblk.descr[i].name); +@@ -799,6 +820,8 @@ CODEmodInit_QueryRegCFSLineHdlr + facilityHdlr, &cs.iDfltFacility, STD_LOADABLE_MODULE_ID)); + CHKiRet(omsdRegCFSLineHdlr((uchar *)"imjournalusepidfromsystem", 0, eCmdHdlrBinary, + NULL, &cs.bUseJnlPID, STD_LOADABLE_MODULE_ID)); ++ CHKiRet(omsdRegCFSLineHdlr((uchar *)"imjournaldefaulttag", 0, eCmdHdlrGetWord, ++ NULL, &cs.dfltTag, STD_LOADABLE_MODULE_ID)); + ENDmodInit + /* vim:set ai: + */ diff --git a/SOURCES/rsyslog.conf b/SOURCES/rsyslog.conf new file mode 100644 index 0000000..c4ac740 --- /dev/null +++ b/SOURCES/rsyslog.conf @@ -0,0 +1,79 @@ +# rsyslog configuration file + +# For more information see /usr/share/doc/rsyslog-*/rsyslog_conf.html +# or latest version online at http://www.rsyslog.com/doc/rsyslog_conf.html +# If you experience problems, see http://www.rsyslog.com/doc/troubleshoot.html + +#### MODULES #### + +module(load="imuxsock" # provides support for local system logging (e.g. via logger command) + SysSock.Use="off") # Turn off message reception via local log socket; + # local messages are retrieved through imjournal now. +module(load="imjournal" # provides access to the systemd journal + StateFile="imjournal.state") # File to store the position in the journal +#module(load="imklog") # reads kernel messages (the same are read from journald) +#module(load"immark") # provides --MARK-- message capability + +# Provides UDP syslog reception +# for parameters see http://www.rsyslog.com/doc/imudp.html +#module(load="imudp") # needs to be done just once +#input(type="imudp" port="514") + +# Provides TCP syslog reception +# for parameters see http://www.rsyslog.com/doc/imtcp.html +#module(load="imtcp") # needs to be done just once +#input(type="imtcp" port="514") + +#### GLOBAL DIRECTIVES #### + +# Where to place auxiliary files +global(workDirectory="/var/lib/rsyslog") + +# Use default timestamp format +module(load="builtin:omfile" Template="RSYSLOG_TraditionalFileFormat") + +# Include all config files in /etc/rsyslog.d/ +include(file="/etc/rsyslog.d/*.conf" mode="optional") + +#### RULES #### + +# Log all kernel messages to the console. +# Logging much else clutters up the screen. +#kern.* /dev/console + +# Log anything (except mail) of level info or higher. +# Don't log private authentication messages! +*.info;mail.none;authpriv.none;cron.none /var/log/messages + +# The authpriv file has restricted access. +authpriv.* /var/log/secure + +# Log all the mail messages in one place. +mail.* -/var/log/maillog + + +# Log cron stuff +cron.* /var/log/cron + +# Everybody gets emergency messages +*.emerg :omusrmsg:* + +# Save news errors of level crit and higher in a special file. +uucp,news.crit /var/log/spooler + +# Save boot messages also to boot.log +local7.* /var/log/boot.log + + +# ### sample forwarding rule ### +#action(type="omfwd" +# An on-disk queue is created for this action. If the remote host is +# down, messages are spooled to disk and sent when it is up again. +#queue.filename="fwdRule1" # unique name prefix for spool files +#queue.maxdiskspace="1g" # 1gb space limit (use as much as possible) +#queue.saveonshutdown="on" # save messages to disk on shutdown +#queue.type="LinkedList" # run asynchronously +#action.resumeRetryCount="-1" # infinite retries if host is down +# Remote Logging (we use TCP for reliable delivery) +# remote_host is: name/ip, e.g. 192.168.0.1, port optional e.g. 10514 +#Target="remote_host" Port="XXX" Protocol="tcp") diff --git a/SOURCES/rsyslog.log b/SOURCES/rsyslog.log new file mode 100644 index 0000000..b101e32 --- /dev/null +++ b/SOURCES/rsyslog.log @@ -0,0 +1,12 @@ +/var/log/cron +/var/log/maillog +/var/log/messages +/var/log/secure +/var/log/spooler +{ + missingok + sharedscripts + postrotate + /usr/bin/systemctl kill -s HUP rsyslog.service >/dev/null 2>&1 || true + endscript +} diff --git a/SOURCES/rsyslog.sysconfig b/SOURCES/rsyslog.sysconfig new file mode 100644 index 0000000..bc65731 --- /dev/null +++ b/SOURCES/rsyslog.sysconfig @@ -0,0 +1,5 @@ +# Options for rsyslogd +# Syslogd options are deprecated since rsyslog v3. +# If you want to use them, switch to compatibility mode 2 by "-c 2" +# See rsyslogd(8) for more details +SYSLOGD_OPTIONS="" diff --git a/SPECS/rsyslog.spec b/SPECS/rsyslog.spec new file mode 100644 index 0000000..b1e9b23 --- /dev/null +++ b/SPECS/rsyslog.spec @@ -0,0 +1,525 @@ +%define rsyslog_statedir %{_sharedstatedir}/%{name} +%define rsyslog_pkidir %{_sysconfdir}/pki/%{name} +%define rsyslog_docdir %{_docdir}/%{name} + + +Summary: Enhanced system logging and kernel message trapping daemon +Name: rsyslog +Version: 8.37.0 +Release: 9%{?dist} +License: (GPLv3+ and ASL 2.0) +Group: System Environment/Daemons +ExcludeArch: i686 +URL: http://www.rsyslog.com/ +Source0: http://www.rsyslog.com/files/download/rsyslog/%{name}-%{version}.tar.gz +Source1: http://www.rsyslog.com/files/download/rsyslog/%{name}-doc-%{version}.tar.gz +Source2: rsyslog.conf +Source3: rsyslog.sysconfig +Source4: rsyslog.log + +BuildRequires: autoconf +BuildRequires: automake +BuildRequires: bison +BuildRequires: flex +BuildRequires: libcurl-devel +BuildRequires: libgcrypt-devel +BuildRequires: libfastjson-devel >= 0.99.8 +BuildRequires: libestr-devel >= 0.1.9 +BuildRequires: libtool +BuildRequires: libuuid-devel +BuildRequires: pkgconfig +BuildRequires: python3-docutils +# it depens on rhbz#1419228 +BuildRequires: systemd-devel >= 219-39 +BuildRequires: zlib-devel + +Requires: logrotate >= 3.5.2 +Requires: bash >= 2.0 +Requires: libestr >= 0.1.9 +Requires(post): systemd +Requires(preun): systemd +Requires(postun): systemd + +Provides: syslog +Obsoletes: sysklogd < 1.5-11 + +# tweak the upstream service file to honour configuration from /etc/sysconfig/rsyslog +Patch0: rsyslog-8.32.0-service.patch +# imjournal: adds "journal" when tag/process name is missing +Patch1: rsyslog-8.37.0-rhbz1659898-imjournal-default-tag.patch +Patch2: rsyslog-8.37.0-rhbz1614179-imfile-symlink-support.patch +Patch3: rsyslog-8.37.0-rhbz1622768-kubernetes-404-handling.patch + +%package crypto +Summary: Encryption support +Group: System Environment/Daemons +Requires: %name = %version-%release + +%package doc +Summary: HTML Documentation for rsyslog +Group: Documentation +#no reason to have arched documentation +BuildArch: noarch + +%package elasticsearch +Summary: ElasticSearch output module for rsyslog +Group: System Environment/Daemons +Requires: %name = %version-%release + +%package gnutls +Summary: TLS protocol support for rsyslog +Group: System Environment/Daemons +Requires: %name = %version-%release +BuildRequires: gnutls-devel + +%package gssapi +Summary: GSSAPI authentication and encryption support for rsyslog +Group: System Environment/Daemons +Requires: %name = %version-%release +BuildRequires: krb5-devel + +%package kafka +Summary: Provides kafka support for rsyslog +Group: System Environment/Daemons +Requires: %name = %version-%release +BuildRequires: librdkafka-devel + +%package mmaudit +Summary: Message modification module supporting Linux audit format +Group: System Environment/Daemons +Requires: %name = %version-%release + +%package mmjsonparse +Summary: JSON enhanced logging support +Group: System Environment/Daemons +Requires: %name = %version-%release + +%package mmkubernetes +Summary: Provides the mmkubernetes module +Group: System Environment/Daemons +Requires: %name = %version-%release + +%package mmnormalize +Summary: Log normalization support for rsyslog +Group: System Environment/Daemons +Requires: %name = %version-%release +BuildRequires: liblognorm-devel + +%package mmsnmptrapd +Summary: Message modification module for snmptrapd generated messages +Group: System Environment/Daemons +Requires: %name = %version-%release + +%package mysql +Summary: MySQL support for rsyslog +Group: System Environment/Daemons +Requires: %name = %version-%release +BuildRequires: mariadb-connector-c-devel + +%package pgsql +Summary: PostgresSQL support for rsyslog +Group: System Environment/Daemons +Requires: %name = %version-%release +BuildRequires: postgresql-devel + +%package relp +Summary: RELP protocol support for rsyslog +Group: System Environment/Daemons +Requires: %name = %version-%release +Requires: librelp >= 1.0.3 +BuildRequires: librelp-devel >= 1.2.16 + +%package snmp +Summary: SNMP protocol support for rsyslog +Group: System Environment/Daemons +Requires: %name = %version-%release +BuildRequires: net-snmp-devel + +%description +Rsyslog is an enhanced, multi-threaded syslog daemon. It supports MySQL, +syslog/TCP, RFC 3195, permitted sender lists, filtering on any message part, +and fine grain output format control. It is compatible with stock sysklogd +and can be used as a drop-in replacement. Rsyslog is simple to set up, with +advanced features suitable for enterprise-class, encryption-protected syslog +relay chains. + +%description crypto +This package contains a module providing log file encryption and a +command line tool to process encrypted logs. + +%description doc +This subpackage contains documentation for rsyslog. + +%description elasticsearch +This module provides the capability for rsyslog to feed logs directly into +Elasticsearch. + +%description gnutls +The rsyslog-gnutls package contains the rsyslog plugins that provide the +ability to receive syslog messages via upcoming syslog-transport-tls +IETF standard protocol. + +%description gssapi +The rsyslog-gssapi package contains the rsyslog plugins which support GSSAPI +authentication and secure connections. GSSAPI is commonly used for Kerberos +authentication. + +%description kafka +The rsyslog-kafka package provides modules for Apache Kafka input and output. + +%description mmaudit +This module provides message modification supporting Linux audit format +in various settings. + +%description mmjsonparse +This module provides the capability to recognize and parse JSON enhanced +syslog messages. + +%description mmkubernetes +The rsyslog-mmkubernetes package provides module for adding kubernetes +container metadata. + +%description mmnormalize +This module provides the capability to normalize log messages via liblognorm. + +%description mmsnmptrapd +This message modification module takes messages generated from snmptrapd and +modifies them so that they look like they originated from the read originator. + +%description mysql +The rsyslog-mysql package contains a dynamic shared object that will add +MySQL database support to rsyslog. + +%description pgsql +The rsyslog-pgsql package contains a dynamic shared object that will add +PostgreSQL database support to rsyslog. + +%description relp +The rsyslog-relp package contains the rsyslog plugins that provide +the ability to receive syslog messages via the reliable RELP +protocol. + +%description snmp +The rsyslog-snmp package contains the rsyslog plugin that provides the +ability to send syslog messages as SNMPv1 and SNMPv2c traps. + +%prep +# set up rsyslog-doc sources +%setup -q -a 1 -T -c + +#regenerate the docs + +#mv build/searchindex.js searchindex_backup.js +#sphinx-build -b html source build +#clean up +#mv searchindex_backup.js build/searchindex.js + +rm -r LICENSE README.md source build/objects.inv +mv build doc + +# set up rsyslog sources +%setup -q -D + +%patch0 -p1 -b .service +%patch1 -p1 -b .default-tag +%patch2 -p1 -b .imfile-symlink +%patch3 -p1 -b .mmkubernetes-404 + +%build +%ifarch sparc64 +#sparc64 need big PIE +export CFLAGS="$RPM_OPT_FLAGS -fPIE" +%else +export CFLAGS="$RPM_OPT_FLAGS -fpie" +%endif +export LDFLAGS="-pie -Wl,-z,relro -Wl,-z,now" + +sed -i 's/%{version}/%{version}-%{release}/g' configure.ac +autoreconf -if +%configure \ + --prefix=/usr \ + --disable-static \ + --disable-testbench \ + --enable-elasticsearch \ + --enable-generate-man-pages \ + --enable-gnutls \ + --enable-gssapi-krb5 \ + --enable-imdiag \ + --enable-imfile \ + --enable-imjournal \ + --enable-imkafka \ + --enable-impstats \ + --enable-imptcp \ + --enable-mail \ + --enable-mmanon \ + --enable-mmaudit \ + --enable-mmcount \ + --enable-mmjsonparse \ + --enable-mmkubernetes \ + --enable-mmnormalize \ + --enable-mmsnmptrapd \ + --enable-mmutf8fix \ + --enable-mysql \ + --enable-omjournal \ + --enable-omkafka \ + --enable-omprog \ + --enable-omstdout \ + --enable-omuxsock \ + --enable-pgsql \ + --enable-pmaixforwardedfrom \ + --enable-pmcisconames \ + --enable-pmlastmsg \ + --enable-pmsnare \ + --enable-relp \ + --enable-snmp \ + --enable-unlimited-select \ + --enable-usertools + +make + +%install +make DESTDIR=%{buildroot} install + +install -d -m 755 %{buildroot}%{_sysconfdir}/sysconfig +install -d -m 755 %{buildroot}%{_sysconfdir}/logrotate.d +install -d -m 755 %{buildroot}%{_sysconfdir}/rsyslog.d +install -d -m 700 %{buildroot}%{rsyslog_statedir} +install -d -m 700 %{buildroot}%{rsyslog_pkidir} +install -d -m 755 %{buildroot}%{rsyslog_docdir}/html + +install -p -m 644 %{SOURCE2} %{buildroot}%{_sysconfdir}/rsyslog.conf +install -p -m 644 %{SOURCE3} %{buildroot}%{_sysconfdir}/sysconfig/rsyslog +install -p -m 644 %{SOURCE4} %{buildroot}%{_sysconfdir}/logrotate.d/syslog +install -p -m 644 plugins/ommysql/createDB.sql %{buildroot}%{rsyslog_docdir}/mysql-createDB.sql +install -p -m 644 plugins/ompgsql/createDB.sql %{buildroot}%{rsyslog_docdir}/pgsql-createDB.sql +install -p -m 644 contrib/mmkubernetes/*.rulebase %{buildroot}%{rsyslog_docdir} +# extract documentation +cp -r doc/* %{buildroot}%{rsyslog_docdir}/html +# get rid of libtool libraries +rm -f %{buildroot}%{_libdir}/rsyslog/*.la +# get rid of socket activation by default +sed -i '/^Alias/s/^/;/;/^Requires=syslog.socket/s/^/;/' %{buildroot}%{_unitdir}/rsyslog.service + +# convert line endings from "\r\n" to "\n" +cat tools/recover_qi.pl | tr -d '\r' > %{buildroot}%{_bindir}/rsyslog-recover-qi.pl + +%post +for n in /var/log/{messages,secure,maillog,spooler} +do + [ -f $n ] && continue + umask 066 && touch $n +done +%systemd_post rsyslog.service + +%preun +%systemd_preun rsyslog.service + +%postun +%systemd_postun_with_restart rsyslog.service + +%files +%doc AUTHORS COPYING* ChangeLog +%exclude %{rsyslog_docdir}/html +%exclude %{rsyslog_docdir}/mysql-createDB.sql +%exclude %{rsyslog_docdir}/pgsql-createDB.sql +%dir %{_libdir}/rsyslog +%dir %{_sysconfdir}/rsyslog.d +%dir %{rsyslog_statedir} +%dir %{rsyslog_pkidir} +%{_sbindir}/rsyslogd +%attr(755,root,root) %{_bindir}/rsyslog-recover-qi.pl +%{_mandir}/man5/rsyslog.conf.5.gz +%{_mandir}/man8/rsyslogd.8.gz +%{_unitdir}/rsyslog.service +%config(noreplace) %{_sysconfdir}/rsyslog.conf +%config(noreplace) %{_sysconfdir}/sysconfig/rsyslog +%config(noreplace) %{_sysconfdir}/logrotate.d/syslog +# plugins +%{_libdir}/rsyslog/fmhash.so +%{_libdir}/rsyslog/fmhttp.so +%{_libdir}/rsyslog/imdiag.so +%{_libdir}/rsyslog/imfile.so +%{_libdir}/rsyslog/imjournal.so +%{_libdir}/rsyslog/imklog.so +%{_libdir}/rsyslog/immark.so +%{_libdir}/rsyslog/impstats.so +%{_libdir}/rsyslog/imptcp.so +%{_libdir}/rsyslog/imtcp.so +%{_libdir}/rsyslog/imudp.so +%{_libdir}/rsyslog/imuxsock.so +%{_libdir}/rsyslog/lmnet.so +%{_libdir}/rsyslog/lmnetstrms.so +%{_libdir}/rsyslog/lmnsd_ptcp.so +%{_libdir}/rsyslog/lmregexp.so +%{_libdir}/rsyslog/lmstrmsrv.so +%{_libdir}/rsyslog/lmtcpclt.so +%{_libdir}/rsyslog/lmtcpsrv.so +%{_libdir}/rsyslog/lmzlibw.so +%{_libdir}/rsyslog/mmanon.so +%{_libdir}/rsyslog/mmcount.so +%{_libdir}/rsyslog/mmexternal.so +%{_libdir}/rsyslog/mmutf8fix.so +%{_libdir}/rsyslog/omjournal.so +%{_libdir}/rsyslog/ommail.so +%{_libdir}/rsyslog/omprog.so +%{_libdir}/rsyslog/omstdout.so +%{_libdir}/rsyslog/omtesting.so +%{_libdir}/rsyslog/omuxsock.so +%{_libdir}/rsyslog/pmaixforwardedfrom.so +%{_libdir}/rsyslog/pmcisconames.so +%{_libdir}/rsyslog/pmlastmsg.so +%{_libdir}/rsyslog/pmsnare.so + +%files crypto +%{_bindir}/rscryutil +%{_mandir}/man1/rscryutil.1.gz +%{_libdir}/rsyslog/lmcry_gcry.so + +%files doc +%doc %{rsyslog_docdir}/html + +%files elasticsearch +%{_libdir}/rsyslog/omelasticsearch.so + +%files gssapi +%{_libdir}/rsyslog/lmgssutil.so +%{_libdir}/rsyslog/imgssapi.so +%{_libdir}/rsyslog/omgssapi.so + +%files gnutls +%{_libdir}/rsyslog/lmnsd_gtls.so + +%files kafka +%{_libdir}/rsyslog/imkafka.so +%{_libdir}/rsyslog/omkafka.so + +%files mmaudit +%{_libdir}/rsyslog/mmaudit.so + +%files mmjsonparse +%{_libdir}/rsyslog/mmjsonparse.so + +%files mmkubernetes +%{_libdir}/rsyslog/mmkubernetes.so +%doc %{rsyslog_docdir}/k8s_filename.rulebase +%doc %{rsyslog_docdir}/k8s_container_name.rulebase + +%files mmnormalize +%{_libdir}/rsyslog/mmnormalize.so + +%files mmsnmptrapd +%{_libdir}/rsyslog/mmsnmptrapd.so + +%files mysql +%doc %{rsyslog_docdir}/mysql-createDB.sql +%{_libdir}/rsyslog/ommysql.so + +%files pgsql +%doc %{rsyslog_docdir}/pgsql-createDB.sql +%{_libdir}/rsyslog/ompgsql.so + +%files relp +%{_libdir}/rsyslog/imrelp.so +%{_libdir}/rsyslog/omrelp.so + +%files snmp +%{_libdir}/rsyslog/omsnmp.so + + +%changelog +* Mon Dec 17 2018 Jiri Vymazal - 8.37.0-9 + RHEL 8.0.0 ERRATUM +- added back legacy option for imjournal default tag + resolves: rhbz#1659898 + +* Fri Dec 14 2018 Jiri Vymazal - 8.37.0-8 + RHEL 8.0.0 ERRATUM +- fixes mmkubenetes handling 404 and 429 errors + resolves: rhbz#1622768 + +* Fri Oct 19 2018 Jiri Vymazal - 8.37.0-7 +- removed version from docdir macro + resolves: rhbz#1638023 + +* Mon Aug 27 2018 Jiri Vymazal - 8.37.0-6 +- updated patch for enhanced imfile symlink support + resolves: rhbz#1614179 + +* Fri Aug 10 2018 Jiri Vymazal - 8.37.0-5 +- rebuild for rebased dependencies +- dependency cleanup and sorted sub-packages in spec + resolves: rhbz#1613880 + +* Fri Aug 10 2018 Jiri Vymazal - 8.37.0-4 +- enabled mmkubernetes module + resolves: rhbz#1614432 + resolves: rhbz#1614441 + +* Thu Aug 09 2018 Josef Ridky - 8.37.0-3 +- Rebuild for Net-SNMP + +* Thu Aug 09 2018 Jiri Vymazal - 8.37.0-2 +- added patch for enhanced imfile symlink support + resolves: rhbz#1614179 + +* Wed Aug 08 2018 Jiri Vymazal - 8.37.0-1 +- rebase to 8.37.0 + resolves: rhbz#1613880 + resolves: rhbz#1564054 + resolves: rhbz#1598218 + - dropped invalid statefile patch - upstreamed + - dropped imjournal duplicates patch - upstreamed + resolves: rhbz#1544394 +- renumbered default tag patch and fitted onto rebased version + +* Fri Aug 03 2018 Jiri Vymazal - 8.36.0-4 +- removed dependency on libee + resolves: rhbz#1612032 + +* Wed Aug 01 2018 Jiri Vymazal - 8.36.0-3 +- dropped json_nonoverwrite patch as there is no reason for + keeping it +- renumbered rest of patches +- added release number to AC_INIT to have it in package error logs + +* Mon Jul 16 2018 Charalampos Stratakis - 8.36.0-2 +- Depend on python3-docutils + +* Mon Jul 02 2018 Jiri Vymazal - 8.36.0-1 +- changed PID file name to follow upstream +- removed config option to disable stdlog as it is now + disabled by default + +* Thu Jun 28 2018 Jiri Vymazal - 8.36.0-1 +- rebase to 8.36 +- removed hiredis module +- removed omudpspoof module + resolves: rhbz#1593762 +- finished converting config to new-style syntax + +* Mon May 21 2018 Jiri Vymazal - 8.35.0-1 +- spec file cleanup +- enabled kafka and hiredis modules + resolves: rhbz#1542497 + resolves: rhbz#1542504 +- renamed patch fixing imjournal duplicating messages + resolves: rhbz#1544394 + +* Thu May 17 2018 Marek Tamaskovic - 8.35.0-1 +- rebase to 8.35 +- rebased patches from 8.32 to 8.35 + - fixed imjournal-duplicates + - fixed imjournal-default-tag + - fixed service patch + - fixed in upstream deserialize-property-name + +* Fri Mar 23 2018 Radovan Sroka - 8.32.0-2 +- rebuild, bumped release number + +* Tue Feb 06 2018 Radovan Sroka - 8.32.0-1 +- initial clean build with plugins from rhel7 +- removed plugins: + - libdbi + - omruleset + - pmrfc3164sd +- imported from fedora26