1 /* mmcount.c
2 * count messages by priority or json property of given app-name.
3 *
4 * Copyright 2013 Red Hat Inc.
5 * Copyright 2014 Rainer Gerhards
6 *
7 * This file is part of rsyslog.
8 *
9 * Licensed under the Apache License, Version 2.0 (the "License");
10 * you may not use this file except in compliance with the License.
11 * You may obtain a copy of the License at
12 *
13 * http://www.apache.org/licenses/LICENSE-2.0
14 * -or-
15 * see COPYING.ASL20 in the source distribution
16 *
17 * Unless required by applicable law or agreed to in writing, software
18 * distributed under the License is distributed on an "AS IS" BASIS,
19 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20 * See the License for the specific language governing permissions and
21 * limitations under the License.
22 */
23 #include "config.h"
24 #include "rsyslog.h"
25 #include <stdio.h>
26 #include <stdarg.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <assert.h>
30 #include <signal.h>
31 #include <errno.h>
32 #include <unistd.h>
33 #include <stdint.h>
34 #include <json.h>
35 #include "conf.h"
36 #include "syslogd-types.h"
37 #include "srUtils.h"
38 #include "template.h"
39 #include "module-template.h"
40 #include "errmsg.h"
41 #include "hashtable.h"
42
43
44 #define JSON_COUNT_NAME "!mmcount"
45 #define SEVERITY_COUNT 8
46
47 MODULE_TYPE_OUTPUT
48 MODULE_TYPE_NOKEEP
49 MODULE_CNFNAME("mmcount")
50
51
52 DEF_OMOD_STATIC_DATA
53
54 /* config variables */
55
56 typedef struct _instanceData {
57 char *pszAppName;
58 int severity[SEVERITY_COUNT];
59 char *pszKey;
60 char *pszValue;
61 int valueCounter;
62 struct hashtable *ht;
63 pthread_mutex_t mut;
64 } instanceData;
65
66 typedef struct wrkrInstanceData {
67 instanceData *pData;
68 } wrkrInstanceData_t;
69
70 struct modConfData_s {
71 rsconf_t *pConf; /* our overall config object */
72 };
73 static modConfData_t *loadModConf = NULL;/* modConf ptr to use for the current load process */
74 static modConfData_t *runModConf = NULL;/* modConf ptr to use for the current exec process */
75
76
77 /* tables for interfacing with the v6 config system */
78 /* action (instance) parameters */
79 static struct cnfparamdescr actpdescr[] = {
80 { "appname", eCmdHdlrGetWord, 0 },
81 { "key", eCmdHdlrGetWord, 0 },
82 { "value", eCmdHdlrGetWord, 0 },
83 };
84 static struct cnfparamblk actpblk =
85 { CNFPARAMBLK_VERSION,
86 sizeof(actpdescr)/sizeof(struct cnfparamdescr),
87 actpdescr
88 };
89
90 BEGINbeginCnfLoad
91 CODESTARTbeginCnfLoad
92 loadModConf = pModConf;
93 pModConf->pConf = pConf;
94 ENDbeginCnfLoad
95
96 BEGINendCnfLoad
97 CODESTARTendCnfLoad
98 ENDendCnfLoad
99
100 BEGINcheckCnf
101 CODESTARTcheckCnf
102 ENDcheckCnf
103
104 BEGINactivateCnf
105 CODESTARTactivateCnf
106 runModConf = pModConf;
107 ENDactivateCnf
108
109 BEGINfreeCnf
110 CODESTARTfreeCnf
111 ENDfreeCnf
112
113
114 BEGINcreateInstance
115 CODESTARTcreateInstance
116 pthread_mutex_init(&pData->mut, NULL);
117 ENDcreateInstance
118
119 BEGINcreateWrkrInstance
120 CODESTARTcreateWrkrInstance
121 ENDcreateWrkrInstance
122
123
124 BEGINisCompatibleWithFeature
125 CODESTARTisCompatibleWithFeature
126 ENDisCompatibleWithFeature
127
128
129 BEGINfreeInstance
130 CODESTARTfreeInstance
131 ENDfreeInstance
132
133
134 BEGINfreeWrkrInstance
135 CODESTARTfreeWrkrInstance
136 ENDfreeWrkrInstance
137
138 static inline void
setInstParamDefaults(instanceData * pData)139 setInstParamDefaults(instanceData *pData)
140 {
141 int i;
142
143 pData->pszAppName = NULL;
144 for (i = 0; i < SEVERITY_COUNT; i++)
145 pData->severity[i] = 0;
146 pData->pszKey = NULL;
147 pData->pszValue = NULL;
148 pData->valueCounter = 0;
149 pData->ht = NULL;
150 }
151
152 static unsigned int
hash_from_key_fn(void * k)153 hash_from_key_fn(void *k)
154 {
155 return *(unsigned int *)k;
156 }
157
158 static int
key_equals_fn(void * k1,void * k2)159 key_equals_fn(void *k1, void *k2)
160 {
161 return (*(unsigned int *)k1 == *(unsigned int *)k2);
162 }
163
164 BEGINnewActInst
165 struct cnfparamvals *pvals;
166 int i;
167 CODESTARTnewActInst
168 DBGPRINTF("newActInst (mmcount)\n");
169 if((pvals = nvlstGetParams(lst, &actpblk, NULL)) == NULL) {
170 ABORT_FINALIZE(RS_RET_MISSING_CNFPARAMS);
171 }
172
173 CODE_STD_STRING_REQUESTnewActInst(1)
174 CHKiRet(OMSRsetEntry(*ppOMSR, 0, NULL, OMSR_TPL_AS_MSG));
175 CHKiRet(createInstance(&pData));
176 setInstParamDefaults(pData);
177
178 for(i = 0 ; i < actpblk.nParams ; ++i) {
179 if(!pvals[i].bUsed)
180 continue;
181 if(!strcmp(actpblk.descr[i].name, "appname")) {
182 pData->pszAppName = es_str2cstr(pvals[i].val.d.estr, NULL);
183 continue;
184 }
185 if(!strcmp(actpblk.descr[i].name, "key")) {
186 pData->pszKey = es_str2cstr(pvals[i].val.d.estr, NULL);
187 continue;
188 }
189 if(!strcmp(actpblk.descr[i].name, "value")) {
190 pData->pszValue = es_str2cstr(pvals[i].val.d.estr, NULL);
191 continue;
192 }
193 dbgprintf("mmcount: program error, non-handled "
194 "param '%s'\n", actpblk.descr[i].name);
195 }
196
197 if(pData->pszAppName == NULL) {
198 dbgprintf("mmcount: action requires a appname");
199 ABORT_FINALIZE(RS_RET_MISSING_CNFPARAMS);
200 }
201
202 if(pData->pszKey != NULL && pData->pszValue == NULL) {
203 if(NULL == (pData->ht = create_hashtable(100, hash_from_key_fn, key_equals_fn, NULL))) {
204 DBGPRINTF("mmcount: error creating hash table!\n");
205 ABORT_FINALIZE(RS_RET_ERR);
206 }
207 }
208 CODE_STD_FINALIZERnewActInst
209 cnfparamvalsDestruct(pvals, &actpblk);
210 ENDnewActInst
211
212
213 BEGINdbgPrintInstInfo
214 CODESTARTdbgPrintInstInfo
215 ENDdbgPrintInstInfo
216
217
218 BEGINtryResume
219 CODESTARTtryResume
220 ENDtryResume
221
222 static int *
getCounter(struct hashtable * ht,const char * str)223 getCounter(struct hashtable *ht, const char *str) {
224 unsigned int key;
225 int *pCounter;
226 unsigned int *pKey;
227
228 /* we dont store str as key, instead we store hash of the str
229 as key to reduce memory usage */
230 key = hash_from_string((char*)str);
231 pCounter = hashtable_search(ht, &key);
232 if(pCounter) {
233 return pCounter;
234 }
235
236 /* counter is not found for the str, so add new entry and
237 return the counter */
238 if(NULL == (pKey = (unsigned int*)malloc(sizeof(unsigned int)))) {
239 DBGPRINTF("mmcount: memory allocation for key failed\n");
240 return NULL;
241 }
242 *pKey = key;
243
244 if(NULL == (pCounter = (int*)malloc(sizeof(int)))) {
245 DBGPRINTF("mmcount: memory allocation for value failed\n");
246 free(pKey);
247 return NULL;
248 }
249 *pCounter = 0;
250
251 if(!hashtable_insert(ht, pKey, pCounter)) {
252 DBGPRINTF("mmcount: inserting element into hashtable failed\n");
253 free(pKey);
254 free(pCounter);
255 return NULL;
256 }
257 return pCounter;
258 }
259
260 BEGINdoAction_NoStrings
261 smsg_t **ppMsg = (smsg_t **) pMsgData;
262 smsg_t *pMsg = ppMsg[0];
263 char *appname;
264 struct json_object *json = NULL;
265 struct json_object *keyjson = NULL;
266 const char *pszValue;
267 int *pCounter;
268 instanceData *const pData = pWrkrData->pData;
269 CODESTARTdoAction
270 appname = getAPPNAME(pMsg, LOCK_MUTEX);
271
272 pthread_mutex_lock(&pData->mut);
273 if(0 != strcmp(appname, pData->pszAppName)) {
274 /* we are not working for this appname. nothing to do */
275 ABORT_FINALIZE(RS_RET_OK);
276 }
277
278 if(!pData->pszKey) {
279 /* no key given for count, so we count severity */
280 if(pMsg->iSeverity < SEVERITY_COUNT) {
281 pData->severity[pMsg->iSeverity]++;
282 json = json_object_new_int(pData->severity[pMsg->iSeverity]);
283 }
284 ABORT_FINALIZE(RS_RET_OK);
285 }
286
287 /* key is given, so get the property json */
288 msgPropDescr_t pProp;
289 msgPropDescrFill(&pProp, (uchar*)pData->pszKey, strlen(pData->pszKey));
290 rsRetVal localRet = msgGetJSONPropJSON(pMsg, &pProp, &keyjson);
291 msgPropDescrDestruct(&pProp);
292 if(localRet != RS_RET_OK) {
293 /* key not found in the message. nothing to do */
294 ABORT_FINALIZE(RS_RET_OK);
295 }
296
297 /* key found, so get the value */
298 pszValue = (char*)json_object_get_string(keyjson);
299 if(pszValue == NULL) { /* json null object returns NULL! */
300 pszValue = "";
301 }
302
303 if(pData->pszValue) {
304 /* value also given for count */
305 if(!strcmp(pszValue, pData->pszValue)) {
306 /* count for (value and key and appname) matched */
307 pData->valueCounter++;
308 json = json_object_new_int(pData->valueCounter);
309 }
310 ABORT_FINALIZE(RS_RET_OK);
311 }
312
313 /* value is not given, so we count for each value of given key */
314 pCounter = getCounter(pData->ht, pszValue);
315 if(pCounter) {
316 (*pCounter)++;
317 json = json_object_new_int(*pCounter);
318 }
319 finalize_it:
320 pthread_mutex_unlock(&pData->mut);
321
322 if(json) {
323 msgAddJSON(pMsg, (uchar *)JSON_COUNT_NAME, json, 0, 0);
324 }
325 ENDdoAction
326
327
328 NO_LEGACY_CONF_parseSelectorAct
329
330
331 BEGINmodExit
332 CODESTARTmodExit
333 ENDmodExit
334
335
336 BEGINqueryEtryPt
337 CODESTARTqueryEtryPt
338 CODEqueryEtryPt_STD_OMOD_QUERIES
339 CODEqueryEtryPt_STD_OMOD8_QUERIES
340 CODEqueryEtryPt_STD_CONF2_OMOD_QUERIES
341 CODEqueryEtryPt_STD_CONF2_QUERIES
342 ENDqueryEtryPt
343
344
345
346 BEGINmodInit()
347 CODESTARTmodInit
348 *ipIFVersProvided = CURR_MOD_IF_VERSION; /* we only support the current interface specification */
349 CODEmodInit_QueryRegCFSLineHdlr
350 DBGPRINTF("mmcount: module compiled with rsyslog version %s.\n", VERSION);
351 ENDmodInit
352